Transformer架构图
Input/Output Embedding(词嵌入向量)
-
作用: 将输入文本中的每个单词(或子词)转换成一个数值向量,是连接人类语言(离散符号)与神经网络(连续计算)之间的桥梁。
-
为什么需要它: 计算机只能处理数字。如果我们直接把单词 “apple” 和 “banana” 这样的字符串送给模型,它无法理解其含义。
-
它是怎么工作的:
-
每个单词在词汇表中都有一个唯一的索引(例如,“apple”是10,“banana”是20)。
-
Embedding Matrix 是一个大小为
[词汇表大小, 模型维度]的矩阵。例如,如果词汇表有50000个词,我们想要的向量维度是512,那么这个矩阵就是[50000, 512]。 -
当输入单词索引 “10” 时,Embedding 层就从这个矩阵中取出第10行。这个长度为512的向量就是单词 “apple” 的嵌入表示。
-
Positional Encoding(位置编码)
-
作用: 为模型提供序列中词语的顺序信息。
-
为什么需要它: Transformer 模型架构的自注意力机制本身是“无序”的,它在处理一个序列时,是同时看到所有词的。对于模型来说,输入“我爱吃苹果”和“苹果爱吃我”在计算注意力时,如果没有额外信息,它看到的只是一组无序的词语集合,无法区分两者的语序差异。
-
它是怎么工作的:
-
输入: 首先,一个句子通过 Input Embedding 层,每个词被转换成一个稠密的词向量(例如,一个512维的向量)。这个向量包含了词的语义信息。
-
生成位置编码: 同时,模型会生成一个与词向量维度完全相同的位置编码向量。这个向量的值取决于该词在序列中的位置(第1个,第2个...)。这个编码包含了词的位置信息。
-
相加融合: 将词向量和位置编码向量按元素相加,得到一个新的、融合了语义和位置信息的向量,再将这个向量送入后续的编码器层中进行处理。
-
Self-Attention(自注意力机制)
-
作用: 让模型能够同时从不同的角度和不同的表示子空间中去关注输入序列的不同部分,从而更丰富、更强大地捕捉上下文信息。
-
为什么需要它: 在传统模型中,一个词通常被建模为其周围一个固定窗口内词的函数(如RNN的逐步传递,CNN的局部卷积)。但自注意力允许序列中的任何一个词直接与序列中所有其他词进行交互,无论它们之间的距离有多远。这完美地捕捉了长距离依赖关系。
-
它是怎么工作的:
-
对于每个词,它生成三个向量:Query(查询)、Key(键)、Value(值)。
-
Query:可以理解为“我正在寻找什么”。
-
Key:可以理解为“我能提供什么信息”。
-
Value:可以理解为“我实际包含的信息”。
-
通过计算一个词的 Query 与序列中所有词的 Key 的相似度(点积),得到一组注意力分数。这组分数表示在编码当前词时,应该对序列中其他词给予多少“注意力”。
-
用这些分数对所有的 Value 向量进行加权求和,得到当前词的输出。输出向量就是所有 Value 向量的混合,其权重由当前词与所有词的关联度决定。
-
Multi-Head Attention(多头注意力机制)
-
作用: 为了解决自注意力机制中的局限而设计的。它的核心思想是:“不要把所有的鸡蛋放在一个篮子里”,不如多次并行地计算注意力,每次关注不同的方面。
-
为什么需要它: 自注意力的一个局限是在单次计算中,它可能会将不同种类的信息“平均化”。例如,在处理“
苹果公司发布了新款苹果手机”这句话时,第一个“苹果”更指向“公司”,第二个“苹果”更指向“水果”品类。单一的注意力机制可能难以同时、清晰地捕捉这两种截然不同的语义关系。 -
它是怎么工作的:
-
分割与投影:
- 将每个词的原始 Query、Key、Value 向量(假设维度是
d_model=512)通过不同的线性变换层,投影到多个(h个)更低维度的子空间中。 - 例如,如果有8个头(h=8),那么每个头的维度就是
d_k = d_v = d_model / h = 512 / 8 = 64。 - 这相当于为每个头准备了一套独立的“Q/K/V投影矩阵”,让它们学习关注不同类型的模式。
- 将每个词的原始 Query、Key、Value 向量(假设维度是
-
并行计算:
- 在每个头对应的低维子空间中,独立地、并行地执行一次完整的自注意力计算。
- 因为每个头有不同的参数,它们会学习到不同的注意力模式。
-
拼接与融合:
-
将8个头计算出的8个输出向量(每个维度是64)拼接(Concat) 起来,得到一个
8 * 64 = 512维的长向量。 -
最后,再通过一个线性投影层,将这个拼接后的向量映射回最终的输出维度(通常是
d_model=512),以整合来自所有头的信息。
-
-
Add(残差连接)
-
作用: 将某一层的输入向量与该层的输出向量相加。
-
为什么需要它:
- 缓解梯度消失问题:Transformer 模型通常非常深(例如,编码器和解码器各有6层或更多)。在训练深层神经网络时,梯度在反向传播过程中可能会变得非常小,以至于较早的层无法得到有效的更新,这被称为梯度消失。残差连接为梯度提供了一条“高速公路”,允许梯度直接、无损地流回较早的层,从而极大地缓解了这个问题。
- 保护原始信息:在没有残差连接的网络中,每一层都必须学习一个“全新”的变换。这有时会导致模型“忘记”最初输入的一些重要信息。残差连接强制模型只学习当前输出与输入之间的残差(或变化量),而不是完整的输出。这使得网络更容易训练,并确保了信息的持久化。
-
它是怎么工作的:
-
数学表达式非常简单:
输出 = Layer(输入) + 输入 -
这里的
Layer可以是自注意力层,也可以是前馈神经网络层。 -
具体来说,该模块的输入(比如,词嵌入向量 + 位置编码)在进入子层(如自注意力)进行计算后,得到的输出会立刻与该子层最初的输入进行逐元素相加。
-
Norm(层归一化)
-
作用: 对向量序列中每一个样本的所有特征维度进行归一化,使其均值为0,方差为1。
-
为什么需要它:
- 稳定训练过程:深度学习模型的训练对网络中每一层输入的分布非常敏感。如果输入分布的均值和方差发生剧烈变化(内部协变量偏移),后续层需要不断地重新适应这种变化,这会导致训练变得不稳定、收敛变慢。归一化通过对分布进行重新调整,使每一层的输入保持稳定。
- 允许使用更高的学习率:未经归一化的网络在训练时通常需要使用较小的学习率,否则容易发散。归一化后,训练过程更加平滑,因此可以使用更大的学习率,从而加快收敛速度。
- 提供轻微的正则化效果:归一化过程中引入的微小噪声可以起到一定的正则化作用,有助于防止过拟合。
-
它是怎么工作的: 层归一化作用于单个样本(例如,一个词对应的向量),而不是一个批次
-
计算均值和方差: 对于一个输入向量
x(例如,经过残差连接后的512维向量),计算该向量自身所有维度的均值μ和方差σ²。 -
归一化: 用这个均值和方差对向量进行归一化:
x_hat = (x - μ) / √(σ² + ε)这里的ε是一个很小的数(例如1e-5),用于防止除以零。 -
仿射变换: 最后,进行一个可学习的仿射变换:
y = γ * x_hat + β-
γ(gamma)和β(beta)是可学习的参数向量,分别称为缩放参数和平移参数。 -
这一步至关重要,因为它让网络有能力恢复归一化操作可能"抹去"的某些重要特征模式。如果归一化有害,网络可以通过学习将
γ设为近似方差,β设为近似均值来大致还原原始分布。
-
-
Feed Forward(前馈神经网络层)
-
作用: 对自注意力机制输出的每个位置(每个词)的表示向量进行独立的、非线性的变换和增强,赋予模型更强的表示能力。
-
为什么需要它:
- 补充非线性能力: 自注意力机制本质上是一系列线性变换(Query, Key, Value的投影)和加权求和,其非线性主要来自于Softmax函数。仅靠这些,模型的复杂度和非线性拟合能力是有限的。前馈神经网络通过其激活函数(如ReLU)引入了关键的非线性变换,使模型能够学习更复杂的函数。
- 独立处理每个位置: 前馈网络作用于序列中的每一个位置(每一个词向量),并且不同位置之间的计算是完全独立、可以并行完成的。这与自注意力(需要关注所有位置)形成互补。这允许模型对每个词的表示进行“深度加工”,而不受其他位置的即时干扰。
- 扩大模型容量: 前馈层通常是整个Transformer模型中参数最多的部分(因为中间维度很大)。它作为一个强大的“处理器”,可以将自注意力机制聚合到的上下文信息进行消化、提炼和转换,从而提升整个模型的表示能力。
-
它是怎么工作的: 前馈网络是一个简单的两层神经网络,它对序列中每个位置的向量进行独立且相同的处理。其经典结构如下
-
第一层(升维与非线性):
- 输入是自注意力层输出的向量(例如,维度
d_model = 512)。 - 首先通过一个线性层将维度扩大。在原论文中,这个中间层的维度是
d_ff = 2048,即扩大到原来的4倍。 - 然后,立即通过一个激活函数,如ReLU(或其变种,如GELU)。这一步引入了至关重要的非线性。
- 公式表示为:
hidden = ReLU(x * W1 + b1) - 其中,
W1是一个[d_model, d_ff]的权重矩阵,b1是偏置项。
- 输入是自注意力层输出的向量(例如,维度
-
第二层(降维与输出):
-
将第一层输出的高维向量(
2048维)通过另一个线性层投影回原始的模型维度(512维)。 -
公式表示为:
output = hidden * W2 + b2 -
其中,
W2是一个[d_ff, d_model]的权重矩阵,b2是偏置项。
-
-
Masked Multi-Head Attention(多头注意力机制,因果掩码机制)
-
作用: 在解码器中,确保在预测某个位置的单词时,只能关注到该位置之前(左侧)的已知单词,而不能"偷看"未来的单词信息,从而保持生成过程的自回归特性。
-
为什么需要它:
- 维持自回归生成: 在训练和推理时,解码器需要逐个生成输出序列的单词。在预测第 t 个位置的单词时,模型只能基于已经生成的前 t-1 个单词来进行预测。如果没有掩码,模型会"作弊"般地看到整个目标序列,包括未来单词,这将导致训练和推理条件不一致,模型无法学会真正地顺序生成。
- 保证推理一致性: 在实际推理(如机器翻译、文本生成)时,模型确实只能看到已经生成的部分,看不到未来内容。训练阶段必须模拟这种条件,模型才能学会在仅有历史信息的情况下做出合理的预测。
- 防止信息泄漏: 如果没有掩码,在计算第 t 个位置的注意力时,模型会同时关注到第 t+1, t+2... 等位置的单词,这会造成标签信息的泄漏,使得模型无法学到真正的语言建模能力。
-
它是怎么工作的: Masked Multi-Head Attention 在标准多头注意力的基础上,增加了一个掩码操作
-
计算注意力分数: 首先,像标准的多头注意力一样,为每个位置计算 Query 与所有位置的 Key 的点积,得到注意力分数矩阵。这个矩阵的每个元素
score[i][j]表示第 i 个位置对第 j 个位置的关注程度。 -
应用因果掩码: 在将注意力分数输入 Softmax 之前,应用一个下三角掩码矩阵:
- 这个掩码矩阵在主对角线及其左下角的位置值为 0(表示允许关注)
- 在主对角线右上角的位置值为负无穷(表示禁止关注)
-
Softmax 归一化: 将掩码后的注意力分数输入 Softmax 函数。由于被掩码的位置是负无穷,经过 Softmax 后这些位置的权重会变为 0,从而确保每个位置只对当前位置及之前的位置产生有效的注意力权重。
-
加权求和: 使用掩码后的注意力权重对 Value 向量进行加权求和,得到每个位置的输出。
-
Linear(全链接层)
-
作用: 将解码器输出的高维向量表示,映射回整个词汇表大小的维度,为每个位置生成一个在所有可能单词上的原始分数(logits),以便后续通过 Softmax 计算生成每个单词的概率分布。
-
为什么需要它:
- 维度匹配: 解码器输出的向量维度是
d_model(例如512维),但我们需要从整个词汇表(例如50000个单词)中选择一个单词。线性层充当了一个"桥梁",将模型内部的表示空间映射到词汇表空间。 - 生成候选分数: 在最终决定输出哪个单词之前,模型需要为词汇表中的每个候选单词计算一个分数,表示该单词在当前上下文中作为下一个单词的合适程度。线性层就是负责生成这些原始分数的。
- 可学习的分类器: 这个线性层本质上是一个可学习的分类器,它学会了将特定的内部表示模式与特定的输出单词关联起来。例如,当解码器输出某种表示模式时,线性层会给"apple"打高分,给"banana"打
- 维度匹配: 解码器输出的向量维度是
-
它是怎么工作的:
-
输入: 接收解码器的最终输出,形状为
[batch_size, seq_len, d_model]。例如[2, 10, 512]表示2个句子,每个句子10个单词,每个单词用512维向量表示。 -
线性变换: 通过一个权重矩阵
W进行线性变换,其中W的形状为[d_model, vocab_size] -
输出: 输出称为 logits(原始分数),形状为
[batch_size, seq_len, vocab_size]
-
Softmax(归一化函数)
-
作用: 将线性层输出的原始分数(logits)转换为一个概率分布,使得每个位置上的所有词汇概率之和为1,从而可以直观地表示每个单词作为下一个输出单词的似然概率。
-
为什么需要它:
- 概率解释性: 线性层输出的 logits 是任意的实数值,可能为正也可能为负,数值范围不确定,无法直接解释为概率。Softmax 将这些分数转换为 0 到 1 之间的概率值,且所有候选词的概率之和为 1,具有明确的概率意义。
- 训练损失计算: 在训练阶段,我们需要计算预测概率分布与真实标签之间的交叉熵损失。这个损失函数要求输入是有效的概率分布,Softmax 正好提供了这个条件。
- 推理决策依据: 在推理(生成)阶段,我们需要基于概率分布来选择下一个单词(无论是贪心搜索选择最高概率的词,还是束搜索考虑多个高概率选项)。Softmax 提供的概率值为这些决策策略提供了基础。
-
它是怎么工作的:
- 输入: 接收线性层输出的 logits,形状为
[batch_size, seq_len, vocab_size]。对于每个位置,都有一个包含词汇表中所有单词分数的向量。 - 输出: 输出一个概率分布,形状与输入相同
[batch_size, seq_len, vocab_size]
- 输入: 接收线性层输出的 logits,形状为