很多朋友在学 Transformer 时,习惯一上来就啃源码、跑模型,结果被torch.nn.MultiheadAttention源码里的 reshape 操作、mask 矩阵、位置编码公式弄得一头雾水。我第一次看 Transformer 源码时也有同感:并不是代码本身有多难,而是它背后聚合了太多前置概念——RNN 的序列建模思路、CNN 的并行计算思想、注意力机制的查询匹配逻辑、词嵌入的表示方式,以及位置编码的注入方法。这些概念如果不提前理清,读源码就会变成“每个函数都认识,连起来不知道在干什么”。
这篇文章就来系统梳理 Transformer 的前置概念。内容定位是“复习”,不是“精讲”:假定你学过深度学习基础,但可能对某些细节记忆模糊。我会按神经网络训练基础、序列模型、注意力机制、表示学习这条线,把 Transformer 之前的关键知识点逐一串起来。学完之后,再去看“手撕 Transformer”一类文章,会轻松很多。
1. Transformer 解决了什么问题
在展开前置概念之前,先把 Transformer 本身定位清楚。
Transformer 是 2017 年 Google 团队在论文《Attention Is All You Need》中提出的序列建模架构。它最初用于机器翻译任务,后来逐渐扩展为自然语言处理、计算机视觉、语音识别等多个领域的通用骨干网络。Swin Transformer、Vision Transformer、BERT、GPT 等模型都建立在 Transformer 基础之上。
从问题域来看,Transformer 解决的核心问题有三个:
- 长距离依赖:文本中相距很远的词之间可能存在语义关联,模型需要有能力捕捉这种跨位置的依赖关系。
- 并行计算:传统循环神经网络(RNN)必须按时间步逐个处理序列,难以大规模并行,训练效率受限。
- 全局建模:卷积神经网络(CNN)受限于卷积核大小,只能看到局部区域,需要堆叠多层才能扩大感受野,而 Transformer 的自注意力机制通过一次计算就让序列中任意两个位置直接交互。
Transformer 之所以能“一战成名”,在于它用注意力机制取代了循环结构,实现了全序列范围的并行建模。但它的核心组件并不是凭空产生的,注意力机制、嵌入表示、残差连接、层归一化等思想,都能在更早的工作中找到影子。所以,把前置概念复习扎实,是理解 Transformer 最有效率的路径。
2. 复习神经网络训练的基础闭环
Transformer 本身是一个庞大的神经网络,它的训练流程和你熟悉的任意深度学习模型并无区别。如果在理解 Transformer 时忽略了训练环节,可能看到损失函数下降时只觉得神奇,却不清楚背后的梯度是怎么流动的。
2.1 前向传播与损失函数
我们可以把一个神经网络看作一个复杂的复合函数:
输入 -> 线性变换 -> 非线性激活 -> 线性变换 -> 非线性激活 -> ... -> 输出在训练阶段,模型接收一批输入数据,经过层层计算后得到预测结果y_pred,然后和真实标签y_true计算损失值。这个损失值衡量的是模型当前预测得有多差。
以分类任务为例,最常用的损失函数是交叉熵损失。下面是一个简化的计算过程:
import numpy as np def softmax(logits): # 为了数值稳定性,减去最大值 exp_logits = np.exp(logits - np.max(logits, axis=-1, keepdims=True)) return exp_logits / np.sum(exp_logits, axis=-1, keepdims=True) def cross_entropy_loss(logits, labels): probs = softmax(logits) batch_size = logits.shape[0] # 取每个样本真实标签对应的概率值 correct_log_probs = -np.log(probs[range(batch_size), labels] + 1e-10) return np.mean(correct_log_probs)这里softmax把模型输出的原始得分(logits)转换成概率分布,cross_entropy_loss再计算预测概率与真实类别之间的差异。Transformer 在分类任务(如文本分类)中的输出层同样使用这个逻辑。
理解这一点很重要:Transformer 不是某种特殊的黑盒训练机制,它依然依赖损失函数来衡量“当前网络输出的质量”,并据此更新参数。
2.2 反向传播与梯度下降
有了损失值,接下来就要回答一个问题:模型参数应该往哪个方向调整,才能让下一次的损失更小?
答案就是反向传播(Back Propagation)。通过链式法则,从输出层开始,逐层计算损失对每个参数的梯度。梯度指示了损失函数上升最快的方向,那么参数更新时应该沿着梯度的反方向前进:
param = param - learning_rate * gradient这个公式是梯度下降的最基本形式。实际训练中,我们通常使用随机梯度下降(SGD)及其改进版本,如 Adam、AdamW。Transformer 的训练最常用的是 Adam 系列优化器,因为它能自适应地调整每个参数的学习率,在模型较大时收敛更稳定。
下面是一段手动实现梯度下降的示意代码,目的是展示完整的参数更新闭环:
import numpy as np # 演示数据:y = 2x + 1 x = np.array([1, 2, 3, 4, 5], dtype=np.float32) y = np.array([3, 5, 7, 9, 11], dtype=np.float32) # 初始化参数 w = 0.0 b = 0.0 learning_rate = 0.01 # 手动梯度下降 for epoch in range(500): y_pred = w * x + b loss = np.mean((y_pred - y) ** 2) # 梯度:d loss / d w, d loss / d b dw = np.mean(2 * (y_pred - y) * x) db = np.mean(2 * (y_pred - y)) # 参数更新 w -= learning_rate * dw b -= learning_rate * db print(f"训练后 w = {w:.4f}, b = {b:.4f}")在 Transformer 中,反向传播的链条远比这个复杂,但基本思想完全一致——计算损失对每一层参数的梯度,然后通过优化器更新参数。
2.3 归一化与残差连接的作用
Transformer 中频繁使用的是层归一化(Layer Normalization)和残差连接(Residual Connection)。如果不理解它们的前置概念,很难读懂 Transformer 结构图中的Add & Norm模块。
- 残差连接:把输入直接加到层的输出上,即
output = layer(x) + x。这样做的好处是,即使深层网络的变换部分效果不好,梯度仍然可以通过恒等分支直接回传,避免梯度消失。 - 层归一化:对每个样本的隐藏层特征做归一化,使数据分布保持稳定。和 Batch Normalization 不同,层归一化不依赖 batch 大小,因此特别适合序列模型。Transformer 解码时是逐个 token 生成的,batch 可能很小,甚至为 1,层归一化在这种情况下依然稳定。
这两个设计在 Transformer 中无处不在,理解它们能帮助你读懂很多源码中看似多余的+ x和norm(x)操作。
3. 序列建模的经典方案:RNN 与 LSTM
Transformer 火起来以后,很多人以为循环神经网络已经被完全取代。但 Transformer 中“位置编码”“序列关系建模”等设计,其实是在解决 RNN 最擅长解决的问题。所以复习 RNN 和 LSTM,能帮你理解 Transformer 为什么在这些地方做出了不同选择。
3.1 RNN 的核心思想
循环神经网络(Recurrent Neural Network)的核心是一个共享的循环单元。它按时间步迭代,每一步接收当前输入x_t,并结合上一步的隐藏状态h_{t-1},更新当前隐藏状态:
h_t = tanh(W_ih * x_t + W_hh * h_{t-1} + b)从代码角度看,一个最简单的 RNN 前向过程可以写成:
import numpy as np class SimpleRNN: def __init__(self, input_size, hidden_size): # 简化初始化,实际会用随机分布 self.W_ih = np.random.randn(input_size, hidden_size) * 0.1 self.W_hh = np.random.randn(hidden_size, hidden_size) * 0.1 self.b = np.zeros((hidden_size,)) def forward(self, inputs): # inputs: 序列,shape = (seq_len, input_size) h = np.zeros((self.W_hh.shape[0],)) hidden_states = [] for x in inputs: h = np.tanh(x @ self.W_ih + h @ self.W_hh + self.b) hidden_states.append(h) return np.stack(hidden_states, axis=0)这样一个结构天然适合处理变长序列。但它的缺点是:信息要沿着时间步逐步传递,如果序列很长,早期位置的信息会在传递过程中不断衰减,导致模型很难学习到长距离依赖关系。
3.2 LSTM 如何缓解长距离依赖问题
长短期记忆网络(Long Short-Term Memory)通过引入门控机制,让信息可以选择性地写入、遗忘和输出。它不要求每个时间步都完全覆盖旧信息,而是通过“记忆细胞”维护一条独立的信息高速公路。
LSTM 的核心公式有三扇门:
- 遗忘门:决定上一时刻的记忆细胞有多少应该被遗忘。
- 输入门:决定当前输入有多少应该写入记忆细胞。
- 输出门:决定当前记忆细胞有多少应该输出到隐藏状态。
理解 LSTM 不需要死记公式,关键是理解“门”的含义——门就是一个小型 Sigmoid 层,输出范围在 0 到 1 之间,表示“允许通过的比例”。RNN 没有这种选择性遗忘机制,所以会随着时间步累积信息衰减问题。
3.3 为什么 RNN 难以并行
RNN 的另一个问题是串行计算。每一个时间步的隐藏状态h_t都依赖于h_{t-1},这意味着无法在一个批次内同时对序列中的所有位置进行独立计算。即使你有 8 块 GPU,处理长度为 100 的序列时,依然要按顺序执行 100 个时间步的计算。
这个问题直接促使 Transformer 走向并行架构。Transformer 的核心思路是:不再把序列逐个时间步喂给模型,而是把整个序列作为一个矩阵输入,通过矩阵运算一次处理所有位置。自注意力机制在这个过程中替代了 RNN 的信息传递功能。
当你理解了 RNN 的优点和缺点,就能理解为什 Transformer 中要引入位置编码。因为 Transformer 不再按顺序处理序列,它必须通过额外的方式告诉模型“哪些 token 在前,哪些 token 在后”。
4. CNN 与序列特征提取的另一种思路
提到 Transformer 的前置概念,很多人会忽略 CNN。但 CNN 在序列建模中同样扮演过重要角色,尤其是一维卷积(1D Convolution)和 TextCNN 这类模型。
4.1 一维卷积如何理解句子
一维卷积在自然语言处理中的思路很直观:用一个固定大小的卷积核(例如宽度为 3 的窗口)在词向量序列上滑动,提取 n-gram 级别的局部特征。这个操作和在图像上做卷积很像,只是把二维卷积变成了沿时间维度滑动的一维操作。
例如,对一句话“我 / 爱 / 自然 / 语言 / 处理”,宽度为 3 的卷积核会依次覆盖“我爱自然”“爱自然语言”“自然语言处理”等多个局部片段,从而提取局部语义信息。
TextCNN 就是利用这种思路做文本分类的经典模型。它通过多个不同宽度的卷积核捕捉不同范围的 n-gram 特征,再通过池化操作得到整个句子的向量表示。
4.2 从 CNN 到 TCN 的尝试
传统 CNN 在序列建模中的局限是感受野受限。如果两个距离很远的词存在依赖关系,需要堆叠很多卷积层,或者使用很大的卷积核才能让它们“看见”彼此。
时域卷积网络(Temporal Convolutional Network,TCN)通过空洞卷积(Dilated Convolution)扩大感受野。空洞卷积允许卷积核在输入上按照指定间隔跳着取值,这样即使卷积核宽度不变,也能覆盖更大范围的输入区域。近年来,TCN 加 Transformer 的组合在股票预测、时间序列预测等领域很受欢迎,因为 TCN 能高效提取局部时序特征,Transformer 能捕捉全局依赖关系。
不过,CNN 也好,TCN 也好,它们都属于“局部到全局”的层层抽象思路,需要堆叠多层才能实现全局交互。而 Transformer 的自注意力一步到位,让所有位置之间直接建立联系。这也是“为什么最后是 Transformer”的一个关键答案。
从序列处理的发展线索看,整个演进过程是:
RNN -> 逐步传递信息,串行,长距离依赖弱 LSTM/GRU -> 门控机制缓解长距离衰减,但仍是串行 CNN/TCN -> 并行计算,但需要多层堆叠扩大感受野 Transformer -> 自注意力一步建立全局依赖,且可并行理清这条线,再看 Transformer 结构图就不会觉得它横空出世了。
5. 注意力机制:从 Seq2Seq 到 Transformer 的核心
如果说 RNN 是 Transformer 要打败的对手,那么注意力机制就是 Transformer 最核心的武器。注意力机制的诞生早于 Transformer,最早应用在 Seq2Seq 机器翻译模型中。
5.1 传统 Seq2Seq 模型的瓶颈
Seq2Seq 模型由编码器(Encoder)和解码器(Decoder)组成,编码器把输入序列压缩成一个固定长度的向量,解码器根据这个向量逐步生成输出。
这种做法的缺点是明显的:当输入序列很长时,把所有信息都压缩到一个向量里,早期输入的信息很容易在压缩过程中丢失。就像要求一个人听完一整本书后,只用一个词总结全书内容,然后要求他基于这个词回答书中任意细节——信息损失太大了。
5.2 Attention 的直觉:聚焦关键信息
注意力机制的引入解决了这个问题。它不再是“只看那一个固定向量”,而是在解码的每一步,重新查看编码器所有位置的隐藏状态,并计算它们对当前解码位置的“重要程度”。
这个“重要程度”就是注意力权重。数学上,它通常通过计算查询向量(Query)和键向量(Key)的相似度来得到:
score = Q * K^T attention_weight = softmax(score) context = attention_weight * V- Query:当前需要“查找什么信息”,在解码器一侧。
- Key:被查找方的“索引标签”,在编码器一侧。
- Value:被查找方真正提供的“内容”。
举个直觉的例子:当你在图书馆想找一本关于深度学习的书时,你的需求向量是 Query,书名或索书号是 Key,书籍内容是 Value。你先把 Query 和所有 Key 做匹配,得到每本书的分数,然后通过 softmax 变成权重,最后按权重汇总各本书的内容,得到一个和你需求最匹配的综合信息。
5.3 缩放点积注意力的代码实现
Transformer 使用的是缩放点积注意力(Scaled Dot-Product Attention),比加性注意力计算更高效。它的公式是:
Attention(Q, K, V) = softmax(Q * K^T / sqrt(d_k)) * V除以sqrt(d_k)是为了防止点积结果过大,导致 softmax 进入梯度很小的饱和区。下面用 NumPy 实现一个简单的缩放点积注意力:
import numpy as np def scaled_dot_product_attention(Q, K, V, mask=None): """ Q, K, V: shape = (batch_size, seq_len, d_k) """ d_k = K.shape[-1] # 计算 Q 和 K 的点积 scores = np.matmul(Q, K.transpose(0, 2, 1)) # (batch, seq_len, seq_len) # 缩放 scores = scores / np.sqrt(d_k) # 如果需要 mask,把不需要的位置替换为极小值 if mask is not None: scores = np.where(mask, scores, -1e9) # softmax 转换为权重 weights = np.exp(scores - np.max(scores, axis=-1, keepdims=True)) weights = weights / np.sum(weights, axis=-1, keepdims=True) # 加权求和 output = np.matmul(weights, V) return output, weights这段代码里最关键的是scores矩阵的解读:scores[i][j]表示序列中第i个位置对第j个位置的注意力得分。得分越高,说明i越应当关注j。
Transformer 中的多头注意力(Multi-Head Attention)就是把 Q、K、V 分别投影到多个子空间,然后并行执行多次注意力计算,最后把结果拼接起来。这样做的好处是,每个注意力头可以关注不同的关系模式——有的头关注语法依赖,有的头关注指代关系,有的头关注位置邻近关系。
5.4 Self-Attention 与 Cross-Attention 的区别
注意力机制根据 Q、K、V 的来源,可以分为两种:
- Self-Attention(自注意力):Q、K、V 都来自同一个序列。也就是说,序列中的每个位置都关注序列中的所有位置,包括自己。这是 Transformer 编码器的核心操作,用来建模输入序列内部的依赖关系。
- Cross-Attention(交叉注意力):Q 来自解码器,K 和 V 来自编码器。也就是说,解码器在生成每个词时,去关注输入序列的哪些部分。这是机器翻译等 Seq2Seq 任务中的核心操作。
理解这两者的区别,是阅读 Transformer 源码的基本功。很多初学者看到MultiheadAttention调用时,如果不清楚 Q、K、V 各自的来源,就会对维度变换一头雾水。
6. 表示学习与位置编码
除了注意力机制,Transformer 还依赖两个重要的表示学习概念:词嵌入(Embedding)和位置编码(Positional Encoding)。它们回答了同一个问题的两个侧面:“模型如何理解一个词”和“模型如何理解词的位置”。
6.1 词嵌入:从 One-Hot 到分布式表示
在深度学习出现之前,文本建模常用 One-Hot 编码。One-Hot 的做法是:如果一个词表有 10000 个词,每个词就表示成一个长度为 10000 的向量,只在对应词的位置上取值为 1,其余位置为 0。
One-Hot 的问题是:向量维度高、稀疏,而且任意两个词的向量内积都是 0,完全无法表示词之间的语义相似性。
词嵌入(Word Embedding)的思路是:把每个词映射到一个低维稠密向量,例如 128 维、256 维或 768 维。通过训练,语义相近的词在向量空间中的距离也更近。“国王”和“王后”的距离,会比“国王”和“苹果”更近。
早期有 Word2Vec、GloVe 这类独立的词向量训练工具,现在 Transformer 模型则直接把嵌入层作为网络的一部分,在训练过程中端到端地学习。PyTorch 中对应的层是torch.nn.Embedding:
import torch import torch.nn as nn vocab_size = 10000 embedding_dim = 256 embedding = nn.Embedding(num_embeddings=vocab_size, embedding_dim=embedding_dim) # 假设一个 batch 中有两个句子,每个句子 5 个 token input_ids = torch.tensor([[1, 5, 23, 78, 12], [3, 8, 45, 6, 99]]) embedded = embedding(input_ids) # shape = (2, 5, 256) print(embedded.shape)这里的input_ids是文本经过分词后,每个 token 在词表中的索引。嵌入层把这些索引映射成稠密向量。
6.2 为什么需要位置编码
RNN 通过逐个时间步处理序列,天然知道词的先后顺序。CNN 靠卷积核在时间维度上的滑动,也隐式包含了相对位置信息。但 Transformer 的自注意力计算是“不分先后”的——它同时对整个序列做矩阵运算,即使把句子中的词顺序打乱,计算出的注意力分数也不会改变。
为了让模型感知顺序信息,Transformer 在输入嵌入向量上叠加了位置编码。位置编码的作用是给每个位置生成一个独一无二的向量,加到 token 的嵌入向量上,这样模型既能知道“这个位置是什么词”,也能知道“这个词在句子的什么位置”。
Transformer 原文中使用的是正余弦函数形式的位置编码:
PE(pos, 2i) = sin(pos / 10000^(2i/d_model)) PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))其中pos表示位置,d_model表示嵌入维度,2i和2i+1表示维度的索引。这种形式的编码有几个好处:一是值域稳定在 [-1, 1],不会随位置增大而溢出;二是可以通过三角函数公式,让模型容易学习到相对位置关系。
下面给出位置编码的生成代码:
import numpy as np def positional_encoding(seq_len, d_model): pe = np.zeros((seq_len, d_model)) position = np.arange(seq_len).reshape(-1, 1) # (seq_len, 1) div_term = np.exp(np.arange(0, d_model, 2) * (-np.log(10000.0) / d_model)) pe[:, 0::2] = np.sin(position * div_term) # 偶数维度用 sin pe[:, 1::2] = np.cos(position * div_term) # 奇数维度用 cos return pe # 生成序列长度为 20,嵌入维度为 64 的位置编码 pe = positional_encoding(20, 64) print(pe.shape) # (20, 64)除了正余弦位置编码,后来也出现了可学习位置编码(Learned Positional Embedding),也就是说位置向量也作为可训练参数,由模型自己学习。BERT 就是采用这种方式。两种方式各有优劣,正余弦编码可以外推到比训练时更长的序列,可学习编码在训练长度范围内通常表现更稳定。
6.3 输入表示的整体过程
在 Transformer 中,一个 token 进入模型之前会经历如下步骤:
- 分词:把文本拆分成 token 序列。
- 查嵌入表:把每个 token 映射成向量,得到
token_embedding。 - 生成位置编码:根据每个 token 的位置生成位置向量。
- 相加:
final_embedding = token_embedding + positional_encoding。
如果是 BERT 这类预训练模型,还会额外加上 Segment Embedding(区分不同句子)。最终得到的向量序列输入到 Transformer 编码器中。
7. 把这些概念串联成 Transformer
前置概念复习到这里,你已经具备了理解 Transformer 原文结构图的全部知识。下面把各个概念组合起来,看它们如何在 Transformer 中协作。
Transformer 的整体架构是编码器-解码器结构。以机器翻译为例,编码器读取源语言句子,解码器逐步生成目标语言句子。
编码器由多个完全相同的层堆叠而成,每一层包含两个子层:
- 多头自注意力子层:让序列内部任意两个位置间建立关联。
- 前馈神经网络子层:对每个位置的表示做非线性变换。
每个子层后面都跟着残差连接和层归一化。
解码器同样由多层堆叠而成,每一层包含三个子层:
- 掩码多头自注意力子层:和编码器类似,但每个位置只能关注当前位置及之前的位置,不能“偷看”未来。
- 多头交叉注意力子层:Q 来自解码器,K、V 来自编码器,让解码器从源语言中提取信息。
- 前馈神经网络子层。
从上到下梳理一遍,你会发现:
- 词嵌入和位置编码负责把离散 token 变成带位置信息的向量序列。
- 自注意力负责建模序列内部的关系,替代 RNN 的信息传递。
- 多头机制让模型能同时关注多种关系模式,是特征表达的扩展。
- 前馈网络为每个位置提供非线性变换能力。
- 残差连接和层归一化保证深层网络的训练稳定性。
为什么最终是 Transformer 成为主流?核心原因是它在长距离建模能力和并行效率之间找到了最佳平衡点。RNN 因串行计算受限,CNN 因感受野受限,注意力机制虽然计算复杂度高一些,但在 GPU 上可以通过大规模矩阵运算来加速。随着算力不断增强,这个权衡的天平自然倒向了 Transformer。
从“手撕 Transformer”的角度看,理解本文前置概念后,你的初始目标可以定在“用 PyTorch 从零实现一个 mini Transformer”,并不依赖torch.nn.Transformer封装,而是自己写多头注意力、前馈网络、位置编码、层归一化和训练循环。这个练习做完,你对 Transformer 的理解会远超只会调 API 的人。
8. 常见概念混淆与学习建议
在学习 Transformer 前置概念的过程中,有几组概念特别容易混淆。这里整理成表格,方便对照:
| 混淆点 | 正确理解 | 说明 |
|---|---|---|
| Transformer 与 Attention | 注意力机制是组件,Transformer 用到了注意力机制 | Seq2Seq + Attention 早于 Transformer |
| Self-Attention 与 Cross-Attention | Self 中 QKV 同源,Cross 中 Q 与 KV 异源 | 编码器用 Self,解码器中间层用 Cross |
| 位置编码与词嵌入 | 词嵌入表示语义,位置编码表示顺序 | 两者相加得到最终输入 |
| LayerNorm 与 BatchNorm | LayerNorm 对每个样本归一化,BatchNorm 对每个特征跨样本归一化 | Transformer 用 LayerNorm |
| 多头注意力与多通道卷积 | 多头是多种关系子空间,不是多通道特征图 | 每个头独立计算后拼接 |
| RNN 与 Transformer 并行性 | RNN 串行,Transformer 并行 | 这是 Transformer 的核心优势之一 |
学习路线上,我建议按照下面的递归方式推进:
- 先用 PyTorch 实现单头缩放点积注意力。
- 再加入多头机制,实现 Multi-Head Attention。
- 加入残差连接和层归一化,搭出一个编码器层。
- 加入掩码机制,实现解码器层。
- 叠层后训练一个简单的机器翻译模型,比如英语到法语的小规模数据集。
每一步都对应本文复习过的一个前置概念。这样做的好处是:每行代码都能对应到具体的设计意图,而不是照抄源码。
我还想补充一点学习心态。Transformer 相关的论文、源码、专栏文章非常多,信息过载是这个领域最典型的问题。我的建议是不要试图一次读完所有材料,而是定住一条主线——注意力机制的核心计算、多头注意力的维度变化、残差与归一化为什么必要——沿着这条线反复精读。遇到不懂的中间概念,再回来复习,这样比来回跳读效率高得多。
如果你手头有原文 PDF,可以重点精读其中第 3.2 节(Attention 部分)和第 3.3 节(位置编码前向过程),配合本文的代码示例一起看,理解速度会快很多。看完之后,再打开 PyTorch 的nn.Transformer源码,你会发现自己能看懂大部分实现了。
9. 最后说几句
Transformer 本身并不复杂,它是由若干个你已经学过的概念组合而成的。真正让它显得“难”的,是概念密度高、前置依赖多,以及大多数资料默认读者已经掌握了 RNN、CNN、注意力机制和词嵌入。把这门前置课补上,后续阅读源码、跑实验、做改进都会顺畅很多。
我推荐你找一个最短的 Transformer 实现,把它从头到尾敲一遍,遇到不理解的模块就回到本文对应章节复习。动手敲代码是检验理解的唯一标准,光看不写很难真正掌握。
如果这篇文章对你准备“手撕 Transformer”有帮助,可以收藏备用。接下来你可以继续阅读“Transformer 代码拆解”或“从零实现多头注意力机制”方向的实战文章,把前置概念落实到每一行代码里。