Skip to content

向量数据库实战(Milvus/Chroma)

从 Embedding 的第一个维度到 RAG 系统的最后一公里——手把手带你玩转向量数据库。


2. Embedding——把万物变成向量

上一章我们知道了:向量数据库靠"计算向量之间的距离"来做语义搜索。但有一个关键前提——你得先把文本变成向量

这个"变"的过程,就是 Embedding

它是整个向量数据库工作流的第一步,也是决定搜索质量的最关键一步——如果向量生成得不好,后面的索引、检索、RAG 全都白搭。


2.1 Embedding 的直觉理解:语义的坐标系

什么是 Embedding

Embedding 的本质是一个映射函数——把非结构化数据(文本、图片、音频)映射成一组固定长度的浮点数:

Embedding 函数的输入输出:

  输入(人类可读)              输出(机器可计算)
  ─────────────────           ─────────────────────────
  "Python 很好用"        →     [0.82, -0.15, 0.93, ..., 0.41]
  "Java 性能很强"        →     [0.71, -0.22, 0.85, ..., 0.38]
  "今天天气真好"          →     [-0.12, 0.88, -0.05, ..., 0.67]

  关键特性:
    • 输出长度固定(如 1536 维)
    • 语义相近 → 向量相近
    • 语义无关 → 向量远离

为什么叫"嵌入"

"Embedding"翻译成"嵌入"——把高维的、复杂的语义信息嵌入到一个连续的数值空间中:

从离散文字到连续空间:

  人类的文字世界(离散、模糊、多义)
  ┌──────────────────────────────────┐
  │ "快"                              │
  │   → 速度快?心情愉快?快递?        │
  │   → 传统搜索无法区分               │
  └──────────────────────────────────┘

        │  Embedding 模型
        │  (神经网络)

  连续数值空间(精确、可计算、消歧义)
  ┌──────────────────────────────────┐
  │ "这个算法运行很快"                 │
  │   → [0.82, 0.15, ...]            │
  │   靠近"性能优化"、"加速"            │
  │                                  │
  │ "今天心情很快乐"                   │
  │   → [-0.31, 0.77, ...]           │
  │   靠近"开心"、"愉悦"               │
  │                                  │
  │ → 同一个"快"字,不同语境           │
  │   → 被映射到空间中完全不同的位置     │
  └──────────────────────────────────┘

核心价值:Embedding 把人类语言中的"模糊语义"变成了计算机能精确计算的"数字坐标"。有了坐标,就能算距离;能算距离,就能做搜索。

Embedding 模型是怎么训练出来的

你不需要自己训练 Embedding 模型(直接用现成的就好),但理解训练原理有助于做出更好的选型决策:

Embedding 模型的训练思路(简化版):

  训练目标:让语义相近的文本对,向量也相近
  ─────────────────────────────────────────

  正样本对(语义相近):
    "怎么提升代码性能" ←→ "如何让程序跑得更快"
    → 训练目标:让这两个文本的向量距离 → 0

  负样本对(语义无关):
    "怎么提升代码性能" ←→ "今天午饭吃什么"
    → 训练目标:让这两个文本的向量距离 → 1

  训练数据来源:
    • 搜索引擎的 query-document 点击日志
    • 问答网站的 question-answer 配对
    • 论文的 title-abstract 配对
    • 人工标注的语义相似度数据集

  模型架构:
    ┌───────────┐     ┌───────────┐
    │  文本 A    │     │  文本 B    │
    └─────┬─────┘     └─────┬─────┘
          │                 │
          ▼                 ▼
    ┌───────────┐     ┌───────────┐
    │ Transformer│     │ Transformer│  ← 共享参数
    │  Encoder   │     │  Encoder   │
    └─────┬─────┘     └─────┬─────┘
          │                 │
          ▼                 ▼
      向量 A             向量 B
          │                 │
          └───── 对比 ──────┘

            是否相似?→ 调整参数

实用结论:训练数据决定了 Embedding 模型擅长什么。用英文数据训练的模型做中文搜索效果差,用通用数据训练的模型做代码搜索也不够好。所以选对模型很重要。

2.2 主流 Embedding 模型选型

Embedding 模型的选择直接决定了搜索质量。选错了模型,后面的向量数据库再怎么调优也救不回来。

三大阵营

Embedding 模型的生态格局:

  ┌─────────────────────────────────────────────────┐
  │                                                 │
  │  1️⃣ 商业 API(开箱即用,按量付费)                │
  │     • OpenAI text-embedding-3-small/large       │
  │     • Google text-embedding-005                 │
  │     • Cohere embed-v4                           │
  │                                                 │
  │  2️⃣ 开源通用模型(免费,本地部署)                 │
  │     • BGE 系列(智源,中文最强)                   │
  │     • GTE 系列(阿里)                            │
  │     • Sentence-Transformers(HuggingFace 生态)  │
  │     • E5 系列(微软)                             │
  │                                                 │
  │  3️⃣ 特定领域模型(针对性场景)                     │
  │     • CodeBERT / UniXcoder(代码搜索)            │
  │     • BiomedCLIP(医学图文)                      │
  │     • 各行业微调模型                              │
  │                                                 │
  └─────────────────────────────────────────────────┘

主流模型对比

模型维度中文能力价格速度适合场景
OpenAI text-embedding-3-small1536★★★★$0.02/100万 token极快(API)通用场景、快速原型
OpenAI text-embedding-3-large3072★★★★$0.13/100万 token快(API)需要更高精度
BGE-large-zh-v1.51024★★★★★免费中等(本地)中文场景首选
BGE-M31024★★★★★免费中等(本地)多语言 + 混合检索
GTE-large-zh1024★★★★★免费中等(本地)中文场景备选
all-MiniLM-L6-v2384★★免费极快(本地)英文轻量场景
E5-large-v21024★★★免费中等(本地)英文高质量

选型决策指南

你的场景是什么?

  需要支持中文?
  ├── 是
  │   ├── 追求省事,有预算? → OpenAI text-embedding-3-small
  │   ├── 数据不能出境?     → BGE-large-zh-v1.5(本地部署)
  │   ├── 需要多语言混合?   → BGE-M3
  │   └── 数据量巨大,要求性价比? → BGE-M3(免费 + 混合检索)

  └── 否(纯英文)
      ├── 快速原型?         → OpenAI text-embedding-3-small
      ├── 资源受限?         → all-MiniLM-L6-v2(384 维,极快)
      └── 高精度要求?       → E5-large-v2 或 OpenAI large

关于维度的一个常见误解

"维度越高 = 效果越好?"

  ❌ 错误理解:
    384 维 < 1024 维 < 1536 维 < 3072 维
    → 所以 3072 维一定最好?

  ✅ 正确理解:
    维度只是向量的"信息容量"
    更重要的是模型的训练质量和数据

    实际表现(MTEB 中文基准测试):
      BGE-large-zh   1024 维  → 得分 67.3
      OpenAI small   1536 维  → 得分 65.1
      all-MiniLM     384 维   → 得分 41.2(中文很差)

    结论:
      → 中文场景下,1024 维的 BGE 比 1536 维的 OpenAI 更好
      → 维度不是决定因素,训练数据和模型架构才是
      → 但维度越高,存储和计算成本越高

  实际建议:
    • 1024 维是目前的甜点(平衡精度和成本)
    • 384 维适合资源极度受限的场景
    • 3072 维通常性价比不高,除非有特殊精度需求

经验之谈:如果你的应用主要面向中文用户,BGE 系列是当前的最优选。免费、本地部署、在中文基准上持续领先。只有在追求开发效率(不想管模型部署)时,才考虑 OpenAI API。

2.3 实操:用 Python 生成文本向量

理论讲完,动手。我们分别用 OpenAI API 和开源模型生成向量,然后亲手验证"语义相近 → 向量相近"。

方案一:OpenAI Embedding API(最快上手)

python
# 安装依赖
# pip install openai numpy

from openai import OpenAI
import numpy as np

client = OpenAI()  # 需要设置 OPENAI_API_KEY 环境变量

# --- 生成单条文本的向量 ---
def get_embedding(text: str, model: str = "text-embedding-3-small") -> list[float]:
    """调用 OpenAI API 生成文本向量"""
    response = client.embeddings.create(
        input=text,
        model=model
    )
    return response.data[0].embedding

# 试一下
embedding = get_embedding("Python 性能优化指南")
print(f"向量维度:{len(embedding)}")      # 1536
print(f"前 5 个值:{embedding[:5]}")       # [0.023, -0.041, ...]

# --- 批量生成(更高效) ---
def get_embeddings(texts: list[str], model: str = "text-embedding-3-small") -> list[list[float]]:
    """批量生成向量,一次 API 调用处理多条文本"""
    response = client.embeddings.create(
        input=texts,
        model=model
    )
    return [d.embedding for d in response.data]

# 批量处理 100 条文本,只需 1 次 API 调用
texts = ["文本1", "文本2", "文本3"]  # 实际可放 100+ 条
embeddings = get_embeddings(texts)

成本估算:text-embedding-3-small 的价格是 $0.02 / 100 万 token。一篇 1000 字的中文文档 ≈ 500-800 token,也就是说 100 万字的文档库,向量化成本不到 1 块钱

方案二:开源模型 BGE(本地部署、免费)

python
# 安装依赖
# pip install sentence-transformers

from sentence_transformers import SentenceTransformer

# 加载 BGE 中文模型(首次运行会下载约 1.2GB)
model = SentenceTransformer("BAAI/bge-large-zh-v1.5")

# --- 生成向量 ---
texts = [
    "Python 性能优化指南",
    "怎么让 Python 程序跑得更快",
    "今天天气真好"
]

# encode() 自动处理批量
embeddings = model.encode(texts, normalize_embeddings=True)

print(f"向量维度:{embeddings.shape}")     # (3, 1024)
print(f"第1条向量前5个值:{embeddings[0][:5]}")

注意:BGE 模型推荐设置 normalize_embeddings=True,这会对向量做 L2 归一化,使得余弦相似度计算更高效(归一化后的向量,内积 = 余弦相似度)。

亲手验证:语义相近 = 向量相近

python
import numpy as np

def cosine_similarity(a, b):
    """计算两个向量的余弦相似度"""
    return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

# 准备 3 组文本
texts = [
    "Python 性能优化指南",           # A: 基准
    "怎么让 Python 程序跑得更快",     # B: 语义相近,措辞不同
    "今天午饭吃什么好呢",             # C: 完全无关
]

# 生成向量(用 BGE 或 OpenAI 都行)
embeddings = model.encode(texts, normalize_embeddings=True)

# 计算相似度
sim_ab = cosine_similarity(embeddings[0], embeddings[1])
sim_ac = cosine_similarity(embeddings[0], embeddings[2])

print(f"A ↔ B 相似度:{sim_ab:.4f}")   # ≈ 0.85 (高度相似)
print(f"A ↔ C 相似度:{sim_ac:.4f}")   # ≈ 0.12 (几乎无关)
运行结果解读:

  "Python 性能优化指南" ↔ "怎么让程序跑得更快"
    → 相似度 ≈ 0.85
    → ✅ 模型理解了"性能优化" ≈ "跑得更快"

  "Python 性能优化指南" ↔ "今天午饭吃什么"
    → 相似度 ≈ 0.12
    → ✅ 模型知道两者毫无关系

  对比关键词搜索:
    → "性能优化" 和 "跑得更快" 没有一个字重叠
    → 关键词搜索会认为它们完全无关
    → 但 Embedding 知道它们在说同一件事

关键收获:这就是为什么向量数据库能做到"关键词搜索做不到的事"——因为 Embedding 模型在训练过程中已经学会了人类语言的语义关系。

Embedding 使用的注意事项

实际项目中容易踩的坑:

  1. 模型不能混用
     ─────────────────────────────────────
     ❌ 文档用 OpenAI 向量化,查询用 BGE 向量化
     → 两个模型的向量空间完全不同,无法比较
     ✅ 必须用同一个模型生成所有向量

  2. 文本长度有上限
     ─────────────────────────────────────
     • OpenAI small:最多 8191 token(≈ 4000 中文字)
     • BGE-large-zh:最多 512 token(≈ 250 中文字)
     → 超长文本必须先分块(Chunking),再逐块向量化

  3. 不同类型的内容需要不同策略
     ─────────────────────────────────────
     • 短文本(标题、问题)→ 直接向量化
     • 长文本(文章、文档)→ 分块后向量化
     • 代码 → 考虑用代码专用 Embedding 模型
     • 多模态 → 考虑 CLIP 等跨模态模型

本章小结

知识点要点
Embedding 本质把非结构化数据映射为固定长度的浮点数向量
核心特性语义相近的文本 → 向量距离近;语义无关 → 向量距离远
训练原理对比学习:正样本对距离 → 0,负样本对距离 → 1
模型选型中文优先 BGE 系列,省事选 OpenAI API
维度迷思维度 ≠ 质量;1024 维是当前甜点
OpenAI API简单、快速、便宜($0.02/百万 token),但数据要出境
BGE 本地部署免费、中文强、数据不出境,但需要 GPU 加速
核心注意点模型不能混用、长文本要分块、选对场景匹配的模型

下一章预告:相似度搜索——向量数据库的核心能力。我们会深入余弦相似度、欧氏距离、内积三种度量方式的原理与对比,理解为什么在百万级数据上"暴力搜索"不可行,以及 ANN 近似搜索如何解决这个问题。

坚持是一种品格