不会NLP的人记录的Transformer笔记。
Seq2Seq
普通Seq2Seq
以翻译来说,就是中文通过编码器得到了一个上下文编码向量,然后通过解码器就可以翻译为英文
编码器和解码器在早期是可以获取时序信息的RNN模块,后来引入了LSTM【输入/遗忘/输出门】、GRU【LSTM的变种,更新/重置门】模块【通过门来控制选择保留的特征】
缺点
- 上下文编码向量是固定长度的,如果长度过长,会导致不合理
- 当时间过长,仍可能会出现信息丢失的问题
基于Attention的Seq2Seq
增加注意力机制,通过对输入的自适应加权,聚焦到关键信息。
流程
编码
大概流程就是将Query(向量)和Key(和Q长度相同的向量)计算相似性,再通过softmax得到相似性的概率权重分布。对应权重乘以value(和Q长度相同的向量),最后相加,就可以得到包含注意力的attention值输出。
看到的一个例子:
- 假设世界上所有小吃都可以被标签化,例如微辣、特辣、变态辣、微甜、有嚼劲….,总共有1000个标签,现在我想要吃的小吃是[微辣、微甜、有嚼劲],这就是我的Query,有没有一个微辣的、微甜的且有嚼劲的食物呀?
- 每个店铺卖的东西不一样,但是肯定可以被标签化,例如第一家小吃被标签化后是[微辣、微咸],第二家小吃被标签化后是[特辣、微臭、特咸],第二家小吃被标签化后是[特辣、微甜、特咸、有嚼劲],其余店铺都可以被标签化,每个店铺的标签就是Keys,但是每家店铺由于卖的东西不一样,单品种类也不一样,所以被标签化后每一家的标签List不一样长【但是描述这个特征的维度必须相同】
- Values就是每家店铺对应的单品,例如第一家小吃的Values是[烤羊肉串、炒花生]
- 将Query和所有的Keys进行一一比对,相当于计算相似性,此时就可以知道我想买的小吃和每一家店铺的匹配情况,最后有了匹配列表,就可以去店铺里面买东西了(Values和相似性加权求和)。最终的情况可能是,我在第一家店铺买了烤羊肉串,然后在第10家店铺买了个玉米,最后在第15家店铺买了个烤面筋。最终去不同店铺买不同东西的过程就是权重和Values加权求和过程。
|
|
解码
- 将第一个开启解码标志
<START>
(也就是Q)与编码器的每一个时间步的隐含状态(一系列Key和Value)进行点乘计算相似性得到每一时间步的相似性分数 - 通过softmax转化为概率分布
- 将概率分布和对应位置向量进行加权求和得到新的上下文向量
- 将上下文向量输入解码器中进行解码输出
缺点
- 不管是采用RNN、LSTM还是GRU都不利于并行训练和推理,因为相关算法只能从左向右依次计算或者从右向左依次计算
- 长依赖信息丢失问题,顺序计算过程中信息会丢失,虽然LSTM号称有缓解,但是无法彻底解决
基于transformer的Seq2Seq
Attention
解决的问题
- 前馈网络和循环网络虽然具有很强的能力,但是因为
- 需要记住很多“信息”,使得模型变得更复杂,同时计算能力限制了它的继续发展
- 虽然局部连接、权重共享以及Pooling,让神经网络更加简单,但是信息的“记忆”能力并不高
- 两者都是局部编码:CNN基于N-gram,RNN由于梯度消失问题只能建立短距离依赖
- 全连接网络虽然可以获取远距离的信息交互,但是无法处理变长的输入序列;
优势
- 允许解码器以不同程度的权重利用编码器的所有信息,可以绕过瓶颈
- 注意力分布可以反映解码器关注什么,只选择一些关键信息,减少模型的复杂度,可解释性强
- 【自注意力】可以动态地生成不同连接的权重,处理变长的信息序列
分类
聚焦式Focus
是一种自上而下的有意识的注意力,主动注意【依赖于任务】
显著性saliency-based
是一种自下而上的有意识的注意力,被动注意【由外界刺激驱动的注意,可以和任务无关】
Max-Pooling
门控Gating机制
几个类别
Soft attention
所有输入信息在注意力分布下的期望
Q和所有K分别计算 【注意力打分机制】
加性模型
点积模型
缩放点积模型
双线性模型
对相似性采用softmax转换为概率分布【注意力概率分布】,表示第i个信息受关注的程度
将概率分布和V进行一一对应相乘【加权平均】
普通模式:Key=Value
键值对模式:Key!= Value
最后相加得到新的和Q一样长的输出向量
Hard attention
只关注到某一个位置上的信息;一般用强化学习来训练
- 选取最高概率的输入信息
- 在注意力分布式上随机采样
缺点
- 最大采样或随机采样导致最终的损失函数与注意力分布之间的函数关系不可导,无法使用反向传播。
Self-attention
主要是输入的不同,QKV都是同一个输入序列的不同线性表示;整体可以表示为:【都是矩阵的乘法操作,可以用GPU加速】
因此时间复杂度为,因为(n, d)*(d, n) -> n^2d, softmax(n, n) -> n^2, (n, n)*(n,d) -> n^2d
自注意力机制的QKV是通过线性变换得到的,X是输入序列;self体现在Q是对自身的一个输入变换【传统的attention中,Q来自于外部】。
通常使用缩放点积作为注意力打分函数(d是输入X的维度)
用softmax得到概率分布
加权平均得到注意力值:
One-head attention
算法
|
|
Multi-headed attention
利用多个查询来平行地计算从输入信息中选取多个信息。每个注意力关注输入信息的不同部分,然后再拼接。
特点
- 类似于分组操作,分别得到输出后,将结果concat
- 加入了残差设计和层归一化,防止梯度消失,加快收敛
优点
- 扩展了模型专注于不同位置的能力
- 给出了注意力层多个“表示子空间”,对于“多头”注意力机制,有多个Query/Key/Value权重矩阵集
算法
|
|
Transformer
优点
- 可解释性强
- 并行处理,速度快
结构
|
|
Input
单词嵌入向量和位置编码向量相加就是编码器的输入了(batch, M, 512)
Embedding
Word2Vec
Positional encoding
transformer内部没有类似RNN的循环结构,没有捕捉顺序序列的能力,positional encoding补充了这个缺陷。
网络自动学习
self.pos_embedding = nn.Parameter(torch.randn(batch, N, 512))
; 其中N是N个位置,512是用来表示坐标特征的维度自定义
- 直接将绝对坐标编码为向量
- sin-cos规则:将向量的512维度切分为奇数行和偶数行;奇数行采用cos函数编码、偶数行采用sin函数编码;按照原始行号拼接
- 由于以及,因此编码后的可以用来表示;这样网络可以较好地学习相对位置信息
Encoder
|
|
Self-attention
QKV来自同一个输入的attention操作就是自注意力。softmax(Q*K^T/\sqrt(d_k))*v=z
;
Feed Foward
进行空间变换,引入了非线性激活函数,增加了模型的表现能力。
|
|
Decoder
解码器的输入不仅包括最后一个后一个编码器的输出,还有前面所有解码器的输入【对于第一个解码器,则还需要额外的输出嵌入向量——目标单词的嵌入向量】
|
|
Postion Embedding
与编码器的不同:
- 解码器的位置嵌入是在进行目标单词嵌入之前;而编码器是在输入单词嵌入之后
- 解码器需要右移一位,新增加的一个位置采用定义好的标志位BOS_WORD代替【开始解码标志】
Feed Forward
由于想要并行运算,但是我们在第3个编码器的时候,只可以用前两个编码器的输出,而不可以使用后面编码器的结果,否则就是一种作弊的现象。因此用mask遮住了。
屏蔽部分在编码器中已经实现了。
循环解码过程:
- 第一次解码:输入BOS_WORD单词嵌入向量,假设是(1,256),而编码器输出始终不变是(100,256),那么第一次解码过程是(1,256)+位置编码作为解码器输入,解码输出是(1,256),经过fc层(参数shape是(256,10000))变成(1,10000),10000是单词总数,此时就可以解码得到第一个单词i;
- 接着将BOS_WORD和i都进行嵌入,得到(2,256)输入,同样运行,输出是(2,256),经过fc是(2,10000),此时不需要第一个维度输出只需要[-1,10000]既可以解码第二个单词,后面就一直迭代直到输出结束解码标注。
Classification Layer
|
|
与CV的结合
分类
An Image is Worth 16x16 Words:Transformers for Image Recognition at Scale
特点
- 彻底抛弃CNN,RNN
- 需要大量的数据进行训练,才会超过CNN,但是对应的训练时间也会增长很多
- 应用transformer的可解释性强
结构
将图片分块,切分为固定的patch个数
将patches拉成序列(flatten),采用了爱因斯坦表达式(用einops库的rearrange实现)
1x = rearrange(img, 'b c (h p1) (w p2) -> b (h w) (p1 p2 c)', p1=p, p2=p)- 输入为
(batch, 3, h, w)
- 分为n个patch,那么每个patch就是
h/sqrt(n), w/sqrt(n))
像素 - rearrange操作即先变成
(batch, 3, sqrt(n)*h/sqrt(n), sqrt(n)*w/sqrt(n))
,最后变成(batch, n, h*w/n*3)
- 可以看作有n个单词向量,每个向量由
h*w/n*3
个元素编码表示
- 输入为
对每一个单词向量的表示进行降维
12self.patch_to_embedding = nn.Linear(patch_dim, dim)x = self.patch_to_embedding(x)在flatten前增加一个开启解码的标志,这个标志是可学习的【在NLP中是固定的】
1234self.cls_token = nn.Parameter(torch.randn(1, 1, dim))cls_tokens = repeat(self.cls_token, '() n d -> b n d', b=b)# 额外追加token,变成(batch, n+1, dim)x = torch.cat((cls_tokens, x), dim=1)- 因为这篇论文在编码之后就进行分类,没有解码器,因此在这一部分增加了解码的一部分操作
上述是一个图像内容的编码,接下来还需要对位置信息进行编码;在论文中设置为了可学习的;相邻位置具有相近的位置编码变量
12# 加一同样是因为增加了解码标志self.pos_embedding = nn.Parameter(torch.randn(1, num_patches + 1, dim))将patch嵌入向量和位置编码向量相加后作为编码器的输入
123x += self.pos_embedding[:, :(n + 1)]x = self.dropout(x)self.transformer = Transformer(dim, depth, heads, mlp_dim, dropout)编码器的输出结果,传入FC分类器
12345678910self.mlp_head = nn.Sequential(nn.LayerNorm(dim),nn.Linear(dim, mlp_dim),nn.GELU(),nn.Dropout(dropout),nn.Linear(mlp_dim, num_classes))# 65个输出里面只需要第0个输出进行后续分类即可self.mlp_head(x[:, 0])
OD
DETR
见博文OD with new tech
.
CS224N
转载请注明出处,谢谢。
愿 我是你的小太阳