# 词向量小史：从 one-hot 到 embedding

> AI 系列第 6 篇。机器只懂数字，怎么把"咖啡店"这三个字变成一个能算的向量？这一篇走完词向量 70 年——从 1957 的 one-hot，到 2013 那个让"国王 − 男人 + 女人 ≈ 女王"成立的 word2vec，再到今天 LLM 内部的 1.2 万维 embedding 空间。

- URL: https://tenggouwa.com/posts/word-vectors/
- 发布: 2026-05-27
- 标签: ai, embedding, word2vec, nlp, ai-series

> AI 系列第 6 篇。上一篇讲了 Transformer 是怎么把整句话并行处理的。
> 这一篇回头补一课：那些被丢进 Transformer 的"词"，本身是怎么变成向量的？
> 你会发现，**词向量** 这个概念，是过去 15 年 NLP 最被低估、也最重要的发明。

## 0. 一个被忽略的根本问题

我们已经讲了好几篇神经网络。但有件事我们一直含糊带过——

**神经网络只能处理数字。一段文字怎么进去？**

```
input: "今天天气真好"
   ▼
神经网络: ??? 这是什么乱码？
```

机器看不懂中文。也看不懂英文。**它只懂浮点数向量**。所以在所有 NLP 工作之前，必须先把字符串变成数字。这就是 **embedding**（词嵌入）的任务。

听起来好像无关紧要的预处理？错。**embedding 决定了模型能"理解"的语义粒度**。embedding 做得不好，后面再厉害的 Transformer 也救不了。

这一篇我们就讲：人类是怎么花了 60 年，才搞清楚"机器眼里的词"应该长什么样的。

---

## 1. 第一代：one-hot 编码——"暴力但天真"

最朴素的想法：**给每个词一个编号。**

```
词典:
  "今天" → 0
  "天气" → 1
  "真好" → 2
  "下雨" → 3
  "可惜" → 4
  ...   (总共 5 万个词)
```

但直接用编号不行——神经网络会把"3"当成"比 2 大、比 4 小"的有序数字，而实际上"下雨"和"真好"之间没有任何"大小关系"。

解决方案：**one-hot 向量**。

```
"今天" → [1, 0, 0, 0, 0, ...]   ← 第 0 位是 1
"天气" → [0, 1, 0, 0, 0, ...]   ← 第 1 位是 1
"真好" → [0, 0, 1, 0, 0, ...]   ← 第 2 位是 1
```

每个词被表示成一个 5 万维的向量，只有自己那一位是 1，其他全是 0。

### one-hot 的两个致命缺陷

#### 缺陷 1：维度灾难

一个英文词典有 50 万词。一个 5 万词的 one-hot 向量乘以一个全连接层，参数量就要爆炸：

```
输入 50000 维 × 隐藏层 1024 维 = 5120 万个参数
```

光是第一层就 5000 万参数。整个网络？吓人。

#### 缺陷 2：所有词都"等距离"

```
"猫" → [1, 0, 0, ...]
"狗" → [0, 1, 0, ...]
"汽车" → [0, 0, 1, ...]
```

在 one-hot 空间里，"猫"和"狗"的距离 = "猫"和"汽车"的距离 = √2。

**模型根本无法知道"猫"和"狗"语义相近、"猫"和"汽车"不相关。** 每个词都是孤岛。

这就是 one-hot 的根本问题——**它编码了身份，但没编码意义**。

---

## 2. 中间过渡：词频统计与 LSA（1957 → 2000s）

人们很早就想：能不能让"语义相近的词"在向量空间里"距离相近"？

**1957 年**，语言学家 J.R. Firth 说过一句被引爆的话：

> *"You shall know a word by the company it keeps."*
> （**一个词的意思，由它经常和谁一起出现来定义。**）

这就是 **分布假说**（distributional hypothesis）。它的逻辑是：

- "猫"经常和"狗"、"宠物"、"喵"、"喂食"一起出现。
- "汽车"经常和"司机"、"轮胎"、"加油"、"高速"一起出现。
- 所以"猫"和"狗"的"邻居词集"高度重合 → 它们语义相近。

### LSA：第一个把这个假说落地的方法

**1988 年** 出现的 **LSA（Latent Semantic Analysis）** 就是这个思路：

1. 建一个超大的"词-文档"矩阵：行是词，列是文档，单元格是词频。
2. 用矩阵分解（SVD）压缩到几百维。
3. 每个词变成一个 300 维的稠密向量。

这是历史上第一次把词变成"低维稠密向量"。但 LSA 的问题是它**只用了统计**，没有学习。给定一个新句子，它没法处理。

90 年代到 2000 年代，词表示一直停留在"统计 + 降维"的阶段。神经网络派偶尔在尝试，但因为算力不够，效果不显著。

---

## 3. 2013：word2vec——那个让"国王 − 男人 + 女人 ≈ 女王"成立的算法

**2013 年 1 月**，Google 的 **Tomas Mikolov** 团队发了两篇论文，提出了 **word2vec**。这是词向量史上最重要的一个里程碑。

word2vec 的核心思想还是 Firth 那句话——**用上下文定义词义**。但它用神经网络来学。

### 训练目标：从上下文预测词（或反过来）

word2vec 有两种训练方式：

**方式 1：CBOW（Continuous Bag of Words）—— 用周围猜中间**

```
输入: ["今天", "天气", ___, "出去", "玩"]
任务: 猜 ___ 是什么词
答案: "不错"
```

**方式 2：Skip-gram —— 用中间猜周围**

```
输入: "不错"
任务: 猜它周围最可能出现的词
答案: ["今天", "天气", "出去", "玩"]
```

训练时，给模型 1000 亿条这样的句子。模型为了猜对，必须学会一件事：**让经常出现在相似上下文里的词，向量靠近**。

训完之后，每个词得到一个 300 维的稠密向量。

### 让全世界震惊的发现

word2vec 训完后，研究者发现这些向量有**数学结构**：

```
vec("国王") − vec("男人") + vec("女人") ≈ vec("女王")
vec("Paris") − vec("France") + vec("Italy") ≈ vec("Rome")
vec("walked") − vec("walk") + vec("swim") ≈ vec("swam")
```

这就是著名的 **word analogy**（词类比）。它说明 word2vec 不只是把相似的词放近，**它还把"语义关系"编码成了向量空间里的方向**。

- "国王 → 男人"和"女王 → 女人"是平行的方向 = 性别关系。
- "Paris → France"和"Rome → Italy"是平行的方向 = 首都-国家关系。
- "walked → walk"和"swam → swim"是平行的方向 = 过去时关系。

模型没人教过它"性别"或"时态"这些概念。它自己从数据里**涌现**出来了。

> 一句你可以拿去吹的话：
> **word2vec 第一次让人类相信：神经网络不只是在做分类、回归。它能从纯文本里，自动提取出语言学家几百年来一直在研究的"语义结构"。**

### word2vec 的影响

2013 之后，几乎所有 NLP 任务都先跑一遍 word2vec 拿到词向量，再喂进下游模型。

```
[原始文本] → [word2vec] → [词向量] → [LSTM/CNN] → [任务输出]
```

这成了 2013–2018 的标配流水线。word2vec 那篇论文今天引用 4 万次。

---

## 4. 2014 年：GloVe——把统计派和神经派合一

word2vec 几乎完美，但有个理论上的不满——它**只用了局部窗口**（左右几个词），没用全局统计。

**2014 年**，斯坦福的 Pennington、Socher、Manning 三人提出 **GloVe（Global Vectors）**。它的目标：让词向量同时反映**全局共现统计** + **局部上下文**。

GloVe 的训练目标可以简化为：

```
学习词向量 w_i 和 w_j，使得：
   w_i · w_j ≈ log(共现次数(i, j))
```

直觉是：两个词在语料里一起出现得越多，它们向量的点积越大。

GloVe 在 word analogy 任务上比 word2vec 略好。但俩兄弟的差异已经不大了。**2014 之后的几年，word2vec / GloVe 几乎成了同义词**。

---

## 5. 2018：ELMo / BERT——"一个词在不同句子里，应该有不同向量"

word2vec 有个隐性假设：**一个词只有一个向量**。

```
"我去银行存钱。"
"河边有一棵银行树。"  ← (假设有这个东西)
"银行" → [0.2, -0.3, 0.5, ...]  ← 永远是这个向量
```

但显然，"银行"在两个句子里意思完全不同。word2vec 把它们混在了同一个向量里，丢了上下文信息。

### ELMo：用 LSTM 算"上下文相关"的向量

**2018 年 2 月**，Allen AI 的研究员提出 **ELMo（Embeddings from Language Models）**。它的核心改变：

> **同一个词，在不同句子里，给不同向量。**

```
"我去银行存钱"  → "银行" 向量 ≈ "金融机构"方向
"河边有银行树"  → "银行" 向量 ≈ "植物"方向
```

ELMo 用一个双向 LSTM 在大语料上预训练，给每个句子里的每个词单独算一个向量。这种向量叫 **contextualized embedding**（上下文化嵌入）。

### BERT：用 Transformer 把这件事做到极致

**2018 年 10 月**，Google 的 **BERT** 把 ELMo 的思路推到顶——但用的不是 LSTM，是 **双向 Transformer encoder**。

BERT 的训练任务很有意思——它玩**完形填空**：

```
原句: "今天天气真好，我想出去散步。"
训练: "今天天气[MASK]好，我想出去[MASK]步。"
任务: 预测 [MASK] 处该填什么
```

为了猜对 mask，模型必须深度理解上下文。训完之后，BERT 内部每一层对每个词的向量表示，都包含了**深度上下文信息**。

BERT 一出现，几乎所有 NLP benchmark 都被它刷爆——情感分类、问答、命名实体识别、相似度…… **2018 年是 NLP 圈的"BERT 之年"**。

> 一句你可以拿去吹的话：
> **从 word2vec 到 BERT，词向量经历了从"一个词一个固定向量"到"一个词在每个句子里都有专属向量"的范式转换。这是 NLP 史上第一次，机器能"读懂语境"。**

---

## 6. 今天的 LLM：embedding 已经"看不见"了

ChatGPT、Claude、Llama 这些大模型里还有 embedding 吗？有，但它已经被深度藏起来了。

现代 LLM 处理一段文本的完整流程：

```
"今天天气真好"
   │
   ▼
[Tokenizer] 把文本切成 token   →  ["今", "天天气", "真好"]
   │
   ▼
[Embedding Layer] 把每个 token 转成向量
                                  → [v_1, v_2, v_3]
                                    每个 v_i 大约 4096 维
   │
   ▼
[Transformer 96 层]
   │
   ▼
最终输出
```

**Embedding Layer 就是一个查找表**：input 是 token id（一个整数），output 是一个固定的向量。这个表的大小是 `vocab_size × hidden_dim`。

GPT-3 的 vocab 是 50,257 个 token，hidden_dim 是 12,288。embedding 表就是：

```
50,257 × 12,288 = 6.17 亿个参数
```

光这个表就比 BERT 整个模型还大。

### 但这是 word2vec 那种"独立学习"的 embedding 吗？

不是。现代 LLM 的 embedding 是**端到端学**的——和整个网络一起训练，不再单独搞一个 word2vec 步骤。

```
1990s:  one-hot                 ── 静态、稀疏
2013:   word2vec / GloVe        ── 静态、稠密、独立预训练
2018:   ELMo / BERT             ── 上下文化、独立预训练
2020+:  现代 LLM 内嵌 embedding   ── 上下文化、端到端学
```

### Embedding 在今天还有什么用？

虽然 LLM 把 embedding 内化了，但**作为单独工具**的 embedding 模型反而越来越火。原因是 **RAG 和向量搜索**：

```
1. 把所有知识库文档切成小段。
2. 用 embedding 模型把每段编成一个向量。
3. 用户问问题时，把问题也变成向量。
4. 在向量库里找"距离最近"的几段。
5. 把这几段 + 问题一起喂给 LLM。
```

这就是 RAG（Retrieval-Augmented Generation）的核心。今天主流的 embedding 模型：

- **OpenAI** `text-embedding-3-large`
- **Cohere** `embed-v3`
- **BGE** / **GTE**（开源）
- **Voyage AI** `voyage-3`

这些模型本质上都是 BERT 的变种，专门优化了"语义相似度"。今天创业公司搞 RAG 时，第一步就是挑一个 embedding 模型——这又把 word2vec 时代的"独立 embedding 训练"思路带了回来。

---

## 7. 关于 tokenizer：embedding 之前还有一步

讲到这要补一刀：**embedding 不是直接作用在"词"上的**，是作用在 **token** 上的。

token 是什么？是 tokenizer 切分出来的"子词"。例如 GPT 的 BPE tokenizer：

```
"GPT-4 是 OpenAI 发布的模型"
   │
   ▼ tokenize
["GPT", "-", "4", " 是", " Open", "AI", " 发布", "的", "模型"]
   │
   ▼ token ids
[3877, 12, 19, 1234, 9012, ...]
   │
   ▼ embedding layer
[v_1, v_2, v_3, ...]
```

注意几件事：

- "GPT-4" 被切成了 `GPT`, `-`, `4` 三个 token。
- " 是" 包含前导空格（GPT BPE 这么干）。
- "OpenAI" 被切成 `Open` + `AI`，不在一个 token 里。

中文也是同理。一个汉字可能是一个 token，也可能两三个汉字组成一个 token，看 tokenizer 怎么训的。

**这是为什么 LLM 偶尔会犯一些"低级错误"——比如数不清 "strawberry" 里有几个 r**。因为它看到的是 token，不是字符。

第 10 篇会专门讲 tokenizer 和 context window 的故事。

---

## 8. 总结这条线

```
1957  分布假说："一个词由它的邻居定义"
1988  LSA：用矩阵分解把这个假说落地
2013  word2vec：用神经网络把它落地，规模爆炸
2014  GloVe：统计派 + 神经派合一
2018  ELMo / BERT：上下文化 embedding，"一词多向量"
2020+ 现代 LLM：embedding 端到端集成
```

> 一句你可以拿去吹的话：
> **从"词是一个独热编号"到"词是一个 12288 维的上下文相关向量"，机器对语言的理解走了 60 年。但真正的飞跃，只在最后 10 年。**

---

## 9. 给你的小作业

1. **用 `gensim` 这个 Python 库加载预训练 word2vec，试试 `vec('国王') - vec('男人') + vec('女人')` 最近的几个词是什么。** 提示：可能不只是"女王"，还会有"王后"、"公主"等。
2. **解释为什么 word2vec 给"银行"只有一个向量，而 BERT 给不同句子里的"银行"不同向量。**
3. **写一段伪代码描述 RAG 的工作流程，明确指出 embedding 在哪里发挥作用。**

> **下一篇钩子**：到这里，我们已经把"模型怎么读懂顺序"和"词怎么变成向量"都搞清楚了。
> 下一篇起，我们正式进入**大模型时代**——
> GPT-1 (2018)、GPT-2 (2019)、GPT-3 (2020)、GPT-3.5 (2022)、GPT-4 (2023)、GPT-4o / o1 (2024–2025)，每一代到底变了什么？
> 答案可能让你意外：**架构基本没变**。变的是规模、数据、训练技巧——以及一些你以为是"smarter"的小动作。
