大模型面试准备(十八):使用 Pytorch 从零实现 Transformer 模型

节前,我们组织了一场算法岗技术&面试讨论会,邀请了一些互联网大厂朋友、参加社招和校招面试的同学,针对大模型技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何备战、面试常考点分享等热门话题进行了深入的讨论。


合集在这里:《大模型面试宝典》(2024版) 正式发布!


2017年Google在论文《Attention is All You Need》中提出了Transformer模型,并成功应用到NLP领域。该模型完全基于自注意力机制Attention mechanism实现,弥补了传统的RNN模型的不足。

本文笔者将详解使用Pytorch从零开始逐步实现Transformer模型。

回顾

首先我们先回顾一下Transformer原理。宏观层面,Transformer可以看成是一个黑箱操作的序列到序列(seq2seq)模型。例如,在机器翻译中,输入一种语言,经Transformer输出翻译后的另一种语言。

图片

拆开这个黑箱,可以看到模型本质就是一个Encoders-Decoders结构。

  • 每个Encoders中分别由6层Encoder组成。(所有Encoder结构完全相同,但是训练参数不同,每个参数是独立训练的,循环执行6次Encode,而不是只训练了一个Encoder然后复制5份)。

  • Decoders同理。

  • 这里每个Encoders包含6层Encoder,只是论文中Nx=6,实际可以自定义。

图片

Transformer整体架构如下图所示。

图片

其中

  • 编码端:经过词向量层(Input Embedding)和位置编码层(Positional Encoding),得到最终输入,流经自注意力层(Multi-Head Attention)、残差和层归一化(Add&Norm)、前馈神经网络层(Feed Forward)、残差和层归一化(Add&Norm),得到编码端的输出(后续会和解码端进行交互)。

  • 解码端:经过词向量层(Output Embedding)和位置编码层(Positional Encoding),得到最终输入,流经掩码自注意力层(Masked Multi-Head Attention,把当前词之后的词全部mask掉)、残差和层归一化(Add&Norm)、交互注意力层(Multi-Head Attention,把编码端的输出和解码端的信息进行交互,Q矩阵来自解码端,K、V矩阵来自编码端的输出)、残差和层归一化(Add&Norm)、前馈神经网络层(Feed Forward)、残差和层归一化(Add&Norm),得到解码端的输出。

注:编码端和解码端的输入不一定等长。

Encoder

图片

下面还是以机器翻译(“我是学生”->“I am a student”)为例说明。

对于上图中,整个模型的输入即为"我是学生",目标是将其翻译为"I am a student",但是计算机是无法识别"我是学生"的,需要将其转化为二进制形式,再送入模型。

将中文转换为计算机可以识别的向量通常有两种方法:

  • One Hot编码:形成高维向量,对于中文来说,汉字的数量就是向量的维度,然后是哪个字就将对应位置变为1,其它位置变为0,以此来表示一句话。

  • Embedding词嵌入:通过网络进行训练或者通过一些训练好的模型将其转化成连续性的向量。

一般来说第二种方法使用较多,因为第一种有几个缺点,第一个就是每个字都是相互独立的,缺少语义联系信息,第二就是汉字数量太多,会导致生成的维度过大,占用系统内存。

输入Input

输入Inputs维度是[batch size,sequence length],经Word2Vec,转换为计算机可以识别的Input Embedding,论文中每个词对应一个512维度的向量,维度是[batch_size,sequence_length,embedding_dimmension]。batch size指的是句子数,sequence length指的是输入的句子中最长的句子的字数,embedding_dimmension是词向量长度。

图片

如上图所示,以机器翻译(“我是学生”->“I am a student”)为例,首先对输入的文字进行Word Embedding处理,每个字(词)用一个连续型向量表示(这里定义的是4维向量),称为词向量。这样一个句子,也就是嵌入后的输入向量input Embedding就可以用一个矩阵表示(4*4维,序列长度为4,每个字用4维向量表示)。input Embedding加上位置信息得到编码器的输入矩阵。

「为什么需要在input Embedding加上位置信息?」 与RNN相比,RNN是一个字一个字输入,自然可以保留每个字的顺序关系信息,而Transformer使用的是自注意力机制来提取信息,一个句子中的每个字/词是并行计算,虽然处理每个字的时候考虑到了所有字对其的影响,但是并没有考虑到各个字相互之间的位置信息,也就是上下文。所以需要引入位置信息。

Transformer中使用Positional Encoding表示每个字/词的位置信息。定义如下:

在这里插入图片描述
这样就即可实现让自注意力机制考虑词的顺序,同时又可以输入所有的词。

代码实现

「Word Embedding」

Word Embedding在Pytorch中通常用nn.Embedding实现。

class Embeddings(nn.Module):"""类的初始化:param d_model: 词向量维度,512:param vocab: 当前语言的词表大小"""def __init__(self, d_model, vocab):super(Embeddings, self).__init__()# 调用nn.Embedding预定义层,获得实例化词嵌入对象self.lutself.lut = nn.Embedding(vocab, d_model)self.d_model = d_model  #表示词向量维度def forward(self, x):"""Embedding层的前向传播参数x:输入给模型的单词文本通过此表映射后的one-hot向量x传给self.lut,得到形状为(batch_size, sequence_length, d_model)的张量,与self.d_model相乘,以保持不同维度间的方差一致性,及在训练过程中稳定梯度"""return self.lut(x) * math.sqrt(self.d_model)

「Positional Encoding」

class PositionalEncoding(nn.Module):"""实现Positional Encoding功能"""def __init__(self, d_model, dropout=0.1, max_len=5000):"""位置编码器的初始化函数:param d_model: 词向量的维度,与输入序列的特征维度相同,512:param dropout: 置零比率:param max_len: 句子最大长度,5000"""super(PositionalEncoding, self).__init__()# 初始化一个nn.Dropout层,设置给定的dropout比例self.dropout = nn.Dropout(p=dropout)# 初始化一个位置编码矩阵# (5000,512)矩阵,保持每个位置的位置编码,一共5000个位置,每个位置用一个512维度向量来表示其位置编码pe = torch.zeros(max_len, d_model)# 偶数和奇数在公式上有一个共同部分,使用log函数把次方拿下来,方便计算# position表示的是字词在句子中的索引,如max_len是128,那么索引就是从0,1,2,...,127# 论文中d_model是512,2i符号中i从0取到255,那么2i对应取值就是0,2,4...510# (5000) -> (5000,1)position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)# 计算用于控制正余弦的系数,确保不同频率成分在d_model维空间内均匀分布div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))# 根据位置和div_term计算正弦和余弦值,分别赋值给pe的偶数列和奇数列pe[:, 0::2] = torch.sin(position * div_term)   # 从0开始到最后面,补长为2,其实代表的就是偶数位置pe[:, 1::2] = torch.cos(position * div_term)   # 从1开始到最后面,补长为2,其实代表的就是奇数位置# 上面代码获取之后得到的pe:[max_len * d_model]# 下面这个代码之后得到的pe形状是:[1 * max_len * d_model]# 多增加1维,是为了适应batch_size# (5000, 512) -> (1, 5000, 512)pe = pe.unsqueeze(0)# 将计算好的位置编码矩阵注册为模块缓冲区(buffer),这意味着它将成为模块的一部分并随模型保存与加载,但不会被视为模型参数参与反向传播self.register_buffer('pe', pe)def forward(self, x):"""x: [seq_len, batch_size, d_model]  经过词向量的输入"""x = x + self.pe[:, :x.size(1)].clone().detach()   # 经过词向量的输入与位置编码相加# Dropout层会按照设定的比例随机“丢弃”(置零)一部分位置编码与词向量相加后的元素,# 以此引入正则化效果,防止模型过拟合return self.dropout(x)

自注意力机制(Self Attention Mechanism)

注意力机制,顾名思义,就是我们对某件事或某个人或物的关注重点。举个生活中的例子,当我们阅读一篇文章时,并非每个词都会被同等重视,我们会更关注那些关键的、与上下文紧密相关的词语,而非每个停顿或者辅助词。

对于机器来说其实就是赋予多少权重(比如0-1之间的小数),越重要的地方或者越相关的地方赋予的权重越高。

注意力机制的实现思想是先计算第1个字与句中每个字的注意力分数(包括第1个字),再用求得的注意力分数与对应字的信息相乘,并相加,得到的结果就是第1个字与句子中所有字的加权和,第2个字、第3个字…以此类推。

图片

在这里插入图片描述

图片

在这里插入图片描述

图片

在这里插入图片描述

图片

最后,用注意力分数矩阵乘以矩阵得到输出矩阵,其中,

即为注意力分数矩阵与矩阵的点积,也是加权和。以上就是注意力机制计算的完整过程。

代码实现

class ScaledDotProductAttention(nn.Module):""" Scaled Dot-Product Attention """def __init__(self, scale_factor, dropout=0.0):super().__init__()self.scale_factor = scale_factor#dropout用于防止过拟合,在前向传播的过程中,让某个神经元的激活值以一定的概率停止工作self.dropout = nn.Dropout(dropout)def forward(self, q, k, v, mask=None):# batch_size: 批量大小# len_q,len_k,len_v: 序列长度 在这里他们都相等# n_head: 多头注意力,论文中默认为8# d_k,d_v: k v 的dim(维度) 默认都是64# 此时q的shape为(batch_size, n_head, len_q, d_k) (batch_size, 8, len_q, 64)# 此时k的shape为(batch_size, n_head, len_k, d_k) (batch_size, 8, len_k, 64)# 此时v的shape为(batch_size, n_head, len_k, d_v) (batch_size, 8, len_k, 64)# q先除以self.scale_factor,再乘以k的转置(交换最后两个维度(这样才可以进行矩阵相乘))。# attn的shape为(batch_size, n_head, len_q, len_k)attn = torch.matmul(q / self.scale_factor, k.transpose(2, 3))if mask is not None:"""用-1e9代替0 -1e9是一个很大的负数 经过softmax之后接近0# 其一:去除掉各种padding在训练过程中的影响# 其二,将输入进行遮盖,避免decoder看到后面要预测的东西。(只用在decoder中)"""scores = scores.masked_fill(mask == 0, -1e9)# 先在attn的最后一个维度做softmax 再dropout 得到注意力分数attn = self.dropout(torch.softmax(attn, dim=-1))# 最后attn与v矩阵相乘# output的shape为(batch_size, 8, len_q, 64)output = torch.matmul(attn, v)# 返回 output和注意力分数return output, attn

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

多头注意力机制即就是把上述的、、三个矩阵从特征维度(词向量长度)上拆分为形状相同的小矩阵,如下图所示,拆分为2个形状相同的小矩阵,即为二头注意力。本例中,句子长度为4,词向量维度是4,小矩阵维度即为[4,4/2=2]。接下来以上述方式计算2个b矩阵,再将每个Head Attention计算出来的b矩阵拼接,即为最终的注意力矩阵。

注:论文中句子长度为5,词向量维度是512,将、、三个矩阵拆分成了8个形状相同的小矩阵,也就是8头注意力,小矩阵维度为[5,512/8=64]。

图片

其中,输入与最后输出b的形状相同。

代码实现

class MultiHeadAttention(nn.Module):""" Multi-Head Attention module """def __init__(self, n_head, d_model, d_k, d_v, dropout=0.1):# 论文中这里的n_head, d_model, d_k, d_v分别默认为8, 512, 64, 64'''# q k v先经过不同的线性层,再用ScaledDotProductAttention,最后再经过一个线性层'''super().__init__()self.n_head = n_headself.d_k = d_kself.d_v = d_vself.w_qs = nn.Linear(d_model, n_head * d_k, bias=False)self.w_ks = nn.Linear(d_model, n_head * d_k, bias=False)self.w_vs = nn.Linear(d_model, n_head * d_v, bias=False)self.fc = nn.Linear(n_head * d_v, d_model, bias=False)self.attention = ScaledDotProductAttention(scale_factor=d_k ** 0.5)self.dropout = nn.Dropout(dropout)self.layer_norm = nn.LayerNorm(d_model, eps=1e-6)  # 默认对最后一个维度初始化def forward(self, q, k, v, mask=None):# q, k, v初次输入为含位置信息的嵌入矩阵X,由于要堆叠N次,后面的输入则是上个多头的输出# q, k, v:batch_size * seq_num * d_modeld_k, d_v, n_head = self.d_k, self.d_v, self.n_head# len_q, len_k, len_v 为输入的序列长度# batch_size为batch_sizebatch_size, len_q, len_k, len_v = q.size(0), q.size(1), k.size(1), v.size(1)# 用作残差连接residual = q# Pass through the pre-attention projection: b x lq x (n*dv)# Separate different heads: b x lq x n x dv# q k v 分别经过一个线性层再改变维度# 由(batch_size, len_q, n_head*d_k) => (batch_size, len_q, n_head, d_k)# (batch_size, len_q, 8*64) => (batch_size, len_q, 8, 64)q = self.layer_norm(q)k = self.layer_norm(k)v = self.layer_norm(v)# 与q,k,v相关矩阵相乘,得到相应的q,k,v向量,d_model=n_head * d_kq = self.w_qs(q).view(batch_size, len_q, n_head, d_k)k = self.w_ks(k).view(batch_size, len_k, n_head, d_k)v = self.w_vs(v).view(batch_size, len_v, n_head, d_v)# Transpose for attention dot product: b x n x lq x dv# 交换维度做attention# 由(batch_size, len_q, n_head, d_k) => (batch_size, n_head, len_q, d_k)# (batch_size, len_q, 8, 64) => (batch_size, 8, len_q, 64)q, k, v = q.transpose(1, 2), k.transpose(1, 2), v.transpose(1, 2)if mask is not None:# 为head增加一个维度mask = mask.unsqueeze(1)   # For head axis broadcasting.# 输出的q为Softmax(QK/d + (1-S)σ)V, attn 为QK/Dq, attn = self.attention(q, k, v, mask=mask)# Transpose to move the head dimension back: b x lq x n x dv# Combine the last two dimensions to concatenate all the heads together: b x lq x (n*dv)# (batch_size, 8, len_k, 64) => (batch_size, len_k, 8, 64) => (batch_size, len_k, 512)q = q.transpose(1, 2).contiguous().view(batch_size, len_q, -1)# 经过fc和dropoutq = self.dropout(self.fc(q))# 残差连接 论文中的Add & Norm中的Addq += residual# 论文中的Add & Norm中的Normq = self.layer_norm(q)# q的shape为(batch_size, len_q, 512)# attn的shape为(batch_size, n_head, len_q, len_k)return q, attn

Add & Layer normalization

Add采用残差神经网络思想,也就是Multi-Head Attention的输入矩阵直接与输出相加,这样可以让网络训练的更深,得到矩阵,再经过Layer normalization归一化处理,加快训练速度,使得的每一行也就是每个句子归一化为标准正态分布,输出为。公式如下:

  • 均值:μ,其中,s是的长度。

  • 方差:μ

  • 归一化:

图片

class LayerNorm(nn.Module):def __init__(self, d_model, eps=1e-12):super().__init__()# 初始化尺度参数gammaself.gamma = nn.Parameter(torch.ones(d_model))# 初始化偏差参数betaself.beta = nn.Parameter(torch.zeros(d_model))# 设置一个小常数,防止除0self.eps = epsdef forward(self, x):# 计算均值mean = x.mean(-1, keepdim=True)# 计算方差,unbiased=False时,方差的计算使用n而不是n-1做分母var = x.var(-1, unbiased=False, keepdim=True)# 归一化计算out = (x - mean) / torch.sqrt(var + self.eps)out = self.gamma * out + self.betareturn out

Feed Forward前馈神经网络

将Add & Layer normalization输出,经过两个全连接层(第一层的激活函数为 Relu,第二层不使用激活函数),再经过Add & Layer normalization得到最后输出矩阵O。

class PoswiseFeedForwardNet(nn.Module):def __init__(self):super(PoswiseFeedForwardNet, self).__init__()self.fc = nn.Sequential(nn.Linear(d_model, d_ff, bias=False),nn.ReLU(),nn.Linear(d_ff, d_model, bias=False))def forward(self, inputs):                             # inputs: [batch_size, seq_len, d_model]residual = inputsoutput = self.fc(inputs)return nn.LayerNorm(d_model).cuda()(output + residual)   # [batch_size, seq_len, d_model]

Mask掉停用词

Mask句子中没有实际意义的占位符,例如’我 是 学 生 P’ ,P对应句子没有实际意义,所以需要被Mask,Encoder_input 和Decoder_input占位符都需要被Mask。

# seq_q: [batch_size, seq_len] ,seq_k: [batch_size, seq_len]
def get_attn_pad_mask(seq_q, seq_k): batch_size, len_q = seq_q.size()batch_size, len_k = seq_k.size()# eq(zero) is PAD tokenpad_attn_mask = seq_k.data.eq(0).unsqueeze(1)  # batch_size x 1 x len_k(=len_q), one is masking# 扩展成多维度return pad_attn_mask.expand(batch_size, len_q, len_k)  # batch_size x len_q x len_k

EncoderLayer代码实现

class EncoderLayer(nn.Module):def __init__(self):super(EncoderLayer, self).__init__()self.enc_self_attn = MultiHeadAttention()                                     # 多头注意力机制self.pos_ffn = PoswiseFeedForwardNet()                                        # 前馈神经网络def forward(self, enc_inputs, enc_self_attn_mask):                                # enc_inputs: [batch_size, src_len, d_model]#输入3个enc_inputs分别与W_q、W_k、W_v相乘得到Q、K、V                          # enc_self_attn_mask: [batch_size, src_len, src_len]enc_outputs, attn = self.enc_self_attn(enc_inputs, enc_inputs, enc_inputs,    # enc_outputs: [batch_size, src_len, d_model], enc_self_attn_mask)                    # attn: [batch_size, n_heads, src_len, src_len]enc_outputs = self.pos_ffn(enc_outputs)                                       # enc_outputs: [batch_size, src_len, d_model]return enc_outputs, attn

Encoder代码实现

"""
编码器
"""
class Encoder(nn.Module):def __init__(self):super(Encoder, self).__init__()self.src_emb = nn.Embedding(src_vocab_size, d_model)                     # 把字转换字向量self.pos_emb = PositionalEncoding(d_model)                               # 加入位置信息self.layers = nn.ModuleList([EncoderLayer() for _ in range(n_layers)])def forward(self, enc_inputs):                                               # enc_inputs: [batch_size, src_len]# 1. 中文字索引进行Embedding,转换成512维度的字向量enc_outputs = self.src_emb(enc_inputs)                                   # enc_outputs: [batch_size, src_len, d_model]# 2. 在字向量上面加上位置信息enc_outputs = self.pos_emb(enc_outputs)                                  # enc_outputs: [batch_size, src_len, d_model]# 3. Mask掉句子中的占位符号enc_self_attn_mask = get_attn_pad_mask(enc_inputs, enc_inputs)           # enc_self_attn_mask: [batch_size, src_len, src_len]enc_self_attns = []# 4. 通过6层的encoder(上一层的输出作为下一层的输入)for layer in self.layers:enc_outputs, enc_self_attn = layer(enc_outputs, enc_self_attn_mask)  # enc_outputs :   [batch_size, src_len, d_model], # enc_self_attn : [batch_size, n_heads, src_len, src_len]enc_self_attns.append(enc_self_attn)return enc_outputs, enc_self_attns

Decoder

输入

Decoder的输入是最后一个Encoder block的输出。如下图所示,以中文翻译“我是学生”为例,首先将“我是学生”整个句子输入到Encoder中,得到最后一个Encoder block的输出后,将在Decoder中输入"S I am a student",s表示开始。注意这里,“S I am a student"不会一并输入,而是在T0时刻先输入"S”,预测出第一个词"I";再在T1时刻,输入"S"和"I"预测下一个单词"am";同理在T2时刻,输入"S"、“I"和"am”,预测出第三个单词"a",依次把整个句子输入到Decoder,预测出"I am a student E"。

图片

这里采用Mask上三角矩阵掩盖了Decoder的输入,T0、T1、T2、T3、T4即为每个时刻的输入。

def get_attn_subsequence_mask(seq):                               # seq: [batch_size, tgt_len]attn_shape = [seq.size(0), seq.size(1), seq.size(1)]subsequence_mask = np.triu(np.ones(attn_shape), k=1)          # 生成上三角矩阵,[batch_size, tgt_len, tgt_len]subsequence_mask = torch.from_numpy(subsequence_mask).byte()  #  [batch_size, tgt_len, tgt_len]return subsequence_mask

Masked Multi-Head Attention

Masked Multi-Head Attention与Multi-Head Attention类似,只是采用了Mask上三角矩阵,掩盖Decoder的输入。如上所述。

Decoder的Multi-Head Attention

Decoder的Multi-Head Attention同样和Encoder的Multi-Head Attention结构一样,只是Decoder的Multi-Head Attention中,K、V矩阵来自Encoder的输出,而Q矩阵来自Masked Multi-Head Attention 的输出。

Decoder的输出预测

Decoder输出矩阵形状是[句子长度,词向量维度],经过nn.Linear全连接层,再通过softmax函数得到每个词的概率,然后选择概率最大的词作为预测结果。

DecoderLayer代码实现

Decoder两次调用MultiHeadAttention时,第一次调用传入的 Q,K,V 的值是相同的,都等于dec_inputs,第二次调用 Q 矩阵是来自Decoder的输入。K,V 两个矩阵是来自Encoder的输出,等于enc_outputs。

class DecoderLayer(nn.Module):def __init__(self):super(DecoderLayer, self).__init__()self.dec_self_attn = MultiHeadAttention()self.dec_enc_attn = MultiHeadAttention()self.pos_ffn = PoswiseFeedForwardNet()def forward(self, dec_inputs, enc_outputs, dec_self_attn_mask, dec_enc_attn_mask): # dec_inputs: [batch_size, tgt_len, d_model]# enc_outputs: [batch_size, src_len, d_model]# dec_self_attn_mask: [batch_size, tgt_len, tgt_len]# dec_enc_attn_mask: [batch_size, tgt_len, src_len]dec_outputs, dec_self_attn = self.dec_self_attn(dec_inputs, dec_inputs, dec_inputs, dec_self_attn_mask)   # dec_outputs: [batch_size, tgt_len, d_model]# dec_self_attn: [batch_size, n_heads, tgt_len, tgt_len]dec_outputs, dec_enc_attn = self.dec_enc_attn(dec_outputs, enc_outputs, enc_outputs, dec_enc_attn_mask)    # dec_outputs: [batch_size, tgt_len, d_model]# dec_enc_attn: [batch_size, h_heads, tgt_len, src_len]dec_outputs = self.pos_ffn(dec_outputs)                                    # dec_outputs: [batch_size, tgt_len, d_model]return dec_outputs, dec_self_attn, dec_enc_attn

Decoder代码实现

class Decoder(nn.Module):def __init__(self):super(Decoder, self).__init__()self.tgt_emb = nn.Embedding(tgt_vocab_size, d_model)self.pos_emb = nn.Embedding.from_pretrained(get_sinusoid_encoding_table(tgt_len+1, d_model),freeze=True)self.layers = nn.ModuleList([DecoderLayer() for _ in range(n_layers)])def forward(self, dec_inputs, enc_inputs, enc_outputs): # dec_inputs : [batch_size x target_len]# 1. 英文字索引进行Embedding,转换成512维度的字向量,并在字向量上加上位置信息dec_outputs = self.tgt_emb(dec_inputs) + self.pos_emb(torch.LongTensor([[5,1,2,3,4]]))# 2. Mask掉句子中的占位符号dec_self_attn_pad_mask = get_attn_pad_mask(dec_inputs, dec_inputs)dec_self_attn_subsequent_mask = get_attn_subsequent_mask(dec_inputs)dec_self_attn_mask = torch.gt((dec_self_attn_pad_mask + dec_self_attn_subsequent_mask), 0)dec_enc_attn_mask = get_attn_pad_mask(dec_inputs, enc_inputs)dec_self_attns, dec_enc_attns = [], []# 3. 通过6层的decoder(上一层的输出作为下一层的输入)for layer in self.layers:dec_outputs, dec_self_attn, dec_enc_attn = layer(dec_outputs, enc_outputs, dec_self_attn_mask, dec_enc_attn_mask)dec_self_attns.append(dec_self_attn)dec_enc_attns.append(dec_enc_attn)return dec_outputs, dec_self_attns, dec_enc_attns

Transformer代码实现

Trasformer的整体结构,输入数据先通过Encoder,再通过Decoder,最后把输出进行多分类,分类数为英文字典长度,也就是判断每一个字的概率。

class Transformer(nn.Module):def __init__(self):super(Transformer, self).__init__()# 编码器self.encoder = Encoder()# 解码器self.decoder = Decoder()# 解码器最后的分类器,分类器的输入d_model是解码层每个token的输出维度大小,需要将其转为词表大小,再计算softmax;计算哪个词出现的概率最大self.projection = nn.Linear(d_model, tgt_vocab_size, bias=False)def forward(self, enc_inputs, dec_inputs):#  Transformer的两个输入,一个是编码器的输入(源序列),一个是解码器的输入(目标序列)# 其中,enc_inputs的大小应该是 [batch_size, src_len] ;  dec_inputs的大小应该是 [batch_size, dec_inputs]"""源数据输入到encoder之后得到 enc_outputs, enc_self_attns;enc_outputs是需要传给decoder的矩阵,表示源数据的表示特征enc_self_attns表示单词之间的相关性矩阵"""enc_outputs, enc_self_attns = self.encoder(enc_inputs)"""decoder的输入数据包括三部分:1. encoder得到的表示特征enc_outputs、2. 解码器的输入dec_inputs(目标序列)、3. 以及enc_inputs"""dec_outputs, dec_self_attns, dec_enc_attns = self.decoder(dec_inputs, enc_inputs, enc_outputs)"""将decoder的输出映射到词表大小,最后进行softmax输出即可"""dec_logits = self.projection(dec_outputs) # dec_logits : [batch_size x 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

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VRRP虚拟路由实验(华为)

思科设备参考:VRRP虚拟路由实验(思科) 一,技术简介 VRRP(Virtual Router Redundancy Protocol)是一种网络协议,用于实现路由器冗余,提高网络可靠性和容错能力。VRRP允许多台路由器…

Windows Server 2016虚拟机安装教程

一、VMware Workstation虚拟机软件的下载 官网下载入口:​​​​​​Download VMware Workstation Pro - VMware Customer Connect​​​​​ 下载好之后自己看着提示安装软件就好. 二、镜像文件的下载 下载网站入口:MSDN, 我告诉你 - 做一个安静…

架构师系列-搜索引擎ElasticSearch(六)- 映射

映射配置 在创建索引时,可以预先定义字段的类型(映射类型)及相关属性。 数据库建表的时候,我们DDL依据一般都会指定每个字段的存储类型,例如:varchar、int、datetime等,目的很明确,就…

边缘计算【智能+安全检测】系列教程--使用OpenCV+GStreamer实现真正的硬解码,完全消除马赛克

通过现有博客的GST_URL = "rtspsrc location=rtsp://admin:abcd1234@192.168.1.64:554/h264/ch01/main/av_stream latency=150 ! rtph264depay ! avdec_h264 ! videorate ! videoconvert ! appsink sync=false" GStreamer的解码方式解码,大多情况应该存在上图马赛克…

QQ农场-phpYeFarm添加数据教程

前置知识 plugin\qqfarm\core\data D:\study-project\testweb\upload\source\plugin\qqfarm\core\data 也就是plugin\qqfarm\core\data是一个缓存文件,如果更新农场数据后,必须要删除才可以 解决种子限制(必须要做才可以添加成功) 你不更改加入了id大于2000直接删除种子 D…

matlab学习(三)(4.9-4.15)

一、空域里LSB算法的原理 1.原理: LSB算法通过替换图像像素的最低位来嵌入信息。这些被替换的LSB序列可以是需要加入的水印信息、水印的数字摘要或者由水印生成的伪随机序列。 2.实现步骤: (1)将图像文件中的所有像素点以RGB形…

《手机维修600G资料》云盘下载地址

无意中发现一个生财之道,哈哈哈,就是发现有人在一些视频平台,发手机维修之类的视频吸引客户。这样自己就不用开店也可以接生意了。问题剩下就一个了,把手机维修技术学好,一技在手,天上我有。 《手机维修600…

JVM虚拟机(六)JVM调优的常用参数

目录 一、介绍二、设置堆空间大小三、虚拟机栈的设置四、年轻代中 Eden区和两个 Survivor 区的大小比例五、年轻代晋升老年代阈值六、设置垃圾回收器 一、介绍 我们知道 JVM 调优的参数是非常多的,如果想要系统地学习可以参考下面的地址,这里面介绍了所…

什么是SMR硬盘?

知识铺垫:SMR与CMR 该部分主要参考知乎 https://zhuanlan.zhihu.com/p/393369645 SMR与CMR都属于HDD,即机械硬盘,机械硬盘之所以叫机械硬盘,就是因为它内部完全由机械结构制成。一块机械硬盘由磁盘面、马达和磁头构成,…

专业140+总分410+北京理工大学826信号处理导论考研经验北理工电子信息通信工程,真题,参考书,大纲。

今年考研专业课826信号处理导论(信号系统和数字信号处理)140,总分410,顺利上岸!回看去年将近一年的复习,还是记忆犹新,有不少经历想和大家分享,有得有失,希望可以对大家复…

【机器学习】一文掌握机器学习十大分类算法(上)。

十大分类算法 1、引言2、分类算法总结2.1 逻辑回归2.1.1 核心原理2.1.2 算法公式2.1.3 代码实例 2.2 决策树2.2.1 核心原理2.2. 代码实例 2.3 随机森林2.3.1 核心原理2.3.2 代码实例 2.4 支持向量机2.4.1 核心原理2.4.2 算法公式2.4.3 代码实例 2.5 朴素贝叶斯2.5.1 核心原理2.…

Python大数据分析——一元与多元线性回归模型

Python大数据分析——一元与多元线性回归模型 相关分析概念示例 一元线性回归模型概念理论分析函数示例 多元线性回归模型概念理论分析示例 线性回归模型的假设检验模型的F检验理论分析示例 模型的T检验理论分析示例 相关分析 概念 a 正相关;b 负相关;c…

STM32常见调试工具介绍

STM32的常见调试工具主要包括ST-LINK、USB转TTL、USB转485以及USB转CAN。这些工具在嵌入式系统开发、调试以及通信中发挥着重要的作用。 1.ST-LINK: ST-LINK是STMicroelectronics公司专为其STM32系列微控制器开发的调试和编程工具。既能仿真也能将编译好的程序下载…

软件杯 深度学习卷积神经网络垃圾分类系统 - 深度学习 神经网络 图像识别 垃圾分类 算法 小程序

文章目录 0 简介1 背景意义2 数据集3 数据探索4 数据增广(数据集补充)5 垃圾图像分类5.1 迁移学习5.1.1 什么是迁移学习?5.1.2 为什么要迁移学习? 5.2 模型选择5.3 训练环境5.3.1 硬件配置5.3.2 软件配置 5.4 训练过程5.5 模型分类效果(PC端) 6 构建垃圾…

如何在MacOS上使用OpenHarmony SDK交叉编译?

本文以cJSON三方库为例介绍如何通过OpenHarmony的SDK在Mac平台进行交叉编译。 环境准备 SDK准备 我们可以通过 openHarmony SDK 官方发布渠道下载对应mac版本的SDK,当前OpenHarmony MAC版本的SDK有2种,一种是x86架构,另一种是arm64&#x…

C语言 函数——断言与防御式编程

目录 如何确定假设的真假? 断言 防御式编程(Defensive programming) 如何确定假设的真假? 程序中的假设 *某个特定点的某个表达式的值一定为真 *某个特定点的某个表达式的值一定位于某个区间等 问题:如何确定这些…

蓝桥杯真题演练:2023B组c/c++

日期统计 小蓝现在有一个长度为 100 的数组,数组中的每个元素的值都在 0 到 9 的范围之内。 数组中的元素从左至右如下所示: 5 6 8 6 9 1 6 1 2 4 9 1 9 8 2 3 6 4 7 7 5 9 5 0 3 8 7 5 8 1 5 8 6 1 8 3 0 3 7 9 2 7 0 5 8 8 5 7 0 9 9 1 9 4 4 6 8 6 3 …

网络篇08 | 运输层 tcp

网络篇08 | 运输层 tcp 01 简介1)运输层的作用2)与应用层的关系3)两个协议的应用场景4)传输的数据单位 02 功能特性1)面向连接2)停止等待协议3)流水线传输协议4)滑动窗口机制5&#…

卷积神经网络(LeNet5实现对Fashion_MNIST分类

参考6.6. 卷积神经网络(LeNet) — 动手学深度学习 2.0.0 documentation (d2l.ai) ps:在这里预备使用pythorch 1.对 LeNet 的初步认识 总的来看,LeNet主要分为两个部分: 卷积编码器:由两个卷积层组成; …

微服务相关

1. 微服务主要七个模块 中央管理平台:生产者、消费者注册,服务发现,服务治理,调用关系生产者消费者权限管理流量管理自定义传输协议序列化反序列化 2. 中央管理平台 生产者A在中央管理平台注册后,中央管理平台会给他…