Transformer原理及解析

x# Transformer

视频链接:https://www.youtube.com/watch?v=ugWDIIOHtPA&t=2055s

transformer是一个拥有self-attention的Sequence2Sequence模型,

Sequence

在这里插入图片描述

RNN是常用的Sequence模型,在双向的sequence模型中,b1 ,b2 的输入为a1到a4 ,适用于解决序列化的问题常用语音和NLP领域。但是序列化执行难以并行处理。希望采用CNN来替代RNN,实现并行化处理,然而CNN只有高层的filter的感受野比较大,能考虑长的序列。

self-attention

在这里插入图片描述

用self-attention来代替RNN,既能考虑长的序列,有能便于并行化。

如何做self-attention?

在这里插入图片描述

  1. 输入的变量为xi ,首先对xi做编码 得到编码后的向量 ai
class Embeddings(nn.Module):
    def __init__(self, d_model, vocab):
        super(Embeddings, self).__init__()
        self.lut = nn.Embedding(vocab, d_model)

    def forward(self, x):
        return self.lut(x) 

nn.Embedding是一个l固定字典lookup的表格,初始化的输入为单词数vocab和编码向量维度d_model。

  1. 对向量ai 分别得到 用于attention的三元组(q,k,v)

  2. 拿每个query去对每个key做attention,用点乘计算: α 1 , i = q 1 ⋅ k i / d \alpha_{1,i}=q^1\cdot k^i/\sqrt{d} α1,i=q1ki/d <img
    在这里插入图片描述

    attention之后用softmax归一化: α ^ 1 , i = e x p ( α 1 , i ) / ∑ j e x p ( α 1 , j ) \hat{\alpha}_{1,i}=exp(\alpha_{1,i})/\sum_j exp(\alpha_{1,j}) α^1,i=exp(α1,i)/jexp(α1,j)

在这里插入图片描述

之后通过加权平均得到最后的输出: b 1 = ∑ i α ^ 1 , i v i b^1=\sum_i\hat{\alpha}_{1,i}v^i b1=iα^1,ivi

在这里插入图片描述

def attention(query, key, value, mask=None, dropout=None):
	d_k = query.size(-1)
	scores = torch.matmul(query, key.transpose(-2, -1)) \
		/ math.sqrt(d_k)
	if mask is not None:
		scores = scores.masked_fill(mask == 0, -1e9)
	p_attn = F.softmax(scores, dim = -1)
	if dropout is not None:
		p_attn = dropout(p_attn)
	return torch.matmul(p_attn, value), p_attn

在实际实验中query,key,value维度为(batch, head个数,序列长度,特征数量)。通过矩阵乘法后scores的维度是(batch,head个数,序列长度,序列长度)。matmul会把query和key的最后两维进行矩阵乘法,这样效率更高。

为什么易于并行?

在这里插入图片描述

可以将每个时刻的输入做concat操作,这样直接通过一个矩阵乘法能够得到所有的值

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

所有的计算都是矩阵乘法,所以易于并行化。

Multi-head self-attention

每个head关注的东西可能不同

在这里插入图片描述

class MultiHeadedAttention(nn.Module):
	def __init__(self, h, d_model, dropout=0.1):
		super(MultiHeadedAttention, self).__init__()
		assert d_model % h == 0
		# We assume d_v always equals d_k
		self.d_k = d_model // h
		self.h = h
		self.linears = clones(nn.Linear(d_model, d_model), 4)
		self.attn = None
		self.dropout = nn.Dropout(p=dropout)
	
	def forward(self, query, key, value, mask=None): 
		if mask is not None:
			# 所有h个head的mask都是相同的 
			mask = mask.unsqueeze(1)
		nbatches = query.size(0)
		
		# 1) 首先使用线性变换,然后把d_model分配给h个Head,每个head为d_k=d_model/h 
		query, key, value = \
			[l(x).view(nbatches, -1, self.h, self.d_k).transpose(1, 2)	
				for l, x in zip(self.linears, (query, key, value))]
		
		# 2) 使用attention函数计算
		x, self.attn = attention(query, key, value, mask=mask, 
			dropout=self.dropout)
		
		# 3) 把8个head的64维向量拼接成一个512的向量。然后再使用一个线性变换(512,521),shape不变。 
		x = x.transpose(1, 2).contiguous() \
			.view(nbatches, -1, self.h * self.d_k)
		return self.linears[-1](x)

先看构造函数,这里d_model(512)是Multi-Head的输出大小,因为有h(8)个head,因此每个head的d_k=512/8=64。接着我们构造4个(d_model x d_model)的矩阵,后面我们会看到它的用处。最后是构造一个Dropout层。

然后我们来看forward方法。输入的mask是(batch, 1, time)的,因为每个head的mask都是一样的,所以先用unsqueeze(1)变成(batch, 1, 1, time),mask我们前面已经详细分析过了。

接下来是根据输入query,key和value计算变换后的Multi-Head的query,key和value。这是通过下面的语句来实现的:

query, key, value = \
		[l(x).view(nbatches, -1, self.h, self.d_k).transpose(1, 2)	
			for l, x in zip(self.linears, (query, key, value))]

zip(self.linears, (query, key, value))是把(self.linears[0],self.linears[1],self.linears[2])和(query, key, value)放到一起然后遍历。我们只看一个self.linears[0] (query)。根据构造函数的定义,self.linears[0]是一个(512, 512)的矩阵,而query是(batch, time, 512),相乘之后得到的新query还是512(d_model)维的向量,然后用view把它变成(batch, time, 8, 64)。然后transponse成(batch, 8,time,64),这是attention函数要求的shape。分别对应8个Head,每个Head的Query都是64维。

Key和Value的运算完全相同,因此我们也分别得到8个Head的64维的Key和64维的Value。接下来调用attention函数,得到x和self.attn。其中x的shape是(batch, 8, time, 64),而attn是(batch, 8, time, time)。

x.transpose(1, 2)把x变成(batch, time, 8, 64),然后把它view成(batch, time, 512),其实就是把最后8个64维的向量拼接成512的向量。最后使用self.linears[-1]对x进行线性变换,self.linears[-1]是(512, 512)的,因此最终的输出还是(batch, time, 512)。我们最初构造了4个(512, 512)的矩阵,前3个用于对query,key和value进行变换,而最后一个对8个head拼接后的向量再做一次变换。
Positional Encoding

在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值