知识点思维导图
29 个知识节点
参考资料
Python(25) - 向量与 Embedding
读完后,你应能完成以下任务:
- 绘制“Python(25) - 向量与 Embedding / 先建立前端锚点:用 CSS 颜色破题”的关键对象与数据流,解释“它就是一个三维向量 [255, 0, 0]。”,并用源码位置、日志或 Trace 标注证据。
- 为“Python(25) - 向量与 Embedding / 为什么不用关键词匹配?”设计正常与异常输入,验证“"退款"和"拿回钱"对机器是两个完全无关的字符串。”,输出首个偏差位置与回归测试结果。
- 实现“Python(25) - 向量与 Embedding / 核心干货二:怎么算"两段文字像不像"——余弦相似度”的最小代码或配置,检验“最常用的是余弦相似度(cosine similarity)。”,输出命令、结果与 Diff,并说明不适用边界。
你想做一个「语义搜索」:用户搜"怎么退款",文档里写的是"取消订单后如何拿回钱"——没有一个字相同,传统的关键词匹配(
includes/LIKE '%退款%')会一条都搜不到。本篇解决这个问题:怎么让机器理解"意思相近",而不是"字面相同"。答案就是 Embedding——把文字压成一串坐标数字,语义相近的文字坐标也挨得近,检索就变成"找最近的点"。这也是下一篇 RAG(第 26 篇)的地基。
依赖链提醒:阶段五是
23 调用大模型API → 24 函数调用/结构化输出 → 25 本篇 Embedding → 26 RAG → 27 Agent。本篇只讲清 Embedding 的最小可用形态(生成向量 + 算相似度 + 内存里检索),完整的"检索增强生成"流程留到第 26 篇。向量运算的底层全是 NumPy,建议先过第 20 篇。
一、先建立前端锚点:用 CSS 颜色破题
别一上来就背"向量""维度"这些术语。你其实早就用过向量了——CSS 颜色。
rgb(255, 0, 0) 是什么?它就是一个三维向量 [255, 0, 0]。三个数字分别是红、绿、蓝三个"维度"上的坐标。
关键直觉来了:
rgb(255, 0, 0) 纯红
rgb(250, 5, 5) 稍微暗一点的红 —— 数值接近,看起来也接近
rgb(0, 0, 255) 纯蓝 —— 数值差很远,看起来也差很远
颜色看起来像不像,约等于这两组数字离得近不近。 这就是向量的全部精髓——把"东西"变成"一串坐标",然后用"坐标距离"衡量"像不像"。
Embedding 干的是同一件事,只是:
| CSS 颜色 | Embedding | |
|---|---|---|
| 输入 | 一个颜色 | 一段文字 |
| 输出 | 3 个数字 [r, g, b] |
一长串数字(OpenAI 是 1536 个) |
| "近" 代表 | 颜色看起来像 | 语义(意思)相近 |
| 怎么算近 | 数值距离 | 向量距离 / 余弦相似度 |
所以一句话定义:Embedding 就是把一段文字压成一串坐标(一个高维向量),语义相近的文字,坐标点挨得近。 三维你能想象成空间里的点,1536 维想象不出来没关系,数学公式一模一样。
⚠️ 边界:颜色的三个维度有明确含义(红绿蓝),但 Embedding 那 1536 个维度没有人类能解释的含义——它是模型训练出来的,你别试图去理解"第 7 个维度代表什么"。你只需要相信:模型保证了"意思近 → 坐标近"这个性质。
二、为什么不用关键词匹配?
先看前端老办法的死穴:
// JS:传统关键词匹配
const docs = ["如何取消订单并拿回钱", "配送时间说明", "积分规则"]
const query = "怎么退款"
// 字面包含匹配:一条都搜不到,因为没有"退款"两个字
const hits = docs.filter(d => d.includes("退款")) // [] ❌
问题就在:includes / 数据库 LIKE 只认字面,不认语义。"退款"和"拿回钱"对机器是两个完全无关的字符串。
Embedding 把每段文字变成向量后,"退款"的向量和"拿回钱"的向量会挨得很近,于是就能搜到。这就是从"字符串匹配"升级到"语义匹配"的跨越。
三、核心干货一:怎么生成 Embedding
Embedding 不是你自己算的,是调模型 API 拿的(和第 23 篇调大模型同一套 SDK)。先装官方库:
pip install openai numpy
对照心智模型:这跟你在前端 fetch 一个 API 拿 JSON 没区别,只是返回的 JSON 里是一长串数字。
⚠️ 边界:embedding 模型 ≠ 聊天模型。
text-embedding-3-small只会吐向量、不会对话;gpt-4只会对话、不给你向量。调错 model 名字会直接报错。
批量更省钱省时——input 直接传列表,一次编码多条:
四、核心干货二:怎么算"两段文字像不像"——余弦相似度
拿到两个向量后,怎么量化它们"近不近"?最常用的是余弦相似度(cosine similarity)。
直觉:把向量看成从原点射出的箭头,余弦相似度衡量两支箭头的"方向"有多一致,跟箭头多长无关。
- 方向几乎相同 → 值接近 1(语义非常像)
- 方向垂直无关 → 值接近 0(不相关)
- 方向相反 → 值接近 -1(语义相反)
公式就是第 20 篇结尾那段:点积 / (两个向量各自的长度相乘)。用 NumPy 一行搞定:
⚠️ 易混点:别用"两点直线距离(欧氏距离)"想当然代替余弦相似度。 文本检索里余弦更常用,因为它只看方向、不受向量长度影响。两者结论大多一致,但语义检索的行业默认是余弦。另外注意:余弦相似度越大越相似,而距离是越小越相似,方向相反,排序时别搞反。
JS 里没有内置这些,得自己写循环,能直观看出 NumPy 帮你省了多少:
// JS:纯手写余弦相似度,对比一下啰嗦程度
function cosineSimilarity(a, b) {
let dot = 0, normA = 0, normB = 0
for (let i = 0; i < a.length; i++) { // 1536 次循环,逐个累加
dot += a[i] * b[i]
normA += a[i] * a[i]
normB += b[i] * b[i]
}
return dot / (Math.sqrt(normA) * Math.sqrt(normB))
}
五、核心干货三:归一化的小技巧(让点积 = 余弦)
如果你把每个向量先归一化(缩放成长度为 1),那么"余弦相似度"就退化成了简单的"点积"——少算一步除法,批量检索时更快。OpenAI 的 embedding 默认已经是归一化的,但自己处理时知道这点有用:
这一节属于"知道就行"的优化点,新手阶段直接用上面的
cosine_similarity即可,不影响功能。
六、核心干货四:搭一个最小语义检索(Embedding 的"Hello World")
把前面拼起来,就是一个内存版语义搜索——这就是 RAG 的雏形,也是 Embedding 最小可用形态:
这段就是语义检索的完整骨架,对照前端你会发现结构很眼熟:
| 阶段 | 干什么 | 类比前端 |
|---|---|---|
| 离线建库 | 把所有文档批量转向量存起来 | 构建期把数据预处理成索引 |
| 在线查询 | 查询转向量 → 和库里逐个算相似度 → 排序取 top_k | 拿到搜索词去索引里 filter + sort |
真实项目不会在内存里逐个 for 比较(几万条会很慢),而是用向量数据库(如 Chroma / FAISS / pgvector)做高效近邻检索——这是第 26 篇 RAG 的内容。本篇你先理解"检索 = 在向量堆里找最近的点"这个本质就够了。
七、前端新手最容易踩的坑
-
以为 embedding 是关键词提取——不是。它不抽关键词,而是把整段语义压成坐标。两段没有共同字的话也能算出"很像"。
-
embedding 模型和聊天模型用混——
text-embedding-3-small出向量,gpt-4出对话,model 名字传错直接报错。 -
每次请求都重新编码整个知识库——文档向量应该离线算一次存起来,只有用户的 query 才需要实时编码。否则又慢又烧钱。
-
相似度方向搞反——余弦相似度越大越相似(接近 1),排序要
reverse=True;如果你改用"距离"则是越小越相似,两者排序方向相反。 -
不同模型的向量混用比较——
text-embedding-3-small的向量和别的模型的向量不在同一个坐标系,不能互相算相似度。整个知识库必须用同一个模型编码。 -
维度对不上还硬算——两个向量长度(维度)必须相同才能算相似度。换了模型导致维度变化(比如 1536 → 3072),旧向量全部作废,得重新生成。
-
拿 list 直接做数学运算——Python 的
list不支持a * b这种向量化运算(那是 NumPy 的本事,第 20 篇),算相似度前记得np.array(...)。
八、总结
- 先建立前端锚点:用 CSS 颜色破题:它就是一个三维向量 [255, 0, 0]。
- 为什么不用关键词匹配?:"退款"和"拿回钱"对机器是两个完全无关的字符串。
- 核心干货一:怎么生成 Embedding:Embedding 不是你自己算的,是调模型 API 拿的(和第 23 篇调大模型同一套 SDK)。
- 核心干货二:怎么算"两段文字像不像"——余弦相似度:最常用的是余弦相似度(cosine similarity)。
- 核心干货四:搭一个最小语义检索(Embedding 的"Hello World"):真实项目不会在内存里逐个 for 比较(几万条会很慢),而是用向量数据库(如 Chroma / FAISS / pgvector)做高效近邻检索——这是第 26 篇 RAG 的内容。
- 前端新手最容易踩的坑:以为 embedding 是关键词提取——不是。 -> embedding 模型和聊天模型用混——text-embedding-3-small 出向量,gpt-4 出对话,model 名字传错直接报错。 -> 每次请求都重新编码整个知识库——文档向量应该离线算一次存起来,只有用户的 query 才需要实时编码。 -> 相似度方向搞反——余弦相似度越大越相似(接近 1),排序要 reverse=True;
学完自测
选择所有正确答案;提交后逐项核对判断依据。