Transformer的位置编码笔记(positional encoding)

一、为什么Transformer需要对输入进行位置编码

因为Transformer的输入并没有内涵位置信息,同样的词在不同位置,或者同一个序列以不同顺序输入,对应的词间都会得到相同的注意力权重和输出,但是在NLP领域,词的顺序会极大地影响句子的含义。

句子1:小明喜欢上了小红

句子2:小红喜欢上了小明

对于这两个句子,分词、embedding处理后得到的词向量是相同的,都是“小红”、“小明”、“喜欢”、“上”、“了” 对应的词向量,它们会以下图的方式计算self-attention

图一

在输入Transformer模型时,只是词向量的顺序不同,但是在计算self-attention时(以“小红”为例,用小红的q,分别与其他token的k相乘,计算相关性a,然后在呈上相应的v的到向量)

图二

所以,哪怕是顺序不同,每个token对应的词向量b 依旧不变,这个问题RNN就不需要考虑,因为在RNN模型中,token是一个接一个进入其中的,它们进入的顺序已经隐含了位置信息。

二、Transformer的Positional Encoding是如何进行确定位置信息的呢?

首先说一下Transformer中位置编码是使用正余弦函数进行编码的,公式如下:

看到这个,肯定会有小伙伴提出疑问了,为什么是sin()、cos()函数呢?为什么里面是10000呢?小小脑袋,大大问号❓❓❓ 别急别急,咱们一个一个来。

1、为什么是sin()、cos()函数呢?

正如图二所示,在求self-attention时,先求qi与kj相乘,求贡献力度aj,然后再用贡献力度aj 乘 vj,之后在相加,得出第i个输入对应的encoder输出。(艾玛,太抽象了,建议看图二)

可以看到,q * k展开之后的第二项,可以理解为绝对位置,就是token(i)和token(j)分别对应的位置P(i)、P(j),,,最后一项,既包括pi也包括pj,和两个位置都有关。

这能说明式子展开之后,已经具备了绝对位置信息,还没有具备相对位置信息,但是有一项表达式和两个位置都有关(最后一项),那我们能不能将最后一项通过人为设定P(x)的表达式,让其隐含相对信息呢?(为什么要希望同时具备绝对位置、相对位置?因为这样更能表示两个变量之间的具体位置呀)

答案是肯定的,这就是位置编码的由来,也就是上面那个公式,具体推导如下:

那么如何才能实现简化之后的式子呢??? 主要是这个g(x)函数怎么构造呢,当时的研究人员就绞尽脑汁的想啊想啊,突然灵光一现,想到了正余弦函数:

这样使用正余弦函数,恰好能满足g(x)的需求,能完成我们的设想。这就是正余弦函数的由来。👏👏👏

那对于多维的编码来说呢,由于内积满足叠加性,更高维度的编码可以看成两两一组的结合:

  1. 为什么里面是呢?

那么通过一个相似公式,我们就把问题转换为积分的渐进估计问题了,把这积分的图像画出来,观察一下:

随着(m-n)的增大,也就是相对距离的增大,积分结果逐渐下降。

可以看到,这里做了很多实验,验证那个数值比较好一些,但是像10000对应的橙色的曲线一定是最好的吗?好像并不是,,,如果让我们选的话,好像1000对应的 蓝线 更好一些。 随意这个就是研究人员选取的,没必要太过于纠结为什么是10000.

  • 21
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
Swin Transformer使用的是Learned Positional Encoding,如果要将其替换为Sinusoidal Positional Encoding,需要进行一些修改。 首先,可以定义一个Sinusoidal Positional Encoding函数,如下所示: ```python import math import torch import torch.nn as nn class SinusoidalPositionalEmbedding(nn.Module): def __init__(self, d_model, max_len=512): super().__init__() self.d_model = d_model self.max_len = max_len pe = torch.zeros(max_len, d_model) position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1) div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-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).transpose(0, 1) self.register_buffer('pe', pe) def forward(self, x): x = x * math.sqrt(self.d_model) seq_len = x.size(1) pe = self.pe[:seq_len, :] pe = pe.repeat(x.size(0), 1, 1) x = x + pe.to(x.device) return x ``` 然后,在Swin Transformer的构造函数中,将使用Learned Positional Encoding的部分替换为Sinusoidal Positional Encoding,如下所示: ```python import torch import torch.nn as nn from einops.layers.torch import Rearrange class SwinTransformer(nn.Module): def __init__(self, img_size=224, patch_size=4, in_chans=3, num_classes=1000, embed_dim=96, depths=[2, 2, 18, 2], num_heads=[3, 6, 12, 24], window_size=7, mlp_ratio=4., qkv_bias=True, qk_scale=None, drop_rate=0., attn_drop_rate=0., drop_path_rate=0.): super().__init__() norm_layer = nn.LayerNorm self.num_classes = num_classes self.num_features = self.embed_dim = embed_dim # stochastic depth decay rule dpr = [x.item() for x in torch.linspace(0, drop_path_rate, sum(depths))] # stochastic depth decay rule # patch embedding self.patch_embed = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=patch_size) self.norm1 = norm_layer(embed_dim) # pos embedding self.pos_embed = SinusoidalPositionalEmbedding(embed_dim, max_len=(img_size//patch_size)**2+1) # swin transformer blocks self.blocks = nn.ModuleList([ SwinTransformerBlock(dim=embed_dim, num_heads=num_heads[i], window_size=window_size, shift_size=window_size // 2 if i == 0 else 0, mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, qk_scale=qk_scale, drop=drop_rate, attn_drop=attn_drop_rate, drop_path=dpr[sum(depths[:i]):sum(depths[:i+1])]) for i in range(len(depths))]) # norm before classifier self.norm2 = norm_layer(embed_dim) # classification head self.head = nn.Linear(embed_dim, num_classes) if num_classes > 0 else nn.Identity() def forward_features(self, x): x = self.patch_embed(x) x = self.norm1(x) x = x.flatten(2).transpose(1, 2) x = self.pos_embed(x) for i, blk in enumerate(self.blocks): x = blk(x) x = self.norm2(x) return x def forward(self, x): x = self.forward_features(x) x = x.mean(dim=1) # global average pooling x = self.head(x) return x ``` 这样,就完成了Swin Transformer模型中Positional Encoding形式的替换。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值