Transformer

前言

transformer网上的资料已经非常多了,这里主要是做笔记,仅对自己可见。博客主要参考李宏毅课程视频:https://www.youtube.com/watch?v=ugWDIIOHtPA&t=538s

self-attention

先说self-attention,总体步骤如下:

1、从词向量中获取三个不同的向量 q、k、v(图片来均来自于李宏毅课程视频):

这里可以将 a i a^i ai 理解为一个词向量,其中,k:query(to match others),k:key(to be matched),v:information to be extracted,它们通过下面式子得到:
q i = W q a i q^i = W^q a^i qi=Wqai k i = W k a i k^i = W^k a^i ki=Wkai v i = W v a i v^i = W^v a^i vi=Wvai
其中 W W W 是不同的矩阵,代码上可以用pytorch这么实现:

        self.q_linear = Linear(inp_dim, d_model)
        self.v_linear = Linear(inp_dim, d_model)
        self.k_linear = Linear(inp_dim, d_model)
        
        q = self.q_linear(a)
		k = self.k_linear(a)
        v = self.v_linear(a)

2、然后用每一个 q 对每一个 v 做一个 attention ,也就是一个点乘运算,即:

公式的形式为:
α 1 , i = q 1 ∗ k i / d \alpha _ {1, i} = q^1 * k^i / \sqrt d α1,i=q1ki/d
这里的 d 是 q 和 v 的维度,之所以有这个 d 是因为,向量的维度越长,那么值也就越大,所以加上这个 d 来平衡。然后我们将结果做一个 softmax :

同样,附上代码更容易懂(score 代表的就是 α ^ \hat{\alpha} α^):

    scores = torch.matmul(q, k.transpose(-2, -1)) /  math.sqrt(d_k)
    scores = F.softmax(scores, dim=-1)

3、得到了 α ^ \hat \alpha α^ 后,我们再拿每一个 α ^ \hat \alpha α^ v v v 相乘,然后每个位置求和,也即:
b j = ∑ i α ^ j , i v i b^j = \sum_{i} \hat{\alpha}_{j, i} v^i bj=iα^j,ivi
直观的理解如下图:

这样,我们就完成的从 a 1 a^1 a1 b 1 b^1 b1 的过程了,同理,其他的位置也是相同的,他们可以并行的运算。这里考虑另外一个问题,假如在计算 b 1 b^1 b1 的时候,我不想看到后面的 a 4 a^4 a4 的信息,这个可以通过 MASK 来解决,就是将后面的数值设为 0,代码实现过程如下:

    scores = torch.matmul(q, k.transpose(-2, -1)) /  math.sqrt(d_k)
    
    if mask is not None:
        scores = scores.masked_fill(mask == 0, -1e9) #这里相当于将mask掉的位置参数设为 0
    
    scores = F.softmax(scores, dim=-1)

    output = torch.matmul(scores, v)   

4、self-attention 平行化,之前的运行可以通过矩阵运算来平行化计算:
q i = W q a i q^i = W^q a^i qi=Wqai
当所有的 a a a 一起运算时:
Q = W q A Q = W^q A Q=WqA
其他的步骤可以类似的并行运算。

5、如果上面的没看懂,可以简单地理解为输入了一个序列,并行地输出了另一个序列:

Multi-head Self-attention

之前每个 a 都是分出一个 q 、k、v,Multi-head 的意思是每个 a 分出多个 q、k、v,其他运算过程一模一样,如下图所示:

代码实现也很简单:

        self.q_linear = Linear(inp_dim, d_model)
        self.v_linear = Linear(inp_dim, d_model)
        self.k_linear = Linear(inp_dim, d_model)
        
        q = self.q_linear(a).view(-1, heads, d_model//heads)
		k = self.k_linear(a).view(-1, heads, d_model//heads)
        v = self.v_linear(a).view(-1, heads, d_model//heads)
        #再将 heads 转置到前面
        q = q.transpose(-2, -3) 
        。。。

这样经过同样的运算后,将几个 heads 的结果拼起来就可以了,代码上实现很简单,用一个 reshape 就可以了,这里就不写了。

Positional Encoding

之前的运算,我们可以看出,它是并行计算了,所以每个词的位置相当于是等价的。但是实际问题中,我们是需要考虑词的相对位置信息的。所以需要额外加一个位置信息,原始 paper 中用的是将每一个 a i a^i ai 加上一个位置向量 e i e^i ei,这个位置向量它直接给出来了,代码如下:

        pe = torch.zeros(max_seq_len, d_model)
        for pos in range(max_seq_len):
            for i in range(0, d_model, 2):
                pe[pos, i] = math.sin(pos / (10000 ** ((2 * i)/d_model)))
                pe[pos, i + 1] = math.cos(pos / (10000 ** ((2 * (i + 1))/d_model)))

例如,输入三个词,维度为4,那么起结果为(图片来自https://www.jianshu.com/p/e7d8caa13b21):

Transformer

1、直接上整体的结构图:

这个图里的核心部分上面已经都写过了,除了 【Add & Norm】和【Feed Forward】,接下来来看这两块实现了什么功能。

2、先说 【Add & Norm】, Add 就是常规的相加操作,Norm 是 layer normalization,这里就不展开讲了,简单的代码实现如下:

# 其中 alpha 和 bias 为学习的参数,eps 是一个很小的值
norm = alpha * (x - x.mean()) / (x.std() + eps) + bias

3、再来看【Feed Forward】,这一层看名字就知道怎么做了,但是它在这个模型中非常重要,因为之前讲的都是线性变化,只有这一层提供了激活函数

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值