向量搜索系统的三个核心优化维度:速度、精度与规模

简介: 本文探讨大规模向量搜索的三大核心优化:性能调优(ANN、IVF、HNSW、量化压缩、GPU加速)、混合搜索(关键词+语义加权融合)与可扩展架构(分片、分布式、缓存),解决亿级向量下“又快又准”的工程难题。

当数据集膨胀到数百万甚至数十亿量级的向量时,怎么让搜索在这种规模下依然又快又准就成了一个实实在在的工程难题。这篇文章要聊的就是向量搜索系统的三个核心优化方向——性能调优、混合搜索和可扩展架构。

传统搜索的问题

传统搜索系统做的事情本质上是词法匹配:找文档里有没有出现查询中的关键词。至于查询背后的意思?它不管。同义词、上下文、用户意图、概念层面的相似性,统统不在考虑范围内。

比如说用户查询

cheap car

,文档里写的是

affordable vehicle

。意思完全一样,但传统搜索直接判定不匹配——因为没有一个词是相同的。

Elasticsearch 这类系统引入了 BM25 排序算法,根据词频和重要性给文档打分。但再怎么优化打分策略,底层逻辑还是关键词重叠。换个说法表达同一个意思,排名可能就掉下去了。传统词法搜索在同义词、改述、深层语义理解面前,始终力不从心。

用一段 Python 代码直观感受一下传统关键词匹配的局限:

 documents = [
    "Affordable vehicle for students",
    "Best gaming laptop",
    "Budget friendly smartphone"
]

def traditional_search(query, docs):
    results = []
    query_words = query.lower().split()

    for doc in docs:
        doc_lower = doc.lower()
        if all(word in doc_lower for word in query_words):
            results.append(doc)

    return results

# Query
query = "cheap car"

 print(traditional_search(query, documents))

输出:

 []

空的。"Affordable vehicle" 和 "cheap car" 语义上几乎等价,但关键词匹配毫无办法。要解决这个问题就得换一种思路——向量搜索。

什么是向量搜索

不再比较字面上的词而是比较含义。把文档和查询都转换成数值向量(Embedding),这些向量编码了语义信息。即使两段文本用词完全不同,只要意思相近,它们的向量在空间中就会彼此靠近。

整个流程拆开来看分四步。

第一步是文档处理,属于离线的索引阶段。所有文档经过一个 Embedding 模型,转换为向量表示,然后存入向量索引或向量数据库。

第二步是查询处理,在线阶段。用户提交查询后,同一个 Embedding 模型把查询也转成向量,发送给向量索引。

第三步是相似性搜索。在向量索引内部,系统拿查询向量和所有文档向量做比较,用余弦相似度之类的距离度量找出最接近的那些向量。

第四步是返回结果。距离最近的文档就是语义上最相关的结果,哪怕它们和查询没有任何共同关键词。

下面动手搭一个小型语义搜索引擎,看看向量搜索在实践中是什么样的。

步骤1:将文本转换为 Embedding

用 Sentence Transformers 的

all-MiniLM-L6-v2

模型:

 from sentence_transformers import SentenceTransformer

documents = [
    "Affordable vehicle for students",
    "Best gaming laptop",
    "Budget friendly smartphone"
]

model = SentenceTransformer("all-MiniLM-L6-v2")

doc_embeddings = model.encode(documents)

 print(doc_embeddings.shape)

输出:

 (3, 384)

每个句子变成了一个 384 维的向量。

步骤2:引入相似性概念

 similarities = model.similarity(doc_embeddings, doc_embeddings)
 print(similarities)

输出:

 tensor([[1.0000, 0.1679, 0.3233],
         [0.1679, 1.0000, 0.2726],
         [0.3233, 0.2726, 1.0000]])

这是一个成对余弦相似度矩阵。单元格 (i, j) 表示句子 i 和句子 j 的语义相似程度。对角线上都是

1.0000

,因为每个句子和自身当然完全相似。可以看到 "Affordable vehicle for students" 和 "Budget friendly smartphone" 之间的相似度(0.3233)比它和 "Best gaming laptop"(0.1679)更高,这符合直觉——前两者都带有"经济实惠"的语义。

步骤3:创建 FAISS 索引

 import faiss
import numpy as np

doc_embeddings = np.array(doc_embeddings).astype("float32")

dimension = doc_embeddings.shape[1]
index = faiss.IndexFlatL2(dimension)
 index.add(doc_embeddings)

FAISS 索引在高维空间里做 K 近邻搜索,用 L2 距离作为度量,能快速找到和查询向量最接近的 top-k 个 Embedding。

步骤4:搜索查询

文档 Embedding 已经入库,现在可以做语义搜索了。

 query = "cheap car"
 query_embedding = model.encode([query]).astype("float32")

 distances, indices = index.search(query_embedding, k=1)

 print(documents[indices[0][0]])

输出:

 Affordable vehicle for students

查询被编码为 384 维向量,FAISS 计算它和每个文档向量的 L2 距离,返回距离最小的那个。"cheap car" 成功匹配到了 "Affordable vehicle for students",这里关键词没有任何重叠,但语义搜索抓住了含义。

这个小演示只处理三条文档,跑起来毫无压力。但现实场景动辄百万、十亿级别的 Embedding,

IndexFlatL2

这种暴力全量比较的方式就扛不住了。搜索延迟飙升,内存吃紧,扩展性成问题。接下来要聊的就是怎么优化。

性能调优

IndexFlatL2

对每个查询都遍历全部向量做比较。数据量小的时候没问题,数据量一上去就是灾难。性能调优围绕三件事展开:压缩搜索时间、降低内存消耗、同时尽量不牺牲检索精度。

精确搜索 vs 近似最近邻(ANN)

前面演示用的是:

 faiss.IndexFlatL2(dimension)

这是精确最近邻搜索,查询和每一个向量都比一遍。精度有保证,但面对百万级 Embedding 就跑不动了。

替代方案是近似最近邻(ANN)。ANN 放弃一点点精度换来数量级的速度提升,FAISS 里常用的 ANN 索引比如 IndexIVFFlat 和 IndexHNSWFlat,都是通过减少实际参与比较的向量数量来加速检索。

倒排文件索引(IVF)

IVF 的思路是把向量先聚类,查询时只在最相关的几个聚类里搜索,而不是扫描全部数据。对大数据集来说,延迟直接降一个量级。

 quantizer=faiss.IndexFlatL2(dimension)
 index=faiss.IndexIVFFlat(quantizer, dimension, nlist=100)
nlist

是聚类数量。聚类越多,精度越高,但计算开销也越大。这里面有个平衡点需要根据实际场景调。

HNSW(基于图的搜索)

HNSW(Hierarchical Navigable Small World)是另外一个方向:不做聚类而是构建一个图结构。每个向量和它最近的邻居相连,搜索时沿着图的边进行导航。搜索速度快,召回率高,可扩展性也不错,是现代向量数据库中非常主流的索引方式。

向量压缩(Product Quantization)

数据量大了之后光是把向量存在内存里就是个问题,Product Quantization 对向量做有损压缩,减小内存占用的同时还能加速搜索。百万、十亿级 Embedding 的场景下,这个技术基本是标配。

硬件加速(GPU 支持)

FAISS 支持 GPU 加速,把向量计算并行化之后延迟大幅降低,适合对实时性要求高的场景,比如推荐系统和大型搜索平台。

混合搜索

实际的搜索系统很少只用关键词匹配或者只用向量相似性,更常见的做法是两者结合。

用户查询进来后,关键词搜索和向量搜索并行执行。关键词搜索产出一个词法相关性分数(比如 BM25),向量搜索产出一个语义相似度分数(比如余弦相似度),两路分数再融合成一个统一的排名分数。

评分公式(分数融合)

常见的加权融合公式长这样:

 FinalScore(d)=α⋅KeywordScore(d)+(1−α)⋅VectorScore(d)

 # 其中,
 d = document
 𝛼 ∈ [0,1] 控制关键词精确度的重要性

这个分数不代表"正确性",只是用来给文档排序。系统按

FinalScore

从高到低排列,排在前面的就是最终结果。

混合搜索伪代码

 # Step 1: Keyword Search
keyword_results = bm25.search(query)

# Step 2: Vector Search
query_embedding = embed(query)
vector_results = vector_db.search(query_embedding)

# Step 3: Merge + Weighted Ranking
def merge_and_rank(keyword_results, vector_results, alpha=0.6):
    keyword_dict = {doc.id: doc.score for doc in keyword_results}
    vector_dict = {doc.id: doc.score for doc in vector_results}

    all_doc_ids = set(keyword_dict.keys()) | set(vector_dict.keys())

    # Normalize scores (important in real systems)
    def normalize(scores):
        if not scores:
            return {}
        min_s = min(scores.values())
        max_s = max(scores.values())
        return {
            doc: (score - min_s) / (max_s - min_s + 1e-9)
            for doc, score in scores.items()
        }

    keyword_dict = normalize(keyword_dict)
    vector_dict = normalize(vector_dict)

    final_scores = {}
    for doc_id in all_doc_ids:
        kw_score = keyword_dict.get(doc_id, 0)
        vec_score = vector_dict.get(doc_id, 0)

        final_scores[doc_id] = (
            alpha * kw_score +
            (1 - alpha) * vec_score
        )

    ranked_docs = sorted(
        final_scores.items(),
        key=lambda x: x[1]S,
        reverse=True
    )

    return ranked_docs

final_results = merge_and_rank(
    keyword_results,
    vector_results,
    alpha=0.6
)

 return final_results

这里有个细节值得注意:归一化。BM25 的分数是无界的,余弦相似度则在 -1 到 1 之间。不做归一化直接加权,某一路的分数可能把另一路完全压过去,排名就失真了。所以实际系统在融合之前一定要先归一化。

扩展向量搜索

小数据集上向量搜索跑得很漂亮。但从几千个向量涨到几百万、几十亿的时候,单机就扛不住了。生产环境下的扩展策略主要三个:Sharding、分布式搜索、缓存。

Sharding(水平扩展)

Sharding 就是把向量分散存储到多台机器上。比如 3000 万个向量,机器 1 存前 1000 万,机器 2 存中间 1000 万,机器 3 存最后 1000 万。

查询来了之后,系统先生成查询 Embedding,然后把它发给所有分片。每个分片各自返回 top-K 结果,最后汇总在一起重新排名。想加容量?加机器就行,这就是水平扩展的好处。

分布式搜索(并行查询处理)

一台服务器扫 1 亿个向量太慢,那就拆成 10 台,每台扫 1000 万,并行跑。工作量分摊了,搜索同步进行,结果最终在协调器节点汇合。这是分布式搜索引擎和现代向量数据库的标准架构。

缓存(速度优化)

逻辑很简单:

If we have already answered this question before, don't compute again.

没有缓存的情况下,每次查询都要走完整流程——生成 Embedding、发请求到所有分片、算相似度、合并结果、返回 top-K。哪怕5秒前刚有人搜过一模一样的东西,全套流程照跑一遍。这就是纯粹的浪费。

有了缓存就不一样了。热门查询再来的时候系统先查缓存:

Have we seen this query before?

命中的话直接返回存好的结果,Embedding 生成和分布式搜索全部跳过。

想想 "iPhone 15 price" 或 "Weather today" 这种查询,每天成千上万人在搜。算一次就够了,后面全部复用。缓存同时砍掉了延迟和基础设施成本。

总结

向量搜索把信息检索从字面匹配带进了语义理解的时代。但光有 Embedding 还不够,真正让系统在生产环境中跑起来的是背后的工程优化——混合搜索把词法和语义两条路打通,ANN 和压缩技术解决性能瓶颈,分布式架构和缓存撑起大规模部署。

本文代码:

https://avoid.overfit.cn/post/f8461443473745e6bd7ea21a5b43f44c

by Pawan

目录
相关文章
|
2月前
|
数据采集 人工智能 监控
3分钟阿里云部署OpenClaw:解锁199+OpenClawSkills指南及ClawHub生态详解
2026年,开源Agent框架OpenClaw(原Clawdbot)凭借“自然语言驱动+模块化技能扩展”的核心优势,成为AI工具领域的现象级产品。其真正的强大之处,在于ClawHub技能市场中9000+款多样化Skills(技能插件)——这些“超级外挂”让OpenClaw从基础对话工具,升级为能执行文件管理、办公自动化、数据抓取、代码开发等复杂任务的全能助手。
1770 2
|
2月前
|
人工智能 测试技术
LLM创造力可以被度量吗?一个基于提示词变更的探索性实验
本文探讨提示词工程为何仍是“玄学”,并通过实验证明:加入明确指令(如“Be as creative as possible”)可显著、可量化地提升LLM输出多样性,效果甚至超过调高温度。研究以embedding距离为代理指标,覆盖13个主流模型,揭示提示词迭代可度量、可预测,为LLM应用从经验走向工程化提供新路径。
135 17
LLM创造力可以被度量吗?一个基于提示词变更的探索性实验
|
5月前
|
机器学习/深度学习 人工智能 缓存
让AI评测AI:构建智能客服的自动化运营Agent体系
大模型推动客服智能化演进,从规则引擎到RAG,再到AI原生智能体。通过构建“评估-诊断-优化”闭环的运营Agent,实现对话效果自动化评测与持续优化,显著提升服务质量和效率。
2512 86
让AI评测AI:构建智能客服的自动化运营Agent体系
|
前端开发
AJAX请求 状态pending
AJAX请求 状态pending
1038 0
AJAX请求 状态pending
|
2月前
|
人工智能 弹性计算 安全
2026年阿里云OpenClaw一键快速部署教程,轻松搭建专属AI助理!
2026年,打造专属AI数字员工超简单:仅需一台阿里云服务器,几分钟用OpenClaw一键部署,接入百炼大模型,即可实现文档编写、资料检索、脚本运行、报表整理等智能办公能力。本地优先、安全可控、7×24在线。
513 5
|
2月前
|
SQL 供应链 监控
Quick BI使用案例12:如何实现分组内“最新”与“次新”订单时间计算
本文详解订单时效性分析:通过LOD_FIXED与BI_MAX函数,快速计算各区域“最新/次新订单时间”,助力识别交易活跃度、预警客户流失、优化供应链。
|
2月前
|
人工智能 自然语言处理 JavaScript
Deepseek百万 Token 窗口的极限实践:一位非专业人员使用实录
摘要:此文非技术评测,而是一份关于Deepseek最新百万token窗口的真实工程“长程思考”实录。本人非AI与计算机专业,从事生物医学与心理学工作,人文爱好者。利用十天时间,通过浏览器deepseek云端模型百万token对话窗口,实现了一套从本地环境设置、工具流搭建、数据建库与向量化的整个工程。本文记录了主要的过程与指标。 时间:2026 年 2 月
|
2月前
|
存储 自然语言处理 安全
MD5在线加密工具分享
一款基于Nuxt 3开发的MD5在线校验工具,支持文本(含Base64/Hex等格式)与文件(拖拽上传、本地分片计算)快速生成MD5摘要,输出可选Hex/Base64等格式并一键转大写。全程浏览器端处理,隐私安全,专为文件完整性校验设计。
682 2
|
2月前
|
机器学习/深度学习 存储 人工智能
基于能量的模型(EBM):用能量函数替代概率分布的建模框架
Yann LeCun指出,当前LLM逐Token预测的路径难达人类级AI;其团队力推基于能量的模型(EBM)——不依赖Softmax归一化,以“能量”度量兼容性,支持多峰建模、连续优化与世界模型构建,为通用智能提供新范式。
135 2
基于能量的模型(EBM):用能量函数替代概率分布的建模框架
|
2月前
|
人工智能 前端开发 API
手把手搭建 Adaptive RAG 系统:从向量检索到 Streamlit 前端全流程
本文带你从零构建端到端AI技术支持助手POC:基于Adaptive RAG动态调整检索深度,LangGraph编排有状态多步推理,FastAPI提供高性能API,Streamlit实现交互式前端——理论落地,开箱即用。
175 4
手把手搭建 Adaptive RAG 系统:从向量检索到 Streamlit 前端全流程