详细理解GPT2模型结构及其训练过程—GPT系列训练与部署

news2024/11/26 15:00:26

        本文为博主原创文章,未经博主允许不得转载。

        本文为专栏《Python从零开始进行AIGC大模型训练与推理》系列文章,地址为“https://blog.csdn.net/suiyingy/article/details/130169592”。

        GPT2模型环境搭建与调试请参考博文《GPT系列训练与部署—GPT2环境配置与模型训练》和《ColossalAI GPT2分布式训练调试配置—GPT系列训练与部署》,地址分别为“https://blog.csdn.net/suiyingy/article/details/128711444”和“https://blog.csdn.net/suiyingy/article/details/128806531”。本节主要详细介绍GPT2模型结构原理及其训练调试过程。

        另外,本专栏具体更新可关注文章下方公众号,也可关注本专栏。所有相关文章会在《Python从零开始进行AIGC大模型训练与推理》中进行更新,地址为“https://blog.csdn.net/suiyingy/article/details/130169592”。所有AIGC类模型部署的体验效果将在RdFast小程序中同步上线。

1 Transformer主要结构

        Transformer模型是一种用于NLP的神经网络模型,由Google的研究人员在2017年提出并发表在论文《Attention is All you need》。该模型的核心是自注意力机制(self-attention),可以在处理长序列时避免RNN中的梯度消失问题,同时也可以更好地捕捉序列中元素之间的关系。Transformer模型是一种革命性的神经网络架构,它在自然语言处理领域取得了显著的成果,并且已成为该领域的重要基础。

        Transformer模型总体结构如下图所示,属于典型的编码-解码结构,左侧为编码结构,右侧为解码结构。

图1 Transformer模型结构

1.1 自注意力机制(self-attention)

        自注意力机制是指模型进行特征提取时会关注到自身数据之间的相关性。注意力表示关注数据的权重大小。假设有一组序列由A、B、C、D构成,那么A分别受到A、B、C、D影响的大小便相当于注意力大小,即A特征提取时注意到A、B、C、D的程度大小。

        Transformer为了衡量这种注意力取值提出了一种QKV(Query,Key,Value)编码模型。Q、K、V相当于原始数据在新的空间下的表征方式。假设输入序列特征维度为(N_seq,C_embedding),N_seq为输入序列长度,C_embedding为序列中每个元素的特征维度,经过全连接层Linear(C_embedding, C_qkv)之后,输出特征维度为(N_seq,C_qkv)。Transformer分别使用三组这样的全连接操作将原始序列转换为Q、K、V,维度均为(N_seq,C_qkv)。

 图2 QKV过程示意图

        这可以理解为用Q、K、V空间来表示原始数据,Q相当于定义了一种原始数据的查询索引,可类比原序列中的元素位置。(K,V)表示数据取值,采用这种Key-Value的方式来表示数据的好处在于计算注意力大小时不影响取值,即取值与注意力大小进行了解耦。

        注意力大小用Q和K之间的相关性来表示,相关性越大,则注意力权重越大。如下图所示,相关性用矩阵乘法(向量相乘)来进行计算,即Q x KT,那么相乘后输出维度为(N_seq, N_seq)。其每一行代表当前元素与所有全元素的相关性。为了保持输出数据的分布特性,相乘后数据需除以sqrt(dk),dk即为上述C_qkv。显然,除以sqrt(dk)并不会改变注意力的相对大小,而是对整体幅度进行了调整。

 图3 注意力计算

        我们希望注意力权重之和为1,各个权重取值相当于占比多少。那么输出的相乘后数据需要通过Softmax来完成这种权重转化。假设输入序列长度为3(x1, x2, x3),那么经过Softmax后的输出分别为exp(x1)/(exp(x1)+exp(x2)+exp(x3))、exp(x2)/(exp(x1)+exp(x2)+exp(x3))和exp(x1)/(exp(x3)+exp(x2)+exp(x3))。

图4 Softmax

        注意力权重与V相乘,即加权求和,可得到输出结果,维度为(N, C_qkv)。这将输入数据维度从(N_seq,C_embedding)转为(N, C_qkv),相当于用新的序列来表示原序列,并且新的序列考虑到元素之间的相关性。

1.2 自注意力机制与卷积神经网络(CNN)

        一般卷积神经网络通过全连接进行特征提取时相当于得到V,网络模型训练完成时,全连接层所有参数是固定的。任何输入都将使用相同的全连接参数进行变换。注意力机制是通过QK来对V进行加权操作。不同输入情况下QK得到的注意力权重是不一样的,这相当于全连接层参数随输入不同而发生变化。因此,使用自注意力机制后,输入特征提取方式更加多样化,可提取更加丰富且有效的特征。当然,参数量也会相应增加。

        在计算机视觉领域,研究人员也设计了很多受输入影响的卷积操作,例如PAConv和Deformable Conv等。

1.3 多头注意力机制(Multi-head Attention)

        多头注意力机制相当于多次采用自注意力机制提取原始特征并进行拼接融合。我们也可以将上述自注意力过程类比成一次卷积过程,多头注意力机制相当于改变输出通道数量。假设单个自注意力机制模块的输出维度为(N, C_qkv),且注意力机制模块的数量为MA(即Head数量,Multi-head),那么输出维度为MA x N x C_qkv。输出特征进行拼接后的维度为N x (MA x C_qkv),这相当于进行了一次特征融合。在卷积神经网络中,对于拼接的特征,通常会进一步采用一层卷积或全连接来对拼接后的特征进行整体融合。Transformer也用到了这种结构,即Feed-Forward网络。除此之外,Transformer还多次用到残差结构来融合输入特征。

        为了使注意力特征与输入特征保持一致,MA x C_qkv应与C_embedding保持一致。假设C_embedding为512,C_qkv为64,那么多头的数量MA应为8。假设C_embedding为768,C_qkv为64,那么多头的数量MA应为12。

1.4 多层堆叠

        如果注意力模块输出维度和输入维度保持一致,那么该模块可进行多层堆叠,即前一个模块输出(N_seq,C_embedding)作为后一模块输入(N_seq,C_embedding),如下文即将介绍的GPT2模型结构。GPT2结构常见层数如下所示。

表1 GPT2层数与参数量

参数量 层数 词向量长度

117M 12 768

345M 24 1024

762M 36 1280

1542M 48 1600

2 GPT2模型结构

        GPT系列模型仅使用了transformer的编码结构,并采用多层堆叠的方式进行。下图是12层GPT2结构示意图。每一层都是一个注意力机制模块。

图5 12层GPT2

3 GPT2训练程序

        接下来所介绍的GPT2程序来源于Colossal-AI框架,地址为“https://github.com/hpcaitech/ColossalAI-Examples/tree/main/language/gpt”。其环境搭建与调试方法在本专栏之前文章中有详细介绍。

3.1 输入数据与分词

3.1.1 输入数据

        测试所使用的数据为OpenWebText,其下载与预处理过程见《GPT系列训练与部署—GPT2环境配置与模型训练》,地址为“https://blog.csdn.net/suiyingy/article/details/128711444”。处理后原始数据保存于train.json,其数据结构为{'text': text, 'url': unique_url},示例如下所示。

{"text": "The space station looks like an airplane or a very bright star moving across the sky, except it doesn't have flashing lights or change direction. It will also be moving considerably faster than a typical airplane (airplanes generally fly at about 600 miles per hour; the space station flies at 17,500 miles per hour).\n\nBelow is a time-lapse photo of the space station moving across the sky.\n\nThe International Space Station is seen in this 30 second exposure as it flies over Elkton, VA early in the morning, Saturday, August 1, 2015. Photo Credit: NASA/Bill Ingalls\n\nVisit the NASA Johnson Flickr Photostream", "url": "http://spotthestation.nasa.gov/sightings/view.cfm?country=United_States®ion=Arizona&city=Phoenix#.UvPTWWSwLpM"}

        模型输入数据为text中的文本,这里将文本允许的最大分词长度设置为1024,即GPT2模型输入的序列长度N_seq为1024。

3.1.2 分词

        自然语言处理中的分词是指将一段文本按照词语进行切分的过程。分词是自然语言处理领域中的重要基础任务,其主要目的是将连续的自然语言文本切分成具有一定语义意义的词汇序列,以便于进一步的语义分析。

        在中文等语言中,词语之间没有像英文中用空格隔开的方式,而是需要通过分词来进行识别和提取。分词对于机器翻译、信息检索、情感分析等领域都非常重要,因为单个字符或字母并不能表达出完整的意思,只有将其组合成合适的词语才能更好地理解和处理自然语言文本。

        分词操作时我们会有一本字典,这个字典里记录了所有词语及其序号。例如,这里的字典是GPT2Tokenizer,共有50257个词。其中,“<|endoftext|>”是它的最后一个词,也是它的unk token。最后一个词的索引序号为50256。“tokenizer = GPT2Tokenizer.from_pretrained('gpt2')”加载了整个字典。根据词语查询序号的字典为tokenizer.encoder,如tokenizer.encoder['bot'];根据序号查询词语的字典为tokenizer.decoder,如tokenizer.decoder[13645]。需要注意,这个字典并不包含中文。因此,不同的自然语言处理任务所采用的分词字典也可能会出现差异。

        分词最终是将一段文本转换为其相应索引序号组成的序列,即程序中的input_ids。由于将输入序列的最大长度N_seq设置为1024,那么当文本分词后序列实际长度大于1024时,将保留前1024个分词,并删除剩余分词。当文本分词后序列实际实际长度小于1024时,那么需要填充至1024个分词。程序中使用<|endoftext|>进行填充,因此input_ids长度不足1024时用50256进行填充。

        程序中分词输出的另一个部分是attention_mask,这个主要是用于标识出有效分词。属于有效分词的位置取值为1,填充补齐的位置取值为0。

        GPT2分词过程关键程序如下所示,程序位于ColossalAI-Examples/labguage/gpt/dataset/webtext.py。用户可在该文件相关位置设置断点进行程序调试。

tokenizer = GPT2Tokenizer.from_pretrained('gpt2')#50257个词
tokenizer.pad_token = tokenizer.unk_token#'<|endoftext|>'
encoded_data = tokenizer(raw_data, padding=True, truncation=True, max_length=seq_len, return_tensors='pt')
self.data = encoded_data['input_ids']#长度大于1024时将保留前1024个分词,并删除剩余分词;长度不足1024时用50256进行填充。
self.attention_mask = encoded_data['attention_mask']#属于有效分词的位置取值为1,填充补齐的位置取值为0。
torch.save((seq_len, self.data, self.attention_mask), encoded_data_cache_path)#第一次运行时将结果保存,后续可直接加载。

3.2 主体结构

        下面GPT2模型训练程序介绍过程中已将训练的Batch Size设置为1。输入是长度为1024分词序列input_id及其attention_mask,维度均为1x1024。1表示Batch Size大小。程序位于“titans/layer/block/gpt_block.py”,可在相应位置设置断点进行调试

#计算输入有效程度
torch.where(input_ids!=50256)[0].size()
#计算attention_mask长度,与输入有效长度相等
attention_mask.sum()
x = self.embed(input_ids)
#attention_mask取值为1的地方转换为0,取值为0的地方转为-10000。较大的负数在通过Softmax求解自注意力权重时取值接近于0。
if attention_mask is not None:
    batch_size = input_ids.shape[0]
    attention_mask = attention_mask.view(batch_size, -1)
    attention_mask = col_nn.partition_batch(attention_mask)
    attention_mask = attention_mask.unsqueeze(1).unsqueeze(2)
    attention_mask = attention_mask.to(dtype=x.dtype)    # fp16 compatibility
    attention_mask = (1.0 - attention_mask) * -10000.0
for block in self.blocks:#最小配置 12层,隐藏变量维度为768
x, attention_mask = block(x, attention_mask)
x = self.head(self.norm(x))#1x1024x50304

3.2.1 词向量

        上述数据处理过程已将输入文本转换为一个长度为1024的序列。词向量则是进一步用一个特征向量来表示每个词,特征向量的长度即为隐藏变量的长度(HIDDEN_SIZE)。词向量处理函数的入口为“x = self.embed(input_ids)”,输入序列维度为BATCH_SIZE x SEQ_LEN(1x1024),输出序列维度为BATCH_SIZE x SEQ_LEN x HIDDEN_SIZE。

        词向量处理有较多方法,一些研究会专门致力于该方向。最简单的方法是准备一个词向量字典。程序中字典中词的数量为50304,每个词用768(HIDDEN_SIZE)维向量表示。这个字典可以采用随机生成的方式得到,并且每一个词向量都已归一化,归一化后的均值为0,方差为1。最后,根据1024个分词的索引序号来进行字典查询得到相应序号的词向量。

        由于序列存在先后顺序,其位置编码也可以用一个序号来表示,进而也可以用一组位置向量来表征这种先后顺序。由于序列最大长度已设置为1024,那么位置索引的向量字典维度为1024x768。词向量和位置向量相加得到总的词向量,维度为1024x768。

        词向量处理的关键程序如下所示,程序位于“titans/layer/embedding/gpt_embedding.py”,可在相应位置设置断点进行调试,输出维度为1x1024x768。

seq_length = input_ids.size(1)
if position_ids is None:
    bs = input_ids.size(0)
    position_ids = torch.arange(seq_length, dtype=torch.long, device=get_current_device()).unsqueeze(0)
    position_ids = position_ids.repeat(bs, 1)
# the size of input_ids is (BATCH_SIZE, SEQ_LEN)
# the size of x after word_embeddings is (BATCH_SIZE, SEQ_LEN, HIDDEN_SIZE)
x = self.word_embeddings(input_ids) + self.position_embeddings(position_ids)#词向量与位置向量,1x1024x768
if self.tokentype_embeddings is not None and tokentype_ids is not None:
    x = x + self.tokentype_embeddings(tokentype_ids)
x = self.dropout(x)#增加轻微扰动
return x

3.2.2 注意力模块

        根据前文描述,该GPT2程序中采用了12层完全一样的注意力模块。每个模块由两个残差模块组成,分别是自注意力模块和Feed Forward前馈模块,并且输入会首先进行归一化。归一化后的均值为0,方差为1。12层注意力模块的最终输出均为1x1024x768。

        (a)自注意力模块

        第一个残差模块如下图所示,其特征提取采用了多头自注意力机制。其中,多头数量为12,QKV特征维度为64,因而最终输出特征维度经过拼接后仍然为768,即12x64。

图6 第一个残差模块

        对应程序如下所示,程序位于“titans/layer/block/gpt_block.py”,可在相应位置设置断点进行调试,输出维度为1x1024x768。attention_mask取值为1的地方转换为0,取值为0的地方转为-10000。较大的负数在通过Softmax求解自注意力权重时取值接近于0。

if not self.apply_post_layernorm:
    residual = x
x = self.norm1(x)#dropout操作之后数据不再是归一化的,需重新归一化,特征维度上均值为0,方差为1
residual = x #1x024x768
x = residual + self.attn(x, attention_mask)#1x1024x768

        程序中关键部分在与自注意力特征提取,即self.attn(x, attention_mask)。具体程序位于“titans/layer/attention/gpt_attention.py”,可在相应位置设置断点进行调试,输出维度为1x1024x768。主要步骤如下所示:

        1)计算QKV,输入维度为1x1024x768。QKV是在768维度上进行变换,各自需要转换成64维度,共64*3个输出。由于多头的数量为12,因此转换后特征总维度为64*3*12,即2304。因此,通过全连接层VanillaLinear(768,2304)计算得到1x1024x2304维度输出。经过维度变换,程序分别提取Q、K、V分量,维度均为1x12x1024x64。

        2)计算自注意力权重得分矩阵。Q与K相乘并除以sqrt(64)得到1x12x1024x1024初步权重矩阵。程序分别将当前词之后的权重设置为较大绝对值的负数,这是因为下一个词的预测结果仅与之前的内容有关。另一方面,权重矩阵与attention_mask相加同样将无效位置权重设置为较大绝对值的负数。经过Softmax操作后,每个词的注意力权重之和为1,并且之前绝对值较大的负数相应位置权重取值接近于0。输出维度为1x12x1024x1024。

        3)加权求和。V经过加权求和后维度仍然为1x12x1024x64。

        4)多头特征拼接。1x12x1024x64维度特征拼接后得到1x1024x768维新特征。

        5)特征拼接融合。1x1024x768维特征拼接后通过全连接层VanillaLinear(768,768)进行再次融合,并加入dropout扰动。最终输出维度为1x1024x768。

        自注意力特征提取的关键程序解析如下所示。

# the size of x is (BATCH_SIZE, SEQ_LEN, HIDDEN_SIZE)
# the size of qkv is (BATCH_SIZE, SEQ_LEN, HIDDEN_SIZE*3)
qkv = self.query_key_value(x)#计算QKV,1x1024x2304
all_head_size = qkv.shape[-1] // 3#多头隐藏变量维度,768
num_attention_heads = divide(all_head_size, self.attention_head_size)#计算头的数量,每个头的基本隐藏变量数量为64,那么768/64=12
new_qkv_shape = qkv.shape[:-1] + (num_attention_heads, 3 * self.attention_head_size)#1x1024x12x192,q、k、v的特征维度各为64
qkv = qkv.view(new_qkv_shape)#1x1024x12x192
qkv = qkv.permute((0, 2, 1, 3))#1x12x1024x192
# the size of q is (BATCH_SZIE, NUM_HEADS, SEQ_LEN, HIDDEN_SIZE//NUM_HEADS)
q, k, v = torch.chunk(qkv, 3, dim=-1)#q、k、v分量,1x12x1024x64, 1x12x1024x64, 1x12x1024x64
# the size of x after matmul is (BATCH_SIZE, NUM_HEADS, SEQ_LEN, SEQ_LEN)
x = torch.matmul(q, k.transpose(-1, -2))#自注意力机制权重得分矩阵,1x12x1024x1024
x = x / math.sqrt(self.attention_head_size)# x / sqrt(64),1x12x1024x1024
q_len, k_len = q.size(-2), k.size(-2)#1024, 1024
causal_mask = torch.tril(torch.ones((q_len, k_len), dtype=torch.uint8, device=get_current_device())).view(1, 1, q_len, k_len).bool()#下三角矩阵,当前词仅与之前词相关,每一行代表一组权重,1x1x1024x1024
x = torch.where(causal_mask, x, torch.tensor(-1e4, dtype=x.dtype, device=get_current_device()))#True为x,False为-1e4,1x12x1024x1024
x = x + attention_mask#无效处全部置为较大负值,从而在softmax操作后权重得分基本为0,1x12x1024x1024。
x = self.softmax(x)#转换为权重概率得分,1x12x1024x1024
x = self.attention_dropout(x)#对数据增加扰动,1x12x1024x1024
# the size of x after matmul is (BATCH_SZIE, NUM_HEADS, SEQ_LEN, HIDDEN_SIZE//NUM_HEADS)
x = torch.matmul(x, v)#对v进行加权求和,1x12x1024x64
x = x.transpose(1, 2)#1x1024x12x64
new_context_layer_shape = x.size()[:-2] + (all_head_size,)#1x1024x768
# the size of x after reshape is (BATCH_SZIE, SEQ_LEN, HIDDEN_SIZE)
x = x.reshape(new_context_layer_shape)#多头拼接,1x1024x768
# the size of x after dense is (BATCH_SZIE, SEQ_LEN, HIDDEN_SIZE)
x = self.dense(x)#Linear(768, 768),相当于再次进行一次特征融合,1x1024x768
x = self.dropout(x)#对数据增加扰动,1x1024x768
return x#1x1024x768

(b)Feed ForWard前馈模块

        第二个残差模块如下图所示。

 图7 第二个残差模块

        对应程序如下所示,程序位于“titans/layer/block/gpt_block.py”,可在相应位置设置断点进行调试,输出维度为1x1024x768。前馈过程主要由两层全连接层VanillaLinear(768, 3072)和VanillaLinear(3072, 768)来完成,最终输出维度仍然为1x1024x768。

residual = x
x = self.norm2(x)#dropout操作之后数据不再是归一化的,需重新归一化,特征维度上均值为0,方差为1
x = residual + self.mlp(x)#VanillaLinear(768, 3072)、VanillaLinear(3072, 768)、dropout,再次特征融合

        注意力模块的最终输出均为1x1024x768。

2)Head

        Head的作用是将特征映射回带解决问题对应的空间,函数入口为“x = self.head(self.norm(x))”。经过注意力模块后程序所提取的特征维度为1x1024x768。我们需要根据每个768维度特征计算出其属于哪个分词,即对特征进行分类。词向量字典共有50304个类别。

        GPT2 Head由一层LayerNorm归一化层和一个全连接层(768,50304)组成,输出维度为1x1024x50304。

3.3 损失函数

        GPT2模型训练采用的是一种无监督方式,逐一采用当前序列预测下一个分词。因此,其标签也为输入分词索引序号。第N个分词提取到的特征与1~N个分词均相关联,并采用该特征预测第N+1个分词索引序号。根据输入分词序列,第N+1个分词序号是已知。

        对应程序如下所示,程序位于“tians/loss/lm_loss/gpt_lmloss.py”,可在相应位置设置断点进行调试,输出维度为1x1024x768。损失函数为交叉损失商函数(CrossEntropyLoss),这也是最常使用的分类损失函数。损失函数的预测输入特征维度为1023x50304,标签维度为1023。

class GPTLMLoss(nn.Module):
    def __init__(self):
        super().__init__()
        self.loss = col_nn.CrossEntropyLoss()
    def forward(self, logits, labels):
        shift_logits = logits[..., :-1, :].contiguous()#预测结果
        shift_labels = labels[..., 1:].contiguous()#下一个取值为预测值对应的标签
        # Flatten the tokens
# shift_logits.view(-1, shift_logits.size(-1)).shape,1023x50304
# shift_labels.view(-1).shape,1023
        return self.loss(shift_logits.view(-1, shift_logits.size(-1)), shift_labels.view(-1))

4 训练命令与结果

        GPT2模型环境搭建与调试请参考博文《GPT系列训练与部署—GPT2环境配置与模型训练》和《ColossalAI GPT2分布式训练调试配置—GPT系列训练与部署》,地址分别为“https://blog.csdn.net/suiyingy/article/details/128711444”和“https://blog.csdn.net/suiyingy/article/details/128806531”。

        ColosssalAI-Examples的GPT训练命令为“colossalai run --nproc_per_node=2 train_gpt.py --config=gpt2_configs/gpt2_vanilla.py --from_torch”。运行结果如下图所示。

 图8 训练结果示意图

5 模型保存与加载

5.1 模型保存

        在ColossalAI-Examples/language/gpt/train_gpt.py文件中,将hook_list中的hooks.SaveCheckpointHook(checkpoint_dir='./ckpt')取消注释后即可保存训练模型。默认设置下,训练模型会保存在当前目录下的ckpt之中。如果采用调试的方式运行,训练模型可能会被保存到用户的home目录下,可以用命令“find ~ -name ckpt”来进行搜索。

5.2 模型训练加载

        ColossalAI-Examples/language/gpt/train_gpt.py文件中默认是没有加载预训练模型的接口,我们需要自己写一个入口程序,如下所示。新的程序添加到114行“logger.info('Build optimizer', ranks=[0])”之后,即第115行。

if os.path.exists('ckpt'):
    logger.info('Loading pretrained model from ckpt', ranks=[0])
    from collections import OrderedDict
    new_state_dict = OrderedDict()
    state_dict = torch.load('ckpt')
    for k, v in state_dict['model'].items():
        name = k[6:]  # remove `module.`
        new_state_dict[name] = v
    model.load_state_dict(new_state_dict)

        本文为博主原创文章,未经博主允许不得转载。

        本文为专栏《Python从零开始进行AIGC大模型训练与推理》系列文章,地址为“https://blog.csdn.net/suiyingy/article/details/130169592”。

        另外,本专栏具体更新可关注文章下方公众号,也可关注本专栏。所有相关文章会在《Python从零开始进行AIGC大模型训练与推理》中进行更新,地址为“https://blog.csdn.net/suiyingy/article/details/130169592”。所有AIGC类模型部署的体验效果将在RdFast小程序中同步上线。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/587338.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一分钟学一个 Linux 命令 - ls

前言 大家好&#xff0c;我是 god23bin。今天我给大家带来的是 Linux 命令系列&#xff0c;每天只需一分钟&#xff0c;记住一个 Linux 命令不成问题。今天&#xff0c;我们要介绍的是一个常用而又强大的命令&#xff1a;ls&#xff08;list&#xff09;。 什么是 ls 命令&am…

CVPR 2023 | 去雨去噪去模糊,图像low-level任务,视觉AIGC系列

Learning A Sparse Transformer Network for Effective Image Deraining 基于Transformer的方法在图像去雨任务中取得了显著的性能&#xff0c;因为它们可以对重要的非局部信息进行建模&#xff0c;这对高质量的图像重建至关重要。本文发现大多数现有的Transformer通常使用查询…

Linux的进程信号(下)

文章目录 1. 阻塞信号1.1 信号其他相关常见概念1.2 在内核中的表示 2. sigset_t3. 信号集操作函数3.1 sigprocmask3.2 sigpending3.3. 实例演示 4. 信号的处理4.1. sigaction4.2 多个信号的处理 5. 可重入函数6. volatile7. SIGCHLD信号 1. 阻塞信号 1.1 信号其他相关常见概念…

java基于springboot自来水收费缴费系统+jsp

本次设计拟采用JAVA技术&#xff0c;对乡镇自来水收费系统的功能需求进行了全面分析&#xff0c;从模块功能定义、前后端交互技术、数据库及编程语言的选择、系统调试及测试、功能完善和改进等方面进行设计&#xff0c;解决了从用户新装、抄表、计费、收费、复查、换表、发票管…

Pyside6-第三篇-QToolButton一个奇葩的按钮

今天是Pyside6的第三篇内容。一起来看另一个按钮。 QToolButton。 from PySide6.QtWidgets import QToolButton, QWidget, QApplicationapp QApplication([])win QWidget() win.setWindowTitle("QToolButton按钮")btn QToolButton(win) btn.setText("触发&qu…

Cell揭秘--慢性压力如何导致肠道炎症

大脑产生的信号传导到肠道神经细胞&#xff0c;导致炎症化学物质的释放。 溃疡性结肠炎患者的肠道组织&#xff08;人工着色&#xff09;。图片来源: Steve Gschmeissner/Science Photo Library 心理压力会加重某些肠道疾病引起的肠道炎症。现在&#xff0c;科学家们找到了原因…

初学QT(Day05)

继续第四天的demo 总结了之前的经验教训&#xff0c;我重新开一个项目项目&#xff0c;先给出demo的结果吧&#xff0c;第一张是第一次写的demo&#xff0c;第二张图是成品的demo 结果还是比较满意的&#xff0c;虽然过程中有遇到的问题不是我自己独立解决的。。。相比于第…

GPT-4能否取代数据分析师?达摩院的初步实验为你解答~

深度学习自然语言处理 原创作者 | 刘嘉玲 最近&#xff0c;数据分析师圈子大家在讨论GPT-4对他们的工作有什么影响&#xff1a;是替代还是辅助&#xff1f;个人认为GPT-4可以帮助我提高工作效率和质量。 要成为一名高级的数据分析师&#xff0c;需要经过长期的学习和实践&#…

无代码玩转GIS应用,我也在行【文末送书】

您好&#xff0c;我是码农飞哥&#xff08;wei158556&#xff09;&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。&#x1f4aa;&#x1f3fb; 1. Python基础专栏&#xff0c;基础知识一网打尽&#xff0c;9.9元买不了吃亏&#xff0c;买不了上当。 Python从入门到精通…

jQuery-图片跟随

<!DOCTYPE html > <html> <head> <meta http-equiv"Content-Type" content"text/html; charsetUTF-8"> <title>图片跟随 </title> <style type"text/css"> body { text-align: center; …

【配电网重构】基于改进二进制粒子群算法的配电网重构研究(Matlab代码实现

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

使用FPGA进行 AI 火灾定位-FirAI

部署在 FPGA 上加速的 AI 火灾侦查。助力消防人员快速应对火灾事故~ 绪论 问题&#xff1a;近年来&#xff0c;不断增加的城市人口、更复杂的人口密集建筑以及与大流行病相关的问题增加了火灾侦查的难度。因此&#xff0c;为了增强消防人员对火灾事件的快速反应&#xff0c;安装…

delmia机器人建模与装配

1 可以用catia中的模型或其他三维建模软件中的模型转化为step格式即可 2 在demlia中打开 3 打开单个零件保存为cgr格式 对机械臂所有零件都做同样的转化 4 新建装配设计&#xff0c;并导入带有坐标的零件 将转化后的零件都选中导入即是装配好的 5 将模式修改为device buildin…

用户行为数据采集:常见埋点方案优劣势对比及选型建议

数据采集是大数据的基石&#xff0c;用户在使用App、微信小程序等各种线上应用产生的行为&#xff0c;只有通过埋点才能进行采集。没有埋点&#xff0c;数据分析决策、数据化运营都是无源之水&#xff0c;巧妇难为无米之炊。但很多时候&#xff0c;“埋点”两个字却成了C端产品…

STM32——关于NVIC讲解及标准库应用(基础篇)

简介&#xff1a; NVIC是指STM32中的中断控制器&#xff08;Nested Vectored Interrupt Controller&#xff09;。中断是STM32中的重要机制&#xff0c;通过中断可以实现异步事件处理。NVIC提供了灵活、高效、可扩展的中断处理机制&#xff0c;支持多级优先级、多向中断、嵌套向…

深入理解网络协议

hi 大家好&#xff0c;之前带小伙伴&#xff0c;一起复习了一遍网络协议&#xff0c;对网络协议的核心知识进行梳理&#xff0c;希望大家早日掌握这些核心知识&#xff0c;打造自己坚实的基础&#xff0c;为自己目标慢慢积累&#xff0c;厚积薄发。 详细点击查看-> 极客星球…

傅一平:一文讲透ERP的下一代架构!

”5月22日&#xff0c;华为宣布仅用15小时便完成了全球88家子公司MetaERP系统的切换。这也意味着华为MetaERP系统研发取得胜利&#xff0c;成功摆脱外国供应商断供停服威胁&#xff0c;实现该系统的全栈自主可控。“ 自己最近对ERP下一代架构有了兴趣&#xff0c;原因有四个&am…

阿里拆成1+6+N,中台还搞不搞了?

&#x1f4e3;&#x1f4e3;&#x1f4e3;&#x1f4e3;&#x1f4e3;&#x1f4e3;&#x1f4e3; &#x1f38d;大家好&#xff0c;我是慕枫 &#x1f38d;前阿里巴巴高级工程师&#xff0c;InfoQ签约作者、阿里云专家博主&#xff0c;一直致力于用大白话讲解技术知识 &#x…

推荐 6 个上周 火火火 的开源项目

本期推荐开源项目目录&#xff1a; 1. ChatGPT 网页应用&#xff08;AI&#xff09; 2. AI 换脸&#xff08;AI&#xff09; 3. API 调用 Midjourney 进行 AI 画图&#xff08;AI&#xff09; 4. 如何使用 Open AI 的 API&#xff1f;&#xff08;AI&#xff09; 5. 中华古诗词…

写在2023年乐夏前

&#xff08;1&#xff09;白衬衣的少年 勇敢的你 站在这里 脸庞清瘦却骄傲 &#xff08;2&#xff09;来啊&#xff0c;一起摇摆啊 这首歌发表在2016年。那时候的他们已经功成名就&#xff0c;彭磊却还能写出这样的词。 纸醉金迷不应该是&#xff1a;防晒霜、付税单、玫瑰金、…