当前位置:   article > 正文

多头注意力代码解读_tensorflow multiheadattention维度

tensorflow multiheadattention维度

 初始化阶段,

其中要注意的是 hid_dim要和Q、K、V词向量的长度相等

  1. import torch
  2. from torch import nn
  3. class MultiheadAttention(nn.Module):
  4. # n_heads:多头注意力的数量
  5. # hid_dim:每个词输出的向量维度
  6. def __init__(self, hid_dim, n_heads, dropout):
  7. super(MultiheadAttention, self).__init__()
  8. self.hid_dim = hid_dim
  9. self.n_heads = n_heads
  10. # 强制 hid_dim 必须整除 h
  11. assert hid_dim % n_heads == 0
  12. # 定义 W_q 矩阵
  13. self.w_q = nn.Linear(hid_dim, hid_dim)
  14. # 定义 W_k 矩阵
  15. self.w_k = nn.Linear(hid_dim, hid_dim)
  16. # 定义 W_v 矩阵
  17. self.w_v = nn.Linear(hid_dim, hid_dim)
  18. self.fc = nn.Linear(hid_dim, hid_dim)
  19. self.do = nn.Dropout(dropout)
  20. # 缩放
  21. self.scale = torch.sqrt(torch.FloatTensor([hid_dim // n_heads]))

前向传播阶段:

其中要注意的是需要将Q、K、V拆分为多组(num_head组)注意力【通过增加一个维度实现】,每组注意力词向量的长度为原来的 1/num_head 倍

  1. def forward(self, query, key, value, mask=None):
  2. # K: [64,10,300], batch_size 为 64,有 12 个词,每个词的 Query 向量是 300 维
  3. # V: [64,10,300], batch_size 为 64,有 10 个词,每个词的 Query 向量是 300 维
  4. # Q: [64,12,300], batch_size 为 64,有 10 个词,每个词的 Query 向量是 300 维
  5. bsz = query.shape[0]
  6. Q = self.w_q(query)
  7. K = self.w_k(key)
  8. V = self.w_v(value)
  9. # 这里把 K Q V 矩阵拆分为多组注意力,变成了一个 4 维的矩阵(增加了一个维度)
  10. # 最后一维就是是用 self.hid_dim // self.n_heads 来得到的,表示每组注意力的向量长度, 每个 head 的向量长度是:300/6=50
  11. # 64 表示 batch size,6 表示有 6组注意力,10 表示有 10 词,50 表示每组注意力的词的向量长度
  12. # Q: [64,12,300] 拆分多组注意力 -> [64,12,6,50] 转置得到 -> [64,6,12,50]
  13. # K: [64,10,300] 拆分多组注意力 -> [64,10,6,50] 转置得到 -> [64,6,10,50]
  14. # V: [64,10,300] 拆分多组注意力 -> [64,10,6,50] 转置得到 -> [64,6,10,50]
  15. # 转置是为了把注意力的数量 6 放到前面,把 10 和 50 放到后面,方便下面计算
  16. Q = Q.view(bsz, -1, self.n_heads, self.hid_dim // self.n_heads).permute(0, 2, 1, 3)
  17. K = K.view(bsz, -1, self.n_heads, self.hid_dim // self.n_heads).permute(0, 2, 1, 3)
  18. V = V.view(bsz, -1, self.n_heads, self.hid_dim // self.n_heads).permute(0, 2, 1, 3)
  19. # 第 1 步:Q 乘以 K的转置,除以scale
  20. # [64,6,12,50] * [64,6,50,10] = [64,6,12,10]
  21. # attention:[64,6,12,10]
  22. attention = torch.matmul(Q, K.permute(0, 1, 3, 2)) / self.scale
  23. # 把 mask 不为空,那么就把 mask 为 0 的位置的 attention 分数设置为 -1e10
  24. if mask is not None:
  25. attention = attention.masked_fill(mask == 0, -1e10)
  26. # 第 2 步:计算上一步结果的 softmax,再经过 dropout,得到 attention。
  27. # 注意,这里是对最后一维做 softmax,也就是在输入序列的维度做 softmax
  28. # attention: [64,6,12,10]
  29. attention = self.do(torch.softmax(attention, dim=-1))
  30. # 第三步,attention结果与V相乘,得到多头注意力的结果
  31. # [64,6,12,10] * [64,6,10,50] = [64,6,12,50]
  32. # x: [64,6,12,50]
  33. x = torch.matmul(attention, V)
  34. # 因为 query 有 12 个词,所以把 12 放到前面,把 6 和 50 放到后面(即将6个头的注意力拼接起来),方便下面拼接多组的结果
  35. # x: [64,6,12,50] 转置-> [64,12,6,50]
  36. x = x.permute(0, 2, 1, 3).contiguous()
  37. # 这里的矩阵转换就是:把多组注意力的结果拼接起来
  38. # 最终结果就是 [64,12,300]
  39. # x: [64,12,6,50] -> [64,12,300]
  40. x = x.view(bsz, -1, self.n_heads * (self.hid_dim // self.n_heads))
  41. x = self.fc(x)
  42. return x

测试使用:

  1. # batch_size 为 64,有 12 个词,每个词的 Query 向量是 300 维
  2. query = torch.rand(64, 12, 300)
  3. # batch_size 为 64,有 10 个词,每个词的 Key 向量是 300 维
  4. key = torch.rand(64, 10, 300)
  5. # batch_size 为 64,有 10 个词,每个词的 Value 向量是 300 维
  6. value = torch.rand(64, 10, 300)
  7. # 1、初始化过程(最终输出的维度,这个维度需要和词向量的维度一样(也就是要和Q、K、V的词向量维度一致,即hid_dim要和Q、K、V的词向量维度相等=300))
  8. attention = MultiheadAttention(hid_dim=300, n_heads=6, dropout=0.1)
  9. # 2、forward过程
  10. output = attention(query, key, value)
  11. ## output: torch.Size([64, 12, 300])
  12. print(output.shape)

在实际中,K、V 矩阵的序列长度是一样的,而 Q 矩阵的序列长度可以不一样。

这种情况发生在:在解码器部分的Encoder-Decoder Attention层中,Q 矩阵是来自解码器下层,而 K、V 矩阵则是来自编码器的输出。

transformer多头注意力的不同框架实现(tensorflow+pytorch) - 西西嘛呦 - 博客园

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号