100分類實(shí)戰(zhàn):細(xì)粒度圖像識(shí)別中如何用好獨(dú)立測(cè)試集)
簡介本資源是面向深度學(xué)習(xí)圖像識(shí)別方向研究者與工程師的專用測(cè)試數(shù)據(jù)集聚焦航空器細(xì)粒度分類任務(wù)特別適用于模型性能評(píng)估與泛化能力驗(yàn)證。數(shù)據(jù)集嚴(yán)格限定為飛機(jī)100類別的測(cè)試子集共3333張高質(zhì)量JPG圖像占全部2000個(gè)文件中的1998個(gè)輔以1個(gè)類別映射JSON文件和1個(gè)可視化Python腳本開箱即用無需額外標(biāo)注或格式轉(zhuǎn)換。壓縮包總大小878.43MB解壓后形成標(biāo)準(zhǔn)ImageFolder結(jié)構(gòu)data-test根目錄下包含100個(gè)子文件夾每類對(duì)應(yīng)獨(dú)立文件夾并以原始機(jī)型命名如波音737-76J便于直接接入PyTorch/TensorFlow等框架的DataLoader。目前已有588人學(xué)習(xí)下載配套的classes.json支持類別索引快速查詢可視化腳本可一鍵展示樣本分布與典型圖像顯著提升測(cè)試流程效率與結(jié)果可解釋性。 最近在幫一個(gè)做航空?qǐng)鼍白R(shí)別的朋友調(diào)試模型他發(fā)來一個(gè)數(shù)據(jù)集鏈接附帶了一句讓我印象很深的話這次測(cè)評(píng)只給測(cè)試集標(biāo)簽在主辦方手里模型跑出來的結(jié)果就是最終成績。這句話幾乎概括了深度學(xué)習(xí)圖像識(shí)別里最容易被人忽視的環(huán)節(jié)——測(cè)試集的意義。很多初學(xué)者接觸到的訓(xùn)練集、測(cè)試集往往是同一批數(shù)據(jù)隨手一split訓(xùn)練時(shí)還反復(fù)在測(cè)試集上觀摩結(jié)果最后真正面對(duì)一個(gè)獨(dú)立測(cè)試集時(shí)才發(fā)現(xiàn)模型的泛化能力遠(yuǎn)低于自己當(dāng)初的預(yù)期。而飛機(jī)100分類數(shù)據(jù)集測(cè)試集正是一個(gè)典型的用于裸考的數(shù)據(jù)集它不會(huì)陪你演練它的存在就是用來檢驗(yàn)?zāi)阌?xùn)練的模型在沒見過的新樣本面前到底幾斤幾兩。這篇文章我想從飛機(jī)100分類這個(gè)具體場景出發(fā)聊聊拿到這樣的數(shù)據(jù)集后怎么理解它的地位怎么準(zhǔn)備數(shù)據(jù)流水線怎么設(shè)計(jì)評(píng)估流程會(huì)遇到哪些細(xì)粒度分類特有的坑以及這個(gè)數(shù)據(jù)集背后能延伸出什么實(shí)際應(yīng)用。適合正在做圖像分類入門、準(zhǔn)備參加視覺競賽或者需要在遙感、機(jī)場、航空器識(shí)別場景下做細(xì)粒度目標(biāo)識(shí)別的朋友參考。順帶說一句文章里給的代碼思路都偏PyTorch但你完全可以把同樣的邏輯遷移到TensorFlow、PaddlePaddle或者Ultralytics的框架上去。1. 為什么只有測(cè)試集的數(shù)據(jù)集反而最考驗(yàn)功力1.1 訓(xùn)練集、驗(yàn)證集、測(cè)試集到底在唱哪出戲很多剛上手深度學(xué)習(xí)的人對(duì)數(shù)據(jù)集的劃分只有一個(gè)模糊概念訓(xùn)練集用來訓(xùn)練測(cè)試集用來測(cè)試。這話大方向沒錯(cuò)但在真實(shí)項(xiàng)目中三個(gè)集合的分工遠(yuǎn)比這句話細(xì)。訓(xùn)練集是教材模型從里面反復(fù)學(xué)習(xí)特征更新權(quán)重。驗(yàn)證集是模擬考它的作用是調(diào)超參數(shù)、做早停Early Stopping、判斷模型有沒有過擬合。你可以反復(fù)在上面看結(jié)果、調(diào)整策略因?yàn)槟P筒]有直接學(xué)過它。測(cè)試集才是期末考試卷和答案嚴(yán)格隔離模型在訓(xùn)練階段絕對(duì)不能接觸。它衡量的是模型面對(duì)從未見過的數(shù)據(jù)時(shí)是否真的學(xué)到了可泛化的規(guī)律。飛機(jī)100分類數(shù)據(jù)集測(cè)試集這種以測(cè)試集為唯一交付物的形態(tài)本質(zhì)上是在強(qiáng)迫你遵守教育學(xué)的鐵律不能拿考過的題去衡量學(xué)生的真實(shí)水平。倘若訓(xùn)練時(shí)把測(cè)試集的信息——哪怕是統(tǒng)計(jì)信息——偷偷用進(jìn)去后來評(píng)估出來的分?jǐn)?shù)就失去了公信力。在工程實(shí)踐中很多人會(huì)犯一個(gè)隱蔽的錯(cuò)誤總愛拿測(cè)試集去做事后驗(yàn)證看看模型在測(cè)試集上表現(xiàn)差是哪個(gè)類拖后腿然后針對(duì)性地調(diào)整數(shù)據(jù)增強(qiáng)參數(shù)。這在日常自嗨項(xiàng)目里問題不大但一旦到了公開測(cè)評(píng)、比賽榜單或第三方驗(yàn)收?qǐng)鼍斑@就是典型的測(cè)試集泄漏最后用測(cè)試集調(diào)參出來的模型上線面對(duì)真實(shí)分布時(shí)往往會(huì)原形畢露。1.2 拿到一個(gè)測(cè)試集背后通常是三類任務(wù)在等你當(dāng)有人給你只有測(cè)試集的數(shù)據(jù)時(shí)先想清楚自己處在什么場景這會(huì)直接影響后續(xù)所有操作。第一類是競賽評(píng)測(cè)。數(shù)據(jù)集的訓(xùn)練集有標(biāo)簽測(cè)試集沒有標(biāo)簽?zāi)P屯评沓龅慕Y(jié)果提交到平臺(tái)由主辦方用他們手里的隱藏標(biāo)簽計(jì)算榜單分?jǐn)?shù)。你真正能控制的只有訓(xùn)練集和驗(yàn)證集的劃分方式測(cè)試集則是一個(gè)封閉的黑盒。飛機(jī)100分類數(shù)據(jù)集如果出現(xiàn)在這種場景里通常還有提交格式、類別編號(hào)等約定先讀清楚規(guī)則再動(dòng)手。第二類是模型選型。一家公司想上一套飛機(jī)分類系統(tǒng)采購方會(huì)提供一批帶標(biāo)簽或者不帶標(biāo)簽的獨(dú)立測(cè)試數(shù)據(jù)讓多個(gè)候選模型分別輸出結(jié)果。這個(gè)測(cè)試集直接決定了誰能中標(biāo)。此時(shí)你的目標(biāo)不是做出一個(gè)在自建數(shù)據(jù)上好看的模型而是讓自己的模型在對(duì)方的封閉測(cè)試集上表現(xiàn)最穩(wěn)。第三類是學(xué)術(shù)基準(zhǔn)。論文里要報(bào)告我們?cè)赬X飛機(jī)數(shù)據(jù)集上達(dá)到SOTA為了方便和同行公平對(duì)比所有人必須在同一個(gè)官方測(cè)試集上跑。這個(gè)時(shí)候測(cè)試集的唯一價(jià)值就是可復(fù)現(xiàn)、可對(duì)比。搞明白這三類場景后你會(huì)發(fā)現(xiàn)測(cè)試集不僅是一份數(shù)據(jù)它本質(zhì)上是一種評(píng)估契約。敬畏這份契約是做好深度學(xué)習(xí)項(xiàng)目的第一步。2. 飛機(jī)100分類到底難在哪細(xì)粒度識(shí)別的三個(gè)硬骨頭2.1 100類不是100個(gè)物種是看起來都差不多的近鄰類飛機(jī)100分類如果只是區(qū)分客機(jī)、戰(zhàn)斗機(jī)、直升機(jī)那壓根不需要上深度學(xué)習(xí)傳統(tǒng)圖像特征加個(gè)SVM都能做到七七八八。但100類意味著什么意味著標(biāo)簽體系是按廠商、系列、型號(hào)往下切的比如波音737下面的737-700、737-800、737-900空客A320系列下的A319、A320、A321它們之間的外觀差異可能僅僅是機(jī)身長度幾米之差、艙門數(shù)量少一個(gè)、翼尖小翼形狀不同。這就叫細(xì)粒度圖像識(shí)別Fine-Grained Image Recognition它的核心難點(diǎn)不是這是什么物種而是同一物種下的不同亞種怎么區(qū)分。貓和狗之間的差異是結(jié)構(gòu)性的而737-800和737-900之間的差異往往是一個(gè)局部細(xì)節(jié)。模型如果只學(xué)整體輪廓很容易把同系列不同型號(hào)混在一起。在深度學(xué)習(xí)里解決細(xì)粒度分類的思路大體分兩條一是用更精細(xì)的注意力機(jī)制讓模型主動(dòng)聚焦到機(jī)頭、機(jī)翼、尾翼、發(fā)動(dòng)機(jī)短艙這些判別性區(qū)域二是用更強(qiáng)的數(shù)據(jù)增強(qiáng)比如隨機(jī)裁剪出局部區(qū)域強(qiáng)制模型學(xué)習(xí)部件特征而不是只盯整體。后面我會(huì)展開講。2.2 視角、遮擋、光照、背景同一個(gè)型號(hào)在不同環(huán)境下的外貌方差飛機(jī)分類數(shù)據(jù)集有個(gè)天然讓初學(xué)者頭疼的地方同類樣本的類內(nèi)方差I(lǐng)ntra-class Variance非常大而不同類的類間方差I(lǐng)nter-class Variance反而很小。一個(gè)典型的場景同樣一架空客A320停在地面平拍、起飛時(shí)仰拍、遙感衛(wèi)星俯拍、機(jī)場監(jiān)控低分辨率拍四張圖在像素層面幾乎不像同一個(gè)東西。飛機(jī)又很吃光照中午頂光下機(jī)翼反光會(huì)蓋掉機(jī)身細(xì)節(jié)陰天時(shí)輪廓模糊陰影遮擋時(shí)發(fā)動(dòng)機(jī)位置都看不清。再加上機(jī)場跑道上常見的地勤車、廊橋、塔臺(tái)等復(fù)雜背景模型要想在所有條件下穩(wěn)定分類訓(xùn)練數(shù)據(jù)必須覆蓋足夠多變的拍攝條件。這里有個(gè)很容易踩的坑訓(xùn)練集里如果大量是某機(jī)場順光、正側(cè)面的標(biāo)準(zhǔn)照模型很可能學(xué)到的是在標(biāo)準(zhǔn)照環(huán)境下的分類而不是飛機(jī)的分類。一旦測(cè)試集換成俯拍、逆光或監(jiān)控視角準(zhǔn)確率斷崖式下跌。所以拿到飛機(jī)100分類測(cè)試集之前先看看訓(xùn)練集的圖像來源分布再?zèng)Q定數(shù)據(jù)增強(qiáng)策略是很有必要的。2.3 細(xì)粒度分類對(duì)數(shù)據(jù)增強(qiáng)和注意力機(jī)制的額外要求普通分類的數(shù)據(jù)增強(qiáng)套路是隨機(jī)裁剪、水平翻轉(zhuǎn)、顏色抖動(dòng)這套組合拳在細(xì)粒度分類上遠(yuǎn)遠(yuǎn)不夠。因?yàn)槟P捅緛砭腿菀缀鲆暭?xì)微差異你隨機(jī)裁剪反而可能把最具判別性的機(jī)頭或尾翼截掉。我個(gè)人在細(xì)粒度分類上更推薦這樣幾招隨機(jī)裁剪后放大Random Resized Crop強(qiáng)制模型從局部特征里找線索。隨機(jī)擦除Random Erasing或者Cutout防止模型死記某個(gè)部件的固定位置。Mixup、CutMix這種樣本混合增強(qiáng)在細(xì)粒度任務(wù)里能明顯提升魯棒性。測(cè)試時(shí)增強(qiáng)TTA推理時(shí)把每張圖做水平翻轉(zhuǎn)、多尺度變換最后對(duì)概率取平均。模型結(jié)構(gòu)層面可以優(yōu)先選帶通道注意力或空間注意力的網(wǎng)絡(luò)比如SENet、CBAM或者干脆用ViT這類Transformer架構(gòu)它們?cè)诩?xì)粒度任務(wù)里通常比純CNN更能捕捉判別性局部區(qū)域。訓(xùn)練完之后還可以用Grad-CAM畫一下激活圖看看模型實(shí)際盯的是機(jī)身還是背景——這一步對(duì)判斷模型到底學(xué)會(huì)了沒有特別有用。3. 從訓(xùn)練到測(cè)試搭建一個(gè)能刷飛機(jī)100分類測(cè)試集的圖像識(shí)別流程3.1 數(shù)據(jù)加載與預(yù)處理一切準(zhǔn)確率的地基模型再花哨如果數(shù)據(jù)讀取和預(yù)處理寫得潦草最終準(zhǔn)確率都會(huì)打折扣。飛機(jī)100分類數(shù)據(jù)集如果按ImageFolder的標(biāo)準(zhǔn)目錄組織PyTorch的torchvision.datasets.ImageFolder可以直接讀如果給了單獨(dú)的CSV標(biāo)注文件就自己寫一個(gè)Dataset子類。核心是把訓(xùn)練集和驗(yàn)證集分開測(cè)試集單獨(dú)一條流水線別把測(cè)試集的標(biāo)簽或者統(tǒng)計(jì)信息混進(jìn)訓(xùn)練流程。以PyTorch為例基礎(chǔ)的預(yù)處理一般長這樣from torchvision import transforms # 訓(xùn)練側(cè)增強(qiáng)拉滿 train_transform transforms.Compose([ transforms.RandomResizedCrop(224, scale(0.7, 1.0)), transforms.RandomHorizontalFlip(), transforms.ColorJitter(brightness0.2, contrast0.2, saturation0.1), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) # 驗(yàn)證/測(cè)試側(cè)只要確定性的預(yù)處理 test_transform transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ])這里有兩個(gè)細(xì)節(jié)值得展開。第一個(gè)是歸一化參數(shù)上面用的是ImageNet的mean和std因?yàn)槟憬又鴷?huì)用ImageNet預(yù)訓(xùn)練權(quán)重做遷移學(xué)習(xí)輸入分布必須和預(yù)訓(xùn)練時(shí)的分布對(duì)齊。第二個(gè)是Resize到256再CenterCrop到224這是很多分類任務(wù)的默認(rèn)做法它相當(dāng)于在測(cè)試時(shí)做了一個(gè)輕微的空間擾動(dòng)比直接把圖拉到224效果略好。要注意的是RandomResizedCrop里的scale(0.7, 1.0)是我針對(duì)細(xì)粒度分類調(diào)的。尺度下限別設(shè)太低否則裁剪出的區(qū)域太碎丟失關(guān)鍵部件信息。如果你發(fā)現(xiàn)模型總把相鄰型號(hào)搞混可以嘗試把尺度下限調(diào)到0.5強(qiáng)制模型關(guān)注更局部的特征。3.2 模型選型為什么我推薦先啃遷移學(xué)習(xí)這塊紅利飛機(jī)100分類數(shù)據(jù)集的樣本量通常不會(huì)大到能支撐一個(gè)幾十層的CNN從零開始訓(xùn)練。如果只靠訓(xùn)練集那幾萬張甚至幾千張圖從零訓(xùn)練一個(gè)ResNet50結(jié)果往往是欠擬合或者嚴(yán)重過擬合。這時(shí)候最劃算的做法是遷移學(xué)習(xí)拿一個(gè)在ImageNet上預(yù)訓(xùn)練好的模型把最后的全連接層換掉在飛機(jī)數(shù)據(jù)集上微調(diào)。遷移學(xué)習(xí)為什么有效因?yàn)镮mageNet預(yù)訓(xùn)練模型的前幾層已經(jīng)學(xué)會(huì)了通用的視覺特征——邊緣、紋理、顏色塊、局部形狀這些底層特征對(duì)任何圖像任務(wù)都有用。你要做的只是讓模型的高層特征去適配飛機(jī)型號(hào)這個(gè)特定任務(wù)。這就像讓一個(gè)已經(jīng)會(huì)打羽毛球的人去學(xué)網(wǎng)球他不需要重新學(xué)眼睛追蹤球和跑動(dòng)到位只需要調(diào)整揮拍動(dòng)作。PyTorch里加載預(yù)訓(xùn)練模型并更換分類頭代碼非常簡單import torchvision.models as models import torch.nn as nn # backbone model models.resnet50(weightsmodels.ResNet50_Weights.IMAGENET1K_V1) num_features model.fc.in_features num_classes 100 model.fc nn.Sequential( nn.Dropout(0.2), nn.Linear(num_features, num_classes) )替換fc層的時(shí)候我習(xí)慣在前面加一個(gè)Dropout尤其是在訓(xùn)練樣本不算多的情況下Dropout能明顯抑制過擬合。你還可以把model.fc替換成兩層的小MLP比如Linear - ReLU - Dropout - Linear效果往往比單層更好但需要配合學(xué)習(xí)率調(diào)度來調(diào)否則容易震蕩。模型選型方面如果算力有限優(yōu)先ResNet50或者EfficientNet-B0/B1如果算力充足可以直接上ViT-B/16。實(shí)測(cè)下來ViT在細(xì)粒度任務(wù)上的收斂速度不如CNN快但上限通常更高尤其是當(dāng)你手里有多尺度數(shù)據(jù)增強(qiáng)撐腰的時(shí)候。3.3 訓(xùn)練超參數(shù)與策略模型能不能收斂就差這口氣選好模型后訓(xùn)練配置也很有講究。Fine-tune預(yù)訓(xùn)練模型時(shí)我最常用的基礎(chǔ)配置是優(yōu)化器AdamW初始學(xué)習(xí)率1e-4權(quán)重衰減1e-4。或者SGDmomentum0.9初始學(xué)習(xí)率1e-3配合CosineAnnealingLR。分類頭新加的fc層的學(xué)習(xí)率可以設(shè)成骨干網(wǎng)絡(luò)學(xué)習(xí)率的10倍因?yàn)樾聦右獜牧銓W(xué)步子可以邁大點(diǎn)。Batch size8卡以下用32或者64顯存不夠就降到16但盡量不要低于16否則BatchNorm統(tǒng)計(jì)不穩(wěn)定。Epoch30到50輪配合早停監(jiān)控驗(yàn)證集準(zhǔn)確率連續(xù)5輪不漲就停。這里我想多說一句學(xué)習(xí)率。很多人微調(diào)時(shí)習(xí)慣統(tǒng)一用1e-4其實(shí)骨干網(wǎng)絡(luò)和分類頭的學(xué)習(xí)率應(yīng)該分開對(duì)待。骨干網(wǎng)絡(luò)已經(jīng)學(xué)好了通用特征學(xué)習(xí)率太高會(huì)把預(yù)訓(xùn)練權(quán)重沖壞而新加的分類頭是隨機(jī)初始化的學(xué)習(xí)率太低又學(xué)得慢。所以我會(huì)用參數(shù)分組來做差異化學(xué)習(xí)率optimizer torch.optim.AdamW([ {params: model.backbone.parameters(), lr: 1e-5}, {params: model.head.parameters(), lr: 1e-4}, ])這個(gè)區(qū)分在細(xì)粒度分類上比普通分類更重要因?yàn)槟阈枰A艄歉删W(wǎng)絡(luò)在ImageNet上學(xué)到的精細(xì)紋理感知能力只針對(duì)飛機(jī)領(lǐng)域的特征做有限調(diào)整。損失函數(shù)上最常見的就是交叉熵?fù)p失。如果你發(fā)現(xiàn)某些飛機(jī)類別樣本特別少可以給交叉熵加類別權(quán)重或者用Label Smoothing。細(xì)粒度分類我用Label Smoothing的頻率很高它讓標(biāo)簽不是非0即1的硬目標(biāo)而是保留一點(diǎn)點(diǎn)模糊度能緩解模型對(duì)訓(xùn)練標(biāo)簽過于自信導(dǎo)致的過擬合。具體做法是把nn.CrossEntropyLoss()換成nn.CrossEntropyLoss(label_smoothing0.1)一行代碼的事收益通常很明顯。4. 模型拿到測(cè)試集之后評(píng)估環(huán)節(jié)的完整動(dòng)作4.1 評(píng)估流程不是predict一下就算完很多人訓(xùn)練完模型把測(cè)試集丟進(jìn)模型里跑一遍準(zhǔn)確率就宣告結(jié)束。這份草率在飛機(jī)100分類這種細(xì)粒度任務(wù)上尤其容易出問題。一份合格的評(píng)估流程至少要包含下面這些動(dòng)作。第一確認(rèn)模型處于評(píng)估模式。PyTorch里是model.eval()TensorFlow里是model.trainable False這一步會(huì)關(guān)閉Dropout和BatchNorm的訓(xùn)練行為。漏掉這一行測(cè)試集的分?jǐn)?shù)會(huì)莫名其妙地低一截因?yàn)镈ropout在推理時(shí)仍在隨機(jī)丟棄神經(jīng)元輸出不穩(wěn)定。第二整個(gè)推理過程包裹在torch.no_grad()里面。測(cè)試時(shí)不計(jì)算梯度既節(jié)約顯存又加快速度。代碼如下def evaluate(model, test_loader, device): model.eval() correct_top1 0 correct_top5 0 total 0 all_preds [] all_probs [] with torch.no_grad(): for images, labels in test_loader: images, labels images.to(device), labels.to(device) outputs model(images) # [B, 100] probs torch.softmax(outputs, dim1) # Top-1 preds probs.argmax(dim1) correct_top1 (preds labels).sum().item() # Top-5 top5_preds probs.topk(5, dim1).indices correct_top5 sum([labels[i] in top5_preds[i] for i in range(labels.size(0))]) total labels.size(0) all_probs.append(probs.cpu().numpy()) top1_acc correct_top1 / total top5_acc correct_top5 / total print(fTop-1 Accuracy: {top1_acc:.4f}, Top-5 Accuracy: {top5_acc:.4f}) return all_probs第三把概率輸出保存下來而不是只保留精度的數(shù)字。因?yàn)楹罄m(xù)要做混淆矩陣、錯(cuò)誤樣本分析、TTA投票都需要原始概率。只留一個(gè)accuracy排查問題的時(shí)候還得重新跑一遍推理浪費(fèi)時(shí)間。4.2 指標(biāo)解讀Top-1、Top-5、混淆矩陣到底看什么飛機(jī)100分類這種類數(shù)多、細(xì)粒度強(qiáng)的任務(wù)只報(bào)一個(gè)Top-1準(zhǔn)確率遠(yuǎn)遠(yuǎn)不夠。我習(xí)慣用一組指標(biāo)多維評(píng)估指標(biāo)說明細(xì)粒度任務(wù)里怎么讀Top-1 Accuracy預(yù)測(cè)概率最高的類別是否對(duì)基礎(chǔ)門檻但容易被類間混淆拖低Top-5 Accuracy前5個(gè)預(yù)測(cè)里是否包含正確類很能反映模型有沒有圈定正確范圍每類準(zhǔn)確率 / Recall每個(gè)類別的召回率快速揪出低分的那幾個(gè)類多半是標(biāo)注噪聲或樣本太少M(fèi)acro-F1所有類F1的均值類別不均衡時(shí)比原始Accuracy更可信混淆矩陣哪個(gè)類被判成哪個(gè)類找最像兄弟的類別對(duì)指導(dǎo)后續(xù)優(yōu)化在實(shí)際項(xiàng)目里我最先看的是混淆矩陣。飛機(jī)100分類里最容易出現(xiàn)的錯(cuò)誤是同一系列不同型號(hào)互相混淆比如波音737-700被識(shí)別成737-800。如果在混淆矩陣?yán)锇l(fā)現(xiàn)這類系統(tǒng)性錯(cuò)誤說明模型沒有抓到關(guān)鍵判別部位而不是隨機(jī)出錯(cuò)。這時(shí)候針對(duì)這兩個(gè)類多采集訓(xùn)練圖或者在數(shù)據(jù)增強(qiáng)里加大局部裁剪比例比盲目換網(wǎng)絡(luò)結(jié)構(gòu)更有效。Top-5準(zhǔn)確率在細(xì)粒度分類里尤其有價(jià)值。很多業(yè)務(wù)場景允許給候選列表比如塔臺(tái)輔助系統(tǒng)可以先給出Top-5的飛機(jī)型號(hào)再由調(diào)度員人工確認(rèn)。此時(shí)Top-5比Top-1更能反映系統(tǒng)的可用性。4.3 測(cè)試時(shí)增強(qiáng)(TTA)白撿的準(zhǔn)確率提升測(cè)試時(shí)增強(qiáng)簡單說就是把測(cè)試圖先做幾次變換得到多個(gè)預(yù)測(cè)再對(duì)預(yù)測(cè)概率求平均最終取平均概率最高的類別。最常用的TTA是水平翻轉(zhuǎn)和尺度縮放。以水平翻轉(zhuǎn)為例import torch from torchvision import transforms def predict_with_tta(model, img, device): model.eval() base_transform transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) flip_transform transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), transforms.RandomHorizontalFlip(p1.0), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) with torch.no_grad(): img_orig base_transform(img).unsqueeze(0).to(device) img_flip flip_transform(img).unsqueeze(0).to(device) prob_orig torch.softmax(model(img_orig), dim1) prob_flip torch.softmax(model(img_flip), dim1) # 注意翻轉(zhuǎn)后的預(yù)測(cè)類別要和原圖對(duì)齊 final_prob (prob_orig prob_flip) / 2.0 pred final_prob.argmax(dim1).item() return pred一個(gè)容易忽略的細(xì)節(jié)水平翻轉(zhuǎn)后模型的輸出概率對(duì)應(yīng)的還是原圖像的類別語義不需要對(duì)類別做鏡像映射。因?yàn)轱w機(jī)型號(hào)分類是平移等變的不像數(shù)字6翻轉(zhuǎn)成9那種方向敏感任務(wù)。TTA在細(xì)粒度分類上通常能帶來1到3個(gè)百分點(diǎn)的Top-1提升成本只是推理時(shí)間翻倍。如果你的測(cè)評(píng)環(huán)境對(duì)推理時(shí)間要求不苛刻強(qiáng)烈建議加上。當(dāng)然TTA也要看場景。如果是比賽提交先確認(rèn)主辦方是否允許對(duì)測(cè)試集做多次推理。大多數(shù)情況下允許但有些線上測(cè)評(píng)會(huì)限制調(diào)用次數(shù)這時(shí)候TTA的性價(jià)比就要重新考量。5. 細(xì)粒度飛機(jī)數(shù)據(jù)集上踩過的雷數(shù)據(jù)質(zhì)量與泛化5.1 標(biāo)注噪聲有些標(biāo)簽從源頭就錯(cuò)了飛機(jī)100分類數(shù)據(jù)集的標(biāo)注難度比普通物體分類高得多因?yàn)閰^(qū)分點(diǎn)太細(xì)。我自己在某次項(xiàng)目中隨機(jī)抽查了200張訓(xùn)練圖發(fā)現(xiàn)大概有10張的標(biāo)簽和圖片內(nèi)容對(duì)不上準(zhǔn)確說不是狗被標(biāo)成貓那種離譜錯(cuò)誤而是波音777-300被標(biāo)成777-200ER這種張冠李戴。這種標(biāo)注噪聲對(duì)細(xì)粒度模型是致命的因?yàn)樗鼈儠?huì)直接扭曲類別邊界。怎么發(fā)現(xiàn)標(biāo)注噪聲一個(gè)有效的辦法是先訓(xùn)練一個(gè)模型然后找出訓(xùn)練集中l(wèi)oss最高的那些樣本比如交叉熵?fù)p失超過3的。這些高loss樣本往往就是兩種情況要么是特別難的正確樣本要么是錯(cuò)標(biāo)/多標(biāo)的臟數(shù)據(jù)。把Top-100的高loss樣本人工過一遍你會(huì)很快發(fā)現(xiàn)規(guī)律。處理標(biāo)注噪聲有幾種策略。最簡單的是直接刪掉或修正錯(cuò)標(biāo)樣本如果噪聲比例不大也可以在訓(xùn)練時(shí)用label_smoothing給模型一點(diǎn)緩沖防止它對(duì)錯(cuò)標(biāo)樣本死記硬背。更進(jìn)階的做法是錯(cuò)標(biāo)樣本重標(biāo)注但人力成本高適合只針對(duì)少量高頻混淆類別做。5.2 類別不均衡模型會(huì)偷偷只學(xué)多數(shù)類飛機(jī)100分類數(shù)據(jù)集的100個(gè)類別樣本量不可能完全均衡。熱門客機(jī)型號(hào)可能有一兩千張冷門的公務(wù)機(jī)或老舊型號(hào)可能只有幾十張。類別不均衡的直接后果是模型在訓(xùn)練時(shí)對(duì)多數(shù)類過擬合對(duì)少數(shù)類欠學(xué)習(xí)最終測(cè)試準(zhǔn)確率被多數(shù)類拉高看起來整體還不錯(cuò)但少數(shù)類的每類準(zhǔn)確率可能只有30%。肉眼可見的表現(xiàn)是模型一看到曲面輪廓明顯的飛機(jī)就傾向預(yù)測(cè)成樣本量大的那幾個(gè)主流客機(jī)型號(hào)。解決不均衡我常用的三個(gè)辦法重采樣訓(xùn)練時(shí)對(duì)少數(shù)類樣本過采樣讓每個(gè)batch里各類數(shù)量盡量接近配合WeightedRandomSampler使用。損失加權(quán)交叉熵里給少數(shù)類更大的權(quán)重weight參數(shù)直接傳入CrossEntropyLoss。模擬過采樣對(duì)少數(shù)類做更強(qiáng)的數(shù)據(jù)增強(qiáng)相當(dāng)于用變換生成偽樣本。這三種方式我通常組合使用但注意別把采樣比例設(shè)得過于極端否則模型會(huì)對(duì)少數(shù)類過擬合反而丟掉通用特征。5.3 訓(xùn)練集和測(cè)試集分布不一致高分可能是假的細(xì)粒度分類里最隱蔽的坑是領(lǐng)域漂移Domain Shift訓(xùn)練集圖像和測(cè)試集圖像來源差異過大。測(cè)設(shè)集是網(wǎng)上爬的高清側(cè)拍訓(xùn)練集是遙感俯拍那訓(xùn)練得再好的模型也白搭。飛機(jī)100分類的測(cè)試集很可能故意和你手里的訓(xùn)練集不完全同源以檢驗(yàn)?zāi)P驼嬲姆夯芰?。最常見的?yīng)對(duì)思路有三種。第一圖像歸一化的時(shí)候不要直接套ImageNet的mean/std而是用訓(xùn)練集和測(cè)試集的統(tǒng)計(jì)量重新算一遍消除掉不同拍攝設(shè)備帶來的顏色偏差第二在訓(xùn)練時(shí)加入更多針對(duì)測(cè)試集風(fēng)格的增強(qiáng)比如低分辨率模擬、模糊模擬、明暗變化模擬第三如果測(cè)試集沒有標(biāo)簽但數(shù)量夠大可以考慮半監(jiān)督學(xué)習(xí)用訓(xùn)練好的模型對(duì)測(cè)試集做高置信度偽標(biāo)簽再混合回去微調(diào)。最后這個(gè)操作有風(fēng)險(xiǎn)偽標(biāo)簽一旦錯(cuò)反而會(huì)把噪聲帶進(jìn)來所以偽標(biāo)簽的置信度閾值要設(shè)得很高比如0.99以上才采納。6. 一個(gè)測(cè)試集能延伸出哪些實(shí)用方向6.1 從分類到檢測(cè)YOLO和旋轉(zhuǎn)框的擴(kuò)展路徑飛機(jī)100分類解決的是這是什么型號(hào)但很多真實(shí)業(yè)務(wù)里第一步得先知道飛機(jī)在哪。這時(shí)候就需要從圖像分類往目標(biāo)檢測(cè)擴(kuò)展。比如Ultralytics YOLOv8在自定義數(shù)據(jù)集上訓(xùn)練先標(biāo)注出圖片里的飛機(jī)目標(biāo)框再配合分類模型去識(shí)別框內(nèi)型號(hào)是航空?qǐng)鼍氨容^通用的兩段式方案。如果你用的圖像里飛機(jī)是斜著停的普通水平框會(huì)把翼展方向的背景也框進(jìn)去很影響下游任務(wù)精度。這時(shí)候需要考慮旋轉(zhuǎn)目標(biāo)檢測(cè)比如mmrotate和基于DOTA數(shù)據(jù)集的檢測(cè)方案。旋轉(zhuǎn)框能更貼合飛機(jī)輪廓提取出的區(qū)域也更干凈為后續(xù)細(xì)粒度分類提供更好的輸入。分類和檢測(cè)不是互斥的。我見過很多工程的做法是檢測(cè)網(wǎng)絡(luò)負(fù)責(zé)畫框分類網(wǎng)絡(luò)負(fù)責(zé)辨認(rèn)。把檢測(cè)網(wǎng)絡(luò)輸出的裁剪圖喂給分類模型既省去全圖滑窗的算力開銷又讓分類模型聚焦于飛機(jī)區(qū)域而不是亂七八糟的背景。6.2 從測(cè)試集反推業(yè)務(wù)落地的評(píng)估方式獨(dú)立測(cè)試集的價(jià)值不止在競賽里。放到真實(shí)業(yè)務(wù)中測(cè)試集其實(shí)就是對(duì)未來線上數(shù)據(jù)流的一次預(yù)演。你今天把評(píng)估腳本寫得多規(guī)范未來模型上線后做監(jiān)控和回歸測(cè)試就有多順手。我會(huì)建議在每個(gè)項(xiàng)目里都建一個(gè)評(píng)估即代碼的管道數(shù)據(jù)集版本、預(yù)處理參數(shù)、模型權(quán)重路徑、評(píng)估指標(biāo)、TTA開關(guān)全部用配置文件和腳本固定下來。下次模型迭代一鍵重跑同一份測(cè)試集就能清楚看到改動(dòng)帶來的真實(shí)收益或損失。6.3 相關(guān)數(shù)據(jù)集與工具生態(tài)如果你對(duì)飛機(jī)細(xì)粒度識(shí)別這個(gè)方向感興趣還可以關(guān)注這些公開資源FGVC-Aircraft經(jīng)典的細(xì)粒度飛機(jī)分類基準(zhǔn)包含100種飛機(jī)型號(hào)和標(biāo)題里的飛機(jī)100分類天然對(duì)位。Aeroscapes航空?qǐng)鼍罢Z義分割數(shù)據(jù)集適合做機(jī)場、跑道、飛行器區(qū)域的分割任務(wù)。DOTA遙感旋轉(zhuǎn)框目標(biāo)檢測(cè)數(shù)據(jù)集里面有大量飛機(jī)目標(biāo)適合檢測(cè)方向進(jìn)階。工具層面torchvision、timm、mmclassification、Ultralytics YOLOv8、mmrotate都是非常實(shí)用的開源庫。timm里EfficientNet、ViT等模型的預(yù)訓(xùn)練權(quán)重很全換網(wǎng)絡(luò)結(jié)構(gòu)時(shí)強(qiáng)烈推薦優(yōu)先試timm的版本。最后再說一次我自己的經(jīng)驗(yàn)別小看測(cè)試集這三個(gè)字。很多時(shí)候模型掉鏈子不是網(wǎng)絡(luò)結(jié)構(gòu)不夠花哨而是數(shù)據(jù)側(cè)和評(píng)估側(cè)沒做好。飛機(jī)100分類這種細(xì)粒度很強(qiáng)的任務(wù)尤其能逼著人把每個(gè)細(xì)節(jié)摳到訓(xùn)練有沒有泄漏測(cè)試信息、評(píng)估是不是正確的eval模式、標(biāo)簽有沒有臟數(shù)據(jù)、類別權(quán)重是不是失衡……哪一環(huán)偷懶測(cè)試集上都會(huì)很誠實(shí)地還給你。所以做這類項(xiàng)目我向來是先花時(shí)間把評(píng)估腳本寫到無懈可擊再回頭去折騰網(wǎng)絡(luò)結(jié)構(gòu)和訓(xùn)練技巧。這個(gè)順序要是反了大概率會(huì)在最后交答案的時(shí)候吃大虧。你手里的測(cè)試集值得你認(rèn)真對(duì)待。本文還有配套的精品資源點(diǎn)擊獲取