详细通俗讲解Transformer结构以及训练、推理流程

该文章已生成可运行项目,

一、模型简介

Transformer 是一种基于自注意力机制(Self-Attention)的深度学习模型架构,其完全抛弃了传统的循环结构(如 RNN、LSTM),通过注意力机制和并行计算,高效建模长距离依赖关系,在多项自然语言处理任务中取得了突破性成果。

Transformer 的提出奠定了后续一系列预训练语言模型的基础。基于 Transformer 架构,研究者发展出了 BERT(用于理解任务)、GPT 系列(用于生成任务)、T5、BART 等模型,这些模型广泛应用于机器翻译、文本摘要、问答系统、情感分析等领域,成为自然语言处理的主流范式。

随着多模态模型的发展(如图文、语音、视频的结合),Transformer 架构也被不断扩展和适配,应用范围除了ChatGPT等大模型外,已超越文本,广泛进入计算机视觉、语音识别、机器人控制、蛋白质结构预测等多个领域,展现出极强的通用性和扩展能力。

二、总体架构

Transformer分为Encoder(编码器)和Decoder(解码器)两部分:

其中Encoder部分由输入、Encoder blocks组成,Decoder部分由输入、Decoder blocks、输出组成:

需要注意的是,Encoder block和Decoder block各重复了N次(一般为6)

三、训练及推理流程

推理流程

以翻译“我爱你”为例,先将Transformer当作一个黑箱,只研究输入输出,Transformer简化模型图如下

在这里插入图片描述

Encoder的输入就是要翻译的文本“我爱你”

Decoder的输入是当前已经翻译出的话

Transformer翻译时是自回归生成,即一个词一个词地翻译:

比方说翻译“我爱你”时是先翻译出“I”,再翻译出“Love”,再翻译出“You“

最一开始Decoder先输入起始符<BOS>

最终模型输出了(1,40000)的概率矩阵,一行代表目前翻译到了第一个词,40000是词表个数

每列代表某个词的概率,取概率最大的一个词

此时“I”概率最大,则翻译出的第一个词是“I”

此时再将[<BOS>, “I”]输入进Decoder:

在这里插入图片描述

(推理过程Encoder的输入一直是“我爱你”,所以Encoder不用每次都算,每次都用第一次的输出就好)

经过运算,Decoder的输出是一个(2×40000)的概率矩阵,其中2是已经翻译出的词数

取其中每行概率最大的那个词(其实只关心最后一行,概率值最大的词是“Love”,则下一个词是“Love”)

Transformer没有向前纠错机制

例如之前已经生成“I”了,在生成“Love”时的第一行概率最大的不是“I”,也不会去修改“I”了

所以推理时只关心最后一行

再将[<BOS>, “I”, “Love”]输入进Decoder,生成“You”

将[<BOS>, “I”, “Love”, “You”]输入进Decoder,生成结束符<EOS>,翻译结束

训练流程

在这里插入图片描述

训练时不用像推理时一遍遍地运算

而是Decoder直接输入[<BOS>, “I”, “Love”, “You”]

得出的四行概率矩阵计算与标签[“I”, “Love”, “You”, <EOS>]的交叉熵损失

具体是计算每一行的交叉熵,然后求平均

L = − l o g P c L=-logP_c L=logPc

其中Pc是真实标签在预测中的概率

比方计算第一行的交叉熵,就是找到这一行“I”对应的概率Pc,然后求负对数

四、各模块结构详解

1.Encoder 输入

输入会经过两个步骤:Embedding和位置编码,Embedding是将单词或字符映射为连续的向量表示,位置编码是添加位置信息。

以翻译任务为例,想要翻译“我爱你”这句话,Inputs为“我爱你”,Embedding会将文字映射到d_model维度的向量,位置编码为每个字添加其在整体中的位置信息,具体来说:

Input Embedding

将单词或字符映射为连续的向量表示

Transformer中先用随机映射将文字映射为向量,在训练中不断优化映射(功能与word2vec相似,但word2vec在训练中是静态的)

在中文翻译中,一般词表里有常用的 5,000 个汉字,embedding 维度设为 512(d_model=512)

embedding = nn.Embedding(num_embeddings=5000, embedding_dim=512)

将“我”字映射到向量,假设“我”字在词表中的id为1024:

embedding(torch.tensor([1024]))# tensor of shape [1, 512]

这样便可将“我”字映射到[1,512]的向量中,其余字同理

Positional Encoding

由于Transformer模型本身不具备处理序列顺序的能力,但因为在文本信息的处理时,当前单词是跟前后单词有关联系的,因此需要在输入嵌入层( input Embedding)后加入位置编码(positional encoding),以提供位置信息

首先给出公式:
P E ( p o s , 2 i ) = s i n ( p o s 1000 0 2 i / d_model ) PE(pos,2i)=sin(\frac{pos}{10000^{2i/\text{d\_model}}}) PE(pos,2i)=sin(100002i/d_modelpos)

P E ( p o s , 2 i + 1 ) = c o s ( p o s 1000 0 2 i / d_model ) PE(pos,2i+1)=cos(\frac{pos}{10000^{2i/\text{d\_model}}}) PE(pos,2i+1)=cos(100002i/d_modelpos)

其中pos为字在语句中的位置,i为向量的位置索引。

Tokenpos
0
1
2

“我”字的位置编码向量为
[ s i n ( 0 1000 0 2 ∗ 0 / d_model ) , c o s ( 0 1000 0 2 ∗ 1 / d_model ) , s i n ( 0 1000 0 2 ∗ 2 / d_model ) , . . . , c o s ( 0 1000 0 2 ∗ 511 / d_model ) ] [sin(\frac{0}{10000^{2*0/\text{d\_model}}}), cos(\frac{0}{10000^{2*1/\text{d\_model}}}), sin(\frac{0}{10000^{2*2/\text{d\_model}}}), ...,cos(\frac{0}{10000^{2*511/\text{d\_model}}})] [sin(1000020/d_model0),cos(1000021/d_model0),sin(1000022/d_model0),...,cos(100002511/d_model0)]
其与d_model维度相同,共512维

"爱"字的位置编码向量为
[ s i n ( 1 1000 0 2 ∗ 0 / d_model ) , c o s ( 1 1000 0 2 ∗ 1 / d_model ) , s i n ( 1 1000 0 2 ∗ 2 / d_model ) , . . . , c o s ( 1 1000 0 2 ∗ 511 / d_model ) ] [sin(\frac{1}{10000^{2*0/\text{d\_model}}}), cos(\frac{1}{10000^{2*1/\text{d\_model}}}), sin(\frac{1}{10000^{2*2/\text{d\_model}}}), ...,cos(\frac{1}{10000^{2*511/\text{d\_model}}})] [sin(1000020/d_model1),cos(1000021/d_model1),sin(1000022/d_model1),...,cos(100002511/d_model1)]
最后将Input Embedding生成的512维向量和位置编码生成的512维向量相加,即完成输入处理

2.Encoder blocks

注意力机制

首先讲橙色部分的多头注意力机制,其由N个头的自注意力组成

自注意力机制

在这里插入图片描述

如上图,当考虑“婴儿在干什么”时,人们对图片各区域的关注程度不同,越重点关注的区域颜色越深。这便是注意力机制的由来,在语言翻译任务中,句子中每个词需要关注其他词的程度不同,比如

在这里插入图片描述

“eating”后面会有一种食物,“eating”与“apple”的Attention Score更高,而“green”只是修饰食物“apple”的修饰词,和“eating”关系不大。

Transformer中实现注意力机制使用了QKV矩阵,首先将block模块的输入分别乘上WQ,WK,WV矩阵,得到QKV矩阵,其中WQ,WK,WV矩阵为注意力模块中的可学习矩阵

实际代码中是直接使用

	WQ=nn.Liner(d_model, dk)
    Q=WQ(Input)

将input映射到(3,dk)维度

实际代码中其实是映射到d_model维度:
关系到后面的多头注意力,直接计算的8个头,省的计算8次再拼接

在这里插入图片描述

K,V矩阵同理,其中dk一般设置为
d k = d_model / h d_k=\text{d\_model}/h dk=d_model/h
本例中d_model为512,h为多头注意力的数量,后面会讲到,一般为8,因此本例中dk为64

再对QKV矩阵进行处理:

用公式表示就是:
A t t e n t i o n ( Q , K , V ) = s o f t m a x ( Q K T d k ) V Attention(Q,K,V)=softmax(\frac{QK^T}{\sqrt{d_k}})V Attention(Q,K,V)=softmax(dk QKT)V

为什么要除以根号dk?:
为了减小方差,防止梯度过大


以上是实现过程,下面讲一讲如何理解QKV

QKV可以分别这样理解:

  • Q:Query(查询),每个词分别提出问题:这个句子中谁和我最相关?
  • K:Key,每个词的特征
  • V:Value,每个词的实际语义

首先每个词提问:其他词谁和我最相关(Q)?所有词提供自己的身份(K),得到注意力权重矩阵(这个词更关注其他哪个词,比如“我”和“爱”对应的权重矩阵值较大,表示“我”比较关注“爱”字,那么在接下来的加权组合中,会将“爱”字的V值线性融合进“我”字中,“我”字不太关注“你”字,“你”的V值就对新的“我”的V值影响不大。

具体来说,

在这里插入图片描述

Q*K^T矩阵表示每个词对其他词的关注程度,比如第一行第二列数值较大,就意味着“我”比较关注“爱”字,第一行第三列的数值较小,就意味着“我”不太关注“你”字,即“你”字对“我”的影响不大

注意力矩阵再乘上V矩阵,即得到每个词融合其他词后的新V值:

在这里插入图片描述

举个例子:

在这里插入图片描述

第一行第二列的数值较大,意味着“我”比较关注“爱”字,即“爱”字对“我”的影响较大,则在新的V值中,“我”的V就融合了比较多的“爱”的V

为什么输入已经有语义了,还要提炼出V值?:
原始词向量是“通用信息”,Value 是“为当前注意力任务精细提炼的表达”

多头注意力机制

为了增强拟合性能,Transformer对Attention继续扩展,提出了多头注意力(Multi-Head Attention)。

对于同样的输入,定义多组不同的WQ, WK, WV,每组分别按上述计算生成不同的Q, K, V,最后叠加并经过一层线性层得到和原始输入维度相同的输出。

因为上面每个自注意力模块得到的V值维度为(3,dk),其中3是因为“我爱你”长度为3,由于上面讲到dk定义为
d k = d_model / h d_k=\text{d\_model}/h dk=d_model/h
所以h个头注意力输出concat后的维度为(3,d_model),与原始输入一致。

残差和层归一化

在经过橙色的多头注意力机制后,进行了残差和层归一化的操作:

在这里插入图片描述

残差连接

在深度神经网络发展历程中,出现了“网络退化”(Degradation Problem)现象:当网络深度超过某临界值(如VGG-19的19层)后,模型在训练集和测试集上的准确率均出现显著下降,这一现象无法简单归因于过拟合或梯度消失问题。

为了解决这一问题,何恺明等人提出了残差连接:

即在通过神经网络后再加上原始输入的值,保证了增加这层网络后性能不低于没有这层网络的结构。

在Transformer中也应用了这一思想,在注意力机制后面增加了残差连接(注意力机制的输出结果与原始输入相加后再进行后续操作),保证增加注意力机制后的性能不低于之前。

层归一化

归一化一般有批归一化(BatchNorm)和层归一化(LayerNorm)

批归一化的使用场景如下:

每个batch中所有样本的同一维度代表同一性质,BatchNorm对同一维度的数据进行归一化,即对所有人的身高、体重分别进行归一化

而在翻译任务中,

同一维度上的“我”和“今”没有任何关系,“爱”和“天”也没有,因此使用批归一化无法有效进行归一化。

而层归一化即

在残差后每个token的维度上进行归一化,明显更适合翻译任务

Transformer中使用Layernorm可以让模型在训练中更快收敛、更稳定、更容易训练深层结构

前馈神经网络

将前面部分的输出再经过一个双层的前馈神经网络,再进行和上面相同的残差和层归一化操作,没有什么难点

输出维度依旧是(3,512),这便是encoder的输出。

3.Decoder输入

Decoder的输入部分和Encoder的输入部分结构完全一样,都是经过了Embedding和位置编码

这里只讲结构,用途和训练流程下面再讲

4.Decoder blocks

在这里插入图片描述

Decoder blocks分为三个部分:掩码自注意力,交叉注意力,前馈神经网络

掩码自注意力

这里大体和Encoder中的多头注意力机制相同,只是多了一个掩码机制

因为训练时Decoder一次性输入“我爱你”三个字时,是不允许“我”字看到“爱”字和“你”字的,

这和Transformer的推理流程有关:

Transformer推理流程:一个字一个字地推理,Encoder的输入是[“我”, “爱”, “你”],一开始Decoder的输入是开始符<BOS>,这时Decoder的输出概率最大的是”I“,保持Encoder不变,Decoder的输入变成[<BOS>, “I”],Decoder输出”Love“,再将Decoder的输入变成[<BOS>, “I”, “Love”],Decoder的输出为”You“,再将Decoder的输入变成[<BOS>, “I”, “Love”, “You”],Decoder的输出为结束符<EOS>,翻译结束。

所以推理时每个字看不到后面的字,训练时也就不能让他看到

首先同样是经过WQ,WK,WV生成QKV矩阵

在这里插入图片描述

在计算
Q K T d k \frac{QK^T}{\sqrt{d_k}} dk QKT
生成注意力矩阵后,进行掩码操作,即

在这里插入图片描述

在注意力矩阵的上三角进行掩码操作,保证每个字看不到后面的字,因为在推理阶段,是一个字一个字地给出的

操作就是将上三角都赋负无穷的值,然后再进行softmax的操作
A t t e n t i o n ( Q , K , V ) = s o f t m a x ( Q K T d k ) V Attention(Q,K,V)=softmax(\frac{QK^T}{\sqrt{d_k}})V Attention(Q,K,V)=softmax(dk QKT)V
V矩阵不用做额外操作

残差和层归一化操作和前面一样

交叉注意力

其结构与Encoder的多头注意力相同,在实现上有一点不一样:Encoder提供KV,Decoder提供Q

可以看作Decoder 在 “询问” Encoder:
👉「我目前已经生成了这么多目标词,我该注意输入句子的哪个部分?」

因此由Decoder提供Q,Encoder提供KV,这里需要注意Decoder提供的是Q

前面已经讲过Encoder输出一个(3,512)的高维语义矩阵,其分别经过可学习的WK,WV矩阵
K = ( 3 , 512 ) @ ( 512 , d k ) = ( 3 , d k ) K=(3,512)@(512,dk)=(3,dk) K=(3,512)@(512,dk)=(3,dk)

V = ( 3 , 512 ) @ ( 512 , d k ) = ( 3 , d k ) V=(3,512)@(512,dk)=(3,dk) V=(3,512)@(512,dk)=(3,dk)

生成KV矩阵

Decoder在掩码自注意力模块输出(n,512)矩阵,其中n代表当前已生成的token数,即Decoder的输入token数

如Decoder输入为[<BOS>,“I”]时,n为2,Decoder输入为[<BOS>,“I”,“Love”]时,n为3

Q = ( n , 512 ) @ ( 512 , d k ) = ( n , d k ) Q=(n, 512) @ (512, dk)=(n,dk) Q=(n,512)@(512,dk)=(n,dk)

则注意力矩阵的维度为
A t t e n t i o n ( Q , K , V ) = s o f t m a x ( Q K T d k ) V = ( n , d k ) @ ( d k , 3 ) @ ( 3 , d k ) = ( n , d k ) Attention(Q,K,V)=softmax(\frac{QK^T}{\sqrt{d_k}})V=(n,dk)@(dk,3)@(3,dk)=(n,dk) Attention(Q,K,V)=softmax(dk QKT)V=(n,dk)@(dk,3)@(3,dk)=(n,dk)
同样经过多头后,生成(n,512)的矩阵

残差和层归一化与前面相同

前馈神经网络

前馈神经网络结构与Encoder中相同

6个blocks

这是省略了其他结构的简略图,Encoder中6轮编码后就不变了,其一直输入给每一轮的解码器

每个解码器以上一个解码器的输出和编码器的输出为输入

5.输出模块

输出模块由一个线性层和softmax组成

线性层是一个简单的单层神经网络,目的是把block的输出(n,512)映射到词表大小(n, 40000)

假设词表大小为50000

再进行softmax,取最后一行概率最大的一个词作为本次输出

这里有个小技巧:

线性层的参数与embedding网络中的一层参数共享,可以节省参数量,提高性能

五、Vision Transformer

Transformer最初被提出是用在翻译任务中,后续被发现在图像任务中也能取得很好的表现

1.总体架构

首先对图像进行Patch embedding以及位置编码(其作用与Transformer中embedding类似)

然后经过Transformer的L个Encoder

最后经过MLP,输出

2.Patch embedding

先将224×224像素的图片分割为14×14个patch,每个patch大小为16×16×3:

将每个patch展平成一维(长度为16×16×3=768)

将14×14(196)个patch合并组成196×768的矩阵

将这个矩阵经过一层神经网络,映射到高维度(base版本映射后还是196×768维)

3.添加 class token

参考BERT,在刚刚得到的矩阵上插入一个专门用于分类的[class]token,这个[class]token是一个可训练的参数,数据格式和其他token一样是一个768维向量

其作用是聚合整张图像的全局特征,以便最后用于分类任务。

  1. 它是一个 可学习的向量,训练中不断调整。
  2. 和其他 patch token 一样参与每一层的 多头注意力机制
  3. 每一层都可以让 class token 和其他 patch token 互相“交流”信息
  4. 最终,class token 吸收了整张图像中各个 patch 的信息。
  5. 最后一层 transformer 输出时,直接取 class token 的输出(shape: (1, D))作为整张图像的表示,再送入分类头(一个线性层)

添加后输出为(197×768)

4.位置编码

生成一个(197×768)的可学习矩阵,直接加上刚刚的输出

原文中实验表明这种可学习的1d位置嵌入性能不比2d差

5.Transformer Encoder

在这里插入图片描述

Vision Transformer中Encoder block和Transformer中结构基本相同,只有一点不同:

就是层归一化的顺序

Transformer中:多头注意力→残差相加→层归一化→线性层→残差相加→层归一化

Vision Transformer中:层归一化→多头注意力→残差相加→层归一化→线性层→残差相加

base版本中Encoder block重复12次

Encoder不改变维度,因此Encoder的输出还是(197×768)

6.MLP

训练ImageNet21K时是由Linear+tanh激活函数+Linear组成

但是迁移到ImageNet1K上或者自己的数据上时,只用一个Linear即可

这里只取Encoder输出的第一行输入进NLP(也就是 class token 对应的向量)

输出是分类的个数

参考:

Transformer | 鲁老师

Transformer从零详细解读(可能是你见过最通俗易懂的讲解)_哔哩哔哩_bilibili

本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值