Transformer代码讲解(最最最最......详细)

更新时间:2023-07-30 14:15:08 阅读: 评论:0

Transformer代码讲解(最最最最......详细)Transformer代码讲解(最最最最…详细)
整个代码主要分为两部分去讲解:
⼀、完整代码
⼆、部分代码剖析
1、主函数if __name__ == '__main__':
2、从整体⽹路结构来看,分为三个部分:编码层,解码层,输出层
3、Encoder 部分包含三个部分:词向量embedding,位置编码部分,注意⼒层及后续的前馈神经⽹络
4、PositionalEncoding 代码实现什么是素质教育
5、get_attn_pad_mask
6、EncoderLayer :包含两个部分,多头注意⼒机制和前馈神经⽹络
7、MultiHeadAttention
8、ScaledDotProductAttentio
9、PoswiFeedForwardNet
10、Decoder
11、DecoderLayer(nn.Module):
12、get_attn_subquent_mask(q)
正⽂:
⼀、完整代码
import numpy as np
import torch
as nn
import torch.optim as optim
import matplotlib.pyplot as plt
import math
def make_batch(ntences):
input_batch =[[src_vocab[n]for n in ntences[0].split()]]
output_batch =[[tgt_vocab[n]for n in ntences[1].split()]]
target_batch =[[tgt_vocab[n]for n in ntences[2].split()]]
return torch.LongTensor(input_batch), torch.LongTensor(output_batch), torch.LongTensor(target_batch)
## 10
欲情魔王def get_attn_subquent_mask(q):
"""
q: [batch_size, tgt_len]
"""
attn_shape =[q.size(0), q.size(1), q.size(1)]
# attn_shape: [batch_size, tgt_len, tgt_len]
subquence_mask = np.s(attn_shape), k=1)# ⽣成⼀个上三⾓矩阵
subquence_mask = torch.from_numpy(subquence_mask).byte()
return subquence_mask  # [batch_size, tgt_len, tgt_len]
## 7. ScaledDotProductAttention
class ScaledDotProductAttention(nn.Module):
def__init__(lf):
super(ScaledDotProductAttention, lf).__init__()
def forward(lf, Q, K, V, attn_mask):
## 输⼊进来的维度分别是 [batch_size x n_heads x len_q x d_k]  K: [batch_size x n_heads x len_k
x d_k]  V: [batch_size x n_heads x len_k x d_v] ##⾸先经过matmul函数得到的scores形状是 : [batch_size x n_heads x len_q x len_k]
scores = torch.matmul(Q, K.transpo(-1,-2))/ np.sqrt(d_k)
## 然后关键词地⽅来了,下⾯这个就是⽤到了我们之前重点讲的attn_mask,把被mask的地⽅置为⽆限⼩,softmax之后基本就是0,对q的单词不起作⽤        scores.masked_fill_(attn_mask,-1e9)# Fills elements of lf tensor with value where mask is one.
attn = nn.Softmax(dim=-1)(scores)
context = torch.matmul(attn, V)
return context, attn
## 6. MultiHeadAttention
class MultiHeadAttention(nn.Module):
def__init__(lf):
super(MultiHeadAttention, lf).__init__()
## 输⼊进来的QKV是相等的,我们会使⽤映射linear做⼀个映射得到参数矩阵Wq, Wk,Wv
lf.W_Q = nn.Linear(d_model, d_k * n_heads)# 这⼉是不是应该是d_q啊
lf.W_K = nn.Linear(d_model, d_k * n_heads)
lf.W_V = nn.Linear(d_model, d_v * n_heads)
lf.linear = nn.Linear(n_heads * d_v, d_model)
lf.layer_norm = nn.LayerNorm(d_model)
def forward(lf, 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 = lf.W_Q(Q).view(batch_size,-1, n_heads, d_k).transpo(1,2)# q_s: [batch_size x n_heads x len_q x d_k]
k_s = lf.W_K(K).view(batch_size,-1, n_heads, d_k).transpo(1,2)# k_s: [batch_size x n_heads x len_k x d_k]
v_s = lf.W_V(V).view(batch_size,-1, n_heads, d_v).transpo(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 = anspo(1,2).contiguous().view(batch_size,-1, n_heads * d_v)# context: [batch_size x len_q x n_heads * d_v]
output = lf.linear(context)
return lf.layer_norm(output + residual), attn # output: [batch_size x len_q x d_model]
## 8. PoswiFeedForwardNet
class PoswiFeedForwardNet(nn.Module):
def__init__(lf):
super(PoswiFeedForwardNet, lf).__init__()
lf.layer_norm = nn.LayerNorm(d_model)
def forward(lf, inputs):
residual = inputs # inputs : [batch_size, len_q, d_model]
output = nn.ReLU()(lf.anspo(1,2)))
output = lf.conv2(output).transpo(1,2)
return lf.layer_norm(output + residual)
## 4. get_attn_pad_mask
## ⽐如说,我现在的句⼦长度是5,在后⾯注意⼒机制的部分,我们在计算出来QK转置除以根号之后,softmax之前,我们得到的形状
## len_input * len_input  代表每个单词对其余包含⾃⼰的单词的影响⼒
## 所以这⾥我需要有⼀个同等⼤⼩形状的矩阵,告诉我哪个位置是PAD部分,之后在计算计算softmax之前会把这⾥置为⽆穷⼤;
## ⼀定需要注意的是这⾥得到的矩阵形状是batch_size x len_q x len_k,我们是对k中的pad符号进⾏标识,并没有对k中的做标识,因为没必要
## q_q 和 q_k 不⼀定⼀致(我⾃⼰的理解是原⽂是德⽂,翻译成英⽂,⽽原⽂的德语的单词个数和英语的单词个数不⼀样多,所以这⼉可能不⼀致),在交互注意⼒,q来⾃解码端,k来⾃编码端,所以告诉模型编码这边pad符号信息就可以,解码端的pad信息在交互注意⼒层是没有⽤到的;
def get_attn_pad_mask(q_q, q_k):
batch_size, len_q = q_q.size()
batch_size, len_k = q_k.size()
# eq(zero) is PAD token
pad_attn_mask = q_k.data.eq(0).unsqueeze(1)# batch_size x 1 x len_k, one is masking
return pad_pand(batch_size, len_q, len_k)# batch_size x len_q x len_k
## 3. PositionalEncoding 代码实现
class PositionalEncoding(nn.Module):
def__init__(lf, d_model, dropout=0.1, max_len=5000):
super(PositionalEncoding, lf).__init__()
## 位置编码的实现其实很简单,直接对照着公式去敲代码就可以,下⾯这个代码只是其中⼀种实现⽅式;
## 从理解来讲,需要注意的就是偶数和奇数在公式上有⼀个共同部分,我们使⽤log函数把次⽅拿下来,⽅便计算;
## pos代表的是单词在句⼦中的索引,这点需要注意;⽐如max_len是128个,那么索引就是从0,1,2,...,127
##假设我的demodel是512,2i那个符号中i从0取到了255,那么2i对应取值就是0,2,4 (510)
lf.dropout = nn.Dropout(p=dropout)
lf.dropout = nn.Dropout(p=dropout)
pe = s(max_len, d_model)
position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
div_term = p(torch.arange(0, d_model,2).float()*(-math.log(10000.0)/ d_model))
pe[:,0::2]= torch.sin(position * div_term)## 这⾥需要注意的是pe[:, 0::2]这个⽤法,就是从0开始到最后⾯,步长为2,其实代表的就是偶数位置
pe[:,1::2]= s(position * div_term)##这⾥需要注意的是pe[:, 1::2]这个⽤法,就是从1开始到最后⾯,步长为2,其实代表的就是奇数位置## 上⾯代码获取之后得到的pe:[max_len*d_model]
臭桂鱼## 下⾯这个代码之后,我们得到的pe形状是:[max_len*1*d_model]
pe = pe.unsqueeze(0).transpo(0,1)
def forward(lf, x):
"""
x: [q_len, batch_size, d_model]
"""
x = x + lf.pe[:x.size(0),:]
return lf.dropout(x)
## 5. EncoderLayer :包含两个部分,多头注意⼒机制和前馈神经⽹络
class EncoderLayer(nn.Module):
def__init__(lf):
super(EncoderLayer, lf).__init__()
<_lf_attn = MultiHeadAttention()
lf.pos_ffn = PoswiFeedForwardNet()
def forward(lf, enc_inputs, enc_lf_attn_mask):
## 下⾯这个就是做⾃注意⼒层,输⼊是enc_inputs,形状是[batch_size x q_len_q x d_model] 需要注意的是最初始的QKV矩阵是等同于这个输⼊的,去看⼀下enc_lf_attn函数 6.
enc_outputs, attn = lf.enc_lf_attn(enc_inputs, enc_inputs, enc_inputs, enc_lf_attn_mask)# enc_inputs to same Q,K,V
enc_outputs = lf.pos_ffn(enc_outputs)# enc_outputs: [batch_size x len_q x d_model]
return enc_outputs, attn
## 2. Encoder 部分包含三个部分:词向量embedding,位置编码部分,注意⼒层及后续的前馈神经⽹络
class Encoder(nn.Module):
def__init__(lf):
super(Encoder, lf).__init__()
lf.src_emb = nn.Embedding(src_vocab_size, d_model)## 这个其实就是去定义⽣成⼀个矩阵,⼤⼩是 src_vocab_size * d_model
lf.pos_emb = PositionalEncoding(d_model)## 位置编码情况,这⾥是固定的正余弦函数,也可以使⽤类似词向量的nn.Embedding获得⼀个可以更新学习的位置编码
lf.layers = nn.ModuleList([EncoderLayer()for _ in range(n_layers)])## 使⽤ModuleList对多个encoder进⾏堆叠,因为后续的encoder并没有使⽤词向量和位置编码,所以抽离出来;
def forward(lf, enc_inputs):
## 这⾥我们的 enc_inputs 形状是: [batch_size x source_len]  # 提问:这⼉的source_len == max_lenmax_len:输⼊⼀段话所包含的词的最多有多少个。
## 下⾯这个代码通过src_emb,进⾏索引定位,enc_outputs输出形状是[batch_size, src_len, d_model]  # 提问:这⼉是因为这⼉只有⼀句话,所以才是s rc_len,当有多句话时,这⼉应该是max_len?
enc_outputs = lf.src_emb(enc_inputs)
## 这⾥就是位置编码,把两者相加放⼊到了这个函数⾥⾯,从这⾥可以去看⼀下位置编码函数的实现;3.
enc_outputs = lf.pos_emb(anspo(0,1)).transpo(0,1)
##get_attn_pad_mask是为了得到句⼦中pad的位置信息,给到模型后⾯,在计算⾃注意⼒和交互注意⼒的时候去掉pad符号的影响,去看⼀下这个函数 4.这句话表⽰什么意思?
enc_lf_attn_mask = get_attn_pad_mask(enc_inputs, enc_inputs)#  get_attn_pad_mask告诉后⾯的层那些位置是被pad填充的
enc_lf_attns =[]
for layer in lf.layers:
## 去看EncoderLayer 层函数 5.
enc_outputs, enc_lf_attn = layer(enc_outputs, enc_lf_attn_mask)
enc_outputs, enc_lf_attn = layer(enc_outputs, enc_lf_attn_mask)
enc_lf_attns.append(enc_lf_attn)
return enc_outputs, enc_lf_attns
## 10.
class DecoderLayer(nn.Module):
def__init__(lf):
super(DecoderLayer, lf).__init__()
文征明滕王阁序
lf.dec_lf_attn = MultiHeadAttention()
lf.dec_enc_attn = MultiHeadAttention()
lf.pos_ffn = PoswiFeedForwardNet()
def forward(lf, dec_inputs, enc_outputs, dec_lf_attn_mask, dec_enc_attn_mask):
dec_outputs, dec_lf_attn = lf.dec_lf_attn(dec_inputs, dec_inputs, dec_inputs, dec_lf_attn_mask)
dec_outputs, dec_enc_attn = lf.dec_enc_attn(dec_outputs, enc_outputs, enc_outputs, dec_enc_attn_mask)
dec_outputs = lf.pos_ffn(dec_outputs)
return dec_outputs, dec_lf_attn, dec_enc_attn
## 9. Decoder
class Decoder(nn.Module):
def__init__(lf):
super(Decoder, lf).__init__()
<_emb = nn.Embedding(tgt_vocab_size, d_model)
dnf召唤加点lf.pos_emb = PositionalEncoding(d_model)
lf.layers = nn.ModuleList([DecoderLayer()for _ in range(n_layers)])
def forward(lf, dec_inputs, enc_inputs, enc_outputs):# dec_inputs : [batch_size x target_len]
dec_outputs = lf.tgt_emb(dec_inputs)# [batch_size, tgt_len, d_model]
dec_outputs = lf.pos_emb(anspo(0,1)).transpo(0,1)# [batch_size, tgt_len, d_model]
气血失调
## get_attn_pad_mask ⾃注意⼒层的时候的pad 部分
dec_lf_attn_pad_mask = get_attn_pad_mask(dec_inputs, dec_inputs)
## get_attn_subquent_mask 这个做的是⾃注意层的mask部分,就是当前单词之后看不到,使⽤⼀个上三⾓为1的矩阵
dec_lf_attn_subquent_mask = get_attn_subquent_mask(dec_inputs)
## 两个矩阵相加,⼤于0的为1,不⼤于0的为0,为1的在之后就会被fill到⽆限⼩
dec_lf_attn_mask = ((dec_lf_attn_pad_mask + dec_lf_attn_subquent_mask),0)
## 这个做的是交互注意⼒机制中的mask矩阵,enc的输⼊是k,我去看这个k⾥⾯哪些是pad符号,给到后⾯的模型;注意哦,我q肯定也是有pad符号,但是这⾥我不在意的,之前说了好多次了哈
dec_enc_attn_mask = get_attn_pad_mask(dec_inputs, enc_inputs)
dec_lf_attns, dec_enc_attns =[],[]
for layer in lf.layers:
dec_outputs, dec_lf_attn, dec_enc_attn = layer(dec_outputs, enc_outputs, dec_lf_attn_mask, dec_enc_attn_mask)
dec_lf_attns.append(dec_lf_attn)
微信美女图片dec_enc_attns.append(dec_enc_attn)
return dec_outputs, dec_lf_attns, dec_enc_attns
## 1. 从整体⽹路结构来看,分为三个部分:编码层,解码层,输出层
class Transformer(nn.Module):
def__init__(lf):
super(Transformer, lf).__init__()
lf.decoder = Decoder()## 解码层
lf.projection = nn.Linear(d_model, tgt_vocab_size, bias=Fal)## 输出层 d_model 是我们解码层每个token输出的维度⼤⼩,之后会做⼀个 tgt_voca b_size ⼤⼩的softmax  # d_model:在这⾥每⼀个词表的维度都被设计成了512,⽽现在这是在预测
# ⼀个德语单词被翻译成英语,它会对应为那个单词,所以这⾥输⼊就是⼀个单词在词表中的维度,这⾥的维度是512,在词表中⼀个单词的维度是512。如果⼀句话有n个单词,那么在翻译的整个过程中就会调⽤n次这个全连接函数。然后假设英语单词有100000个,那么这⼉的tgt_vocab_size就是1000000个# 到达这⼉,就好像是⼀个分类任务,看这个单词属于这100000个类中的哪⼀个类,最后全连接分类的结果然后再进⾏⼀个softmax就会得到这100000个单词每个单词的概率。那个那个单词的概率最⼤,那么我们就把这个德语单词翻译成那个单词。也就是我们这⼉的projection就是那个德语单词被翻译成英语单词的词。

本文发布于:2023-07-30 14:15:08,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/82/1123799.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:单词   注意   编码
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图