第3章 输入是如何进入 Transformer 的本章目标理解文字是如何一步步变成 Transformer 可以处理的向量的以及为什么需要 Position Encoding。3.1 从文字到向量的三步I love AITokenizer分词[101, 2293, 9932]Token IDEmbedding Layer查表[[0.1, 0.3, ...],[0.5, 0.2, ...],[0.8, 0.1, ...]]Position Encoding加位置信息最终输入向量三步Tokenizer文字 → Token IDEmbeddingToken ID → 向量Position Encoding向量 位置信息3.2 Tokenizer文字变成数字Tokenizer 的任务是把文字切成 Token再把每个 Token 映射到一个整数 ID。3.2.1 什么是 TokenToken 不一定是一个完整的单词可以是一个完整单词love→2293一个子词learning→[learn, ##ing]→[4553, 1075]一个字符A→65一个标点.→1193.2.2 为什么要用子词方案优点缺点字符级词表小序列太长语义弱单词级语义强词表太大OOV问题子词级BPE/WordPiece平衡需要训练现代 LLM 几乎都用子词方案GPT 系列BPEByte Pair EncodingBERTWordPieceQwen/LlamaBPE基于字节3.2.3 一个例子输入I love deep learning. Tokenizer 输出BERT [CLS] I love deep learning . [SEP] 101 146 2293 1996 4083 119 102[CLS]和[SEP]是特殊 Token后面章节会详细讲。3.3 Embedding Layer数字变成向量Token ID 只是一个整数神经网络无法直接处理。Embedding Layer 的作用是把每个 Token ID 映射到一个 d_model 维的向量。本质上是一个查找表Lookup Table词表大小 30000 d_model 512 Embedding 矩阵形状[30000, 512] Token ID 2293 → 取第 2293 行 → 得到一个 512 维向量Token ID: 2293Embedding 矩阵[30000 × 512]向量: [0.1, 0.3, 0.5, ..., 0.2]512维3.3.1 Embedding 是如何训练的Embedding 矩阵是随机初始化的在训练过程中通过反向传播更新。训练完成后语义相近的词向量也相近向量(king) - 向量(man) 向量(woman) ≈ 向量(queen)3.3.2 Tensor Shape输入 Token ID[batch_size, seq_len] 例如 [32, 128] 经过 Embedding[batch_size, seq_len, d_model] 例如 [32, 128, 512]3.4 为什么 Transformer 不认识顺序这是一个非常重要的问题。RNN 天然知道顺序因为它按时间步处理t1: I t2: love t3: AI但 Transformer 的 Self-Attention 是所有 Token 同时计算互相交互。这意味着[I, love, AI]和[AI, love, I]在 Self-Attention 看来是完全一样的因为 Attention 只关心 Token 之间的关系不关心顺序。所以必须手动告诉 Transformer每个 Token 在第几个位置。这就是Position Encoding。3.5 Absolute Position Encoding绝对位置编码原始论文使用正弦/余弦函数生成位置编码PE(pos,2i)sin(pos100002i/dmodel)PE_{(pos, 2i)} \sin\left(\frac{pos}{10000^{2i/d_{model}}}\right)PE(pos,2i)sin(100002i/dmodelpos)PE(pos,2i1)cos(pos100002i/dmodel)PE_{(pos, 2i1)} \cos\left(\frac{pos}{10000^{2i/d_{model}}}\right)PE(pos,2i1)cos(100002i/dmodelpos)其中posToken 的位置0, 1, 2, …i向量的维度索引d_model向量维度如 5123.5.1 为什么用正弦/余弦特性说明有界值在 [-1, 1] 之间不会太大唯一每个位置的编码都不同相对关系任意两个位置的相对距离可以用线性变换表示外推可以处理训练时没见过的更长序列3.5.2 如何使用Token Embedding[batch, seq_len, d_model] Position Encoding[seq_len, d_model] 最终输入 Token Embedding Position EncodingToken Embedding[batch, seq, d_model]Position Encoding[seq, d_model]最终输入[batch, seq, d_model]3.5.3 为什么相加不会互相干扰一个自然的疑问位置信息和语义信息直接相加后面的网络怎么分辨哪些是位置、哪些是语义答案有三层高维空间的正交性在 512 维空间中两个随机向量几乎总是接近正交的夹角接近 90°。这意味着 Position Encoding 和 Token Embedding 在大多数维度上不会互相干扰——它们各自占据了高维空间的不同方向。网络会学着分辨Attention 层的WQW_QWQ、WKW_KWK、WVW_VWV矩阵是可学习的。它们可以学会用某些维度关注位置信息、用另一些维度关注语义信息。实际上研究表明某些 Attention Head 确实学到了主要关注位置关系的模式。这确实不是最优方案你的直觉是对的——相加确实存在信息混淆的问题。这也是为什么后来 RoPE 取代了绝对位置编码。RoPE 不是把位置加到向量上而是把位置编码到 Q 和 K 的旋转中见第15章从根本上避免了这个问题。3.6 Learned Position Embedding可学习位置编码BERT 和 GPT 使用的是可学习的位置编码位置编码也是一个 Embedding 矩阵通过训练学习。Position Embedding 矩阵[max_seq_len, d_model] 例如 [512, 768] 位置 0 → 取第 0 行 位置 1 → 取第 1 行 ...方案优点缺点正弦/余弦固定可外推到更长序列不能学习可学习更灵活不能外推到训练时没见过的长度3.6.1 绝对位置编码的根本问题无论是正弦/余弦还是可学习方案绝对位置编码都有一个深层问题问题同一个词在不同位置会得到完全不同的输入向量句子A: I love AI → love 的输入 E(love) P₂ 句子B: ... I love AI → love 的输入 E(love) P₁₀₂两个 love 的语义完全相同但因为位置不同送入 Transformer 的向量截然不同。更深的问题语言真正在意的是相对位置而非绝对位置The cat sits on the mat. Yesterday the little black cat sits on the old mat.虽然 cat 的绝对位置从2变成了5但 cat 和 sits 的相对距离没变——语法关系不取决于第几个词而取决于离谁多远。这就是为什么绝对位置编码最终被 RoPE 取代——RoPE 把位置编码到 Q 和 K 的旋转中使得 Attention Score 天然只依赖相对距离。3.7 RoPERotary Position EmbeddingRoPE 是 Qwen、Llama、DeepSeek 等现代 LLM 使用的位置编码方案。3.7.1 核心思想RoPE 不是把位置信息加到向量上而是把位置信息编码到 Query 和 Key 的旋转中。数学上对于位置m的 Query 向量qqqRoPE 将其旋转一个角度qmRm⋅qq_m R_m \cdot qqmRm⋅q其中RmR_mRm是一个旋转矩阵角度与位置m相关。这样两个 Token 的 Attention Score 只依赖于它们的相对位置而不是绝对位置。3.7.2 为什么 RoPE 更好特性绝对位置编码RoPE外推能力差好相对位置感知间接直接长序列支持有限强配合 YaRN 等技术实现复杂度简单中等3.8 TensorFlow 实现3.8.1 Embedding Layerimporttensorflowastf vocab_size30000d_model512seq_len128batch_size32# Token Embeddingembeddingtf.keras.layers.Embedding(vocab_size,d_model)# 输入 Token ID: [batch, seq_len]token_idstf.random.uniform([batch_size,seq_len],maxvalvocab_size,dtypetf.int32)# 输出: [batch, seq_len, d_model]xembedding(token_ids)print(x.shape)# (32, 128, 512)3.8.2 正弦位置编码importnumpyasnpdefget_positional_encoding(seq_len,d_model):positionsnp.arange(seq_len)[:,np.newaxis]# [seq_len, 1]dimsnp.arange(d_model)[np.newaxis,:]# [1, d_model]angle_rates1/np.power(10000,(2*(dims//2))/d_model)anglespositions*angle_rates# 分别计算 sin 和 cos写入新数组避免原地修改penp.zeros((seq_len,d_model))pe[:,0::2]np.sin(angles[:,0::2])pe[:,1::2]np.cos(angles[:,1::2])returntf.cast(pe[np.newaxis,:,:],dtypetf.float32)# [1, seq_len, d_model]peget_positional_encoding(128,512)print(pe.shape)# (1, 128, 512)# 加到 Embedding 上xxpeprint(x.shape)# (32, 128, 512)3.9 Tensor Shape 总结Token IDs[batch, seq_len]例: [32, 128]Embedding LayerToken Embeddings[batch, seq_len, d_model]例: [32, 128, 512]Position Encoding[1, seq_len, d_model]例: [1, 128, 512]最终输入[batch, seq_len, d_model]例: [32, 128, 512]本章总结步骤输入输出作用Tokenizer文字Token ID文字→整数EmbeddingToken ID向量整数→语义向量Position Encoding向量向量位置告诉模型顺序本章思考题为什么 Transformer 需要显式的 Position Encoding而 RNN 不需要如果去掉 Position EncodingTransformer 会有什么问题BPE 和 WordPiece 的主要区别是什么为什么 RoPE 比绝对位置编码更适合长序列下一章预告下一章我们进入Transformer Block。这是整本书最核心的部分我们将看到Self-Attention 是如何计算的Residual 为什么能防止梯度消失LayerNorm 在哪里Feed Forward 的作用是什么