当前位置:   article > 正文

transformer模型代码_srcemb

srcemb
  1. import numpy as np
  2. import torch
  3. import torch.nn as nn
  4. import torch.optim as optim
  5. import matplotlib.pyplot as plt
  6. import math
  7. def make_batch(sentences):
  8. input_batch = [[src_vocab[n] for n in sentences[0].split()]]
  9. output_batch = [[tgt_vocab[n] for n in sentences[1].split()]]
  10. target_batch = [[tgt_vocab[n] for n in sentences[2].split()]]
  11. return torch.LongTensor(input_batch), torch.LongTensor(output_batch), torch.LongTensor(target_batch)
  12. ## 10
  13. def get_attn_subsequent_mask(seq):
  14. """
  15. seq: [batch_size, tgt_len]
  16. """
  17. attn_shape = [seq.size(0), seq.size(1), seq.size(1)]
  18. # attn_shape: [batch_size, tgt_len, tgt_len]
  19. subsequence_mask = np.triu(np.ones(attn_shape), k=1) # 生成一个上三角矩阵
  20. subsequence_mask = torch.from_numpy(subsequence_mask).byte()
  21. return subsequence_mask # [batch_size, tgt_len, tgt_len]
  22. ## 7. ScaledDotProductAttention
  23. class ScaledDotProductAttention(nn.Module):
  24. def __init__(self):
  25. super(ScaledDotProductAttention, self).__init__()
  26. def forward(self, Q, K, V, attn_mask):
  27. ## 输入进来的维度分别是 [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]
  28. ##首先经过matmul函数得到的scores形状是 : [batch_size x n_heads x len_q x len_k]
  29. scores = torch.matmul(Q, K.transpose(-1, -2)) / np.sqrt(d_k)
  30. ## 然后关键词地方来了,下面这个就是用到了我们之前重点讲的attn_mask,把被mask的地方置为无限小,softmax之后基本就是0,对q的单词不起作用
  31. scores.masked_fill_(attn_mask, -1e9) # Fills elements of self tensor with value where mask is one.
  32. attn = nn.Softmax(dim=-1)(scores)
  33. context = torch.matmul(attn, V)
  34. return context, attn
  35. ## 6. MultiHeadAttention
  36. class MultiHeadAttention(nn.Module):
  37. def __init__(self):
  38. super(MultiHeadAttention, self).__init__()
  39. ## 输入进来的QKV是相等的,我们会使用映射linear做一个映射得到参数矩阵Wq, Wk,Wv
  40. self.W_Q = nn.Linear(d_model, d_k * n_heads)
  41. self.W_K = nn.Linear(d_model, d_k * n_heads)
  42. self.W_V = nn.Linear(d_model, d_v * n_heads)
  43. self.linear = nn.Linear(n_heads * d_v, d_model)
  44. self.layer_norm = nn.LayerNorm(d_model)
  45. def forward(self, Q, K, V, attn_mask):
  46. ## 这个多头分为这几个步骤,首先映射分头,然后计算atten_scores,然后计算atten_value;
  47. ##输入进来的数据形状: 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]
  48. residual, batch_size = Q, Q.size(0)
  49. # (B, S, D) -proj-> (B, S, D) -split-> (B, S, H, W) -trans-> (B, H, S, W)
  50. ##下面这个就是先映射,后分头;一定要注意的是q和k分头之后维度是一致额,所以一看这里都是dk
  51. 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]
  52. 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]
  53. 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]
  54. ## 输入进行的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个头上
  55. attn_mask = attn_mask.unsqueeze(1).repeat(1, n_heads, 1, 1)
  56. ##然后我们计算 ScaledDotProductAttention 这个函数,去7.看一下
  57. ## 得到的结果有两个:context: [batch_size x n_heads x len_q x d_v], attn: [batch_size x n_heads x len_q x len_k]
  58. context, attn = ScaledDotProductAttention()(q_s, k_s, v_s, attn_mask)
  59. 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]
  60. output = self.linear(context)
  61. return self.layer_norm(output + residual), attn # output: [batch_size x len_q x d_model]
  62. ## 8. PoswiseFeedForwardNet
  63. class PoswiseFeedForwardNet(nn.Module):
  64. def __init__(self):
  65. super(PoswiseFeedForwardNet, self).__init__()
  66. self.conv1 = nn.Conv1d(in_channels=d_model, out_channels=d_ff, kernel_size=1)
  67. self.conv2 = nn.Conv1d(in_channels=d_ff, out_channels=d_model, kernel_size=1)
  68. self.layer_norm = nn.LayerNorm(d_model)
  69. def forward(self, inputs):
  70. residual = inputs # inputs : [batch_size, len_q, d_model]
  71. output = nn.ReLU()(self.conv1(inputs.transpose(1, 2)))
  72. output = self.conv2(output).transpose(1, 2)
  73. return self.layer_norm(output + residual)
  74. ## 4. get_attn_pad_mask
  75. ## 比如说,我现在的句子长度是5,在后面注意力机制的部分,我们在计算出来QK转置除以根号之后,softmax之前,我们得到的形状
  76. ## len_input * len*input 代表每个单词对其余包含自己的单词的影响力
  77. ## 所以这里我需要有一个同等大小形状的矩阵,告诉我哪个位置是PAD部分,之后在计算计算softmax之前会把这里置为无穷大;
  78. ## 一定需要注意的是这里得到的矩阵形状是batch_size x len_q x len_k,我们是对k中的pad符号进行标识,并没有对k中的做标识,因为没必要
  79. ## seq_q 和 seq_k 不一定一致,在交互注意力,q来自解码端,k来自编码端,所以告诉模型编码这边pad符号信息就可以,解码端的pad信息在交互注意力层是没有用到的;
  80. def get_attn_pad_mask(seq_q, seq_k):
  81. batch_size, len_q = seq_q.size()
  82. batch_size, len_k = seq_k.size()
  83. # eq(zero) is PAD token
  84. pad_attn_mask = seq_k.data.eq(0).unsqueeze(1) # batch_size x 1 x len_k, one is masking
  85. return pad_attn_mask.expand(batch_size, len_q, len_k) # batch_size x len_q x len_k
  86. ## 3. PositionalEncoding 代码实现
  87. class PositionalEncoding(nn.Module):
  88. def __init__(self, d_model, dropout=0.1, max_len=5000):
  89. super(PositionalEncoding, self).__init__()
  90. ## 位置编码的实现其实很简单,直接对照着公式去敲代码就可以,下面这个代码只是其中一种实现方式;
  91. ## 从理解来讲,需要注意的就是偶数和奇数在公式上有一个共同部分,我们使用log函数把次方拿下来,方便计算;
  92. ## pos代表的是单词在句子中的索引,这点需要注意;比如max_len是128个,那么索引就是从0,1,2,...,127
  93. ##假设我的demodel是512,2i那个符号中i从0取到了255,那么2i对应取值就是0,2,4...510
  94. self.dropout = nn.Dropout(p=dropout)
  95. pe = torch.zeros(max_len, d_model)
  96. position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
  97. div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))
  98. pe[:, 0::2] = torch.sin(position * div_term)## 这里需要注意的是pe[:, 0::2]这个用法,就是从0开始到最后面,补长为2,其实代表的就是偶数位置
  99. pe[:, 1::2] = torch.cos(position * div_term)##这里需要注意的是pe[:, 1::2]这个用法,就是从1开始到最后面,补长为2,其实代表的就是奇数位置
  100. ## 上面代码获取之后得到的pe:[max_len*d_model]
  101. ## 下面这个代码之后,我们得到的pe形状是:[max_len*1*d_model]
  102. pe = pe.unsqueeze(0).transpose(0, 1)
  103. self.register_buffer('pe', pe) ## 定一个缓冲区,其实简单理解为这个参数不更新就可以
  104. def forward(self, x):
  105. """
  106. x: [seq_len, batch_size, d_model]
  107. """
  108. x = x + self.pe[:x.size(0), :]
  109. return self.dropout(x)
  110. ## 5. EncoderLayer :包含两个部分,多头注意力机制和前馈神经网络
  111. class EncoderLayer(nn.Module):
  112. def __init__(self):
  113. super(EncoderLayer, self).__init__()
  114. self.enc_self_attn = MultiHeadAttention()
  115. self.pos_ffn = PoswiseFeedForwardNet()
  116. def forward(self, enc_inputs, enc_self_attn_mask):
  117. ## 下面这个就是做自注意力层,输入是enc_inputs,形状是[batch_size x seq_len_q x d_model] 需要注意的是最初始的QKV矩阵是等同于这个输入的,去看一下enc_self_attn函数 6.
  118. enc_outputs, attn = self.enc_self_attn(enc_inputs, enc_inputs, enc_inputs, enc_self_attn_mask) # enc_inputs to same Q,K,V
  119. enc_outputs = self.pos_ffn(enc_outputs) # enc_outputs: [batch_size x len_q x d_model]
  120. return enc_outputs, attn
  121. ## 2. Encoder 部分包含三个部分:词向量embedding,位置编码部分,注意力层及后续的前馈神经网络
  122. class Encoder(nn.Module):
  123. def __init__(self):
  124. super(Encoder, self).__init__()
  125. self.src_emb = nn.Embedding(src_vocab_size, d_model) ## 这个其实就是去定义生成一个矩阵,大小是 src_vocab_size * d_model
  126. self.pos_emb = PositionalEncoding(d_model) ## 位置编码情况,这里是固定的正余弦函数,也可以使用类似词向量的nn.Embedding获得一个可以更新学习的位置编码
  127. self.layers = nn.ModuleList([EncoderLayer() for _ in range(n_layers)]) ## 使用ModuleList对多个encoder进行堆叠,因为后续的encoder并没有使用词向量和位置编码,所以抽离出来;
  128. def forward(self, enc_inputs):
  129. ## 这里我们的 enc_inputs 形状是: [batch_size x source_len]
  130. ## 下面这个代码通过src_emb,进行索引定位,enc_outputs输出形状是[batch_size, src_len, d_model]
  131. enc_outputs = self.src_emb(enc_inputs)
  132. ## 这里就是位置编码,把两者相加放入到了这个函数里面,从这里可以去看一下位置编码函数的实现;3.
  133. enc_outputs = self.pos_emb(enc_outputs.transpose(0, 1)).transpose(0, 1)
  134. ##get_attn_pad_mask是为了得到句子中pad的位置信息,给到模型后面,在计算自注意力和交互注意力的时候去掉pad符号的影响,去看一下这个函数 4.
  135. enc_self_attn_mask = get_attn_pad_mask(enc_inputs, enc_inputs)
  136. enc_self_attns = []
  137. for layer in self.layers:
  138. ## 去看EncoderLayer 层函数 5.
  139. enc_outputs, enc_self_attn = layer(enc_outputs, enc_self_attn_mask)
  140. enc_self_attns.append(enc_self_attn)
  141. return enc_outputs, enc_self_attns
  142. ## 10.
  143. class DecoderLayer(nn.Module):
  144. def __init__(self):
  145. super(DecoderLayer, self).__init__()
  146. self.dec_self_attn = MultiHeadAttention()
  147. self.dec_enc_attn = MultiHeadAttention()
  148. self.pos_ffn = PoswiseFeedForwardNet()
  149. def forward(self, dec_inputs, enc_outputs, dec_self_attn_mask, dec_enc_attn_mask):
  150. dec_outputs, dec_self_attn = self.dec_self_attn(dec_inputs, dec_inputs, dec_inputs, dec_self_attn_mask)
  151. dec_outputs, dec_enc_attn = self.dec_enc_attn(dec_outputs, enc_outputs, enc_outputs, dec_enc_attn_mask)
  152. dec_outputs = self.pos_ffn(dec_outputs)
  153. return dec_outputs, dec_self_attn, dec_enc_attn
  154. ## 9. Decoder
  155. class Decoder(nn.Module):
  156. def __init__(self):
  157. super(Decoder, self).__init__()
  158. self.tgt_emb = nn.Embedding(tgt_vocab_size, d_model)
  159. self.pos_emb = PositionalEncoding(d_model)
  160. self.layers = nn.ModuleList([DecoderLayer() for _ in range(n_layers)])
  161. def forward(self, dec_inputs, enc_inputs, enc_outputs): # dec_inputs : [batch_size x target_len]
  162. dec_outputs = self.tgt_emb(dec_inputs) # [batch_size, tgt_len, d_model]
  163. dec_outputs = self.pos_emb(dec_outputs.transpose(0, 1)).transpose(0, 1) # [batch_size, tgt_len, d_model]
  164. ## get_attn_pad_mask 自注意力层的时候的pad 部分
  165. dec_self_attn_pad_mask = get_attn_pad_mask(dec_inputs, dec_inputs)
  166. ## get_attn_subsequent_mask 这个做的是自注意层的mask部分,就是当前单词之后看不到,使用一个上三角为1的矩阵
  167. dec_self_attn_subsequent_mask = get_attn_subsequent_mask(dec_inputs)
  168. ## 两个矩阵相加,大于0的为1,不大于0的为0,为1的在之后就会被fill到无限小
  169. dec_self_attn_mask = torch.gt((dec_self_attn_pad_mask + dec_self_attn_subsequent_mask), 0)
  170. ## 这个做的是交互注意力机制中的mask矩阵,enc的输入是k,我去看这个k里面哪些是pad符号,给到后面的模型;注意哦,我q肯定也是有pad符号,但是这里我不在意的,之前说了好多次了哈
  171. dec_enc_attn_mask = get_attn_pad_mask(dec_inputs, enc_inputs)
  172. dec_self_attns, dec_enc_attns = [], []
  173. for layer in self.layers:
  174. dec_outputs, dec_self_attn, dec_enc_attn = layer(dec_outputs, enc_outputs, dec_self_attn_mask, dec_enc_attn_mask)
  175. dec_self_attns.append(dec_self_attn)
  176. dec_enc_attns.append(dec_enc_attn)
  177. return dec_outputs, dec_self_attns, dec_enc_attns
  178. ## 1. 从整体网路结构来看,分为三个部分:编码层,解码层,输出层
  179. class Transformer(nn.Module):
  180. def __init__(self):
  181. super(Transformer, self).__init__()
  182. self.encoder = Encoder() ## 编码层
  183. self.decoder = Decoder() ## 解码层
  184. self.projection = nn.Linear(d_model, tgt_vocab_size, bias=False) ## 输出层 d_model 是我们解码层每个token输出的维度大小,之后会做一个 tgt_vocab_size 大小的softmax
  185. def forward(self, enc_inputs, dec_inputs):
  186. ## 这里有两个数据进行输入,一个是enc_inputs 形状为[batch_size, src_len],主要是作为编码段的输入,一个dec_inputs,形状为[batch_size, tgt_len],主要是作为解码端的输入
  187. ## enc_inputs作为输入 形状为[batch_size, src_len],输出由自己的函数内部指定,想要什么指定输出什么,可以是全部tokens的输出,可以是特定每一层的输出;也可以是中间某些参数的输出;
  188. ## enc_outputs就是主要的输出,enc_self_attns这里没记错的是QK转置相乘之后softmax之后的矩阵值,代表的是每个单词和其他单词相关性;
  189. enc_outputs, enc_self_attns = self.encoder(enc_inputs)
  190. ## dec_outputs 是decoder主要输出,用于后续的linear映射; dec_self_attns类比于enc_self_attns 是查看每个单词对decoder中输入的其余单词的相关性;dec_enc_attns是decoder中每个单词对encoder中每个单词的相关性;
  191. dec_outputs, dec_self_attns, dec_enc_attns = self.decoder(dec_inputs, enc_inputs, enc_outputs)
  192. ## dec_outputs做映射到词表大小
  193. dec_logits = self.projection(dec_outputs) # dec_logits : [batch_size x src_vocab_size x tgt_vocab_size]
  194. return dec_logits.view(-1, dec_logits.size(-1)), enc_self_attns, dec_self_attns, dec_enc_attns
  195. if __name__ == '__main__':
  196. ## 句子的输入部分,
  197. sentences = ['ich mochte ein bier P', 'S i want a beer', 'i want a beer E']
  198. # Transformer Parameters
  199. # Padding Should be Zero
  200. ## 构建词表
  201. src_vocab = {'P': 0, 'ich': 1, 'mochte': 2, 'ein': 3, 'bier': 4}
  202. src_vocab_size = len(src_vocab)
  203. tgt_vocab = {'P': 0, 'i': 1, 'want': 2, 'a': 3, 'beer': 4, 'S': 5, 'E': 6}
  204. tgt_vocab_size = len(tgt_vocab)
  205. src_len = 5 # length of source
  206. tgt_len = 5 # length of target
  207. ## 模型参数
  208. d_model = 512 # Embedding Size
  209. d_ff = 2048 # FeedForward dimension
  210. d_k = d_v = 64 # dimension of K(=Q), V
  211. n_layers = 6 # number of Encoder of Decoder Layer
  212. n_heads = 8 # number of heads in Multi-Head Attention
  213. model = Transformer()
  214. criterion = nn.CrossEntropyLoss()
  215. optimizer = optim.Adam(model.parameters(), lr=0.001)
  216. enc_inputs, dec_inputs, target_batch = make_batch(sentences)
  217. for epoch in range(200):
  218. optimizer.zero_grad()
  219. outputs, enc_self_attns, dec_self_attns, dec_enc_attns = model(enc_inputs, dec_inputs)
  220. loss = criterion(outputs, target_batch.contiguous().view(-1))
  221. print('Epoch:', '%04d' % (epoch + 1), 'cost =', '{:.6f}'.format(loss))
  222. loss.backward()
  223. optimizer.step()

代码源自微信公众号:DASOU

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/喵喵爱编程/article/detail/886972
推荐阅读
相关标签
  

闽ICP备14008679号