Transformer 中的 Decoder 机制

大家好,今天和各位分享一下 Transformer 中的 Decoder 部分涉及到的知识点计算 self-attention 时用到的两种 mask

本文是对前两篇文章的补充,强烈建议大家先看一下:

1.《Transformer代码复现》:https://blog.csdn.net/dgvv4/article/details/125491693

2.《Transformer中的Encoder机制》:https://blog.csdn.net/dgvv4/article/details/125507206


1. Decoder 的 self-attention 中的 mask

本节介绍的 mask 对应模型结构图中的位置:

如下图,decoder 的 self-attention 中使用的 mask 是一个下三角矩阵,当 decoder 预测第一个单词时,给它的输入是一个特殊字符 x1,当 decoder 预测第二个位置时,给它的输入是特殊字符 x1 和目标序列的第一个单词 x2

下面举一个例子:

encoder的输入:  i love you

decoder的输入:  /f 我 爱 你

此时的 decoder 是由4个词组成的向量,Mask 是一个 4*4 大小的矩阵

当 decoder 预测第一个单词 '我' 时, decoder 的输入是一个特殊字符 '/f',mask为[1,0,0,0]

当 decoder 预测第二个单词 '爱' 时, decoder 的输入是一个特殊字符 '/f' 和第一个单词 '我',mask为[1,1,0,0]

代码如下:


   
   
  1. import torch
  2. from torch.nn import functional as F
  3. # ------------------------------------------------------ #
  4. #(1)构建下三角形状的mask
  5. # ------------------------------------------------------ #
  6. # 目标序列中有两个句子,分别包含3、4个单词
  7. tgt_len = torch.Tensor([ 3, 4]).to(torch.int32)
  8. # 目标序列有效单词矩阵 shape=[3,3], shape=[4,4]
  9. tgt_matrix = [torch.ones(L, L) for L in tgt_len]
  10. # 对每个元素全为1句子矩阵构造一个下三角矩阵
  11. tri_matrix = [torch.tril(mat) for mat in tgt_matrix]
  12. # 第一个句子长度为3,生成3*3大小且下三角区域的元素权威1,其余全为0的矩阵
  13. print(tri_matrix) # 每个mask的shape=[seq_len,seq_len]
  14. # 构建有效单词的矩阵,通过padding将每个句子的矩阵大小调整成一样的
  15. new_tri_matrix = [] # 保存padding后mask矩阵
  16. for seq_len, matrix in zip(tgt_len, tri_matrix): # 遍历每个下三角矩阵mask
  17. matrix = F.pad(matrix, pad=( 0, max(tgt_len)-seq_len, 0, max(tgt_len)-seq_len)) # 在矩阵的下方和右侧padding成相同相撞
  18. matrix = torch.unsqueeze(matrix, dim= 0) # 维度扩充[seq_len,seq_len]==>[1,seq_len,seq_len]
  19. new_tri_matrix.append(matrix)
  20. # 将列表类型变成tensor, 其中值为0对应的元素代表需要mask掉
  21. valid_tri_matrix = torch.cat(new_tri_matrix, dim= 0)
  22. print( '有效下三角矩阵mask:', valid_tri_matrix) # shape=[2,4,4]
  23. # 将需要mask的元素用布尔类型表示,True代表需要mask
  24. invalid_tri_matrix = ( 1 - valid_tri_matrix).to(torch. bool)
  25. print( '布尔mask:', invalid_tri_matrix)
  26. # ------------------------------------------------------ #
  27. #(2)对decoder的输入张量做mask
  28. # ------------------------------------------------------ #
  29. # 随机初始化一个 Q @ K^T 的计算结果 [batch, tgt_seq_len, tgt_seq_len]
  30. score = torch.randn( 2, max(tgt_len), max(tgt_len))
  31. # 将mask中True元素对应score中的值变成非常小的值
  32. masked_score = score.masked_fill(invalid_tri_matrix, value=- 1e10)
  33. # 将mask后的结果经过softmax,得到注意力矩阵
  34. softmax_score = F.softmax(masked_score, dim=- 1)
  35. print( '原始输入:', score)
  36. print( 'mask后的输入:', masked_score)

然后构造一个 decoder 的输入 Q@K^T,它的 shape=[batch, seq_len, seq_len],如下面的第三个矩阵。

将输入张量 score 中与 mask 中True元素对应的位置变成一个非常小的数,如下面的第四个矩阵。


   
   
  1. # 有效下三角矩阵mask:
  2. tensor([[[ 1., 0., 0., 0.],
  3. [ 1., 1., 0., 0.],
  4. [ 1., 1., 1., 0.],
  5. [ 0., 0., 0., 0.]],
  6. [[ 1., 0., 0., 0.],
  7. [ 1., 1., 0., 0.],
  8. [ 1., 1., 1., 0.],
  9. [ 1., 1., 1., 1.]]])
  10. # 布尔mask:
  11. tensor([[[ False, True, True, True],
  12. [ False, False, True, True],
  13. [ False, False, False, True],
  14. [ True, True, True, True]],
  15. [[ False, True, True, True],
  16. [ False, False, True, True],
  17. [ False, False, False, True],
  18. [ False, False, False, False]]])
  19. # 原始输入scorce:
  20. tensor([[[ 0.5266, - 0.7873, - 0.2481, 0.5554],
  21. [- 1.3146, 0.1668, - 1.6488, - 0.5159],
  22. [- 0.1590, - 2.1458, 0.0217, 0.4044],
  23. [ 1.0169, 0.8640, - 0.9029, 0.5957]],
  24. [[- 0.6277, 0.0611, - 1.3732, - 0.6897],
  25. [- 1.3523, 0.6712, 0.0491, 2.2301],
  26. [ 0.4627, 0.1737, 1.0111, - 1.4099],
  27. [ 0.1994, 0.2538, 0.5689, - 0.2558]]])
  28. # mask后的输入:
  29. tensor([[[ 5.2655e-01, - 1.0000e+10, - 1.0000e+10, - 1.0000e+10],
  30. [- 1.3146e+00, 1.6676e-01, - 1.0000e+10, - 1.0000e+10],
  31. [- 1.5899e-01, - 2.1458e+00, 2.1674e-02, - 1.0000e+10],
  32. [- 1.0000e+10, - 1.0000e+10, - 1.0000e+10, - 1.0000e+10]],
  33. [[- 6.2770e-01, - 1.0000e+10, - 1.0000e+10, - 1.0000e+10],
  34. [- 1.3523e+00, 6.7119e-01, - 1.0000e+10, - 1.0000e+10],
  35. [ 4.6272e-01, 1.7366e-01, 1.0111e+00, - 1.0000e+10],
  36. [ 1.9943e-01, 2.5381e-01, 5.6886e-01, - 2.5576e-01]]])

2. Decoder 中特征序列和目标序列之间的 Mask

该部分的 mask 代码对应结构图中的区域如下。这部分的 mask 涉及到目标序列和特征序列,在计算 self-attention 时,是目标序列的 query 和特征序列的 key、value 做计算。其中 key 和 value 是 Encoder 的输出query 是上一个 DecoderBlock 的输出

首先分别构造一个特征序列和一个目标序列,特征序列中第一句话有2个单词,第二句话有4个单词;目标序列中的第一句话有3个单词,第二句话有5个单词。

接下来就需要把特征序列和目标序列的长度各自给统一起来,将特征序列的所有句子都填充成4个单词,目标序列的所有句子都填充成5个单词有效单词区域的元素用 1 来表示,padding 的元素用 0 来表示

代码如下:


   
   
  1. # Decoder部分的目标序列对特征序列的muti-head-attention中的mask
  2. # 目标序列和特征序列之间的长度不一样,需要将原序列中和目标序列中padding后的元素mask掉
  3. import torch
  4. from torch import nn
  5. from torch.nn import functional as F
  6. # ------------------------------------------------------ #
  7. #(1)构造序列
  8. # ------------------------------------------------------ #
  9. src_len = torch.Tensor([ 2, 4]).to(torch.int32) # 特征序列中有两个句子,分别包含2、4个单词
  10. tgt_len = torch.Tensor([ 3, 5]).to(torch.int32) # 目标序列中有两个句子,分别包含3、5个单词
  11. # 对序列编码,有效单词位置的元素为1
  12. valid_src_pos = [torch.ones(L) for L in src_len] # 特征序列 [tensor([1., 1.]), tensor([1., 1., 1., 1.])]
  13. valid_tgt_pos = [torch.ones(L) for L in tgt_len] # 目标序列 [tensor([1., 1., 1.]), tensor([1., 1., 1., 1., 1.])]
  14. # 在计算时需要保证特征序列的长度和目标序列的长度一致,因此将每句话的单词数padding成相同长度
  15. max_src_len = max(src_len) # 将特征序列的单词数统一成4个
  16. max_tgt_len = max(tgt_len) # 将目标序列的单词数统一成5个
  17. new_valid_pos = [] # 保存padding后的特征序列和目标序列
  18. for sent in valid_src_pos: # 遍历每个特征句子
  19. sent = F.pad(sent, pad=( 0, max_src_len - len(sent))) # 将每句话的长度填充到4
  20. sent = torch.unsqueeze(sent, dim= 0) # 维度扩充 [max_src_len]==>[1, max_src_len]
  21. new_valid_pos.append(sent)
  22. for sent in valid_tgt_pos: # 遍历每个目标句子
  23. sent = F.pad(sent, pad=( 0, max_tgt_len - len(sent))) # 将每句话的长度填充到5
  24. sent = torch.unsqueeze(sent, dim= 0) # 维度扩充 [max_tgt_len]==>[1, max_tgt_len]
  25. new_valid_pos.append(sent)
  26. # 前两个句子属于特征序列,后两个句子属于目标序列。将列表类型在axis=0维度上堆叠
  27. valid_src_pos = torch.cat(new_valid_pos[: 2], dim= 0) # tensor([[1., 1., 0., 0.], [1., 1., 1., 1.]])
  28. valid_tgt_pos = torch.cat(new_valid_pos[ 2:], dim= 0) # tensor([[1., 1., 1., 0., 0.], [1., 1., 1., 1., 1.]])
  29. # ------------------------------------------------------ #
  30. #(2)构造mask
  31. # Q @ K^T 的shape为 [batch, tgt_seq_len, src_seq_len]
  32. # 因此mask的shape也为 [batch, tgt_seq_len, src_seq_len]
  33. # ------------------------------------------------------ #
  34. # 有效特征序列[2,4]==>[2,4,1], 有效目标序列[2,5]==>[2,5,1]
  35. valid_src_pos = torch.unsqueeze(valid_src_pos, dim=- 1) # 值为1的元素代表有效单词,值为0的元素代表padding后的区域
  36. valid_tgt_pos = torch.unsqueeze(valid_tgt_pos, dim=- 1)
  37. # 计算目标序列对特征序列有效性关系的矩阵,元素为0代表是padding后的单词
  38. # [b, tgt_seq_len, 1] @ [b, 1, src_seq_len] = [b, tgt_seq_len, src_seq_len]
  39. valid_cross_pos_matrix = torch.bmm(valid_tgt_pos, valid_src_pos.transpose( 1, 2))
  40. print( '有效关系矩阵:', valid_cross_pos_matrix) # torch.Size([2, 5, 4])
  41. # 得到无效矩阵,1代表需要mask的元素,变成布尔类型,True代表需要mask的元素
  42. invalid_cross_pos_matrix = 1 - valid_cross_pos_matrix
  43. invalid_cross_pos_matrix = invalid_cross_pos_matrix.to(torch. bool)
  44. print( 'mask矩阵:', invalid_cross_pos_matrix) # torch.Size([2, 5, 4])
  45. # ------------------------------------------------------ #
  46. #(3)对输入张量做mask
  47. # ------------------------------------------------------ #
  48. # 随机初始化一个 Q @ K^T 的计算结果 [batch, tgt_seq_len, src_seq_len]
  49. score = torch.randn( 2, 5, 4)
  50. # mask中True元素对应的score中的元素值变成一个非常小的数
  51. masked_score = torch.masked_fill(score, mask=invalid_cross_pos_matrix, value=- 1e10)
  52. print( '原输入:', score)
  53. print( '打上mask后的输入:', masked_score)

接下来构造 mask它的 shape 是和 Q@K^T 计算后的矩阵的 shape 相同,即 [batch, tgt_seq_len, src_seq_len],其中 tgt_seq_len 代表目标序列中每个句子包含多少个单词,src_seq_len 代表特征序列中每个句子包含多少个单词。

下面的第一个矩阵代表对目标序列和特征序列计算关系矩阵,元素为1代表有效单词,0 代表是经过padding 后得到的单词。

之后计算一个无效区域矩阵将所有 padding 得到的单词区域像素值变成 True,代表需要将这个元素 mask 掉。如下面的第二个矩阵。

然后构造一个和 self-attention 中 Q@K^T 计算结果 shape 相同的输入 source,如下面的第三个矩阵。

然后对输入 source 添加 mask将 mask 中元素 True 对应的 source 元素变成一个非常小的值,这样在梯度反向传播过程中 padding 的元素梯度更新非常小,降低 padding 区域对有效单词区域的影响。如下面的第四个矩阵。


   
   
  1. # 有效关系矩阵:
  2. tensor([[[ 1., 1., 0., 0.],
  3. [ 1., 1., 0., 0.],
  4. [ 1., 1., 0., 0.],
  5. [ 0., 0., 0., 0.],
  6. [ 0., 0., 0., 0.]],
  7. [[ 1., 1., 1., 1.],
  8. [ 1., 1., 1., 1.],
  9. [ 1., 1., 1., 1.],
  10. [ 1., 1., 1., 1.],
  11. [ 1., 1., 1., 1.]]])
  12. # mask矩阵:
  13. tensor([[[ False, False, True, True],
  14. [ False, False, True, True],
  15. [ False, False, True, True],
  16. [ True, True, True, True],
  17. [ True, True, True, True]],
  18. [[ False, False, False, False],
  19. [ False, False, False, False],
  20. [ False, False, False, False],
  21. [ False, False, False, False],
  22. [ False, False, False, False]]])
  23. # 原输入:
  24. tensor([[[ 1.4030, - 0.0176, - 2.9678, - 0.5551],
  25. [ 2.6138, - 0.8088, 0.6641, - 0.0128],
  26. [- 0.0370, - 0.3206, - 0.6634, 0.3626],
  27. [ 1.1978, 1.9831, - 0.3541, - 0.8766],
  28. [ 0.0655, 0.4267, - 0.3459, 1.8217]],
  29. [[- 0.2351, - 1.3515, 0.4783, - 0.9379],
  30. [ 0.2302, - 1.5482, - 0.0825, 1.0711],
  31. [- 0.3793, - 0.9595, 0.9457, - 1.5746],
  32. [ 0.3685, 1.1116, - 2.3528, - 0.3916],
  33. [- 1.2416, 0.9410, - 0.5407, 0.8035]]])
  34. # 打上mask后的输入:
  35. tensor([[[ 1.4030e+00, - 1.7624e-02, - 1.0000e+10, - 1.0000e+10],
  36. [ 2.6138e+00, - 8.0884e-01, - 1.0000e+10, - 1.0000e+10],
  37. [- 3.7038e-02, - 3.2057e-01, - 1.0000e+10, - 1.0000e+10],
  38. [- 1.0000e+10, - 1.0000e+10, - 1.0000e+10, - 1.0000e+10],
  39. [- 1.0000e+10, - 1.0000e+10, - 1.0000e+10, - 1.0000e+10]],
  40. [[- 2.3507e-01, - 1.3515e+00, 4.7825e-01, - 9.3789e-01],
  41. [ 2.3023e-01, - 1.5482e+00, - 8.2474e-02, 1.0711e+00],
  42. [- 3.7931e-01, - 9.5949e-01, 9.4568e-01, - 1.5746e+00],
  43. [ 3.6855e-01, 1.1116e+00, - 2.3528e+00, - 3.9157e-01],
  44. [- 1.2416e+00, 9.4099e-01, - 5.4066e-01, 8.0347e-01]]])
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值