我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Transformer模型入门详解及代码实现

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Transformer模型入门详解及代码实现

目录

前言

一.什么是Transformer

二.Encoder的组成

1.输入部分

2.注意力机制

2.1注意力机制的含义

2.2在TRM中的实现

3.前馈神经网络

3.1 前馈神经网络,BP算法,BP神经网络的区别:

3.2Encoder中的前馈神经网络

4 Encoder流程详解

三.Decoder的组成

3.1 带Mask的多头注意力机制

3.1.1 为什么需要Mask处理

3.1.2 如何进行Mask处理

3.2 Encoder与Decoder的交互层

四.Transformer的特点

4.1 并行处理

4.2Encoder与Decoder的联系

4.3 Decoder的输入与输出

五.基于pytorch实现Transformer模型

   5.1 Transformer的三类应用

   5.2 重要代码详解

5.2.1 样本格式

5.2.2 数字索引

 5.2.3 超参数设置

5.2.4 Transformer模型构建

5.2.5 Encoder实现

5.2.6 位置编码的实现 

5.2.7 多头自注意力机制

 5.2.8 前馈神经网络

5.2.9 Decoder


前言

        Transformer模型在各个领域的应用广泛,无论是脱胎于Encoder的BERT,还是从Decoder得到的GPT,都是该模型丰富适用性的体现。本文对Transformer模型的基本原理做了入门级的介绍,意在为读者描述整体思路,而并非拘泥于细微处的原理剖析,并附上了基于PYTORCH实现的Transformer模型代码及详细讲解。


一.什么是Transformer

        一种基于自注意力机制的模型,最初用来完成不规则的文本翻译任务,主体包含Encoder和Decoder部分,分别负责对提取原始句子的意义和将提取出的意义转换为对应的语言,目前应用领域极其广泛。

        图中左侧的6个Encoder在结构上相同,在具体参数上不同,因此是同时训练6个Enocder而不是只训练1个复制6份,右侧的Decoder也是如此。

二.Encoder的组成

单个Encoder共有三个组成部分。

1.输入部分

单个单词的输入表示由Embedding和位置编码两部分相加得到。

Embedding是词向量表示降低维度,而位置嵌入则是因为RNN循环网络中的输入具有先后的序列顺序,而Transformer是并行处理句子成分,损失了原始句子的序列顺序,所以引入位置编码表示序列顺序特征。

2.注意力机制

2.1注意力机制的含义

注意力机制就是找出利用输入向量和原始文本,找出原始文本中对输入向量影响最大的部分。

三个矩阵Q,K,V:Q是查询序列(输入向量),K是待查序列(原始文本),V是待查序列的自身含义编码序列。Q与K矩阵点积除以根号dk是因为防止点积过大导致梯度消失。

实例如下:

如果要探究婴儿更关注图片的哪个部分,应当把婴儿(Q矩阵)依次与各个方位(K矩阵)相乘求点积表示婴儿与各个方位之间的相似度,再把这些相似度利用Softmax完成归一化,得到图中的Value部分。将Value部分(数值)分别于矩阵V进行矩阵乘法后再求和就是注意力机制的输出值(一个矩阵)。详细过程如下:

Query矩阵与K矩阵利用F中的规则求得相似度表示S,再将各个S归一化得到标准相似度a,将a与V相乘求和得到最后的输出值。

2.2在TRM中的实现

2.1 由词向量生成Q,K,V矩阵

对于输入向量X1,将其与三个W矩阵分别相乘后得到Q1,K1,V1矩阵。这三个W矩阵均是首先随机初始化,再由神经网络训练得到,神将网络训练的目标就是得到合适的W矩阵。

2.2 矩阵替代向量作为输入

在实际使用中,使用矩阵而不是单个向量作为输入(并行加快速度)

2.3 多头注意力机制(Multi-Head Attention)

即单个Encoder中拥有多套W矩阵,拥有多个输出,要把这些输出合在一起乘以一个矩阵就得到了最后的输出。实验证明多头的效果优秀。

2.3 残差层 (图中的Add)

通过残差处理,避免了梯度消失

2.4 BN与LN (图中的Norm)

该部分的作用是消除量纲的影响,加快模型训练时的收敛速度。

①BN

Batch Normalization是2015年一篇论文中提出的数据归一化方法,往往用在深度神经网络中激活层之前。其作用是消除量纲的影响,加快模型训练时的收敛速度,使得模型训练过程更加稳定,避免梯度爆炸或者梯度消失。并且起到一定的正则化作用,几乎代替了Dropout。但是在NLP领域中BN的适用性很差,因此采用LN。

BN的核心是对同一个Btach中所有样本的相同维度(某些情况下可表述为同一个特征)进行运算,因此在NLP领域中:

因为输入样本(自然语言语句) 的长度不一定相等,导致BN在很多时候的均值,方差等运算都是只代表了部分甚至单个样本的数据,不符合BN的意义,此时采用BN方法的效果很差。

② LN

LN的核心是单独对一个样本的所有单词(维度)进行处理。

3.前馈神经网络

3.1 前馈神经网络,BP算法,BP神经网络的区别:

前馈网络和BP网络的区别

1.1 前馈神经网络

一种单向多层的网络结构,信息从输入层开始,逐层向一个方向传递,一直到输出层结束。前馈是指输出入方向是前向,此过程不调整权值。神经元之间不存在跨层连接、同层连接,输入层用于数据的输入,隐含层与输出层神经元对数据进行加工。

1.2.反向传播算法

(英语:Backpropagation,缩写为BP)是“误差反向传播”的简称,是一种与最优化方法(如梯度下降法)结合使用的,用来训练人工神经网络的常见方法。该方法对网络中所有权重计算损失函数的梯度。这个梯度会反馈给最优化方法,用来更新权值以最小化损失函数。

1.3.BP神经网络:

也是前馈神经网络,只是它的参数权重值是由反向传播学习算法调整的。

1.4.总结:

前馈描述的是网络的结构,指的是网络的信息流是单向的,不会构成环路。它是和“递归网络”(RNN)相对的概念;

BP算法是一类训练方法,可以应用于FFNN,也可以应用于RNN,而且BP也并不是唯一的训练方法,其他可用的还有比如遗传算法(GA)等。所以BP神经网络属于前馈网络,前馈网络不一定是BP网络(还可以用别的算法训练权值参数)

3.2Encoder中的前馈神经网络

   Encoder中前馈神经网络以下图中的”Feed Forward“存在:

4 Encoder流程详解

下图比图2-1更详细的说明了Encoder部分的内容:

①首先输入词向量X1(图中绿色部分),对输入部分进行处理,为X1增加位置编码,保留词序特征。

②其次处理后的X1(浅绿色)经过注意力层后,得到Z1(浅粉红色)。

③将Z1依次进行Add(浅绿色X1与Z1做残差处理)和Norm(LN去量纲处理)后,得到Z1(深粉红色)。

④将Z1(深粉红色)放入前馈神经网络层(Feed Forward),将输出结果依次进行Add(与Z1做残差处理)和Norm后,得到Encoder的输出结果。

三.Decoder的组成

如下图,Decoder也是由多个部分组合而成:主要包括1(带Mask的多注意力机制)和2(Encoder与Decoder的交互层)。

3.1 带Mask的多头注意力机制

3.1.1 为什么需要Mask处理

因为Decoder部分的输出是Q矩阵,也就是单词的词向量构成的矩阵,这些单词都是预测出的结果。自注意力机制如下图所示,单词的预测将会考虑整个句子中的单词。

然而在实际的预测阶段中,模型并不能预先得到完整的句子,如果不进行Mask处理,则模型的预测和训练阶段的规则不同统一,模型的效果差。

3.1.2 如何进行Mask处理

如下图所示,对当前单词和之后的单词做遮盖处理(忽视)即可。

3.2 Encoder与Decoder的交互层

每个Decoder输出的Q矩阵都是经过与所有Encoder交互后得到的:

交互行为如下:

Decoder从每个Encoder中取出K,V矩阵,之后输出Q矩阵,作为预测的结果。

四.Transformer的特点

4.1 并行处理

decoder预测时一次一个字,训练的时候可以一次性计算从头到当前字符的向量。并行是相对而言的,对比rnn一次只能处理一个字符,transformer中的注意力机制一次可以处理一整句话,不过decoder的时候还是一次处理一个字符

4.2Encoder与Decoder的联系

注意encoder的输出并没直接作为decoder的直接输入。

encoder的输出包括隐向量以及K /Q /V。decoders层中相较encoders层多了一个encoder-decoder attention模块,其计算跟多头自注意力计算也类似,只是它的Q是前一个decoder层的输出乘上新的参数矩阵进行转换得来的,K, V 则来自于与encoder的输出乘上新的参数矩阵进行转换得来的。仔细想想其实可以发现,这里的交互模块就跟seq2seq with attention中的机制一样,目的就在于让Decoder端的单词(token)给予Encoder端对应的单词(token)“更多的关注(attention weight)”

4.3 Decoder的输入与输出

五.基于pytorch实现Transformer模型

  5.1 Transformer的三类应用

 5.2 重要代码详解

5.2.1 样本格式

        代码中默认batch-size为1,因此此时下图中的三句话都只是一个样本。其中P(padding)是编码端的输入,S(strat)是解码端的输入,E(end)是该样本的正确答案,并不是输入。

if __name__ == '__main__':    #1.数据集导入    ## 句子的输入部分,    sentences = ['ich mochte ein bier P', 'S i want a beer', 'i want a beer E']    #上方的列表是一个样本而不是三个样本,其中P(padding),S(start),E(end)分别代表编码端输入,解码端输入,解码端输入的真实标签(即正确答案)    # E用来计算与解码端输出结果(训练预测答案)的损失,从而判断模型效果    #P用来对输入矩阵进行填充,当batch-size大于1时,各样本的句子长度不一定相等,因此通过padding对矩阵的大小进行规范处理

5.2.2 数字索引

        本项目只是进行模型基本功能演示,因此直接对样例句子进行词表构建,之后将对应词语转换为数字,再由数字索引确定BERT训练出的词向量,实现原始词语到词向量的转换。

## 构建词表,将字符与数字对应,更加方便计算机识别    #编码端词表:    class="lazy" data-src_vocab = {'P': 0, 'ich': 1, 'mochte': 2, 'ein': 3, 'bier': 4}    class="lazy" data-src_vocab_size = len(class="lazy" data-src_vocab)        #解码端词表:    tgt_vocab = {'P': 0, 'i': 1, 'want': 2, 'a': 3, 'beer': 4, 'S': 5, 'E': 6}    tgt_vocab_size = len(tgt_vocab)        #规定编码端和解码端的输入句子长度,也就是输入矩阵的“列”    class="lazy" data-src_len = 5 # length of source    tgt_len = 5 # length of target

 5.2.3 超参数设置

        对词向量维度,前馈神经网络,注意力机制中的Q,K,V矩阵的大小,Encoder和Decoder的堆叠层数,多头注意力机制的头数进行了设置。

## 模型参数设置    d_model = 512  # Embedding Size,字符转换为词向量的维度    d_ff = 2048  # FeedForward dimension,前馈神经网络中线性层linear映射到的维度    d_k = d_v = 64  # dimension of K(=Q), V    n_layers = 6  # number of Encoder of Decoder Layer,6个Encoder和Decoder    n_heads = 8  # number of heads in Multi-Head Attention,考虑多头注意力机制时,有8个头

5.2.4 Transformer模型构建

        整体由三部分组成,构建好整体框架后再进行具体的功能实现。

## 1. 从整体网路结构来看,分为三个部分:编码层,解码层,输出层class Transformer(nn.Module):    def __init__(self):        super(Transformer, self).__init__()        self.encoder = Encoder()  ## 编码层        self.decoder = Decoder()  ## 解码层        self.projection = nn.Linear(d_model, tgt_vocab_size, bias=False) ## 输出层 ,d_model 是我们解码层每个token(预测结果)输出的维度大小,        #之后会做一个 tgt_vocab_size 大小的softmax        #把预测结果映射进解码端词表里,表现为各个词的概率大小            ## forward接收输入     def forward(self, enc_inputs, dec_inputs):        ## 这里有两个数据进行输入,一个是enc_inputs 形状为[batch_size, class="lazy" data-src_len],主要是作为编码段的输入,一个dec_inputs,形状为[batch_size, tgt_len],主要是作为解码端的输入                #编码端的输出:        ## enc_inputs作为输入 形状为[batch_size, class="lazy" data-src_len]。        ## 输出格式由自己的函数内部指定,想要什么指定输出什么,可以是全部tokens的输出,可以是特定每一层的输出;也可以是中间某些参数的输出;        ## enc_outputs就是主要的输出,enc_self_attns这里没记错的是QK转置相乘之后softmax之后的矩阵值,代表的是每个单词和其他单词相关性;        enc_outputs, enc_self_attns = self.encoder(enc_inputs)        # 解码端的输出:        ## dec_outputs 是decoder主要输出,用于后续的linear映射; dec_self_attns类比于enc_self_attns 是查看每个单词对decoder中输入的其余单词的相关性;        ## dec_enc_attns是decoder中每个单词对encoder中每个单词的相关性;        dec_outputs, dec_self_attns, dec_enc_attns = self.decoder(dec_inputs, enc_inputs, enc_outputs)        ## 解码端输出结果到词表的映射        ## dec_outputs做映射到词表大小        dec_logits = self.projection(dec_outputs) # dec_logits : [batch_size x class="lazy" data-src_vocab_size x tgt_vocab_size]        return dec_logits.view(-1, dec_logits.size(-1)), enc_self_attns, dec_self_attns, dec_enc_attns

5.2.5 Encoder实现

        按照上文流程图所示,Encoder部分主要可以分为三部分,一是原始输入转换为词向量,二是词向量增加位置编码,三是多头自注意力机制和前馈神经网络部分。

class Encoder(nn.Module):    def __init__(self):        super(Encoder, self).__init__()        self.class="lazy" data-src_emb = nn.Embedding(class="lazy" data-src_vocab_size, d_model)  ## 词向量层,这个其实就是去定义生成一个矩阵,大小是 class="lazy" data-src_vocab_size * d_model        self.pos_emb = PositionalEncoding(d_model) ## 位置编码层,这部分自己实现,表示位置编码情况,这里是固定的正余弦函数,        # 也可以使用类似词向量的nn.Embedding获得一个可以更新学习的位置编码        self.layers = nn.ModuleList([EncoderLayer() for _ in range(n_layers)]) ## 注意力机制和前馈神经网络,        # 使用ModuleList对多个encoder进行堆叠,因为后续的encoder并没有使用词向量和位置编码,所以抽离出来;    ## forward接收编码端输入    def forward(self, enc_inputs):        ## 这里我们的 enc_inputs(1个输入) 形状是: [batch_size x source_len]        ## 下面这个代码通过class="lazy" data-src_emb,根据字符对应的数字进行索引定位,将数字对应的词向量提取出来,构成一个矩阵,enc_outputs输出形状是[batch_size, class="lazy" data-src_len, d_model]        ## 总体思路是把输入词语由字符转换为数字,再由数字索引找到对应的词向量        enc_outputs = self.class="lazy" data-src_emb(enc_inputs)        ## 这里就是位置编码,把两者相加放入到了这个函数里面,从这里可以去看一下位置编码函数的实现;3.        enc_outputs = self.pos_emb(enc_outputs.transpose(0, 1)).transpose(0, 1)        ## get_attn_pad_mask是为了得到句子中pad的位置信息,并得到一个相同大小的01矩阵,0表示词语,1表示填充的pad;        ## 之所以要这样,是为了在计算自注意力和交互注意力的时候去掉pad符号的影响,否则会把pad视作句子中的词语研究;去看一下这个函数 4.        ## 此处是编码端的自注意力机制,因此两个输入都是enc_inputs,在编码端和解码端之间的交互注意力机制中,两个输入不相同        enc_self_attn_mask = get_attn_pad_mask(enc_inputs, enc_inputs)        enc_self_attns = []                ## 多个Encoder和Decoder,因此采用for循环进行        for layer in self.layers:            ## 去看EncoderLayer 层函数 5.            ## 这里的输入分别是上一轮的输出结果,标识句子中pad位置的符号矩阵            enc_outputs, enc_self_attn = layer(enc_outputs, enc_self_attn_mask)            enc_self_attns.append(enc_self_attn)        return enc_outputs, enc_self_attns

5.2.6 位置编码的实现 

        一般的Transformer模型中,关于位置编码的实现步骤基本相同,首先是拆解原始公式并进行数学公式的化简,之后按照公式进行处理即可。

## 3. PositionalEncoding 代码实现,这部分的实现过程基本固定class PositionalEncoding(nn.Module):    ## max_len是句子的最大长度,结合padding对输入矩阵进行规范    def __init__(self, d_model, dropout=0.1, max_len=5000):        super(PositionalEncoding, self).__init__()        ## 位置编码的实现其实很简单,直接对照着公式去敲代码就可以,下面这个代码只是其中一种实现方式;        ## 从理解来讲,需要注意的就是偶数和奇数在公式上有一个共同部分,我们使用log函数把次方拿下来,方便计算;        ## pos代表的是单词在句子中的索引,这点需要注意;比如max_len是128个,那么索引就是从0,1,2,...,127        ##假设我的d_model是512,2i那个符号中i从0取到了255,那么2i对应取值就是0,2,4...510        self.dropout = nn.Dropout(p=dropout)        pe = torch.zeros(max_len, d_model)        position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)        div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))## 表示公式中共有的部分,此处是将原公式进行了变化,利用了对数的运算,无误        pe[:, 0::2] = torch.sin(position * div_term)## 公式中PE的求取;这里需要注意的是pe[:, 0::2]这个用法,就是从0开始到最后面,补长为2,其实代表的就是偶数位置        pe[:, 1::2] = torch.cos(position * div_term)## 公式中PE的求取;这里需要注意的是pe[:, 1::2]这个用法,就是从1开始到最后面,补长为2,其实代表的就是奇数位置        ## 上面代码获取之后得到的pe:[max_len*d_model]        ## 下面这个代码之后,我们得到的pe形状是:[max_len*1*d_model]        pe = pe.unsqueeze(0).transpose(0, 1)## transpose实现矩阵的转置,unsqueeze(0)是增加矩阵的维度,此处变为三维矩阵        '''transpose(X,Y)函数和矩阵的转置是一个意思,相当于行为X轴,列为Y轴,X轴和Y轴调换了位置;          X轴用0表示,Y轴用1表示;          例如:如果transport(1,0)表示行与列调换了位置;            此处交换了x与y轴,否则pe的形状会是[1*max_len*d_model]'''                '''一、先看torch.squeeze() 这个函数主要对数据的维度进行压缩,去掉维数为1的的维度,比如是一行或者一列这种,一个一行三列(1,3)的数去掉第一个维数为一的维度之后就变成(3)行。            1.squeeze(a)就是将a中所有为1的维度删掉。不为1的维度没有影响。            2.a.squeeze(N) 就是去掉a中指定的维数为一的维度。              还有一种形式就是b=torch.squeeze(a,N) a中去掉指定的维数N为一的维度。            二、再看torch.unsqueeze()这个函数主要是对数据维度进行扩充。            给指定位置加上维数为一的维度,比如原本有个三行的数据(3),在0的位置加了一维就变成一行三列(1,3)。a.unsqueeze(N) 就是在a中指定位置N加上一个维数为1的维度。            还有一种形式就是b=torch.unsqueeze(a,N) a就是在a中指定位置N加上一个维数为1的维度'''        self.register_buffer('pe', pe)  ## 定一个缓冲区,其实简单理解为这个参数不更新就可以

5.2.7 多头自注意力机制

        需注意,在该部分之前应对pad部分进行标识,构建01符号矩阵,用1标识出pad在K矩阵中的位置,否则多头注意力机制将会视pad为原始语句中的成分,并考虑其的影响。

## 6. MultiHeadAttentionclass MultiHeadAttention(nn.Module):    def __init__(self):        super(MultiHeadAttention, self).__init__()        ## 输入进来的QKV是相等的,我们会使用映射linear做一个映射得到参数矩阵Wq, Wk,Wv        self.W_Q = nn.Linear(d_model, d_k * n_heads)        self.W_K = nn.Linear(d_model, d_k * n_heads)        self.W_V = nn.Linear(d_model, d_v * n_heads)        self.linear = nn.Linear(n_heads * d_v, d_model)        self.layer_norm = nn.LayerNorm(d_model)    def forward(self, Q, K, V, attn_mask):        ## 这个多头分为这几个步骤,首先映射分头,然后计算atten_scores,然后计算atten_value;        ##输入进来的数据形状: Q: [batch_size x len_q x d_model], K: [batch_size x len_k x d_model], V: [batch_size x len_k x d_model]        residual, batch_size = Q, Q.size(0)        # (B, S, D) -proj-> (B, S, D) -split-> (B, S, H, W) -trans-> (B, H, S, W)        ##下面这个就是先映射,后分头;一定要注意的是q和k分头之后维度是一致额,所以一看这里都是dk        q_s = self.W_Q(Q).view(batch_size, -1, n_heads, d_k).transpose(1,2)  # q_s: [batch_size x n_heads x len_q x d_k]        k_s = self.W_K(K).view(batch_size, -1, n_heads, d_k).transpose(1,2)  # k_s: [batch_size x n_heads x len_k x d_k]        v_s = self.W_V(V).view(batch_size, -1, n_heads, d_v).transpose(1,2)  # v_s: [batch_size x n_heads x len_k x d_v]        ## 输入进行的attn_mask形状是 batch_size x len_q x len_k,然后经过下面这个代码得到 新的attn_mask : [batch_size x n_heads x len_q x len_k],就是把pad信息重复了n个头上        attn_mask = attn_mask.unsqueeze(1).repeat(1, n_heads, 1, 1)        ##然后我们计算 ScaledDotProductAttention 这个函数,去7.看一下        ## 得到的结果有两个:context: [batch_size x n_heads x len_q x d_v], attn: [batch_size x n_heads x len_q x len_k]        context, attn = ScaledDotProductAttention()(q_s, k_s, v_s, attn_mask)        context = context.transpose(1, 2).contiguous().view(batch_size, -1, n_heads * d_v) # context: [batch_size x len_q x n_heads * d_v]        output = self.linear(context)        return self.layer_norm(output + residual), attn # output: [batch_size x len_q x d_model]

 5.2.8 前馈神经网络

        该部分实现线性变换即可。

## 8. PoswiseFeedForwardNetclass PoswiseFeedForwardNet(nn.Module):    def __init__(self):        super(PoswiseFeedForwardNet, self).__init__()        self.conv1 = nn.Conv1d(in_channels=d_model, out_channels=d_ff, kernel_size=1)        self.conv2 = nn.Conv1d(in_channels=d_ff, out_channels=d_model, kernel_size=1)        self.layer_norm = nn.LayerNorm(d_model)    def forward(self, inputs):        residual = inputs # inputs : [batch_size, len_q, d_model]        output = nn.ReLU()(self.conv1(inputs.transpose(1, 2)))        output = self.conv2(output).transpose(1, 2)        return self.layer_norm(output + residual)

5.2.9 Decoder

        Decoder部分的具体思路和Encoder部分大致相同,按流程图理解即可,值得注意的是:在Decoder部分,进行了两次mask处理,一是对目标词之后的词语进行掩盖处理以达到更好的预测效果,二是对pad进行掩盖处理;Decoder部分的交互注意力机制的Q矩阵来自自身,而K矩阵来自Encoder部分。 

来源地址:https://blog.csdn.net/cuguanren/article/details/126540189

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Transformer模型入门详解及代码实现

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

详解Swin Transformer核心实现,经典模型也能快速调优

Swin Transformer是一种基于Transformer结构的图像分类模型,其核心实现主要有以下几个方面:1. 分块式图片处理:Swin Transformer将输入图片分为多个非重叠的小块,每个小块称为一个局部窗格。然后通过局部窗
2023-09-20

Android TelephonyManager详解及实现代码

JAVA的反射机制,探秘TelephonyManager在Framework里包含却在SDK隐藏的几项功能。先来看看本文程序运行的效果图: 本文程序演示了以下功能: 1.所有来电自动接听; 2.所有来电自动挂断
2022-06-06

java 实现汉诺塔详解及实现代码

java 实现汉诺塔详解及实现代码汉诺塔问题:有三根柱子A,B,C,其中A上面有n个圆盘,从上至下圆盘逐渐增大,每次只能移动一个圆盘,并且规定大的圆盘不能叠放在小的圆盘上面,现在想要把A上面的n个圆盘全部都移动到C上面,输出移动的总步数以及
2023-05-31

java 汉诺塔详解及实现代码

java 汉诺塔详解及实现代码实现效果图打印的方法在 moveTheTopOne() 方法中被调用,调用该方法前打印出移动的方向--从X号塔往Y号塔汉诺塔要求:将第一座塔上的所有盘子,借助第二座塔,全部搬运到第三座塔上。规则:一次只能搬运一
2023-05-31

20行代码简单实现koa洋葱圈模型示例详解

这篇文章主要为大家介绍了20行代码简单实现koa洋葱圈模型示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-01-17

Python 实现随机数详解及实例代码

Python3实现随机数random是用于生成随机数的,我们可以利用它随机生成数字或者选择字符串。random.seed(x)改变随机数生成器的种子seed。一般不必特别去设定seed,Python会自动选择seed。random.rand
2022-06-04

Android Style.xml的应用详解及代码实现

Style.xml的妙用Style.xml之于Android犹如css之于Jsp 妙用 2022-06-06

Android 混合动画详解及实现代码

Android 混合动画 在Android开发,我们会经常使用到动画,但是简单的一种动画(如旋转、缩放、渐变、位移等)有时候并不能满足我们项目的要求,这时候就需要运用到混合动画,那么在安卓中是如何实现一个炫酷的混合动画,下面是一个混合动画实
2022-06-06

Android Spinner与适配器模式详解及实例代码

最近做项目对Android Spinner 使用,这里简单写个小例子,来测试如何使用。 Spinner是一个下拉列表,往安卓界面中拖拽一个Spinner控件,在属性中设置Android:entries=“@array/spinner_dat
2022-06-06

Android UI 实现老虎机详解及实例代码

Android UI 实现老虎机详解listview 的使用步骤简单的listview老虎机实现 1.实现效果图2.需要掌握的知识listview的使用步骤listview的Adapter接口的实现listview中的MVC 3.知识详解
2022-06-06

详解B+树的原理及实现Python代码

B+树是自平衡树的高级形式,其中所有值都存在于叶级中。B+树所有叶子都处于同一水平,每个节点的子节点数量≥2。B+树与B树的区别是各节点在B树上不是相互连接,而在B+树上是相互连接的。B+树多级索引结构图B+树搜索规则1、从根节点开始
详解B+树的原理及实现Python代码
2024-01-24

使用Python实现桥接模式的代码详解

Python桥接模式代码详解摘要桥接模式将抽象和实现分离,允许独立修改。在Python中,抽象类定义接口,由具体类实现。接口定义了方法,具体类必须实现。创建抽象类和具体类的实例,并动态改变实现。桥接模式提供了解耦、可扩展性、可读性和可维护性优势。它适用于需要分离抽象和实现,或需要动态更改对象实现的场景。
使用Python实现桥接模式的代码详解
2024-04-02

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录