跳转到内容

7.1.3 词向量与语义表示

Embedding 语义空间图

One-hot ID 能区分词,但不能表达哪些词相关。稠密 embedding 会把 token 放进一个向量空间:

token idembedding table lookupdense vector

在这个空间里:

  • 距离近的向量通常表示用法相关;
  • cosine similarity 衡量方向是否相近;
  • 句向量通常由 token 向量 pooling 得到;
  • 上下文模型能让同一个 token 根据周围词改变位置。

从独热编码(One-Hot)到稠密向量

Section titled “从独热编码(One-Hot)到稠密向量”

从 one-hot 到稠密 embedding 的语义空间图

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 -> close
password and reset -> close
refund and password -> far

这种几何关系不是手写规则,而是从数据里学出来的。经常出现在相似上下文里的词,向量也倾向于接近。

先跑这个极小 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))

预期输出:

Terminal window
refund vs return : 1.0
refund vs password: 0.293
password vs reset : 1.0

这样理解:

  • cosine 高表示方向相似,不代表含义完全相同;
  • refundreturn 接近,是因为这个玩具表把它们放在客服退款区域;
  • passwordreset 接近,是因为它们属于账号问题区域;
  • refundpassword 远,是因为意图不同。

现在把 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}")

预期输出:

Terminal window
B password reset: 1.000
C coupon return: 0.335
A refund policy: 0.333

这就是向量检索的核心:

query textquery vectorcompare with document vectorstop-k results

真实 RAG 系统会使用更强的 embedding model 和 vector database,但逻辑仍然是相似度排序。

Mean pooling 很好理解,但会丢掉重要信息:

  • 词序;
  • 否定;
  • 强调;
  • 长距离依赖;
  • 哪个 token 更关键。

例如玩具检索器会把 reset passwordpassword 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])

预期输出:

Terminal window
bank in finance: [0.8, 0.4, 0.7]
bank in river : [0.3, 0.75, 0.4]

Embedding 实验结果图

这不是一个真实 Transformer,只是帮助记忆:同一个 token 混入上下文后,可以得到不同表示。

场景embedding 提供什么注意点
RAG 检索找到语义相关 chunkchunk 切得差或 metadata 过期仍会伤害答案
FAQ 聚类合并相似问题接近不一定等于重复
去重找近似重复内容模板化文本和改写会干扰分数
分类把文本变成特征标签质量和校准仍然重要
推荐比较用户、物品或查询热门偏差可能压过语义相似
  • 如果库没有自动处理,先把向量 normalize 再算 cosine。
  • 打印 top-k 分数,不只看 top-1;分差小代表检索不确定。
  • 检查 false positive:相关词不一定就是正确答案。
  • 对同一批数据比较 static、sentence、上下文相关 embedding。
  • 多语言项目要先测跨语言样本,不要默认 embedding model 已经对齐语言。

学完这一页,至少保留这张证据卡:

向量
至少三个文本 embedding 或玩具向量
相似性检查
最近一对和分数
检索结果
某个查询的最佳匹配
局限
平均或相似度会忽略上下文/否定/顺序
下次使用
这将成为第 8 章中的检索证据
  1. coupon 移到更接近 password 的位置,观察检索如何出错。
  2. 添加文档 D recover account,并给 recoveraccount 设计向量。
  3. 创建 查询 refund order,你认为哪条文档应该排第一?
  4. 用自己的话解释:为什么 doctorhospital 可能接近,但不是同义词?
  5. 在 RAG 项目中,你会收集什么证据证明 embedding model 足够好?
项目交付参考与讲解
  1. 如果把 coupon 放得很接近 password,相似度检索可能把无关的促销或退货政策文本错误召回给账号恢复问题。问题不是随机的,而是向量空间位置错了。
  2. recoveraccount 应该靠近密码、账号支持等概念,而不是靠近无关的促销或退货政策概念。新增文档应该能匹配账号恢复类查询。
  3. 如果 embedding 空间同时捕捉到 refund 意图和 order 语义,refund order 应该让退款/订单文档排第一。
  4. doctorhospital 接近,是因为它们常出现在同一领域。相似度可以表示主题相关,不一定表示严格同义。
  5. 有用证据包括固定查询集、期望 top-k 文档、召回分数、已知失败样例、延迟、成本,以及“换说法但意图不变”的测试。

Embedding 把离散 token ID 变成几何关系:

identityvectordistanceretrieval / clustering / model input

真正重要的不是公式,而是语义变成了可以比较、排序并传入神经网络的东西。