
譯者 | 朱先忠
審校 | 重樓
本文介紹ColPali與DocLayNet結(jié)合的多模態(tài)RAG系統(tǒng),通過(guò)視覺(jué)語(yǔ)言建模理解文檔中的表格、圖表等布局信息,顯著提升復(fù)雜文檔問(wèn)答的準(zhǔn)確性和上下文感知能力。
簡(jiǎn)介
檢索增強(qiáng)生成(RAG)已成為構(gòu)建開(kāi)放領(lǐng)域和特定領(lǐng)域問(wèn)答系統(tǒng)的標(biāo)準(zhǔn)范例。傳統(tǒng)意義上,RAG流程嚴(yán)重依賴于基于文本的檢索器,這些檢索器使用密集或稀疏嵌入來(lái)索引和檢索段落。雖然這些方法對(duì)于純文本內(nèi)容有效,但在處理視覺(jué)復(fù)雜的文檔(例如科學(xué)論文、財(cái)務(wù)報(bào)告或掃描的PDF)時(shí),往往會(huì)遇到困難,因?yàn)檫@些文檔中的關(guān)鍵信息嵌入在表格、圖形或結(jié)構(gòu)化布局中,而這些布局無(wú)法很好地轉(zhuǎn)換為純文本。

論文插圖:與傳統(tǒng)方法相比,ColPali簡(jiǎn)化了文檔檢索流程,同時(shí)提供了更高的性能和更低的延遲(來(lái)源:arxiv)
為了突破這些限制,Manuel Faysse等人近期的研究成果提出了ColPALI(ICLR 2025),這是一個(gè)視覺(jué)語(yǔ)言檢索框架,它使用類似ColBERT的視覺(jué)嵌入后期交互,基于圖像理解來(lái)檢索文檔內(nèi)容。與此同時(shí),Pfitzmann等人提出了Yolo DocLayNet(CVPR 2025),這是一個(gè)快速且布局感知的對(duì)象檢測(cè)模型,專門(mén)用于以高精度和高效率提取文檔組件,例如表格、圖表和章節(jié)標(biāo)題。
在本文中,我將指導(dǎo)你完成混合RAG管道的實(shí)際實(shí)現(xiàn),該管道結(jié)合了ColPALI和DocLayout-YOLO,以實(shí)現(xiàn)對(duì)閱讀和查看的文檔的問(wèn)答。
RAG系統(tǒng)中的視覺(jué)盲點(diǎn)
盡管在處理文本查詢方面取得了成功,但大多數(shù)RAG系統(tǒng)都忽略了一個(gè)關(guān)鍵問(wèn)題。它們幾乎忽略了表格、圖表和圖形等視覺(jué)元素,而這些元素在許多實(shí)際文檔中都承載著至關(guān)重要的意義。讓我們來(lái)看看下圖。

圖片來(lái)自:SLB 2023年年度報(bào)告的摘錄(資料來(lái)源:報(bào)告)
通過(guò)應(yīng)用常見(jiàn)的OCR工具從表格中提取文本,我們可以得到以下結(jié)果。
Option Awards Stock Awards
Name
Option/
PSU/RSU
...
...
... (truncated)
Shares, Units, or
Other Rights That
Have Not Vested
($)(1)
D. Ralston 1/20/2021 67,220(2) 3,498,129
1/20/2021 33,610(3) 1,749,064
2/3/2021 29,390(4) 1,529,456
1/19/2022 64,587(5) 3,361,107
1/19/2022 22,321(6) 1,161,585
1/18/2023 41,668(7) 2,168,403
1/18/2023 14,427(8) 750,781顯然,OCR結(jié)果無(wú)法捕捉多層級(jí)標(biāo)題結(jié)構(gòu)和列分組,導(dǎo)致文本呈現(xiàn)扁平的線性,數(shù)值與其對(duì)應(yīng)指標(biāo)之間的關(guān)聯(lián)性缺失。這使得數(shù)據(jù)所屬類別(例如授予日期與股票獎(jiǎng)勵(lì))難以識(shí)別,從而降低了提取數(shù)據(jù)對(duì)下游分析的實(shí)用性。
用戶查詢:What is the market value of unearned shares, units, or other rights that have not vested for the 1/20/2021 grant date?(2021年1月20日授予日尚未歸屬的未賺取股份、單位或其他權(quán)利的市場(chǎng)價(jià)值是多少?)
RAG回應(yīng):The market value of unearned shares, units, or other rights that have not vested for the 1/20/2021 grant date is $1,749,064.(2021年1月20日授予日尚未歸屬的未賺取股份、單位或其他權(quán)利的市場(chǎng)價(jià)值為1,749,064美元。)
由于提取的信息缺乏結(jié)構(gòu)和上下文,因此產(chǎn)生的RAG響應(yīng)不準(zhǔn)確,因?yàn)樗鼰o(wú)法可靠地將值與原始表中的預(yù)期含義關(guān)聯(lián)起來(lái)。
讓我們探討另一個(gè)例子來(lái)進(jìn)一步說(shuō)明傳統(tǒng)RAG系統(tǒng)的局限性。

圖片來(lái)源:SLB 2023年年度報(bào)告中的數(shù)據(jù)圖表片段(來(lái)源:報(bào)告)
這是從上圖中提取的OCR結(jié)果。
Total Shareholder Return
Indexed ($100)
$50.0 \n$40.0 \n$30.0 \n$20.0 \n$10.0 \n$0.0-$10.0-$20.0
2020 2021 2023 2022
CEO CAP Avg. NEO CAP SLB TSR-$10.6
$2.2 \n$32.4 \n$13.1 \n$39.4 \n$26.2 \n$17.3 \n$9.3
...
...
... (truncated)
$40.00 \n$20.00 \n$0.00
OSX TSR
CAP vs. Total Shareholder Return
(SLB and OSX)
(in millions of US dollars)
Compensation Actually Paid易知,該圖表的OCR輸出也缺乏結(jié)構(gòu)一致性,未能捕捉數(shù)據(jù)點(diǎn)之間的關(guān)系,例如哪些條形或標(biāo)簽對(duì)應(yīng)特定年份或股東回報(bào)率線。此外,它還未能將數(shù)值與其視覺(jué)元素進(jìn)行對(duì)齊,導(dǎo)致難以區(qū)分每年的CEO CAP、NEO CAP和TSR值。
問(wèn)題:What was the SLB Total Shareholder Return (TSR) in 2022?(2022年SLB總股東回報(bào)率(TSR)是多少?)
RAG回應(yīng):The SLB Total Shareholder Return (TSR) in 2022 was $134.09.(SLB2022年的總股東回報(bào)(TSR)為134.09美元。)
與前面的示例一樣,由于OCR提取的數(shù)據(jù)中結(jié)構(gòu)和上下文的丟失,此處的RAG響應(yīng)也不準(zhǔn)確。
多模態(tài)RAG架構(gòu)

作者插圖:我們實(shí)驗(yàn)中的多模態(tài)RAG架構(gòu)
該架構(gòu)由兩個(gè)主要組件組成:索引管道和聊天推理管道。在索引階段,文檔語(yǔ)料庫(kù)通過(guò)兩條并行路徑進(jìn)行處理。第一條路徑使用YOLO-DocLayNet檢測(cè)器識(shí)別表格和圖形等視覺(jué)元素,然后使用ColPALI圖像編碼器將其嵌入并存儲(chǔ)在圖像向量集合中。第二條路徑使用PyTesseractOCR從文檔中提取原始文本,然后使用Mxbai-Embed模型對(duì)其進(jìn)行編碼,并保存在同一向量數(shù)據(jù)庫(kù)中的文本向量集合中。
在聊天推理過(guò)程中,用戶查詢會(huì)同時(shí)由用于Mxbai-Embed文本檢索的編碼器和用于視覺(jué)語(yǔ)言檢索的ColPALI編碼器進(jìn)行編碼。然后,系統(tǒng)會(huì)針對(duì)各自的向量集合執(zhí)行雙重檢索(文本到文本和文本到圖像)。檢索到的文本和圖像區(qū)域會(huì)被轉(zhuǎn)發(fā)到多模態(tài)LLM(LLaMA-4),該模型會(huì)綜合兩種模態(tài),生成上下文感知且準(zhǔn)確的響應(yīng)。這種設(shè)計(jì)將文本理解與細(xì)粒度的視覺(jué)推理相結(jié)合,從而實(shí)現(xiàn)強(qiáng)大的文檔質(zhì)量保證(QA)。
我的測(cè)試設(shè)置
A. 環(huán)境設(shè)置
為了高效運(yùn)行完整的多模式RAG管道,我使用單個(gè)NVIDIA RTX A6000 GPU和48GB的VRAM,這為運(yùn)行ColPALI、YOLO模型和句子嵌入模型提供了足夠的內(nèi)存。
對(duì)于軟件環(huán)境,我建議使用Miniconda來(lái)隔離你的依賴關(guān)系并確??芍貜?fù)性。
1.創(chuàng)建Conda環(huán)境
conda create -n multimodal_rag pythnotallow=3.11
conda activate multimodal_rag2. 準(zhǔn)備requirements.txt
ultralytics
git+https://github.com/illuin-tech/colpali
groq
pill
pymilvus
sentence-transformers
uvicorn
fastapi
opencv-python
pytesseract
PyMuPDF
pydantic
chainlit
pybase64
huggingface_hub[hf_transfer]3.安裝Python依賴項(xiàng)
pip install -r requirements.txtB.預(yù)訓(xùn)練模型設(shè)置
要復(fù)現(xiàn)此實(shí)驗(yàn),你需要下載檢索和布局提取流程中使用的三個(gè)預(yù)訓(xùn)練模型。所有模型都將存儲(chǔ)在該pretrained_models/目錄下,以保持一致性并更易于加載。
下面是使用Hugging Face CLI下載它們的命令hf transfer,該命令針對(duì)更快的下載速度進(jìn)行了優(yōu)化:
# 下載YOLO DocLayNet用于布局檢測(cè)
HF_HUB_ENABLE_HF_TRANSFER=1 hf download hantian/yolo-doclaynet --local-dir pretrained_models/yolo-doclaynet
# 下載 ColQwen 2.5(用于 ColPALI)用于基于圖像的檢索
HF_HUB_ENABLE_HF_TRANSFER=1 hf download vidore/colqwen2.5-v0.2 --local-dir pretrained_models/colqwen2.5-v0.2
# 下載 Mixedbread Embed Large 模型用于基于文本的檢索
HF_HUB_ENABLE_HF_TRANSFER=1 hf download mixedbread-ai/mxbai-embed-large-v1 --local-dir pretrained_models/mxbai-embed-large-v1C. 代碼設(shè)置和初始化
import torch
from Typing import Cast
from Ultralytics import YOLO
from transforms.utils.import_utils import is_flash_attn_2_available
from colpali_engine.models.paligemma.colpali.processing_colpali import ColPaliProcessor
from colpali_engine.models import ColQwen2_5, ColQwen2_5_Processor
from sentence_transformers import SentenceTransformer
# 定義設(shè)備
device = "cuda" if torch.cuda.is_available() else "cpu"
# 定義知識(shí)庫(kù)源和目標(biāo)圖像目錄
document_source_dir = "document_sources"
img_dir = "image_database"
os.makedirs(img_dir, exist_ok= True ) # 確保目錄存在
# YOLO-12L-Doclaynet
yolo_model = YOLO( "pretrained_models/yolo-doclaynet/yolov12l-doclaynet.pt" )
yolo_model = yolo_model.to(device)
# ColQwen2.5-Colpali
colpali_model = ColQwen2_5.from_pretrained(
"pretrained_models/colqwen2.5-v0.2" ,
torch_dtype=torch.bfloat16,
device_map=device, # 或 "mps" 如果在 Apple Silicon 上
attn_implementatinotallow= "flash_attention_2" 如果is_flash_attn_2_available() else None ,
)。eval ()
colpali_processor = ColQwen2_5_Processor.from_pretrained( "pretrained_models/colqwen2.5-v0.2" )
processor = cast(
ColPaliProcessor,
colpali_processor)
# Mxbai-embed-large-v1
embed_model = SentenceTransformer( "pretrained_models/mxbai-embed-large-v1" ,device=device)
# 定義實(shí)體顏色
ENTITIES_COLORS = {
"Picture" : ( 255 , 72 , 88 ),
"Table" : ( 128 , 0 , 128 )
}
print ( "FINISH SETUP..." )上述代碼初始化了多模態(tài)檢索系統(tǒng)的核心組件。它設(shè)置了設(shè)備(GPU或CPU),確保圖像輸出目錄存在,并加載了三個(gè)預(yù)訓(xùn)練模型:YOLOv12L-DocLayNet模型(用于檢測(cè)表格和圖形等布局元素)、ColQwen2.5模型(其ColPALI處理器用于對(duì)裁剪圖像區(qū)域進(jìn)行視覺(jué)語(yǔ)言嵌入)以及mxbai-embed-large-v1模型(使用SentenceTransformers嵌入文本)。此外,它還定義了檢測(cè)到的實(shí)體類型的顏色映射,以支持預(yù)處理過(guò)程中的可視化。
索引管道

作者插圖:索引管道
索引管道負(fù)責(zé)將原始文檔轉(zhuǎn)換為結(jié)構(gòu)化的、可搜索的文本和視覺(jué)表示形式。在本節(jié)中,我們將逐步講解實(shí)際實(shí)現(xiàn)過(guò)程,展示如何使用代碼處理、編碼和存儲(chǔ)文檔內(nèi)容。
A. 準(zhǔn)備
在繼續(xù)開(kāi)發(fā)之前,讓我們準(zhǔn)備一些對(duì)分析有用的處理函數(shù)。
import matplotlib.pyplot as plt
import cv2
def display_img_array ( img_arr ):
image_rgb = cv2.cvtColor(img_arr, cv2.COLOR_BGR2RGB)
plt.figure(figsize=( 30 , 30 ))
plt.imshow(image_rgb)
plt.axis( 'off' )
plt.show()
def show_layout_detection ( detection_results, img_arr ):
for result indetection_results :
boxes = result.boxes # 獲取檢測(cè)框
for box in boxes:
x, y, w, h = box.xywh[ 0 ] # 框坐標(biāo)(中心 x, y, 寬度, 高度)
x, y, w, h = int (x), int (y), int (w), int (h) # 轉(zhuǎn)換為整數(shù)
conf = box.conf.item() # 置信度得分
cls = int (box.cls.item()) # 類 ID
label = f" {yolo_model.model.names[cls]} {conf: .2 f} "
color = ENTITIES_COLORS[yolo_model.model.names[cls]] # 獲取此類的顏色
top_left = (x - w // 2 , y - h // 2 )
bottom_right = (x + w // 2 , y + h // 2 ) # 特定類的彩色框
cv2.rectangle(img_arr, top_left, bottom_right, color, 2 )
cv2.putText(img_arr, label, (top_left[ 0 ], top_left[ 1 ] - 10 ), cv2.FONT_HERSHEY_SIMPLEX, 0.9 , color, 2 ) # 匹配文本顏色
display_img_array(img_arr)上面的兩個(gè)輔助函數(shù)將用于可視化布局檢測(cè)。其中,display_img_array將BGR圖像轉(zhuǎn)換為RGB并使用matplotlib顯示它;同時(shí),show_layout_detection使用YOLO檢測(cè)結(jié)果和特定于類的顏色在檢測(cè)到的布局元素(例如表格、圖形)上疊加邊界框和標(biāo)簽。
接下來(lái),讓我們準(zhǔn)備要加載的PDF中的特定頁(yè)面,如下所示。
import fitz # PyMuPDF
import numpy as np
import cv2
import os
# 定義文件名和頁(yè)面
page_id = 38
filename = "SLB-2023-Annual-Report.pdf"
# 閱讀文檔
doc = fitz. open (os.path.join(document_source_dir,filename))
page = doc.load_page(page_id)
pix = page.get_pixmap(dpi= 300 )
img_rgb = np.frombuffer(pix.samples, dtype=np.uint8).reshape((pix.height, pix.width, pix.n))
img_page = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2BGR)此代碼使用PyMuPDF從PDF文件加載特定頁(yè)面,以高分辨率呈現(xiàn),并將其轉(zhuǎn)換為適合OpenCV處理的BGR圖像數(shù)組。
B.布局檢測(cè)
現(xiàn)在,讓我們編寫(xiě)如下布局檢測(cè)代碼。
def layout_detection(img_doc):
return yolo_model.predict(
source=img_doc,
classes=[6,8],
cnotallow=0.25,
iou=0.45)
layout_results = layout_detection(img_page)
show_layout_detection(layout_results, img_page)在此步驟中,我們專門(mén)過(guò)濾檢測(cè)到的布局元素,使其僅包含與嵌入的相關(guān)視覺(jué)區(qū)域相對(duì)應(yīng)的類標(biāo)簽6(表格)和8(圖形)。

圖片來(lái)源:SLB 2023年年度報(bào)告第37頁(yè)(來(lái)源:報(bào)告)
在這里,我們成功定位了相關(guān)的視覺(jué)對(duì)象,例如圓形圖和表格,這些將用于下游的視覺(jué)嵌入。
C. 提取并保存
接下來(lái),讓我們從圖像中檢索本地化的表格和圖片區(qū)域,并用白色遮罩它們以將它們從原始頁(yè)面視圖中刪除。
def extract_and_masking_images(img_doc, layout_results):
height, width, _ = img_doc.shape
extracted_imgs = []
for box in layout_results[0].boxes:
x, y, w, h = map(int,box.xywh[0]) #矩形坐標(biāo)(中心x、y、寬度、高度)
# 計(jì)算左上角(x_min, y_min)
x_min = x - w // 2
y_min = y - h // 2
x_max = x_min + w
y_max = y_min + h
# 將坐標(biāo)夾緊到圖像邊界
x_start = max(0, x_min)
y_start = max(0, y_min)
x_end = min(width, x_max)
y_end = min(height, y_max)
# 如果區(qū)域無(wú)效,則跳過(guò)
if x_start >= x_end or y_start >= y_end:
continue
# 將圖像提取到extracted_imgs數(shù)組中
extracted_imgs.append(img_doc[y_start:y_end, x_start:x_end].copy())
#將區(qū)域設(shè)置為白色
img_doc[y_start:y_end, x_start:x_end] = [255, 255, 255]
return extracted_imgs, img_doc
extracted_imgs, img_page = extract_and_masking_images(img_page, layout_results)
display_img_array(img_page)此函數(shù)從文檔圖像中提取檢測(cè)到的表格和圖片區(qū)域,并用白色遮罩這些區(qū)域,返回裁剪后的視覺(jué)效果和更新后的圖像。更新后的頁(yè)面圖像如下所示。

圖片來(lái)源:SLB 2023年年度報(bào)告第37頁(yè)(來(lái)源:報(bào)告)
接下來(lái),讓我們使用如下代碼保存提取的圖形。
import os
import cv2
def save_img_files(extracted_imgs, filename, page_id):
#目標(biāo)路徑
save_path = os.path.join(img_dir, filename, f"page_{page_id}/")
# 確保目錄存在
os.makedirs(os.path.dirname(save_path), exist_ok=True)
# 保存圖像
for i in range(len(extracted_imgs)):
cv2.imwrite(save_path+f"fig_{i}.jpg", extracted_imgs[i])
save_img_files(extracted_imgs, filename, page_id)此代碼將提取的圖形和表格圖像保存到指定的本地目標(biāo)目錄中img_dir。
D.文本OCR
在此步驟中,讓我們使用帶有pytesseract的普通OCR從屏蔽文檔圖像中提取剩余的文本信息。
import pytesseract
text = pytesseract.image_to_string(img_page)
print(text)結(jié)果如下:
Short-Term Cash Incentive Awards
We pay performance-based short-term (annual) cash incentives to
our executives to foster a results-driven, pay-for-performance culture,
and to align executives’ interests with those of our shareholders. STI
awards are earned according to the achievement of quantitative
Company financial and non-financial objectives, as well as strategic
objectives. Our Compensation Committee selects performance
measures that it believes support our strategy and strike a balance
between motivating our executives to increase near-term financial and
operating results and driving profitable long-term Company growth
and value for shareholders.
2022 STI Opportunity Mix
Compensation Discussion and Analysis
For 2023, 70% of our NEOs’ target STI opportunity was based on
achieving quantitative Company financial objectives, 10% was based
on achieving quantitative Company non-financial objectives, and 20%
was based on strategic personal objectives. The financial portion of
the target plan was evenly split between adjusted EBITDA and free
cash flow performance goals. The total maximum STI payout for 2023
was 200% of target—consistent with 2022—and the weighted payout
range for each metric as a percentage of target is reflected by the outer
bars in the 2023 STI Opportunity Mix chart below.
2023 STI Opportunity Mix
?>
?>
In January 2023, our Compensation Committee determined to leave the target STI opportunity for all NEOs unchanged from 2022, following
a review of market data indicating that our NEOs’ target STI opportunity (as a percentage of base salary) was competitively positioned. As a
result, the 2023 target STI opportunity for our CEO was 150% of his base salary and for our other NEOs it was 100% of base salary.
The following table reflects our NEOs’ full-year 2023 STI results, together with relevant weightings of the different components and payouts
under each component.
(1) Equals the sum of the financial, non-financial, and personal portions of the STI achieved, shown as a percentage of base salary.
(2) In January 2024, due to factors not contemplated in the 2023 forecast, our Compensation Committee applied a discretionary downward
adjustment to reduce all executive payouts by 5% under our 2023 STI plan.
2024 Proxy StatementE. 使用Milvus DB Client建立索引
在此步驟中,我們將使用Milvus數(shù)據(jù)庫(kù)進(jìn)行向量存儲(chǔ)和檢索;我們選擇使用文件milvus_file.db的簡(jiǎn)單本地實(shí)例來(lái)進(jìn)行此實(shí)驗(yàn),而不是使用可擴(kuò)展的生產(chǎn)級(jí)設(shè)置。
1.Retriever類
讓我們定義兩個(gè)檢索器:用于細(xì)粒度基于圖像的檢索的ColBERT樣式檢索器和用于基于文本的檢索的基本密集檢索器。
from pymilvus import MilvusClient, DataType
import numpy as np
import concurrent.futures
import os
import base64
class MilvusColbertRetriever:
def __init__(self, milvus_client, collection_name, img_dir, dim=128):
#使用Milvus客戶端、集合名稱和向量嵌入的維度初始化檢索器。
# If the collection exists, load it.
self.collection_name = collection_name
self.client = milvus_client
if self.client.has_collection(collection_name=self.collection_name):
self.client.load_collection(collection_name)
self.dim = dim
self.img_dir = img_dir
def create_collection(self):
# 在Milvus中創(chuàng)建一個(gè)新的集合來(lái)存儲(chǔ)嵌入。
#如果現(xiàn)有集合已存在,請(qǐng)刪除該集合,并為該集合定義架構(gòu)。
if self.client.has_collection(collection_name=self.collection_name):
self.client.drop_collection(collection_name=self.collection_name)
schema = self.client.create_schema(auto_id=True, enable_dynamic_field=True)
schema.add_field(field_name="pk", datatype=DataType.INT64, is_primary=True)
schema.add_field(field_name="vector", datatype=DataType.FLOAT_VECTOR, dim=self.dim)
schema.add_field(field_name="seq_id", datatype=DataType.INT16)
schema.add_field(field_name="doc_id", datatype=DataType.INT64)
schema.add_field(field_name="doc", datatype=DataType.VARCHAR, max_length=65535)
self.client.create_collection(collection_name=self.collection_name, schema=schema)
def create_index(self):
# 在向量字段上創(chuàng)建索引,以實(shí)現(xiàn)快速相似性搜索。
# 在使用指定參數(shù)創(chuàng)建新索引之前,釋放并刪除任何現(xiàn)有索引。
self.client.release_collection(collection_name=self.collection_name)
self.client.drop_index(collection_name=self.collection_name, index_name="vector")
index_params = self.client.prepare_index_params()
index_params.add_index(
field_name="vector",
index_name="vector_index",
index_type="IVF_FLAT",
metric_type="IP",
)
self.client.create_index(collection_name=self.collection_name, index_params=index_params, sync=True)
def search(self, data, topk):
# 對(duì)集合執(zhí)行向量搜索,以找到前k個(gè)最相似的文檔。
search_params = {"metric_type": "IP", "params": {}}
results = self.client.search(
self.collection_name,
data,
limit=int(50),
output_fields=["vector", "seq_id", "doc_id","$meta"],
search_params=search_params,
)
doc_meta = {}
for r_id in range(len(results)):
for r in range(len(results[r_id])):
entity = results[r_id][r]["entity"]
doc_id = entity["doc_id"]
if doc_id not in doc_meta:
doc_meta[doc_id] = {
"page_id": entity["page_id"],
"fig_id": entity["fig_id"],
"filename": entity["filename"],
}
scores = []
def rerank_single_doc(doc_id, data, client, collection_name):
#通過(guò)檢索單個(gè)文檔的嵌入并計(jì)算其與查詢的相似度來(lái)對(duì)其重新排序。
doc_colbert_vecs = client.query(
collection_name=collection_name,
filter=f"doc_id in [{doc_id}]",
output_fields=["seq_id", "vector", "doc"],
limit=1000,
)
doc_vecs = np.vstack(
[doc_colbert_vecs[i]["vector"] for i in range(len(doc_colbert_vecs))]
)
score = np.dot(data, doc_vecs.T).max(1).sum()
return (score, doc_id)
with concurrent.futures.ThreadPoolExecutor(max_workers=300) as executor:
futures = {
executor.submit(
rerank_single_doc, doc_id, data, self.client, self.collection_name
): doc_id
for doc_id in doc_meta.keys()
}
for future in concurrent.futures.as_completed(futures):
score, doc_id = future.result()
meta = doc_meta[doc_id]
img_path = os.path.join(self.img_dir, meta["filename"], f"page_{meta['page_id']}", f"fig_{meta['fig_id']}.jpg")
with open(img_path, "rb") as f:
img_base64 = base64.b64encode(f.read()).decode('utf-8')
scores.append({
"score":float(score),
"page_id": meta["page_id"],
"fig_id": meta["fig_id"],
"filename": meta["filename"],
"content": img_base64})
scores.sort(key=lambda x: x["score"], reverse=True)
if len(scores) >= topk:
return scores[:topk]
else:
return scores
def insert(self, data):
# 將文檔的ColBERT嵌入和元數(shù)據(jù)插入集合中。
#將數(shù)據(jù)作為多個(gè)向量(每個(gè)序列一個(gè))與相應(yīng)的元數(shù)據(jù)一起插入。
colbert_vecs = [vec for vec in data["colbert_vecs"]]
seq_length = len(colbert_vecs)
self.client.insert(
self.collection_name,
[
{
"vector": colbert_vecs[i],
"seq_id": i,
"doc_id": data["doc_id"] ,
"doc": "",
"page_id": data["page_id"],
"fig_id": data["fig_id"],
"filename": data["filename"],
}
for i in range(seq_length)
],
)
class MilvusBasicRetriever:
def __init__(self, milvus_client, collection_name, dim=1024):
# 使用Milvus客戶端、集合名稱和向量嵌入的維度初始化檢索器。
#如果集合存在,則加載之。
self.collection_name = collection_name
self.client = milvus_client
if self.client.has_collection(collection_name=self.collection_name):
self.client.load_collection(collection_name)
self.dim = dim
def normalize(self, vec):
#把向量規(guī)范化
norm = np.linalg.norm(vec)
if norm == 0:
return vec
return vec / norm
def create_collection(self):
# 在Milvus中創(chuàng)建一個(gè)新的集合來(lái)存儲(chǔ)嵌入。
#如果現(xiàn)有集合已存在,請(qǐng)刪除該集合,并為該集合定義架構(gòu)。
if self.client.has_collection(collection_name=self.collection_name):
self.client.drop_collection(collection_name=self.collection_name)
schema = self.client.create_schema(auto_id=True, enable_dynamic_field=True)
schema.add_field(field_name="pk", datatype=DataType.INT64, is_primary=True)
schema.add_field(field_name="vector", datatype=DataType.FLOAT_VECTOR, dim=self.dim)
schema.add_field(field_name="content", datatype=DataType.VARCHAR, max_length=65535)
self.client.create_collection(collection_name=self.collection_name, schema=schema)
def create_index(self):
#在向量字段上創(chuàng)建索引,以實(shí)現(xiàn)快速相似性搜索。
# 在使用指定參數(shù)創(chuàng)建新索引之前,釋放并刪除任何現(xiàn)有索引。
self.client.release_collection(collection_name=self.collection_name)
self.client.drop_index(collection_name=self.collection_name, index_name="vector")
index_params = self.client.prepare_index_params()
index_params.add_index(
field_name="vector",
index_name="vector_index",
index_type="IVF_FLAT", # or any other index type you want
metric_type="IP", # or the appropriate metric type
)
self.client.create_index(collection_name=self.collection_name, index_params=index_params, sync=True)
def search(self, data, topk):
#對(duì)集合執(zhí)行向量搜索,以找到前k個(gè)最相似的文檔。
normalized_data = self.normalize(data)
search_params = {"metric_type": "IP", "params": {}}
results = self.client.search(
self.collection_name,
[normalized_data],
limit=topk,
output_fields=["vector", "content","$meta"],
search_params=search_params,
)
return_arr = []
for hit in results[0]:
return_arr.append({
"score":hit.distance,
"page_id":hit["entity"]["page_id"],
"filename":hit["entity"]["filename"],
"content":hit["entity"]["content"]
})
return return_arr
def insert(self, data):
data["vector"] = self.normalize(np.array(data["vector"])).tolist()
self.client.insert(
self.collection_name,
[data]
)這段代碼定義了兩個(gè)與Milvus交互的檢索器類,以支持混合檢索:
- MilvusColbertRetriever專為使用ColBERT樣式的多向量嵌入進(jìn)行基于圖像的檢索而設(shè)計(jì)。它支持插入來(lái)自ColPALI的塊級(jí)視覺(jué)嵌入,使用后期交互(MaxSim)對(duì)結(jié)果進(jìn)行重新排序,并返回最匹配的圖像區(qū)域及其元數(shù)據(jù)和base64編碼的內(nèi)容。
- MilvusBasicRetriever用于基于文本的檢索。它存儲(chǔ)并搜索來(lái)自句子嵌入的單個(gè)密集向量,對(duì)余弦相似度進(jìn)行歸一化(通過(guò)內(nèi)積),并檢索最相關(guān)的文本塊及其源元數(shù)據(jù)。
兩種檢索器均可處理集合創(chuàng)建、索引和插入,從而實(shí)現(xiàn)對(duì)視覺(jué)和文本文檔內(nèi)容的靈活的多模式檢索。
2. Retriever設(shè)置
使用上面定義的檢索器類,讓我們初始化ColBERT風(fēng)格的圖像檢索器和基本文本檢索器,并將它們安裝在由milvus_file.db支持的本地Milvus實(shí)例上,以進(jìn)行存儲(chǔ)和檢索。
client = MilvusClient("milvus_file.db")
colbert_retriever = MilvusColbertRetriever(collection_name="colbert", milvus_client=client,img_dir=img_dir)
basic_retriever = MilvusBasicRetriever(collection_name="basic", milvus_client=client)對(duì)于初始化步驟,我們必須為兩個(gè)檢索器創(chuàng)建集合和索引,如下所示。
colbert_retriever.create_collection()
colbert_retriever.create_index()
basic_retriever.create_collection()
basic_retriever.create_index()3.圖像數(shù)據(jù)加載器
讓我們將使用ColPALI模型的圖像嵌入過(guò)程包裝到數(shù)據(jù)加載函數(shù)中。
from colpali_engine.utils.torch_utils import ListDataset
from torch.utils.data import DataLoader
from typing import List
from tqdm import tqdm
from PIL import Image
def create_image_embedding_loader(extracted_imgs):
images = [Image.fromarray(img_arr) for img_arr in extracted_imgs]
dataloader_images = DataLoader(
dataset=ListDataset[str](images),
batch_size=1,
shuffle=False,
collate_fn=lambda x: processor.process_images(x),
)
ds: List[torch.Tensor] = []
for batch_doc in tqdm(dataloader_images):
with torch.no_grad():
batch_doc = {k: v.to(colpali_model.device) for k, v in batch_doc.items()}
embeddings_doc = colpali_model(**batch_doc)
ds.extend(list(torch.unbind(embeddings_doc.to("cpu"))))
return ds
embedding_loader = create_image_embedding_loader(extracted_imgs)
embedding_loader此函數(shù)將提取的圖像區(qū)域列表包裝到DataLoader中,并使用ColPALI模型對(duì)其進(jìn)行處理,并返回每個(gè)圖像的多向量嵌入列表。返回列表的結(jié)果如下。
[tensor([[-0.0055, 0.0991, -0.0903, ..., -0.0474, -0.0042, -0.1138],
[-0.0067, 0.1064, -0.0488, ..., -0.0723, 0.0535, -0.0986],
[-0.0200, 0.1113, -0.1084, ..., -0.0747, 0.0447, -0.0786],
...,
[-0.0027, 0.0811, -0.1602, ..., 0.0354, -0.0112, -0.1670],
[-0.0557, -0.1099, 0.0128, ..., 0.0203, -0.0728, -0.0688],
[ 0.1025, 0.0145, -0.0420, ..., 0.0894, -0.0413, 0.1650]], dtype=torch.bfloat16),
tensor([[-0.0055, 0.0991, -0.0903, ..., -0.0474, -0.0042, -0.1138],
[-0.0067, 0.1064, -0.0488, ..., -0.0723, 0.0535, -0.0986],
[-0.0200, 0.1113, -0.1084, ..., -0.0747, 0.0447, -0.0786],
...,
[-0.0141, 0.0645, -0.1377, ..., 0.0430, -0.0061, -0.1338],
[-0.0835, -0.1094, 0.0049, ..., 0.0211, -0.0608, -0.0645],
[ 0.1396, 0.0549, -0.0669, ..., 0.0942, 0.0038, 0.1514]], dtype=torch.bfloat16),
tensor([[-0.0053, 0.0996, -0.0894, ..., -0.0471, -0.0042, -0.1128],
[-0.0068, 0.1060, -0.0491, ..., -0.0713, 0.0532, -0.0986],
[-0.0204, 0.1118, -0.1089, ..., -0.0752, 0.0444, -0.0791],
...,
[ 0.0330, 0.0398, -0.0505, ..., 0.0586, 0.0250, -0.1099],
[-0.0508, -0.0981, -0.0126, ..., 0.0183, -0.0791, -0.0713],
[ 0.1387, 0.0698, -0.0330, ..., 0.0238, 0.0923, 0.0337]], dtype=torch.bfloat16)]4. 圖像和文本索引
在此步驟中,讓我們將頁(yè)面圖像中提取的數(shù)據(jù)組件(包括圖像和文本)索引到數(shù)據(jù)庫(kù)集合中。
import random
for i in range(len(extracted_imgs)):
data = {
"colbert_vecs": embedding_loader[i].float().numpy(),
"doc_id": random.getrandbits(63),
"page_id": page_id,
"fig_id": i,
"filename": filename,
}
colbert_retriever.insert(data)此代碼將每個(gè)圖像嵌入到ColBERT樣式檢索器中,并附帶相關(guān)元數(shù)據(jù),分配唯一的doc_id并存儲(chǔ)頁(yè)面和圖形索引引用。
data = {
"vector": embed_model.encode(text),
"content": text,
"page_id": page_id,
"filename": filename
}
basic_retriever.insert(data)此代碼將文本嵌入及其元數(shù)據(jù)(包括內(nèi)容、頁(yè)面ID和文件名)插入到基本文本檢索器中進(jìn)行索引。
5. Retriever測(cè)試
最后,讓我們測(cè)試一下上面設(shè)置的檢索器。
query = "O.Le Peuch Payout Results in percentage according to SLB Financial Objectives"
batch_query = colpali_processor.process_queries([query]).to(device)
embeddings_query = torch.unbind(colpali_model(**batch_query).to("cpu"))[0].float().numpy()
colbert_retriever_result = colbert_retriever.search(embeddings_query, topk=3)
colbert_retriever_result此代碼使用ColPALI模型嵌入用戶查詢,并從ColBERT風(fēng)格的檢索器中檢索出最相關(guān)的前3個(gè)圖像區(qū)域。結(jié)果如下。
[{ 'score' : 20.13466208751197,
'page_id' : 38,
'fig_id' : 1,
'filename' : 'SLB-2023-Annual-Report.pdf' ,
'content' : '/9j/4AAQSkZJRgABAQAAAQABAAD/...' },
{ 'score' : 20.13466208751197,
'page_id' : 38,
'fig_id' : 1,
'filename' : 'SLB-2023-Annual-Report.pdf' ,
'content' : '/9j/4AAQSkZJRgABAQAAAQABAAD/...' },
{ 'score' : 15.088707166083623,
'page_id':41,
'fig_id':1,
'filename':'SLB-2023-Annual-Report.pdf',
'content':'/9j/4AAQSkZJRgABAQAAAQABAAD/...' }]接下來(lái),讓我們測(cè)試一下基本的檢索器。
query = "Potential Payout as a % of Target Opportunity"
basic_retriever_result = basic_retriever.search(embed_model.encode(query), topk=3)
basic_retriever_result此代碼使用文本嵌入模型對(duì)查詢進(jìn)行編碼,并從基本檢索器中檢索出最相關(guān)的前3個(gè)文本條目。結(jié)果如下。
[{'score': 0.6565427184104919,
'page_id': 38,
'filename': 'SLB-2023-Annual-Report.pdf',
'content': 'Short-Term Cash Incentive Awards\n\nWe pay performance-based short-term (annual) cash incentives to\nour ... (truncated)'},
{'score': 0.6533020734786987,
'page_id': 40,
'filename': 'SLB-2023-Annual-Report.pdf',
'content': "Free Cash Flow Targets and Results\n\nIn January 2023, our Compensation Committee considered SLB's\n2022 ... (truncated)"},
{'score': 0.6505128145217896,
'page_id': 59,
'filename': 'SLB-2023-Annual-Report.pdf',
'content': "Executive Compensation Tables\n\nChange in Control\n\nUnder our omnibus incentive plans, in the event of ... (truncated)"}]在這里,得分的差異是由于檢索方法造成的:ColBERT風(fēng)格的檢索器使用內(nèi)積(IP),從而產(chǎn)生更大的分?jǐn)?shù)值,而基本檢索器反映余弦相似度,通常會(huì)產(chǎn)生在-1和之間較小范圍內(nèi)的分?jǐn)?shù)+1。
聊天推理管道

作者插圖:聊天推理管道
聊天推理管道通過(guò)從文本和圖像嵌入中檢索相關(guān)內(nèi)容來(lái)處理用戶查詢,從而生成準(zhǔn)確且情境感知的響應(yīng)。在本節(jié)中,我們將實(shí)現(xiàn)查詢編碼、檢索和多模態(tài)步驟,以完成端到端問(wèn)答工作流程。
A. 準(zhǔn)備
對(duì)于聊天補(bǔ)全模型,我們使用Meta開(kāi)發(fā)的一款功能強(qiáng)大的多模態(tài)LLM Llama-4。我們將使用GROQ API Key來(lái)使用此Llama模型。代碼如下。
import os
from groq import Groq
# Groq API-Llama4
os.environ["GROQ_API_KEY"] = "<your-api-key>"
client_groq = Groq()接下來(lái),讓我們準(zhǔn)備一些處理函數(shù),如下所示。
def url_conversion(img_base64):
return f"data:image/jpeg;base64,{img_base64}"
def llama4_inference(messages, token=1024):
completion = client_groq.chat.completions.create(
model="meta-llama/llama-4-maverick-17b-128e-instruct",
messages=messages,
temperature=0.1,
max_completion_tokens=token,
top_p=1,
stream=True,
stop=None,
)
inference_result = ""
for chunk in completion:
chunk_inference = chunk.choices[0].delta.content or ""
inference_result += chunk_inference
text = inference_result
return text此代碼定義了一個(gè)函數(shù),用于將base64編碼的圖像轉(zhuǎn)換為可顯示的URL,以及一個(gè)llama推理函數(shù),用于通過(guò)Groq的API使用LLaMA 4 Maverick模型執(zhí)行流推理。
B. 用戶查詢和相關(guān)上下文
現(xiàn)在,讓我們定義用戶查詢并檢索相關(guān)上下文,如下所示。
user_query = "I want to know the payout"
batch_query = colpali_processor.process_queries([user_query]).to(device)
embeddings_query = torch.unbind(colpali_model(**batch_query).to("cpu"))[0].float().numpy()
colbert_retriever_result = colbert_retriever.search(embeddings_query, topk=3)
basic_retriever_result = basic_retriever.search(embed_model.encode(user_query), topk=3)此代碼使用ColPALI模型執(zhí)行文本到圖像檢索的查詢嵌入,并使用句子嵌入模型執(zhí)行文本到文本檢索,遵循與上一個(gè)檢索步驟相同的方法。
C.系統(tǒng)指令
接下來(lái),讓我們?yōu)槲覀兊膌lama模型定義系統(tǒng)指令提示,如下所示。
system_instruction = """
You are a helpful assistant designed to answer user queries based on document-related content.
You will be provided with two types of context:
1. Text-based context — extracted textual content from documents.
2. Image-based context — visual content (e.g., figures, tables, or screenshots) extracted from documents.
Your tasks are:
- Analyze the user query and determine the appropriate response using the available context.
- Decide whether the answer requires information from the image-based context.
If the image context is necessary to answer the query:
- Set "need_image" to True.
- Set "image_index" to the appropriate index of the image used (e.g., 0 for the first image, 1 for the second, and so on).
- Include a clear explanation or reasoning in the response.
If the image context is **not** needed:
- Set "need_image" to False.
- Set "image_index" to -1.
All responses **must be returned in strict JSON format**:
{"response": <string>, "need_image": <true|false>, "image_index": <int>}
If you are unsure or cannot answer based on the given context, clearly state that you do not know.
Examples:
{"response": "The chart in image 1 shows the revenue trend.", "need_image": true, "image_index": 1}
{"response": "The policy details are outlined in the text section.", "need_image": false, "image_index": -1}
"""該系統(tǒng)指令定義了助手應(yīng)如何基于兩種類型的文檔上下文(基于文本和基于圖像)回答用戶查詢。它指導(dǎo)模型判斷是否需要圖像來(lái)回答查詢,并以嚴(yán)格的JSON格式構(gòu)建響應(yīng),并包含一個(gè)標(biāo)志(need_image)和一個(gè)image_indexif applicable標(biāo)記。該指令確保文檔理解任務(wù)的響應(yīng)一致、可解釋且支持多模式感知。
D. 消息有效載荷
接下來(lái),讓我們創(chuàng)建將傳遞給Llama模型API的消息有效負(fù)載,如下所示。
#定義有效載荷內(nèi)容
payload_content = [{
"type": "text",
"text": f"User Query: {user_query}"
}]
# 構(gòu)造正在檢索的圖像URL
for i in range(len(colbert_retriever_result)):
img_payload = {
"type": "image_url",
"image_url": {"url":url_conversion(colbert_retriever_result[i]["content"])}
}
payload_content.append(img_payload)
# 構(gòu)建基于文本的上下文
for i in range(len(basic_retriever_result)):
txt_payload = {
"type": "text",
"text": f"Text-based Context #{i+1}:\n{basic_retriever_result[i]['content']}"
}
payload_content.append(txt_payload)
#創(chuàng)建最終消息形式
messages = [
{
"role": "system",
"content": system_instruction
},
{
"role": "user",
"content": payload_content
}
]此代碼通過(guò)將用戶查詢、檢索到的圖像(以base64 URL的形式)和基于文本的上下文組合成結(jié)構(gòu)化消息格式,構(gòu)建LLM的輸入負(fù)載。然后,它將這些內(nèi)容與系統(tǒng)指令一起包裝,形成最終的messages推理輸入。
構(gòu)造messages如下。
[{'role': 'system',
'content': '\nYou are a helpful assistant designed to answer user queries based on document-related content.\n\nYou will be provided with two types of context:\n1. Text-based ... (truncated)'},
{'role': 'user',
'content': [{'type': 'text',
'text': 'User Query: I want to know the payout'},
{'type': 'image_url',
'image_url': {'url': 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/...'}},
{'type': 'image_url',
'image_url': {'url': 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/...'}},
{'type': 'image_url',
'image_url': {'url': 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/...'}},
{'type': 'text',
'text': 'Text-based Context #1:\nExecutive Compensation Tables\n\nSummary Compensation Table\n\nThe following table sets forth information regarding the total compensation ... (truncated)'},
{'type': 'text',
'text': 'Text-based Context #2:\nExecutive Compensation Tables\n\nGrants of Plan-Based Awards in 2023\n\nThe following table provides additional information regarding cash ... (truncated)'},
{'type': 'text',
'text': 'Text-based Context #3:\nPay vs. Performance Comparison\n\nPay vs. Performance Comparison\n\nAs discussed in the CD&A above, our Compensation Committee has implemented ... (truncated)'}]}E.模型推理
現(xiàn)在,讓我們使用Llama模型來(lái)預(yù)測(cè)響應(yīng),如下所示。
import json
import re
chat_result = llama4_inference(messages)
chat_result = re.findall( r'\{[^{}]+\}' , chat_result)
chat_result = json.loads(chat_result[- 1 ])
chat_result此代碼運(yùn)行LLM推理,使用正則表達(dá)式從輸出中提取最后的JSON格式的響應(yīng),并將其解析為Python字典以供進(jìn)一步使用。
由此推論可得出如下結(jié)果。
{'response': 'The payout varies based on the performance metric. For Relative TSR Percentile Rank, Delta ROCE, and FCF Margin, the payouts are illustrated in the provided graphs. For example, at a Relative TSR Percentile Rank of 60%, the payout is 60%; at a Delta ROCE of 0 bps, the payout is 100%; and at an FCF Margin of 10%, the payout is 100%.',
'need_image': True,
'image_index': 0}F. 輸出響應(yīng)
下一步是按如下方式構(gòu)建輸出響應(yīng)。
if chat_result[ "need_image" ]:
img_content = colbert_retriever_result[chat_result[ 'image_index' ]][ 'content' ]
else :
img_content = ""
output_response = {
"response" :chat_result[ "response" ],
"need_image" :chat_result[ "need_image" ],
"img_base64" :img_content
}
output_response此代碼檢查L(zhǎng)LM響應(yīng)是否需要圖像;如果需要,則從ColBERT檢索器結(jié)果中檢索相應(yīng)的base64圖像內(nèi)容。然后,它會(huì)構(gòu)建一個(gè)最終響應(yīng)字典,其中包含答案文本、圖像標(biāo)志以及圖像內(nèi)容(如果適用)。
最終構(gòu)建的響應(yīng)如下。
{'response': 'The payout varies based on the performance metric. For Relative TSR Percentile Rank, Delta ROCE, and FCF Margin, the payouts are illustrated in the provided graphs. For example, at a Relative TSR Percentile Rank of 60%, the payout is 60%; at a Delta ROCE of 0 bps, the payout is 100%; and at an FCF Margin of 10%, the payout is 100%.',
'need_image': True,
'img_base64': '/9j/4AAQSkZJRgABAQAAAQABAAD/...'}評(píng)估
在本節(jié)中,我們將討論我們提出的多模態(tài)RAG管道和標(biāo)準(zhǔn)純文本RAG管道之間的定性比較,重點(diǎn)介紹檢索相關(guān)性和答案質(zhì)量方面的關(guān)鍵差異,特別是對(duì)于基于視覺(jué)的查詢。



作者插圖:我們的管道與常見(jiàn)的RAG管道的比較
我們的定性比較表明,多模態(tài)RAG流程比標(biāo)準(zhǔn)的純文本RAG系統(tǒng)能夠提供更準(zhǔn)確的答案,尤其是在涉及表格、圖形和圖表等結(jié)構(gòu)化視覺(jué)內(nèi)容的查詢時(shí)。標(biāo)準(zhǔn)RAG流程依賴OCR將文檔視覺(jué)內(nèi)容轉(zhuǎn)換為純文本,這通常會(huì)導(dǎo)致空間結(jié)構(gòu)的丟失和關(guān)鍵信息的誤解。
相比之下,我們的系統(tǒng)結(jié)合了基于ColPALI的圖像檢索、用于布局檢測(cè)的YOLO DocLayNet以及標(biāo)準(zhǔn)文本嵌入,從而同時(shí)保留視覺(jué)和語(yǔ)義上下文。這使得它能夠準(zhǔn)確地檢索和推理基于OCR的流程通常會(huì)遺漏的內(nèi)容,凸顯了真正多模態(tài)方法的有效性。
進(jìn)一步的演示
我開(kāi)發(fā)了一個(gè)簡(jiǎn)單的基于Chainlit的應(yīng)用程序來(lái)總結(jié)我們的實(shí)驗(yàn)。以下是該Web應(yīng)用程序的前端概覽。

作者插圖:多模式RAG應(yīng)用程序的前端
通過(guò)此應(yīng)用程序,聊天機(jī)器人能夠檢索文本和圖像信息來(lái)回答用戶查詢。當(dāng)用戶查詢相關(guān)時(shí),它還可以顯示相關(guān)圖像,以增強(qiáng)理解并提供更清晰的背景信息。
為了復(fù)制此Web應(yīng)用程序及其對(duì)應(yīng)的后端服務(wù)器,我創(chuàng)建了一個(gè)GitHub存儲(chǔ)庫(kù),你可以在此處訪問(wèn)。此存儲(chǔ)庫(kù)完全復(fù)制了我們的實(shí)驗(yàn),包括完整的知識(shí)庫(kù)索引管道以及端到端部署所需的所有組件。
結(jié)論
在本文中,我們構(gòu)建了一個(gè)多模態(tài)RAG系統(tǒng),該系統(tǒng)結(jié)合了用于基于圖像的檢索的ColPALI和用于視覺(jué)區(qū)域檢測(cè)的YOLO-DocLayNet,從而突破了傳統(tǒng)純文本檢索的局限性。通過(guò)實(shí)際結(jié)果演示,我們展示了如何將文本和視覺(jué)上下文相結(jié)合,在文檔問(wèn)答任務(wù)中提供更準(zhǔn)確、更具有上下文感知的答案。
參考文獻(xiàn)
- Faysse, M.,Sibille, H.,Wu, T.,Omrani, B.,Viaud, G.,Hudelot, C.和Colombo, P.(2024)。ColPali:基于視覺(jué)語(yǔ)言模型的高效文檔檢索。arXiv預(yù)印本arXiv:2407.01449。地址:https ://arxiv.org/abs/2407.01449。
- Pfitzmann, B.,Auer, C.,Dolfi, M.,Nassar, AS和Staar, P.(2022)。DocLayNet:用于文檔布局分割的大型人工注釋數(shù)據(jù)集。載于第28屆ACM SIGKDD知識(shí)發(fā)現(xiàn)與數(shù)據(jù)挖掘會(huì)議論文集(第3743-3751頁(yè))。
- Reimers, N.和Gurevych, I.(2020)。利用知識(shí)蒸餾將單語(yǔ)句子嵌入多語(yǔ)言化。載于2020年自然語(yǔ)言處理實(shí)證方法會(huì)議(EMNLP)論文集。計(jì)算語(yǔ)言學(xué)協(xié)會(huì)。地址:https ://arxiv.org/abs/2004.09813。
譯者介紹
朱先忠,51CTO社區(qū)編輯,51CTO專家博客、講師,濰坊一所高校計(jì)算機(jī)教師,自由編程界老兵一枚。
原文標(biāo)題:ColPALI Meets DocLayNet: A Vision-Aware Multimodal RAG for Document-QA,作者:Abu Hanif Muhammad Syarubany























