一.为什么需要向量嵌入?
先问一个问题:为什么不能直接用传统关系型数据库来解决语义检索?
传统数据库主要依托关键词匹配和字符比对来干活。日常通过 WHERE title="气候变化" 做精准查询,或者用 LIKE "%气候%" 来模糊检索。这套逻辑在面对字面内容高度一致的数据时确实够用,没毛病。
但问题在于,用户的真实需求往往不是“找一模一样的关键词”,而是“找到意思相近的内容”。比如用户问“什么是健康饮食习惯”,他期望出来的是“健康饮食与生活习惯建议”,而不是非要原词完全重合。像“全球变暖”和“气候变化”这种同义表达,传统的关键词模式是真的很难命中。下面这张表就很说明问题:
| 用户查询 | 期望匹配文档(标题) | 传统关键词能否命中 |
|---|---|---|
| 什么是健康饮食与生活习惯 | 健康饮食与生活习惯建议 | 部分命中(需包含相同词) |
| 向量数据库怎么用 | Milvus 向量数据库使用教程 | 难以命中(词面不同) |
| 全球变暖对生态的影响 | 气候变化对生态环境的影响 | 难以命中(同义表达) |
向量嵌入的思路就是把文本映射到一个高维连续向量空间里。在这个空间里,语义相近的文本,它们在空间中的距离也就更近。配合向量数据库,就能实现真正的语义检索。整体流程示意如下:
二.文本向量化的核心原理
(1)从字符到向量的流水线
文本向量化不是简单地查个字典就完事了,它背后是一条深度学习流水线:
首先对原始文本做分词处理,把整句拆解成模型能识别的子词单元,然后再将这些子词映射成对应的数字标识。接下来,基于Transformer的预训练嵌入模型上场,它为每个子词生成初始向量,并结合上下文语境来发掘词与词之间的语义关联。
深层特征编码完成后,通过CLS池化或均值池化这类操作,把长度不一的序列向量压缩整合成一个固定维度的全局文本向量——这就是我们需要的标准稠密向量了。最后根据需求,还可以做L2归一化处理,统一向量的模长,让它能更好地适配余弦相似度、内积等主流的相似度计算方法,为后续的向量库存储和语义匹配检索做好准备。
(2)稠密向量与稀疏向量
这两者的区别,直接看表格最直观:
| 类型 | 维度特点 | 典型来源 | 语义能力 | 关键词能力 |
|---|---|---|---|---|
| 稠密向量 Dense | 固定低维(128~4096),几乎全部维度非零 | Transformer Embedding 模型 | 强(同义、泛化) | 弱 |
| 稀疏向量 Sparse | 超高维(词汇表大小),仅少量维度非零 | TF-IDF、BM25 | 弱 | 强(精确词匹配) |
在文本向量表征体系里,稠密向量和稀疏向量是两类应用场景完全不同的形式,它们在维度结构、生成方式和语义表达能力上的差异非常明显。
稠密向量维度固定,取值范围集中,大部分维度都有有效数值。它主要靠大语言类嵌入模型训练得来,擅长挖掘文本的深层语义,能精准识别同义替换、语义引申这类抽象表达,但在精准的关键词匹配上就比较弱了。
稀疏向量呢,维度规模庞大,跟语料的词汇表量级保持一致,只有极少数维度有有效的权重数值。它多由传统的统计检索算法生成,在关键词精准命中、字词匹配场景中表现很好,但深度语义理解这块就是短板了。
在实际的RAG检索系统搭建过程中,通常的做法是把两种向量结合使用,让语义理解能力和关键词检索优势相辅相成,补齐单一向量检索的功能短板。
(3)嵌入空间的直觉
一个训练良好的嵌入模型,应该满足几个特点:
1. 语义相近的词,比如“国王”和“皇帝”,它们在向量空间中夹角要小。
2. 语义对立的词,比如“健康”和“疾病”,夹角要大。
3. 能体现类比关系,例如经典的 Word2Vec 现象:vec("国王") - vec("男人") + vec("女人") ≈ vec("女王"),现代模型仍然保留着类似的结构。
在维度选择上,存在一个需要权衡的点:
| 维度 | 优点 | 缺点 |
|---|---|---|
| 256~512 | 存储小、检索快 | 语义表达能力有限 |
| 1024(常用) | 平衡精度与性能 | 主流选择 |
| 2048~4096 | 细粒度语义区分 | 存储与计算成本高 |
三.相似度度量与向量空间
向量入库和检索之前,必须先把距离/相似度的度量方式约定好。
(1)常用度量公式与适应场景
| 度量名称 | 公式 | Milvus metric_type | 适用场景 |
|---|---|---|---|
| 欧氏距离 L2 | d = sqrt( Σ(ai - bi)^2 ) | L2 | 未归一化向量 |
| 内积 IP | s = Σ ai · bi | IP | L2归一化后等价于余弦 |
| 余弦相似度 | cos = (a·b) / (|a| |b|) | COSINE | 文本 Embedding 最常用 |
| BM25 | 基于词频的稀疏打分 | BM25 | 稀疏向量/全文检索 |
需要注意的是,在 Milvus 中 distance 字段的含义会根据 metric 不同而变化:L2 是越小越相似,IP / COSINE / BM25 则是越大越相似。后处理时设置的阈值必须跟 metric 对齐,否则结果会很乱。
(2)余弦相似度的几何直觉
在高维向量空间里,所有文本向量都以原点为起点进行分布。查询向量和各类文档向量之间会形成不同的夹角。
夹角大小直接决定语义相似度的高低——夹角越小,余弦相似度数值越高,文本的语义契合度也就越好。
当所有向量都做完 L2 归一化处理后,内积的计算结果和余弦相似度的排序逻辑就完全统一了。所以在实际项目开发中,大家大多直接采用内积来完成相似度判定,这样可以简化运算流程、降低检索计算开销。
下图能很直观地说明这一点:
四.文本向量化的工程实践
(1)单文本向量化与批量向量化
在实际工程中,API限流、批量大小、异常重试等问题都需要认真处理。下面这段 embedding_demo.py 演示了完整流程:
from embedding_utils import get_embedding, get_embeddings
# 单条文本向量化
query = "什么是健康饮食与生活习惯"
query_vector = get_embedding(query)
print(f"向量维度: {len(query_vector)}") # 1024
# 批量向量化自动按 BATCH_MAX_SIZE 分批
texts = [
"人工智能技术正在改变医疗行业",
"Python 是一门简洁易懂的编程语言",
"全球气候变暖导致极端天气增多",
]
vectors = get_embeddings(texts)
print(f"批量数量: {len(vectors)}, 单条维度: {len(vectors[0])}")
(2)向量化工具函数设计要点
文本批量向量化工具函数在设计时,需要充分结合接口调用规范和项目安全规范。使用批量接口能有效减少请求频次,大幅降低接口响应延迟和调用成本。同时要按照接口单次最大请求数量对文本数据进行分批切片,严格遵守平台接口的传入条数限制,避免请求报错。
函数要统一设置向量维度入参,确保存入向量数据库同一集合内的所有向量维度保持一致,这是数据库存储和检索的基础要求。在密钥管理方面,应该把接口密钥放在环境变量里调用,抛弃代码硬编码的写法,从源头杜绝密钥泄露风险,提升项目的整体安全性和可维护性。
| 设计要点 | 原因 |
|---|---|
| 批量接口 | 减少API调用次数,降低延迟与费用 |
| 分批切片 | 遵守API batch_size 上限 |
| 统一维度参数 | 同一 Collection 内所有向量维度必须一致 |
| 环境变量存 API Key | 避免密钥硬编码泄露 |
(3)文档入库前的数据装配
把业务字段和向量字段组装成 Milvus 可以接受的记录结构:
import json
with open("sample_data.json") as f:
data = json.load(f)
texts = [item["content"] for item in data]
embeddings = get_embeddings(texts)
for item, vector in zip(data, embeddings):
item["vector"] = vector # 装配稠密向量字段
client.insert(collection_name="demo_collection", data=data)
其中 sample_data.json 的记录结构举例如下:
[
{
"id": 1,
"title": "健康饮食与生活习惯指南",
"content": "日常保持均衡饮食,多食用蔬菜水果,减少高油高盐食物摄入,同时坚持规律作息与适度运动,养成良好生活方式。",
"source": "生活健康科普文库",
"vector": [0.021, 0.135, -0.087, 0.204, 0.056, -0.112, 0.093, -0.037]
},
五.Milvus数据库架构概览
Milvus 是专为海量高维向量设计的开源分布式数据库,核心解决存储、索引和近似最近邻(ANN)检索这三大问题。
(1)整体架构
这里不配图了,简单描述一下。想深入了解的话,还是建议看看官方文档。
Milvus 采用分层式架构设计,自上而下依次是:客户端层、接入层、协调层、工作节点层和底层存储层。
客户端可以通过官方 SDK 或 REST 接口发起各类业务请求;请求统一经过 Proxy 接入层完成路由转发和身份校验;各类协调器负责统筹管理元数据、数据分片、查询任务和索引任务,合理分发调度;最后数据节点、查询节点、索引节点完成数据写入、向量检索、索引生成等实际业务操作。所有数据、元数据和增量日志则分别依托对象存储、etcd 和消息队列来持久化,保障集群稳定运行和数据可靠存储。
(2)单机模式与分布式模式
Milvus 主要分为单机部署和集群部署两种运行模式,适配不同场景。
单机模式可以通过 Docker Compose 快速搭建,适合日常学习调试、项目开发测试以及小规模业务上线,本地默认通过 https://localhost:19530 连接访问。
集群模式依托 Kubernetes 实现多节点分布式部署,具备强大的扩容能力和并发处理能力,能承载亿级海量向量数据的存储,支撑高并发检索业务场景,客户端通过集群袋里地址建立连接。
如果是学习阶段,用 Milvus Lite 或 Docker Standalone 就行,API 跟集群版是一致的。
(3)核心概念关系
在 Milvus 中,Database 是顶层的逻辑容器,内部可以包含多个 Collection。
Collection 是存储数据的核心单元,既定义各个字段的结构,也可以创建检索索引,同时用来存储一条条实体数据记录。
Field 是构成实体的基本单元,可以针对性地建立索引来加速查询。Entity 对应实际存入的业务数据行,包含主键、文本字段和向量字段等核心内容。整体架构逻辑与传统关系型数据库的表结构高度相似,上手学习难度不大。
| 概念 | 类比关系型数据库 | 说明 |
|---|---|---|
| Database | Database | 逻辑命名空间隔离 |
| Collection | Table | 一组同 Schema 的实体 |
| Field | Column | 标量字段或向量字段 |
| Entity | Row | 一条记录 |
| Index | Index | 加速向量/标量检索 |
| Partition | Partition | Collection 内逻辑分片(可选) |
六.Collection与Schema设计
(1)简单Schema
稠密向量检索的话,代码可以这样写:
# 快速创建隐式 Schema
client.create_collection(
collection_name="demo_collection",
dimension=1024, # 向量维度
)
(2)完整Schema:多字段与混合检索
示例代码如下:
from pymilvus import MilvusClient, DataType, Function, FunctionType
schema = MilvusClient.create_schema()
schema.add_field("id", DataType.INT64, is_primary=True, auto_id=True)
schema.add_field("title", DataType.VARCHAR, max_length=512)
schema.add_field("content", DataType.VARCHAR, max_length=65535,
enable_analyzer=True, enable_match=True,
analyzer_params={"tokenizer": "standard", "filter": ["lowercase"]})
schema.add_field("sparse_vector", DataType.SPARSE_FLOAT_VECTOR)
schema.add_field("dense_vector", DataType.FLOAT_VECTOR, dim=1024)
# BM25 函数:content 到 sparse_vector,入库时自动计算
bm25_fn = Function(
name="bm25",
function_type=FunctionType.BM25,
input_field_names="content",
output_field_names="sparse_vector",
)
schema.add_function(bm25_fn)
Schema 字段设计参考如下:
| 字段名 | 数据类型 | 索引 | 用途 |
|---|---|---|---|
id | INT64, PK | — | 主键,支持精确查询 |
title | VARCHAR | — | 展示、标量过滤 |
content | VARCHAR + Analyzer | — | 正文、BM25 输入 |
metadata | JSON | — | 灵活扩展元信息 |
dense_vector | FLOAT_VECTOR[1024] | HNSW + IP | 语义检索 |
sparse_vector | SPARSE_FLOAT_VECTOR | SPARSE_INVERTED_INDEX | 关键词检索 |
七.索引类型与检索算法
向量全量暴力扫描(FLAT)在百万级数据量下是不可接受的,Milvus 通过 ANN 索引,牺牲少量精度来换取百倍甚至更高的加速效果。
(1)稠密向量检索对比表
| 索引类型 | 原理 | 构建速度 | 查询速度 | 内存占用 | 适用场景 |
|---|---|---|---|---|---|
| FLAT | 暴力扫描 | 无需构建 | 慢 | 低 | 小数据集、基准测试 |
| IVF_FLAT | 聚类 + 倒排 | 中 | 中 | 中 | 中等规模 |
| IVF_PQ | 聚类 + 乘积量化 | 中 | 快 | 低 | 大规模、内存敏感 |
| HNSW | 分层导航小世界图 | 慢 | 很快 | 高 | 生产首选 |
| DISKANN | 磁盘友好图索引 | 慢 | 快 | 低磁盘 | 超大规模 |
(2)稀疏向量索引
| 索引类型 | metric_type | 说明 |
|---|---|---|
| SPARSE_INVERTED_INDEX | BM25 | 倒排索引,经典全文检索 |
(3)创建索引示例
下面这段代码用于批量配置 Milvus 集合索引并完成集合创建,分别为稠密向量和稀疏向量配置专属索引规则。
针对 dense_vector 稠密向量字段,选用主流的 HNSW 索引,搭配内积 IP 作为相似度计算方式;针对 sparse_vector 稀疏向量字段,采用稀疏倒排索引,适配 BM25 关键词检索模式。最后结合预设的数据表结构和索引参数,完成向量集合的一键创建,适配语义 + 关键词双路检索场景:
index_params = MilvusClient.prepare_index_params()
# 稠密向量:HNSW 与内积
index_params.add_index(
field_name="dense_vector",
index_type="HNSW",
metric_type="IP",
params={"M": 16, "efConstruction": 200},
)
# 稀疏向量:倒排与 BM25
index_params.add_index(
field_name="sparse_vector",
index_type="SPARSE_INVERTED_INDEX",
metric_type="BM25",
)
client.create_collection(COLLECTION_NAME, schema=schema, index_params=index_params)
HNSW 索引有三个核心可调参数。其中 M 代表图结构每层节点的最大连接数量,数值越大检索精度越高,但内存占用也会上升,日常取值范围在 8 到 64 之间。efConstruction 是索引构建阶段的候选检索数量,调大这个参数能提升索引的整体质量,但也会拉长索引构建的耗时。ef 是检索阶段的候选集数量,提升它有助于提高内容召回率,但会增加查询响应延迟。具体怎么配,要看业务对速度和精度的要求。
八.检索流程:从查询到结果
(1)三种检索模式
精确查询通过指定主键 ID 直接定位数据,能快速精准地获取指定记录。
标量过滤依托文本字段完成关键词的模糊筛选,沿用的是传统数据库的检索逻辑,实现字面内容的匹配。
语义检索则传入向量化后的查询向量,借助 ANN 近似近邻算法完成相似度比对,突破了文字表层的限制,真正实现基于语义含义的智能检索。
(2)语义检索完整代码
首先导入向量化工具函数,将用户输入的自然语言查询语句转为对应的语义向量。然后调用 Milvus 的检索接口,指定目标向量集合、传入查询向量,设置返回结果的数量并声明需要展示的文本字段。接口执行 ANN 近似近邻检索后返回匹配结果,结果是一个双层列表结构,内层每条结果包含数据主键、相似度分值和完整的实体信息。最后遍历检索结果,格式化输出相似度数值和文档标题,直观展示语义匹配度最高的相关内容。
from embedding_utils import get_embedding
query = "什么是健康饮食与生活习惯"
query_vector = get_embedding(query)
hits = client.search(
collection_name="demo_collection",
data=[query_vector],
limit=5,
output_fields=["title", "content"],
)
# hits 结构: List[List[Hit]],每个 Hit 含 id, distance, entity
for hit in hits[0]:
print(f"距离={hit['distance']:.4f} | {hit['entity']['title']}")
(3)后处理:阈值过滤与 Top-K 截断
模型返回的 Top-K 结果未必都足够相关。工程上通常会加一个阈值来做筛选:
TOP_K = 3
THRESHOLD = 0.3 # IP/COSINE 越大越相似
final_results = []
for item in hits[0]:
if item["distance"] >= THRESHOLD and len(final_results) < TOP_K:
final_results.append(item)
else:
break # 假设结果按 distance 降序,低分直接终止
后处理策略及其作用如下:
| 后处理策略 | 作用 |
|---|---|
| 距离阈值 | 过滤低相关结果,减少噪声 |
| Top-K 截断 | 控制上下文长度(RAG 场景) |
| 重排序 Rerank | 用 Cross-Encoder 二次精排(可选) |
(4)检索链路时序
用户向业务应用发起自然语言提问,应用先调用嵌入服务接口,将文本内容转换为固定维度的查询向量。
随后带着向量数据向 Milvus 发起检索请求,数据库调用底层的 HNSW 索引执行近似最近邻遍历搜索,快速筛选出相似度靠前的候选数据并计算匹配分值。
数据库把携带业务字段的检索结果回传给应用端,应用再通过相似度阈值筛选、结果重排序等方式完成二次优化,剔除低质量和无关的内容,最后整理规整后,把精准的最终结果反馈给用户。
整体时序如下图所示:
九.混合检索:稠密与稀疏的结合
单一稠密检索可能会漏掉精确的关键词;单一 BM25 又无法理解同义表达。混合检索(Hybrid Search)就是为了融合两者的优势。
(1)混合架构与代码示意
混合检索会同时开启两条检索链路。用户查询文本一方面通过嵌入模型生成稠密向量,依靠 HNSW 索引完成语义层面的检索;另一方面借助 BM25 算法生成稀疏向量,利用倒排索引实现关键词的精准匹配。
两路检索结果汇总后,通过 RRF(倒数排名融合)或加权打分的方式统一排序,综合语义相似度和关键词匹配度,筛选出相关性最优的 Top-K 结果输出。这种方式兼顾了语义理解能力和精准词条匹配能力,示意图如下。
下面是混合检索的示例代码:
from pymilvus import AnnSearchRequest, RRFRanker
query = "什么是健康饮食与生活习惯?"
sparse_request = AnnSearchRequest(
data=[query],
anns_field="sparse_vector",
param={"metric_type": "BM25"},
limit=5,
)
dense_request = AnnSearchRequest(
data=[get_embedding(query)],
anns_field="dense_vector",
param={"metric_type": "IP"},
limit=5,
)
results = client.hybrid_search(
collection_name="demo_hybrid_collection",
reqs=[sparse_request, dense_request],
ranker=RRFRanker(), # Reciprocal Rank Fusion
limit=5,
output_fields=["title", "content"],
)
(2)融合策略对比
常用的融合策略和优劣分析如下表所示:
| 融合算法 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| RRF | 按排名倒数加权求和 | 无需调参、鲁棒 | 无法强调某一路 |
| Weighted | 加权合并两路分数 | 可自定义权重 | 需调参 |
| Rerank | Cross-Encoder 重排 | 精度最高 | 延迟大、成本高 |
十.总结
这篇文章梳理了向量嵌入技术与 Milvus 向量数据库的完整知识体系。从传统检索方式存在的语义匹配短板出发,逐层讲解了文本向量化的底层流程、向量分类差异和空间分布逻辑,清晰阐明了各类相似度计算方式的适用场景与使用规范。
同时结合实际开发场景,落地讲解了文本批量向量化、数据入库装配等工程化实操方案,打通了从原始文本到标准向量数据的全流程处理链路。
此外,还剖析了 Milvus 的分层架构、部署模式和核心数据概念,详细介绍了集合结构设计、主流向量索引选型规则和参数调优思路。最后完整演示了单一路径语义检索和稠密稀疏混合检索的实现方式,并对比了多种结果融合策略的优劣。
整体内容兼顾理论原理和项目实战,既理清了向量语义检索的核心逻辑,也提供了可直接复用的代码方案和落地规范,能够为 RAG 知识库搭建、智能语义检索系统开发提供一份完整的技术参考和实践指导。
