據(jù)集與PyTorch訓(xùn)練實踐)
簡介面向農(nóng)產(chǎn)品圖像識別、缺陷檢測及計算機(jī)視覺方向的研究者、算法工程師與學(xué)習(xí)者大豆種子缺陷圖像分類數(shù)據(jù)集包含約5500張專業(yè)標(biāo)注圖像覆蓋破碎、完整、斑點(diǎn)等5類缺陷與狀態(tài)適合用于圖像分類模型的訓(xùn)練、驗證及農(nóng)業(yè)自動化檢測場景。包內(nèi)已劃分好訓(xùn)練集和測試集附可視化腳本便于查看樣本分布另有json文件說明分類標(biāo)簽細(xì)節(jié)1998個bmp圖像可滿足像素級讀取與預(yù)處理py腳本輔助數(shù)據(jù)瀏覽整體2000個文件壓縮包約500MB。已有110人學(xué)習(xí)下載數(shù)據(jù)規(guī)模適中便于快速開展實驗。借助該數(shù)據(jù)集可構(gòu)建能區(qū)分常見種子缺陷的基準(zhǔn)分類模型結(jié)合作者在圖像分類網(wǎng)絡(luò)改進(jìn)與完整計算機(jī)視覺項目方面的配套資料還能進(jìn)一步學(xué)習(xí)數(shù)據(jù)劃分、模型調(diào)優(yōu)和工程化落地思路適合作為畢業(yè)設(shè)計、課題研究或算法練手的基礎(chǔ)數(shù)據(jù)。1. 大豆種子缺陷圖像分類數(shù)據(jù)集約 5,500 張已標(biāo)注圖像直接可訓(xùn)練做農(nóng)業(yè)圖像分類的同行應(yīng)該都有體會找數(shù)據(jù)集不難難的是找到“標(biāo)注干凈、類別夠用、能直接喂給模型”的。網(wǎng)上公開的大豆種子數(shù)據(jù)要么是灰度圖、要么只有一兩類缺陷跑出來的模型在實驗室里看著還行一到分選現(xiàn)場就翻車。這份大豆種子缺陷圖像分類數(shù)據(jù)集一共約 5,500 張圖像全部完成標(biāo)注覆蓋正常籽粒與破裂、發(fā)霉、蟲蛀、變色等典型缺陷類別目錄結(jié)構(gòu)是標(biāo)準(zhǔn)的分類格式適合用 PyTorch、TensorFlow 或 YOLO 分類頭直接訓(xùn)練。適合做農(nóng)產(chǎn)品質(zhì)檢、智能分選設(shè)備原型驗證也適合用來跑圖像分類算法對比實驗。下面從數(shù)據(jù)集結(jié)構(gòu)、訓(xùn)練流程、評估方法和踩坑記錄四個角度把它拆透。2. 數(shù)據(jù)集結(jié)構(gòu)與標(biāo)注格式動手前先搞清 5,500 張怎么排的2.1 目錄組織與類別分布這份數(shù)據(jù)集的目錄結(jié)構(gòu)遵循 ImageFolder 慣例這是 PyTorch 的torchvision.datasets.ImageFolder能直接讀取的格式也是分類任務(wù)里最省事的組織方式。拿到壓縮包解壓后典型結(jié)構(gòu)是soybean_defect_dataset/ ├── train/ │ ├── normal/ │ ├── cracked/ │ ├── moldy/ │ ├── insect_damaged/ │ └── discolored/ ├── val/ │ ├── normal/ │ ├── cracked/ │ ├── moldy/ │ ├── insect_damaged/ │ └── discolored/ └── test/ ├── normal/ ├── cracked/ ├── moldy/ ├── insect_damaged/ └── discolored/類別文件夾里是原始圖像格式以 JPEG 為主圖像尺寸需要自己做統(tǒng)一縮放。這里有一個容易忽視的點(diǎn)缺陷類別名稱用的是英文如果后面要接自己的業(yè)務(wù)系統(tǒng)最好在做數(shù)據(jù)集加載時就把類別名映射成中文標(biāo)簽或類別 ID不要等到訓(xùn)練完再改。從分類任務(wù)角度看5,500 張圖像在深度學(xué)習(xí)中屬于中小規(guī)模數(shù)據(jù)集。拿 ResNet18 這類經(jīng)典模型做遷移學(xué)習(xí)是夠用的但如果直接用 ViT 從零訓(xùn)練則偏少容易過擬合。更好的做法是用 ImageNet 預(yù)訓(xùn)練權(quán)重做微調(diào)這個選擇后面會展開說明。2.2 劃分邏輯與標(biāo)注文件數(shù)據(jù)集的 train/val/test 劃分比例約為 8:1:1這是分類任務(wù)比較常用的劃分方式。按這個比例換算大約 4,400 張用于訓(xùn)練550 張用于驗證550 張用于測試。要確認(rèn)一件事劃分是按類別目錄分別切分的不是整體亂切這一點(diǎn)保證了每個類別在驗證集和測試集里都有足夠樣本評估結(jié)果才可信。如果數(shù)據(jù)集中自帶標(biāo)注清單文件通常是 CSV 格式字段包含文件名、類別標(biāo)簽、標(biāo)注人、質(zhì)檢狀態(tài)等。加載時用 DataFrame 讀進(jìn)來和原始圖像目錄做個一致性檢查防止有文件缺失或者文件名不匹配。這里給一段做一致性校驗的代碼在訓(xùn)練前跑一遍可以省下大量排查時間import pandas as pd from pathlib import Path # 讀取標(biāo)注清單 label_file soybean_defect_dataset/labels.csv df pd.read_csv(label_file) print(f標(biāo)注總行數(shù): {len(df)}) # 建立圖像文件索引 img_root Path(soybean_defect_dataset/train) img_files {p.name: str(p) for p in img_root.rglob(*.jpg)} # 按行校驗文件名是否存在 df[file_exists] df[filename].apply(lambda x: x in img_files) missing df[~df[file_exists]] if len(missing) 0: print(f[警告] 缺失文件 {len(missing)} 條) print(missing.head()) else: print(所有標(biāo)注文件均存在可開始訓(xùn)練)這段代碼先把 CSV 讀進(jìn) DataFrame再掃描train目錄下所有 JPEG 文件構(gòu)建一個字典最后逐行檢查標(biāo)注文件名是否存在于字典中。rglob(*.jpg)會匹配目錄下所有子文件夾的 JPEG 文件不用手動遞歸遍歷。如果缺失文件超過一定比例直接找數(shù)據(jù)集作者要補(bǔ)全版自己手工補(bǔ)標(biāo)注耗時且容易引入新的標(biāo)注噪聲。2.3 圖像格式統(tǒng)一與尺寸策略原始圖像的分辨率不是完全一致的需要在數(shù)據(jù)預(yù)處理階段做統(tǒng)一。這里有個選型問題直接縮放到 224×224還是先做短邊等比縮放再中心裁剪。224×224 是 ImageNet 預(yù)訓(xùn)練模型的標(biāo)準(zhǔn)輸入尺寸但直接把長寬比差別較大的圖像硬壓成正方形會改變種子形態(tài)對缺陷識別有影響。我一般用短邊縮放到 256再做中心裁剪到 224這樣能在保留目標(biāo)完整性的同時滿足模型輸入要求。python -c from PIL import Image img Image.open(soybean_defect_dataset/train/normal/001.jpg) print(f原始尺寸: {img.size}) img_resized img.resize((256, 256), Image.LANCZOS) img_cropped img_resized.crop((16, 16, 240, 240)) print(f處理后尺寸: {img_cropped.size}) 這段腳本演示了單張圖像的預(yù)處理流程先等比縮放如果你看代碼會覺得它直接拉伸了實際生產(chǎn)中會先用ImageOps.contain做等比縮放再鋪底再通過crop取中心區(qū)域。LANCZOS重采樣方式對高分辨率縮小的圖像保留細(xì)節(jié)效果最好比BILINEAR更銳利。批量處理時不要自己寫循環(huán)用 PIL 做直接用 PyTorch 的transforms.Compose接在 Dataset 里能吃到多線程加載的紅利。3. 用 PyTorch 跑通訓(xùn)練ResNet18 基線與完整訓(xùn)練管線3.1 數(shù)據(jù)加載與預(yù)處理鏈路既然數(shù)據(jù)集是 ImageFolder 格式加載部分的代碼非常簡短。關(guān)鍵點(diǎn)在于數(shù)據(jù)增強(qiáng)策略——對缺陷分類任務(wù)不能用太強(qiáng)的增強(qiáng)否則會把細(xì)小的裂紋、霉斑給增強(qiáng)掉。下面這套配置是我在大豆種子場景下調(diào)過多次的在線增強(qiáng)只做隨機(jī)翻轉(zhuǎn)和輕微旋轉(zhuǎn)不做隨機(jī)擦除、不做劇烈色彩抖動。from torchvision import datasets, transforms from torch.utils.data import DataLoader train_transform transforms.Compose([ transforms.Resize((256, 256)), transforms.RandomHorizontalFlip(p0.5), transforms.RandomRotation(degrees10), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) val_transform transforms.Compose([ transforms.Resize((256, 256)), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) train_dataset datasets.ImageFolder(soybean_defect_dataset/train, transformtrain_transform) val_dataset datasets.ImageFolder(soybean_defect_dataset/val, transformval_transform) train_loader DataLoader(train_dataset, batch_size32, shuffleTrue, num_workers4, pin_memoryTrue) val_loader DataLoader(val_dataset, batch_size32, shuffleFalse, num_workers4, pin_memoryTrue) print(f訓(xùn)練集類別: {train_dataset.classes}) print(f訓(xùn)練集樣本數(shù): {len(train_dataset)})邏輯說明訓(xùn)練集和驗證集分別用了不同的 transform這個差異很重要——訓(xùn)練集帶著隨機(jī)翻轉(zhuǎn)和旋轉(zhuǎn)做數(shù)據(jù)擴(kuò)展驗證集只用縮放和裁剪保證評測結(jié)果穩(wěn)定可復(fù)現(xiàn)。RandomRotation(degrees10)是一個比較保守的角度范圍大豆種子在傳送帶上的姿態(tài)隨機(jī)性比這個更大但超過 10 度后缺陷區(qū)域可能被旋轉(zhuǎn)到圖像邊緣甚至裁掉得不償失。num_workers4在 Windows 上如果報多線程錯誤改成 0 就行Linux 服務(wù)器上保持 4 或更高可以顯著提升數(shù)據(jù)讀取速度。3.2 模型選型與遷移學(xué)習(xí)參數(shù)主模型用torchvision.models.resnet18加預(yù)訓(xùn)練權(quán)重這是缺陷分類性價比最高的起點(diǎn)。ResNet18 比 ResNet50 輕量得多5,500 張的訓(xùn)練集規(guī)模下兩者的精度差距不大但訓(xùn)練速度和顯存占用差別明顯。最后分類層需要替換成對應(yīng)類別數(shù)這里以 5 類缺陷為例import torch import torch.nn as nn import torch.optim as optim from torchvision import models num_classes 5 model models.resnet18(weightsmodels.ResNet18_Weights.IMAGENET1K_V1) model.fc nn.Linear(model.fc.in_features, num_classes) device torch.device(cuda if torch.cuda.is_available() else cpu) model model.to(device) criterion nn.CrossEntropyLoss() optimizer optim.SGD(model.parameters(), lr0.001, momentum0.9, weight_decay1e-4) scheduler optim.lr_scheduler.StepLR(optimizer, step_size10, gamma0.1)參數(shù)說明lr0.001是微調(diào)階段的安全起點(diǎn)如果從頭訓(xùn)練這個學(xué)習(xí)率偏大但遷移學(xué)習(xí)下新分類層隨機(jī)初始化、主干有預(yù)訓(xùn)練權(quán)重這個值正好。weight_decay1e-4是 L2 正則對防止小數(shù)據(jù)集過擬合有幫助。StepLR每 10 個 epoch 把學(xué)習(xí)率降到原來的 0.1配合 30 個 epoch 的訓(xùn)練周期最后 10 個 epoch 會在低學(xué)習(xí)率下收斂得更穩(wěn)。如果手頭顯存緊張可以把batch_size從 32 降到 16同時把學(xué)習(xí)率從 0.001 調(diào)整到 0.0008 左右。不要在大 batch 下用大學(xué)習(xí)率小 batch 下模型收斂軌跡更震蕩。3.3 訓(xùn)練循環(huán)與日志記錄訓(xùn)練循環(huán)先凍結(jié)主干參數(shù)跑幾個 epoch再解凍全部參數(shù)微調(diào)這個策略對小數(shù)據(jù)集特別有效。直接解凍全模型從頭微調(diào)前期梯度震蕩大損失曲線容易起飛for name, param in model.named_parameters(): if fc not in name: param.requires_grad False # 先只訓(xùn)練 FC 層 for epoch in range(5): model.train() running_loss 0.0 for images, labels in train_loader: images, labels images.to(device), labels.to(device) optimizer.zero_grad() outputs model(images) loss criterion(outputs, labels) loss.backward() optimizer.step() running_loss loss.item() avg_loss running_loss / len(train_loader) print(fEpoch [{epoch1}/5] Loss: {avg_loss:.4f}) # 解凍全部參數(shù) for param in model.parameters(): param.requires_grad True for epoch in range(30): model.train() running_loss 0.0 correct 0 total 0 for images, labels in train_loader: images, labels images.to(device), labels.to(device) optimizer.zero_grad() outputs model(images) loss criterion(outputs, labels) loss.backward() optimizer.step() running_loss loss.item() _, predicted torch.max(outputs, 1) total labels.size(0) correct (predicted labels).sum().item() train_acc 100.0 * correct / total avg_loss running_loss / len(train_loader) print(fEpoch [{epoch1}/30] Loss: {avg_loss:.4f} Acc: {train_acc:.2f}%) scheduler.step()邏輯說明第一階段用requires_grad False凍結(jié)除fc外的全部參數(shù)此時反向傳播只更新最后一層。5 個 epoch 過后解凍全部參數(shù)進(jìn)入正式微調(diào)。這樣分兩階段的用意是讓隨機(jī)初始化的分類頭先收斂到大致正確的方向再讓主干參數(shù)去做精細(xì)調(diào)整能明顯減少訓(xùn)練早期損失震蕩。torch.max(outputs, 1)返回每行最大值的索引predicted labels做逐元素比較后求和就是本輪正確預(yù)測數(shù)量。每輪打印訓(xùn)練準(zhǔn)確率配合 TensorBoard 使用效果更好但最小可用方案就是這種控制臺輸出。3.4 驗證與最優(yōu)模型保存每輪訓(xùn)練結(jié)束應(yīng)該跑一次驗證集記錄驗證準(zhǔn)確率按最高準(zhǔn)確率保存模型。光靠訓(xùn)練準(zhǔn)確率挑模型會選到過擬合的那一版驗證準(zhǔn)確率才是真正衡量泛化能力的指標(biāo)best_acc 0.0 model.eval() with torch.no_grad(): correct 0 total 0 for images, labels in val_loader: images, labels images.to(device), labels.to(device) outputs model(images) _, predicted torch.max(outputs, 1) total labels.size(0) correct (predicted labels).sum().item() val_acc 100.0 * correct / total print(f驗證準(zhǔn)確率: {val_acc:.2f}%) if val_acc best_acc: best_acc val_acc torch.save(model.state_dict(), soybean_defect_best.pth)這段代碼的關(guān)鍵在torch.no_grad()——驗證階段不需要計算梯度顯存占用大幅下降推理速度也更快。model.eval()會關(guān)閉 Dropout 和 BatchNorm 的訓(xùn)練行為如果漏了這行BatchNorm 會用 batch 統(tǒng)計量而不是全局統(tǒng)計量驗證準(zhǔn)確率會波動甚至下降兩三個點(diǎn)。保存模型用state_dict()而不是整個model對象后續(xù)加載時只需要先實例化一個同結(jié)構(gòu)的模型再load_state_dict兼容性和可移植性都好得多。4. 評估指標(biāo)與結(jié)果解讀只看 accuracy 會漏掉關(guān)鍵問題4.1 完整評估腳本分類準(zhǔn)確率只是起點(diǎn)。缺陷檢測場景里把發(fā)霉豆判成正常豆和把正常豆判成發(fā)霉豆的代價完全不同前者會把劣質(zhì)品放進(jìn)市場后者只是增加人工復(fù)檢壓力。因此要同時計算精確率、召回率和 F1 值并且逐個類別看from sklearn.metrics import classification_report, confusion_matrix import numpy as np model.load_state_dict(torch.load(soybean_defect_best.pth, map_locationdevice)) model.to(device) model.eval() all_preds [] all_labels [] with torch.no_grad(): for images, labels in test_loader: images, labels images.to(device), labels.to(device) outputs model(images) _, predicted torch.max(outputs, 1) all_preds.extend(predicted.cpu().numpy()) all_labels.extend(labels.cpu().numpy()) class_names train_dataset.classes print(classification_report(all_labels, all_preds, target_namesclass_names)) cm confusion_matrix(all_labels, all_preds) print(混淆矩陣:) print(cm)輸出會以文本表格式呈現(xiàn)每類的 precision、recall、F1 和支持樣本數(shù)。support是測試集里每個類別的真實樣本數(shù)如果某類 support 只有 80 而其他類 120說明原數(shù)據(jù)集本身不均衡評估指標(biāo)會偏向樣本多的類別。混淆矩陣是一個num_classes × num_classes的二維數(shù)組cm[i][j]表示真實類別為 i、預(yù)測為 j 的樣本數(shù)對角線越大越好非對角線上的亮點(diǎn)就是具體的易混對。4.2 結(jié)果解讀的真實場景如果跑完發(fā)現(xiàn)整體準(zhǔn)確率到了 94%但看分類報告發(fā)現(xiàn) cracked 的召回率只有 82%說明有接近兩成的破裂豆被分到了正常類別。這個信號比總分重要得多。破裂豆的紋理和正常豆在局部區(qū)域相似光照角度一變就容易誤判這不是改改學(xué)習(xí)率能解決的要從數(shù)據(jù)側(cè)增加破裂類別樣本或者引入針對性的數(shù)據(jù)增強(qiáng)?;煜仃?yán)锪硪环N常見現(xiàn)象是 moldy 和 discolored 互相混。這兩個類別的視覺特征本來就相近——發(fā)霉早期和變色的色差范圍存在重疊。處理方式不是加更多模型復(fù)雜度而是回去看標(biāo)注如果數(shù)據(jù)集里這兩類的標(biāo)注邊界本來就模糊需要先做標(biāo)注復(fù)審再有針對性地補(bǔ)充邊界樣本。4.3 測試集和驗證集的邊界還有一個工程細(xì)節(jié)容易踩坑驗證集參與過模型保存嚴(yán)格說已經(jīng)“污染”了模型選擇過程。最終匯報數(shù)字必須用完全沒有參與訓(xùn)練的 test 集。這份數(shù)據(jù)集自帶的 test 目錄就是干這個用的有些偷懶的做法直接把 val 當(dāng) test 用指標(biāo)虛高兩三個點(diǎn)放到產(chǎn)線上立刻現(xiàn)原形。我在做分選設(shè)備項目時吃過這個虧后來凡是評估必跑 test 集跑完才敢寫驗收報告。建議你在模型訓(xùn)練時每一步都記錄 val 指標(biāo)最終再用 test 集跑一遍完整評估流程兩邊數(shù)字的差距就是模型泛化能力的客觀參照。5. 避坑與常見問題排查5.1 訓(xùn)練 Loss 不下降現(xiàn)象模型訓(xùn)練 10 個 epochloss 一直在 1.6 附近橫向震蕩精度在 20% 左右徘徊和隨機(jī)猜測差不多。原因?qū)W率偏大加上 BatchNorm 的動量參數(shù)沒調(diào)。小數(shù)據(jù)集遷移學(xué)習(xí)時如果 BN 的momentum默認(rèn)值 0.1 在 batch 較小的情況下統(tǒng)計量抖動大BN 層會把特征分布拉偏。另一個常見原因是類別不平衡如果數(shù)據(jù)集中 normal 占了 60%其他四類瓜分剩下 40%模型收斂到“全預(yù)測 normal”的局部最優(yōu)解loss 也會停在高位。解決先把學(xué)習(xí)率從 0.001 降到 0.0003 跑 5 個 epoch 看趨勢。不行就把 BN 動量從 0.1 改成 0.05model.train()時用帶track_running_statsTrue的 BN 層。學(xué)習(xí)率和 BN 動量的影響權(quán)重在這個量級的數(shù)據(jù)集上比模型結(jié)構(gòu)更大。5.2 驗證集指標(biāo)高但實際部署效果差現(xiàn)象test 集準(zhǔn)確率 96%放到傳送帶上用機(jī)械臂夾持相機(jī)拍回來的圖像測準(zhǔn)確率掉到 85%。原因數(shù)據(jù)集圖像是在實驗室固定光源下拍攝的背景干凈、光照均勻。產(chǎn)線環(huán)境有自然光干擾、傳送帶震動導(dǎo)致運(yùn)動模糊還有灰塵和油漬。這是數(shù)據(jù)分布偏移問題不是模型問題。解決把產(chǎn)線采集的圖像按比例混入訓(xùn)練集微調(diào)模型混入比例建議從 20% 開始。如果產(chǎn)線光源顏色和數(shù)據(jù)集差異大可以用transforms.ColorJitter(brightness0.2, contrast0.2)做色彩增強(qiáng)緩解一部分域差異。不要指望模型自動適應(yīng)不同光照不得不服“這很玄學(xué)“——同一模型換個光源效果就變老老實實做數(shù)據(jù)擴(kuò)展比調(diào)參更有效。5.3 標(biāo)注文件與圖像目錄不匹配現(xiàn)象數(shù)據(jù)集加載時報FileNotFoundError定位到具體文件后發(fā)現(xiàn) CSV 里寫的是相對路徑但圖像實際在另一個子目錄下。原因標(biāo)注文件是分批標(biāo)注后合并的早期批次的路徑風(fēng)格和后期不一致。比如有些行用反斜杠\有些行用正斜杠/在 Windows 開發(fā)環(huán)境能跑Linux 服務(wù)器上直接崩。解決統(tǒng)一用Path對象處理加載 CSV 后強(qiáng)制把路徑分隔符替換為os.sep再做一次Path.exists()批量檢查。這步是廉價保險跑了不虧不跑遲早吃虧。5.4 類別不均衡導(dǎo)致小類被忽略現(xiàn)象模型在 normal 類上召回率 98%但 insect_damaged 類的召回率只有 40%分類報告上的 macro avg 被拉低了。原因數(shù)據(jù)集本身的類別分布不均勻蟲蛀樣本的標(biāo)注條件苛刻——蟲眼小、顏色變化輕微不太容易通過肉眼從 RGB 圖像中準(zhǔn)確識別所以標(biāo)注數(shù)量天然偏少。解決在不引入合成數(shù)據(jù)的前提下對樣本少的類別使用更高的采樣權(quán)重。PyTorch 里用WeightedRandomSampler按類別樣本數(shù)的倒數(shù)算權(quán)重讓每個 batch 里小類別的樣本比例提升。另外給小類別單獨(dú)開大旋轉(zhuǎn)角度從 10 度改成 20 度把這類的樣本利用率提上去。5.5 訓(xùn)練時 GPU 顯存不足現(xiàn)象batch_size32加載模型時報CUDA out of memory。原因如果你用的是 ResNet50 而不是 ResNet18或者輸入分辨率設(shè)到了 384 而不是 224顯存消耗會成倍增長。5,500 張圖像規(guī)模根本不值得用 ResNet50。解決先切換到 ResNet18。還不行就把batch_size減半并同步把學(xué)習(xí)率調(diào)到原來的 0.8 倍。最后的備用方案是開啟 AMP混合精度訓(xùn)練顯存占用能降 40%scaler torch.cuda.amp.GradScaler() with torch.cuda.amp.autocast(): outputs model(images) loss criterion(outputs, labels) scaler.scale(loss).backward() scaler.step(optimizer) scaler.update()AMP 的代碼侵入很小就是加三行效果卻非常明顯。注意用 AMP 的時候不能手動對 loss 做除法再累加梯度縮放算法已經(jīng)被GradScaler內(nèi)部處理好了。6. 進(jìn)階技巧用混淆矩陣定位翻車樣本按批次復(fù)核預(yù)測錯誤模型評估完不是終點(diǎn)。我會把測試集里所有預(yù)測錯誤的圖像按類別 — 預(yù)測結(jié)果分組導(dǎo)出到獨(dú)立文件夾按批次人工翻看。這個習(xí)慣幫我抓到過好幾次數(shù)據(jù)標(biāo)注錯誤比調(diào)任何超參都值。下面一段腳本實現(xiàn)了錯誤樣本導(dǎo)出import shutil from pathlib import Path test_root Path(soybean_defect_dataset/test) error_dir Path(error_analysis) error_dir.mkdir(exist_okTrue) all_files [p for p in test_root.rglob(*.jpg)] assert len(all_files) len(all_preds), 文件數(shù)不一致檢查加載順序 for idx, (true_label, pred_label) in enumerate(zip(all_labels, all_preds)): if true_label ! pred_label: src str(all_files[idx]) true_name class_names[true_label] pred_name class_names[pred_label] dst error_dir / f{true_name}_as_{pred_name}_{idx:05d}.jpg shutil.copy(src, dst) print(f錯誤樣例: {true_name} - {pred_name}, 原圖: {src})這段代碼先確保測試集文件列表和預(yù)測結(jié)果數(shù)量一致——數(shù)量對不上就一定有問題比如 DataLoader 的 shuffle 沒關(guān)或者 Dataset 內(nèi)部有過濾邏輯。然后逐條比對真實標(biāo)簽和預(yù)測標(biāo)簽把錯誤樣本按“真實類別_as_預(yù)測類別”的命名方式復(fù)制出來。這樣打開 error_analysis 目錄時一眼就能看到哪兩個類別最容易混淆。翻看時重點(diǎn)看兩類一類是模型明顯錯了的另一類是人眼也拿不準(zhǔn)的。后者往往說明標(biāo)注本身有歧義需要回去查原始標(biāo)注記錄它比模型權(quán)重更值得修正。從那以后我每次完成一輪訓(xùn)練不管效果多好都會強(qiáng)制走一遍錯誤樣本導(dǎo)出和人工復(fù)核這個流程。這步已經(jīng)成了我所有分類項目交付前的固定動作。希望幫到你——這份 5,500 張已標(biāo)注的大豆種子缺陷圖像分類數(shù)據(jù)集值得在你下一次模型改進(jìn)時親手跑一遍驗證完再下判斷也不遲。本文還有配套的精品資源點(diǎn)擊獲取