【大模型应用开发】Embedding 与词向量 —— 从 One-Hot 到语义空间
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-small | 1536 | 通用、便宜 |
OpenAI text-embedding-3-large | 3072(可降维) | 效果更强 |
| BGE(智源) | 1024 | 中文效果好、开源 |
| M3E / text2vec | 768 | 中文轻量方案 |
调用示例(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 层,只是不单独用 |
更多推荐
所有评论(0)