Embedding 是什么?1536 维什么意思?

#RAG#Agent#面试八股

Embedding 是什么?1536 维什么意思?

结论:Embedding(嵌入)是一个把文本映射成定长稠密向量的函数,形如 f(text) → [0.021, -0.113, ...]。 "1536 维"说的是这个向量有 1536 个浮点数,它是模型输出层的宽度,不是 1536 个各自可解释的特征。 语义相近的文本,向量在空间里的方向接近,所以能用余弦相似度(Cosine Similarity)做检索。

1. 一段文字是怎么变成 1536 个数的

"报销要在 30 天内提交"
   │ BPE 分词
   ▼
["报销", "要", "在", " 30", " 天", "内", "提交"]   → 7 个 token id
   │ Transformer 编码,每层做自注意力,token 之间交换上下文
   ▼
7 × 1536 的上下文矩阵(每个 token 一个 1536 维向量)
   │ 池化(Pooling):取 [CLS] 位,或对所有 token 求平均
   ▼
1 × 1536
   │ L2 归一化,使 ‖v‖₂ = 1
   ▼
[0.0134, -0.0871, 0.0522, ...]   共 1536 个 float32

归一化之后,余弦相似度就等于点积:cos(a,b) = a·b,向量库内部只做一次乘加,省掉开方与除法。

2. 为什么"语义接近"能变成"距离接近"

Embedding 模型通常用对比学习(Contrastive Learning)训练:一个批次里,把"同一句话的两种说法"或"问题-答案对"当正样本, 其余组合当负样本,损失函数要求正样本相似度升高、负样本相似度降低。训练久了,"怎么开发票"和"发票怎么开"自然被推到相邻位置, 而"发票"和"发票打印机型号"虽然共享字面,却在语义上被拉开。

这也解释了它的软肋:训练数据没覆盖的领域(内部缩写、产品代号)向量会挤在一起,此时必须靠关键词检索兜底。

3. "1536 维"到底意味着什么

模型维度最大输入说明
text-embedding-3-small15368191 token性价比高,默认首选
text-embedding-3-large30728191 token更强,存储翻倍
text-embedding-ada-00215368191 token老模型,新项目不必再用
bge-large-zh-v1.51024512 token中文单语,可本地部署
bge-m310248192 token多语言,稠密+稀疏+多向量

维度不是越大越好,三笔账要算清楚:

维度存储(100 万条)检索耗时精度
384≈1.5 GB基准一般
1024≈3.9 GB×2.5较好
1536≈5.9 GB×4好
3072≈11.7 GB×8略好于 1536

存储按 N × d × 4 字节 估算,索引结构还要再乘以 1.5 左右。text-embedding-3 系列支持 dimensions 参数把 1536 截断到 512, 精度只掉一点——这是 Matryoshka 表示学习的特性,前面的维度承载主要信息,适合"先便宜地粗排,再用全维度精排"。

4. 代码:把相似度算出来看

import numpy as np
from openai import OpenAI

client = OpenAI()

def embed(texts: list[str]) -> np.ndarray:
    # 返回 shape = (len(texts), 1536)
    resp = client.embeddings.create(model="text-embedding-3-small", input=texts)
    return np.array([d.embedding for d in resp.data], dtype=np.float32)

def cosine_matrix(mat: np.ndarray) -> np.ndarray:
    # 服务端已返回归一化向量,再归一次防止某些兼容实现偷懒
    unit = mat / np.linalg.norm(mat, axis=1, keepdims=True)
    return unit @ unit.T

texts = [
    "怎么申请开票",
    "发票申请流程是什么",
    "服务器磁盘占满了怎么处理",
    "开票要提交哪些材料",
]
sim = cosine_matrix(embed(texts))
print(sim.round(3))
# 输出(示意,具体数值随模型版本浮动):
# [[1.    0.941 0.187 0.876]
#  [0.941 1.    0.192 0.861]
#  [0.187 0.192 1.    0.179]
#  [0.876 0.861 0.179 1.   ]]

三句"开票"互相在 0.86 以上,和"磁盘占满"只有 0.18 左右——这就是检索能工作的全部依据。

5. 常见追问

Q:每个维度有含义吗? 没有。单看第 37 维,你无法解释它代表"正式程度"还是"是否涉及金额"。语义是分布式编码在整组维度里的,不要试图给维度起名字。

Q:为什么用余弦而不是欧氏距离? 文本长度会改变向量模长,方向才承载语义。归一化之后余弦与欧氏距离单调对应,两者排序等价,选哪个只是实现偏好。

Q:换 embedding 模型要注意什么? 向量空间不可比,新旧模型混在一个索引里等于随机噪声,必须全量重建。同时把 model 和 dimension 记进集合元数据,防止误用。

Q:输入超长被截断怎么办? 8191 token 之外的内容会被静默丢弃,长文档必须先分块;这也是一篇文档要切成多个片段的直接原因。

小结

Embedding 是 RAG 的"翻译层",把语义压进几何空间。维度决定成本,模型决定效果,两者都不能在索引建好之后随意更换。

笔记加载中…