檢索優化-混合檢索

混合檢索(Hybrid Search)是一種結合了 稀疏向量(Sparse Vectors) 和 密集向量(Dense Vectors) 優勢的先進搜索技術。旨在同時利用稀疏向量的關鍵詞精確匹配能力和密集向量的語義理解能力,以克服單一向量檢索的局限性,從而在各種搜索場景下提供更準確、更魯棒的檢索結果。

在本節中,我們將首先分析這兩種核心向量的特性,然后探討它們如何融合,最后通過milvus實現混合檢索。

一、稀疏向量 vs 密集向量

為了更好地理解混合檢索,首先需要厘清兩種向量的本質區別。

1.1 稀疏向量

稀疏向量,也常被稱為“詞法向量”,是基于詞頻統計的傳統信息檢索方法的數學表示。它通常是一個維度極高(與詞匯表大小相當)但絕大多數元素為零的向量。它采用精準的“詞袋”匹配模型,將文檔視為一堆詞的集合,不考慮其順序和語法,其中向量的每一個維度都直接對應一個具體的詞,非零值則代表該詞在文檔中的重要性(權重)。這類向量的典型代表是 TF-IDF 和 BM25,其中,BM25 是目前最成功、應用最廣泛的稀疏向量計分算法之一,其核心公式如下:
在這里插入圖片描述
在這里插入圖片描述
這種方法的優點是可解釋性極強(每個維度都代表一個確切的詞),無需訓練,能夠實現關鍵詞的精確匹配,對于專業術語和特定名詞的檢索效果好。然而,其主要缺點是無法理解語義,例如它無法識別“汽車”和“轎車”是同義詞,存在“詞匯鴻溝”。

1.2 密集向量

密集向量,也常被稱為“語義向量”,是通過深度學習模型學習到的數據(如文本、圖像)的低維、稠密的浮點數表示。這些向量旨在將原始數據映射到一個連續的、充滿意義的“語義空間”中來捕捉“語義”或“概念”。在理想的語義空間中,向量之間的距離和方向代表了它們所表示概念之間的關系。一個經典的例子是 vector(‘國王’) - vector(‘男人’) + vector(‘女人’) 的計算結果在向量空間中非常接近 vector(‘女王’),這表明模型學會了“性別”和“皇室”這兩個維度的抽象概念。它的典型代表包括 Word2Vec、GloVe、以及所有基于 Transformer 的模型(如 BERT、GPT)生成的嵌入(Embeddings)。

其主要優點是能夠理解同義詞、近義詞和上下文關系,泛化能力強,在語義搜索任務中表現卓越。但其缺點也同樣明顯:可解釋性差(向量中的每個維度通常沒有具體的物理意義),需要大量數據和算力進行模型訓練,且對于未登錄詞(OOV)1的處理相對困難。

1.3 實例對比

稀疏向量表示:

稀疏向量的核心思想是只存儲非零值。例如,一個8維的向量 [0, 0, 0, 5, 0, 0, 0, 9],其大部分元素都是零。用稀疏格式表示,可以極大地節約空間。常見的稀疏表示法有兩種:

字典 / 鍵值對 (Dictionary / Key-Value): 這種方式將非零元素的 索引 (0-based) 作為鍵,值 作為值。上面的向量可以表示為:
在這里插入圖片描述
坐標列表 (Coordinate list - COO): 這種方式通常用一個元組 (維度, [索引列表], [值列表]) 來表示。上面的向量可以表示為:
在這里插入圖片描述
這種格式在 SciPy 等科學計算庫中非常常見。

假設在一個包含5萬個詞的詞匯表中,“西紅柿”在第88位,“炒”在第666位,“蛋”在第999位,它們的BM25權重分別是1.2、0.8、1.5。那么它的稀疏表示(采用字典格式)就是:

在這里插入圖片描述
如果采用坐標列表(COO)格式,它會是這樣:
在這里插入圖片描述
這兩種格式都清晰地記錄了文檔的關鍵信息,但它們的局限性也很明顯:如果我們搜索“番茄炒雞蛋”,由于“番茄”和“西紅柿”是不同的詞條(索引不同),模型將無法理解它們的語義相似性。

密集向量表示:

與稀疏向量不同,密集向量的所有維度都有值,因此使用數組 [] 來表示是最直接的方式。一個預訓練好的語義模型在讀取“西紅柿炒蛋”后,會輸出一個低維的密集向量:
在這里插入圖片描述
這個向量本身難以解讀,但它在語義空間中的位置可能與“番茄雞蛋面”、“洋蔥炒雞蛋”等菜肴的向量非常接近,因為模型理解了它們共享“雞蛋類菜肴”、“家常菜”、“酸甜口味”等核心概念。因此,當我們搜索“蛋白質豐富的家常菜”時,即使查詢中沒有出現任何原文關鍵詞,密集向量也很有可能成功匹配到這份菜譜。

二、混合檢索

通過上文可以看出稀疏向量和密集向量各有千秋,那么將它們結合起來,實現優勢互補,就成了一個不錯的選擇。混合檢索便是基于這個思路,通過結合多種搜索算法(最常見的是稀疏與密集檢索)來提升搜索結果相關性和召回率。

主要目標:解決單一檢索技術的局限性。例如,關鍵詞檢索無法理解語義,而向量檢索則可能忽略掉必須精確匹配的關鍵詞(如產品型號、函數名等)。混合檢索旨在同時利用稀疏向量的精確性和密集向量的泛化性,以應對復雜多變的搜索需求。

2.1 技術原理與融合方法

混合檢索通常并行執行兩種檢索算法,然后將兩組異構的結果集融合成一個統一的排序列表。以下是兩種主流的融合策略:

2.1.1 倒數排序融合 (Reciprocal Rank Fusion, RRF)

RRF 不關心不同檢索系統的原始得分,只關心每個文檔在各自結果集中的排名。其思想是:一個文檔在不同檢索系統中的排名越靠前,它的最終得分就越高。

其計分公式為:
在這里插入圖片描述
在這里插入圖片描述

2.1.2 加權線性組合

這種方法需要先將不同檢索系統的得分進行歸一化(例如,統一到 0-1 區間),然后通過一個權重參數 α 來進行線性組合。
在這里插入圖片描述
通過調整 α 的值,可以靈活地控制語義相似性與關鍵詞匹配在最終排序中的貢獻比例。例如,在電商搜索中,可以調高關鍵詞的權重;而在智能問答中,則可以側重于語義。

2.2 優勢與局限

在這里插入圖片描述

三、代碼實踐:通過 Milvus 實現混合檢索

實踐是檢驗真理的唯一標準?。接下來使用 Milvus 來實現一個完整的混合檢索流程,從定義 Schema、插入數據,到執行查詢。

3.1 步驟一:定義 Collection

在上一章中我們實現了多模態圖文檢索,現在還是同樣的步驟先創建一個 Collection。

import json
import os
os.environ["HF_ENDPOINT"] = "https://hf-mirror.com"
import numpy as np
from pymilvus import connections, MilvusClient, FieldSchema, CollectionSchema, DataType, Collection, AnnSearchRequest, RRFRanker
from pymilvus.model.hybrid import BGEM3EmbeddingFunction# 1. 初始化設置
COLLECTION_NAME = "dragon_hybrid_demo"
MILVUS_URI = "http://localhost:19530"  # 服務器模式
DATA_PATH = "../../data/C4/metadata/dragon.json"  # 相對路徑
BATCH_SIZE = 50# 2. 連接 Milvus 并初始化嵌入模型
print(f"--> 正在連接到 Milvus: {MILVUS_URI}")
connections.connect(uri=MILVUS_URI)print("--> 正在初始化 BGE-M3 嵌入模型...")
ef = BGEM3EmbeddingFunction(use_fp16=False, device="cpu")
print(f"--> 嵌入模型初始化完成。密集向量維度: {ef.dim['dense']}")# 3. 創建 Collection
milvus_client = MilvusClient(uri=MILVUS_URI)
if milvus_client.has_collection(COLLECTION_NAME):print(f"--> 正在刪除已存在的 Collection '{COLLECTION_NAME}'...")milvus_client.drop_collection(COLLECTION_NAME)fields = [FieldSchema(name="pk", dtype=DataType.VARCHAR, is_primary=True, auto_id=True, max_length=100),FieldSchema(name="img_id", dtype=DataType.VARCHAR, max_length=100),FieldSchema(name="path", dtype=DataType.VARCHAR, max_length=256),FieldSchema(name="title", dtype=DataType.VARCHAR, max_length=256),FieldSchema(name="description", dtype=DataType.VARCHAR, max_length=4096),FieldSchema(name="category", dtype=DataType.VARCHAR, max_length=64),FieldSchema(name="location", dtype=DataType.VARCHAR, max_length=128),FieldSchema(name="environment", dtype=DataType.VARCHAR, max_length=64),FieldSchema(name="sparse_vector", dtype=DataType.SPARSE_FLOAT_VECTOR),FieldSchema(name="dense_vector", dtype=DataType.FLOAT_VECTOR, dim=ef.dim["dense"])
]# 如果集合不存在,則創建它及索引
if not milvus_client.has_collection(COLLECTION_NAME):print(f"--> 正在創建 Collection '{COLLECTION_NAME}'...")schema = CollectionSchema(fields, description="關于龍的混合檢索示例")# 創建集合collection = Collection(name=COLLECTION_NAME, schema=schema, consistency_level="Strong")print("--> Collection 創建成功。")# 創建索引print("--> 正在為新集合創建索引...")sparse_index = {"index_type": "SPARSE_INVERTED_INDEX", "metric_type": "IP"}collection.create_index("sparse_vector", sparse_index)print("稀疏向量索引創建成功。")dense_index = {"index_type": "AUTOINDEX", "metric_type": "IP"}collection.create_index("dense_vector", dense_index)print("密集向量索引創建成功。")collection = Collection(COLLECTION_NAME)
collection.load()
print(f"--> Collection '{COLLECTION_NAME}' 已加載到內存。")

在這里插入圖片描述
在這里插入圖片描述
fields字段類型分析:

  • pk: 主鍵設計,auto_id=True 讓 Milvus 自動生成唯一標識,避免主鍵沖突
  • 標量字段: 7個VARCHAR字段用于存儲元數據,max_length 根據實際數據分布優化存儲
  • 稀疏向量: SPARSE_FLOAT_VECTOR 類型,存儲關鍵詞權重
  • 密集向量: FLOAT_VECTOR 類型,固定1024維,存儲語義特征

3.2 步驟二:BGE-M3 雙向量生成

這里使用 BGE-M3 作為向量生成器,它能夠同時生成稀疏向量和密集向量。

3.2.1 數據加載與預處理
if collection.is_empty:print(f"--> Collection 為空,開始插入數據...")with open(DATA_PATH, 'r', encoding='utf-8') as f:dataset = json.load(f)docs, metadata = [], []for item in dataset:parts = [item.get('title', ''),item.get('description', ''),item.get('location', ''),item.get('environment', ''),]docs.append(' '.join(filter(None, parts)))metadata.append(item)

Collection 此時已加載到內存但為空狀態。通過 is_empty 檢查避免重復插入。多字段文本合并中每個實體對應一個完整的數據記錄。

3.2.2 向量生成
print("--> 正在生成向量嵌入...")
embeddings = ef(docs)
print("--> 向量生成完成。")# 獲取兩種向量
sparse_vectors = embeddings["sparse"]    # 稀疏向量:詞頻統計
dense_vectors = embeddings["dense"]      # 密集向量:語義編碼

在這里插入圖片描述

3.2.2 向量生成
print("--> 正在生成向量嵌入...")
embeddings = ef(docs)
print("--> 向量生成完成。")# 獲取兩種向量
sparse_vectors = embeddings["sparse"]    # 稀疏向量:詞頻統計
dense_vectors = embeddings["dense"]      # 密集向量:語義編碼
3.2.3 Collection 批量數據插入為每個字段準備批量數據
# 為每個字段準備批量數據
img_ids = [doc["img_id"] for doc in metadata]
paths = [doc["path"] for doc in metadata]
titles = [doc["title"] for doc in metadata]
descriptions = [doc["description"] for doc in metadata]
categories = [doc["category"] for doc in metadata]
locations = [doc["location"] for doc in metadata]
environments = [doc["environment"] for doc in metadata]# 插入數據
collection.insert([img_ids, paths, titles, descriptions, categories, locations, environments,sparse_vectors, dense_vectors
])
collection.flush()

字段映射: 嚴格按照 Schema 定義的字段順序插入,9個字段(7個標量+2個向量)
flush() 作用: 強制將內存緩沖區數據寫入磁盤,使數據立即可搜索
最終狀態: Collection 包含6個Entity,索引層使用稀疏向量的 SPARSE_INVERTED_INDEX 和密集向量的 AUTOINDEX

3.3 步驟三:實現混合檢索

最后使用 milvus 中封裝好的 RRF 排序算法來完成混合檢索:

3.3.1 查詢向量生成
# 6. 執行搜索
search_query = "懸崖上的巨龍"
search_filter = 'category in ["western_dragon", "chinese_dragon", "movie_character"]'
top_k = 5print(f"\n{'='*20} 開始混合搜索 {'='*20}"
print(f"查詢: '{search_query}'")
print(f"過濾器: '{search_filter}'")# 生成查詢向量
query_embeddings = ef([search_query])
dense_vec = query_embeddings["dense"][0]
sparse_vec = query_embeddings["sparse"]._getrow(0)
3.3.2 混合檢索執行

使用 RRF 算法進行混合檢索,通過 milvus 封裝的 RRFRanker 實現。RRFRanker的核心參數是 k 值(默認60),用于控制 RRF 算法中的排序平滑程度。

其中 k 值越大,排序結果越平滑;越小則高排名結果的權重越突出

# 定義搜索參數
search_params = {"metric_type": "IP", "params": {}}# 先執行單獨的搜索
print("\n--- [單獨] 密集向量搜索結果 ---")
dense_results = collection.search([dense_vec],anns_field="dense_vector",param=search_params,limit=top_k,expr=search_filter,output_fields=["title", "path", "description", "category", "location", "environment"]
)[0]for i, hit in enumerate(dense_results):print(f"{i+1}. {hit.entity.get('title')} (Score: {hit.distance:.4f})")print(f"    路徑: {hit.entity.get('path')}")print(f"    描述: {hit.entity.get('description')[:100]}...")

在這里插入圖片描述

print("\n--- [單獨] 稀疏向量搜索結果 ---")
sparse_results = collection.search([sparse_vec],anns_field="sparse_vector",param=search_params,limit=top_k,expr=search_filter,output_fields=["title", "path", "description", "category", "location", "environment"]
)[0]for i, hit in enumerate(sparse_results):print(f"{i+1}. {hit.entity.get('title')} (Score: {hit.distance:.4f})")print(f"    路徑: {hit.entity.get('path')}")print(f"    描述: {hit.entity.get('description')[:100]}...")

在這里插入圖片描述

print("\n--- [混合] 稀疏+密集向量搜索結果 ---")
# 創建 RRF 融合器
rerank = RRFRanker(k=60)# 創建搜索請求
dense_req = AnnSearchRequest([dense_vec], "dense_vector", search_params, limit=top_k)
sparse_req = AnnSearchRequest([sparse_vec], "sparse_vector", search_params, limit=top_k)# 執行混合搜索
results = collection.hybrid_search([sparse_req, dense_req],rerank=rerank,limit=top_k,output_fields=["title", "path", "description", "category", "location", "environment"]
)[0]# 打印最終結果
for i, hit in enumerate(results):print(f"{i+1}. {hit.entity.get('title')} (Score: {hit.distance:.4f})")print(f"    路徑: {hit.entity.get('path')}")print(f"    描述: {hit.entity.get('description')[:100]}...")

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/95115.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/95115.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/95115.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Day17(前端:JavaScript基礎階段)

接續上文:Day16(前端:JavaScript基礎階段)_前端題目 csdn-CSDN博客 點關注不迷路喲。你的點贊、收藏,一鍵三連,是我持續更新的動力喲!!! 主頁:一位搞嵌入式的 genius-CSDN博客 系列文章專欄: https://blog.csdn.ne…

OpenCV 輪廓分析實戰:從檢測到形狀匹配的完整指南

輪廓(Contour)是圖像中連續且具有相同灰度值的像素集合,是描述目標形狀、位置和結構的核心特征。在計算機視覺中,輪廓分析廣泛應用于目標定位、形狀識別、尺寸測量等場景(如工業零件檢測、手寫數字識別)。本…

2025最新uni-app橫屏適配方案:微信小程序全平臺兼容實戰

以下為uni-app實現微信小程序橫屏適配技術方案,包含核心原理、配置方法、代碼示例和注意事項:一、橫屏適配原理 微信小程序默認采用豎屏模式,橫屏適配需通過以下機制實現: 全局配置:在app.json中聲明支持橫屏頁面級配置…

深入解析Nginx常見模塊1

在Web服務器和反向代理服務器領域,Nginx憑借其高性能、穩定性和豐富的功能獲得了廣泛的應用。本文將介紹一些Nginx中常見的模塊,幫助你更好地理解和使用它們。 Nginx模塊簡介 Nginx的模塊系統是其強大功能的核心所在,它允許用戶根據需要靈活配置服務器的行為。Nginx的模塊大…

淺談new與::operator new

目錄 前言 1.為什么C要引入new/delete? 2.operator new與operator delete函數 它們的實際作用 Placement New(定位new表達式) 總結 前言 在寫上一篇博客“vector的模擬實現”時,我一直很好奇vector的private成員為什么要用三個封…

Java中Integer轉String

在 Java 中,將 Integer 轉換為 String 有多種方法,以下是常見的幾種方式:1. 使用 Integer.toString() 方法javaInteger num 123; String str Integer.toString(num); // 直接調用 Integer 的靜態方法2. 使用 String.valueOf()javaInteger n…

智能裝備如何與軟件結合?

一、什么是智能裝備? 智能裝備是具備“感知-決策-執行-自適應”閉環能力的智能化系統,本質是“傳統物理裝備”與“數字智能”的深度融合。它不僅能完成預設動作(如傳統機械臂焊接),還能通過傳感器“觀察”環境、用算法…

react性能優化有哪些

React 性能優化的手段比較多,既有代碼層面的,也有構建層面的,還涉及到運行時調優。我幫你系統性梳理一份:🔹 一、渲染性能優化1. 減少不必要的渲染React.memo:對函數組件做淺比較,避免相同 prop…

騰訊云OpenCloudOS 9系統部署OpenTenBase數據庫詳細教程

OpenTenBase簡介OpenTenBase是一個關系型數據庫集群平臺,提供寫入可靠性和多節點數據同步功能。可以在一臺或多臺主機上配置OpenTenBase,并將數據存儲在多個物理主機上。OpenTenBase架構組件:Coordinator Node (CN):應用程序訪問入…

【計算機視覺】Pixel逐像素分類Mask掩碼分類理解摘要

目標檢測和實例分割是計算機視覺的基本任務。目標檢測的傳統方法中通常利用邊界框技術進行對象定位,然后利用逐像素分類為這些本地化實例分配類。但是當處理同一類的重疊對象時,或者在每個圖像的對象數量不同的情況下,這些方法通常會出現問題…

C++之stack類的代碼及其邏輯詳解

1. stack介紹及使用方法stack是一種后進先出的數據結構,所以在C的STL庫中也同樣遵循了這一點,我們在使用的時候不支持隨機訪問或迭代器遍歷。注意事項調用 top() 或 pop() 前需確保棧非空,否則可能引發未定義行為。stack 沒有 clear() 函數&a…

Spring Cache實現簡化緩存功能開發

一. 介紹Spring Cache 是 Spring 框架提供的緩存抽象層,它簡化了在應用中添加緩存功能的開發工作。通過 Spring Cache,開發者無需關注具體緩存實現的細節,只需通過注解就能快速實現方法級別的緩存管理。核心特點1. 與具體緩存實現解耦&#x…

Lombok(簡化Java當中的開發)

Lombok概述 以前的Java項目中,充斥著太多不友好的代碼:POJO的getter/setter/toString/構造方法;打印日志;I/O流的關閉操作等等,這些代碼既沒有技術含量,又影響著代碼的美觀,Lombok應運而生。 LomBok可以通過注解,幫助開發人員消除JAVA中尤其是POJO類中的冗長代碼。 使…

【DeepSeek】公司內網部署離線deepseek+docker+ragflow本地模型實戰

企業內部可能有些數據比較敏感,不能連接互聯網。本次實驗操作是將deepseek完全離線后遷移至內網使用,實驗基于Windows server 2022 datacenter系統安裝deepseek、docker、ragflow。 目錄使用VMware新建WIN2022虛擬機一、安裝DeepSeek模型二.安裝Docker使…

【軟考架構】面向服務的體系結構(SOA)深度解析

面向服務的體系結構(SOA)深度解析 面向服務的體系結構(Service-Oriented Architecture, SOA)是一種以服務為核心的軟件架構范式,通過標準化接口實現異構系統間的高效集成與協作。以下從概念定義、發展脈絡、技術演進、…

centos7中MySQL 5.7.32 到 5.7.44 升級指南:基于官方二進制包的原地替換式升級

目錄前言1. 升級概述1.1 升級背景1.2 升級目的1.3 升級方法概述1.4 升級策略與注意事項2. 升級準備2.1 備份工作2.2 下載目標版本2.3 停止 MySQL 服務3. 替換二進制文件3.1 解壓官方二進制包3.2 替換核心二進制文件3.3 更新共享庫4. 執行升級并驗證4.1 啟動 MySQL 服務4.2 監控…

數學七夕花禮(MATLAB版)

前言參考的視頻在抖音,電腦版的抖音一直登錄不了,用手機分享的鏈接如下所示。4.35 Iv.FH yTl:/ 04/04 復制打開抖音👀數學送的七夕花禮,記得查收噢.# 七夕花禮請查收 ... https://v.douyin.com/H-YpOJCyQyg/rho4sin(8theta)公式&a…

LeetCode - 21. 合并兩個有序鏈表

題目 21. 合并兩個有序鏈表 思路 我會采用雙指針的方法,同時遍歷兩個鏈表,比較當前節點的值,將較小的節點添加到結果鏈表中。 具體思路是這樣的: 首先創建一個啞節點(dummy node)作為合并后鏈表的頭部,這樣可以簡…

ES01-環境安裝

ES01-環境安裝 文章目錄ES01-環境安裝1-參考網址2-知識總結1-參考網址 elasticsearch官網地址:https://www.elastic.co/安裝elasticsearch9.0.0參考:https://zhuanlan.zhihu.com/p/1920780524991017021安裝elasticsearch9.0.0參考:http://ww…

UI前端大數據可視化實戰策略:如何設計符合用戶認知的數據可視化界面?

hello寶子們...我們是艾斯視覺擅長ui設計、前端開發、數字孿生、大數據、三維建模、三維動畫10年經驗!希望我的分享能幫助到您!如需幫助可以評論關注私信我們一起探討!致敬感謝感恩!UI前端大數據可視化實戰策略:如何設計符合用戶認知的數據可視化界面?數…