Transformer的历史由来
Transformer模型是由Vaswani等人在2017年提出的,自其发布以来,已成为自然语言处理(NLP)领域中最具影响力的模型之一。Transformer模型最初是在论文《Attention Is All You Need》中介绍的,该论文提出了一种全新的基于注意力机制(Attention Mechanism)的深度学习架构,用于解决序列到序列(Seq2Seq)任务,比如机器翻译。
Attention is all you need
摘要
这一段主要为我们呈现的是,模型完全使用的是注意力机制,而没有用到任何传统的RNN和CNN架构,这一点也是本文的最大创新点
同时还为我们展现了本文提出的架构在具体Task的表现,表现效果好,训练速度较快
引言
这一段它主要讲的就是,传统的RNN,LSTM模型,无论怎么变换它都是一个连接一个的模型,也就是一种递归的结构,而本文提出的Transformer架构是不基于递归架构的
在这里也就是说,这个架构完全依赖于注意力机制来绘制输入和输出之间的全局依赖关系
模型
实际上Q,K,V就是对Input做的一个线性变换,换句话说就是矩阵相乘之后的结果
其中Wq,Wk,Wv都是可以用感知机来学习的
这个公式看起来很恐怖,直接来看图
这个图就是论文中多头注意力机制的原图,对V,K,Q的Linear层,文章说的是将它投影到较低的维度中,文章说他们发现这样更有益
接着进行刚才的注意力机制,h的意思是学习h次,我感觉有点多通道的意思
其实就看论文中的讲解,我还不是特别理解,我去网上查了一下相关的资料,查到了这张图片
我们可以有不同的Q,K,V,不同的Q可以负责不同种类的相关性
要获得不同的QKV可以通过感知机来学习
然后将两个生成的B接起来,和Wo进行相乘输出,对应前面concat的做法
代码
位置编码
class PositionalEncoding(nn.Module):
def __init__(self, d_model, max_seq_length):
"""
初始化位置编码
:param d_model: 词嵌入的维度
:param max_seq_length: 序列的最大长度
"""
super(PositionalEncoding, self).__init__()
# 初始化位置编码矩阵
self.encoding = torch.zeros(max_seq_length, d_model)
self.encoding.requires_grad = False
# 生成位置矩阵, unsqueeze使得维度为(max_seq_length,1)
position = torch.arange(0, max_seq_length).unsqueeze(1)
div_term = torch.exp(torch.arange(0, d_model, 2) * -(math.log(10000.0) / d_model))
# 正余弦部分
self.encoding[:, 0::2] = torch.sin(position * div_term)
self.encoding[:, 1::2] = torch.cos(position * div_term)
def forward(self, x):
"""
前向传播函数
:param x: 输入张量 (batch_size,seq_len,d_model)
:return: 加上位置编码的张量
"""
batch_size, seq_len, d_model = x.size()
return x + self.encoding[:seq_len, :].to(x.device)
在我们的位置编码中,我们仿照原论文的思路进行书写:
也就是说,在偶数的位置采用一个正弦的位置编码;在奇数的时候采用一个余弦的位置编码
多头注意力机制
class MultiHeadAttention(nn.Module):
def __init__(self, d_model, nhead):
"""
初始化多头注意力机制
:param d_model: 词嵌入的维度
:param nhead: 注意力头的数量
"""
super(MultiHeadAttention, self).__init__()
# 确保 d_model 可以被 nhead 整除
assert d_model % nhead == 0, "d_model must be divisible by nhead"
self.d_model = d_model
self.nhead = nhead
self.d_k = d_model // nhead
# 定义线性变换层
self.query = nn.Linear(d_model, d_model)
self.key = nn.Linear(d_model, d_model)
self.value = nn.Linear(d_model, d_model)
self.out = nn.Linear(d_model, d_model)
self.dropout = nn.Dropout(0.1)
def attention(self, query, key, value, mask=None):
"""
计算注意力分数
:param query: 查询张量,(batch_size,nhead,seq_len,d_k)
:param key: 键张量,(batch_size,nhead,seq_len,d_k)
:param value: 值张量,(batch_size,nhead,seq_len,d_k)
:param mask: 掩码张量,(batch_size,1,seq_len)
:return: 和value乘好的和注意力权重张量
"""
scores = torch.matmul(query, key.transpose(-2