Transformer学习


Transformer 是什么?架构是怎么样的?


引言

Transformer 是什么?架构是怎么样的?

内容一

Transformer 是什么?架构是怎么样的?

假设 2050 年 AI 取代了人类,成为了蓝星扛把子,而人类正好又发明了时光机。答应我,回到 2017 年阻止那篇名叫《Attention Is All You Need》的论文发表。没有它,就没有 Transformer 架构,也就没有后来的 GPT、Gemini 这些大模型。

今天我们就假装不了解 Transformer 是什么,来看一下它的原理到底是怎么样的。


从翻译问题说起

货拉拉拉布拉多,短短 10 个字里有 6 个”拉”字,含拉量达到惊人的 60%,人一看就能理解它的意思,所以很自然就能翻译成英文:看,货拉拉 carry 了拉布拉多。

注意,关键是理解,所以才能翻译。但计算机却表示很难理解,因为每个”拉”字在不同的位置都代表不同的意思。尤其是中间这个”拉布拉”——在中文里”拉”可以表示拉货,也可以表示拉扯;在英文里,既可以翻译为 carry,也可以是 drag 或 pull。必须结合前面的”货拉拉”和后面的”拉布拉多”,才能准确理解”拉布拉”。

那么问题就来了:计算机该如何理解一句话里每个词的含义?


Token与向量

我们知道计算机只认识 0、1 这样的数字,所以在翻译之前需要把文本变成数字。最简单的方法是将文本汉字转成对应的数字编号,比如 Unicode 的编码。但这有个问题:编号只是个纯数字,不能承载任何语义信息,所以两个数字编号之间也不会有什么语义关联。

那我们需要一种既能数字化又能承载语义的方案,怎么办呢?

首先将这句话切分成很多个词块,每个词块叫一个 Token。然后想象有这么一个二维坐标系:X 轴表示是否和交通相关,Y 轴表示是否和动物相关。那么”货拉拉”会落在 X 轴正方向,”拉布拉多”会落在 Y 轴正方向。”物流平台”“网约车”这类词在坐标系上会更靠近”货拉拉”,而”金毛”“牧羊犬”则会更靠近”拉布拉多”。

但只有两个维度不够,无法表达复杂的语义关系。比如”货拉拉”“物流平台”“网约车”在二维里都和交通相关,靠得很近。但如果加入第三维——是否运货,”货拉拉”和”物流平台”偏向运货物,”网约车”偏向运人,它们就被区分开了。

随着维度的增多,世界上所有已知词汇的 Token 都能在这个多维坐标系里找到属于自己的位置。通过海量语料训练就可以做到:语义相近的词在坐标系空间中靠得很近,与之无关的词则离得很远。

词和词之间的关系还能通过数学方式计算出来。比如:国王的坐标减去男人加上女人,得到的结果会非常接近王后的坐标。

至于”拉布拉”这种多义词,它在坐标系里只是一个模糊的平均位置。只有结合前后词之后,它的位置才会被拉向正确的语义区域。

每个坐标维度都可以表示为数字,所以每个 Token 都可以用一组数字来表示。这种表示方式就是 Embedding,也叫向量。

但语言不仅有词义还有顺序。比如”货拉拉”和”拉布拉多”顺序颠倒,意思就会完全不同。所以模型还会为每个 Token 加入位置信息,让向量同时包含”它是什么”和”它在什么位置”。


QKV 是什么?

知道了每个 Token 对应的向量,我们就可以考虑翻译的事情。但问题就又来了:在翻译”拉布拉”这种多义词时,怎么结合上下文判断这个”拉”究竟是 carry 还是 pull 呢?

我们知道每个词块除了表面上的字面意思,还有背后含义。比如”拉布拉多”除了字面意思,背后的含义有宠物狗等。所以我们可以把一句话抽象成一个词块数据库的查询:Key 就是词块本身,Value 就是词块背后的含义。

那么计算机在理解”拉布拉”时应该重点关注哪些词,就可以抽象为:拿着”拉布拉”这个词去词块数据库里挨个遍历一遍,看哪个词对”拉布拉”参考价值更大。

注意,这个过程中有三个概念:查询、键、值

但直接拿 Token 的向量去做这三件事不够灵活,因为一个向量要同时扮演三个角色。所以我们通过三个权重矩阵 Wq、Wk、Wv 的变换,让每个 Token 的向量分别变成三个专门的向量,各司其职:

角色 名称 含义 类比
查询 Q 这个 Token 想关注什么? 数据库的查询接口
K 这个 Token 能提供什么信息? 数据库里的键
V 这个 Token 背后的真正含义? 数据库里键对应的值

注意力是什么?

有了每个 Token 的 QKV 之后,我们看一下怎么用它们进行计算。

首先,每个 Token 的 Q 都会遍历句子中所有 Token 的 K,用点积计算相关程度。就像这样——点积,说白了就是相乘。从数学上看,其实就是在计算两个向量在坐标系里指向是否一致,越同向数值就越大。

从效果上看,这是两者相关程度越高,说明这个 Token 越值得被重点参考,可以多关注它。比如针对”拉布拉”这个 Token,计算结束后就能知道”拉布拉”对整个句子中所有 Token 的关注程度。

此时的计算机就会发现:”拉布拉”和”货拉拉”之间的相关性很高,那”拉布拉”就会更关注”货拉拉”,所以后续”拉货”这一含义就会被显著放大。

如果向量维度较大,这些点积结果可能会整体偏大,容易导致某一个 Token 的关注程度被放大得过于极端。为了让不同 token 之间的关注差异更容易被区分,模型会在点积之后除以维度的开方根号 dk。这一步被称为 Scale

接下来模型会对这些关注程度做一次 Softmax,把它们转换成一组权重。这样一来,”拉布拉”对所有 Token 的关注程度之和就是 1,可以理解为注意力分配比例。

最后再用这组权重去加权所有 Token 的 V 向量。相当于知道了”拉布拉”这个 Token 应该以多大的权重,从其他 Token 那里取哪些信息。最终得到的结果就是”拉布拉”这个 Token 融合了上下文信息之后的表示。我们可以把它记作 H₂。”货拉拉”和”拉布拉多”也会以同样的方式分别得到各自的 H₁ 和 H₃。

对,这一整套计算过程就是所谓的注意力机制。可以用这段公式来表示:

Attention(Q,K,V) = Softmax(QKᵀ / √dk) × V

它可以让每个 Token 根据当前语境自动决定,应该重点参考其他哪些 Token。

怕你们还是理解不了,我再总结下:

步骤 作用
Q 点积 K 用来计算 Token 之间关注谁、关注程度
Scale(除以 √dk) 防止点积数值过大,避免 Softmax max 饱和、梯度消失,让模型训练更稳定
Softmax 把关注程度转换成注意力权重,也就是概率分布
乘以 V 用这些权重从被关注的 Token 中提取并融合具体的语义内容

多头注意力

但只算一次注意力是不够的。因为词与词之间的关系是多样的:有的是语义上的关联,有的是语法结构上的依赖,单一角度很难捕捉全面。

所以模型会并行地做多次注意力计算,每一次关注不同的侧重点。这些并行计算的结果最终会被拼接在一起,这就是多头注意力


Add 和 Norm 是什么?

通过多头注意力,我们就将原句中的各种语义信息通过向量表示了出来。

接着模型首先会把注意力的结果和原始输入相加,确保 Token 自身的信息不会被覆盖掉。这一步叫 Add(残差连接)。接着为了让数值保持在合理范围内,模型会对结果做一次统一调整,这一步通常被称为 Norm(归一化)。这样既保留原有信息,又避免数值在多层计算后失控。


前馈网络(FFN)是什么?

经过残差连接和归一化处理后,从原始输入到当前输出的映射关系仍然是线性的。可以理解为不同 Token 向量的变换路径都是笔直的直线,没法捕捉复杂的语义关联。

为了让模型具备更强的表达能力——拟合自然语言里非线性的语义关系——模型会让向量通过前馈网络 FFN。它的核心作用是通过 ReLU 等激活函数,把线性的映射路径掰弯,变成非线性的曲线。

随后同样会再接一次 Add 和 Norm


编码器是什么?

多头注意力 → Add & Norm → 前馈网络 → Add & Norm,共同构成了一个编码层。一个编码层能让模型对整句话形成一次上下文理解。多个编码层堆叠起来,模型对语义的理解就会逐层加深。多个编码层组成一个编码器

编码器的最终输出是一组向量,它们是原句的各个词块融合了上下文语义信息之后的数字表现形式。它是计算机对输入文本的整体语义理解。


翻译原理

接下来就是基于编码器输出的整体语义理解,开始翻译成英文。

生成英文时,我们会放入一个固定的起始符号 BOS(Beginning of Sentence),它表示英文从这里开始翻译。接着模型会结合编码器输出的中文理解,预测最合理的第 1 个英文词。这个例子最终生成的是 can

接着模型会把 BOS 和 can 一起作为输入,预测下一个词”货拉拉”……以此类推,每一步都是在已有结果的基础上预测下一个最合理的英文词,就像成语接龙。

这个过程需要解决两个问题:

第 1 个问题:新生成的词怎么和已经写下的英文内容衔接,保证语法和表达的连贯?

好办,用注意力机制解决。毕竟大佬说过 Attention Is All You Need

我们还是将一句话理解成词块数据库:Q 是当前要翻译单词的位置,KV 呢?既然要保证英文衔接自然连贯,那模型只关注已经生成的英文就够了。所以 KV 来自英文构成的词块数据库。这就相当于预测第 n 个词时只依赖前 n-1 个词的结果,保证生成过程从左到右,像掩盖了后面部分做词语预测一样,所以叫带掩码的多头自注意力

第 2 个问题:在原句已经理解好的整体语义中,此刻翻译最该参考原句哪部分信息?

同样还是用注意力去解决:Q 是当前要翻译单词的位置,而 KV 自然就是来自中文构成的词块数据库。

比如在已经生成 carry 之后,要翻译”拉布拉”时,由于编码器输出的中文理解中,它和运货相关的信息关联更强,因此更可能翻译成 carry 而不是 pull 或 drag。这部分叫多头交叉注意力


解码器是什么?

这两次注意力计算之后,输出结果都会经过残差网络、归一化以及前馈网络这些流程。每一步的作用都和前面处理中文时几乎一致,我就不重复了。

这些组件共同构成了一个解码层。多个这样的解码层堆叠在一起,形成完整的解码器。每一层都在上一层输出的基础上进一步加工,使得生成的英文在语法、语义和上下文理解上更准确。

最终会将解码器输出的向量通过词表转成可读的文字,也就是英文输出。


Transformer是什么?

编码器和解码器配合工作,共同完成了翻译任务。这就是 Transformer 的经典架构。

之所以说是经典,是因为并不是所有任务都需要编码器和解码器同时存在:

架构 用途 代表模型
仅编码器 理解文本,不需要生成 BERT(文本分类、情感分析)
仅解码器 理解 + 生成,省略独立编码器 ChatGPT、DeepSeek
编码器 + 解码器 完整翻译架构 早期机器翻译模型

而且大模型也能做翻译,所以像前面提到的编码器+解码器的完整架构,在后续更多用于小模型和低资源场景。

内容二

AI到底是怎么看懂一句话的?今天我就是帮你把它的核心思想Transformer用15分钟时间讲透。这期视频我们从一个最简单的中文句子的翻译入手,探讨计算机理解和处理自然语言的复杂性。

接着我们会详细介绍Transformer是怎么让每个词互相商量,靠自注意力机制动态搞懂自己在句子里面的角色,再聊一聊它是如何用多个视角同时捕捉不同的语义关系,又为什么要叠好多层来挖出深层的含义。最后我们会说一说解码器怎么一边看上下文,一边一步步生成答案,并且顺带看一看Transformer怎么撑起今天的智能问答、自动摘要,甚至是大模型的半边天。

大家看”用锁链锁锁链会不会被锁死”这句话看似简单,当你试着把它翻译成英文的时候,可能只需要你稍加停顿一下。但是对于计算机,如果只一个字一个字地看,翻译出来大概率会翻车。为什么呢?因为句子里面有6个”锁”字,它们在不同位置有不同的含义:第1个”锁”是名词,而第2个呢是动词。

要准确地翻译这句话,计算机必须理解每个字在上下文中的具体意义。显然单独去看每一个字是无法判断其含义的,必须从整体关联入手。换句话说,句子中每个字都相互关联,互为整体。而Transformer之所以能成为今天大模型的基石,正是因为它解决了这个问题。它不是孤立地看每个词,而是让整句话互相商量,动态判断每个字到底是什么意思。

换句话来说,理解语言的关键不是单个词,而是词与词之间的关系。那具体怎么实现呢?

第1步,得把文字转换成模型能够处理的形式。那我们会先用像BPE这样的分词工具把句子拆成最小的子单元,也就是token。比如说”锁链”可能是一个token,单独出现时又是另一个。这一步看似简单,但直接影响后续模型的理解能力。现在很多大厂在优化中文大模型的时候,第1件事就是重新设计分词策略。

接着每个token会被映射成一个512维的词向量,你可以理解成用512个数字来描述这个词的特征。但问题来了,这些词向量是一起塞进模型的,模型根本不知道谁先谁后。大家知道在语言里面”我打你”和”你打我”顺序一换意思天差地别,所以Transformer就给每个词额外加了一个位置编码,相当于是给每个词贴上”我是第几个”的标签。

这样一来呢,我们就得到一个形状为10×512的词向量组,用X表示。它既包含了词义,也包含了顺序。

那么模型是如何通过X找出每个词之间的关联的呢?答案是让词相互计算相似度。具体来说,模型使用三个权重矩阵Wq、Wk和Wv,分别和每个词向量相乘,生成Q、K、V三个向量。这里的Q是查询向量,代表当前词想要关注的内容;K呢是键向量,表示这个词提供的信息,可以把它想象成一本书的索引标签;V呢则是值向量,包含了这个词的实际信息内容,是真正被检索和聚合的信息本身。

然后每个词拿着自己的Q去和句子里所有的K算相似度,其实就是计算点积得分。得分高的,说明这两个词关系密切。那么为了避免数值爆炸,这个分数还会除以√dk做缩放,然后再对这个分数应用Softmax函数,把分数归一化到0~1之间。这就是所谓的注意力权重。

这组权重就反映了词与词之间的关联程度。比如第1个”锁”的Q和第1个”锁”的K匹配度就很高,和后面当动词的”锁”就弱一点。那么有了权重之后,我们把每一个词的V向量乘以对应的注意力权重,然后把每一行进行加权求和,最终得到一个新的向量。这个向量已经不再是独立的”锁”了,而是融合了上下文信息的智能”锁”,知道自己在这句话里面到底是备用的工具还是执行的动作。

那么整个过程叫做自注意力机制。打个比方,这就像是你带了一个问题Q走进图书馆,图书馆里面的每一本书都有一个索引标签K。你快速地扫一遍,发现几本书特别相关,就多花比较多的时间来读它的内容V。等走出图书馆的时候,脑子里面已经整合出了一个全新的答案,而这个答案就是模型对这句话的理解。

同样的,通过自注意力计算之后,每个token都生成了对其他token的抽象理解,而这种理解就隐藏在了这组加权后的向量中。我们把这组向量定义为向量Z。

不过尽管Z已经包含了相当多的语义信息,它仍然不够全面。但注意,注意力一直这样,通常只能让一个词对其他词产生单一的关注和理解。但是在一个真实场景中,一个词往往包含很多方面的语义信息,比如说动词与宾语的关系、代词与指代名词的关系,还有语义角色关系、情感或是修饰关系等等。所以单靠一次注意力计算很难捕捉这么多维度的信息。

对,正是为什么工业级的Transformer不会只用一个头,它会同时开多个视角并行分析不同类型的语义关系。

Transformer把输入向量X直接拆成8个独立的头,这时候呢512维的X向量就被拆成了8个64维的向量,它们各自独立进行自注意力计算。这意味着什么呢?简单来说,每个头就像是从一个不同的视角来看同一个句子。比如说第1个头可能关注动词和宾语的关系,第2个头呢则关注代词和指代名词的关系。通过这种方式,模型能够从多个角度整合复杂的语义信息。

接着这些独立计算的结果会被拼接在一起,形成一个10×512维的向量Z。这个新的向量不仅包含了原始信息,还融合了各个头的不同理解。整个过程就叫做多头自注意力机制,它是Transformer的核心模块之一。通过这一系列操作,一个句子中复杂的语义信息就被充分提取出来了。

不过尽管单层的多头自注意力机制处理一个句子已经相当强大了,但在实际应用中往往需要处理更长的文本,比如说一段话、一篇文章甚至是整本书。这时候单层的结构就显得有点吃力了。因此为了更好地学习更深层次的语义信息,Transformer通常会设置多层结构,每一层都包含一个多头自注意力机制,层层叠加,使得模型可以从多个层次理解文本。

那这里有个问题:虽然层数增加,神经网络容易出现梯度消失的问题。简单说,如果某一层训练得到最优解是什么都不做,它的输出F(x)就等于0,导致梯度信息无法传播,相当于只有少数几层神经网络在起作用。好在解决方法其实并不复杂,我们只需要把这一层的输出改成F(x)+x就可以了。这样不管F(x)是不是等于0,这里都有一个原始的X在传递梯度,确保信息不会丢失。这就是所谓的残差连接。在每一层多头注意力后面加上残差连接,可以确保深层网络的训练更加稳定。

除了残差连接,我们还需要对每一层输入值进行归一化处理,让向量中的数值都保持在合理范围内,避免出现波动很大的数值。这样做的好处是,无论输入是什么,模型都能够稳定地输出结果。我们把经过归一化后的输出向量定义为向量A。

接下来我们在每一层添加一个前馈神经网络FFN,对A的每个元素使用ReLU激活函数进行非线性变换。为什么要这么做呢?因为之前的计算再怎么复杂,它输出的结果都是线性的,就像是一条直线。但是当我们使用ReLU进行非线性变换之后,这条直线就可以拐弯了,甚至可以画出任何形状的曲线,拟合任意复杂的函数。这就大大增强了模型的表达能力。

同样,在前馈神经网络之后,我们也会加入一个残差连接和归一化处理,确保每一层的输出都能够稳定且有效地传递给下一层。经过N层这样的结构,最后输出的向量就是X_out。以上这一系列操作被称为编码器,它的主要功能是负责理解输入的语言。

那么理解了输入的句子之后,下一步就是根据这些理解生成翻译了,这个任务由解码器来完成。解码器的核心原理可以用一个词来概括,那就是瞻前顾后——也就是说不仅要参考编码器提供的上下文信息,还要结合已经生成的部分序列,逐词预测接下来的内容。

具体来说,解码器有两个主要输入:一个是编码器输出的理解信息X_out,另一个是模型已经生成的单词序列。基于这两部分信息,解码器预测下一个token。如果此时还没有生成任何token,我们会先输入一个起始符来作为起点。

首先,输入的向量会进入掩码多头注意力机制层进行计算。这里的关键在于”掩码”二字。掩码多头注意力机制方式和前面提到的多头自注意力差不多,都是对输入的每个token计算它们之间的注意力权重。但不同的是,掩码确保了解码器在训练时看不到未来的信息。比如说当前只有一个起始符,就让它只和自己进行计算。

那为什么需要掩码呢?是因为在训练过程中,我们通常会把整个答案都输入进去,但是为了模拟真实的翻译过程,我们需要对未生成的部分进行掩码处理。这样解码器就只能根据已经生成的单词进行预测。每当它预测一个新的token,我们就把这个token给放出来,和实际答案进行对比,计算误差,并且据此调整模型的参数。这种方式大大简化了训练流程,让模型能够更高效地学习。

不过在实际使用中,输入给编码器的本来就只有已经生成的序列,所以从理论上来讲掩码不掩码就不重要了。而且在代码实现中,还是会对未生成序列的位置进行掩码处理。比如在用Qi和Kj计算的时候,可以把未来位置(也就是j>i)的分数设置为一个非常大的负数,接近负无穷。这样做主要是为了保证训练和推理的一致性,避免额外调整模型逻辑。

经过掩码多头注意力之后,我们同样会在后面加上一个残差连接和归一化处理,最终输出向量A。

接着这个向量会进入到交叉注意力机制,用于”瞻前”操作。和自注意力机制不同的是,这里的向量不再是自己和自己进行计算,而是和编码器的输出向量X_out进行计算,从而生成预测结果。

对于X_out里面每个token,我们会让它和X_out里面所有token计算注意力权重,并且加权实现输入和输出语义的对齐,就类似于翻译时源语言和目标语言的对齐。同样的,交叉注意力机制也采用QKV计算,并且分多个头独立处理。每个头计算结果后面也会有一个残差连接和前馈神经网络,再加一个残差连接。经过N层这样的结构,最后我们得到解码器的输出向量Y_out。

这整个过程就是解码器做的事情。现在问题来了,我们已经有了一个512维的Y_out向量,那它还不是我们能够直接读取的真实单词,我们需要把这个向量映射成一个真实的单词。

为了让Y_out映射到具体的词汇表中的单词,我们需要进行线性变换。具体做法是:我们让Y_out乘以一个形状为512×|V|的权重矩阵W(|V|是词表的大小),再加上一个形状为|V|的偏置向量b。通过这一系列操作,我们就得到一个名为Logits的向量,其中每个值表示对应单词的得分。

接下来我们要把这个向量进行Softmax操作,把Logits转换为概率分布。这样一来,向量中的每个值就表示对应单词的概率了。最后我们需要对Logits做Argmax操作,找出概率最大的单词并输出。以上过程循环执行,直到输出结束符为止。

好了,以上就是关于Transformer原理的全部内容了。从输入编码到输出的生成,整个过程环环相扣。

请记住,我们最开始展示的那个翻译任务,其实只是Transformer众多应用场景中的一个缩影。实际上Transformer的应用远不止于此。举个例子,如果你用问答数据来训练模型,它就会变成一个智能问答助手,能够解答各种复杂的问题。当前主流大语言模型就是通过这种方法训练而成的,它们不仅能够生成流畅的文本,还能够展示出多样的智能特性。无论是对话系统、自动摘要还是情感分析,Transformer都能够大显身手。

此外,根据不同的应用场景,Transformer的架构也会有所调整和优化。比如说现在流行的一些大语言模型,像GPT系列,实际上就是只有解码器的结构,专注于文本生成任务。这种设计非常适合生成长篇连贯的文本,因为它们不需要对输入进行精确的编码和对齐。相比之下,那些同时拥有编码器和解码器的模型,比如说BERT或T5,则更适合处理需要精确对齐的任务,比如说机器翻译、文本分类或者是信息检索等等。这些模型能够在理解和生成之间找到平衡,从而实现更精准的任务处理。

最后我们总结一下:Transformer的核心不是看词,而是看关系。它通过自注意力机制让每个词动态理解上下文含义,像”用锁链锁锁链会不会被锁死”这样的句子,变成机器也能读懂的逻辑。从编码器到解码器,从位置编码到多头注意力,整套设计都是为了一个目标——像人一样理解语言的整体意义。

现在你明白为什么大模型能听懂人话了吧?技术在变,但原理不变。掌握Transformer,就握住了通往AI时代的钥匙。






参考资料


返回