在 NLP 模型中输入的第一步几乎都是同一个操作—把离散的词语变成连续的向量这个操作就是Embedding Lookup。它看起来简单但理解它的本质是读懂 Transformer、GPT、BERT 等一切语言模型的前提。一、从一个最基本的问题出发 神经网络只能处理数值不能直接处理applebanana这样的文本。那么怎样把一个词交给神经网络 最直觉的方案是给每个词分配一个编号索引然后通过某种方式把编号转换成一组浮点数向量——这组向量就是词的embedding而通过索引取出向量这个动作就是Embedding Lookup。二、直观理解Embedding Lookup 在做什么 假设我们有一个包含 4 个词的词表[apple,banana,orange,fruit] 同时我们维护一个embedding 矩阵每一行对应一个词的向量表示索引词embedding 向量 (示例)0apple[0.1, 0.3, 0.5]1banana[0.2, 0.4, 0.6]2orange[0.3, 0.5, 0.7]3fruit[0.0, 0.1, 0.2] 当模型遇到banana时它不关心这个字符串本身只关心它的索引111。Embedding Lookup 做的事情就是拿着索引 1去矩阵里取第 1 行返回[0.2,0.4,0.6][0.2, 0.4, 0.6][0.2,0.4,0.6]。 简单说embedding lookup 就是用词的索引去查 embedding 表然后返回对应的向量。你可以把 embedding 矩阵想象成一本词向量字典—Embedding Lookup 就是翻到某一页把对应的向量抄出来。三、形式化定义3.1 数学视角 设词表大小为VVVembedding 维度为DDD则 embedding 矩阵定义为E∈RV×D E \in \mathbb{R}^{V \times D}E∈RV×D 对于输入词wiw_iwi索引为iiiEmbedding Lookup 的操作为Embedding Lookup(i)E[i,:]∈RD \text{Embedding Lookup}(i) E[i,:] \in \mathbb{R}^DEmbedding Lookup(i)E[i,:]∈RD 以上面的例子为例E[0.10.30.50.20.40.60.30.50.70.00.10.2]∈R4×3 E \begin{bmatrix} 0.1 0.3 0.5 \\ 0.2 0.4 0.6 \\ 0.3 0.5 0.7 \\ 0.0 0.1 0.2 \end{bmatrix} \in \mathbb{R}^{4 \times 3}E0.10.20.30.00.30.40.50.10.50.60.70.2∈R4×3 查找banana索引 1E[1,:][0.2,0.4,0.6] E[1,:] [0.2, 0.4, 0.6]E[1,:][0.2,0.4,0.6] 本质上这就是一次矩阵的行索引操作没有任何乘法或加法。 虽然前向只是索引但在反向传播时梯度会精确回传到被查到的那一行 embedding 向量上因此 embedding 矩阵可以被逐行高效地学习。3.2 PyTorch实现 在代码层面这通常就是一次矩阵索引importtorch# 假设词表大小为 4embedding 维度为 3embedding_matrixtorch.tensor([[0.1,0.3,0.5],[0.2,0.4,0.6],[0.3,0.5,0.7],[0.0,0.1,0.2]])# 输入词索引word_indextorch.tensor([1])# bananaembedding_vectorembedding_matrix[word_index] 输出即为[0.2,0.4,0.6]四、Embedding Lookup 与 One-Hot 编码的关系4.1 从 One-Hot 到 Embedding 在深度学习早期更常见的做法是先将词编码为 one-hot 向量再通过矩阵乘法映射到低维空间。以banana为例Step 1构造 one-hot 列向量x[0100] \mathbf{x} \begin{bmatrix} 0 \\ 1 \\ 0 \\ 0 \end{bmatrix}x0100Step 2计算E⊤xE^\top \mathbf{x}E⊤xE⊤x[0.10.20.30.00.30.40.50.10.50.60.70.2][0100][0.20.40.6] E^\top \mathbf{x} \begin{bmatrix} 0.1 0.2 0.3 0.0 \\ 0.3 0.4 0.5 0.1 \\ 0.5 0.6 0.7 0.2 \end{bmatrix} \begin{bmatrix} 0 \\ 1 \\ 0 \\ 0 \end{bmatrix} \begin{bmatrix} 0.2 \\ 0.4 \\ 0.6 \end{bmatrix}E⊤x0.10.30.50.20.40.60.30.50.70.00.10.201000.20.40.6 结果和直接 lookup 完全一致——因为 one-hot 向量中只有一个 1E⊤xE^\top \mathbf{x}E⊤x实际上就是从E⊤E^\topE⊤中选出对应的一列等价于EEE的对应行。这是线性代数的基本性质矩阵右乘一个 one-hot 列向量eie_iei结果就是取出该矩阵的第iii列。A,eiA:,i A , e_i A_{:,i}A,eiA:,i所以 $E^\top e_i$ 取出的是 $E^\top$ 的第 $i$ 列而 $E^\top$ 的第 $i$ 列就是 $E$ 的第$i$ 行——正好就是第 $i$ 个词的 embedding 向量。4.2 为什么不用 One-Hot 既然结果等价为什么要用 Embedding Lookup 替代 one-hot 乘法看一组对比One-Hot 矩阵乘法Embedding Lookup空间需构造VVV维稀疏向量仅需一个整数索引计算量O(V×D)O(V \times D)O(V×D)O(1)O(1)O(1)实际操作大量乘以 0的无效计算直接定位目标行 特别地当词表规模达到 5 万GloVe、3 万BERT WordPiece甚至 15 万GPT-2 BPE时为每个 token 构造一个数万维的稀疏向量再做矩阵乘法显然是不可接受的浪费。Embedding Lookup 是对这一过程的等价简化。五、 PyTorch 中的nn.Embedding 理解了原理之后来看实际代码。PyTorch 提供了nn.Embedding模块来封装整个 lookup 过程。5.1 基本用法importtorchimporttorch.nnasnn# 创建 embedding 层词表大小 10000向量维度 256embednn.Embedding(num_embeddings10000,embedding_dim256) 这行代码在内部创建了一个 shape 为(10000, 256)的 embedding 矩阵每一行对应词表中一个词的 256 维向量。5.2input_ids的含义 在 NLP 中文本需要先经过分词 编码才能输入模型。假设我们有两个句子句子 1: I love deep learning → 分词后 → [I, love, deep, learning] 句子 2: AI is fun PAD → 分词后 → [AI, is, fun, PAD] 每个词在词表中有一个唯一索引由 tokenizer 查表得到于是两个句子变成两行整数# 每个数字是该词在词表中的索引不是词本身# 一行 一个句子一列 一个位置input_idstorch.tensor([[12,345,6789,0],# I12, love345, deep6789, learning0[42,7,999,3]# AI42, is7, fun999, PAD3])# shape: (2, 4) — 2 个句子每个句子 4 个词5.3 批量Lookup 过程 把input_ids传入 embedding 层每个整数索引会被替换为对应的 256 维向量vectorsembed(input_ids)# shape: (2, 4, 256) shape 变化(2, 4)→(2, 4, 256)即(batch_size, seq_len)→(batch_size, seq_len, embed_dim)。 展开来看这一步等价于# 对 input_ids 中的每个索引分别去 embedding 矩阵查一行vectors[0][0]embed.weight[12]# I 的向量vectors[0][1]embed.weight[345]# love 的向量vectors[0][2]embed.weight[6789]# deep 的向量vectors[0][3]embed.weight[0]# learning 的向量vectors[1][0]embed.weight[42]# AI 的向量# ... 以此类推 本质上就是对每个位置做了一次 Embedding Lookup只不过nn.Embedding帮我们批量完成了。5.4 其他实用功能权重自动初始化embed.weight是一个(V, D)的nn.Parameter创建时随机初始化训练过程中随模型一起更新。padding_idx可以指定某个索引为填充符号其向量始终为零向量且不参与梯度更新embednn.Embedding(num_embeddings10000,embedding_dim256,padding_idx0)# index 0 的向量永远是 [0, 0, ..., 0]且不会被训练更新加载预训练权重可以用 Word2Vec、GloVe 等预训练向量初始化 embedding 矩阵pretrained_vectors...# shape: (V, D) 的 Tensorembednn.Embedding.from_pretrained(pretrained_vectors,freezeFalse)# freezeFalse 表示微调时继续更新freezeTrue 则冻结权重六、Embedding Lookup 在哪里被使用 Embedding Lookup 不仅仅用于 word embedding它几乎出现在所有需要将离散 ID 映射为连续向量的场景中场景输入Embedding 矩阵词嵌入Word Embedding词/token 的索引词向量矩阵位置编码Positional Embedding位置索引 0, 1, 2, …位置向量矩阵Token Type EmbeddingBERT句子类型 0 或 1类型向量矩阵推荐系统用户/物品 ID用户/物品向量矩阵图神经网络节点 ID节点特征矩阵 在 BERT 中一个 token 的输入表示就是三次 Embedding Lookup 的结果之和hiEtoken[ti]Eposition[i]Esegment[si] \mathbf{h}_i E_{\text{token}}[t_i] E_{\text{position}}[i] E_{\text{segment}}[s_i]hiEtoken[ti]Eposition[i]Esegment[si] 可以理解为“这个词是什么 它在第几个位置 它属于哪句话”七、总结Embedding Lookup用索引从 embedding 表中查找向量是 NLP、推荐系统、GNN 中最基础也最关键的操作本质是索引但梯度可以精确回传是 One-Hot 矩阵乘法的高效等价形式理解 Embedding Lookup等于真正迈进了深度学习处理离散符号的世界。