在这个例子里,做的是翻译任务,首先输入是64 * 10的矩阵,代表64个句子,每个句子10个词。
X = self.positionalEncoding(self.embedding(X)*math.sqrt(self.num_hiddens))
在经过embeddeding之后,变为64 * 10 *32 矩阵,每个词使用32维向量表示。然后将数据放入 X = encoder_block(X,valid_lens)
,这里我们将block设为1,就是encoderBlock只有一层。valid_lens
是一个64 * 1的向量,表示每句话的有用的向量。
随后就进入Y = self.addnorm1(X,self.multihead_attention(X,X,X,valid_lens))
,先进入多头注意力机制。
queries = transpose_qkv(self.W_q(queries),self.num_heads)
keys = transpose_qkv(self.W_k(keys),self.num_heads)
values = transpose_qkv(self.W_v(values),self.num_heads)
self.W_q、self.W_k、self.W_v
均为全连接层,将输入的X,进行不同的变换。这里的num_heads为4,transpose_qkv函数会将query进行切分。
def transpose_qkv(X,num_heads):
# 输入为64*10*32
X = X.reshape(X.shape[0],X.shape[1],num_heads,-1)
#torch.Size([64, 10, 4, 8])
X = X.permute(0, 2, 1, 3)
#torch.Size([64, 4, 10, 8])
return X.reshape(-1, X.shape[2], X.shape[3])
#输入为torch.Size([256, 10, 8])
一共有64句话,此次的head为4,也就是每行句子需要4次不同的self-attention,一共需要256次,
其中每次self-attention中query与单词个数相同均为10,query的由一个长度为8的向量表示。这也就是256*10*8的由来。
因为是self-attention,所以key和value也均为256 * 10 * 8,这里还需要处理valid_lens,我们要将valid_lens重复head次,这样每个注意力都可以使用。valid_lens = torch.repeat_interleave(valid_lens,repeats=self.num_heads,dim=0)
之后就会进行attention的处理scores = torch.bmm(queries, keys.transpose(1,2)) / math.sqrt(d)
这里就会实现公式
这里只计算了括号里面的内容。然后就需要进行mask操作,在之前我们已经将valid_lens 1 * 64 扩展为 1 * 256 ,而我们计算得出的query-key矩阵为256 * 10 * 10,其中的10 * 10就是Query-key矩阵,每行均需要进行遮蔽。这里进行mask,主要是因为输入的句子是经过填充之后的,填充部分,需要进行mask。所以需要再将valid_lens扩大10倍,变为2560大小。再紧接着就是以下代码:
mask = torch.arange((maxlen), dtype=torch.float32,device=X.device)[None, :] < valid_len[:, None]
X[~mask] = value
value是负的极小值,在经过softmax之后,趋近于0.
将进行遮蔽后的数据,在经过softmax后,乘以value(256 * 10 * 8),就可以了,结果为256 * 10 * 8的矩阵,然后在经过转换变为torch.Size([64, 10, 32]。多头注意力结果。将结果返回至Y = self.addnorm1(X,self.multihead_attention(X,X,X,valid_lens))
做残差化和规范化。最后经过return self.addnorm2(Y,self.ffn(Y))
进入前馈神经网络,残差化和规范化。
前馈神经网络其实就是一个MLP,将输入扩大4倍,最后输出的时候,再回到原来的大小。
mlp应该是增强信息的泛化能力,即在抽象层面表示更多信息。说白了就是拿到所有的输入序列做一次空间转换,转换的方法是学出来的,而转换的特点是输入输出形状相同
encoder部分结束。
decode开始之前,会执行dec_state = self.decoder.init_state(enc_outputs, *args)
def init_state(self, enc_outputs, enc_valid_lens,*args):
return [enc_outputs,enc_valid_lens,[None]*self.num_layers]
创建并返回一个表示解码器初始状态的列表,其中包含了编码器的输出、编码器输入序列的有效长度以及对应层数的初始状态(初始值为 None)。其中enc_outputs为[64, 10, 32],enc_valid_lens为torch.Size([64])
此外我们还需要注意在DecoderBlock里 forward中,有以下代码:
enc_outputs, enc_valid_lens = state[0], state[1]
这里将state里数据取出来,均是encoder里面的数据。
if state[2][self.i] is None:
keys_values = X
我们要明确X,为label,在训练的时候,输入encode不同的数据,会产生不同的state,所以state[2]的状态为None。
而在预测的时刻,encode只会输入一次数据,只会初始化一次state,而在预测的时候,我们是用之前的预测出来的所有结果,来预测这一次的输入,
state[2][self.i]将会存储之前的预测结果,keys_values 将之前的预测结果,与此时的输入连接起来,作为网络的输入。
else:
keys_values = torch.cat([state[2][self.i], X], dim=1)
state[2][self.i] = keys_values
然后就进入decode,return self.decoder(dec_X, dec_state)
放入decoder中有两部分,其中dec_X为label,dec_state为encoder传给decoder的数据。首先,X = self.positionalEncoding(self.embedding(X)*math.sqrt(self.num_hiddens))
与encoder部分对输入的处理方法相同,
随后就会有两种方法,分别对应训练和预测。在训练,每一个batch都会调用一次init_state函数,所以state[2]始终是一个None列表,当测试时,由于每次根据当前时间步的词元预测下一个词元时都不会重新调用init_state()函数,不会重新初始化state,因此state[2]里面保存的是之前时间步预测出来的词元信息(存的是decoder每层第一个掩码多头注意力state信息)
if state[2][self.i] is None:
keys_values = X
else:
keys_values = torch.cat([state[2][self.i], X], dim=1)
state[2][self.i] = keys_values
然后就是处理mask,dec_valid_lens = torch.arange(1, num_step + 1, device=X.device).repeat(batch_size, 1)
这里与encoder部分原理类似,不再复述。其中生成的矩阵如下所示:文章来源:https://www.toymoban.com/news/detail-617731.html
tensor([[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
共64行,这里略去。比如:在进行注意力计算的时候,第三个query只能与第一、二、三的key进行计算,
其余的不可以进行计算。
]
随后开始计算注意力X2 = self.mask_multihead_attention1(X, keys_values, keys_values, dec_valid_lens)
,计算过程与上面类似,随后就是残差和规范化,之后多头注意力 Y2 = self.mutilhead_attention2(Y, enc_outputs, enc_outputs, enc_valid_lens)
这里的Key与value是从encoder中得到的。随后残差和规范化、前馈神经网络、残差和规范化。将结果经过self.dense = nn.Linear(num_hiddens,vocab_size)
这个全连接层得到在vocab上每个词的概率。
这里再解释一下cross_entropy,我们最终得到预测大小为64 * 10 * 201大小的矩阵,label矩阵为64 * 10大小,需要将输入的维度变为64 * 201 * 10,与之进行计算loss。文章来源地址https://www.toymoban.com/news/detail-617731.html
到了这里,关于transformer从开始到结束的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!