🐸 青蛙编程网
首页 / AI / Transformer 原理 / 2. 从文字到向量

2. 从文字到向量:分词与嵌入

这是什么:分词(Tokenization) = 把一句话切成一个个小片段;嵌入(Embedding) = 给每个片段在一张大表里查出一行固定长度的数字,叫词向量。

为什么用:模型只认识数字。想让大模型接龙,第一步就是"话变数字"。上一章用点积算的"相似度",算的对象也正是这些向量——先把话变成向量,注意力才有东西可比。

如何用:本章把"猫吃鱼了"切成词、编码成 id、再到查表里领出一行行向量,看数字如何顶替文字登场。

第一步:分词——把句子切成查字典的"字条"

模型出厂自带一本词表(vocabulary):表里每行是一个它认识的小片段,配一个编号。把句子拿去词表里查号,碰上词表没收的词就用一个兜底片段<未知>顶上:

import numpy as np

vocab = {"<未知>": 0, "猫": 1, "吃": 2, "鱼": 3, "了": 4}   # 假装的小词表
sentence = ["猫", "吃", "鱼", "了"]

ids = [vocab.get(w, 0) for w in sentence]   # 查表编码,查不到就给 0(<未知>)
print(ids)

你会看到:

[1, 2, 3, 4]
代码 作用
vocab = {...} 词表:片段 → 编号,真实模型有几万到几十万行
vocab.get(w, 0) 查字典:查不到返回默认值 0,即兜底片段 <未知>
列表推导式 一行把整句切成 id 序列,"猫吃鱼了"变成 [1,2,3,4]

第二步:嵌入——每个 id 领一行自己的数字

id 只是个编号,还带不来"猫和狸花猫很像"这种信息。真正的钥匙是嵌入矩阵(embedding matrix):每行对应一个 id,存的就是这个片段的向量——矩阵是训练出来的,谁和谁常一起出现,向量方向就挨得近:

E = np.array([
    [0.0, 0.0, 0.0],   # <未知>
    [0.9, 0.1, 0.0],   # 猫
    [0.2, 0.8, 0.3],   # 吃
    [0.1, 0.3, 0.9],   # 鱼
    [0.4, 0.6, 0.8],   # 了
])
vectors = E[ids]           # 按 id 逐行取出,4 个词各领一行向量
print(vectors)
print(vectors.shape)

你会看到:

[[0.9 0.1 0. ]
 [0.2 0.8 0.3]
 [0.1 0.3 0.9]
 [0.4 0.6 0.8]]
(4, 3)
代码 作用
E = np.array([...]) 嵌入矩阵:行数 = 词表大小,列数 = 向量维度(示意 3 维,真实是几百到几千维)
E[ids] NumPy 的花式索引:用 id 列表一次取出 4 行
vectors.shape (4, 3):整句变回了 4×3 的纯数字矩阵

记忆口诀:话切成片段,片段查编号;编号领向量,数字才登场。

第三步:还差一件事——位置

到目前为止嵌入只回答了"是哪个词",没回答"排在第几"。"猫追狗"和"狗追猫"的词向量一模一样,含义却相反。Transformer 的解法不是词表,而是一套专门的位置编码——第 5 章再拆。

动手改

  1. 在 sentence 里塞一个词表没有的词(如 ["猫", "啃", "鱼"]),看 id 序列怎么变出兜底的 0
  2. 把嵌入矩阵的维度从 3 改到 5(每行补两个数),观察 shape 的输出
  3. 手算 E[1] 和 E[2] 的点积,再改大第一维的差异,看点积怎么变

下一章:注意力机制,让每个词学会"看该看的人" 🐸

📥 下载本章代码

ZIP 内含本章全部代码文件与说明,解压即用,与教程内容同步生成。