:BM25召回、BERT重排與答案span抽取)
簡介FinBERT-QA 是一套面向自然語言處理與信息檢索學(xué)習(xí)者的金融問答檢索方案聚焦于從大量金融段落中為查詢精準(zhǔn)定位答案這一任務(wù)。項目先用 Lucene 工具包為每個查詢召回前 50 個候選答案再以預(yù)訓(xùn)練 BERT 模型對候選答案重排并采用 Transfer and Adapt 思路先在通用問答數(shù)據(jù)上微調(diào)再借助 FiQA 數(shù)據(jù)集適配金融領(lǐng)域最終在 nDCG、MRR、Precision 三項排序指標(biāo)上平均提升約 20%。壓縮包共 62 個文件、約 142.71MB含 9 個 Python 腳本覆蓋數(shù)據(jù)處理、訓(xùn)練、預(yù)測與評測、23 個 pickle 序列化特征與模型文件、4 個 tsv 數(shù)據(jù)集與索引文件另有 2 個 Jupyter Notebook 用于檢索分析與數(shù)據(jù)流程演示配合 Dockerfile 與 requirements 可復(fù)現(xiàn)實驗環(huán)境。已有 2091 人學(xué)習(xí)下載適合想理解“召回重排”兩階段問答架構(gòu)、上手 BERT 微調(diào)與排序評估的中高級讀者參考。1. 金融問答為什么不能直接套用通用 BERT一句話里的數(shù)字和立場都是坑把在 SQuAD 上刷到 F1 90 以上的通用 BERT 直接搬到金融領(lǐng)域問答準(zhǔn)確率常常掉到及格線以下這是很多做信息檢索和問答的團(tuán)隊踩過的第一個坑。金融文本里的答案往往不是一個干凈的實體而是一段帶條件的觀點、一組對比數(shù)字甚至是一句“要看情況”。FinBERT-QA 的思路不是把模型換得更大而是承認(rèn)金融問答本質(zhì)上是“先找對段落、再抽出答案”的兩段式任務(wù)用預(yù)訓(xùn)練 BERT 分別微調(diào)檢索器與閱讀器并在 FiQA 數(shù)據(jù)集上把這兩段各自校準(zhǔn)。這套拆分為什么站得住、兩段各自怎么實現(xiàn)值得從底層拆一遍。2. Retriever 的兩段式設(shè)計BM25 召回加 BERT 重排的工程動機(jī)先給結(jié)論FinBERT-QA 的檢索階段不是讓 BERT 直接在全量語料里算相似度而是先用 BM25 這類詞法檢索把候選段落從數(shù)萬條壓到幾十條再用微調(diào)后的 BERT 對這幾十條做相關(guān)性重排。這個設(shè)計不是偷懶而是被計算復(fù)雜度和金融語料特性同時逼出來的。2.1 為什么粗排不能省BERT 的平方復(fù)雜度與候選規(guī)模BERT 的 self-attention 對序列長度是平方復(fù)雜度把一個問題和一個段落拼成 512 長度的輸入跑一次前向在單張普通 GPU 上大約幾毫秒到十幾毫秒。FiQA 這類數(shù)據(jù)集里每個問題平均對應(yīng)近九十個候選答案段落如果對每個候選都跑一次 BERT 打分一個問題的檢索開銷就是幾百毫秒放到整個測試集上直接不可接受。更關(guān)鍵的是候選段落大多和問題毫無關(guān)系讓 BERT 去逐條判斷“不相關(guān)”是巨大的算力浪費(fèi)。BM25 的價值在這里就體現(xiàn)出來了它靠詞頻和逆文檔頻率做倒排索引百萬級文檔的召回可以在毫秒級完成代價是它只認(rèn)字面匹配對同義改寫和隱含語義無能為力。金融問答里“美聯(lián)儲加息對銀行股的影響”和“利率上行如何傳導(dǎo)到金融板塊估值”這種同義表達(dá)BM25 很容易漏掉。所以合理的分工是BM25 負(fù)責(zé)高召回、低成本地把候選池縮小兩個數(shù)量級BERT 負(fù)責(zé)在這個小池子里做語義級的精排。常見的實現(xiàn)里召回階段取 top 50 到 top 100精排階段再截斷到 top 10 到 top 20 交給閱讀器。提示粗排的召回率是整條鏈路的硬上限。如果 BM25 的 top 100 里根本不含正確段落后面 BERT 再強(qiáng)也救不回來。調(diào)參時先盯 Recall100別急著看精排指標(biāo)。2.2 Pointwise、Pairwise、Listwise三種微調(diào)范式的目標(biāo)函數(shù)差異檢索器微調(diào)的核心問題是怎么把一個“問題-段落”對變成一個可訓(xùn)練的監(jiān)督信號。FinBERT-QA 里討論的三種范式差異不在模型結(jié)構(gòu)而在損失函數(shù)如何構(gòu)造樣本之間的關(guān)系。Pointwise 最直白把每個“問題-段落”對獨(dú)立送進(jìn) BERT做二分類判斷相關(guān)與否。它的好處是實現(xiàn)簡單、推理時可以并行打分缺點是它只學(xué)“這個段落相關(guān)嗎”不學(xué)“這個段落比那個段落更相關(guān)”排序能力靠分?jǐn)?shù)絕對值撐訓(xùn)練數(shù)據(jù)里正負(fù)比例失衡時容易退化成全部預(yù)測負(fù)類。Pairwise 把正樣本和負(fù)樣本組成一對只要求正樣本的分?jǐn)?shù)比負(fù)樣本高出一個 margin不關(guān)心分?jǐn)?shù)的絕對大小。這更貼合排序任務(wù)的目標(biāo)因為檢索評估看的是相對順序MRR、NDCG不是絕對相關(guān)性。缺點是樣本對的數(shù)量隨候選數(shù)平方增長訓(xùn)練時要控制采樣。Listwise 把一個問題下的所有候選作為一個列表用 softmax 把分?jǐn)?shù)轉(zhuǎn)成概率分布再用交叉熵對齊相關(guān)性標(biāo)簽。它最接近真實排序目標(biāo)但需要每個問題都帶完整的候選列表和相關(guān)性等級數(shù)據(jù)構(gòu)造成本最高。下面用 PyTorch 把三種損失的核心邏輯寫出來模型主體都是同一個 BERT 編碼器差別只在輸出層和 loss 上。import torch import torch.nn as nn from transformers import BertForSequenceClassification class PointwiseRetriever(nn.Module): def __init__(self, model_namebert-base-uncased): super().__init__() # 單塔結(jié)構(gòu)query 和 passage 用 [SEP] 拼接后一起進(jìn) BERT # num_labels2 表示相關(guān) / 不相關(guān)二分類 self.bert BertForSequenceClassification.from_pretrained( model_name, num_labels2 ) def forward(self, input_ids, attention_mask, token_type_ids, labelsNone): out self.bert( input_idsinput_ids, attention_maskattention_mask, token_type_idstoken_type_ids, labelslabels, ) # logits[:, 1] 是相關(guān)這一類的分?jǐn)?shù)拿來排序 return out.logits[:, 1], out.loss這段代碼的關(guān)鍵在num_labels2和最后的logits[:, 1]。二分類用的是標(biāo)準(zhǔn)交叉熵labels是 0/1 的硬標(biāo)簽。為什么不用回歸去擬合一個 0 到 1 的相關(guān)性分?jǐn)?shù)因為 FiQA 的相關(guān)性標(biāo)注本身是離散等級硬標(biāo)簽加交叉熵對標(biāo)注噪聲更魯棒回歸容易被個別標(biāo)錯的樣本帶偏。參數(shù)上token_type_ids在單塔結(jié)構(gòu)里其實可以全 0因為它區(qū)分不了 query 和 passage真正區(qū)分靠的是[SEP]的位置。def pairwise_loss(pos_score, neg_score, margin0.5): # pos_score / neg_score: [batch]正負(fù)樣本各自的打分 # 只要求正樣本比負(fù)樣本高出 margin不約束分?jǐn)?shù)絕對值 return torch.relu(margin neg_score - pos_score).mean()Pairwise 的 margin 取 0.5 是經(jīng)驗值。margin 太小模型學(xué)不到區(qū)分度margin 太大正樣本分?jǐn)?shù)被推得過高反而過擬合到少數(shù)簡單樣本。訓(xùn)練時每個 batch 里正負(fù)對的比例建議控制在 1:4 到 1:8負(fù)樣本從 BM25 召回但未標(biāo)注為相關(guān)的段落里采這就是所謂的“難負(fù)樣本”。def listwise_loss(scores, labels, tau1.0): # scores: [batch, num_candidates]每個問題下所有候選的分?jǐn)?shù) # labels: 同形狀相關(guān)性等級0/1 或分級 log_probs torch.log_softmax(scores / tau, dim-1) # 用相關(guān)性標(biāo)簽做加權(quán)正樣本越靠前 loss 越小 return -(labels * log_probs).sum(dim-1).mean()tau是溫度系數(shù)控制分布的陡峭程度。tau越小softmax 越接近 argmax訓(xùn)練越激進(jìn)tau越大分布越平滑梯度更穩(wěn)。Listwise 的坑在于如果 labels 全是 0/1 且正樣本只有一個log_softmax會把大量概率分給負(fù)樣本需要配合 label smoothing 或者對負(fù)樣本降權(quán)。2.3 訓(xùn)練數(shù)據(jù)構(gòu)造負(fù)采樣比例決定檢索器的實際表現(xiàn)模型結(jié)構(gòu)定下來之后真正拉開效果差距的是訓(xùn)練數(shù)據(jù)怎么造。FinBERT-QA 用的 FiQA 數(shù)據(jù)集只標(biāo)了哪些段落和問題相關(guān)沒標(biāo)“不相關(guān)”的負(fù)樣本負(fù)樣本得自己采。常見的做法是混合三種來源BM25 召回里排名靠前但未標(biāo)注相關(guān)的難負(fù)樣本、隨機(jī)從語料里抽的易負(fù)樣本、以及同一問題下其他不相關(guān)段落。難負(fù)樣本讓模型學(xué)會區(qū)分“看起來像但其實不對”的段落易負(fù)樣本防止模型把所有分?jǐn)?shù)都壓到負(fù)類。負(fù)正比不是越高越好。比例到 1:16 以上時pointwise 的交叉熵會被負(fù)類主導(dǎo)模型傾向于輸出低分排序時區(qū)分度反而下降。實踐中 1:4 到 1:8 是比較穩(wěn)的區(qū)間具體要看 BM25 的召回質(zhì)量。如果 BM25 本身召回就很差難負(fù)樣本里混進(jìn)了真正相關(guān)的段落模型會被教壞這時候要么先修 BM25要么把難負(fù)樣本的權(quán)重降下來。3. Reader 的抽取式答案機(jī)制SQuAD 式 span 預(yù)測如何在金融語料上收斂檢索器把候選段落排好序之后閱讀器的任務(wù)是從 top-k 段落里定位答案的起止位置。FinBERT-QA 的閱讀器沿用 BERT 在 SQuAD 上的做法把問題和段落拼成一個序列讓模型輸出兩個概率分布分別表示答案起點和終點落在每個 token 上的概率。聽起來簡單但金融語料的答案形態(tài)和 SQuAD 差別很大直接套用會出問題。3.1 起止指針網(wǎng)絡(luò)BERT 輸出如何變成兩個概率分布BERT 的最后一層對每個 token 輸出一個 768 維向量閱讀器在它上面接兩個線性層一個映射到起點 logits一個映射到終點 logits維度都是序列長度。訓(xùn)練時用真實答案的起止 token 位置做交叉熵推理時對所有合法的 (start, end) 組合算start_prob * end_prob取分?jǐn)?shù)最高的那個 span。這里有個容易忽略的細(xì)節(jié)起點和終點的聯(lián)合概率不是獨(dú)立的start end這個約束必須在解碼時強(qiáng)制。如果只對起點和終點分別取 argmax很可能得到start end的非法 span。常見的實現(xiàn)是在解碼時遍歷所有滿足start end且長度不超過max_answer_len的組合取聯(lián)合概率最大的。import torch from transformers import BertForQuestionAnswering model BertForQuestionAnswering.from_pretrained(bert-base-uncased) def decode_span(start_logits, end_logits, max_answer_len30): # start_logits / end_logits: [1, seq_len] start_probs torch.softmax(start_logits, dim-1) end_probs torch.softmax(end_logits, dim-1) best None seq_len start_probs.size(-1) for s in range(seq_len): # 強(qiáng)制 end start且答案長度受限 for e in range(s, min(s max_answer_len, seq_len)): score start_probs[0, s] * end_probs[0, e] if best is None or score best[0]: best (score.item(), s, e) return best # (score, start_idx, end_idx)max_answer_len的取值很關(guān)鍵。SQuAD 的答案大多是短語長度限制在 30 以內(nèi)效果很好但金融問題的答案經(jīng)常是一整句甚至幾句限制太短會把正確答案截斷在中間模型輸出的 span 分?jǐn)?shù)反而比錯誤答案低。常見的做法是根據(jù)數(shù)據(jù)集里答案長度的分位數(shù)來定比如取 95 分位作為上限。參數(shù)start_probs * end_probs用的是概率乘積等價的 log 形式是log_start log_end數(shù)值上更穩(wěn)避免長序列下概率下溢。3.2 從 FiQA 的觀點型答案到可抽取 span 的轉(zhuǎn)換FiQA 的答案標(biāo)注和 SQuAD 有一個本質(zhì)區(qū)別SQuAD 的答案是原文里連續(xù)的一段子串FiQA 的答案常常是“觀點”需要從多個段落里綜合標(biāo)注的是“這個段落相關(guān)答案大致在這段話里”。直接拿這種標(biāo)注訓(xùn)練 span 抽取模型學(xué)到的起止位置會不穩(wěn)定因為同一段話里可能有多個合理的答案片段。FinBERT-QA 的處理思路是把閱讀器訓(xùn)練和檢索器訓(xùn)練解耦閱讀器先在一個標(biāo)準(zhǔn)的 SQuAD 式抽取數(shù)據(jù)集上預(yù)訓(xùn)練學(xué)會“給定問題和段落定位答案 span”這個通用能力然后在 FiQA 上做微調(diào)時只用那些標(biāo)注了明確答案片段的樣本標(biāo)注模糊的樣本降權(quán)或丟棄。這相當(dāng)于承認(rèn) FiQA 的標(biāo)注噪聲不硬讓模型去擬合不可靠的起止位置。這個取舍的代價是閱讀器的訓(xùn)練樣本變少對長尾問題的覆蓋不足。一個補(bǔ)償手段是把檢索器召回的多個相關(guān)段落都送進(jìn)閱讀器讓閱讀器在多個段落上分別抽 span再按檢索分?jǐn)?shù)和 span 分?jǐn)?shù)加權(quán)融合。這樣即使單段落的標(biāo)注不精確多段落投票也能穩(wěn)定結(jié)果。3.3 答案后處理把 span 還原成可讀答案閱讀器輸出的是 token 索引要還原成文本還有幾步去掉[CLS]、[SEP]這類特殊 token處理 WordPiece 的子詞拼接比如fin和##ance要拼回finance再根據(jù)原始文本的字符偏移映射回原文。這一步看起來是純工程但出錯率很高尤其是金融文本里的數(shù)字和縮寫。數(shù)字尤其要小心。BERT 的 WordPiece 分詞器會把2023拆成202和##3把3.5%拆成3、.、5、%。如果答案恰好在這些 token 邊界上截斷還原出來就是殘缺的數(shù)字。常見的做法是在預(yù)處理階段對數(shù)字做歸一化或者在解碼后檢查 span 兩端是否落在數(shù)字中間如果是就擴(kuò)展到完整的數(shù)字邊界。金融問答里一個數(shù)字錯了整個答案就失去意義這點比通用問答更敏感。4. FiQA 數(shù)據(jù)集的標(biāo)注結(jié)構(gòu)為什么這份數(shù)據(jù)決定了系統(tǒng)的上限聊完兩階段模型得回到數(shù)據(jù)本身。FinBERT-QA 選 FiQA 作為金融問答的基準(zhǔn)不是因為它大而是因為它的標(biāo)注結(jié)構(gòu)恰好覆蓋了“檢索 抽取”兩段式任務(wù)需要的全部監(jiān)督信號。理解這份數(shù)據(jù)的組織方式才能理解模型指標(biāo)為什么卡在某個水平上不去。4.1 問題-段落-答案的三元組結(jié)構(gòu)FiQA 的每條樣本可以拆成三層一個金融問題、若干候選答案段落、以及每個段落與問題的相關(guān)性等級。問題和段落來自真實的金融社區(qū)問答段落是用戶回答的原始文本相關(guān)性標(biāo)注由人工判斷。這種結(jié)構(gòu)的特別之處在于它同時提供了檢索信號段落相不相關(guān)和抽取信號答案在段落里的位置剛好對應(yīng) FinBERT-QA 的兩個階段。層級內(nèi)容對應(yīng)任務(wù)典型量級問題層金融領(lǐng)域自然語言問題輸入數(shù)百條段落層社區(qū)回答原文每條幾十到幾百字檢索數(shù)萬條相關(guān)性層段落與問題的相關(guān)等級檢索監(jiān)督每個問題近九十條候選答案層段落內(nèi)的答案片段標(biāo)注抽取監(jiān)督覆蓋部分段落表格里的“典型量級”按公開描述的量級給出具體數(shù)字以數(shù)據(jù)集官方發(fā)布為準(zhǔn)。問題數(shù)只有數(shù)百條意味著訓(xùn)練集很小模型很容易過擬合候選段落數(shù)萬條意味著檢索階段的負(fù)樣本空間很大BM25 召回的 top-100 里真正相關(guān)的可能只有個位數(shù)。這兩個特點疊加決定了 FinBERT-QA 必須靠預(yù)訓(xùn)練 BERT 的遷移能力來彌補(bǔ)數(shù)據(jù)量不足而不是從頭訓(xùn)練。4.2 劃分方式與標(biāo)簽噪聲為什么按問題劃分比按段落劃分更靠譜數(shù)據(jù)劃分看起來是小事實際上直接決定評估指標(biāo)可不可信。FiQA 的候選段落是跨問題共享的同一個段落可能同時出現(xiàn)在多個問題的候選列表里。如果按段落隨機(jī)劃分訓(xùn)練集和測試集同一個段落在訓(xùn)練時見過、測試時又出現(xiàn)模型會記住段落的表面特征而不是學(xué)會匹配指標(biāo)虛高。正確的做法是按問題劃分訓(xùn)練集和測試集的問題完全不重疊測試集里的問題在訓(xùn)練時從未出現(xiàn)過。這樣評估的是模型對未見問題的泛化能力接近真實上線場景。代價是測試集里會出現(xiàn)訓(xùn)練時沒見過的段落檢索器的詞表覆蓋和閱讀器的 span 定位都要真正泛化指標(biāo)會比按段落劃分低不少但這個低才是真實的。標(biāo)簽噪聲是另一個繞不開的問題。相關(guān)性標(biāo)注是人工判斷的不同標(biāo)注者對“這個段落算不算回答了問題”的標(biāo)準(zhǔn)不一致尤其是金融問題經(jīng)常有多個合理答案。這種噪聲在檢索階段表現(xiàn)為同一個問題的正樣本數(shù)量波動很大有的問題標(biāo)了十幾個相關(guān)段落有的只標(biāo)了一兩個。訓(xùn)練時如果對正樣本數(shù)量少的問題用同樣的損失權(quán)重模型會偏向正樣本多的問題。常見的補(bǔ)償是按問題做損失歸一化或者對正樣本過少的問題降采樣。4.3 MRR 與 NDCG評估指標(biāo)背后的排序邏輯檢索階段的評估不看準(zhǔn)確率看排序質(zhì)量因為下游閱讀器只消費(fèi) top-k。MRR 衡量的是第一個相關(guān)段落排在第幾位如果排第一貢獻(xiàn) 1排第二貢獻(xiàn) 0.5排第 n貢獻(xiàn) 1/n再對所有問題取平均。它關(guān)心的是“最快找到第一個對的”適合答案唯一或少數(shù)幾個的場景。NDCG 考慮的是整個排序列表每個位置的相關(guān)性貢獻(xiàn)按1/log2(位置1)折損再把所有位置加起來除以理想排序的得分。它關(guān)心的是“相關(guān)段落整體排得靠不靠前”對多個相關(guān)段落的場景更友好。金融問答里一個問題常有多個相關(guān)段落NDCG 比 MRR 更能反映真實體驗。這兩個指標(biāo)有個共同的盲區(qū)它們只評估檢索不評估最終答案對不對。檢索指標(biāo)高但閱讀器抽錯 span用戶看到的還是錯答案。所以完整的評估要分兩段看先看 Recallk 確認(rèn)正確段落進(jìn)了候選池再看閱讀器的 Exact Match 和 F1最后看端到端的答案準(zhǔn)確率。任何一段掉鏈子端到端指標(biāo)都會暴露。5. 訓(xùn)練與推理的常見坑從 loss 不降到答案跑偏的排查路徑這套兩階段架構(gòu)跑起來不難難的是訓(xùn)練過程中各種不報錯但效果不對的情況。下面幾條是實際調(diào)試?yán)锓磸?fù)出現(xiàn)的按“現(xiàn)象、原因、解決”拆開寫遇到時可以直接對照。第一條檢索器 loss 卡在 0.69 不動?,F(xiàn)象是訓(xùn)練幾個 epoch 后 loss 幾乎不變驗證集 MRR 和隨機(jī)猜差不多。原因是正負(fù)樣本比例嚴(yán)重失衡pointwise 的二分類被負(fù)類主導(dǎo)模型學(xué)會了全部輸出“不相關(guān)”。解決是先統(tǒng)計訓(xùn)練集的正負(fù)比把負(fù)正比壓到 1:8 以內(nèi)再檢查labels是不是真的按 batch 傳進(jìn)去了很多實現(xiàn)里 label 維度對不上會被廣播成全 0。第二條檢索指標(biāo)很高端到端答案準(zhǔn)確率卻很低?,F(xiàn)象是 Recall10 到了 0.8 以上但用戶看到的答案經(jīng)常答非所問。原因是閱讀器訓(xùn)練時喂的是“標(biāo)準(zhǔn)答案段落”推理時喂的是“檢索器實際召回的段落”兩者分布不一致閱讀器沒見過檢索器犯錯的樣本。解決是把檢索器在訓(xùn)練集上的召回結(jié)果直接作為閱讀器的訓(xùn)練輸入讓閱讀器學(xué)會在有噪聲的候選里定位答案這個做法本質(zhì)上是一種數(shù)據(jù)增強(qiáng)。第三條答案總是被截斷在半句話?,F(xiàn)象是閱讀器輸出的 span 明顯不完整經(jīng)常停在逗號或數(shù)字中間。原因有兩個一是max_answer_len設(shè)得太小二是 FiQA 的答案標(biāo)注本身就跨句而 span 抽取只能輸出連續(xù)片段。解決是先畫一下訓(xùn)練集答案長度的分布把上限設(shè)到 95 分位對于跨句答案在解碼后做一次后處理把 span 擴(kuò)展到完整句子邊界或者讓閱讀器對多個段落分別抽取再拼接。第四條驗證集指標(biāo)虛高上線后大幅下降。現(xiàn)象是本地驗證 F1 很漂亮換一批真實問題就崩。原因是數(shù)據(jù)劃分按段落做了隨機(jī)切分訓(xùn)練集和測試集共享段落模型記住了段落表面特征。解決是改成按問題劃分確保測試集的問題在訓(xùn)練時完全沒見過改完指標(biāo)會掉一截但那才是真實水平。第五條金融數(shù)字被 tokenizer 切碎導(dǎo)致答案錯誤?,F(xiàn)象是答案里的數(shù)字缺位或錯位比如3.5%變成5%。原因是 WordPiece 把數(shù)字拆成子詞span 邊界正好落在數(shù)字中間。解決是在預(yù)處理階段用占位符保護(hù)數(shù)字或者在解碼后檢查 span 兩端是否在數(shù)字內(nèi)部是則擴(kuò)展到數(shù)字完整邊界。金融問答里數(shù)字錯一位答案就完全錯了這條比通用問答更值得較真。注意這五條里有三條的根因都在數(shù)據(jù)而不是模型。調(diào)參之前先把數(shù)據(jù)分布、劃分方式、tokenizer 行為查一遍比盲目換學(xué)習(xí)率有效得多。6. 設(shè)計取舍兩段式架構(gòu)的邊界在哪里什么時候該換路線兩段式架構(gòu)最大的價值是把一個復(fù)雜問題拆成兩個可獨(dú)立評估、獨(dú)立優(yōu)化的子問題。檢索器負(fù)責(zé)“找對”閱讀器負(fù)責(zé)“抽準(zhǔn)”任何一段出問題都能通過分階段指標(biāo)定位而不是面對一個端到端黑盒猜。這種可觀測性在工程上比多拿一兩個點指標(biāo)更重要。但它的邊界也很清楚。第一檢索器的召回上限就是整個系統(tǒng)的上限BM25 加 BERT 重排這套組合對詞法匹配依賴仍然很重遇到大量同義改寫或者跨語言場景粗排就會漏。第二閱讀器只能抽連續(xù) span對需要跨段落綜合、需要推理計算的金融問題無能為力比如“比較兩家公司近三年毛利率變化”答案不在任何單個段落里。第三兩階段意味著兩次推理延遲是單階段的近兩倍對實時性要求高的場景不友好。判斷要不要換路線我一般看兩個信號。一是 Recall100 長期卡在 0.7 以下說明粗排已經(jīng)成為瓶頸這時候該考慮換成稠密檢索或者混合檢索把語義召回補(bǔ)上。二是端到端錯誤里超過一半是“檢索對了但閱讀器抽錯”說明 span 抽取的范式不適合這批數(shù)據(jù)該考慮生成式閱讀器或者抽取加生成的混合方案。這兩個信號都比單純看 F1 更有指導(dǎo)性。驗證方法上我習(xí)慣把評估拆成三層先單獨(dú)跑檢索器看 Recallk 和 NDCG再單獨(dú)跑閱讀器在“給定正確段落”條件下的 EM 和 F1最后跑端到端。三層指標(biāo)一起看才能分清是檢索的問題還是閱讀的問題。只看端到端指標(biāo)出了問題根本不知道從哪下手。從那以后我每次接這類兩階段問答任務(wù)都強(qiáng)制先把這三層評估腳本跑通再動模型血淚經(jīng)驗是曾經(jīng)在一個項目里調(diào)了兩周閱讀器最后發(fā)現(xiàn)是檢索器把正確段落排到了第 30 位開外。希望幫到你。本文還有配套的精品資源點擊獲取