8.1 问题:模型怎么理解"词"?

第 2 课我们学了:文字要先切成 Token,再转成数字编号。

"苹果" → 编号 867
"香蕉" → 编号 129
"苹果手机" → 编号 867, 5511

但问题来了:数字编号只表达了"这是谁",没表达"它是什么意思"。

  • 867 和 129 的差是 738,这个数字毫无意义
  • 模型看不出"苹果"和"香蕉"都是一种水果
  • 模型更看不出"苹果"(水果)和"苹果"(手机品牌)的区别

我们需要一种方式,把"词的含义"编码成模型能计算的数字。这就是 Embedding。


8.2 从 One-Hot 说起(为什么它不行)

最早的想法是 One-Hot 编码:

词汇表 = [苹果, 香蕉, 苹果手机, 手机, 电脑, ...]  共 5 万个词

苹果    → [1, 0, 0, 0, 0, ...]    (第 1 位是 1)
香蕉    → [0, 1, 0, 0, 0, ...]    (第 2 位是 1)
手机    → [0, 0, 0, 1, 0, ...]    (第 4 位是 1)

致命缺点

问题说明
维度爆炸5 万词的词汇表 = 5 万维向量,存起来是天量
无语义“苹果"和"香蕉"的向量完全不相干(余弦相似度为 0),模型学不到"都是水果”
无关联"苹果"和"苹果手机"毫无关系,但现实中它们相关

One-Hot 就像给每个人发一个独一无二的编号,编号之间没有任何联系。模型无法从编号中学到任何语义。


8.3 词向量的核心思想

Embedding 的本质:把每个词映射成一个"稠密向量"(比如 128 维、1024 维),让语义相近的词,向量也相近。

苹果      → [0.12, -0.45, 0.89, 0.03, ...]   ← 一串小数
香蕉      → [0.15, -0.42, 0.85, 0.01, ...]   ← 和苹果很接近 ✅
苹果手机  → [0.87, 0.23, -0.11, 0.66, ...]   ← 和手机更接近 ✅

关键性质

  • 语义相近 → 向量距离近(“苹果” ≈ “香蕉”)
  • 语义相反 → 向量距离远(“热” ≠ “冷”)
  • 向量可以计算!经典的例子:
向量(国王) - 向量(男人) + 向量(女人) ≈ 向量(女王)

这个"减法"操作在向量空间里真实成立,因为它捕捉到了"性别"这个语义维度。


8.4 Word2Vec:词向量是怎么学出来的?

Word2Vec(2013 年,Google)是词向量鼻祖。核心思想一句话:

“一个词的意思,由它周围的词决定。”(You shall know a word by the company it keeps)

两种训练方式

方式一 CBOW:根据上下文猜中心词
  上下文: "我 __ 了一个苹果"  → 预测空格是"吃"

方式二 Skip-gram:根据中心词猜上下文
  中心词: "吃" → 预测周围的词是"我"、"一个"、"苹果"

训练时,模型不断调整每个词的向量,直到"能正确预测上下文"为止。训练完成后,那些经常出现在相似语境里的词,向量自然就靠近了。

类比:一个新员工通过观察"谁总和谁一起开会"来理解每个人的角色。天天一起开会的两个人,你自然觉得他们"是一伙的"。


8.5 向量相似度怎么算?

余弦相似度(最常用)

similarity = cos(θ) = (A · B) / (|A| × |B|)

结果范围:-1(完全相反)到 1(完全相同)
0 左右 = 无关
import numpy as np

def cosine_similarity(vec_a, vec_b):
    dot = np.dot(vec_a, vec_b)
    norm = np.linalg.norm(vec_a) * np.linalg.norm(vec_b)
    return dot / norm

apple  = np.array([0.12, -0.45, 0.89, 0.03])
banana = np.array([0.15, -0.42, 0.85, 0.01])
phone  = np.array([0.87, 0.23, -0.11, 0.66])

print(f"苹果 vs 香蕉: {cosine_similarity(apple, banana):.3f}")  # 接近 1
print(f"苹果 vs 手机: {cosine_similarity(apple, phone):.3f}")   # 接近 0

"相似度"是 RAG、搜索、推荐、聚类等一切"按语义找相似"应用的地基。 这一课把它吃透,后面全是它的应用。


8.6 从词向量到句子/文档向量

Word2Vec 只能给单个词算向量。但现实中我们更常需要整个句子/文档的向量,比如:

“帮我找公司合同里所有关于违约金条款的段落”

现代 Embedding 模型

现在不需要自己训练了,直接用现成的:

模型维度特点
OpenAI text-embedding-3-small1536通用、便宜
OpenAI text-embedding-3-large3072(可降维)效果更强
BGE(智源)1024中文效果好、开源
M3E / text2vec768中文轻量方案

调用示例(OpenAI 兼容格式):

from openai import OpenAI

client = OpenAI()  # 配置你的 api_key 和 base_url

response = client.embeddings.create(
    model="text-embedding-3-small",
    input=["苹果", "香蕉", "手机", "今天天气很好"]
)

for i, item in enumerate(response.data):
    vec = item.embedding
    print(f"第 {i} 条 → {len(vec)} 维向量,前 5 个值: {vec[:5]}")

句向量的核心 trick

一个句子能生成一个向量,靠的是模型的注意力机制(第 9、10 课会讲),它能把整句的信息"压缩"成一个向量。这个向量就代表整个句子的语义。


8.7 Embedding 的应用场景

场景原理
语义搜索把问题和文档都转成向量,找相似度最高的文档
RAG(知识库问答)上面这个场景,就是 RAG 的第一步
文本聚类语义相近的文档自动归到一组
推荐系统用户偏好向量 + 内容向量算相似度
去重两段文字语义相同(哪怕字不同)→ 向量也相近

预告:第 23~27 课「RAG 篇」会完整用到这一课的内容——"向量化 + 相似度检索"正是 RAG 的两大支柱之一。


8.8 常见误区

误区真相
“维度越高越好”不一定。维度高能表达更多信息,但计算贵、可能过拟合(参考 qa-answers/qa-08
“Embedding 是模型算出来的”更准确地说:是训练出来的,靠海量语料学出来的
“向量近 = 意思一样”只是语义相近,不等于等价,别过度解读
“词向量过时了”现代 LLM 内部依然有 embedding 层,只是不单独用

更多推荐