數(shù)據(jù)集559張VOC+YOLO格式:從標(biāo)注到訓(xùn)練避坑指南)
簡(jiǎn)介面向目標(biāo)檢測(cè)與計(jì)算機(jī)視覺實(shí)踐的三輪車數(shù)據(jù)集共559張jpg原圖并配套完整的Pascal VOCxml與YOLOtxt標(biāo)注文件可直接送入YOLO系列、SSD等模型進(jìn)行訓(xùn)練和驗(yàn)證。數(shù)據(jù)集中僅含tricycle一個(gè)類別由labelImg人工繪制659個(gè)矩形框標(biāo)注規(guī)范、邊界清晰既可充當(dāng)入門級(jí)目標(biāo)檢測(cè)教學(xué)數(shù)據(jù)也便于開展數(shù)據(jù)增強(qiáng)、類別遷移等擴(kuò)展實(shí)驗(yàn)。整體共1679個(gè)文件除jpg圖片和配對(duì)標(biāo)注外還包含少量附加文件壓縮包僅25.22MB命名規(guī)律、路徑簡(jiǎn)明能夠快速完成數(shù)據(jù)集劃分與格式轉(zhuǎn)換。數(shù)據(jù)集未含分割路徑等冗余內(nèi)容圖片與標(biāo)注一一對(duì)應(yīng)直接可用。目前已有646人學(xué)習(xí)/下載使用該資源可免去自行采集、清洗和標(biāo)注的繁瑣過程有效提升模型開發(fā)、課程設(shè)計(jì)或算法對(duì)比的效率。1. 三輪車檢測(cè)為什么值得單獨(dú)做一個(gè)數(shù)據(jù)集三輪車這個(gè)類目在公開的大規(guī)模檢測(cè)數(shù)據(jù)里一直是個(gè)稀罕物通用數(shù)據(jù)里頂多給你標(biāo)成“自行車”或者“卡車”真正要判斷“這是載貨三輪還是帶篷三輪”的時(shí)候往往只能自己重新標(biāo)。這個(gè)“三輪車數(shù)據(jù)集559張VOCYOLO格式手工labellmg標(biāo)注”正好卡在需求和數(shù)據(jù)之間559張不算大但雙格式標(biāo)注、手工精標(biāo)意味著它可以直接拿來做遷移學(xué)習(xí)起點(diǎn)、驗(yàn)證檢測(cè)流程或者作為業(yè)務(wù)場(chǎng)景的種子數(shù)據(jù)。適合誰想快速跑通三輪車檢測(cè)、又不想從零標(biāo)幾千張圖的從業(yè)者。它的價(jià)值不在“多”而在“干凈”。手工labelImg標(biāo)注的框邊界通常比自動(dòng)標(biāo)注更貼合目標(biāo)尤其是三輪車這種結(jié)構(gòu)復(fù)雜、輪子和車斗容易標(biāo)歪的對(duì)象。559張圖足夠你完成一次完整的訓(xùn)練、驗(yàn)證和調(diào)參閉環(huán)也能暴露小數(shù)據(jù)集最常見的過擬合、類別不平衡和標(biāo)簽不一致問題。這篇文章我就按自己的使用習(xí)慣把這套數(shù)據(jù)的目錄結(jié)構(gòu)、格式轉(zhuǎn)換、訓(xùn)練配置、避坑點(diǎn)一路拆到跑出第一個(gè)像樣的檢測(cè)框。2. 先摸清559張圖的底目錄結(jié)構(gòu)、五類目標(biāo)與雙格式標(biāo)注大多數(shù)情況拿到這類手工標(biāo)注的數(shù)據(jù)集第一件事不是訓(xùn)練而是盤清楚里面到底有什么。三輪車這個(gè)類目比想象中復(fù)雜同樣是三輪載貨、帶篷、快遞、家用、腳踏這五類在用檢測(cè)網(wǎng)絡(luò)時(shí)的判別難度完全不同前兩類的視覺差異可能只是有沒有頂篷標(biāo)注標(biāo)準(zhǔn)稍微一松后面訓(xùn)練出來的置信度就很玄學(xué)。2.1 圖像構(gòu)成與類別分布先數(shù)一遍框再動(dòng)手我習(xí)慣先把“圖數(shù)”“框數(shù)”“每類框數(shù)”三個(gè)數(shù)字跑出來。559張圖像聽起來不多但平均每張圖里會(huì)有1到2個(gè)目標(biāo)一標(biāo)就是七八百個(gè)框這個(gè)規(guī)模人工核查一遍大約需要半天。圖像來源通常比較雜分辨率從接近標(biāo)清到全高清都有白天和傍晚的光照交錯(cuò)出現(xiàn)這意味著訓(xùn)練時(shí)不能把輸入尺寸和光照增強(qiáng)的參數(shù)照搬通用配置。類別定義是這套數(shù)據(jù)能不能用好的關(guān)鍵。按常見三輪車檢測(cè)項(xiàng)目的落地方案一般會(huì)分成五類載貨三輪車斗敞開、帶篷三輪乘客車廂有頂、快遞三輪封閉車廂且有快遞標(biāo)識(shí)、家用三輪小型代步、腳踏三輪人力驅(qū)動(dòng)。分類依據(jù)建議按“車身結(jié)構(gòu)和外露特征”來不要按“用途”猜??爝f三輪和帶篷三輪最容易標(biāo)混因?yàn)榭爝f車也有頂篷差別只在車廂尺寸和涂裝。2.2 VOC格式XML里的annotation、object與bndboxlabelImg默認(rèn)保存的就是VOC格式一個(gè)圖像對(duì)應(yīng)一個(gè)同名XML文件。我最常檢查的四個(gè)關(guān)鍵字段是filename、size、object、bndbox。filename決定了訓(xùn)練時(shí)能不能找到圖size決定了歸一化坐標(biāo)的分母object里的name決定類別bndbox決定框的位置。一份典型的XML長(zhǎng)這樣annotation folderimages/folder filenametricycle_001.jpg/filename size width1280/width height720/height depth3/depth /size object namecargo_tricycle/name bndbox xmin412/xmin ymin188/ymin xmax896/xmax ymax664/ymax /bndbox /object /annotation注意這里的坐標(biāo)是像素絕對(duì)值xmin、ymin是左上角xmax、ymax是右下角不含歸一化。很多入門者第一次轉(zhuǎn)YOLO格式時(shí)直接拿這些絕對(duì)坐標(biāo)除以圖像寬高結(jié)果發(fā)現(xiàn)框全部偏到左上角原因就是把VOC的“左上右下”理解成了“中心點(diǎn)寬高”。VOC格式里不需要你換算它是給人看的YOLO格式才是給訓(xùn)練過程吃的。2.3 YOLO格式class_id與歸一化坐標(biāo)的計(jì)算口徑轉(zhuǎn)成YOLO格式后每張圖對(duì)應(yīng)一個(gè)txt文件每一行是“class_id x_center y_center width height”全部歸一化到0到1之間。class_id從0開始順序要和訓(xùn)練配置里的類別列表完全一致。歸一化的計(jì)算口徑是x_center (xmin xmax) / 2 / image_width y_center (ymin ymax) / 2 / image_height width (xmax - xmin) / image_width height (ymax - ymin) / image_height一份標(biāo)準(zhǔn)的YOLO標(biāo)注長(zhǎng)這樣0 0.510937 0.591667 0.378125 0.661111 2 0.230469 0.301389 0.171875 0.387500這里第一列是類別ID后面四列是歸一化后的框信息小數(shù)位數(shù)建議保留6位。有一個(gè)容易被忽略的細(xì)節(jié)如果XML里的size字段和實(shí)際圖片分辨率不一致按XML算出來的歸一化坐標(biāo)在訓(xùn)練時(shí)會(huì)錯(cuò)位。我拿到任何數(shù)據(jù)集的第一件事就是抽三張圖核對(duì)XML里的size是否等于圖片真實(shí)分辨率這一步能擋掉后面很多莫名其妙的訓(xùn)練問題。3. 從559張到可訓(xùn)練的輸入劃分、轉(zhuǎn)格式與訓(xùn)練參數(shù)數(shù)據(jù)格式摸清了接下來就是把這套數(shù)據(jù)送進(jìn)檢測(cè)網(wǎng)絡(luò)。這里有個(gè)關(guān)鍵認(rèn)知不是把圖片和標(biāo)簽往目錄里一丟就能訓(xùn)練。劃分策略、格式轉(zhuǎn)換腳本、訓(xùn)練參數(shù)三件事任何一個(gè)做糙了都會(huì)讓559張圖的價(jià)值打?qū)φ?。這一章給出的做法是我在類似規(guī)模項(xiàng)目里反復(fù)驗(yàn)證過的路徑。3.1 訓(xùn)練/驗(yàn)證/測(cè)試的劃分別讓同一場(chǎng)景進(jìn)兩個(gè)集合559張的規(guī)模下常見劃分是訓(xùn)練約440張、驗(yàn)證約60張、測(cè)試約59張比例接近8:1:1。但比例只是表象真正要命的是場(chǎng)景泄漏。如果數(shù)據(jù)里同一個(gè)路口、同一個(gè)時(shí)間段拍的連續(xù)幀被隨機(jī)分到了訓(xùn)練和驗(yàn)證兩個(gè)集合驗(yàn)證指標(biāo)會(huì)虛高真實(shí)場(chǎng)景一測(cè)就翻車。我一般會(huì)按“場(chǎng)景分組”來做劃分先按文件名前綴或者拍攝批次把圖像分成小組再把整個(gè)組劃入某一個(gè)集合。簡(jiǎn)單做法是看文件名的數(shù)字段如果連續(xù)編號(hào)代表同一次采集就不要讓相鄰編號(hào)被切到兩邊。如果這套數(shù)據(jù)沒有明確場(chǎng)景標(biāo)記退而求其次的做法是手動(dòng)抽查驗(yàn)證集圖片確認(rèn)沒有和訓(xùn)練集高度相似的連續(xù)幀。# 劃分前先按文件名排序觀察編號(hào)連續(xù)性人工決定是否按組切分 ls images/ | sort | head -20 ls images/ | sort | tail -20這一步看起來笨但在小數(shù)據(jù)集上是性價(jià)比最高的防泄漏手段。我見過有人因?yàn)槭〉暨@一步驗(yàn)證mAP跑了0.89換到實(shí)拍視頻直接掉到0.4排了半天才發(fā)現(xiàn)是場(chǎng)景泄漏惹的禍。3.2 VOC轉(zhuǎn)YOLO的轉(zhuǎn)換腳本與邊界clip如果這套數(shù)據(jù)自帶YOLO格式轉(zhuǎn)換腳本可以跳過但我仍然建議自己在本地跑一遍轉(zhuǎn)換原因有兩個(gè)一是核對(duì)原有txt是否正確二是當(dāng)你后續(xù)要調(diào)整類別定義時(shí)重轉(zhuǎn)一次比手改幾十個(gè)txt靠譜得多。我常用的轉(zhuǎn)換腳本如下import os import xml.etree.ElementTree as ET # 類別映射name - class_id順序不能亂 CLASS_MAP { cargo_tricycle: 0, # 載貨三輪 passenger_tricycle: 1, # 帶篷三輪 express_tricycle: 2, # 快遞三輪 family_tricycle: 3, # 家用三輪 pedal_tricycle: 4 # 腳踏三輪 } def convert_xml_to_yolo(xml_path, out_path, default_w, default_h): tree ET.parse(xml_path) root tree.getroot() # 優(yōu)先用XML里的size字段避免和圖片實(shí)際分辨率不一致 size root.find(size) if size is not None: default_w int(size.find(width).text) default_h int(size.find(height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in CLASS_MAP: print(fskip unknown class: {name}) continue box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) x_center ((xmin xmax) / 2.0) / default_w y_center ((ymin ymax) / 2.0) / default_h box_w (xmax - xmin) / default_w box_h (ymax - ymin) / default_h # 邊界clip避免剛好在圖像邊緣時(shí)歸一化越界 x_center min(max(x_center, 1e-3), 1 - 1e-3) y_center min(max(y_center, 1e-3), 1 - 1e-3) box_w min(max(box_w, 1e-3), 1 - 1e-3) box_h min(max(box_h, 1e-3), 1 - 1e-3) lines.append(f{CLASS_MAP[name]} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}) with open(out_path, w, encodingutf-8) as f: f.write(\n.join(lines))這段腳本的核心邏輯是“讀出XML、按公式歸一化、clip邊界、寫成txt”。其中clip邊界這一步很多人會(huì)漏掉但手工標(biāo)注時(shí)框緊貼圖像邊緣是常事標(biāo)注者手一抖xmax就等于了圖像寬度一除就變成1.0訓(xùn)練時(shí)檢測(cè)網(wǎng)絡(luò)會(huì)認(rèn)為這個(gè)框的右邊界在圖像外面損失計(jì)算直接飄。腳本里的CLASS_MAP順序必須和訓(xùn)練配置里的names列表一一對(duì)應(yīng)否則訓(xùn)練時(shí)類別就全亂了。3.3 訓(xùn)練關(guān)鍵參數(shù)批次、學(xué)習(xí)率、迭代與早停559張的規(guī)模訓(xùn)練參數(shù)不能照搬幾萬張數(shù)據(jù)的大套路。我常用的起點(diǎn)配置如下參數(shù)建議值說明輸入尺寸640x640三輪車屬于中大型目標(biāo)不需要一上來就上1280批次大小16顯存不夠就降到8但不要低于4初始學(xué)習(xí)率0.001遷移學(xué)習(xí)場(chǎng)景下用這個(gè)值起手迭代輪次120超過這個(gè)數(shù)基本在背訓(xùn)練集早停耐心值20連續(xù)20輪驗(yàn)證指標(biāo)不漲就停數(shù)據(jù)增強(qiáng)適度開啟重點(diǎn)是光照和尺度不要極端旋轉(zhuǎn)批次大小這里有一個(gè)取舍559張圖、440張訓(xùn)練如果batch設(shè)32一個(gè)epoch只有不到14步梯度更新太粗如果batch設(shè)4每個(gè)epoch步數(shù)太多訓(xùn)練時(shí)間拉長(zhǎng)不說小數(shù)據(jù)集上還容易震蕩。16是一個(gè)平衡點(diǎn)。學(xué)習(xí)率可以從0.001起手如果前幾個(gè)epoch的損失不降反升先降到0.0005不要直接砍到0.0001那樣收斂會(huì)慢得讓人失去耐心。訓(xùn)練命令的寫法按你選用的檢測(cè)代碼倉庫調(diào)整但結(jié)構(gòu)和參數(shù)項(xiàng)是通用的# tricycle.yaml 里需要寫清楚 train/val 路徑和類別名列表 python train.py \ --data tricycle.yaml \ --weights pretrained_weight.pt \ --imgsz 640 \ --batch 16 \ --epochs 120 \ --patience 20注意--weights不是隨便填的。559張數(shù)據(jù)訓(xùn)練強(qiáng)烈建議用在大規(guī)模通用數(shù)據(jù)上預(yù)訓(xùn)練過的權(quán)重起步哪怕這個(gè)預(yù)訓(xùn)練數(shù)據(jù)里沒有三輪車它學(xué)到的邊緣、紋理和整體形狀特征也能大幅壓低過擬合。從零訓(xùn)練的話120輪基本不夠看。4. 手工標(biāo)注與559張小數(shù)據(jù)的5個(gè)坑現(xiàn)象、原因與處理這套數(shù)據(jù)是labelImg手工標(biāo)注的好處是框貼合目標(biāo)壞處是人工標(biāo)注的不一致性會(huì)在小數(shù)據(jù)集上被放大。559張意味著平均每個(gè)類別只有一百多張圖一個(gè)標(biāo)錯(cuò)的框、一類模糊的定義都會(huì)直接影響檢測(cè)效果。這一章我挑5個(gè)高頻坑按“現(xiàn)象、原因、解決”寫清楚。4.1 類別定義模糊帶篷三輪和快遞三輪到底怎么分現(xiàn)象訓(xùn)練完成后驗(yàn)證結(jié)果里帶篷三輪和快遞三輪互相誤檢嚴(yán)重置信度都不低混淆矩陣?yán)镞@兩類的格子特別亮。原因標(biāo)注階段沒有預(yù)先寫清楚分類規(guī)則。標(biāo)的人有的按“有沒有篷”分帶篷三輪和快遞三輪都有篷自然就標(biāo)混了有的按“用途”猜看到涂裝像快遞的就標(biāo)快遞。labelImg標(biāo)注界面里沒有強(qiáng)制規(guī)則提示全靠標(biāo)注者自己拿捏。解決我在做這類數(shù)據(jù)時(shí)會(huì)先寫一頁標(biāo)注規(guī)范核心是“看結(jié)構(gòu)不看用途”??爝f三輪必須同時(shí)滿足封閉車廂、尺寸較大、有明顯快遞箱體特征三個(gè)條件只滿足有頂篷的一律歸帶篷三輪。規(guī)范寫好后需要對(duì)現(xiàn)有標(biāo)注跑一遍巡檢把“車廂比例”或“長(zhǎng)寬比”異常的目標(biāo)抽出來人工復(fù)核。這個(gè)坑不補(bǔ)后面所有調(diào)參都沒意義。4.2 邊界貼近圖像邊緣歸一化坐標(biāo)下越界現(xiàn)象訓(xùn)練日志里loss出現(xiàn)異常波動(dòng)或者檢測(cè)框把圖像邊界當(dāng)成目標(biāo)邊界輸出框直接卡在畫面邊緣。原因手工標(biāo)注時(shí)框貼邊轉(zhuǎn)YOLO格式時(shí)沒有做clip歸一化坐標(biāo)出現(xiàn)0.0或1.0。檢測(cè)網(wǎng)絡(luò)在做錨框匹配時(shí)這種越界坐標(biāo)會(huì)讓正樣本匹配變得混亂。解決在轉(zhuǎn)換腳本里加clip邊界約束把坐標(biāo)限制在1e-3到1-1e-3之間。另外訓(xùn)練前用腳本掃一遍所有txt標(biāo)簽文件檢查是否存在小于0或大于1的數(shù)值# 掃描所有l(wèi)abel文件中是否存在越界坐標(biāo) grep -rnE [0-9]\.([0-9]{0,6}) labels/ | awk -F {for(i2;iNF;i) if($i0 || $i1) print $0}這個(gè)坑我踩過當(dāng)時(shí)整整一個(gè)下午在找loss不收斂的原因最后發(fā)現(xiàn)是有三張圖的框xmax等于圖像寬度歸一化后是1.0。手工標(biāo)注是無法完全避免貼邊框的所以clip和掃描這兩步不能省。4.3 驗(yàn)證指標(biāo)先漲后跌小數(shù)據(jù)過擬合的三個(gè)信號(hào)現(xiàn)象前30輪訓(xùn)練損失和驗(yàn)證損失一起下降隨后訓(xùn)練損失繼續(xù)降驗(yàn)證損失掉頭上升mAP在某個(gè)峰值后開始回落。原因559張的訓(xùn)練集對(duì)檢測(cè)網(wǎng)絡(luò)來說容量有限訓(xùn)練到中后期網(wǎng)絡(luò)開始“背”訓(xùn)練圖像而不是學(xué)泛化特征。這是小數(shù)據(jù)集最典型的表現(xiàn)幾乎逃不掉。解決第一個(gè)信號(hào)看驗(yàn)證損失和訓(xùn)練損失的剪刀差出現(xiàn)就準(zhǔn)備早停第二個(gè)信號(hào)看mAP50-95和mAP50的差距如果差距持續(xù)拉大說明網(wǎng)絡(luò)在過擬合第三個(gè)信號(hào)是類別AP的波動(dòng)某個(gè)類別的AP突然大幅下降但其他類正常多半是在背圖。我是這樣處理的早停耐心值設(shè)為20在驗(yàn)證mAP達(dá)到峰值后繼續(xù)觀察10輪不漲就回滾到峰值時(shí)的權(quán)重。4.4 labelImg的保存問題文件名不對(duì)、XML沒落盤現(xiàn)象訓(xùn)練時(shí)報(bào)FileNotFoundError或者看了標(biāo)注統(tǒng)計(jì)某些圖片對(duì)應(yīng)的XML缺失但當(dāng)時(shí)標(biāo)注時(shí)明明保存過。原因labelImg在切換圖片目錄或者手動(dòng)修改文件名后容易出現(xiàn)XML名稱和圖片名稱對(duì)不上的情況另外標(biāo)注過程中不小心點(diǎn)了“跳過”而不是“保存”也會(huì)漏掉XML。還有一種是標(biāo)注中途軟件崩潰最后幾張圖沒寫盤。解決訓(xùn)練前跑一個(gè)文件名匹配腳本把圖片名和XML名一一核對(duì)缺失的列出來補(bǔ)標(biāo)或丟棄。這個(gè)小腳本順手就能寫import os img_dir images xml_dir annotations missing_xml [] for f in os.listdir(img_dir): stem os.path.splitext(f)[0] xml_path os.path.join(xml_dir, stem .xml) if not os.path.exists(xml_path): missing_xml.append(f) print(fimages with missing xml: {len(missing_xml)}) for name in missing_xml[:20]: print(name)這類問題和數(shù)據(jù)本身好壞無關(guān)純粹是交接數(shù)據(jù)時(shí)最容易翻車的地方。我每次拿到數(shù)據(jù)集都會(huì)先跑一遍這個(gè)腳本成本一分鐘換來的是一整輪訓(xùn)練的確定性。4.5 各類別數(shù)量不均少數(shù)類AP被拉低的處理現(xiàn)象五類目標(biāo)里載貨三輪的框數(shù)明顯多家用三輪的框數(shù)明顯少訓(xùn)練完看每類AP少的類明顯偏低。原因數(shù)據(jù)采集時(shí)目標(biāo)出現(xiàn)頻率本身就不均衡手工標(biāo)注只是如實(shí)反映了現(xiàn)實(shí)分布。559張圖里載貨三輪可能有兩百多個(gè)框家用三輪可能只有五六十個(gè)。檢測(cè)網(wǎng)絡(luò)對(duì)樣本多的類學(xué)得更充分這是數(shù)據(jù)分布決定的不完全是網(wǎng)絡(luò)的問題。解決優(yōu)先做的是“擴(kuò)數(shù)據(jù)”少類別的補(bǔ)充優(yōu)先級(jí)要高于整體數(shù)據(jù)量擴(kuò)不了的情況下在訓(xùn)練時(shí)對(duì)少類別做在線重采樣或者在評(píng)估時(shí)把mAP改為按類別平均而不是按框數(shù)加權(quán)。還有一個(gè)實(shí)用技巧是調(diào)低少類別的置信度閾值讓網(wǎng)絡(luò)更愿意“猜”少類別代價(jià)是誤檢增加需要按業(yè)務(wù)場(chǎng)景權(quán)衡。5. 用這份數(shù)據(jù)跑通第一個(gè)檢測(cè)流程從統(tǒng)計(jì)到出框前幾章把數(shù)據(jù)本身吃透了這一章進(jìn)入動(dòng)手環(huán)節(jié)。從拿到數(shù)據(jù)集到看到第一張帶框輸出圖我通常走四步統(tǒng)計(jì)底數(shù)、核對(duì)目錄、訓(xùn)練評(píng)估、推理驗(yàn)證。每一步都有明確目的不搞多余動(dòng)作。5.1 跑通前先統(tǒng)計(jì)底數(shù)圖片數(shù)、框總數(shù)、每個(gè)類別框數(shù)數(shù)據(jù)集到手第一件事永遠(yuǎn)是統(tǒng)計(jì)不要直接開訓(xùn)。統(tǒng)計(jì)腳本幫你看清三個(gè)問題圖數(shù)是否和描述一致、每類框數(shù)是否均衡、有沒有標(biāo)注遺漏。我常用的統(tǒng)計(jì)邏輯是按XML統(tǒng)計(jì)類別import os import xml.etree.ElementTree as ET xml_dir annotations cls_counter {} for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, xml_file)) root tree.getroot() for obj in root.findall(object): name obj.find(name).text cls_counter[name] cls_counter.get(name, 0) 1 for name, count in cls_counter.items(): print(f{name}: {count}) print(ftotal boxes: {sum(cls_counter.values())})這段腳本的輸出能直接決定你后續(xù)的策略。如果某一類的框數(shù)不到50就應(yīng)該優(yōu)先補(bǔ)這類數(shù)據(jù)或者考慮合并類別而不是強(qiáng)行訓(xùn)練五類。5.2 核對(duì)目錄缺失標(biāo)簽與失配文件排查統(tǒng)計(jì)完框數(shù)下一步檢查目錄完整性。除了4.4節(jié)提到的“缺XML”問題還要檢查圖片和標(biāo)簽是否在同一個(gè)訓(xùn)練列表里、圖片擴(kuò)展名是否統(tǒng)一、有沒有重復(fù)圖片。我一般再跑一段檢查確保訓(xùn)練列表里的每一行都能同時(shí)找到圖片和標(biāo)簽# 逐行檢查train.txt中的每個(gè)條目 while read line; do img$line label${line%.jpg}.txt if [ ! -f $img ]; then echo missing img: $img; fi if [ ! -f $label ]; then echo missing label: $label; fi done train.txt這一步過一遍能擋住90%的訓(xùn)練中斷問題。我見過有人拿著好好的數(shù)據(jù)集因?yàn)閳D片擴(kuò)展名有的是jpg、有的是JPG訓(xùn)練列表沒區(qū)分大小寫結(jié)果跑了一晚上全是空輸入。5.3 訓(xùn)練、評(píng)估與首次調(diào)參目錄核對(duì)無誤后按3.3節(jié)的參數(shù)起訓(xùn)練。訓(xùn)練過程中不要只盯著loss曲線重點(diǎn)看驗(yàn)證集上的mAP變化。第一輪訓(xùn)練結(jié)束后記錄三個(gè)指標(biāo)mAP50、mAP50-95、每類AP。首次調(diào)參的優(yōu)先級(jí)我是這么排的先看輸入尺寸再看學(xué)習(xí)率最后看增強(qiáng)策略。輸入尺寸在640和960之間試一輪三輪車這類目標(biāo)如果部署畫面的拍攝距離較遠(yuǎn)尺寸小導(dǎo)致目標(biāo)在圖中占比例低就需要更大輸入如果目標(biāo)近大遠(yuǎn)小明顯640足夠。學(xué)習(xí)率調(diào)整看訓(xùn)練初段的損失走向。增強(qiáng)策略調(diào)整放最后因?yàn)樾?shù)據(jù)集上增強(qiáng)過度會(huì)制造“偽樣本”反而干擾網(wǎng)絡(luò)學(xué)習(xí)真實(shí)特征。5.4 推理驗(yàn)證把驗(yàn)證集圖片跑一遍看輸出圖指標(biāo)只能告訴你“好不好”輸出圖才能告訴你“哪里不對(duì)”。我把驗(yàn)證集的輸出圖按一行四張排列肉眼過一遍重點(diǎn)看三個(gè)細(xì)節(jié)框是不是緊貼車身、有沒有把三輪車旁邊的行人或電動(dòng)車圈進(jìn)去、錯(cuò)誤框主要出現(xiàn)在哪類目標(biāo)上。這個(gè)習(xí)慣幫我抓到過很多指標(biāo)看不出來的問題。比如有一次mAP50已經(jīng)到0.85但輸出圖上所有載貨三輪的框都偏大了一圈原因是標(biāo)注時(shí)把車斗后方的空隙也圈了進(jìn)去解決方式不是調(diào)參數(shù)而是重新調(diào)整那批標(biāo)注的邊界。6. 數(shù)據(jù)再升級(jí)增強(qiáng)、半自動(dòng)標(biāo)注與針對(duì)性調(diào)優(yōu)559張做完一輪訓(xùn)練只是第一步。要讓這個(gè)方向真正可用通常還需要把數(shù)據(jù)規(guī)模滾上去同時(shí)針對(duì)三輪車的特點(diǎn)做針對(duì)性調(diào)整。這一章講三個(gè)我實(shí)際用過的做法。6.1 針對(duì)三輪車的增強(qiáng)策略幅度要克制三輪車是一個(gè)結(jié)構(gòu)特征明顯的目標(biāo)輪子、車斗、車把、頂篷構(gòu)成一個(gè)穩(wěn)定的骨架。訓(xùn)練增強(qiáng)的重點(diǎn)應(yīng)該放在光照擾動(dòng)、隨機(jī)縮放和輕微平移上而不是極端旋轉(zhuǎn)和扭曲。我一般會(huì)把隨機(jī)旋轉(zhuǎn)角度限制在正負(fù)10度以內(nèi)超過這個(gè)范圍三輪車的結(jié)構(gòu)比例會(huì)被破壞網(wǎng)絡(luò)學(xué)到的是變形的三輪車而不是真實(shí)的三輪車。光照增強(qiáng)可以適當(dāng)加強(qiáng)尤其是早晚和逆光場(chǎng)景。這類數(shù)據(jù)里白天和傍晚的光照差異明顯如果訓(xùn)練時(shí)的光照擾動(dòng)不足部署時(shí)遇到逆光路段的檢測(cè)會(huì)明顯變差。做法是調(diào)高亮度對(duì)比度擾動(dòng)的概率讓網(wǎng)絡(luò)見過足夠多“看不清楚”的樣本。6.2 半自動(dòng)標(biāo)注559張?jiān)趺礉L到千級(jí)手工標(biāo)注559張已經(jīng)夠累了要擴(kuò)到一千張以上純手工不現(xiàn)實(shí)。常見做法是用當(dāng)前訓(xùn)練好的檢測(cè)網(wǎng)絡(luò)對(duì)新采集圖片做預(yù)標(biāo)注人工只負(fù)責(zé)修正。流程是新圖片先跑一遍推理置信度大于0.6的框保留人工檢查漏檢和錯(cuò)框修正后回填到數(shù)據(jù)集。這樣一小時(shí)的標(biāo)注量能頂手工兩三小時(shí)。代價(jià)是網(wǎng)絡(luò)自身的不完美會(huì)被帶進(jìn)新標(biāo)注里形成“確認(rèn)偏誤”——新數(shù)據(jù)只會(huì)強(qiáng)化網(wǎng)絡(luò)已經(jīng)學(xué)會(huì)的模式很難給網(wǎng)絡(luò)帶來認(rèn)知之外的多樣性。所以半自動(dòng)標(biāo)注擴(kuò)展出來的數(shù)據(jù)要定期人工隨機(jī)抽檢抽檢比例不低于20%。6.3 針對(duì)性調(diào)優(yōu)類別裁剪與閾值設(shè)置如果你的業(yè)務(wù)場(chǎng)景里根本不需要區(qū)分快遞三輪和帶篷三輪訓(xùn)練時(shí)直接合并這兩類效果提升會(huì)非常明顯。類別合并減少了網(wǎng)絡(luò)需要區(qū)分的類間差異原本被混淆的樣本變成一致的正樣本mAP通常能漲三到五個(gè)點(diǎn)。這不算作弊而是讓任務(wù)定義和業(yè)務(wù)目標(biāo)對(duì)齊。置信度閾值不要固定不動(dòng)。檢測(cè)網(wǎng)絡(luò)對(duì)小目標(biāo)或者罕見姿態(tài)的三輪車天然置信度低如果在部署場(chǎng)景里漏檢的代價(jià)高于誤檢把置信度閾值從0.5降到0.3把非極大值抑制的IoU閾值從0.5調(diào)到0.6能救回來不少邊界場(chǎng)景。說句實(shí)在話我最早拿這種手工標(biāo)注的小數(shù)據(jù)集時(shí)也走過彎路。當(dāng)時(shí)拿到數(shù)據(jù)第一件事就是配置訓(xùn)練參數(shù)結(jié)果訓(xùn)練連續(xù)翻車最后才發(fā)現(xiàn)是類別定義沒統(tǒng)一、坐標(biāo)越界沒清掉?,F(xiàn)在我的習(xí)慣已經(jīng)固化了不管數(shù)據(jù)集描述得多完整先統(tǒng)計(jì)、再核對(duì)、后訓(xùn)練。這三步看起來笨但能省下后面十倍的排查時(shí)間。這份三輪車數(shù)據(jù)足夠你走完一整個(gè)檢測(cè)落地的閉環(huán)希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取