Vector Databases

Purpose and Functionality

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

核心原理

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

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

相似度度量:

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

查询三阶段

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

与传统数据库的区别

维度传统数据库向量数据库
数据类型字符串、整数(精确值)高维向量(相似度)
查询方式精确匹配相似度搜索
索引方式B-tree、HashHNSW、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:文档分类、语义搜索、情感分析、问答系统
  • 推荐系统:个性化相似商品推荐
  • 图像/视频分析:监控、图像识别
  • 时间序列相似性搜索:检测历史数据中的相似模式

与其他向量数据库对比

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

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 其他向量数据库对比

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

常见问题

Q:Chroma 和 FAISS 有什么区别?

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

Q:Chroma 适合多大数据量?

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

Q:如何选择嵌入模型?

场景推荐模型
英文文本all-MiniLM-L6-v2(轻量)、text-embedding-3
中文文本m3e-base、bge-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 维6KB0.75KB8x
3072 维12KB1KB12x

缺点:有精度损失,约 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-NNANN
精度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

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐