Sentence Transformers 是一個 Python 函式庫,用於嵌入和重排序模型,應用於檢索增強生成、語義搜尋等。v6.0 更新後,它新增了第四種模型類型:MultiVectorEncoder,專為 ColBERT 風格的晚期互動檢索設計。
任何 PyLate 或 Stanford-NLP ColBERT 檢查點都能直接載入,甚至 colpali-engine 的視覺文件檢索模型也能透過熟悉的 API 使用,就像您使用密集、稀疏和重排序模型一樣。
傳統嵌入模型將整個文本壓縮成單一向量,而多向量模型則為每個詞元保留一個向量,並使用 MaxSim 運算子來比對查詢與文件。這保留了單一向量模型必須平均掉的詞元級別匹配資訊,通常意味著以更大的索引為代價,換取更強大的檢索能力。它也是視覺文件檢索的最新技術,可以直接將文本查詢與頁面圖像匹配,無需光學字元辨識 (OCR) 步驟。
本部落格文章將展示如何使用這些模型:包括載入各種檢查點格式、編碼與評分、將它們整合到搜尋堆疊中、在頁面圖像上運行,以及如何控制索引成本。以下所有內容都只需透過 pip install -U sentence-transformers 即可運行。
密集嵌入模型讀取文本並返回一個固定大小的單一向量。模型注意到的所有資訊都必須壓縮到這 384、768 或 1024 個數字中,相似度則透過兩個摘要之間的點積來計算。這種方法效果顯著,但壓縮方式存在特定損失:罕見實體、精確識別碼或長段落中的關鍵子句都必須在同一向量中爭奪空間。
一個同時包含多個需求的查詢也會遇到同樣的限制。例如,對於「綠色、木腿、圓形坐墊的沙發」,單一向量必須將這四個特徵融合為一個點,導致綠色但腿部不對的沙發最終會與您實際尋找的沙發非常接近。
多向量模型(也稱為晚期互動或 ColBERT 風格模型,名稱源自 ColBERT 論文)則跳過了這種壓縮。它運行相同的轉換器,但不是將詞元嵌入池化成單一向量,而是將每個詞元嵌入投影到一個較小的維度(通常是 128),並保留所有這些向量。一個 9 個詞元的文件會變成一個 9x128 的矩陣,而不是一個 1x128 的向量。
查詢和文件之間的互動會延遲到評分時進行,這也是「晚期互動」名稱的由來。交叉編碼器(cross-encoder)會提早互動:兩個文本一起通過模型,這雖然準確,但無法預先計算任何內容,因為每個文件都必須針對每個新查詢重新編碼。雙編碼器(bi-encoder),即上述的密集嵌入模型,幾乎不進行互動(兩個完成的摘要之間只有一個點積),這正是它能讓您一次性編碼集合並快速查詢的原因。
晚期互動則介於兩者之間:文件仍可獨立編碼並離線建立索引,但評分時會比較每個查詢詞元與每個文件詞元,這為兩者之間的互動留下了更多空間。
評分使用 MaxSim 運算子:對於每個查詢詞元,取其與任何文件詞元之間最高的相似度,然後將這些最大值加總到查詢中。由於詞元嵌入是 L2 歸一化的,每個點積都是 [-1, 1] 範圍內的餘弦相似度,因此總和落在 [-查詢詞元數, 查詢詞元數] 之間。
您可以將此運算子理解為一種軟性對齊:每個查詢詞元指向最能解釋它的文件詞元,而分數則代表文件對查詢的整體支持程度。這種對齊不一定是詞彙上的,因為詞元嵌入是上下文相關的。使用 lightonai/mLateOn 對「企鵝住在哪裡?」和「企鵝棲息於南極洲。」
進行編碼,查詢詞元「live」在文件詞元「inhabit」上找到了最佳匹配,相似度為 0.94,儘管兩者沒有共享任何字元!這是詞彙檢索無法做到的,BM25 及其相關方法需要詞彙本身,因此同義詞和釋義會被忽略。當然,密集嵌入模型也能彌補這一點。
晚期互動的額外優勢在於它沒有放棄另一個方向:當精確匹配很重要時(例如產品代碼、姓氏、函數名稱),MaxSim 仍然能獨立保留該詞元,而單一向量模型則必須將其與其他所有內容平均化。它也不是一對一的,因為多個查詢詞元通常會落在同一個文件詞元上。
您將獲得檢索品質的提升,特別是在以下情況:文件中的某個特定部分使其相關、像上述沙發那樣每個需求都能找到各自證據的多需求查詢,以及密集模型壓縮針對不同分佈進行調整的域外數據。這種壓縮是從訓練查詢中學習的,因此模型會學習保留它們所需的內容並丟棄其他所有內容,這可能恰好包含您的生產查詢所詢問的內容。這種效果隨著文件長度而增加,因為更多文本必須適應相同的固定向量。
代價是索引大小。每個詞元一個向量而不是每個文件一個向量,這意味著更多的向量,儘管維度較小,但只能部分抵消。使用 lightonai/LateOn 編碼 4,874 個 Natural Questions 段落,產生了 608,414 個詞元向量,平均每個段落 124.8 個:這大約是 MiniLM 索引儲存空間的 42 倍,或每個段落 62 KiB。
然而,索引通常會被壓縮,例如,相同的 608,414 個向量作為 fast-plaid 索引時僅佔 92 MB,因為 PLAID 儲存的是質心 ID 加上每個向量的量化殘差,而不是向量本身。以規模而言,像 Qwen3-Embedding-8B 這樣 4096 維的密集模型,對於這 4,874 個段落大約需要 80 MB,因此壓縮後的多向量索引與人們已經使用的密集索引處於相同範圍。
詞元池化(Token Pooling)在這些之前就減少了向量數量,而檢索與重排序(Retrieve and Rerank)則完全避免了建立索引。
PyLate 在本文中多次提及,簡要說明一下:Sentence Transformers 處理密集和稀疏模型,但不支持晚期互動,因此 LightOn 在其基礎上構建了 PyLate,以彌補這一空白,增加了這些模型所需的訓練、推斷和檢索部分。
您將在下面載入的許多模型都是用它訓練的,LightOn 也圍繞它建立了一個生態系統,包括 fast-plaid,即在「索引」部分出現的晚期互動索引。隨著 v6.0 的發布,這些功能已整合到 Sentence Transformers 本身中。考慮到權衡,讓我們開始運行一個模型。
多向量模型只需簡單安裝即可使用:pip install -U sentence-transformers。對於 ColPali 風格的視覺文件檢索,您還需要圖像相關的依賴項(請參閱安裝以獲取所有額外功能,以及多模態嵌入與重排序模型以獲取一般多模態支持):pip install -U "sentence-transformers[image]"。
Sentence Transformers v6.0 需要 transformers v5.x、torch 2.2+ 和 huggingface-hub v1.x。如果您鎖定任何較低版本,請先規劃升級。請參閱遷移指南以獲取所有重大變更的完整列表。
載入多向量模型與載入任何其他 Sentence Transformers 模型完全相同:from sentence_transformers import MultiVectorEncoder,然後 model = MultiVectorEncoder("lightonai/LateOn")。
要找到可用的模型,請在 Hub 上尋找帶有 multi-vector 和 sentence-transformers 標籤的模型。任何帶有這些標籤的模型都可以透過上述程式碼載入,無論它最初是 PyLate 檢查點、Stanford-NLP ColBERT 檢查點,還是用於視覺文件檢索的 ColPali 系列模型。我們正在努力為每個可用的模型添加這些標籤,因此列表會不斷增長。
在底層,MultiVectorEncoder 讀取這些檢查點多年來發布的各種格式,因此即使尚未添加標籤,PyLate 和 Stanford-NLP 檢查點也能直接載入:
```python
from sentence_transformers import MultiVectorEncoder
原生 Sentence Transformers 檢查點。PyLate 建構在相同的架構上,
因此任何 PyLate 檢查點都能以相同方式載入
model = MultiVectorEncoder("lightonai/LateOn")
model = MultiVectorEncoder("mixedbread-ai/mxbai-edge-colbert-v0-17m")
model = MultiVectorEncoder("LiquidAI/LFM2.5-ColBERT-350M", trust_remote_code=True)
任何 Stanford-NLP ColBERT 檢查點,透過 HF_ColBERT 架構標記檢測。
內聯投影權重和配方來自 artifact.metadata
model = MultiVectorEncoder("colbert-ir/colbertv2.0")
model = MultiVectorEncoder("answerdotai/answerai-colbert-small-v1")
裸轉換器:會附加一個新的隨機投影,因此需要訓練
model = MultiVectorEncoder("answerdotai/ModernBERT-base")
```
視覺文件檢索模型是個例外。ColPali 系列檢查點以 colpali-engine 自己的格式發布,其中不包含 Sentence Transformers 可以使用的資訊,因此每個模型都需要在其儲存庫中添加一個小配置才能載入。大部分工作已經完成並等待合併。請參閱「支援的模型」以了解當前狀態以及如何載入它們。
多向量模型帶有一些因檢查點而異的配方設定:查詢和文件的標記前綴、長度上限、查詢是否用 [MASK] 詞元填充,以及評分文件時跳過哪些詞元。所有這些都存在於模組配置中,因此 print(model) 會顯示您載入了什麼。以下是原始的 ColBERTv2 檢查點,它將每個查詢填充到恰好 32 個詞元,並將文件截斷為 180 個詞元:
```python
from sentence_transformers import MultiVectorEncoder
model = MultiVectorEncoder("colbert-ir/colbertv2.0")
print(model)
```
這就是經典的 ColBERT 管線:一個轉換器產生上下文相關的詞元嵌入,一個詞元級別的 Dense 層將每個嵌入投影到 128 維,一個 MultiVectorMask 決定哪些詞元在評分時計數,以及一個詞元級別的 Normalize 層。
其他檢查點會填入不同的值。lightonai/GTE-ModernColBERT-v1 使用相同的四個模組,帶有 [Q] 和 [D] 提示詞,沒有查詢擴展,上限分別為 48 和 300。您很少需要修改這些,因為每個發布的檢查點都已自行配置。
當您從裸骨幹模型構建模型時,這才重要,這在「建立自定義模型」中有所介紹。不過,有一個值值得根據您自己的數據進行檢查。document_length 會截斷文本,因此超過該長度的任何內容都不會到達索引。



