token id 只是个数字,模型怎么知道它是什么意思?—— 这一课回答你自己提的 Q5。
| Topic | Embedding(词嵌入) |
| Objectives | 搞懂 token id → 有意义的向量;修正 Token 课的两个误区 |
| Prerequisites | Token ✅ |
| Outcome | 能解释"意义"存在哪里,以及 transformer 真正吃的是什么 |
| Warning | embedding 只是入口,还没"理解上下文"(那是 attention 的活) |
tokenizer 给了你一串整数:[1917, 995, ...]。但整数不能直接喂神经网络,两个原因:
1917 和 1918 只差 1,但可能是 "world" 和 "banana",毫无关系。神经网络会做加减乘 —— 直接喂整数等于骗它"这俩很接近"。我们需要一种表示:语义相近的 token → 数值上也相近。这就是 embedding 要解决的问题。
想象一张巨大的查找表 E,本质就是个矩阵。token id = 行号,取出对应的一整行向量:
1917。整数只负责"去第几行取"。[N × d_model] 矩阵。这个"矩阵"发生在 embedding 层,不是 tokenizer;tokenizer 只吐整数序列。表 E 里的数字一开始是随机的,不是人填的,而是在训练中学出来的:
训练时模型不断读海量文本、预测下一个词;预测错了就微调这些数字。经过千亿次调整,出现在相似上下文里的 token,它们的向量会被拉到相似的位置。
所以"意义"不在整数 1917 里,而在它那行向量在高维空间里的"位置"里。 cat 和 dog 总出现在相似句子中 → 向量靠得近;world 和 banana → 离得远。
vector("king") − vector("man") + vector("woman") ≈ vector("queen")
"性别"是一个方向,"王室身份"是另一个方向 —— 早期 word2vec 的经典直觉,帮你建感觉。
like=123 和 like=7 有区别吗?没区别。 模型拿到整数后第一件事就是把它当行号去 embedding 表取向量,从此再也不碰这个整数——它不会对 123 做任何加减乘。整数的"数值大小"在取完向量那一刻就被彻底丢掉,只有"取的是哪一行"有意义。所以换门牌号不影响座位上那个向量该学成什么样。编号分配是任意的,没有优劣。
"任意"的限定词是——任意,但前后必须一致(一个固定的置换)。把 like 从 123 改成 7,就必须同时把它的向量从第 123 行搬到第 7 行,且 tokenizer 以后永远给 like 吐 7。
tokenizer 和 embedding 表是一对绑定的搭档。不能对训练好的模型随便交换 id,除非把 embedding 表的行也按同样方式重排。编号自由,但"编号 ↔ 向量行"的对应一旦定了就锁死。
"tokenizer 做的事"其实是两个不同问题,优劣情况相反:
| 切成哪些碎片(词表设计) | 有优劣 —— 好词表更高效(更少 token、生僻词/多语言更好)。BPE 统计在优化这个。 |
| 每个碎片编成几号(id 分配) | 无优劣 —— 纯任意,通常按合并顺序/频率排,但换一套也一样。 |
不会,index 永远不迁移。 关键在于区分两种完全不同的"训练":
| ① Tokenizer 训练(BPE) | 决定「切成哪些碎片」+「每个碎片编几号」= 那张词表(token↔id 映射)。建模型之前单独跑一次,之后冻结,再不变。 |
| ② 模型训练(梯度下降) | 只改 embedding 表里每行向量的数值。id 映射当作常量不动,只有向量在变。 |
所以 like=123 由 ① 定死;② 阶段 index 绝不迁移,改的只是"第 123 行那串数字"。
🏠 门牌号在开训前就钉死了;训练只重新装修每个房间里的家具(向量),不搬门牌。
like+d → lik+ed),之前的 embedding 要全重跑吗?对一个已训练好的模型:tokenizer 锁死,改切分 ≈ 重训。 整个模型(不只 embedding,还有 attention/FFN)都是假设着这套切分训出来的;改了切分,喂进去的 id 序列全变,而表里各行学的还是旧含义 → 输出垃圾。
但"积重难返"只对半:没人会中途改主意。 tokenizer 是开训前一次性选好、然后冻结的,不存在"训练到一半改切分"。发现更好的方案 → 下一代新模型用新 tokenizer 从头训(如 GPT-2/3 与 GPT-4 用不同 tokenizer,是各自独立训练的模型)。所以不是被早期选择永远困住,而是"每个新模型可重选;一旦进入训练就锁死"。
| 新增 token(加新词) | 较轻 —— embedding 表加几行、初始化、微调即可,老的不动。 |
| 改动已有切分边界 | 很重 —— 老 id 含义与分布全变 ≈ 重训。 |
补充:不只是 embedding 要重跑,整个模型都依赖那套 token 分布;embedding 只是"最直接绑定"的一层。学术上有"换 tokenizer 不从头训"的技巧(旧词表向量映射到新词表当初始化再微调,如 vocabulary transfer / WECHSEL),能省掉完整预训练,但仍需可观微调。
"重训"是个光谱,从最贵到最省三档:
| ① 从头预训练 | 权重随机初始化 → 在海量原始文本上做 next-token prediction。换 tokenizer 通常是新一代模型的一揽子改动之一,反正从头训。标准做法,代价最大。 |
| ② 词表迁移 + 继续训练 | 复用旧模型深层权重(attention/FFN 不扔),只把 embedding 表按"旧词↔新词"映射重新初始化(WECHSEL / vocabulary transfer),再继续训练一段。省钱中间档,但仍需可观训练。 |
| ③ 蒸馏(distillation) | 让"学生"模型模仿"老师"的输出概率分布,主要用于压缩(大模型学成小模型)或迁移能力。 |
蒸馏不是修 tokenizer 的自然解法: 它在 token 概率分布层面对齐,若师生用不同 tokenizer,两边输出词表对不上号(老师第 1917 号 ≠ 学生第 1917 号)。跨 tokenizer 蒸馏有研究但很 tricky,非常规。
答完 3 题,再按格式给我一份 Lesson Summary(My Understanding / Key Mental Models / Remaining Questions / Misconceptions Fixed)。批改后进入 Vector 向量空间直觉(很短),然后上 Transformer。