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