Vector Databases

Purpose and Functionality

向量数据库是专为存储和检索高维向量数据而设计的数据库,通过相似度搜索而非精确匹配来找到最相关的结果。

核心原理

向量数据库的核心是向量嵌入(Vector Embeddings):将非结构化数据(文本、图像、音频等)转换为数值数组,在高维空间中表示其语义特征。

数据 → 嵌入模型 → 向量嵌入 → 向量数据库存储
                                    ↓
用户查询 → 嵌入模型 → 查询向量 → 相似度匹配 → 返回最近邻结果

相似度度量

  • 余弦相似度(Cosine Similarity):衡量向量方向夹角
  • 欧氏距离(Euclidean Distance):衡量直线距离
  • 点积(Dot Product):快速计算相似度

查询三阶段

  1. Indexation(索引):将向量嵌入转换为快速搜索的数据结构
  2. Inquiry(查询):比较查询向量与索引向量,找到最近邻
  3. Post-production(后处理):对最近邻进行后处理,生成最终输出

与传统数据库的区别

维度 传统数据库 向量数据库
数据类型 字符串、整数(精确值) 高维向量(相似度)
查询方式 精确匹配 相似度搜索
索引方式 B-tree、Hash HNSW、LSH、ANN
适用场景 事务处理 AI/ML 场景

何时使用 vs 何时不用

适合使用向量数据库的场景

  • 大规模知识库,需要数百万次查询
  • 长期存在的共享数据
  • 需要语义搜索、推荐系统、图像识别

可能不需要向量数据库的场景

  • 数据是短暂的(每次请求新鲜上下文)
  • 用户只问少量问题
  • 需要最快速度响应
  • 场景:RAG 系统中用户上传临时文档,问几个问题后丢弃

基准测试参考(50,000 嵌入,1,536 维度):

方法 时间
朴素 KNN 搜索 ~24ms/查询
HNSW 索引构建 ~277ms(一次性)
HNSW 查询 ~0.47ms/查询

盈亏平衡点:约 11,510 次查询后,索引成本才能被查询时间节省抵消。

Top 5 向量数据库

数据库 特点 适用场景
Pinecone 完全托管、简单易用 云原生应用、快速上手
Milvus 开源、高性能、硬件加速 大规模生产环境
Zilliz 开源、分布式搜索 大数据、AI 应用
Qdrant 复杂过滤条件支持 需要精确筛选的搜索
Deeplake 云原生、实时摄取 ML 工作流

12 大应用场景

  1. NLP:语义搜索、情感分析、文本分类
  2. 异常检测:网络流量分析、欺诈检测
  3. 推荐系统:相似度匹配、个性化推荐
  4. 图像识别:以图搜图、视觉相似
  5. 搜索引擎:语义搜索、相关内容返回
  6. RAG:为 LLM 提供外部知识库,减少幻觉
  7. 医疗:患者相似性分析、药物发现
  8. 自动驾驶:传感器数据处理、环境理解
  9. 金融:欺诈检测、风险评估
  10. 音乐/多媒体:相似内容推荐
  11. 聚类分类:基于相似度的数据分组
  12. 图分析:社区识别、连接预测

优势

  • 数据类型:专为向量嵌入设计
  • 可扩展性:可存储数十亿高维向量
  • 高速搜索:先进索引算法(HNSW)
  • 相似度搜索:找到最佳匹配而非精确匹配
  • 灵活性:处理结构和非结构化数据

核心挑战

  • 事务支持弱(数据管理)
  • 高维向量索引计算成本高
  • 搜索计算量大、成本高
  • 调试困难(高维空间难以可视化)
  • 非向量格式数据不适用
  • 数据稀疏性导致效率问题
  • 大数据集查询延迟更高
  • 更新/删除需要维护索引一致性

选择标准

  1. 可扩展性和性能:数据量、维度、查询响应时间
  2. 数据模型和索引方法:是否支持灵活 schema、ANN 算法
  3. 易用性:文档、社区、配置难度
  4. 集成能力:API、SDK、与现有系统兼容性
  5. 社区和支持:活跃度、响应速度
  6. 成本:许可费用、运维成本

Popular Vector Databases

FAISS

FAISS(Facebook AI Similarity Search)是 Meta AI 开发的开源向量相似度搜索库,专注于高效相似度搜索和向量聚类。

核心算法

算法 说明
K-means 聚类 将数据分成簇,查询时缩小搜索空间,只在相关簇中搜索
乘积量化(PQ) 将向量压缩为短编码,大幅减少内存使用,加速搜索同时保持较高精度
优化乘积量化(OPQ) PQ 增强版,旋转数据以更好地适应量化网格,提高压缩向量精度

距离度量

  • 欧几里得距离:直线距离,关注向量几何相似性
  • 余弦相似度:方向角度,适合文本分析(方向比长度更重要)

关键特性

特性 说明
可扩展性 支持百万到数十亿向量,使用倒排文件系统和 HNSW 图
速度 GPU 加速可达 CPU 的 20 倍(Pascal 架构)
准确性 可在速度和精度间权衡,支持 1-recall@1 等指标
多样性 支持图像、文本、音频等各类数据

应用场景

  • 推荐系统:快速找到相似产品/电影/文章
  • 图像视频搜索:通过视觉特征找到相似内容
  • 异常检测:欺诈检测、网络安全、质量控制
  • 信息检索:基于语义的文档搜索

代码示例

from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings

# 构建索引
db = FAISS.from_documents(docs, embeddings)

# 查询
query = "What is machine learning?"
docs = db.similarity_search(query)

注意:Faiss 是一个而非完整数据库,适用于需要将向量搜索集成到现有系统的场景。相比完整的向量数据库(Pinecone、Milvus 等),Faiss 更轻量但需要更多手动管理。

Pinecone

Pinecone 是一个云原生向量数据库,专为处理高维向量数据设计,使用近似最近邻(ANN)搜索实现高效相似度匹配。

工作原理

数据 → 向量化 → 创建索引 → ANN 搜索 → 相似度比较 → 后处理 → 返回结果

核心特性

| 特性 | 说明 |
|—|—|—|
| 快速且新鲜的向量搜索 | 亿级数据规模下极低延迟;索引实时更新 |
| 过滤向量搜索 | 结合元数据过滤器(类别、价格等)实现更快相关结果 |
| 实时更新 | 支持数据动态更新,无需全量重建索引 |
| 备份和集合 | 自动备份,支持选择性备份特定索引 |
| 友好 API | 语言无关,支持多种编程语言 |
| 成本效益 | 按使用量付费 |

应用场景

  • 音频/文本搜索:高维数据快速相似度搜索
  • NLP:文档分类、语义搜索、情感分析、问答系统
  • 推荐系统:个性化相似商品推荐
  • 图像/视频分析:监控、图像识别
  • 时间序列相似性搜索:检测历史数据中的相似模式

与其他向量数据库对比

特性 Pinecone FAISS Milvus
部署方式 云原生(托管服务) 库(需自行集成) 开源(可自托管)
实时更新 支持 需重建索引 支持
元数据过滤 支持 需额外实现 支持
成本 按使用量付费 免费(自托管) 免费(自托管)

Chroma

什么是 Chroma?

Chroma 是一个专为 AI 应用设计的开源向量数据库,以轻量级、易用性著称。它主要用于存储和检索高维向量嵌入,是 RAG(检索增强生成)系统的核心组件。

核心特点

  • 开源免费(Apache 2.0)
  • 轻量级,API 简洁直观
  • 支持多种嵌入模型
  • 多语言客户端:Python、JavaScript/TypeScript、Rust
  • 可本地部署或 Docker 部署

安装

pip install chromadb

# 或使用 Docker
docker pull chromadb/chroma

核心概念

概念 类比(MySQL) 说明
Client 数据库连接 与 Chroma 数据库的连接
Collection 数据表 存储嵌入、文档和元数据的集合
Document 文本内容 要存储的原始文本
Embedding 向量 文档的数值表示
Metadata 附加信息 关于文档的额外数据

快速上手

创建客户端
import chromadb

# 方式一:内存模式(重启后数据丢失,适合测试)
client = chromadb.Client()

# 方式二:持久化模式(数据保存在本地磁盘)
client = chromadb.PersistentClient(path="./chroma_db")

# 方式三:HTTP 客户端(连接远程服务)
client = chromadb.HttpClient(host="localhost", port=8000)
创建集合
# 创建新集合
collection = client.create_collection(name="my_collection")

# 获取已存在的集合
collection = client.get_collection(name="my_collection")

# 获取或创建(如果不存在则创建)
collection = client.get_or_create_collection(name="my_collection")

# 删除集合
client.delete_collection(name="my_collection")
添加数据
collection.add(
    documents=[
        "This is a document about pineapple",
        "This is a document about oranges"
    ],
    ids=["id1", "id2"],
    metadatas=[
        {"source": "fruit article", "category": "tropical"},
        {"source": "fruit article", "category": "citrus"}
    ]
)

# 添加后查看数量
print(f"文档数量: {collection.count()}")
查询数据
# 相似度搜索
results = collection.query(
    query_texts=["This is a query document about hawaii"],
    n_results=2  # 返回最相似的 2 个结果
)

print(results)
# 输出格式:
# {
#     'ids': [['id1', 'id2']],
#     'documents': [['This is a document about pineapple', 'This is a document about oranges']],
#     'metadatas': [[{'source': 'fruit article', 'category': 'tropical'}, ...]],
#     'distances': [[0.5, 0.8]],  # 距离越小越相似
#     'embeddings': None
# }
获取和删除数据
# 根据 ID 获取
results = collection.get(ids=["id1", "id2"])

# 更新数据
collection.update(
    ids=["id1"],
    documents=["Updated document content"],
    metadatas=[{"source": "updated"}]
)

# 删除数据
collection.delete(ids=["id1"])

自定义嵌入函数

Chroma 默认使用嵌入模型,你也可以指定自己的模型:

from chromadb.utils import embedding_functions

# 使用 OpenAI 的嵌入模型
openai_ef = embedding_functions.OpenAIEmbeddingFunction(
    api_key="your-api-key",
    model_name="text-embedding-3-small"
)

# 创建集合时指定嵌入函数
collection = client.create_collection(
    name="my_collection",
    embedding_function=openai_ef
)

# 使用 Sentence Transformers(本地模型)
sentence_ef = embedding_functions.SentenceTransformerEmbeddingFunction(
    model_name="all-MiniLM-L6-v2"  # 或指定本地路径
)

collection = client.create_collection(
    name="my_collection",
    embedding_function=sentence_ef
)

元数据过滤

查询时可以结合元数据进行精确筛选:

# 创建带元数据的集合
collection = client.create_collection(name="filtered_collection")

collection.add(
    documents=["Apple pie recipe", "Orange juice recipe", "Carrot cake recipe"],
    ids=["id1", "id2", "id3"],
    metadatas=[
        {"type": "dessert", "calories": 300},
        {"type": "drink", "calories": 100},
        {"type": "dessert", "calories": 400}
    ]
)

# 只查询 type="dessert" 的文档
results = collection.query(
    query_texts=["sweet treats"],
    n_results=2,
    where={"type": "dessert"}  # 元数据过滤条件
)

# 复合条件
results = collection.query(
    query_texts=["light food"],
    n_results=2,
    where={
        "type": {"$eq": "dessert"},  # type == "dessert"
        "calories": {"$lt": 350}      # calories < 350
    }
)

支持的操作符

操作符 含义 示例
$eq 等于 {"type": {"$eq": "dessert"}}
$ne 不等于 {"type": {"$ne": "drink"}}
$lt 小于 {"calories": {"$lt": 300}}
$lte 小于等于 {"calories": {"$lte": 300}}
$gt 大于 {"calories": {"$gt": 100}}
$gte 大于等于 {"calories": {"$gte": 100}}

与 LangChain 集成

from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings

# 创建向量存储
vectorstore = Chroma.from_documents(
    documents=texts,           # 文档列表
    embedding=OpenAIEmbeddings(),  # 嵌入函数
    persist_directory="./chroma_langchain_db"  # 持久化路径
)

# 相似度搜索
docs = vectorstore.similarity_search("your query", k=3)

# 带分数的搜索
docs_with_scores = vectorstore.similarity_search_with_score("your query", k=3)

Chroma 1.0+ 新特性

1.0.0 版本核心更新

  • Rust 前端成为默认,性能提升 20%+
  • SQLite 迁移,单机部署更轻量
  • 多语言 SDK 全面升级(Python、JS/TS、Rust)
  • OpenAPI 规范更新,接口文档更清晰

1.0.8 版本核心更新

  • SPANN 索引默认启用,提升高维向量检索效率
  • 集合复制(Collection Forking)功能
  • 负载动态连接配置
  • 更完善的 GC(垃圾回收)系统

部署方式对比

方式 适用场景 特点
内存模式 Client() 本地开发测试 快速、免维护、重启丢失
持久化模式 PersistentClient() 本地生产环境 数据持久化、轻量
Docker 部署 团队共享/生产 隔离性好、易扩展
云服务 企业级应用 免运维、自动扩缩容

Chroma vs 其他向量数据库对比

特性 Chroma FAISS Pinecone Milvus
类型 完整数据库 向量索引库 云服务 开源数据库
部署难度 ⭐ 简单 ⭐⭐ 中等 ⭐ 简单(托管) ⭐⭐⭐ 复杂
功能完整性 CRUD+检索 仅索引/检索 CRUD+检索+托管 CRUD+检索+高级功能
适用规模 千-百万级 百万级 亿级+ 亿级+
成本 免费 免费 按量付费 免费(自托管)
适合场景 AI 应用开发、原型验证 嵌入式场景 企业级生产 大规模生产

常见问题

Q:Chroma 和 FAISS 有什么区别?

Chroma 是完整的数据库产品,开箱即用带 CRUD 功能;FAISS 只是一个向量索引库,需要自己集成到现有系统中。

Q:Chroma 适合多大数据量?

Chroma 适合中小规模数据(数千到数百万向量)。对于十亿级数据,考虑使用 Milvus、Pinecone 等。

Q:如何选择嵌入模型?

场景 推荐模型
英文文本 all-MiniLM-L6-v2(轻量)、text-embedding-3
中文文本 m3e-basebge-base-zh
多语言 multilingual-e5-base

最小示例

import chromadb

client = chromadb.Client()
collection = client.create_collection(name="demo")

collection.add(documents=["苹果很好吃", "香蕉也不错"], ids=["1", "2"])

results = collection.query(query_texts=["水果"], n_results=1)
print(results['documents'])
# 输出:[['苹果很好吃']]

参考链接

  • 官方文档:https://docs.trychroma.com
  • GitHub:https://github.com/chroma-core/chroma
  • 知乎教程:https://zhuanlan.zhihu.com/p/658217843
  • CSDN 深度解析:https://blog.csdn.net/leohu0723/article/details/157844388

Indexing Embeddings

什么是向量索引?

向量索引是将高维向量数据组织成一种特殊数据结构的过程,目的是让搜索速度从"大海捞针"变成"按图索骥"。

打个比方:假设你要在一百万本书里找"和《活着》最相似的书":

  • 没有索引:你得把每一本书都翻开来和《活着》对比一遍 — O(N) 复杂度,百万本书可能要等几分钟
  • 有索引:图书管理员已经把你的一百万本书按"主题/风格/年代"分门别类放好了,你直接去"现实主义文学"那个书架找 — O(log N) 复杂度,可能只要几毫秒

向量索引就是这个"图书分类系统",只不过分类的依据是向量在高维空间中的几何位置。


为什么需要索引?

朴素搜索的困境

假设我们有 100 万个 1536 维的向量(OpenAI ada-002 的输出维度)。每次查询时:

方法 计算量 延迟(估算)
暴力搜索(逐个比较) 100万次距离计算 ~100-500ms
建立索引后搜索 100-1000次距离计算 ~1-10ms

关键数字:暴力搜索 100 万个向量大约需要 100ms,而建立索引后只需要 1ms 左右,快了 100 倍

更重要的是:当数据量从 100 万增长到 10 亿时,暴力搜索延迟会线性增长到 10 秒(不可用),而好的索引算法只会增长到 ~10-50ms。


核心概念:ANN 搜索

向量索引基于 ANN(Approximate Nearest Neighbor,近似最近邻) 搜索,而非精确搜索。

ANN vs KNN

特性 KNN(精确搜索) ANN(近似搜索)
结果质量 100% 精确 95-99% 精确
搜索速度
内存占用 较高(需要额外索引结构)
适用规模 <10万 10万-10亿+

ANN 的核心思想:牺牲 1-5% 的精度,换取 10-100 倍的性能提升。在实际应用中,用户几乎感知不到差异。


主流索引算法详解

1. HNSW(分层可导航小世界图)

一句话理解:想象一个"高速公路网络"——全国高速连成一张网,城市内部又有地方公路。查询时先走高速快速缩小范围,再走地方公路找到精确目标。

Layer 2: ●━━━━━━━●━━━━━━━●━━━━━━━●   ← 少量节点,广域快速跳转
         │       │       │       │
Layer 1: ●●━━━━●●━━━━●●━━━━●●━━━━●●  ← 中等密度
         |||     |||     |||     |||
Layer 0: ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●  ← 所有节点,最近邻连接

工作原理

  1. 建索引时:自底向上构建多层图,上层节点少、下层节点多,像金字塔
  2. 查询时:从顶层最远节点开始,比较后跳到最近的邻居节点,类似贪心爬山
  3. 特点:查询快(O(log N))、精度高、内存占用中等

适用场景:大多数通用场景,Pinecone 默认使用

参数调优

  • M:每个节点维护多少条边(默认 16,越大越精准但越慢)
  • efConstruction:建索引时的搜索广度(默认 200)

2. IVF(倒排文件索引)

一句话理解:先把向量按 K-means 聚类分成 N 个簇,查询时只搜索最相关的几个簇,而非全部。

原始向量空间:
    ●●●●●●●●  Cluster 0 (centroid: C0)
    ●●●●●●●●  Cluster 1 (centroid: C1)
    ●●●●●●●●  Cluster 2 (centroid: C2)

查询向量 Q:
    距离 C0: 0.8  ← 跳过
    距离 C1: 0.1  ← 最相关,只在这个簇里搜索
    距离 C2: 0.6  ← 跳过

工作原理

  1. 用 K-means 把所有向量分成 N 个簇,每个簇有个中心点
  2. 查询时先和所有中心点比较,选出最近的 K 个簇
  3. 只在这 K 个簇内做暴力搜索

优点:原理简单,易于理解
缺点:维度高时聚类效果下降,搜索精度不稳定

适用场景:数据分布均匀、维度不是特别高(<512维)


3. LSH(局部敏感哈希)

一句话理解:把相似的向量"哈希"到同一个桶里,查询时直接到对应桶里找。

向量 v1 = [0.1, 0.8, 0.2]  → 哈希值 "0101" → 桶 A
向量 v2 = [0.2, 0.7, 0.1]  → 哈希值 "0101" → 桶 A  ← 和 v1 在同一桶,大概率相似
向量 v3 = [0.9, 0.1, 0.8]  → 哈希值 "1010" → 桶 B

查询 Q = [0.15, 0.75, 0.2] → 哈希值 "0101" → 桶 A → 返回 v1, v2

核心特点

  • 概率保证:相似的向量以高概率落在同一桶
  • 但不是精确保证:有可能两个很相似的向量恰好落在不同桶

适用场景:需要"哈希碰撞"来加速的场景,如去重、相似文档检测


4. PQ(乘积量化)

一句话理解:把大向量"压缩"成短编码,压缩 4-16 倍,大幅降低内存和计算量。

原始向量:[0.12, 0.85, 0.33, 0.91, 0.45, 0.67, 0.23, 0.78]
                    ↓ 分成4段,每段2个数
子向量:[0.12, 0.85] [0.33, 0.91] [0.45, 0.67] [0.23, 0.78]
                    ↓ 每段独立量化
量化编码:    3           7           2           5    ← 原来8个数变成4个数字

为什么叫"乘积"量化

  • 把向量分成 M 段(通常 M=8 或 16)
  • 每段独立做 K-means 聚类(比如 K=256)
  • 存储时只存每段所属的聚类 ID,而非原始向量

压缩效果

向量维度 原始大小 PQ 压缩后 压缩比
1536 维 6KB 0.75KB 8x
3072 维 12KB 1KB 12x

缺点:有精度损失,约 2-5%

适用场景:内存受限场景,如边缘设备、大规模数据集


索引算法对比

算法 查询速度 精度 内存占用 适用规模 核心原理
HNSW ★★★★★ ★★★★★ ★★★☆☆ 10亿+ 图导航
IVF ★★★☆☆ ★★★★☆ ★★☆☆☆ 1亿 聚类
LSH ★★★☆☆ ★★☆☆☆ ★★★★☆ 1000万 哈希碰撞
PQ ★★★★★ ★★★☆☆ ★★★★★ 10亿+ 向量压缩

实际选择建议

  • 通用场景 → HNSW(Pinecone、Milvus 默认)
  • 内存受限 → PQ 或 HNSW+QP
  • 需要精确结果 → HNSW + 增大搜索参数
  • 快速原型 → IVF(实现简单)

实际使用中的索引参数

Pinecone 的索引配置

import pinecone

# 创建索引时指定索引类型和参数
pinecone.create_index(
    name="my-index",
    dimension=1536,
    metric="cosine",  # 或 "euclidean", "dotproduct"
    spec={
        "serverless": {
            "cloud": "aws",
            "region": "us-west-2"
        }
    }
)

Pinecone 内部自动选择最优索引算法(HNSW 变体),你只需关注:

  • dimension:向量维度(必须和嵌入模型输出一致)
  • metric:距离度量方式(影响相似度计算)
  • pod 类型:影响性能和成本

调整搜索精度 vs 速度

# 查询时控制搜索范围
query_results = index.query(
    vector=query_vector,
    top_k=10,
    include_metadata=True,
    # 精度调优参数(不同数据库 API 不同)
    ef=200  # HNSW 的 ef 参数,越大越精准但越慢
)

索引构建的最佳实践

1. 批量导入效率高

# 低效:逐条插入
for item in items:
    index.upsert([(id, vector)])

# 高效:批量插入
batch_size = 1000
for i in range(0, len(items), batch_size):
    batch = items[i:i+batch_size]
    vectors = [(item.id, item.vector) for item in batch]
    index.upsert(vectors)

2. 索引构建时机

策略 说明 适用场景
预先构建 数据全部导入后统一建索引 一次性导入
增量更新 新数据来了实时更新索引 持续数据流入
混合策略 定期全量重建 + 实时增量更新 生产环境

3. 索引监控指标

  • 查询延迟(P99):95% 的查询应该在 X ms 内完成
  • 召回率:ANN 结果与暴力搜索结果的比值,越接近 1 越好
  • 索引大小:索引占用的内存/磁盘空间

常见问题

Q:索引会影响查询结果的准确性吗?

A:ANN 搜索返回的是"近似最优"而非"精确最优"。但实际上:

  • 召回率通常在 95-99%,即 100 个最近邻中漏掉 1-5 个
  • 对于大多数应用(推荐、搜索),用户感知不到差异
  • 如果必须 100% 精确,只能用暴力搜索,但延迟会高 50-100 倍

Q:HNSW 的 M 参数越大越好吗?

A:不是,需要权衡:

  • M↑ → 精度↑,但内存↑、建索引时间↑
  • 建议值:M=16-64,根据数据规模和延迟要求调整
  • 小规模数据(<10万)可以不用 HNSW,直接暴力搜索也很快

Q:如何选择距离度量?

度量 适用场景 特点
余弦相似度 文本嵌入(方向比长度重要) 值域 [-1, 1],0 表示正交
点积 归一化向量、推荐系统 值域 [-1, 1](归一化后)
欧氏距离 图像嵌入、坐标数据 值域 [0, +∞),物理距离

经验法则:如果你用了 OpenAI 或 Cohere 的嵌入模型,选择 余弦相似度


总结

向量索引是将"大海捞针"变成"按图索骥"的关键技术:

  1. HNSW 是当前最流行的算法,综合性能最好
  2. ANN 搜索牺牲 1-5% 精度换取 10-100 倍性能提升
  3. PQ 量化是内存受限场景的首选
  4. 实际使用中,优先用云服务的默认配置,有性能问题再调参

Similarity Search

什么是相似度搜索?

相似度搜索(Similarity Search)是在高维向量空间中找到与查询向量最相似的结果的过程。与传统数据库的精确匹配不同,它返回的是"最接近"的若干结果,按相似度排序。

一句话理解

把 1000 万张图片转化为一千万个点保存在空间中,查询时:

  • 输入一张"猫"的图片 → 转换为向量 → 在空间中找距离最近的点 → 返回"相似猫图片"

搜索的三步骤

1. 向量化:文本/图像 → 嵌入模型 → 高维向量
2. 索引化:向量 → 索引结构(HNSW/IVF 等)
3. 搜索:查询向量 → 计算距离 → 返回最近邻

向量表示:如何把万物变成向量?

核心思想

将现实世界的对象(文本、图像、音频)转换为连续的数值向量,语义相似的内容在向量空间中距离更近。

常见嵌入模型

模型 用途 输出维度 特点
Word2Vec 词嵌入 100-300 维 捕捉词的语义关系,“king - man + woman ≈ queen”
GloVE 词嵌入 50-300 维 融合全局统计信息
Universal Sentence Encoder (USE) 句子嵌入 512 维 整句语义,而非单词叠加
CNN (如 VGG) 图像嵌入 数百-数千维 提取视觉特征

嵌入空间可视化

                    生物相关
                       ↑
        🐱 猫    🐕 狗    🐴 马
                      ↗    ↘
              🍎 苹果        🍊 橙子
                    ↖ 食物相关 ↓
                    🍞 面包

语义相似的词在向量空间中聚集在一起,形成"语义簇"。


距离度量:如何衡量"相似"?

距离度量是相似度搜索的核心,决定了如何计算向量之间的"远近"。

1. 欧几里得距离(Euclidean Distance)

几何直觉:直线距离,像用尺子测量两点间的长度。

     B(4,4)
    /|
   / |
  /  |
 A(1,1)

 d = √[(4-1)² + (4-1)²] = √18 ≈ 4.24

公式

d(A,B) = √[Σ(ai - bi)²]

适用场景

  • 密集连续数据(如图像特征)
  • 物理距离有意义的场景
  • 低维或中等维数据(<100维)

局限性:高维空间中"维度灾难"问题,距离趋于相近


2. 曼哈顿距离(Manhattan Distance / L1)

几何直觉:像在城市中开车,只能沿格子路走,不能直穿大楼。

     B(4,4)
    /|
   / |
  /  |
 A(1,1)

 d = |4-1| + |4-1| = 3 + 3 = 6

公式

d(A,B) = Σ|ai - bi|

适用场景

  • 网格状数据(如棋盘移动)
  • 对异常值更鲁棒
  • 特征尺度不同的场景

3. 余弦相似度(Cosine Similarity)

几何直觉:不看向量长度,只看方向。两个向量方向越接近,相似度越高。

         A
        ↗
       ↗ 60°
      ↗______ B

 cos(θ) = (A·B) / (|A|·|B|)

公式

cos(A,B) = (A·B) / (|A|·|B|)

值域[-1, 1]

  • 1:完全相同方向
  • 0:正交(无关联)
  • -1:完全相反

为什么文本搜索首选余弦相似度

  • 文本向量维度高且稀疏(很多 0)
  • 关注"词出现与否"而非"词出现多少次"
  • 归一化后长度不影响结果

4. 切比雪夫距离(Chebyshev Distance)

几何直觉:像象棋中的"王",可以横竖斜走,取最大移动次数。

公式

d(A,B) = max(|ai - bi|)

适用场景

  • 棋盘类游戏
  • 允许对角线移动的网格导航

距离度量对比与选择

度量 公式 值域 适用数据 特点
欧几里得 √[Σ(ai-bi)²] [0, +∞) 密集连续、图像 直观,但高维效果差
曼哈顿 Σ ai-bi [0, +∞)
余弦 (A·B)/(|A||B|) [-1, 1] 文本、高维稀疏 只看方向不看长度
切比雪夫 max|ai-bi| [0, +∞) 棋盘、网格导航 考虑最极端差异

选择决策树

数据特点?
├── 高维稀疏(文本)→ 余弦相似度 ✓
├── 密集连续(图像特征)→ 欧几里得距离 ✓
├── 网格/坐标系 → 曼哈顿距离 ✓
└── 游戏/网格导航 → 切比雪夫距离 ✓

搜索算法:如何高效找到最近邻?

k-NN(k-Nearest Neighbors)精确搜索

原理:计算查询向量与所有向量的距离,返回最近的 K 个。

查询向量 Q:
与所有点计算距离 → 排序 → 返回最近的 3 个

Q 到 A: 0.85
Q 到 B: 0.12  ← K=3 结果
Q 到 C: 0.23  ← K=3 结果
Q 到 D: 0.31  ← K=3 结果
Q 到 E: 0.67

优点:100% 精确
缺点:O(N) 复杂度,百万级数据太慢

适用规模:<10 万向量


ANN(近似最近邻)搜索

原理:牺牲少量精度换取 10-100 倍速度提升。

与 k-NN 的对比

特性 k-NN ANN
精度 100% 95-99%
速度
内存 较高
数据规模 <10万 10万-10亿+

常见 ANN 算法:HNSW、IVF、LSH(详见 Indexing 章节)


实际应用场景

1. 电商推荐

用户浏览:Nike Air Max 运动鞋
         ↓ 向量化
向量空间中找到相似向量
         ↓
返回:Nike Air Jordan、Adidas Ultra Boost、Puma RS-X

2. 图像搜索

上传图片 → CNN 提取特征向量 → 在向量空间中搜索 → 返回相似图片

3. 文档检索

搜索"机器学习教程"
         ↓ USE 编码
找到语义最相似的文档(即使没用"机器学习"这个词)

4. 欺诈检测

新交易 → 转换为特征向量 → 找最近的历史交易
         ↓
相似异常交易多 → 高风险

实战代码示例

使用 FAISS 进行相似度搜索

import numpy as np
from faiss import IndexFlatL2

# 创建 10000 个 128 维的随机向量(模拟嵌入)
dimension = 128
num_vectors = 10000
vectors = np.random.random((num_vectors, dimension)).astype('float32')

# 建立索引(使用欧几里得距离的暴力搜索)
index = IndexFlatL2(dimension)
index.add(vectors)

# 查询:找最相似的 5 个
query = np.random.random((1, dimension)).astype('float32')
distances, indices = index.search(query, k=5)

print(f"最相似的 5 个向量索引: {indices}")
print(f"对应的距离: {distances}")

使用余弦相似度

from sklearn.metrics.pairwise import cosine_similarity
import numpy as np

# 两个句子的嵌入向量(简化示例)
sentence1 = np.array([[0.1, 0.8, 0.3, 0.5]])
sentence2 = np.array([[0.2, 0.7, 0.2, 0.6]])

similarity = cosine_similarity(sentence1, sentence2)
print(f"余弦相似度: {similarity[0][0]:.4f}")

常见问题

Q:余弦相似度和点积有什么区别?

特性 余弦相似度 点积
归一化 是(除以向量长度)
值域 [-1, 1] 无界
对长度敏感 不敏感 敏感

如果向量已经归一化,余弦相似度 ≈ 点积。

Q:高维空间中欧几里得距离为什么失效?

维度灾难:随着维度增加,所有点之间的距离趋于相等。

100 维空间中随机向量:

  • 最小距离 ≈ 最大距离的 0.7 倍
  • 有效区分度大大降低

解决方案:降维(PCA/UMAP)或使用对高维更鲁棒的度量(余弦相似度)。

Q:如何选择 K 值?

K 值 特点 适用场景
K=1 最近邻,易受噪声影响 快速匹配、异常检测
K=5~10 平衡精确度和稳定性 通用推荐
K=20+ 稳定,但可能过于宽泛 聚类、分类

经验:从 K=10 开始,根据效果调整。


总结

  1. 向量表示是基础:Embedding 模型决定了你能否捕捉到"语义相似性"
  2. 距离度量是核心:余弦适合文本,欧几里得适合图像
  3. 搜索算法是效率:数据量大时必须用 ANN
  4. 选择原则
    • 文本/推荐 → 余弦相似度
    • 图像/特征 → 欧几里得距离
    • 大规模数据 → HNSW 索引 + ANN 搜索
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐