與踩坑復盤)
在垂類模型的數(shù)據(jù)工程里前面幾個階段我們基本都在跟數(shù)據(jù)和標注較勁真正把“語義”這個事落地還是得到 Embedding 訓練這一步。這一階段討論的是怎么把一套領域相關的文本表示從通用模型手里接過來讓它能區(qū)分專業(yè)場景里那些微妙差異我的選擇是用雙編碼器架構配合 InfoNCE 損失函數(shù)來做整套訓練閉環(huán)。這篇文章記錄的就是這一個階段從架構選型、數(shù)據(jù)構造到實際訓練和踩坑的完整流程主要面向正在做垂類檢索、召回或者語義匹配的工程同學也適合準備把對比學習用在自己領域數(shù)據(jù)上的研究者參考。我會把原理和實操放在一起講有些結論看起來反直覺但跑完一輪實驗基本都能對上。1. 為什么垂類模型不能直接吃通用Embedding1.1 通用向量的“領域失明”問題先說我遇到的一個真實場景。早期為了快速出效果我直接拿來一個在大規(guī)模通用語料上預訓練好的文本向量模型用來做某個細分行業(yè)的單據(jù)檢索。日常運營人員的提問都是“這個單子能不能在臺賬里查到”標準單據(jù)名稱是像“某類設備維修記錄單”這種規(guī)范文本聽起來好像沒什么但實際跑出來的 top10 檢索結果非常失敗。失敗不在于模型本身能力差而在于通用模型訓練語料里這種垂直場景的數(shù)據(jù)占比太低。模型對“維修”、“單”這些詞的理解偏向日常生活對領域內(nèi)的同義改寫、簡寫和編號規(guī)則幾乎無感。同一個意思用戶輸入和庫里的正式名稱字面完全不像但語義上應該匹配。通用向量的語義空間是按開放域問題訓練出來的它不知道“質(zhì)保期”在這個行業(yè)里等同于“三包有效期”這種專有映射。這種問題在開放域搜索里可能不明顯因為通用語料本身覆蓋了各種問法。但一到垂類場景領域詞表是封閉的、句式是固定的、標準答案是明確的通用模型分配在“常見語義”上的容量就對不上了。所以做垂類模型數(shù)據(jù)工程到最后真正拉開差距的不是模型結構多新而是 Embedding 有沒有被領域數(shù)據(jù)重新校準過。1.2 用“語義空間偏移”理解垂類訓練的目標如果從訓練目標看垂類 Embedding 要做的事只有一件在這個封閉領域里重新度量“相似”。通用模型給每個文本一個向量這個向量在通用語義空間里是合理的但在垂類語義空間里不是。比如“卡”這個詞用在支付行業(yè)和用在機械行業(yè)代表完全不同的實體。想讓模型理解這一點只能讓它看足量的領域內(nèi)正負樣本通過損失函數(shù)把向量空間里的距離關系掰過來。我把這個階段看作數(shù)據(jù)工程鏈條中“從量到質(zhì)”的轉換前幾個階段解決的是數(shù)據(jù)有沒有、干不干凈Embedding 訓練解決的是用這些數(shù)據(jù)能不能形成有效的度量。你需要先接受一個前提通用 Embedding 可以作為初始權重但絕不應該作為終態(tài)。如果一開始就抱著“通用模型反正能跑湊合用吧”的心態(tài)后續(xù)所有召回優(yōu)化都是在有瑕疵的地基上打補丁。1.3 先做一輪低成本語義體檢在進入訓練之前建議先花半天時間做一次便宜的體檢也是我每個垂類項目都會重復的動作準備大概 50 條領域內(nèi)的真實 query以及一個對應的候選文檔池用通用 Embedding 做 recall10再把結果人工瞄一遍。不用做完整評估只要看 top10 里有多少是“類型對得上但內(nèi)容不對”或者“字面相關但語義不相關”。我見過的絕大多數(shù)垂類項目這一步之后都會得到同一個結論通用向量在領域內(nèi)會把字面相似性和語義相關性嚴重混淆。典型的例子是搜索“設備故障”和標準文檔里“機器運轉異常處理規(guī)范”字面完全不重疊但語義應該相關而通用模型往往排序很低。體檢的價值在于給你一個量化的基線。后面訓練完再測同一個體檢集提升多少一目了然。這一階段的所有工作本質(zhì)上都是在跟這個基線作對比。2. 雙編碼器架構拆解從結構選擇到部署約束2.1 為什么是“兩個塔”而不是一個交叉編碼器垂類語義匹配的技術棧里有兩個常用套路交叉編碼器和雙編碼器。交叉編碼器把 query 和文檔拼成一個序列用一個模型做完整交互效果好但慢得離譜因為它需要為每個候選對跑一次前向。雙編碼器把 query 和文檔分別編碼成兩個向量之后只需要算向量相似度可以用近似最近鄰檢索做大規(guī)模召回。我選雙編碼器核心原因是生產(chǎn)環(huán)境約束。線上一個 query 打到幾百上千萬的候選庫如果每條都過交叉編碼器響應時間直接爆炸。雙塔結構等于把重計算放在離線階段文檔塔可以提前把庫里所有文檔變成向量存進索引線上只需要編碼一次 query然后做一個向量檢索。這種“離線重、在線輕”的結構才是支撐高性能召回的唯一現(xiàn)實選擇。很多剛接觸的人會覺得雙塔丟失了交互信息不如交叉編碼器。這個批評沒有錯但要看場景。垂類搜索里的候選集往往很大需要的是先快速召回一個高質(zhì)量的子集語義精排再交給交叉編碼器這類重模型。雙塔負責“大海撈針”交叉編碼器負責“針尖對麥芒”兩者是配合關系不是替代關系。2.2 雙塔內(nèi)部的三個關鍵設計點第一個設計點是兩個塔是否共享參數(shù)。共享參數(shù)的好處是參數(shù)少、訓練穩(wěn)定適合 query 和文檔文本分布比較接近的場景比如檢索式問答。不共享參數(shù)的好處是每個塔可以適應不同文本風格適合 query 普遍短小、文檔普遍長且結構化的垂類場景。我在大多數(shù)垂類項目里選擇兩個塔結構相同但權重獨立因為 query 側的用詞和文檔側的規(guī)范術語分布差異太大了。第二個設計點是序列編碼的池化方式。文本經(jīng)過編碼器后得到一串 token 向量通常有幾種做法把它壓成一個句子向量。最粗糙的是用 [CLS] 位好一點的是對所有 token 做均值池化更好一點的是按 token 的權重加權求和。垂類文檔往往很長存在大量與核心語義無關的章節(jié)比如模板里的“注意事項”“參考文件”這種噪聲對均值池化傷害很大。我會在文檔塔上嘗試加權池化權重來自一個簡單訓練的領域詞表映射效果往往比默認池化高出不少。第三個設計點是向量歸一化。雙塔輸出的向量在算點積之前一定要做 L2 歸一化讓所有向量都落在單位球面上這樣點積等價于余弦相似度語義可比性才穩(wěn)定。如果不做歸一化向量模長會參與相似度計算導致長文檔天然占便宜排序結果會失真。這個細節(jié)看似小但對后續(xù)難負樣本挖掘和閾值設定影響巨大我見過好幾個項目因為漏掉這一步調(diào)了很久溫度參數(shù)都調(diào)不對。2.3 底層模型怎么選不要盲目追求大垂類場景的文本通常有個特點領域內(nèi)專用詞占比高、句式重復性強、單條文本長度波動大。底層預訓練模型的選擇上我的建議是先用中等規(guī)模的通用模型初始化再把領域語料繼續(xù)做少量預訓練。這個操作成本不高但收益穩(wěn)定尤其是那些領域術語跟通用詞義沖突大的場景比如法律、醫(yī)療、工業(yè)制造。大模型不一定更優(yōu)。模型越大訓練和線上推理成本越高而且垂類數(shù)據(jù)量通常有限大模型容易過擬合到少部分高頻 pattern 上。雙塔訓練的核心是讓兩個 encoder 學到“好匹配”的判別邊界而不是讓它們變得更加博學。領域知識由預訓練階段負責匹配能力由雙塔訓練負責這個分工想清楚之后模型選型就不會被參數(shù)規(guī)模帶跑。3. InfoNCE 損失函數(shù)一個公式怎么讓兩個塔“學會對齊”3.1 從實例到公式InfoNCE 到底在算什么雙塔架構定下來后核心問題就是怎么定義“拉近正樣本、推開負樣本”。我最早用的是三元組損失但很快就換成了 InfoNCE因為 InfoNCE 在訓練穩(wěn)定性、負樣本利用率和最終效果上都明顯更好。它本質(zhì)上在做一件很簡單的事給定一個 batch 里的 query把當前 batch 里唯一的正樣本從所有樣本中識別出來。如果用一句話說清楚 InfoNCE 的思路那就是把匹配問題變成分類問題每個 query 有一個正確答案其余 batch 內(nèi)的文檔都是錯誤答案讓模型學會從一堆候選中挑出那個正確的。寫成公式就是這個樣子[ L_{InfoNCE} -\log \frac{\exp(\text{sim}(q,p^)/\tau)}{\exp(\text{sim}(q,p^)/\tau) \sum_{i1}^{N-1} \exp(\text{sim}(q,p_i^-)/\tau)} ]其中 sim 是余弦相似度τ 是溫度系數(shù)p^ 是正樣本p_i^- 是負樣本。這個公式里面有一個關鍵設計分母中的負樣本數(shù)量越多模型需要區(qū)分的類別就越多學到的語義邊界就越細。這一特點正好匹配垂類檢索的訴求——因為真實召回面對候選集是百萬級的訓練時區(qū)分幾十個負樣本只是最小成本的模擬。建議訓練中用大 batch 來增加負樣本數(shù)量這也是 InfoNCE 的核心收益之一。batch 里每個 query 對應的正文檔對其他 query 來說恰好就是負樣本這一機制下產(chǎn)生的“batch 內(nèi)負樣本”數(shù)量等于 batch size 減一完全不需要額外構造和存儲訓練效率非常高。后續(xù)講數(shù)據(jù)構造時會再展開。3.2 溫度系數(shù) τ 的直覺理解與調(diào)參影響InfoNCE 公式里的溫度系數(shù) τ 是最容易被忽略、但影響最大的超參數(shù)。它的作用相當于給相似度打分做縮放分數(shù)除以一個更小的 τ會讓相似度分布的對比度變大相當于把模型的注意力集中到那些最難區(qū)分的高相似度負樣本上τ 太大則會讓所有分數(shù)的差異被抹平模型學不動τ 太小又會讓模型對噪聲極度敏感一點擾動就會被放大。我的經(jīng)驗是τ 一般初始設在 0.05 左右再根據(jù)訓練集難度微調(diào)。判斷標準很簡單如果訓練一開始 loss 掉不下去檢查 τ如果 loss 掉得飛快但評估指標沒有同步提升很可能 τ 太小模型在死記硬背區(qū)分訓練樣本而不是泛化出了語義邊界。比如之前在一個保險場景里我把 τ 從 0.05 調(diào)小到 0.02訓練 loss 降得明顯更快但 recall5 反而掉了調(diào)回 0.04 效果最好。這里有個實操細節(jié)值得注意如果向量沒有做 L2 歸一化τ 的實際效果會失控。因為余弦相似度本身的取值范圍被限制在 [-1, 1]除以一個很小的 τ 前還說得過去如果向量沒有歸一化點積的范圍可能是 [-10, 10] 甚至更大同一個 τ 就會產(chǎn)生截然不同的梯度尺度。所以“先歸一化再調(diào)溫度”是雙塔訓練里絕對不能打破的順序。3.3 InfoNCE 與三元組損失的對比我看到很多項目還在用三元組損失也不是完全不行但有幾個坑很難繞開。首先三元組損失需要顯式構造 anchor、positive、negative 三元組負樣本的難度全部依賴人工挖掘策略而 InfoNCE 天然利用了 batch 內(nèi)全部負樣本不需要額外設計。其次三元組損失只有一個固定 margin遇到簡單負樣本和困難負樣本距離相差很大時梯度容易被簡單樣本主導。InfoNCE 的另一個優(yōu)勢是它與 softmax 分類天然同構。從公式里可以看出它就是帶溫度的 softmax 交叉熵損失每個 query 被要求預測自己對應的正樣本。這讓整個訓練過程非常穩(wěn)定因為梯度有明確的“分類不確定性”作為校準信號不會像 margin loss 那樣經(jīng)常出現(xiàn)負樣本離得太遠或太近導致的波動。我之前做過一個對比實驗同樣數(shù)據(jù)一份用三元組損失一份用 InfoNCE訓練同樣的步數(shù)后者的 recall10 高出 6 個百分點左右。而且三元組損失需要更多小 trick 才能訓起來比如動態(tài)難樣本挖掘、margin 自適應InfoNCE 則只需要關注數(shù)據(jù)和溫度省心太多。除非你有特殊的場景需求否則 InfoNCE 是我在這個階段的首選。4. 垂類訓練數(shù)據(jù)構建正樣本、負樣本與難負樣本的分寸4.1 正樣本從哪里來日志、規(guī)則與人工標注的配合Embedding 訓練需要成對的 query 和正樣本文檔。在垂類項目里正樣本來源通常有三種一是用戶檢索點擊日志用戶搜完之后點開了哪條文檔這是一個弱標注的正樣本二是基于規(guī)則和領域知識構造的規(guī)范匹配對比如“報銷憑證”和“費用結算證明”就是人工規(guī)則認定的同義對三是人工標注數(shù)量少但質(zhì)量最高用來做種子集。我的做法是把這三種來源結合先用日志數(shù)據(jù)跑一個召回模型從日志里取高置信度的點擊對作為自動正樣本再用規(guī)則構造一批同義改寫對補充長尾表達最后請領域同學標幾百條最難的語義對重點關注那些通用模型容易混淆的邊界情況。日志數(shù)據(jù)噪聲很大用戶點開文檔不一定代表內(nèi)容匹配需要用停留時長和后續(xù)行為做置信度過濾否則模型會被噪聲帶偏。關于正樣本數(shù)量很多人會問是不是越多越好。我的經(jīng)驗是垂類模型對正樣本的質(zhì)量敏感度遠高于數(shù)量敏感度。幾百條高質(zhì)量種子對配合幾千條相對可信的日志對就能訓練出一個明顯優(yōu)于通用 Embedding 的模型。如果日志數(shù)據(jù)本身有系統(tǒng)性偏向比如只覆蓋熱門類型冷門類型完全沒有點擊那再多數(shù)據(jù)也救不了冷門類的召回。4.2 負樣本的層次化設計隨機負樣本與難負樣本負樣本的重要性經(jīng)常被低估。隨機負樣本簡單粗暴從候選庫里隨機取一段文本跟 query 大概率不相關這保證了模型能區(qū)分基礎的正負語義。但如果只用隨機負樣本模型會變成“字面匹配器”只要字面重疊高就認為相關這對垂類搜索是致命的。大部分垂類場景里真正難的是區(qū)分那些看起來很像但語義不同的樣本。比如“設備安裝”和“設備調(diào)試”在同一個項目流程里可能都出現(xiàn)但用戶搜“安裝”時“調(diào)試規(guī)范”就不應該排前面。這類難負樣本需要主動挖掘用當前模型跑一遍訓練集把每個 query 召回排名靠前但不相關的文檔撈出來作為難負樣本混入訓練數(shù)據(jù)下一輪模型就會對這類樣本更敏感。難負樣本的加入量需要控制分寸。我在實踐中一般把負樣本池設計成三部分隨機負樣本占 60%難負樣本占 30%領域內(nèi)規(guī)則構造的“相似但不同”樣本占 10%。難負樣本比例如果太高訓練初期模型還沒有區(qū)分能力會把大量難負樣本當成正樣本的近鄰導致?lián)p失波動劇烈。先訓幾輪讓模型把基礎語義學扎實再逐步提高難負樣本比例效果會更穩(wěn)定。4.3 數(shù)據(jù)清洗與去重防止數(shù)據(jù)泄漏和“病態(tài)捷徑”訓練數(shù)據(jù)構造中還有一個很容易翻車的點數(shù)據(jù)泄漏。如果同一條文檔既出現(xiàn)在訓練集里又出現(xiàn)在測試集/驗證集里召回指標會被嚴重高估。垂類數(shù)據(jù)總量小這個問題尤其容易出現(xiàn)。我在每個項目里都會做嚴格去重不只是整段去重還要做改寫后去重因為很多文檔只是換了標題正文基本一致。另一個容易被忽略的問題是“病態(tài)捷徑”。模型很聰明會去找數(shù)據(jù)里最省力的判別信號。如果所有正樣本文檔都集中在某種格式比如都帶“規(guī)范”兩個字模型可能學會只看“規(guī)范”這個 token 就判相關而不是理解真正的語義關系。這種模型在測試集上表現(xiàn)尚可一上線遇到格式不同的真實文檔就崩。避免病態(tài)捷徑的辦法有兩個一是讓正負樣本在格式特征上盡量同分布保證表面特征不是有效區(qū)分信號二是在訓練數(shù)據(jù)里加入大量對抗性噪聲樣本專門把那些“表面很像、語義不同”的例子做成負樣本逼模型放棄表面匹配策略。這兩個操作聽起來簡單但實戰(zhàn)中需要反復看 bad case 才能找到模型的“偷懶點”。4.4 數(shù)據(jù)規(guī)模與冷啟動確實沒有數(shù)據(jù)時怎么辦垂類項目最尷尬的情況是冷啟動連日志都沒有人工標注也沒預算。這種情況下我的建議是先做兩件事一是用通用模型對領域文檔做一次聚類把同一簇內(nèi)但字面差異大的文本對自動挖掘成弱正樣本二是利用領域詞表做同義替換生成合成數(shù)據(jù)。這兩招不完美但能在一天內(nèi)構造出初始訓練集讓模型先跑起來。等模型跑出第一版就能用模型自身的召回結果做第二輪數(shù)據(jù)迭代這就是所謂的“自舉”。先用弱數(shù)據(jù)訓一個粗模型再讓粗模型去挖掘難負樣本和高置信度正樣本擴大訓練集。每一輪迭代之后模型對領域語義的理解都會加深也會在下一輪數(shù)據(jù)挖掘中找到更難、更有價值的樣本。數(shù)據(jù)工程的這四個階段最后都會收斂到這個“模型輔助數(shù)據(jù)生產(chǎn)”的模式上。5. 訓練實操模型配置、批量大小與調(diào)參策略5.1 訓練流程的整體設計與關鍵參數(shù)整個訓練流程我會分成三個階段領域預訓練、雙塔對比訓練、可選精調(diào)。領域預訓練是在通用模型基礎上繼續(xù)用垂類語料做掩碼語言建模這一步不是必須但對領域術語多的場景收益明顯。雙塔對比訓練是主力用前文的數(shù)據(jù)構造方案喂給 InfoNCE??蛇x精調(diào)則是在少量人工標注數(shù)據(jù)上用更小的學習率再走一輪專門修正邊界案例。我用一段偽代碼概括 InfoNCE 訓練的核心邏輯方便不熟悉實現(xiàn)細節(jié)的讀者理解整個過程# 偽代碼InfoNCE 雙塔訓練前向 def info_nce_loss(query_emb, doc_emb, tau0.05): # query_emb 和 doc_emb 均已做 L2 歸一化 # 假設 batch 內(nèi)第 i 個 query 的正確答案是第 i 個 doc logits query_emb doc_emb.T / tau # [batch, batch] labels torch.arange(len(query_emb)) # 對角線為正確匹配 return cross_entropy(logits, labels)這里唯一容易被忽視的是 labels 的構造對角線樣本對是正樣本同一行其他列都是負樣本。如果你的數(shù)據(jù) pipeline 把正樣本對排列打亂了必須維護一個索引映射否則 batch 內(nèi)“負樣本”里混進正確答案訓練損失就直接崩了。關鍵超參我整理了一張表這是我在垂類項目里比較通用的一組起點配置參數(shù)推薦范圍說明學習率1e-5 ~ 3e-5基于預訓練模型微調(diào)不建議太大batch size64 ~ 256越大隱式負樣本越多但受顯存限制溫度 τ0.03 ~ 0.1先小后大視訓練難度微調(diào)難負樣本比例20% ~ 40%逐步加入不要一開始就給滿訓練輪數(shù)3 ~ 8垂類數(shù)據(jù)通常不需要訓很多輪warmup前 10% step避免學習率突變打亂預訓練權重5.2 大規(guī)模 batch 的實現(xiàn)技巧與顯存優(yōu)化InfoNCE 非常吃 batch size。batch 越大隱式負樣本越多訓練出的語義邊界越細。但雙塔結構有一個實現(xiàn)優(yōu)勢query 塔和文檔塔可以分開前向不需要同時把兩側的長文本都存進顯存顯存峰值比交叉編碼器低得多。我一般用梯度累積來模擬更大的 batch先把文檔塔的向量存下來再編碼 query這樣即使單卡也能跑到 128 以上。垂類文檔通常很長如果原樣截斷會損失關鍵信息。我的做法是先把文檔按語義分段對每段分別編碼再把所有段的向量做加權合并。這樣做的原因是文檔里真正跟 query 匹配的核心信息往往只出現(xiàn)在某一段全局均值反而把信號稀釋了。合并權重可以通過一個可學習的注意力層來獲得訓練時和雙塔一起更新。如果資源允許多卡訓練值得投入。雙塔的并行策略很簡單每張卡處理一個子 batch前向完成后把所有 doc 向量匯總到一塊做全量 logits 計算。注意這里的同步通信是必要的因為 InfoNCE 的負樣本是跨卡共享的如果只在單卡內(nèi)做負樣本batch 效應會被打折效果明顯不如全量共享。5.3 評估與早停不能用 loss 判斷模型好壞訓練過程中最容易犯的錯誤是盯著 loss 看。InfoNCE loss 下降不代表檢索效果好因為它只反映模型區(qū)分訓練集 batch 內(nèi)樣本的能力而檢索效果取決于模型在真實候選集上的語義排序能力。所以我在訓練過程中每個 epoch 結束都會跑一次固定的驗證評估集算 recallk 和 nDCG。垂類場景的離線評估集設計要貼近線上真實分布query 來自真實的用戶日志正樣本來自人工確認負樣本從全量候選庫里隨機抽取。我用 recall5 和 recall20 作為主要指標因為線上召回階段只要把正確文檔帶回精排就算完成目標。如果 recall5 提升不明顯但 recall20 提升明顯說明模型把語義帶到了更前面的位置同樣值得發(fā)布。早停的標準應該圍繞評估集指標而不是 loss 變化。我通常設置 patience 為 3 個 epoch如果評估 recall20 連續(xù)三輪沒有上升就停止。垂類數(shù)據(jù)量不大訓練通常不會太長關鍵是把握好“繼續(xù)訓練會不會過擬合到難負樣本”這個臨界點。一旦評估指標下降立刻回滾到最佳 checkpoint不要戀戰(zhàn)。6. 實戰(zhàn)中踩過的坑與完整排查鏈路6.1 坑一難負樣本加太猛訓練直接發(fā)散第一次做難負樣本挖掘時我犯過一個典型錯誤把模型自身召回 top20 的不相關文檔全部塞進負樣本池比例一度拉到 50% 以上。結果訓練 loss 上下亂跳評估指標不升反降。原因現(xiàn)在看很簡單訓練初期模型根本沒有區(qū)分難負樣本的能力把大量高相似度負樣本丟給一個沒準備好的模型等于讓它在黑暗中猜拳梯度信號互相打架。排查過程花了很長時間因為我先懷疑是學習率問題調(diào)了幾輪沒有改善又懷疑是數(shù)據(jù)量不夠加了數(shù)據(jù)還是不行最后把我把負樣本比例按輪次動態(tài)調(diào)整前兩輪只保留隨機負樣本第三輪開始才逐漸加入難負樣本一切才恢復正常。這個經(jīng)驗后來固化成我的訓練策略難負樣本是給“已經(jīng)學會基礎語義的模型”加量的不是給冷啟動模型補課的。6.2 坑二向量沒有歸一化調(diào)參怎么調(diào)都不對還有一個項目A 同學搭建了一套雙塔訓練代碼照著網(wǎng)上的配置跑但無論怎么調(diào)學習率和溫度評估指標都很差。我?guī)兔ε挪闀r第一眼看的就是向量是否做了歸一化發(fā)現(xiàn)訓練代碼把 raw encoder 輸出的向量直接做了點積沒有 L2 歸一化。這里引發(fā)的問題非常隱蔽向量模長參差不齊長文檔總是占優(yōu)模型學會了“長度匹配”而不是“語義匹配”。排查鏈路是給幾個樣本打出來看相似度矩陣發(fā)現(xiàn)所有相似度都集中在某個區(qū)間分布完全不像余弦相似度的樣子。把歸一化加上之后同樣的訓練配置和超參效果立刻恢復正常。這個坑我很確定地寫下來是想提醒所有做雙塔訓練的讀者歸一化必須在 loss 計算之前一個都不能漏。它可以放在 encoder 輸出之后作為一個獨立的 normalize 層。6.3 坑三數(shù)據(jù)泄漏導致評估虛高差點帶病上線訓練數(shù)據(jù)里有一條很隱蔽的數(shù)據(jù)泄漏路徑評估集中某些 query 的正樣本文檔在訓練集中也以較高相似度的變體出現(xiàn)過。導致評估指標看起來非常好但如果把評估集換成線上真實 query效果就崩了。當時我們甚至差點把那個模型發(fā)布上線還好在最后的 bad case 分析中發(fā)現(xiàn)模型對某些文檔標題異常偏愛詞表之外幾乎不看。排查手段是評估集和訓練集做雙向相似度檢索把訓練集每條文檔去評估集里找最相似的樣本如果相似度超過閾值就檢查是否屬于同一份文檔的變體。這個操作看起來耗時但對垂類這種小數(shù)據(jù)量環(huán)境非常值得做。從這之后我把“訓練-評估隔離檢查”寫進了每一個數(shù)據(jù)工程的驗收清單每次發(fā)版前都必須過一遍。6.4 坑四溫度 τ 成為“全局開關”一調(diào)就全局生效最后一個想記錄的坑是 τ 的定位偏差。一開始我把它當作一個“能漲點就調(diào)”的參數(shù)每次模型效果不理想就先把 τ 從小到大掃一遍結果發(fā)現(xiàn)它雖然能帶來短期提升但無法彌補數(shù)據(jù)層面的問題。后來我把 τ 當成一個“和作用域綁定的開關”它主要影響難樣本區(qū)分的銳度只有在數(shù)據(jù)質(zhì)量、負樣本比例、歸一化都正確的前提下調(diào) τ 才有意義。這個認知改變了我整個調(diào)參流程的順序。現(xiàn)在遇到效果不好的情況我會先排查數(shù)據(jù)問題再檢查歸一化和訓練代碼最后才動溫度系數(shù)。垂類模型數(shù)據(jù)工程走到 Embedding 訓練階段時數(shù)據(jù)就是天花板模型和參數(shù)只是負責逼近這個天花板。把順序搞反的人往往在參數(shù)調(diào)節(jié)上花掉大量時間卻始終突破不了那個數(shù)據(jù)決定的性能邊界。這一階段的實戰(zhàn)做下來我的一個整體感覺是雙編碼器加 InfoNCE 這個組合在垂類場景里足夠穩(wěn)它能幫你從前幾個階段積累的數(shù)據(jù)里榨出一套真正屬于領域語義的向量表示?;乜凑麄€訓練過程最有價值的建議還是那幾句老話先體檢再動手先歸一化再調(diào)參先保數(shù)據(jù)隔離再談指標。項目推進中總會有各種突發(fā)問題但只要每一步的選擇都能說清楚為什么遇到問題時排查鏈路就會清晰很多。技術上雙塔結構本身并不復雜真正的復雜度永遠在數(shù)據(jù)和細節(jié)里這也是為什么我把這一階段的筆記重點都放在了踩坑和排錯上。