檢測實戰(zhàn):數(shù)據(jù)集構(gòu)建與模型訓(xùn)練部署全流程解析)
做甲骨文目標(biāo)檢測這件事我最早是被一個現(xiàn)實問題推進來的手上攢了兩千多張甲骨拓片和照片需要按字切分、歸類、建索引純靠人工看眼睛真的會瞎。于是開始折騰目標(biāo)檢測圖像數(shù)據(jù)集一路做到這一期的第10341批數(shù)據(jù)整理和模型升級。這個過程里踩過的坑、繞過的彎路、以及最后沉淀下來的一套可復(fù)用的處理流程我覺得比模型本身更值得寫出來。這篇就圍繞這一期數(shù)據(jù)把從圖像預(yù)處理、標(biāo)注質(zhì)量把控、模型選型訓(xùn)練到推理部署和跨期迭代的完整鏈路盡量講透。先說個總體結(jié)論甲骨文識別在技術(shù)棧上和通用目標(biāo)檢測沒有本質(zhì)區(qū)別難點幾乎全在數(shù)據(jù)側(cè)。字形的殘缺、拓片的噪聲、類別體系的模糊、標(biāo)注標(biāo)準(zhǔn)的不統(tǒng)一這些才是真正拉開效果差距的地方。后面每一個章節(jié)我盡量按實際處理順序來寫你照著走一遍基本能復(fù)現(xiàn)出可用的檢測系統(tǒng)。1. 甲骨文目標(biāo)檢測真正的門檻不是模型是數(shù)據(jù)形態(tài)1.1 拓片、照片、摹本之間的分布差異我一開始犯過一個很典型的錯誤拿館藏高清掃描圖做訓(xùn)練拿手機翻拍圖做測試結(jié)果mAP直接掉了二十多個點。原因不復(fù)雜——拓片是黑底白字博物館數(shù)字化掃描光線均勻、背景干凈手機翻拍往往帶角度、帶反光、帶環(huán)境陰影紙張還有彎曲變形。模型在干凈背景上學(xué)到的特征一到真實場景就失效了。所以做這一期數(shù)據(jù)時我特意把三種來源分開管理一是黑白拓片掃描件二是原色照片三是次生載體上的摹寫或影印。訓(xùn)練集里三種都要有且比例不能太偏。從實際效果看原色照片占比至少應(yīng)到三成否則模型對光照變化非常脆。更穩(wěn)妥的做法是對每張圖像額外記錄來源標(biāo)簽后續(xù)分析哪些類別在哪種來源上容易漏檢也方便做針對性補數(shù)據(jù)。1.2 “一個字”到底怎么定義在古文字場景里是模糊的通用目標(biāo)檢測里“目標(biāo)”的邊界通常是常識性的貓就是貓、車就是車。但甲骨文里沒有這么干凈。同一片甲骨上單字、合文、偏旁部件混在一起同一個字形可能存在多個異體寫法有些殘字只剩一半筆劃專家也未必能確定該歸到哪個釋讀。如果你直接拿現(xiàn)成標(biāo)注工具開框會很快被“這個到底算不算一個字”的問題卡住。我參與整理的這批第10341期數(shù)據(jù)類別體系最終收斂了這樣一套規(guī)則圖像上只要能明確判斷是一個獨立書寫單元的就單獨標(biāo)框單體字按釋讀分類已經(jīng)學(xué)界公認(rèn)的合文作為一整個框處理不拆成多個單字模糊到無法判斷歸屬的統(tǒng)一進入“未定類”而不是硬塞進某個相近類別。這條規(guī)則看起來簡單但極大減少了后續(xù)訓(xùn)練時的標(biāo)簽噪聲。1.3 類別體系按字形分類還是按釋讀分類這里還有一個隱藏沖突。甲骨文研究有時候按“字形”組織有時候按“釋讀”組織但目標(biāo)檢測的類別標(biāo)簽只能二選一。我試過兩版標(biāo)注第一版按主流釋讀結(jié)果標(biāo)注第二版盡量按字形聚類。實測下來按釋讀結(jié)果標(biāo)注模型對異體字的泛化更好按字形聚類類內(nèi)一致性高但類別數(shù)爆炸而且訓(xùn)練樣本被切得太碎許多類別只有幾十個實例。所以最后的妥協(xié)方案是以釋讀類別為主干把批量小但寫法穩(wěn)定的異體字保留為子類那些既沒有釋讀結(jié)論、字形又孤立的樣本集中歸到“未定類”。這個歸類邏輯要寫進標(biāo)注規(guī)范文檔里否則標(biāo)注人員每換一批就會按照自己的理解重新定義一遍。規(guī)范文字不需要多宏大能解決“這個框歸哪一類”的日常疑問就夠了。2. 從原始圖像到干凈訓(xùn)練集第10341期的完整加工鏈路2.1 一張圖是否要做二值化我的實測答案是不做很多做文檔識別的項目喜歡在前處理階段做二值化或背景去除讓前景文字更“干凈”。我在甲骨文圖像上也試過幾種經(jīng)典二值化算法、自適應(yīng)閾值和形態(tài)學(xué)降噪。結(jié)論比較反直覺在目標(biāo)檢測 pipeline 里做二值化收益很小風(fēng)險很大。風(fēng)險主要來自拓片自身的紋理。甲骨拓片的裂紋、石花、紙紋在二值化后很容易被保留為邊緣噪聲和字形筆劃分辨不開而溫和的閾值參數(shù)又會削掉淺淡筆劃導(dǎo)致文字殘缺。相比之下讓模型直接看灰度圖或原圖它反而能學(xué)到“哪些紋理是字形、哪些紋理是背景”的區(qū)分能力。我自己后來只保留了兩步前處理統(tǒng)一縮放尺寸以及將彩色圖轉(zhuǎn)灰度圖再做一次輕度的直方圖均衡化。如果訓(xùn)練數(shù)據(jù)里原色照片多我甚至不做灰度轉(zhuǎn)換。這一步省掉之后訓(xùn)練速度變化不大但測試集的誤報明顯少了。這一期的數(shù)據(jù)我在保存時同時保留原圖和處理圖兩套文件后面重訓(xùn)模型也方便做前后對照。2.2 高效標(biāo)注預(yù)標(biāo)注加人工修正而不是從零開框標(biāo)注是目標(biāo)檢測圖像數(shù)據(jù)集構(gòu)建里最費人力的環(huán)節(jié)。第10341期這批圖像如果全部從零標(biāo)注按每個框三十秒估算要消耗大量工時。實際做法是先用上一版模型在這批圖上跑一遍預(yù)標(biāo)注再把置信度在0.4以上的框?qū)霕?biāo)注工具人工負(fù)責(zé)增補漏框、刪除誤報、修正偏移框。這套流程能把標(biāo)注效率提升不少但有一個系統(tǒng)性風(fēng)險預(yù)標(biāo)注模型本身的偏差會被人工標(biāo)注“繼承”。比如舊模型對某一類字形系統(tǒng)性漏檢人工修正時如果沒注意到新標(biāo)注數(shù)據(jù)也會缺這部分。我用的對策是隨機抽一批樣本不導(dǎo)入任何預(yù)標(biāo)注框直接從零標(biāo)注拿它和“預(yù)標(biāo)注加修正”的結(jié)果做增量對比確認(rèn)舊模型漏掉的類別在人工階段被補回來了。雙側(cè)校驗做完這批數(shù)據(jù)的標(biāo)注覆蓋度才有保障。2.3 雙重標(biāo)注一致性校驗漏標(biāo)比錯框更致命我讓兩名標(biāo)注員獨立標(biāo)注了同一批共80張圖然后計算標(biāo)注一致性。一致性檢驗如果只看框的位置和類別很容易忽略“某些字整個沒標(biāo)”的情況。真正的統(tǒng)計口徑是把所有框按類別和大致位置匹配起來統(tǒng)計漏標(biāo)數(shù)、多標(biāo)數(shù)、類別不一致數(shù)、邊界偏移量。實測下來漏標(biāo)率對訓(xùn)練影響最大因為漏標(biāo)會讓模型把“背景/區(qū)域”學(xué)成“無目標(biāo)”在真實推斷時表現(xiàn)為對該字形視而不見。針對漏標(biāo)我的處理是定期跑“反向校驗”用當(dāng)前模型檢測已完成的標(biāo)注圖凡是模型給出高置信度但標(biāo)注中不存在的框一律彈出來讓人工復(fù)核。這個步驟能撈回不少漏標(biāo)。很多團隊只關(guān)注框準(zhǔn)不準(zhǔn)卻忽略了“有沒有把所有目標(biāo)都框出來”這是我在幾輪數(shù)據(jù)整理之后感受最深的一點。另外標(biāo)注工具有必要同時導(dǎo)出多份格式。這一期我保留了VOC XML和YOLO txt兩種版本前者方便后續(xù)二次處理與可視化后者直接喂給訓(xùn)練腳本。兩個格式間的轉(zhuǎn)換腳本必須做字段核對不要只做字符串替換否則很容易在坐標(biāo)格式上翻車。2.4 數(shù)據(jù)增強策略少即是多mosaic不宜拉滿通用目標(biāo)檢測里mosaic增強能顯著提升模型對遮擋和多尺度目標(biāo)的適應(yīng)力。但甲骨文和自然場景不太一樣一個字的核心信息集中在細(xì)窄筆劃上過強的幾何增強和顏色擾動反而會破壞筆劃結(jié)構(gòu)。我在第10341期數(shù)據(jù)上做過一組消融對比mosaic設(shè)為0.5時mAP50有小幅提升mAP50-95基本持平mosaic設(shè)為1.0時字形斷裂類誤檢開始增多顏色增強hsv_h拉大到0.05以上模型對淺淡殘字的召回反而下降。所以最終訓(xùn)練配置里我保留了輕度翻轉(zhuǎn)和微小尺度抖動關(guān)閉了大部分色彩擾動mosaic開到0.5而不是滿值。旋轉(zhuǎn)增強也控制在小角度范圍因為甲骨文的文字方向雖然有傾斜但整體接近水平過大的旋轉(zhuǎn)會生成現(xiàn)實中幾乎不存在的樣本等同變相引入噪聲。3. 模型選型和訓(xùn)練參數(shù)第10341期實測出的平衡點3.1 模型規(guī)模為什么我沒有選更大的骨干網(wǎng)絡(luò)第10341期這批標(biāo)注完成的數(shù)據(jù)實例總數(shù)不到8000個類別約30類。在訓(xùn)練目標(biāo)檢測模型時數(shù)據(jù)量決定了模型復(fù)雜度上限。我對比過YOLO系列里兩個體量的模型輕量版本和更大規(guī)模版本。同樣訓(xùn)練300輪大模型在訓(xùn)練集上擬合得更徹底但在驗證集上的mAP50-95反而低1到2個點訓(xùn)練時間也長不少。原因就是數(shù)據(jù)量撐不起大模型的容量過擬合來得更早。輕量版本在小數(shù)據(jù)集上收斂快、靈活性高后期做量化部署也更方便。真實項目里準(zhǔn)確率固然重要但可迭代性同樣關(guān)鍵。我們經(jīng)常需要根據(jù)新的考古發(fā)現(xiàn)或新的釋讀意見調(diào)整類別體系輕量模型一次重訓(xùn)只需要幾十分鐘大模型動輒幾小時迭代效率完全不同。3.2 訓(xùn)練參數(shù)配置和取值理由我直接給出一份訓(xùn)練配置復(fù)制就能用條件是你的標(biāo)注格式是YOLO格式。yolo detect train \ dataoracle.yaml \ modelyolov8n.pt \ imgsz640 \ epochs300 \ batch16 \ optimizerAdamW \ lr00.001 \ lrf0.01 \ mosaic0.5 \ close_mosaic10 \ hsv_h0.015 \ hsv_s0.3 \ hsv_v0.3 \ fliplr0.5 \ scale0.3逐個說下關(guān)鍵參數(shù)的考慮imgsz640數(shù)據(jù)尺寸的默認(rèn)值和預(yù)訓(xùn)練權(quán)重輸入一致多數(shù)情況下夠用。batch16在顯存允許范圍內(nèi)盡量大。小batch加高學(xué)習(xí)率在數(shù)據(jù)類別不均衡時容易震蕩。epochs300帶上早停實際跑到220輪左右就停了并不是越多越好。判斷標(biāo)準(zhǔn)是驗證集mAP是否連續(xù)20輪不漲。optimizerAdamW收斂比SGD穩(wěn)尤其在不做大幅學(xué)習(xí)率預(yù)熱的情況下。close_mosaic10訓(xùn)練最后10輪關(guān)閉mosaic讓模型在接近真實分布的數(shù)據(jù)上做最后微調(diào)。這個細(xì)節(jié)能顯著降低驗證集loss的抖動。hsv變化全部調(diào)低上文已經(jīng)說過甲骨文識別對顏色擾動敏感。這份配置在驗證集上的結(jié)果大概在mAP50為0.87、mAP50-95為0.63的水平不算驚艷但對于類別多、殘缺多、噪聲大的甲骨文數(shù)據(jù)屬于比較合理且穩(wěn)定的區(qū)間。3.3 訓(xùn)練曲線怎么看loss掉但mAP不動先查數(shù)據(jù)問題很多人在訓(xùn)練時只看訓(xùn)練loss還在降就認(rèn)為模型在變好。實際上如果訓(xùn)練loss一路下降、驗證mAP卻停滯絕大多數(shù)時候不是訓(xùn)練輪數(shù)不夠而是標(biāo)簽里有系統(tǒng)性錯誤。一個很典型的例子由于標(biāo)注規(guī)范里“未定類”把大量噪聲框都吸收進來了模型會把很多背景紋理當(dāng)成目標(biāo)學(xué)結(jié)果就是訓(xùn)練loss很漂亮驗證集上全是誤報。我后來改進了診斷方式每訓(xùn)練50輪就保存一次權(quán)重用固定的一批測試圖跑一遍可視化推理。不要只看指標(biāo)曲線直接看檢測框畫到圖上是什么效果很多問題在圖像上比在數(shù)字里暴露得更快。第10341期就靠這種方式發(fā)現(xiàn)了一個很隱蔽的問題同一種偏旁出現(xiàn)在上下不同位置模型頻繁把偏旁所在區(qū)域預(yù)測成鄰近的整字類別。這個屬于采樣偏差——訓(xùn)練集里該偏旁的獨立樣本太少模型被迫靠上下文猜而上下文又不夠穩(wěn)定。3.4 兩次典型的跑偏經(jīng)歷一次是數(shù)據(jù)劃分出錯某一張拓片被裁剪成四個子圖其中三個進了訓(xùn)練集、一個進了驗證集。因為它們來自同一原始圖像圖像風(fēng)格、裂紋背景、字形風(fēng)格高度一致導(dǎo)致驗證集分?jǐn)?shù)虛高了大約2.5個點。這類泄漏最隱蔽因為背景特征成了模型判斷類別的捷徑。后來我引入了基于圖像來源路徑的分組劃分確保來自同一張原始圖像的所有子圖只出現(xiàn)在同一個劃分里。另一次是增強參數(shù)過強我給訓(xùn)練集開了接近默認(rèn)值的mosaic和旋轉(zhuǎn)結(jié)果模型對甲骨上常見邊框線的誤檢率明顯升高。原因也很直接放大旋轉(zhuǎn)后裂痕和邊框在形狀上越來越像某些字形筆劃。后來回退增強強度后誤檢率降回正常水平。增強不是越狠越好這個教訓(xùn)我是真金白銀換來的。4. 推理部署中的真實表現(xiàn)驗證集分?jǐn)?shù)高不代表能直接上線4.1 認(rèn)清驗證集和真實使用場景的差距驗證集分?jǐn)?shù)高很多時候只說明你的模型和你的驗證集“合拍”。真實的甲骨文檢測使用場景可能是學(xué)者在電腦上放大的高清單張圖也可能是考古現(xiàn)場用手機拍的工作照還可能是從PDF論文里截出來的低分辨率插圖。三種場景差異極大。我驗證過的一個現(xiàn)象很能說明問題模型在掃描拓片測試集上mAP50將近0.9但放到低分辨率截圖測試集上直接降到0.6左右。原因不只是分辨率下降更在于低分辨率圖經(jīng)過壓縮后細(xì)筆劃周圍產(chǎn)生了明顯振鈴和模糊模型提取不到原來的邊緣特征。應(yīng)對方法是訓(xùn)練時imgsz用640但推理時可以按長寬比先做超分辨率增強另一個更省事的方法是準(zhǔn)備一批低分辨率樣本加進訓(xùn)練集。4.2 置信度閾值和NMS的調(diào)參經(jīng)驗對部署來說閾值設(shè)定甚至可以算作模型效果的一部分。我測過不同置信度閾值在一批混合來源測試集上的表現(xiàn)置信度閾值精確率召回率框數(shù)量主觀觀感0.100.340.82非常多大量誤報沒法用0.250.550.75偏多仍有零散誤報0.400.720.66適中整體舒服偶漏0.600.840.48偏少漏檢較多甲骨文檢測的信噪比天然比自然場景低閾值太低會崩太高會漏。綜合考慮人工復(fù)核成本之后我默認(rèn)選擇0.35到0.45之間。學(xué)術(shù)研究場景我甚至?xí)为毺峁?.30的“高召回”版本方便專家盡可能看到候選區(qū)域?qū)幎辔鹑?。NMS的IoU閾值同樣值得調(diào)。甲骨文里兩個相鄰字形經(jīng)常離得很近如果NMS閾值太高兩個真實框可能被合并成一個如果太低一個字的多個重疊框又會同時保留。實測設(shè)0.5比較均衡。4.3 最常見的四類誤報拋開閾值問題模型在真實圖像上常見的誤報大致有四種第一類是紙張裂紋和折痕被識別成字的筆劃尤其是長直裂紋經(jīng)常和“橫”類字形撞車。第二類是拓片上的石花或砂點在低置信度區(qū)間大量出現(xiàn)表現(xiàn)為大塊密集小框。第三類是掃描形成的陰影或者反光帶容易在邊緣產(chǎn)生一個或幾個高置信度誤報。第四類是表格線、邊框線或排版痕跡被模型認(rèn)成某些橫筆較多的字。針對第一類和第四類我試過加負(fù)樣本專門截取那些“像字但不是字”的圖像塊放進訓(xùn)練集把背景類別顯式地引入。這個方法對降誤報出奇有效但也可能讓模型變得過于保守。所以負(fù)樣本的比例要控制我一般控制在正樣本量的20%以內(nèi)并且持續(xù)做單測。4.4 高分辨率長圖的切塊推理整張拓片往往是一張超長圖直接縮到640推理會丟失大量小字細(xì)節(jié)。我的做法是滑窗切塊推理把長圖按1024的步長切出帶重疊區(qū)域的子圖每個子圖單獨推理再把所有檢測框映射回原圖坐標(biāo)做融合。重疊區(qū)域用0.2比較穩(wěn)太少了邊框處的字容易漏太多了同一位置會被多個子圖重復(fù)檢測雖然NMS能合并但計算量浪費不少。映射回原圖時要注意坐標(biāo)如果不在原尺度上需要先拉伸這里是一個典型的容易出bug的位置所有縮放比例必須先統(tǒng)一。我處理完切塊推理后會把整圖的疊加重疊框結(jié)果可視化一遍用肉眼抽查邊界處有沒有出現(xiàn)“半截框”或“雙線框”。5. 跨期數(shù)據(jù)迭代第10341期不是終點是怎么接入舊模型5.1 增量訓(xùn)練和全量重訓(xùn)的選擇當(dāng)一批新數(shù)據(jù)標(biāo)注完成后最大的誘惑是直接在舊權(quán)重上繼續(xù)訓(xùn)練。我試過幾次發(fā)現(xiàn)直接fine-tune會把舊數(shù)據(jù)上的能力逐漸沖掉尤其是那些舊數(shù)據(jù)里占比較高、新數(shù)據(jù)里占比低的類別。所以后來堅持每次新數(shù)據(jù)合并進總數(shù)據(jù)集重新從頭訓(xùn)練。代價是訓(xùn)練時間變長但換來的是能力穩(wěn)定不偏科。也有一些中間路線比如凍結(jié)部分層只訓(xùn)練檢測頭或給舊數(shù)據(jù)做回放采樣。在類別體系沒有大改動時這些方法也能用。但類別體系一旦有增改一定要從頭訓(xùn)練因為檢測頭的輸出維度變了舊權(quán)重結(jié)構(gòu)對應(yīng)不上。5.2 固定回歸測試集是防止模型退步的好工具每期數(shù)據(jù)整理后我都會從歷史數(shù)據(jù)里抽出大約兩百張覆蓋所有類別和典型難例的圖組成一個凍結(jié)的回歸測試集。只要訓(xùn)練腳本或數(shù)據(jù)有改動就用這份回歸集跑一遍指標(biāo)對比?;貧w測試集的意義在于它不是讓你看到數(shù)字變高而是防止舊能力悄悄丟掉。比如某一期我調(diào)整了數(shù)據(jù)增強策略總體驗證集mAP提升了但回歸集里幾個特定的殘字漏檢變多了。如果沒有這組固定測試這個問題很可能要在上線后被用戶反饋才發(fā)現(xiàn)。保持一份長期不動的測試集能幫你把“整體變好”和“局部變差”分開看清楚。5.3 處理重復(fù)數(shù)據(jù)同一拓片被多次收錄怎么辦跨期整理數(shù)據(jù)一定會遇到同一張拓片在不同期次里被再次收錄。如果不去重訓(xùn)練集和驗證集之間就可能出現(xiàn)同源圖像輕則分?jǐn)?shù)虛高重則導(dǎo)致“背題”假象。我用的是感知哈希粗篩加人工復(fù)核對所有圖像計算感知哈希兩兩相似度超過閾值的自動標(biāo)記然后人工判斷是否是同一來源。同一來源的圖只保留一個代表樣本其余進候選池備用。雖然相似度閾值偶爾會誤傷不同圖像但漏掉的代價是近重復(fù)數(shù)據(jù)“污染”測試集。兩害相權(quán)取其輕我寧可多標(biāo)記一些候選人上去復(fù)核也不想漏掉任何一張。5.4 每一期數(shù)據(jù)接入之前先跑一輪“凍結(jié)基線”我的標(biāo)準(zhǔn)流程是新數(shù)據(jù)不直接進訓(xùn)練集。先把它單獨放一邊用上一版模型在上面跑一次完整推理統(tǒng)計指標(biāo)并可視化結(jié)果。這一份結(jié)果就是“凍結(jié)基線”能告訴我們舊模型對這批新數(shù)據(jù)知道多少、缺多少。然后把新數(shù)據(jù)合并進來重訓(xùn)再用同一批圖做測試和凍結(jié)基線對比。這個流程雖然多花一點時間但能清楚量化每一期數(shù)據(jù)帶來的提升。第10341期我當(dāng)時記錄的對比情況是融合重訓(xùn)后mAP50比舊模型在該批上提升了大約11個點其中“未定類”單項召回率提升最明顯。不跑基線的話這種提升只能靠感覺沒法形成可靠的數(shù)據(jù)洞察?;氐介_頭說的那個場景當(dāng)我把這套流程完整跑通之后新到的數(shù)據(jù)對我而言就不再有“拆盲盒”的感覺。每一批數(shù)據(jù)進來都有明確的加工路徑、質(zhì)量校驗和回歸驗證方法。做小眾領(lǐng)域的目標(biāo)檢測圖像數(shù)據(jù)集本質(zhì)上拼的是流程設(shè)計和對領(lǐng)域噪聲的敏感度。模型本身已經(jīng)高度工具化了反而是數(shù)據(jù)側(cè)的那些細(xì)節(jié)決定了系統(tǒng)最終能被信任到什么程度。如果有人也正在整理類似的小樣本識別數(shù)據(jù)集我建議先把數(shù)據(jù)規(guī)范文檔、回歸測試集和基線對比這三件事做扎實再碰模型后面你會感謝當(dāng)初沒有偷懶的自己。