OOV (Out of Vocabulary)
OOV 指输入文本中不在模型词表内的词,是 NLP 系统必须处理的边界情况。子词分词(如 BPE)通过拆分未知词为已知片段来缓解此问题。
OOV (Out of Vocabulary)
直觉理解
想象你拿着一本英汉词典去翻译一篇文章。遇到 "iPhone" 这个词——词典里没有。这就是 OOV 问题:模型的词表是有限的,但自然语言是无限的。
任何基于固定词表的系统都必须回答一个问题:遇到不认识的词怎么办?
为什么会出现 OOV
| 原因 | 示例 |
|---|---|
| 新造词 / 专有名词 | ChatGPT、元宇宙 |
| 拼写错误 | teh → the |
| 多语言混合 | "这个 feature 很 nice" |
| 低频词未收录 | 训练语料中未出现的罕见词 |
| 领域术语 | 医学、法律专业词汇 |
传统处理方式
早期 NLP 系统的做法是用一个特殊标记 <UNK>(Unknown)替换所有 OOV 词:
这种方式简单粗暴,但丢失了所有语义信息。如果一句话有多个 OOV 词,模型看到的只是一串 <UNK>,完全无法区分。
子词分词:终结 OOV
现代语言模型使用子词分词(Subword Tokenization)从根本上缓解了 OOV 问题。核心思路:
不认识整个词?那就把它拆成认识的片段。
以 BPE(Byte Pair Encoding)为例:
text
"unhappiness" → ["un", "happi", "ness"]
"ChatGPT" → ["Chat", "G", "PT"]
每个片段都在词表中有对应的向量表示,模型可以通过组合这些片段的语义来理解整个词。
| 方案 | OOV 率 | 语义保留 | 词表大小 |
|---|---|---|---|
| 词级分词 | 高 | ❌ 未知词变 UNK | 数十万 |
| 字符级分词 | 0 | ⚠️ 序列过长 | ~256 |
| 子词分词 (BPE) | ≈0 | ✅ 片段组合 | 3-10 万 |
与 Embedding 的关系
OOV 问题直接影响 Embedding 层的设计:
- 词级 Embedding:OOV 词只能映射到同一个
<UNK>向量,无法学到有意义的表示 - 子词 Embedding:即使是全新的词,其子词片段也各有向量,通过后续网络层组合出整词语义
这就是为什么现代 Tokenizer(如 GPT 的 tiktoken、BERT 的 WordPiece)都采用子词方案——让 OOV 从"致命问题"变成"基本不存在的问题"。
小结
OOV 是 NLP 发展史上的一个核心挑战。从 <UNK> 替换到子词分词,解决思路的演进体现了一个设计哲学:与其硬记所有词,不如学会拆解和组合。
引用本术语的文章
- Vercel AI SDK 深度解析:ToolLoopAgent 是 Facade,真正的循环在 generateText 里
- 编译器的三重身份:五大前端框架编译管线深度拆解
- 一个设计决策如何决定一切:五大前端框架源码级横向解剖
- 【pi-mono 源码解析 1/4】深入 pi-mono Agent Loop:一个 TypeScript AI 代理的上下文管理哲学
- 【pi-mono 源码解析 2/4】展平的代价:LLM Agent 框架里被忽视的工具调用原子性问题
- Coding Agent实战:从零构建一个能写代码的AI助手
- 从 setTimeout 到 VSync:一篇讲透 Event Loop 的前世今生