Transformer详解之Embedding、Positional Encoding层(面向初学者)

1.Embedding层介绍

        在理解Embedding层之前,我们需要关注于文本数据的特点,举例:"我爱北京天安门",我们在做文本数据处理得时候,传统的汉字是没有办法做运算的,大家时刻要记住,计算机在处理任何数据的基础,就是能够进行运算。为了解决汉字运算的问题,提出了将汉字进行编码(或叫文本张量表示)的思想。目前主流的编码方式有one-hot编码及word Embedding。下面就两种编码进行介绍,其中穿插关于word2vec的理解。

1.1 one-hot编码

        该编码格式较为傻瓜式,就是将词库中所有的单词,从0到max_len的进行编号,使用哪个词对应的编号位置部分置1,其余部分置0。举例说明:如果整个词库为“我爱北京天安门”,依次对其进行编号,便会得到如下的表示:

我:1000 000        爱:0100 000        北:0010 000        京:0001 000        天:0000 100

安:0000 010        门:0000 001

        由上面的编码可知,只要得到词库,我们就能对所有的文字进行编码,用以运算,但是这种编码方式,存在一个非常大的问题,即编码的长度不是固定的,与词库有关,且经过one-hot得到的张量,存在大量的0,浪费内存。且文字与文字之间的关系无法得到体现。

1.2 什么是word2vec

        word2vec从字面意思能够看出来,即文本推导张量,它存在两个模式,一个是CBOW,一个是skipgram,其中CBOW类似于我们在英文考试中的完形填空,即根据上下文推导中间的单词。而skipgram与之相反,通过某个单词,推导上下文。可以通过fasttext实现word2vec的训练和使用。

1.3 理解什么是Embedding层及利用nn.Embedding创建时的参数含义

        了解了什么是one-hot编码后,发现了该编码方式的优点和缺点非常的突出,为了更高效的使用内存,更加合理的进行文本的编码,常常使用Embedding层进行文本的数字张量表示,这里我用白话的方式向大家进行讲解,文本只是一个符号,简单的理解文本的作用,即表示实体和实体之间的逻辑性的符号,但是实体与实体之间是存在联系的。如果我们把Embedding层理解成文本数据扩充维度的表现,是否可以更好的理解这部分内容呢。即如果有3句话,每句话有10个字,通过张量表示,则该张量的形状一定是(3, 10),可是如果我们把每个文字用60个数字来抽象的表示,那该文本的张量形状是不是变成了(3, 10, 60)其中60就是所谓的词嵌入维度。所以从字面上理解,只要给定词库的数字,以及要转化的词嵌入维度,我们就能够得到Embedding层的输出。

        即nn.Embedding(max_len, d_model),这样一个简单的Embedding层创建完毕。其中我们的max_len也可以写成vocab表示词表的大小。

2.Positional Encoding层

2.1 了解什么Positional Encoding层

        文本数据经过了Embedding层后,我们会得到一个三维的数据,第一维表示共有几句话, 第二位表示每句话有多少个字,第三维数据表示,每个字使用多少数据进行表示(也就是Embedding层的词嵌入维度d_model)。文本与文本之间不仅仅有逻辑关系,还存在一定的位置关系,例如:小红欺负小明,小红在前,说明小红是个施暴者。如果没有位置信息的填入,可能会出现小明欺负小红的完全相反的信息。所以在经过Embedding后的数据,要添加一个Positional Encoding层信息。Positional Encoding层信息有两种,一种是固定不变的,一种是可以进行训练的,通常我们使用torch.arange(0, max_len)创建一个1维的列表,然后通过unsqueeze(1),将列表变成形状为(max_len, 1)的数据,然后再使用unsqueeze(0),将列表形状变为(1, max_len, 1)的数据。然后再将张量的第二维下标为奇数的部分,进行math.sin()函数的变换,将张量第二维下标为偶数的部分进行math.cos()函数的变换。

        最后在将Positional Encoding层数据与Embedding层后的数据相加时,如果Positional Encoding数据不需要变换则直接相加,如果需要进行训练,则使用Variable()方法进行封装。然后再相加。这样关于Embedding层和Positional Encoding层的讲解到此完毕。

        其实应该添加代码供大家阅读,但因本人过于懒惰,就暂时简单的白话讲解了,这是本人第一个文章,有什么错误,和不正确的地方,欢迎各位大佬指正!!!

        

  • 30
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
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形式的替换。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值