據(jù)集實(shí)戰(zhàn):從數(shù)據(jù)標(biāo)注到Y(jié)OLO檢測(cè)的完整指南)
簡(jiǎn)介面向鐵路基礎(chǔ)設(shè)施維護(hù)與計(jì)算機(jī)視覺(jué)研究的鐵軌裂紋數(shù)據(jù)集第一部分重點(diǎn)解決軌道裂紋、缺陷的自動(dòng)識(shí)別與定位問(wèn)題適合從事目標(biāo)檢測(cè)、智能巡檢和軌道安全監(jiān)測(cè)的研究者用于模型訓(xùn)練與驗(yàn)證。資源采用VOC2007標(biāo)準(zhǔn)標(biāo)注格式所有XML注釋文件均由LabelImg工具人工生成詳細(xì)標(biāo)出每個(gè)裂紋和缺陷的位置、大小與類(lèi)別可直接作為深度學(xué)習(xí)模型的輸入。壓縮包共包含2000個(gè)文件以XML標(biāo)注文件與JPG原圖為主整體約252.69MB已有4815人學(xué)習(xí)下載。依托這份數(shù)據(jù)集使用者能夠系統(tǒng)開(kāi)展圖像預(yù)處理、特征提取、模型訓(xùn)練與效果驗(yàn)證等完整流程還能通過(guò)閱讀XML了解VOC格式的目錄組織方式后續(xù)若結(jié)合第二部分?jǐn)?shù)據(jù)可進(jìn)一步增強(qiáng)模型的泛化能力與魯棒性為鐵路安全自動(dòng)化檢測(cè)提供扎實(shí)的數(shù)據(jù)支撐。1. 鐵軌裂紋數(shù)據(jù)集先搞懂這份數(shù)據(jù)再談高精度檢測(cè)你花了兩周把模型結(jié)構(gòu)調(diào)得花團(tuán)錦簇一上軌檢車(chē)實(shí)拍圖漏檢率直接打回原形。這種翻車(chē)十有八九不是模型的問(wèn)題而是訓(xùn)練數(shù)據(jù)本身沒(méi)吃透。鐵軌裂紋數(shù)據(jù)集第一部分就是用來(lái)補(bǔ)這門(mén)課的它把軌道表面裂紋的圖像、標(biāo)注和場(chǎng)景分布打包好讓做目標(biāo)檢測(cè)的人能在真實(shí)光照、真實(shí)銹蝕、真實(shí)枕木干擾下訓(xùn)練和驗(yàn)證模型而不是在一套干凈得不像話的合成圖里自欺欺人。這份數(shù)據(jù)適合兩類(lèi)人一是剛接觸軌檢視覺(jué)的開(kāi)發(fā)者需要一套帶標(biāo)注的數(shù)據(jù)跑通檢測(cè)流程二是已經(jīng)在做鋼軌傷損識(shí)別、但對(duì)訓(xùn)練集和驗(yàn)證集劃分沒(méi)有把握的工程師想用一套結(jié)構(gòu)清晰的數(shù)據(jù)檢驗(yàn)自己的預(yù)處理和模型配置。你能從這份數(shù)據(jù)里得到的不是「跑起來(lái)就完事」的體驗(yàn)而是把裂紋檢測(cè)當(dāng)作一個(gè)正經(jīng)目標(biāo)檢測(cè)任務(wù)來(lái)對(duì)待的完整路徑數(shù)據(jù)怎么組織、標(biāo)簽怎么解析、劃分怎么做、訓(xùn)練參數(shù)怎么定、哪些坑會(huì)導(dǎo)致指標(biāo)虛高或直接崩掉。本文按這個(gè)順序展開(kāi)每一章都可以直接照著操作。2. 讀懂?dāng)?shù)據(jù)組織方式圖像、標(biāo)注與兩類(lèi)常見(jiàn)格式拿到數(shù)據(jù)集的第一步不是訓(xùn)練是先搞清楚它內(nèi)部的目錄結(jié)構(gòu)和標(biāo)注格式。鐵軌裂紋數(shù)據(jù)集第一部分的常見(jiàn)組織方式是 images 目錄存原始圖像labels 目錄存標(biāo)注文件另外附帶一份類(lèi)別說(shuō)明。這個(gè)結(jié)構(gòu)看著簡(jiǎn)單實(shí)際用起來(lái)有幾個(gè)容易忽視的細(xì)節(jié)。2.1 圖像內(nèi)容與裂紋形態(tài)橫裂、縱裂和網(wǎng)裂為什么不能混在一個(gè)類(lèi)別里軌道表面裂紋按走向和形態(tài)大體分三類(lèi)橫向裂紋垂直于鋼軌走向、縱向裂紋沿鋼軌走向延伸、網(wǎng)狀裂紋龜裂或不規(guī)則分叉。如果數(shù)據(jù)集把所有裂紋統(tǒng)一標(biāo)成一個(gè) crack 類(lèi)別訓(xùn)練出來(lái)的模型只能告訴你「這里有異?!沟F(xiàn)場(chǎng)工程師真正需要的是「這是什么類(lèi)型的傷損」——橫裂涉及斷軌風(fēng)險(xiǎn)縱裂可能和材質(zhì)缺陷有關(guān)網(wǎng)裂往往伴隨疲勞累積。所以拿到數(shù)據(jù)集先看類(lèi)別清單確認(rèn)是否區(qū)分了多個(gè)裂紋類(lèi)別這決定了后續(xù)模型是單類(lèi)檢測(cè)還是多類(lèi)檢測(cè)評(píng)估指標(biāo)也要跟著分開(kāi)看。實(shí)際標(biāo)注中橫向裂紋的框通常窄而扁縱向裂紋的框又長(zhǎng)又細(xì)兩者在 aspect ratio寬高比上差異極大。如果你用 COCO 預(yù)訓(xùn)練權(quán)重直接遷移學(xué)習(xí)默認(rèn)的 anchor 配置對(duì)極端長(zhǎng)寬比目標(biāo)很不友好后面會(huì)細(xì)說(shuō)。另外軌枕、扣件、銹蝕斑、油污在圖像里經(jīng)常被誤標(biāo)成裂紋或者反過(guò)來(lái)把細(xì)小裂紋漏標(biāo)了這份數(shù)據(jù)集里同樣存在屬于標(biāo)注噪聲的一部分第 5 章會(huì)專(zhuān)門(mén)講怎么處理。2.2 XML 與 TXT 兩種標(biāo)注格式各自的字段含義與解析要點(diǎn)鐵軌裂紋數(shù)據(jù)集常見(jiàn)的標(biāo)注文件有兩種格式一種是 Pascal VOC 風(fēng)格的 XML另一種是 YOLO 風(fēng)格的 TXT。XML 適合做人眼檢查因?yàn)樽鴺?biāo)是絕對(duì)像素值TXT 適合直接訓(xùn)練因?yàn)樽鴺?biāo)是相對(duì)圖像的歸一化值。兩種格式在發(fā)布包里有時(shí)候會(huì)同時(shí)提供有時(shí)候只提供一種建議先確認(rèn)拿到的是哪種再?zèng)Q定數(shù)據(jù)加載腳本怎么接。XML 格式的核心字段是 object 節(jié)點(diǎn)下的 name類(lèi)別名和 bndboxxmin、ymin、xmax、ymax 四個(gè)絕對(duì)坐標(biāo)另外還有 width、height 記錄圖像尺寸。解析時(shí)要把坐標(biāo)轉(zhuǎn)成模型需要的格式而且注意 bndbox 的坐標(biāo)是 0 基還是 1 基——VOC 規(guī)范里 xmin/ymin 從 1 開(kāi)始轉(zhuǎn)換到 YOLO 歸一化坐標(biāo)時(shí)要先減去 1 再除以寬高否則邊界框會(huì)偏移一個(gè)像素。這個(gè)偏移對(duì)粗框影響不大但對(duì)細(xì)長(zhǎng)裂紋這種目標(biāo)一兩個(gè)像素的偏差就可能讓 IoU 掉一截。TXT 格式每一行對(duì)應(yīng)一個(gè)目標(biāo)五個(gè)數(shù)值依次是 class_id、x_center、y_center、width、height全部歸一化到 01。解析時(shí)最常出的問(wèn)題有兩個(gè)一是某些標(biāo)注文件里寬度或高度算成了 0說(shuō)明標(biāo)注時(shí)誤點(diǎn)了兩個(gè)重合的點(diǎn)二是坐標(biāo)值出現(xiàn)負(fù)數(shù)或大于 1 的情況多半是標(biāo)注工具導(dǎo)出時(shí)沒(méi)做裁剪目標(biāo)貼邊導(dǎo)致框超出圖像邊界。這兩種臟數(shù)據(jù)如果不提前過(guò)濾訓(xùn)練時(shí)輕則 warning 刷屏重則 loss 變成 NaN。2.3 解析前的自查腳本三分鐘找出配對(duì)缺失和坐標(biāo)越界拿到數(shù)據(jù)集后別急著開(kāi)訓(xùn)先寫(xiě)一個(gè)檢查腳本把下面的問(wèn)題過(guò)一次。我一般把這個(gè)腳本存成 inspect_data.py每次換數(shù)據(jù)集都跑一遍。import os from glob import glob from PIL import Image img_dir images label_dir labels ext_map {.jpg: .txt, .jpeg: .txt, .png: .txt} img_files glob(os.path.join(img_dir, *)) label_files glob(os.path.join(label_dir, *.txt)) img_stems {os.path.splitext(os.path.basename(f))[0] for f in img_files} label_stems {os.path.splitext(os.path.basename(f))[0] for f in label_files} missing img_stems - label_stems orphan label_stems - img_stems print(missing labels:, sorted(missing)[:10]) print(orphan labels:, sorted(orphan)[:10]) for tf in label_files: with open(tf, r) as f: lines f.readlines() for line in lines: parts line.strip().split() if len(parts) ! 5: print(bad line in, tf, :, line) else: x, y, w, h map(float, parts[1:]) if w 0 or h 0 or x 0 or y 0 or x 1 or y 1 or w 1 or h 1: print(out-of-range in, tf, :, line)這段腳本做了三件事第一把圖像文件和標(biāo)簽文件的文件名做差集比對(duì)找出有圖沒(méi)標(biāo)、有標(biāo)沒(méi)圖的文件第二檢查每一行標(biāo)注是否恰好五個(gè)數(shù)值第三檢查歸一化坐標(biāo)是否在合理區(qū)間內(nèi)。跑完如果有輸出先把這些問(wèn)題文件隔離到單獨(dú)目錄不要直接刪因?yàn)槟氵€需要它們來(lái)反向排查標(biāo)注工具的導(dǎo)出配置。常見(jiàn)做法是先修數(shù)據(jù)再訓(xùn)練而不是讓模型去「學(xué)會(huì)」容忍臟標(biāo)注。3. 訓(xùn)練集與驗(yàn)證集的劃分策略為什么不能隨機(jī)拆分很多人在拿到數(shù)據(jù)集后直接train_test_split按 8:2 隨機(jī)拆訓(xùn)練出來(lái)的模型在驗(yàn)證集上指標(biāo)不錯(cuò)一到現(xiàn)場(chǎng)就露餡。這不是玄學(xué)而是鐵軌數(shù)據(jù)的天然特性導(dǎo)致的——同一根鋼軌的連續(xù)圖像在光照、角度、背景上高度相似隨機(jī)拆分會(huì)把同一段鋼軌的圖像同時(shí)分進(jìn)訓(xùn)練集和驗(yàn)證集模型等于提前看到了答案。3.1 按場(chǎng)景維度劃分同一段鋼軌不要同時(shí)出現(xiàn)在訓(xùn)練集和驗(yàn)證集鐵軌裂紋數(shù)據(jù)的拍攝通常來(lái)自軌檢車(chē)或手持設(shè)備的一次連續(xù)走行一次采集會(huì)得到大量連續(xù)幀。這些幀里裂紋的形態(tài)變化不大但拍攝距離、亮度、曝光在短時(shí)間內(nèi)是漸變的。如果隨機(jī)劃分驗(yàn)證集里會(huì)出現(xiàn)和訓(xùn)練集幾乎一模一樣的幀最后的評(píng)估分?jǐn)?shù)會(huì)虛高幾個(gè)點(diǎn)這不代表真實(shí)泛化能力。正確做法是先按場(chǎng)景分組再在場(chǎng)景級(jí)別上劃分。比如一次采集是一個(gè) scene一個(gè) scene 里的所有圖像要么全進(jìn)訓(xùn)練集要么全進(jìn)驗(yàn)證集。這樣才能模擬「模型見(jiàn)到?jīng)]走過(guò)的鋼軌段」的真實(shí)推理?xiàng)l件。鐵軌裂紋數(shù)據(jù)集通常會(huì)在發(fā)布時(shí)說(shuō)明圖像來(lái)自哪些采集批次如果沒(méi)有說(shuō)明可以通過(guò)文件名前綴或拍攝時(shí)間戳來(lái)推斷分組依據(jù)。3.2 光照與干濕狀態(tài)分層不控制分布驗(yàn)證分?jǐn)?shù)就是自欺欺人除了場(chǎng)景維度光照條件也強(qiáng)烈影響裂紋的視覺(jué)表現(xiàn)。白天強(qiáng)光下裂紋陰影清晰陰天漫射光下裂紋對(duì)比度低夜間閃光燈下金屬表面反光可能把裂紋完全淹沒(méi)。如果訓(xùn)練集里大量是晴天白圖驗(yàn)證集里也大量是晴天白圖訓(xùn)練過(guò)程會(huì)學(xué)到一個(gè)取巧的 shortcut——看到高亮金屬表面就傾向于預(yù)測(cè)裂紋。這個(gè)現(xiàn)象在視覺(jué)任務(wù)里很常見(jiàn)模型沒(méi)有真正學(xué)會(huì)區(qū)分裂紋亮度模式只學(xué)會(huì)了區(qū)分圖像整體亮度。分層劃分的思路是把圖像先按拍攝條件打上標(biāo)簽強(qiáng)光/陰天/夜間/雨霧然后在每個(gè)條件內(nèi)部按比例劃分保證訓(xùn)練集和驗(yàn)證集里的光照分布一致。鐵軌裂紋數(shù)據(jù)集第一部分如果自帶這些 meta 信息直接利用如果沒(méi)有可以用圖像的亮度直方圖做一個(gè)粗略的區(qū)分作為分層的替代方案。3.3 分層分割腳本按場(chǎng)景ID和光照標(biāo)簽的劃分實(shí)現(xiàn)下面這段腳本展示了按場(chǎng)景分組之后再按光照條件做分層采樣的劃分邏輯。關(guān)鍵函數(shù)是train_test_split的stratify參數(shù)它保證每個(gè)光照類(lèi)別在訓(xùn)練集和驗(yàn)證集中的比例一致。import os import random import pandas as pd from sklearn.model_selection import train_test_split image_dir images records [] for fname in os.listdir(image_dir): if not fname.lower().endswith((.jpg, .png, .jpeg)): continue scene_id fname.split(_)[0] # 假設(shè)文件名前綴是場(chǎng)景ID img_path os.path.join(image_dir, fname) records.append({fname: fname, scene_id: scene_id}) df pd.DataFrame(records) # 按場(chǎng)景分組取每個(gè)場(chǎng)景第一條記錄的scene_id作為分組特征 scene_df df.groupby(scene_id).agg( count(fname, count), scene_id(scene_id, first) ).reset_index() # 在場(chǎng)景級(jí)別上做劃分防止同一場(chǎng)景的幀同時(shí)出現(xiàn)在訓(xùn)練和驗(yàn)證集 train_scenes, val_scenes train_test_split( scene_df[scene_id], test_size0.2, random_state42, stratifyscene_df[scene_id].apply(lambda x: scene_light_level(x)) ) train_df df[df[scene_id].isin(train_scenes)] val_df df[df[scene_id].isin(val_scenes)] print(train frames:, len(train_df)) print(val frames:, len(val_df))這段代碼的核心思想是先按場(chǎng)景ID聚合再在場(chǎng)景上進(jìn)行劃分而不是直接對(duì)圖像做隨機(jī)拆分。stratify參數(shù)用來(lái)保證不同光照條件下的場(chǎng)景在訓(xùn)練集和驗(yàn)證集中占比一致。這里的scene_light_level函數(shù)在實(shí)際使用時(shí)要替換成你的光照分級(jí)邏輯可以用拍攝時(shí)間戳、可以用文件名里的光照標(biāo)記、也可以用圖像亮度均值聚類(lèi)。劃分完后把 train_df 和 val_df 寫(xiě)出成兩個(gè) txt 文件每行一個(gè)圖像路徑后面訓(xùn)練時(shí)直接按 list 加載。注意random_state固定為固定值是為了可復(fù)現(xiàn)。如果你做了數(shù)據(jù)增廣增廣隨機(jī)性不受它控制但數(shù)據(jù)集劃分本身每次運(yùn)行結(jié)果一致方便排查問(wèn)題。4. 最小訓(xùn)練配置用 YOLO 系列工程跑通裂紋檢測(cè)劃分好數(shù)據(jù)之后就可以開(kāi)始訓(xùn)練了。以目標(biāo)檢測(cè)領(lǐng)域最常用的 YOLO 系列代碼庫(kù)為例把鐵軌裂紋數(shù)據(jù)集接進(jìn)去的步驟是準(zhǔn)備 yaml 配置文件、調(diào)整圖像尺寸、改類(lèi)別數(shù)、然后啟動(dòng)訓(xùn)練。4.1 為什么 YOLO 系列適合起步單階段檢測(cè)對(duì)長(zhǎng)條小目標(biāo)的平衡裂紋檢測(cè)有兩種主流路線兩階段檢測(cè)器Faster R-CNN 系精度有優(yōu)勢(shì)但訓(xùn)練慢、部署重單階段檢測(cè)器YOLO 系速度快、生態(tài)成熟、預(yù)訓(xùn)練權(quán)重好找對(duì)細(xì)長(zhǎng)目標(biāo)的召回率通過(guò)調(diào) anchor 也能提到可用水平。鐵軌裂紋檢測(cè)的實(shí)際場(chǎng)景大部分是軌檢車(chē)離線分析或者手持設(shè)備的實(shí)時(shí)提示前者對(duì)速度不敏感后者要求實(shí)時(shí)。如果你只有一次訓(xùn)練資源先跑 YOLO 系拿到一個(gè)能用的 baseline再根據(jù)漏檢形態(tài)決定是否上兩階段模型。YOLO 系代碼庫(kù)通常自帶數(shù)據(jù)加載、增廣、評(píng)估腳本對(duì)新手最友好的一點(diǎn)是訓(xùn)練過(guò)程中的 loss 曲線和 PR 曲線都直接可視化。你需要改的主要是三個(gè)地方數(shù)據(jù)配置 yaml、模型配置 yaml、訓(xùn)練超參數(shù)。下面按這三個(gè)文件逐一說(shuō)明。4.2 數(shù)據(jù)配置與模型配置類(lèi)別數(shù)、圖像尺寸和 anchor數(shù)據(jù)配置里最重要的字段是nc類(lèi)別數(shù)量和names類(lèi)別名列表。鐵軌裂紋數(shù)據(jù)集第一部分如果是單類(lèi)就寫(xiě)nc: 1如果區(qū)分橫裂、縱裂、網(wǎng)裂等多類(lèi)必須和標(biāo)注文件里的 class_id 一一對(duì)應(yīng)。類(lèi)別名寫(xiě)錯(cuò)會(huì)導(dǎo)致訓(xùn)練正常但推理時(shí)標(biāo)簽錯(cuò)位這種低級(jí)錯(cuò)誤在項(xiàng)目交付時(shí)很容易被現(xiàn)場(chǎng)人員罵。# rail_crack.yaml path: ./rail_crack_dataset train: train_list.txt val: val_list.txt nc: 3 names: 0: transverse_crack 1: longitudinal_crack 2: network_crack這個(gè) yaml 文件告訴訓(xùn)練工程三件事數(shù)據(jù)路徑、訓(xùn)練集和驗(yàn)證集的文件列表、類(lèi)別對(duì)應(yīng)關(guān)系。train_list.txt里面每一行是一個(gè)圖像的絕對(duì)或相對(duì)路徑對(duì)應(yīng)的標(biāo)注文件路徑由代碼庫(kù)根據(jù)擴(kuò)展名自動(dòng)替換。注意不同 YOLO 代碼庫(kù)對(duì)train字段的寫(xiě)法可能不同有的是目錄有的是 txt 列表看官方示例確認(rèn)一下不要套錯(cuò)。圖像尺寸是另一個(gè)關(guān)鍵參數(shù)。裂紋屬于小目標(biāo)原圖如果是 1920×1080 級(jí)別的大圖直接縮放到 640×640 會(huì)把細(xì)裂紋縮得只剩幾個(gè)像素模型學(xué)不到有效紋理。常見(jiàn)做法是先用 1280×1280 訓(xùn)練推理時(shí)再用滑窗或原圖縮放。代價(jià)是顯存占用變大batch size 要相應(yīng)調(diào)小。如果你的卡只有 8GB 顯存1280 分辨率下 batch size 可能只能開(kāi)到 4這時(shí)候?qū)幙砂?batch size 調(diào)小也不要降分辨率分辨率對(duì)裂紋檢測(cè)的影響遠(yuǎn)大于 batch size。anchor 方面細(xì)長(zhǎng)裂紋的寬高比經(jīng)常超過(guò) 1:5甚至 1:10默認(rèn) anchor 設(shè)定基本匹配不上。YOLO 系列工程一般提供 k-means 聚類(lèi) anchor 的工具拿訓(xùn)練集的標(biāo)注框跑一遍把聚類(lèi)得到的 anchor 替代默認(rèn)值召回率會(huì)有肉眼可見(jiàn)的回升。4.3 訓(xùn)練啟動(dòng)命令與三個(gè)必調(diào)超參數(shù)啟動(dòng)訓(xùn)練前還需要關(guān)注三個(gè)超參數(shù)epoch、學(xué)習(xí)率和 mosaic 增廣。epoch 從 100 起步觀察驗(yàn)證集 PR 曲線在 60~80 epoch 之間是否還在上升如果上升明顯就繼續(xù)加如果已經(jīng)震蕩就提前停止。學(xué)習(xí)率用代碼庫(kù)默認(rèn)的 warmup cosine 策略即可但不要隨手開(kāi)很大的初始學(xué)習(xí)率裂紋檢測(cè)的標(biāo)注噪聲一般比通用數(shù)據(jù)集高學(xué)習(xí)率太大容易讓模型過(guò)擬合到噪聲框上。mosaic 增廣在拼接四張圖時(shí)會(huì)把裂紋截?cái)喑蓛砂雽?duì)細(xì)長(zhǎng)目標(biāo)反而有害我通常在訓(xùn)練后半段關(guān)閉 mosaic只保留基本的翻轉(zhuǎn)和顏色抖動(dòng)。python train.py --data rail_crack.yaml --weights yolov5s.pt --img 1280 --batch 8 --epochs 100 --cache這條命令的核心參數(shù)含義拆開(kāi)看--data指定 4.2 節(jié)里寫(xiě)好的配置--weights用 COCO 預(yù)訓(xùn)練權(quán)重做遷移學(xué)習(xí)不要從隨機(jī)初始化開(kāi)始裂紋數(shù)據(jù)量不足以支撐從零訓(xùn)練--img 1280是輸入分辨率前面提過(guò)這是小目標(biāo)任務(wù)的關(guān)鍵選擇--batch 8是顯存允許范圍內(nèi)的最大值--cache把圖像緩存到內(nèi)存里加速數(shù)據(jù)讀取第一次跑可以不開(kāi)后面調(diào)參時(shí)再開(kāi)。訓(xùn)練結(jié)束后在驗(yàn)證集上跑val.py得到每個(gè)類(lèi)別的 mAP、precision、recall以及一張 PR 曲線圖。PR 曲線右下角面積大說(shuō)明模型在低置信度下也能保持高召回這對(duì)裂紋檢測(cè)很重要——漏檢的代價(jià)遠(yuǎn)高于誤報(bào)現(xiàn)場(chǎng)寧可多看幾個(gè)誤報(bào)也不愿放過(guò)一條裂紋。5. 鐵軌裂紋數(shù)據(jù)集的 5 個(gè)常見(jiàn)坑每個(gè)都是血淚經(jīng)驗(yàn)這部分把我在實(shí)際使用類(lèi)似數(shù)據(jù)時(shí)遇到最多的問(wèn)題整理成清單每條按現(xiàn)象、原因、解決來(lái)寫(xiě)。你會(huì)發(fā)現(xiàn)大部分坑不在模型而在數(shù)據(jù)和評(píng)測(cè)流程。5.1 驗(yàn)證集出現(xiàn)漏標(biāo)注框指標(biāo)集體翻車(chē)現(xiàn)象訓(xùn)練 loss 正常下降驗(yàn)證集 mAP 卡在 0.5 上不去PR 曲線在低 recall 區(qū)域出現(xiàn)明顯掉點(diǎn)。原因數(shù)據(jù)集里存在漏標(biāo)注——圖像里明明有裂紋標(biāo)注文件里沒(méi)有對(duì)應(yīng)框。模型有時(shí)能檢測(cè)到這些「未標(biāo)注的裂紋」但評(píng)測(cè)腳本把它算作 false positiveprecision 被拉低同時(shí)模型學(xué)到這些區(qū)域的紋理特征導(dǎo)致對(duì)訓(xùn)練集里同類(lèi)區(qū)域產(chǎn)生誤檢。解決先做一輪「模型輔助標(biāo)注」清洗。用訓(xùn)練好的模型對(duì)訓(xùn)練集和驗(yàn)證集做推理挑出模型置信度高但和現(xiàn)有標(biāo)注框 IoU 小于 0.1 的預(yù)測(cè)把這些區(qū)域提出來(lái)人工確認(rèn)。如果確認(rèn)是漏標(biāo)補(bǔ)上標(biāo)注然后再訓(xùn)練。這一步很費(fèi)人力但對(duì)質(zhì)量不高的數(shù)據(jù)集是必須的投入。某個(gè)模擬項(xiàng)目X里我僅靠補(bǔ)漏標(biāo)就把驗(yàn)證集 mAP 從 0.52 提到了 0.63。5.2 細(xì)長(zhǎng)裂紋匹配不到 anchor召回率低到懷疑人生現(xiàn)象訓(xùn)練過(guò)程正常但驗(yàn)證集 recall 只有 0.3 左右查看檢測(cè)結(jié)果發(fā)現(xiàn)很多裂紋被識(shí)別成很短的碎片框或者完全沒(méi)有輸出。原因默認(rèn) anchor 是針對(duì) COCO 數(shù)據(jù)集聚類(lèi)出來(lái)的COCO 里絕大多數(shù)目標(biāo)寬高比在 1:1 到 1:3 之間。裂紋寬高比動(dòng)輒 1:8 甚至 1:15默認(rèn) anchor 和 ground truth 的 IoU 低于匹配閾值導(dǎo)致這些目標(biāo)在訓(xùn)練時(shí)被標(biāo)記為「無(wú)匹配」而被忽略。解決用訓(xùn)練集標(biāo)注重新聚類(lèi) anchor。YOLO 系工程里一般有anchors.py之類(lèi)的聚類(lèi)腳本輸入訓(xùn)練集 label 文件輸出新的 anchor 值替換到模型配置文件里。聚類(lèi)數(shù)量可以先用默認(rèn)的 9 個(gè)觀察匹配率如果匹配率仍低增加到 12 個(gè)重點(diǎn)保證長(zhǎng)條形 anchor 有覆蓋。聚類(lèi)時(shí)過(guò)濾掉異常寬高比比如寬大于圖像寬 0.9 的標(biāo)注多半是標(biāo)錯(cuò)了。5.3 暗光樣本占比失衡模型學(xué)到的是亮度而不是裂紋現(xiàn)象驗(yàn)證集整體 mAP 不錯(cuò)但按光照分層統(tǒng)計(jì)時(shí)暗光圖像那一組的 mAP 只有正常組的 1/3。原因訓(xùn)練集中暗光圖像占比太低比如只有 5%模型沒(méi)有見(jiàn)過(guò)足夠多的暗光正樣本只能通過(guò)亮度特征猜測(cè)——亮圖傾向于輸出低置信度暗圖傾向于輸出高置信度或直接漏檢。解決按第 3 章的光照分層先檢查和調(diào)整分布。如果暗光圖像實(shí)在很少優(yōu)先做色彩增強(qiáng)類(lèi)的增廣隨機(jī)降低亮度、提高對(duì)比度、加高斯噪聲讓模型看到更多「?jìng)伟倒狻箻颖?。不要用循環(huán)生成對(duì)抗網(wǎng)絡(luò)之類(lèi)的生成式方案成本高且容易引入偽影傳統(tǒng)光度增廣對(duì)這個(gè)任務(wù)夠用。另外可以在推理時(shí)對(duì)輸入圖像做自適應(yīng)的直方圖均衡化CLAHE把暗部紋理拉出來(lái)再進(jìn)模型這在很多軌檢實(shí)拍場(chǎng)景下比換模型結(jié)構(gòu)收益更大。5.4 類(lèi)別不平衡橫裂只有幾十個(gè)框網(wǎng)裂有上千個(gè)框現(xiàn)象多類(lèi)訓(xùn)練時(shí)總 mAP 尚可但按類(lèi)別看樣本少的類(lèi)別 mAP 幾乎為 0PR 曲線不穩(wěn)定。原因橫裂這類(lèi)高危傷損在實(shí)際數(shù)據(jù)里本來(lái)就稀有而網(wǎng)裂分布廣、數(shù)量多。模型在訓(xùn)練時(shí)被大量網(wǎng)裂樣本主導(dǎo)對(duì)橫裂的梯度信號(hào)被稀釋。解決第一按類(lèi)別統(tǒng)計(jì)框數(shù)量確認(rèn)不平衡程度。第二對(duì)樣本量少的類(lèi)別做過(guò)采樣思路是訓(xùn)練時(shí)每輪多加載幾張包含該類(lèi)別的圖像而不是復(fù)制粘貼同一張圖。第三調(diào)整 loss 的類(lèi)別權(quán)重給稀有類(lèi)別更高的權(quán)重系數(shù)。最直接的做法是把稀有類(lèi)別的圖像復(fù)制幾份到訓(xùn)練集里簡(jiǎn)單粗暴但有效注意不要導(dǎo)致數(shù)據(jù)泄漏——同一圖像的所有副本只能出現(xiàn)在訓(xùn)練集。某些代碼庫(kù)支持class_weight參數(shù)直接用也不錯(cuò)。5.5 訓(xùn)練到第 1 個(gè) epoch loss 變成 NaN標(biāo)注坐標(biāo)惹的禍現(xiàn)象模型啟動(dòng)訓(xùn)練后 loss 在幾十個(gè) iteration 內(nèi)變成 NaN訓(xùn)練進(jìn)程直接崩掉或開(kāi)始輸出無(wú)意義的權(quán)重。原因最常見(jiàn)的是標(biāo)注文件里有class_id超出了nc的范圍或者坐標(biāo)值出現(xiàn)極端的負(fù)數(shù)和大于 1 的數(shù)。另一個(gè)原因是某些代碼庫(kù)在歸一化坐標(biāo)時(shí)除以了圖像的寬高如果寬高是 0損壞的圖像文件結(jié)果就是無(wú)窮大。解決用 2.3 節(jié)的檢查腳本先全量過(guò)濾。對(duì)于class_id越界的情況回頭查數(shù)據(jù)集附帶的類(lèi)別說(shuō)明文檔確認(rèn) 0 基還是 1 基索引有時(shí)發(fā)布方自己寫(xiě)混了把類(lèi)別 ID 從 1 開(kāi)始標(biāo)而訓(xùn)練工程默認(rèn)從 0 開(kāi)始需要統(tǒng)一偏移。對(duì)于損壞圖像直接排除并記錄文件名不要修修復(fù)的元數(shù)據(jù)可能帶來(lái)更多不可控問(wèn)題。如果檢查完仍然 NaN再看學(xué)習(xí)率是不是設(shè)得過(guò)高把初始學(xué)習(xí)率除以 10 再試。這類(lèi)問(wèn)題調(diào)試時(shí)間占比最大的是定位過(guò)程——先腳本檢查再挨個(gè)排查別一上來(lái)就改網(wǎng)絡(luò)結(jié)構(gòu)。6. 最后一招在大圖上滑窗推理把細(xì)裂紋找出來(lái)再切成小圖訓(xùn)練完的模型默認(rèn)是 640×640 或 1280×1280 輸入但軌檢相機(jī)拍的原始圖經(jīng)常是 4096×4096 甚至更高。直接把大圖縮放到模型輸入尺寸細(xì)裂紋在縮放過(guò)程中就消失了?;巴评硎菓?yīng)對(duì)這個(gè)問(wèn)題的最可靠方案把大圖切成有重疊的若干小塊逐塊送入模型再把檢測(cè)框映射回原圖坐標(biāo)?;暗暮诵膮?shù)是窗口大小和重疊率。窗口大小一般取模型訓(xùn)練時(shí)的分辨率不要大于 1280否則顯存不夠重疊率取 20%50%重疊的目的是讓跨越窗口邊界的裂紋至少在一個(gè)窗口內(nèi)是完整的避免裂紋被切成兩段導(dǎo)致漏檢。推理時(shí)保留每個(gè)窗口的原始坐標(biāo)偏移把檢測(cè)框換算回完整圖像坐標(biāo)系最后用 NMS非極大值抑制合并重疊窗口產(chǎn)生的重復(fù)檢測(cè)框。NMS 的 IoU 閾值設(shè) 0.5 比較合適太高會(huì)保留重復(fù)框太低會(huì)把相鄰的橫裂和縱裂合并成一個(gè)誤檢框?;巴评淼拇鷥r(jià)是速度變慢一張 4096×4096 的圖用 1280 窗口加 50% 重疊大約要跑 25 個(gè)窗口在普通辦公卡上大概花 2~4 秒。如果對(duì)實(shí)時(shí)性有要求可以先把大圖縮小到 2048 再滑窗速度提升 4 倍左右代價(jià)是小裂紋的召回率會(huì)輕微下降。實(shí)際項(xiàng)目中我一般是離線分析用全分辨率滑窗現(xiàn)場(chǎng)實(shí)時(shí)檢測(cè)用整圖縮放加更高的置信度閾值兩條線并行。偽標(biāo)簽清洗是一個(gè)值得做的收尾動(dòng)作。把訓(xùn)練好的模型在無(wú)標(biāo)注的大圖上跑一遍篩選出置信度大于 0.9 的檢測(cè)框回填成新的標(biāo)注數(shù)據(jù)進(jìn)行二次訓(xùn)練能顯著提升模型在地域變化后的數(shù)據(jù)集上的表現(xiàn)。注意只回填高置信度結(jié)果且只做半次迭代——用偽標(biāo)簽訓(xùn)練出的模型再生成偽標(biāo)簽再訓(xùn)練噪聲會(huì)逐輪積累一般兩輪后就停了。我自己的習(xí)慣是每拿到一份新數(shù)據(jù)先花一個(gè)下午做檢查和劃分再花一個(gè)晚上跑 baseline永遠(yuǎn)不在沒(méi)有看過(guò)具體 bad case 的情況下調(diào)模型結(jié)構(gòu)。裂紋檢測(cè)是一個(gè)檢測(cè)精度與工程成本強(qiáng)相關(guān)的領(lǐng)域你多花一天在數(shù)據(jù)檢查上后面調(diào)試模型的效率可能省出一周。這份數(shù)據(jù)集的價(jià)值恰恰在于它暴露了足夠多的真實(shí)場(chǎng)景噪聲值得你從頭到尾按上面的流程走一遍。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取