7.1.3 词向量与语义表示

先建立心智模型
Section titled “先建立心智模型”One-hot ID 能区分词,但不能表达哪些词相关。稠密 embedding 会把 token 放进一个向量空间:
token idembedding table lookupdense vector
在这个空间里:
- 距离近的向量通常表示用法相关;
- cosine similarity 衡量方向是否相近;
- 句向量通常由 token 向量 pooling 得到;
- 上下文模型能让同一个 token 根据周围词改变位置。
从独热编码(One-Hot)到稠密向量
Section titled “从独热编码(One-Hot)到稠密向量”
One-hot 向量里,任何不同词都一样“不同”:
refund -> [1, 0, 0, 0]return -> [0, 1, 0, 0]password -> [0, 0, 1, 0]coupon -> [0, 0, 0, 1]稠密向量可以表达更有用的几何关系:
refund and return -> closepassword and reset -> closerefund and password -> far这种几何关系不是手写规则,而是从数据里学出来的。经常出现在相似上下文里的词,向量也倾向于接近。
实验 1:比较词语相似度
Section titled “实验 1:比较词语相似度”先跑这个极小 embedding table。数字是为了教学手写的,但操作和真实向量检索一致。
from math import sqrt
embeddings = { "refund": [0.90, 0.80, 0.10], "return": [0.88, 0.78, 0.12], "password": [0.10, 0.20, 0.95], "reset": [0.12, 0.18, 0.92], "order": [0.75, 0.70, 0.15], "coupon": [0.05, 0.95, 0.10], "policy": [0.82, 0.74, 0.18],}
def cosine(a, b): dot = sum(x * y for x, y in zip(a, b)) norm_a = sqrt(sum(x * x for x in a)) norm_b = sqrt(sum(x * x for x in b)) return dot / (norm_a * norm_b)
print("refund vs return :", round(cosine(embeddings["refund"], embeddings["return"]), 3))print("refund vs password:", round(cosine(embeddings["refund"], embeddings["password"]), 3))print("password vs reset :", round(cosine(embeddings["password"], embeddings["reset"]), 3))预期输出:
refund vs return : 1.0refund vs password: 0.293password vs reset : 1.0这样理解:
- cosine 高表示方向相似,不代表含义完全相同;
refund和return接近,是因为这个玩具表把它们放在客服退款区域;password和reset接近,是因为它们属于账号问题区域;refund和password远,是因为意图不同。
实验 2:做一个迷你语义检索器
Section titled “实验 2:做一个迷你语义检索器”现在把 token 向量平均成句向量,再按 query 给三条文档排序。
def mean_embedding(tokens): vectors = [embeddings[token] for token in tokens if token in embeddings] dim = len(vectors[0]) return [sum(vector[i] for vector in vectors) / len(vectors) for i in range(dim)]
query = mean_embedding(["reset", "password"])documents = { "A refund policy": ["refund", "policy"], "B password reset": ["password", "reset"], "C coupon return": ["coupon", "return"],}
ranked = sorted( ( (name, cosine(query, mean_embedding(tokens))) for name, tokens in documents.items() ), key=lambda item: item[1], reverse=True,)
for name, score in ranked: print(f"{name}: {score:.3f}")预期输出:
B password reset: 1.000C coupon return: 0.335A refund policy: 0.333这就是向量检索的核心:
query textquery vectorcompare with document vectorstop-k results
真实 RAG 系统会使用更强的 embedding model 和 vector database,但逻辑仍然是相似度排序。
平均向量有用,但不够强
Section titled “平均向量有用,但不够强”Mean pooling 很好理解,但会丢掉重要信息:
- 词序;
- 否定;
- 强调;
- 长距离依赖;
- 哪个 token 更关键。
例如玩具检索器会把 reset password 和 password reset 看成完全一样。这对建立直觉可以,但不适合推理要求高的任务。

静态 embedding 通常一个词一个向量。上下文模型会让向量受周围词影响:
bank account -> bank 靠近金融语义river bank -> bank 靠近地理语义跑一个小模拟:
base_bank = [0.50, 0.50, 0.50]finance_context = [0.30, -0.10, 0.20]river_context = [-0.20, 0.25, -0.10]
bank_in_finance = [a + b for a, b in zip(base_bank, finance_context)]bank_in_river = [a + b for a, b in zip(base_bank, river_context)]
print("bank in finance:", [round(x, 2) for x in bank_in_finance])print("bank in river :", [round(x, 2) for x in bank_in_river])预期输出:
bank in finance: [0.8, 0.4, 0.7]bank in river : [0.3, 0.75, 0.4]
这不是一个真实 Transformer,只是帮助记忆:同一个 token 混入上下文后,可以得到不同表示。
项目里怎么用
Section titled “项目里怎么用”| 场景 | embedding 提供什么 | 注意点 |
|---|---|---|
| RAG 检索 | 找到语义相关 chunk | chunk 切得差或 metadata 过期仍会伤害答案 |
| FAQ 聚类 | 合并相似问题 | 接近不一定等于重复 |
| 去重 | 找近似重复内容 | 模板化文本和改写会干扰分数 |
| 分类 | 把文本变成特征 | 标签质量和校准仍然重要 |
| 推荐 | 比较用户、物品或查询 | 热门偏差可能压过语义相似 |
- 如果库没有自动处理,先把向量 normalize 再算 cosine。
- 打印 top-k 分数,不只看 top-1;分差小代表检索不确定。
- 检查 false positive:相关词不一定就是正确答案。
- 对同一批数据比较 static、sentence、上下文相关 embedding。
- 多语言项目要先测跨语言样本,不要默认 embedding model 已经对齐语言。
学完这一页,至少保留这张证据卡:
- 向量
- 至少三个文本 embedding 或玩具向量
- 相似性检查
- 最近一对和分数
- 检索结果
- 某个查询的最佳匹配
- 局限
- 平均或相似度会忽略上下文/否定/顺序
- 下次使用
- 这将成为第 8 章中的检索证据
- 把
coupon移到更接近password的位置,观察检索如何出错。 - 添加文档
D recover account,并给recover、account设计向量。 - 创建 查询
refund order,你认为哪条文档应该排第一? - 用自己的话解释:为什么
doctor和hospital可能接近,但不是同义词? - 在 RAG 项目中,你会收集什么证据证明 embedding model 足够好?
项目交付参考与讲解
- 如果把
coupon放得很接近password,相似度检索可能把无关的促销或退货政策文本错误召回给账号恢复问题。问题不是随机的,而是向量空间位置错了。 recover和account应该靠近密码、账号支持等概念,而不是靠近无关的促销或退货政策概念。新增文档应该能匹配账号恢复类查询。- 如果 embedding 空间同时捕捉到 refund 意图和 order 语义,
refund order应该让退款/订单文档排第一。 doctor和hospital接近,是因为它们常出现在同一领域。相似度可以表示主题相关,不一定表示严格同义。- 有用证据包括固定查询集、期望 top-k 文档、召回分数、已知失败样例、延迟、成本,以及“换说法但意图不变”的测试。
Embedding 把离散 token ID 变成几何关系:
identityvectordistanceretrieval / clustering / model input
真正重要的不是公式,而是语义变成了可以比较、排序并传入神经网络的东西。