LLM之RAG實戰(五十二)| 如何使用混合搜索優化RAG 檢索

? ? ? ?在RAG項目中,大模型生成的參考內容(專業術語稱為塊)來自前一步的檢索,檢索的內容在很大程度上直接決定了生成的效果,因此檢索對于RAG項目至關重要,最常用的檢索方法是關鍵字搜索和語義搜索。本文將分別介紹這兩種搜索策略,然后將它們結合起來進行混合檢索。

一、使用 BM25 進行關鍵字搜索

? ? ? ?BM25 是關鍵字搜索的首選算法。使用 BM25,我們可以為語料庫中每個文檔的查詢獲得分數。

? ? ? ?BM25 基于 TF-IDF 算法,這意味著公式的核心是術語頻率 (TF) 和逆向文檔頻率 (IDF) 的乘積。

? ? ? TF-IDF 算法基于以下理念:“對頻率較低、更具體的術語的匹配比對頻繁術語的匹配更有價值”

? ? ? 換句話說,TF-IDF 算法會查找包含查詢中罕見關鍵字的文檔。

圖片

? ? ? ?如果我們看一下 LangChain 源碼(https://api.python.langchain.com/en/latest/_modules/langchain_community/retrievers/bm25.html#BM25Retriever),可以看到它使用了 rank_bm25 包中的 BM25Okapi 類,該類是 ATIRE BM25 算法的略微修改版本。

? ? ?在 ATIRE BM25 版本中,獲得文檔 d 和由多個詞 t 組成的給定查詢 q的分數的公式如下

圖片

  • N 是語料庫中的文檔數

  • df_t 是包含術語 t 的文檔數 (也稱為文檔頻率)

  • tf_td 是術語 t 在文檔 d 中出現的次數(也稱為術語頻率)

  • L_d是我們文檔的長度,L_avg是平均文檔長度

  • 有兩個經驗調優參數: b 和 k_1

我們看到公式對所有項 t 求和,我們可以將其視為單詞。

? ? ? BM25 方程中的左手因子 log(N/df_t) 稱為逆文檔頻率。對于像 “the” 這樣的常用詞,我們所有的文檔都可能包含,所以逆向文檔頻率將為零(因為 log(1) 為零)。

? ? ?另一方面,非常罕見的單詞只會出現在少數文檔中,從而增加左因子。因此,逆向文檔頻率是衡量術語 t 中包含多少信息的量度。

? ? ? 右因子受術語 t 在文檔 d 中出現的次數的影響。

? ? 該文檔 d=["I like red cats, black cats, white cats, and brown cats"] 對詞 t=“cats” 具有非常高的詞頻tf_td,這將導致包含單詞 “cats” 的查詢獲得較高的 BM25 分數。

? ? ? 讓我們使用 BM25 來使用 Python 庫rank_bm25來獲得一些直覺。

pip?install rank_bm25

? ? ? 首先,我們加載庫并使用我們的標記化語料庫初始化 BM25。

from rank_bm25 import BM25Okapicorpus = [? ??"The cat, commonly referred to as the domestic cat or house cat, is a small domesticated carnivorous mammal.", ? ?? ??"The dog is a domesticated descendant of the wolf.", ? ?? ??"Humans are the most common and widespread species of primate, and the last surviving species of the genus Homo.", ? ?? ??"The scientific name Felis catus was proposed by Carl Linnaeus in 1758"]tokenized_corpus = [doc.split(" ") for doc in corpus]
bm25 = BM25Okapi(tokenized_corpus)

? ? ? 接下來,我們對查詢進行標記化。???????

query?=?"The cat"tokenized_query?= query.split(" ")

? ? ?最后,我們使用 BM25 算法計算分數。高分表示文檔和查詢之間的匹配良好。???????

doc_scores = bm25.get_scores(tokenized_query)
print(doc_scores)
>> [0.92932018 0.21121974 0. 0.1901173]# scores for documents 1, 2, 3, and 4

? ? ? ?由于 BM25 查找完全匹配的術語,因此查詢術語“cats”、“Cat”或“feline”都將導致三個示例文檔的分數為 doc_scores = [0,0,0]。

二、使用密集嵌入的語義搜索

? ? ? ?當我們通過密集嵌入執行語義搜索時,我們會將單詞轉換為數字表示。其理念是,在這種新的數學表示形式中,相似的單詞緊密相連。

圖片

? ? ? ?文本嵌入是單個單詞或整個句子的高維向量。它們稱為 dense,因為向量中的每個條目都是一個有意義的數字。相反,當許多 vector 條目只是為零時,稱為 sparse。

? ? ? ?在將單詞轉換為嵌入之前,首先通過稱為編碼器的神經網絡嵌入模型將token轉換為嵌入向量。

圖片

? ? ? ?在將文檔語料庫中的所有文本轉換為嵌入后,可以執行語義搜索以查看哪個嵌入文檔最接近我們的嵌入查詢。

? ? ? ?我們可以通過繪制嵌入維度并找到與我們的查詢最匹配的文檔來可視化此任務。

圖片

? ? ? ?在數學上,我們使用余弦距離函數找到最接近的匹配項。對于兩個嵌入向量 a 和 b,我們可以使用點積計算余弦相似度,如下所示:

圖片

? ? ? ?其中分子是兩個嵌入向量的點積,分母是它們量級的乘積。

? ? ? ?在幾何學上,余弦相似度是向量之間的角度。余弦相似性分數范圍為 -1 到 +1。

? ? ? ?余弦相似度分數 -1 表示嵌入 a 和 b 正好朝向相反的方向,0 表示它們的角度為 90 度(它們無關),+1 表示它們相同。 因此,在將搜索查詢與文檔匹配時,我們會尋找接近 +1 的值。

? ? ? ? 如果我們事先對嵌入進行歸一化,則余弦相似度測度將等效于點積相似度測度(分母變為 1)。

? ? ? ? 下面讓我們使用 Python 包 sentence-transformers 來計算一下基本的語義搜索。

pip?install sentence-transformers

? ? ?首先,從 HuggingFace 下載全 MiniLM-L6-v2 編碼器模型,可生成 384 維密集嵌入。???????

from?sentence_transformers?import?SentenceTransformer
# 1. Load a pretrained Sentence Transformer modelmodel = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')

? ? ? 然后,我們使用與以前相同的文檔語料庫。???????

# The documents to encodecorpus = [? ??"The cat, commonly referred to as the domestic cat or house cat, is a small domesticated carnivorous mammal.", ? ?? ??"The dog is a domesticated descendant of the wolf.", ? ?? ??"Humans are the most common and widespread species of primate, and the last surviving species of the genus Homo.", ? ?? ??"The scientific name Felis catus was proposed by Carl Linnaeus in 1758"]? ??# Calculate embeddings by calling model.encode()document_embeddings = model.encode(corpus)? ??# Sanity checkprint(document_embeddings.shape)>> (4, 384)

? ? ? ?對查詢進行嵌入:???????

query?=?"The cat"query_embedding?= model.encode(query)

? ? ? 最后,計算余弦相似度分數。可以使用 sentence_transformers 中的 utility 函數 cos_sim,而不是自己編寫公式。???????

from sentence_transformers.util import cos_sim# Compute cosine_similarity between documents and queryscores = cos_sim(document_embeddings, query_embedding)

print(scores)>>?tensor([[0.5716], ?# score for document 1>>? ? ? ? ?[0.2904], ?# score for document 2>>? ? ? ? ?[0.0942], ?# score for document 3>>? ? ? ? ?[0.3157]])?# score for document 4

? ? ? ?為了了解使用密集嵌入的語義搜索的強大功能,我可以使用查詢 “feline” 重新運行代碼:???????

query_embedding = model.encode("feline")
scores = cos_sim(document_embeddings, query_embedding)
print(scores)>> tensor([[0.4007],>> ? ? ? ? [0.3837],>> ? ? ? ? [0.0966],>> ? ? ? ? [0.3804]])

? ? ? ?即使 “feline” 一詞沒有出現在文檔語料庫中,語義搜索仍然將有關貓的文本列為最高匹配度。

三、語義搜索還是關鍵字搜索?

? ? ? ?哪種搜索方法更好?這要看情況。兩者都有優點和缺點。現在我們知道了兩者的工作原理,我們可以看到它們在哪些方面有用,哪些方面可能失敗。

? ? ? ?使用 BM25 進行關鍵字搜索會查找查詢詞的完全匹配項。當我們在尋找短語的精確匹配時,這可能非常有用。

? ? ? ?如果我在找《帽子里的貓》(The Cat in the Hat),我可能在找這本書/電影。而且我不希望出現語義上相似的結果,這些結果接近 hats 或 cats。

? ? ? ?關鍵字搜索的另一個用例是編程。如果我正在尋找特定的函數或代碼段,我想要一個完全匹配。

? ? ? ?另一方面,語義搜索會查找語義相似的內容。這意味著語義搜索還會查找具有同義詞或不同拼寫(如復數、大寫等)的文檔。

? ? ? ?由于這兩種算法都有其用例,因此混合搜索同時使用這兩種算法,然后將它們的結果合并為一個最終排名。

? ? ? ?混合搜索的缺點是它比只運行一種算法需要更多的計算資源。

四、混合搜索

? ? ? ?我們可以使用倒數秩融合 (RRF) 將 BM25 和余弦相似性的結果結合起來。RRF 是一種簡單的算法,用于組合不同評分函數的排名 [4]。

? ? ? ?首先,我們需要獲取每種評分算法的文檔排名。在我們的示例中,這將是:???????

corpus?= [? ??"The cat, commonly referred to as the domestic cat or house cat, is a small domesticated carnivorous mammal.", ? ?? ??"The dog is a domesticated descendant of the wolf.", ? ?? ??"Humans are the most common and widespread species of primate, and the last surviving species of the genus Homo.", ? ?? ??"The scientific name Felis catus was proposed by Carl Linnaeus in 1758",]? ??query?=?"The cat"bm25_ranking?= [1,?2,?4,?3]?# scores = [0.92932018 0.21121974 0. 0.1901173]cosine_ranking?= [1,?3,?4,?2]?# scores = [0.5716, 0.2904, 0.0942, 0.3157]

? ? ? ?每個文檔 d 的綜合 RRF 分數公式如下:

圖片

? ? ? ?其中 k 是一個參數(原始論文使用 k=60),r(d) 是 BM25 和余弦相似度的排名。

? ? ? ?現在,我們可以通過分別進行 BM25 和余弦相似性,然后將結果與 RRF 相結合來實現我們的混合搜索。

? ? ? ?首先,讓我們定義 RRF 的函數和將浮點分數轉換為 int 排名的輔助函數。???????

import?numpy?as?np
def?scores_to_ranking(scores:?list[float]) ->?list[int]:? ??"""Convert float scores into int rankings (rank 1 is the best)"""? ??? ??return?np.argsort(scores)[::-1] +?1? ??def?rrf(keyword_rank:?int, semantic_rank:?int) ->?float:? ??"""Combine keyword rank and semantic rank into a hybrid score."""? ??? ? k =?60? ??? ? rrf_score =?1?/ (k + keyword_rank) +?1?/ (k + semantic_rank) ? ?? ??return?rrf_score

? ? ? ? 這是我使用上述概念的簡單混合搜索實現。???????

from?rank_bm25?import?BM25Okapifrom?sentence_transformers?import?SentenceTransformerfrom?sentence_transformers.util?import?cos_sim
model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")def?hybrid_search(? ? query:?str, corpus:?list[str], encoder_model: SentenceTransformer? ? ) ->?list[int]:? ??# bm25 ? ?? ? tokenized_corpus = [doc.split(" ")?for?doc?in?corpus] ? ?? ? tokenized_query = query.split(" ") ? ?? ? bm25 = BM25Okapi(tokenized_corpus) ? ?? ? bm25_scores = bm25.get_scores(tokenized_query) ? ?? ? bm25_ranking = scores_to_ranking(bm25_scores) ? ?? ??? ??# embeddings ? ?? ? document_embeddings = model.encode(corpus) ? ?? ? query_embedding = model.encode(query) ? ?? ? cos_sim_scores = cos_sim(document_embeddings, query_embedding).flatten().tolist() ? ?? ? cos_sim_ranking = scores_to_ranking(cos_sim_scores) ? ?? ??? ??# combine rankings into RRF scores ? ?? ? hybrid_scores = [] ? ?? ??for?i, doc?in?enumerate(corpus): ? ? ? ?? ? ? ? document_ranking = rrf(bm25_ranking[i], cos_sim_ranking[i]) ? ? ? ?? ? ? ??print(f"Document?{i}?has the rrf score?{document_ranking}") ? ? ? ?? ? ? ? hybrid_scores.append(document_ranking) ? ?? ? ? ??? ??# convert RRF scores into final rankings ? ?? ? hybrid_ranking = scores_to_ranking(hybrid_scores) ? ?? ??return?hybrid_ranking

現在我們可以將 hybrid_search 用于不同的查詢。???????

hybrid_ranking = hybrid_search(? ? query="What is the scientifc name for cats?", corpus=corpus, encoder_model=model? ? )print(hybrid_ranking)>> Document 0 has the rrf score 0.03125>> Document 1 has the rrf score 0.032266458495966696>> Document 2 has the rrf score 0.03225806451612903>> Document 3 has the rrf score 0.032266458495966696>> [4 2 3 1]

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

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

相關文章

[學成在線]07-視頻轉碼

視頻轉碼 視頻上傳成功后需要對視頻進行轉碼處理。 首先我們要分清文件格式和編碼格式: 文件格式:是指.mp4、.avi、.rmvb等這些不同擴展名的視頻文件的文件格式 ,視頻文件的內容主要包括視頻和音頻,其文件格式是按照一定的編碼…

Leetcode算法方法總結

1. 雙指針法解決鏈表/數組題目 只要數組有序,就要想到雙指針做法。還有二分法 回文串一般也會用到雙指針,回文串的長度由于可能是奇數也可能是偶數,所以在尋找時,既需要尋找奇數長度的回文串,也需要尋找偶數長度的回文…

一周掌握Flutter開發--9. 與原生交互(上)

文章目錄 9. 與原生交互核心場景9.1 調用平臺功能:MethodChannel9.1.1 Flutter 端實現9.1.2 Android 端實現9.1.3 iOS 端實現9.1.4 使用場景 9.2 使用社區插件9.2.1 常用插件9.2.2 插件的優勢 總結 9. 與原生交互 Flutter 提供了強大的跨平臺開發能力,但…

基于Flask的通用登錄注冊模塊,并代理跳轉到目標網址

實現了用戶密碼的加密,代理跳轉到目標網址,不會暴露目標路徑,未登錄的情況下訪問proxy則自動跳轉到登錄頁,使用時需要修改配置項config,登錄注冊頁面背景快速修改,可以實現登錄注冊模塊的快速復用。 1.app…

Java課程設計(雙人對戰游戲)持續更新......

少廢話,當然借助了ai,就這么個實力,后續會逐漸完善...... 考慮添加以下功能: 選將,選圖,技能,天賦,道具,防反,反重力,物理反彈,擊落…

Ai工作流工具有那些如Dify、coze扣子等以及他們是否開源

Dify (https://difycloud.com/) 核心定位:專業級 LLM 應用開發平臺,支持復雜 AI 工作流構建與企業級管理。典型場景:企業智能客服、數據分析系統、復雜自動化流程構建等。適合需要深度定制、企業級管理和復雜 AI 邏輯…

Debezium系列之:使用Debezium和Apache Iceberg構建數據湖

Debezium系列之:使用Debezium和Apache Iceberg構建數據湖 Debezium Server Iceberg“Debezium Server Iceberg” 消費者設置數據復制Upsert 模式保留已刪除的記錄使用Upsert模式追加模式優化批處理大小在數據分析的世界中,數據湖是存儲和管理大量數據以滿足數據分析、報告或機…

docker run -p 5000:5000 my-flask-app

docker run -p 5000:5000 my-flask-app代碼的意思是: 運行 my-flask-app 容器,并把 Flask 服務器的 5000 端口映射到本機的 5000 端口。 拆解解釋 docker run -p 5000:5000 my-flask-app? docker run → 運行一個 Docker 容器 ? -p 5000:5000 → 端口…

高光譜工業相機+LED光源系統助力材料分類和異物檢測、實現高速在線檢測

檢測光源包括可見光,如紅光、藍光和綠光以及其他波長的光,如紫外和紅外波長,可以選擇與檢測對象物相應的波長。但由于能夠照射的波長較窄,例如受到同色異物混入或多個素材的材質分類等,可能需要使用可照射多種波長的光…

Spring 攔截器(Interceptor)與過濾器(Filter)對比

Spring 攔截器(Interceptor)與過濾器(Filter)對比 核心對比表格 對比維度攔截器(Interceptor)過濾器(Filter)定義Spring MVC 提供的組件,集成于 Spring 處理器鏈。Servl…

VulnHub-FALL通關攻略

第一步:確定靶機IP為192.168.40.129 第二步:掃描后臺及開放端口 #開放端口 22 --- ssh 25 --- SMTP簡單郵件傳輸協議 80 --- HTTP萬維網傳輸信息協議 110 --- POP3郵件協議3 139 --- NetBIOS服務 443 --- https服務 445 --- SMB協議 3306 --- Mysql 808…

Qt 線程和 QObjects

線程和 QObjects QThread 繼承于 QObject。 它發出信號來指示線程開始或結束執行,并提供一些插槽。 更有趣的是,QObjects 可以在多個線程中使用,發出信號以調用其他線程中的插槽,并向 "生活 "在其他線程中的對象發布事件…

華為、浪潮、華三鏈路聚合概述

1、華為 鏈路聚合可以提高鏈路帶寬和鏈路冗余性。有三種類型,分別是手工鏈路聚合,靜態lacp鏈路聚合,動態lacp鏈路聚合。 手工鏈路模式:也稱負載分擔模式,需手動指定鏈路,各鏈路之間平均分擔流量。靜態LAC…

HarmonyOS NEXT 鴻蒙中關系型數據庫@ohos.data.relationalStore API 9+

核心API ohos.data.relationalStore API 9 數據庫 數據庫是存儲和管理數據的系統 數據庫(Database)是一個以特定方式組織、存儲和管理數據的集合,通常用于支持各種應用程序和系統的運行。它不僅是存放數據的倉庫,還通過一定的…

步進電機 cia402協議 報文自己的理解 (筆記)

1. cai402 協議是什么 CiA 402 協議(CAN in Automation 402),它是工業自動化領域中的一種通信協議,主要用于運動控制(如伺服驅動器、步進電機等)( )所屬標準 CiA 402 是 CANopen 應用…

鴻蒙攝像機,一場智能安防的“平權革命”

2025的春天,全國各行各業都感受到了普惠AI的魅力。大模型帶來的技術平權,讓每一個人都能輕松用上AI。 這時候,企業想知道,每時每刻離不開的攝像機,究竟什么時候才能迎來智能技術的平權與普惠。 博思數據研究中心的一份…

解決HuggingFaceEmbeddings模型加載報錯:缺少sentence-transformers依賴包

遇到報錯 報錯信息: Error loading model: Could not import sentence_transformers python package. Please install it with pip install sentence-transformers. 裝包信息: pip install modelscope langchain sentence_transformers langchain-huggingface on…

從泛讀到精讀:合合信息文檔解析如何讓大模型更懂復雜文檔

從泛讀到精讀:合合信息文檔解析如何讓大模型更懂復雜文檔 一、引言:破解文檔“理解力”瓶頸二、核心功能:合合信息的“破局”亮點功能亮點1:復雜圖表的高精度解析圖表解析:為大模型裝上精準“標尺”表格數據精準還原 功…

Python+requests實現接口自動化測試框架

為什么要做接口自動化框架 1、業務與配置的分離 2、數據與程序的分離;數據的變更不影響程序 3、有日志功能,實現無人值守 4、自動發送測試報告 5、不懂編程的測試人員也可以進行測試 正常接口測試的流程是什么? 確定接口測試使用的工具…

信息學奧賽一本通 1514:【例 2】最大半連通子圖 | 洛谷 P2272 [ZJOI2007] 最大半連通子圖

【題目鏈接】 ybt 1514:【例 2】最大半連通子圖 洛谷 P2272 [ZJOI2007] 最大半連通子圖 【題目考點】 1. 圖論:強連通分量 縮點 2. 圖論:拓撲排序 有向無環圖動規 【解題思路】 對于圖中任意兩頂點u、v,滿足u到v或v到u有路徑…