Python 与 AI 开发教程

Embedding 向量

把一段文字变成一串数字,意思相近的文字数字也相近。语义搜索、RAG 和长期记忆都建立在这一步上。

  • 大模型基础 · 第 7 章
  • 7 分钟
  • 2 组对比
  • 基于 OpenAI Python SDK 3.42 · NumPy 2.5 · OpenAI Java SDK 4.95

什么是 embedding

embedding 把一段文字映射成一个固定长度的数字列表,也叫向量。模型经过训练,让意思相近的文字在向量空间里距离也近。

关键词搜索只匹配字面,搜「怎么退款」找不到「退货流程」。向量比较的是意思,所以能找到。

有几个基本事实:

  • 向量长度由模型决定,常见的是几百到几千维
  • 不同模型生成的向量不能混用,换模型要全部重新计算
  • 向量本身没有可读的含义,只有比较才有意义
  • embedding 模型和聊天模型是两种模型,有单独的接口和价格

生成向量

接口接收一段文字或一批文字,一次传多条比循环调用更快。下面的 EMBEDDING_MODEL 换成服务商提供的向量模型。

生成向量

Python
from openai import OpenAI

client = OpenAI()
response = client.embeddings.create(
    model=EMBEDDING_MODEL,
    input=["怎么退款", "今天天气不错"],
)

vectors = [item.embedding for item in response.data]
print(len(vectors))  # => 2
print(len(vectors[0]))  # => 8
var params = EmbeddingCreateParams.builder()
    .model(EMBEDDING_MODEL)
    .inputOfArrayOfStrings(List.of("怎么退款", "今天天气不错"))
    .build();

var response = client.embeddings().create(params);
List<Float> vector = response.data().get(0).embedding();
System.out.println(vector.size());
对比项PythonJava
批量输入input 传列表inputOfArrayOfStrings
结果类型list[float]List<Float>
结果顺序相同和输入相同

示例运行时用的是假模型,所以向量只有 8 维。真实模型的向量长度要看文档,有些模型能用 dimensions 参数调短,省存储。

比较两个向量

最常用的是余弦相似度,它看两个向量的夹角,结果在 -1 到 1 之间,越大越相似。

向量运算用 NumPy,它相当于 Python 的高性能数组库,一行代码就能处理整个数组。

余弦相似度

Python
import numpy as np

def cosine(a: list[float], b: list[float]) -> float:
    a, b = np.array(a), np.array(b)
    return float(a @ b / (np.linalg.norm(a) * np.linalg.norm(b)))

round(cosine([1, 0], [1, 0]), 2)  # => 1.0
round(cosine([1, 0], [0, 1]), 2)  # => 0.0
round(cosine([1, 1], [1, 0]), 2)  # => 0.71
double cosine(double[] a, double[] b) {
    double dot = 0, normA = 0, normB = 0;
    for (var i = 0; i < a.length; i++) {
        dot += a[i] * b[i];
        normA += a[i] * a[i];
        normB += b[i] * b[i];
    }
    return dot / (Math.sqrt(normA) * Math.sqrt(normB));
}

System.out.println(cosine(new double[]{1, 0}, new double[]{1, 0}));  // => 1.0
对比项PythonJava
点积a @ b自己写循环
长度np.linalg.norm自己算
速度NumPy 用 C 实现,很快循环本身就快

很多模型返回的向量已经归一化,长度是 1,这时点积就等于余弦相似度。

用好 embedding

  • 文本不能太长。 模型有输入上限,一大段文字压成一个向量,细节也会丢失,所以长文档要先切块。
  • 向量要缓存。 同样的文字生成的向量相同,存进数据库,不要重复计算。
  • 查询和文档用同一个模型。 有的模型区分查询和文档两种模式,要按文档设置。
  • 相似度没有绝对标准。 不同模型的分数范围不同,0.8 算不算相关要用真实数据试。
向量运算写法对照
PythonJava说明
client.embeddings.create()client.embeddings().create()生成向量
np.array(list)double[]NumPy 数组适合批量运算
a @ b自己写循环点积
matrix @ vector嵌套循环一次算完全部相似度
np.argsort(scores)自己排序取最相似的几条

本章要点

  • embedding 把文字变成向量,意思相近的文字向量也相近
  • 向量长度由模型决定,不同模型的向量不能混用
  • 比较用余弦相似度,归一化后就是点积
  • 语义搜索:给文档和问题生成向量,取相似度最高的几条
  • 数据少时用 NumPy 就够,多了再用 pgvector
  • 长文档先切块,向量要缓存,换模型要全部重算

参考资料:OpenAI Python SDKOpenAI Java SDKNumPy 快速入门pgvector