深度学习入门--Transformer中的Positional Encoding详解

😁大家好,我是CuddleSabe,目前大四在读,深圳准入职算法工程师,研究主要方向为多模态(VQA、ImageCaptioning等),欢迎各位佬来讨论!
🍭我最近在有序地计划整理CV入门实战系列NLP入门实战系列。在这两个专栏中,我将会带领大家一步步进行经典网络算法的实现,欢迎各位读者(da lao)订阅🍀

Transformer中的Positional Encoding详解

为什么要有Postional Encoding

因为Self-Attention相对于传统的RNN在输入计算时没有输入先后顺序,而是采用并行化的思想来加快运算,这样Self-Attention在前一个token结果还没有出来的时候便可以同时处理下一个token。但是这样在并行化提升运算速度的同时也会带来一个问题,那便是丧失了序列的顺序性。因此为了不损失顺序性,在将序列输入之前还需要结合位置编码(Positional Encoding)。
在这里插入图片描述

Encoding的选择

这里我们先考虑最简单的一种Encoding方式:用token的序号来进行编码。
即:北京欢迎您,我爱它。
P E i = i PE_{i}=i PEi=i
那么这种最简单的Encoding结果为(不考虑标点符号):

tokenEncoded
‘北’0
‘京’1
‘欢’2
‘迎’3
‘您’4
‘我’5
‘爱’6
‘它’7

可以看到,这种编码方式的最明显缺点是:
对于特长序列,编码长度会不可控。

那么再看一种编码方式
P E i = i l e n PE_{i}=\frac{i}{len} PEi=leni
那么这样编码结果为(不考虑标点符号):

tokenEncoded
‘北’0 .000
‘京’0.125
‘欢’0.250
‘迎’0.375
‘您’0.500
‘我’0.625
‘爱’0.750
‘它’0.875
这样编码结果限制为0到1,
但同时也会带来一个新的问题,那就是编码结果的尺度随着不同序列长度变化也在变化。
例如,10个单词和100个单词的句子经过编码后,相邻单词的位置编码差距不是同一个数量级,这种尺度不统一的话,便无法进行训练。

因此,一种良好的位置编码方式需要达到以下两点:
1.位置编码长度不会随着序列长度而变化,而是限制在一定范围
2.位置序列的变化尺度需要统一

数学基础好的同学可能已经猜到了,满足这两条的便是———三角函数!

公式如下,如果想要详细解释可查看这篇文章:
地址:Positional Encoding详细解释

公式:
P E p o s , 2 i = s i n ( p o s / 1000 0 2 i / d ) PE_{pos,2i}=sin(pos/10000^{2i/d}) PEpos,2i=sin(pos/100002i/d)
P E p o s , 2 i + 1 = c o s ( p o s / 1000 0 2 i / d ) PE_{pos,2i+1}=cos(pos/10000^{2i/d}) PEpos,2i+1=cos(pos/100002i/d)
这里d为Embedding时的d_model参数,也是Embedding时一个单词的特征数,详细请看我的另一篇文章:
PyTorch文字处理及Embedding

Postional Encoding公式推导变换

我们已知公式如下,那么我们如果使用PyTorch实现呢?
P E p o s , 2 i = s i n ( p o s / 1000 0 2 i / d ) PE_{pos,2i}=sin(pos/10000^{2i/d}) PEpos,2i=sin(pos/100002i/d)
P E p o s , 2 i + 1 = c o s ( p o s / 1000 0 2 i / d ) PE_{pos,2i+1}=cos(pos/10000^{2i/d}) PEpos,2i+1=cos(pos/100002i/d)
这里我们需要将公式进行变换
我们设
T 2 i = 1 / 1000 0 2 i / d T_{2i}=1/10000^{2i/d} T2i=1/100002i/d T 2 i + 1 = 1 / 1000 0 2 i / d T_{2i+1}=1/10000^{2i/d} T2i+1=1/100002i/d
那么进一步,两边取对数
l n T = − 2 i d l n 10000 lnT=-\frac{2i}{d}ln10000 lnT=d2iln10000
进而
T = e − 2 i d l n 10000 T=e^{-\frac{2i}{d}ln10000} T=ed2iln10000
至于这里为什么不直接使用初始公式进行计算而是推导到现在,
个人理解是因为可以避免10000的幂作为分母而带来的计算机的计算误差。

实现代码

class PositionalEncoding(nn.Module):
    "实现PE功能"
    def __init__(self, d_model, dropout, max_len=5000):
        super(PositionalEncoding, self).__init__()
        self.dropout = nn.Dropout(p=dropout)
         
        pe = torch.zeros(max_len, d_model)
        position = torch.arange(0., max_len).unsqueeze(1)
        div_term = torch.exp(torch.arange(0., d_model, 2) *
                             -(math.log(10000.0) / d_model))
         
        pe[:, 0::2] = torch.sin(position * div_term)    # 偶数列
        pe[:, 1::2] = torch.cos(position * div_term)    # 奇数列
        pe = pe.unsqueeze(0)           # [1, max_len, d_model]
        self.register_buffer('pe', pe)
         
    def forward(self, x):
        x = x + Variable(self.pe[:, :x.size(1)], requires_grad=False)   # 这里是Embedding与Positional Encoding相结合
        return self.dropout(x)

编码可视化

将pe画出,得到以下图像

在这里插入图片描述

  • 12
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CuddleSabe

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值