第 3 章:NLP 基础与文本表示¶
1. 本章要解决的问题¶
前两章里,我们已经建立了一个非常关键的认识:
- 模型训练时处理的是数字,不是字符串
- PyTorch 里流动的是 Tensor,不是自然语言本身
- loss、梯度、参数更新,都是建立在数值计算之上的
但这时会立刻冒出一个非常现实的问题:
人写出来的是文本,模型吃进去的是数字。
那么一句:
I love deep learning
到底是怎么从“人能读懂的句子”,变成“模型能处理的向量”的?
这就是本章要解决的问题。
这一章不会直接展开复杂的 Transformer 结构,也不会提前进入 Tokenizer 的工程细节,而是先把更底层的一条链路讲清楚:
文本 -> token -> id -> 向量表示 -> 上下文中的表示
如果这条链路不清楚,后面你会很容易把很多概念混在一起:
- token 和 word 是不是一回事
- vocabulary 到底是什么
- embedding 是不是就是 tokenizer
- Word2Vec 和 LLM 里的 embedding 有什么关系
- 为什么还需要 subword,而不能直接按整词处理
所以这一章的作用,可以理解为:
- 它承接前两章的“模型只能做数值计算”
- 它为第 4 章的“语言模型到底在预测什么”做准备
- 它也为第 8 章的“Tokenizer 与数据处理”打概念基础
如果第 1 章讲的是“训练闭环”,第 2 章讲的是“训练闭环如何写成 PyTorch 代码”,那么第 3 章讲的就是:
训练闭环里的输入文本,最开始是怎么被表示出来的。
2. 你学完后应该会什么¶
- 能解释为什么文本必须先变成离散符号和数字 id,模型才能处理
- 能区分
token、vocabulary、token id、embedding这几个最容易混淆的概念 - 能理解 one-hot 表示的基本思想,以及它为什么不适合作为现代语言模型的主表示方式
- 能理解静态词向量和上下文表示的差别
- 能解释 OOV 问题是什么,以及为什么现代 LLM 大多采用 subword 粒度
- 能顺着“文本表示”这条主线自然过渡到下一章的语言模型
3. 为什么文本表示是 NLP 的起点¶
在图像任务里,一张图片天然就可以表示成像素矩阵。
例如一张彩色图片,本质上就是一个形状类似 [H, W, 3] 的数值张量。
但文本不一样。
一句话从人的角度看很自然:
today is a good day
可从计算机底层看,它首先只是一串符号。
如果不先建立一种表示方法,模型根本不知道:
- 哪些位置是一个完整词
- 每个词应该对应哪个整数
- 不同词之间是否相似
- 句子里同一个词在不同上下文中的意思是否一样
所以自然语言处理的第一步,从来不是“理解语义”,而是先解决:
如何把文本稳定地映射成模型可以计算的对象。
这件事可以粗略分成两层:
第一层:离散化¶
也就是先决定把文本切成哪些基本单元。
例如一句话:
I love deep learning
可能被切成:
Ilovedeeplearning
也可能进一步切成更小的片段,比如 subword。
第二层:数值化¶
也就是把这些离散单元映射成数字表示。
例如:
I -> 17love -> 208deep -> 931learning -> 5021
再进一步把这些 id 变成向量。
只有完成这两步,文本才真正变成模型能处理的输入。
4. 从文本到 token:模型最先看到的不是句子,而是离散单元¶
很多初学者第一次接触 NLP 时,会下意识认为模型是“按词”理解文本的。
这在某些传统方法里不算错,但在现代 LLM 里,这种理解往往不够准确。
更准确的说法是:
模型首先看到的,不是原始句子,而是一串 token。
4.1 什么是 token¶
token 可以理解成:
文本在进入模型之前,被切分出来的最小处理单元。
这个单元不一定等于“一个完整单词”。
例如英文里它可能是:
- 一个词
- 一个词的一部分
- 标点符号
- 特殊符号
中文里则更明显,因为中文天然没有空格分词,token 可能是:
- 单个字
- 多字词片段
- 标点符号
所以你要先把一个误区拿掉:
token != word
在一些场景下它们可能重合,但在很多现代模型里,token 更接近“Tokenizer 定义的处理单元”。
4.2 什么是 vocabulary¶
如果 token 是模型使用的离散符号,那么 vocabulary 就是:
模型允许使用的全部 token 集合。
可以把它想成一本字典。
字典里的每个 token 都有一个固定编号,也就是 token id。
例如:
<pad> -> 0<bos> -> 1<eos> -> 2I -> 17love -> 208learn -> 931ing -> 415
后面模型真正看到的,通常不是字符串本身,而是这些编号构成的序列。
4.3 token id 是什么¶
token id 就是 token 在词表中的整数索引。
例如句子:
I love learning
如果按某个词表切分后得到:
Ilovelearning
那么对应的 id 序列可能是:
[17, 208, 931, 415]
这一刻开始,模型处理的已经不是“英语单词”,而是整数序列。
这点非常关键,因为后面你看到的 embedding lookup、位置编码、self-attention,都是建立在“输入已经是一串 token id”的前提上。
5. One-hot:最原始的文本数值表示¶
有了 token 和 token id 之后,下一步自然会问:
一个 token 应该怎样表示成向量?
最朴素的方法,就是 one-hot。
5.1 one-hot 的定义¶
假设词表大小是 5:
catdogapplerunblue
那么每个词都可以用一个长度为 5 的向量表示,并且只有自己所在的位置是 1,其余位置全是 0。
例如:
cat = [1, 0, 0, 0, 0]dog = [0, 1, 0, 0, 0]apple = [0, 0, 1, 0, 0]
这就是 one-hot 向量。
它最大的优点是简单、明确、无歧义:
- 每个 token 都有唯一表示
- 非常容易从 id 映射到向量
- 很适合作为最初级的离散符号编码方式
5.2 one-hot 为什么不够用¶
虽然 one-hot 很直观,但它有几个致命问题。
问题 1:维度太高,而且非常稀疏¶
如果词表有 50,000 个 token,那么一个 one-hot 向量就是 50,000 维,里面只有一个位置是 1。
这会带来两个后果:
- 存储和计算都不划算
- 表示里绝大多数维度没有信息
问题 2:它不能表达语义相似性¶
在 one-hot 空间里:
cat和dog的内积是 0cat和apple的内积也是 0
也就是说,模型无法从表示本身看出:
cat和dog更接近cat和apple更远
从几何角度看,所有 one-hot 向量彼此几乎一样“远”。
但自然语言并不是这样。
我们明明知道:
king和queen比king和banana更相近car和vehicle比car和sad更相近
one-hot 没有能力把这种语义结构编码进去。
问题 3:它没有上下文概念¶
one-hot 只是在说:
“这是词表里的第几个 token。”
它完全不关心这个 token 在什么句子里,也不关心它当前表达什么意思。
所以它只能解决“身份标识”问题,解决不了“语义表示”问题。
这就引出了 embedding。
6. Embedding:从稀疏离散符号到稠密向量¶
现代 NLP 和 LLM 里,一个最核心的思想就是:
不要直接使用高维稀疏的 one-hot,而是把 token 映射到低维、稠密、可学习的向量空间里。
这类向量通常就叫 embedding。
6.1 embedding 的基本直觉¶
假设词表大小是 50,000,我们不再用一个 50,000 维 one-hot 来表示每个 token,而是给每个 token 分配一个可训练向量,比如 768 维。
那么:
cat -> [0.12, -0.45, ..., 0.31]dog -> [0.09, -0.39, ..., 0.28]apple -> [-0.77, 0.11, ..., -0.42]
这些向量不是手工写死的,而是在训练中学出来的。
模型会逐渐把“在类似上下文中经常出现”的 token,调整到相对接近的位置上。
这就是 embedding 比 one-hot 强大的地方:
它不只是区分“你是谁”,还开始携带“你和谁更像”的信息。
6.2 embedding lookup 本质上是什么¶
如果从实现角度看,embedding 层可以理解为一个矩阵:
其中:
|V|是词表大小d是 embedding 维度
每一行对应一个 token 的向量表示。
当输入某个 token id 时,本质上就是去这个矩阵里查对应那一行。
例如:
- id = 17,就取
E[17] - id = 208,就取
E[208]
所以 embedding lookup 可以看成:
token id -> embedding vector
6.3 为什么 embedding 可以学到语义结构¶
这里最重要的不是背数学,而是理解训练信号从哪里来。
如果两个词经常出现在相似上下文中,那么模型为了更好地完成训练目标,往往会把它们的向量调得更接近。
例如:
doctornursehospital
它们可能经常共现在相似语境里,因此 learned embedding 往往会表现出一定的语义相似性。
当然,这里要注意一件事:
embedding 里的“相似”不是人类词典式的严格定义,而是任务驱动、数据驱动的统计相似。
也就是说,向量接近表示它们对当前训练目标而言“可替代性更强”或“共现模式更像”,而不是说它们一定完全同义。
7. 静态词向量:Word2Vec 和 GloVe 在解决什么问题¶
在深度学习 NLP 发展早期,一个非常重要的进步就是:
大家开始不满足于 one-hot,而是希望直接学出更有语义结构的词向量。
这就出现了 Word2Vec、GloVe 这类经典方法。
7.1 它们的共同目标¶
虽然具体训练方法不同,但它们共同在做一件事:
为每个词学习一个固定向量,使得语义或统计上更接近的词,在向量空间里也更接近。
这一类方法非常重要,因为它们把 NLP 从“离散符号工程”往“连续向量表示”推进了一大步。
你可以把它们看成现代 embedding 思想的经典起点。
7.2 为什么它们当时很有影响力¶
因为一旦词能表示成连续向量,很多之前很难表达的性质就开始出现了。
例如:
- 词和词之间可以计算相似度
- 词向量可以作为下游模型输入
- 某些关系会在向量空间里表现出规律
最著名的例子通常是类似:
king - man + woman ≈ queen
这个例子不需要神化,但它确实说明了一件事:
向量空间开始承载词之间的结构信息了。
7.3 静态词向量的核心局限¶
Word2Vec 和 GloVe 虽然重要,但它们有一个非常本质的限制:
同一个词,无论出现在什么上下文里,它的向量都固定不变。
这就叫静态表示。
例如单词:
bank
在下面两句话里意思并不一样:
I deposited money in the bank.We sat on the river bank.
但如果使用静态词向量,那么这两个 bank 会拿到同一个向量。
这显然不够好。
因为自然语言里,词义高度依赖上下文。
8. 上下文表示:同一个词,在不同句子里应该有不同含义¶
静态词向量的瓶颈直接推动了上下文表示的发展。
核心思想非常自然:
一个 token 的表示,不应该只由它自己决定,还应该由它所在的上下文决定。
8.1 什么是 contextual representation¶
假设有两个句子:
The bank approved the loan.The fisherman sat by the bank.
在上下文表示里,这两个句子中的 bank,虽然表面字符串相同,但模型内部得到的表示应该不同。
因为:
- 前者更接近金融机构
- 后者更接近河岸
这就是 contextual embedding 的核心优势:
表示不再是“词表里这个词的固定身份卡”,而是“这个 token 在当前上下文中的含义摘要”。
8.2 为什么这一步对现代 LLM 特别关键¶
语言理解里最难的部分,往往不是识别这个词长什么样,而是判断它在当前语境里是什么意思。
例如:
- 指代消解依赖上下文
- 否定关系依赖上下文
- 多义词解释依赖上下文
- 长距离依赖也依赖上下文
所以从静态词向量走向上下文表示,不只是表示方式升级,而是 NLP 能力边界的一次明显扩张。
9. BERT 和 GPT 的表示差异:它们都用上下文,但任务视角不同¶
讲到上下文表示时,很多人会自然想到 BERT 和 GPT。
这里先不急着深入模型结构,只抓住它们在“表示是怎么来的”这一层区别。
9.1 BERT 更偏向双向上下文编码¶
BERT 的典型特点是:
一个 token 的表示可以同时利用左边和右边的上下文。
例如句子中间的某个词,BERT 在编码它时,通常可以同时看:
- 它前面出现了什么
- 它后面出现了什么
因此,BERT 更像是在做一件事:
给整句话里的每个 token 产出一个上下文化表示。
这使它特别适合理解类任务,例如:
- 文本分类
- 命名实体识别
- 句子匹配
- 阅读理解中的编码部分
9.2 GPT 更偏向自回归上下文表示¶
GPT 则不同。
它在生成第 t 个位置时,通常只能利用当前位置之前的内容。
也就是说,它的表示是为“根据前文预测下一个 token”服务的。
因此 GPT 的表示虽然同样依赖上下文,但这种上下文是单向的、自回归的。
这点和下一章会直接连起来:
GPT 风格语言模型的核心训练目标不是“给整句做编码”,而是“根据前文预测后文”。
9.3 这里最重要的不是背模型名词,而是抓住表示逻辑¶
你现阶段真正需要抓住的是:
- 静态词向量:同一个词一个固定向量
- 上下文表示:同一个词在不同上下文里表示会变化
- BERT 风格:更偏编码、双向上下文
- GPT 风格:更偏生成、自回归上下文
只要这层想清楚,后面再学习 Transformer 结构时就不会那么抽象。
10. OOV:为什么不能只按整词建词表¶
到这里,你可能会有一个很自然的想法:
既然我们已经能把词映射成向量,为什么不直接把所有单词都放进词表里?
问题在于,这个想法在真实语言里很快就会撞墙。
最大的麻烦之一,就是 OOV。
10.1 什么是 OOV¶
OOV 是 Out-Of-Vocabulary 的缩写,意思是:
输入里出现了词表中不存在的词。
例如你的词表里有:
playplayerhappy
但推理时来了一个:
playfulness
如果它不在词表里,那么模型就不知道该怎么表示它。
这就是 OOV 问题。
10.2 为什么整词词表会很脆弱¶
如果词表按整词构建,那么现实语言里会不断出现新词、罕见词、拼写变体、专有名词和领域术语。
例如:
- 人名
- 地名
- 产品名
- 编程变量名
- 医学术语
- 年份、数字、符号拼接形式
你几乎不可能把所有可能出现的整词都提前收进词表。
而且即使勉强这么做,也会带来新的问题:
- 词表会变得极大
- 长尾词大多训练次数很少
- 存储和训练成本都会变高
所以按整词建模,在工程上和泛化上都不理想。
11. Subword:在“字符太细”和“整词太粗”之间取平衡¶
为了解决 OOV 和词表规模问题,现代 NLP 和 LLM 普遍采用 subword 粒度。
subword 可以理解为:
比整词更小,但通常又比单字符更有意义的片段。
11.1 为什么不是直接按字符处理¶
一个看起来简单的方案是:
既然整词会 OOV,那干脆按字符切,不就不会有未知词了吗?
这确实能缓解 OOV,但又会带来新的问题:
- 序列变得太长
- 高层语义组合更困难
- 训练和建模成本会明显上升
例如英文单词 unbelievable:
- 按整词处理,可能太稀有
- 按字符处理,序列太碎
- 按 subword 处理,则可能变成
un + believ + able
这通常是更合理的折中。
11.2 subword 的核心优势¶
subword 之所以在现代 LLM 中非常常见,是因为它同时解决了几类问题:
- 能显著降低 OOV 风险
- 能控制词表大小
- 能复用词根、词缀、常见片段
- 对多语言、专有名词、代码、数字混合文本更稳健
例如一个新词即使没完整出现过,也可以拆成已知子片段来表示。
这意味着模型即使第一次看到某个新词,也不至于完全无从下手。
11.3 这一章只讲“为什么需要 subword”,不展开“怎么训练 tokenizer”¶
这里先有意停一下。
因为:
- 第 3 章的任务,是帮你理解文本表示为什么会从 word-level 走向 subword-level
- 第 8 章的任务,才是系统讲 BPE、SentencePiece、Tokenizer 训练和数据处理细节
你现在只需要先把逻辑链路建立起来:
整词词表容易 OOV -> 字符粒度又太碎 -> 所以现代系统常常采用 subword
这条线一旦明白,后面学 tokenizer 就会容易很多。
12. 把整条链路串起来:一句文本是怎样变成模型输入的¶
到这里,我们可以把这一章的主线完整收一遍。
假设输入一句文本:
I love deep learning
它进入现代语言模型前,通常会经历类似过程:
- 先被切分成 token 或 subword 片段
- 每个 token 被映射成一个 token id
- token id 经过 embedding lookup,变成稠密向量
- 这些向量在模型内部继续和上下文交互
- 最终每个位置都会得到一个上下文化表示
如果是 GPT 风格语言模型,那么接下来还会进一步用于:
根据前文预测下一个 token。
所以这一章讲的,并不是“语言模型如何预测”,而是更靠前的那一步:
语言模型到底是如何拿到可计算输入的。
13. 常见误区¶
误区 1:embedding 就是 tokenizer¶
这不对。
Tokenizer 负责把文本切分成 token,并映射成 id。
Embedding 负责把 token id 变成向量。
一个处理的是“离散符号系统”,一个处理的是“连续数值表示”,它们不是同一个层次的东西。
误区 2:token 一定等于单词¶
这也不对。
在现代模型里,token 可能是整词、子词、字符片段、标点,甚至特殊控制符号。
把 token 简单等同于单词,会让你在学习 tokenizer 和 LLM 时经常产生误解。
误区 3:有了词向量,模型就真正理解语言了¶
这仍然不对。
词向量只是在数值空间里学习统计结构,它让模型更容易利用共现模式和上下文信息,但这不等于人类意义上的“理解”。
更准确的说法是:
表示方法决定了模型能看到什么信息、怎样组织信息,但最终能力还取决于模型结构、训练目标、数据规模和优化过程。
误区 4:subword 只是为了解决英文问题¶
不是。
subword 对英文当然重要,但它在中文、多语言、代码、数字混排、专有名词处理中同样关键。
它本质上是在解决开放词汇表场景下的泛化和效率问题,而不是只服务某一种语言。
14. 本章小结¶
这一章你可以只记住下面这条演化链路:
- 最开始,文本只是字符串,模型不能直接处理
- 所以需要先切成 token,并建立 vocabulary 与 token id
- one-hot 能表示 token 身份,但过于稀疏,也没有语义结构
- embedding 用低维稠密向量替代 one-hot,开始承载统计语义信息
- 静态词向量为每个词分配固定表示,但不能处理多义词的上下文变化
- 上下文表示让同一个 token 在不同句子里拥有不同表示
- 整词词表会遇到 OOV 和词表过大问题,因此现代系统大量采用 subword
如果把这章压缩成一句话,那就是:
现代 NLP 和 LLM 的第一步,不是直接“理解语言”,而是先把语言变成一种可计算、可学习、可泛化的表示。
15. 面试题¶
Q1:为什么 one-hot 不适合作为现代语言模型的核心文本表示?¶
因为它高维、稀疏,计算和存储成本高,而且不能表达 token 之间的语义相似性。同样,one-hot 也不携带上下文信息,无法表示同一个词在不同语境里的不同含义。
Q2:embedding 和 one-hot 的根本区别是什么?¶
one-hot 是固定的离散身份编码,只表示“它是谁”;embedding 是可学习的稠密向量表示,不只区分 token,还能在向量空间中表达统计上的相似性和结构关系。
Q3:静态词向量和上下文表示有什么区别?¶
静态词向量对同一个词始终给出同一个向量,不考虑上下文;上下文表示会根据句子环境动态变化,因此更能处理多义词、长距离依赖和真实语言中的语义变化。
Q4:为什么现代大模型通常不用整词词表,而更偏向 subword?¶
因为整词词表容易遇到 OOV,词表规模也会迅速膨胀。subword 在表达能力、词表大小、泛化能力和序列长度之间提供了更平衡的折中。
16. 下一章预告¶
这一章解决的是:
文本怎样变成模型可以处理的表示。
下一章要继续往前走一步,回答另一个关键问题:
当模型已经拿到这些 token 表示之后,它到底在训练什么?
答案就是:
语言模型本质上在做 next-token prediction。
这也是我们从文本表示走向 n-gram、再走向神经语言模型的起点。