:從數(shù)據(jù)集構(gòu)建到PyTorch基線模型)
簡介這份深度學(xué)習(xí)圖像分類數(shù)據(jù)集面向從事計算機(jī)視覺入門與垃圾分類識別實踐的開發(fā)者、學(xué)生及算法愛好者圍繞塑料瓶、玻璃瓶、金屬瓶等可回收物類別構(gòu)建可直接用于訓(xùn)練與評估卷積神經(jīng)網(wǎng)絡(luò)分類模型也可作為課程設(shè)計、畢業(yè)項目或算法對比實驗的數(shù)據(jù)基礎(chǔ)。資源包共約2000個文件以1998張jpg圖像為主體另附1個py腳本與1個json類別文件壓縮包大小約39.65MB圖像按類別目錄分門別類存放便于直接接入ImageFolder等標(biāo)準(zhǔn)加載方式。數(shù)據(jù)已預(yù)先劃分為訓(xùn)練集約2200張、驗證集約200張、測試集約100張省去自行切分的步驟json文件可用于核對具體類別名稱與索引映射。目前已有269人學(xué)習(xí)下載適合希望快速驗證分類網(wǎng)絡(luò)效果、練習(xí)數(shù)據(jù)增強與遷移學(xué)習(xí)流程的讀者參考使用。1. 垃圾分類圖像分類數(shù)據(jù)集從「這堆圖能不能用」到跑通第一個基線你手頭可能有一批從社區(qū)回收站、校園垃圾桶旁、或者公開渠道攢下來的瓶子與垃圾照片幾百到幾千張不等文件夾里混著塑料瓶、玻璃瓶、紙盒、果皮命名亂七八糟?,F(xiàn)在你想用深度學(xué)習(xí)做一個圖像分類模型把「可回收瓶類」「其他垃圾」這些類別自動分開。這件事的核心不是模型多深而是數(shù)據(jù)集能不能撐起訓(xùn)練——類別是否均衡、標(biāo)注是否一致、圖像質(zhì)量是否夠用。垃圾分類、瓶子垃圾圖像分類這個方向本質(zhì)是一個細(xì)粒度、強背景干擾的視覺分類任務(wù)適合剛接觸深度學(xué)習(xí)圖像分類的從業(yè)者拿來練手也適合做環(huán)保、回收分揀場景的工程團(tuán)隊做原型驗證。這一章先把「這是什么、能解決什么、適合誰」講清楚后面幾章再落到具體怎么做、參數(shù)怎么設(shè)、坑在哪。2. 垃圾分類數(shù)據(jù)集長什么樣類別體系、標(biāo)注格式與選型理由2.1 先定類別體系再談圖像數(shù)量很多人一上來就問「多少張圖夠」但更關(guān)鍵的問題是「分幾類」。垃圾分類的類別體系直接決定模型能不能落地。常見做法是兩級一級粗分可回收物、廚余垃圾、有害垃圾、其他垃圾二級細(xì)分塑料瓶、玻璃瓶、易拉罐、紙盒等。如果你只做瓶子垃圾圖像分類建議先聚焦 4 到 6 個視覺差異明顯的類比如「透明塑料瓶」「有色塑料瓶」「玻璃瓶」「易拉罐」「紙杯」「其他」。類別太少模型學(xué)不到區(qū)分度類別太多且視覺邊界模糊比如「可回收塑料」和「其他塑料」會讓標(biāo)注一致性崩掉。我一般會先畫一張類別定義表把每個類別的判定標(biāo)準(zhǔn)寫清楚比如「透明塑料瓶瓶身透光、無標(biāo)簽或標(biāo)簽面積小于 30%」。這張表不是給模型看的是給標(biāo)注的人看的。沒有這張表后面標(biāo)注出來的數(shù)據(jù)大概率是玄學(xué)。類別名判定要點易混淆項建議最少樣本數(shù)透明塑料瓶瓶身透光標(biāo)簽面積小玻璃瓶、有色塑料瓶300有色塑料瓶瓶身有顏色不透光透明塑料瓶300玻璃瓶材質(zhì)反光瓶壁厚透明塑料瓶250易拉罐金屬反光圓柱形紙杯250紙杯/紙盒紙質(zhì)紋理無金屬反光易拉罐200其他垃圾不屬于以上類別全部200這張表里的「建議最少樣本數(shù)」不是硬性標(biāo)準(zhǔn)而是經(jīng)驗下限。如果某個類少于 200 張模型大概率會在這個類上翻車。類別不均衡時優(yōu)先補少樣本類而不是繼續(xù)堆多樣本類。2.2 標(biāo)注格式文件夾分類 vs CSV 清單圖像分類最省事的標(biāo)注方式就是按文件夾分。每個類別一個文件夾文件夾名就是類別名圖片直接放進(jìn)去。這種格式幾乎所有訓(xùn)練框架都認(rèn)比如torchvision.datasets.ImageFolder、tensorflow.keras.preprocessing.image_dataset_from_directory。另一種是 CSV 清單兩列filename,label。CSV 的好處是靈活可以隨時改標(biāo)簽、做交叉驗證劃分也方便記錄來源和備注。我一般會同時保留兩份原始文件夾結(jié)構(gòu)用于快速預(yù)覽CSV 用于訓(xùn)練腳本讀取。下面是一個把文件夾結(jié)構(gòu)轉(zhuǎn)成 CSV 的腳本順便做一次基礎(chǔ)校驗。import os import csv from pathlib import Path # 數(shù)據(jù)集根目錄下面每個子文件夾是一個類別 data_root Path(dataset/garbage_cls) # 輸出 CSV 路徑 csv_path Path(dataset/train_labels.csv) # 允許的圖片后綴避免把 .DS_Store 之類的文件讀進(jìn)來 valid_ext {.jpg, .jpeg, .png, .bmp, .webp} rows [] class_names sorted([d.name for d in data_root.iterdir() if d.is_dir()]) print(類別列表:, class_names) for cls_name in class_names: cls_dir data_root / cls_name for img_path in cls_dir.iterdir(): if img_path.suffix.lower() not in valid_ext: continue # 相對路徑方便后續(xù)遷移數(shù)據(jù)集位置 rel_path img_path.relative_to(data_root) rows.append({filename: str(rel_path), label: cls_name}) # 寫 CSV帶表頭 with csv_path.open(w, newline, encodingutf-8) as f: writer csv.DictWriter(f, fieldnames[filename, label]) writer.writeheader() writer.writerows(rows) print(f共寫入 {len(rows)} 條記錄到 {csv_path})這段腳本的邏輯很直白遍歷每個類別文件夾過濾掉非圖片文件把相對路徑和類別名寫成 CSV。參數(shù)上data_root指向你的數(shù)據(jù)集根目錄csv_path是輸出位置。跑完之后你可以用pandas讀一下檢查每個類別的數(shù)量是否均衡。import pandas as pd df pd.read_csv(dataset/train_labels.csv) print(df[label].value_counts())如果某個類別的數(shù)量明顯偏少比如只有 80 張而其他類有 500 張那就需要先處理不均衡問題而不是直接開訓(xùn)。常見做法是數(shù)據(jù)增強、重采樣、或者用類別權(quán)重。這個后面在訓(xùn)練章節(jié)會展開。2.3 圖像質(zhì)量與背景干擾垃圾分類數(shù)據(jù)集的天然難點垃圾分類圖像和 ImageNet 那種干凈數(shù)據(jù)集不一樣。你的圖片可能來自手機(jī)拍攝背景里有地面、手、其他垃圾、光照變化。瓶子類還有反光、透明、形變的問題。透明塑料瓶和玻璃瓶在視覺上非常接近模型很容易把兩者搞混。這不是模型不行是數(shù)據(jù)本身就有歧義。我一般會做三件事第一人工抽檢 100 張看看有沒有明顯標(biāo)錯的第二統(tǒng)計圖像尺寸分布太小的圖比如小于 100x100直接剔除第三對透明瓶和玻璃瓶這類易混類別額外補充側(cè)光、逆光、不同角度的樣本。如果條件允許用albumentations做在線增強時對這兩類加更強的顏色抖動和隨機(jī)裁剪讓模型學(xué)到材質(zhì)差異而不是背景捷徑。提示不要用「瓶子」和「垃圾」這種粗標(biāo)簽直接訓(xùn)。粗標(biāo)簽會讓模型學(xué)到「有瓶子就是可回收」這種捷徑換個場景就廢了。3. 用 PyTorch 跑通瓶子垃圾圖像分類基線從 DataLoader 到第一個 epoch3.1 環(huán)境準(zhǔn)備與最小依賴深度學(xué)習(xí)環(huán)境這件事新手最容易卡在版本沖突上。我一般用 conda 建一個干凈環(huán)境只裝必要的東西。下面這套是 CPU 和單卡 GPU 都能跑的配置不追求最新追求能復(fù)現(xiàn)。conda create -n garbage_cls python3.10 -y conda activate garbage_cls pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install pandas pillow scikit-learn matplotlib tqdm如果你沒有 GPU把cu118換成cpu即可。裝完之后用下面這行驗證import torch print(torch.__version__, torch.cuda.is_available())torch.cuda.is_available()返回True說明 GPU 可用。返回False也不影響跑基線只是慢一點。3.2 構(gòu)建 Dataset 和 DataLoaderPyTorch 里最穩(wěn)的做法是繼承torch.utils.data.Dataset自己寫__getitem__。這樣你能完全控制讀取邏輯也方便后面加增強。import torch from torch.utils.data import Dataset, DataLoader from PIL import Image from torchvision import transforms import pandas as pd from pathlib import Path class GarbageDataset(Dataset): def __init__(self, csv_file, img_root, transformNone): self.df pd.read_csv(csv_file) self.img_root Path(img_root) self.transform transform # 類別到索引的映射排序保證可復(fù)現(xiàn) self.classes sorted(self.df[label].unique()) self.class_to_idx {c: i for i, c in enumerate(self.classes)} def __len__(self): return len(self.df) def __getitem__(self, idx): row self.df.iloc[idx] img_path self.img_root / row[filename] # 統(tǒng)一轉(zhuǎn) RGB防止灰度圖或 RGBA 圖導(dǎo)致通道數(shù)不一致 image Image.open(img_path).convert(RGB) label self.class_to_idx[row[label]] if self.transform: image self.transform(image) return image, label # 訓(xùn)練增強隨機(jī)裁剪、翻轉(zhuǎn)、顏色抖動 train_tf transforms.Compose([ transforms.Resize((256, 256)), transforms.RandomResizedCrop(224, scale(0.7, 1.0)), transforms.RandomHorizontalFlip(), transforms.ColorJitter(brightness0.3, contrast0.3, saturation0.3), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]), ]) # 驗證集只做 resize 和歸一化不做隨機(jī)增強 val_tf transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]), ]) train_ds GarbageDataset(dataset/train_labels.csv, dataset/garbage_cls, train_tf) val_ds GarbageDataset(dataset/val_labels.csv, dataset/garbage_cls, val_tf) train_loader DataLoader(train_ds, batch_size32, shuffleTrue, num_workers4) val_loader DataLoader(val_ds, batch_size32, shuffleFalse, num_workers4)這里有幾個參數(shù)值得說清楚。batch_size32是單卡 8G 顯存下的穩(wěn)妥值顯存不夠就降到 16 或 8。num_workers4在 Windows 上有時會出問題如果報錯就改成 0。Normalize用的均值和方差是 ImageNet 的統(tǒng)計值因為后面要用預(yù)訓(xùn)練模型保持一致最省事。RandomResizedCrop的scale(0.7, 1.0)表示隨機(jī)裁取原圖 70% 到 100% 的區(qū)域?qū)ζ孔舆@種主體居中的圖比較合適裁太狠會把瓶身切掉。3.3 選一個 backboneResNet18 還是更小的模型垃圾分類數(shù)據(jù)集通常不大幾千到幾萬張。這種規(guī)模下ResNet18 或 EfficientNet-B0 是性價比最高的選擇。ResNet18 參數(shù)量約 1100 萬ImageNet 預(yù)訓(xùn)練權(quán)重容易拿訓(xùn)練穩(wěn)定。EfficientNet-B0 更小但輸入分辨率要求 224 以上對透明瓶的細(xì)節(jié)可能更友好。我一般先用 ResNet18 跑基線如果準(zhǔn)確率卡在某個值上不去再換 backbone 對比。import torch.nn as nn from torchvision import models def build_model(num_classes, backboneresnet18, pretrainedTrue): if backbone resnet18: model models.resnet18(weightsmodels.ResNet18_Weights.DEFAULT if pretrained else None) # 替換最后的全連接層輸出類別數(shù) model.fc nn.Linear(model.fc.in_features, num_classes) elif backbone efficientnet_b0: model models.efficientnet_b0(weightsmodels.EfficientNet_B0_Weights.DEFAULT if pretrained else None) model.classifier[1] nn.Linear(model.classifier[1].in_features, num_classes) else: raise ValueError(f不支持的 backbone: {backbone}) return model num_classes len(train_ds.classes) model build_model(num_classes, backboneresnet18, pretrainedTrue) device torch.device(cuda if torch.cuda.is_available() else cpu) model model.to(device) print(f類別數(shù): {num_classes}, 設(shè)備: {device})pretrainedTrue會加載 ImageNet 權(quán)重這對小數(shù)據(jù)集非常關(guān)鍵。從頭訓(xùn) ResNet18 在幾千張圖上大概率過擬合。替換fc層之后只有最后一層是隨機(jī)初始化的前面都是預(yù)訓(xùn)練特征。3.4 訓(xùn)練循環(huán)與關(guān)鍵參數(shù)訓(xùn)練循環(huán)本身不復(fù)雜但有幾個參數(shù)決定你能不能跑出結(jié)果。優(yōu)化器用AdamW學(xué)習(xí)率1e-3對預(yù)訓(xùn)練模型微調(diào)偏大我一般用3e-4或1e-4。損失函數(shù)用CrossEntropyLoss如果類別不均衡加weight參數(shù)。import torch.optim as optim from tqdm import tqdm criterion nn.CrossEntropyLoss() optimizer optim.AdamW(model.parameters(), lr3e-4, weight_decay1e-4) scheduler optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max20) def train_one_epoch(model, loader, criterion, optimizer, device): model.train() total_loss, correct, total 0.0, 0, 0 for images, labels in tqdm(loader, desctrain): images, labels images.to(device), labels.to(device) optimizer.zero_grad() outputs model(images) loss criterion(outputs, labels) loss.backward() optimizer.step() total_loss loss.item() * images.size(0) preds outputs.argmax(dim1) correct (preds labels).sum().item() total labels.size(0) return total_loss / total, correct / total torch.no_grad() def evaluate(model, loader, criterion, device): model.eval() total_loss, correct, total 0.0, 0, 0 for images, labels in tqdm(loader, descval): images, labels images.to(device), labels.to(device) outputs model(images) loss criterion(outputs, labels) total_loss loss.item() * images.size(0) preds outputs.argmax(dim1) correct (preds labels).sum().item() total labels.size(0) return total_loss / total, correct / total best_acc 0.0 for epoch in range(20): train_loss, train_acc train_one_epoch(model, train_loader, criterion, optimizer, device) val_loss, val_acc evaluate(model, val_loader, criterion, device) scheduler.step() print(fepoch {epoch1}: train_loss{train_loss:.4f} train_acc{train_acc:.4f} fval_loss{val_loss:.4f} val_acc{val_acc:.4f}) if val_acc best_acc: best_acc val_acc torch.save(model.state_dict(), best_garbage_cls.pth) print(f保存最佳模型val_acc{best_acc:.4f})CosineAnnealingLR的T_max20表示 20 個 epoch 內(nèi)學(xué)習(xí)率按余弦曲線降到接近 0。如果你只跑 10 個 epoch就把T_max改成 10。weight_decay1e-4是 AdamW 的常規(guī)值能壓一點過擬合。保存模型時只存state_dict不存整個模型對象這樣加載時更靈活。跑完 20 個 epoch如果驗證集準(zhǔn)確率能到 85% 以上說明數(shù)據(jù)集和基線是通的。如果卡在 60% 左右大概率是類別混淆或標(biāo)注問題不是模型問題。4. 垃圾分類圖像分類的避坑與排查標(biāo)注、不均衡、過擬合、透明瓶混淆4.1 標(biāo)注不一致同一個瓶子被標(biāo)成兩個類現(xiàn)象訓(xùn)練 loss 震蕩驗證準(zhǔn)確率上不去混淆矩陣?yán)锬硟蓚€類互相錯得特別多。原因標(biāo)注時沒有統(tǒng)一標(biāo)準(zhǔn)比如有人把「透明塑料瓶」標(biāo)成「玻璃瓶」有人反過來。解決先抽 200 張做一致性檢查讓兩個人獨立標(biāo)同一批圖算一下一致率。低于 90% 就重新定標(biāo)準(zhǔn)把易混類別的判定規(guī)則寫得更細(xì)比如「瓶底有厚玻璃紋路算玻璃瓶瓶身有塑料接縫算塑料瓶」。4.2 類別不均衡少樣本類被模型忽略現(xiàn)象總體準(zhǔn)確率看著還行但少樣本類的召回率極低模型幾乎全預(yù)測成多樣本類。原因CrossEntropyLoss默認(rèn)每個樣本權(quán)重一樣多樣本類主導(dǎo)梯度。解決給CrossEntropyLoss加weight權(quán)重和類別頻率成反比?;蛘哂弥夭蓸幼屆總€ batch 里少樣本類被抽到的概率更高。import numpy as np class_counts train_ds.df[label].value_counts().sort_index().values class_weights 1.0 / class_counts class_weights class_weights / class_weights.sum() * len(class_counts) class_weights torch.tensor(class_weights, dtypetorch.float32).to(device) criterion nn.CrossEntropyLoss(weightclass_weights) print(類別權(quán)重:, class_weights)這段代碼先統(tǒng)計每個類的樣本數(shù)取倒數(shù)再歸一化到均值為 1 左右。weight傳給損失函數(shù)后少樣本類的 loss 會被放大梯度更新時更受重視。4.3 過擬合訓(xùn)練準(zhǔn)確率 99%驗證準(zhǔn)確率 70%現(xiàn)象訓(xùn)練集準(zhǔn)確率一路漲到 99%驗證集卡在 70% 不動甚至下降。原因數(shù)據(jù)集太小、模型太大、增強不夠。解決先加數(shù)據(jù)增強RandomResizedCrop、ColorJitter、RandomRotation都可以上。再加DropoutResNet18 的fc前面可以插一個nn.Dropout(0.5)。最后考慮凍結(jié)前面層只訓(xùn)fc或者用更小的學(xué)習(xí)率。4.4 透明瓶與玻璃瓶混淆模型學(xué)的是背景不是材質(zhì)現(xiàn)象混淆矩陣?yán)锿该魉芰掀亢筒A炕ハ噱e得最多但人眼看這兩類差異明顯。原因模型可能學(xué)到了背景捷徑比如透明瓶常出現(xiàn)在某類背景上玻璃瓶在另一類背景上。解決把背景裁掉或者用分割模型先摳出瓶身再分類。更簡單的做法是加隨機(jī)背景替換增強把瓶子貼到不同背景上逼模型看瓶身。4.5 圖像尺寸和通道不一致導(dǎo)致訓(xùn)練中斷現(xiàn)象訓(xùn)練到一半報錯提示 tensor 尺寸不匹配或通道數(shù)不對。原因數(shù)據(jù)集里混了灰度圖、RGBA 圖、尺寸差異極大的圖。解決在__getitem__里統(tǒng)一convert(RGB)在transform里統(tǒng)一Resize。如果還有問題寫個腳本掃一遍所有圖片把打不開的、尺寸小于 50x50 的直接列出來剔除。from PIL import Image from pathlib import Path bad_files [] for img_path in Path(dataset/garbage_cls).rglob(*): if img_path.suffix.lower() not in {.jpg, .jpeg, .png, .bmp, .webp}: continue try: with Image.open(img_path) as im: w, h im.size if w 50 or h 50: bad_files.append((str(img_path), too_small, w, h)) except Exception as e: bad_files.append((str(img_path), unreadable, str(e))) for item in bad_files: print(item) print(f共發(fā)現(xiàn) {len(bad_files)} 個問題文件)這個腳本會列出所有打不開或太小的圖片。跑一遍把問題文件處理掉能省掉很多訓(xùn)練中途報錯的麻煩。5. 把基線推到可用混淆矩陣診斷、難例挖掘與一個提點技巧5.1 用混淆矩陣定位問題類別準(zhǔn)確率只是一個數(shù)字真正有用的是混淆矩陣。下面這段代碼在驗證集上跑一遍畫出每個類別的錯分情況。from sklearn.metrics import confusion_matrix, classification_report import numpy as np torch.no_grad() def get_all_preds(model, loader, device): model.eval() all_preds, all_labels [], [] for images, labels in loader: images images.to(device) outputs model(images) preds outputs.argmax(dim1).cpu().numpy() all_preds.extend(preds) all_labels.extend(labels.numpy()) return np.array(all_labels), np.array(all_preds) labels, preds get_all_preds(model, val_loader, device) cm confusion_matrix(labels, preds) print(混淆矩陣:) print(cm) print(classification_report(labels, preds, target_namesval_ds.classes))classification_report會給出每個類的 precision、recall、f1-score。如果某個類的 recall 特別低說明這個類被大量錯分。結(jié)合混淆矩陣你能看出它主要被錯分成了哪個類。比如透明塑料瓶的 recall 只有 0.6且大部分錯分到玻璃瓶那就針對這兩類做難例挖掘。5.2 難例挖掘把錯分樣本挑出來單獨看難例挖掘的思路很簡單用當(dāng)前模型跑一遍訓(xùn)練集把預(yù)測錯誤的樣本挑出來人工看一遍確認(rèn)是標(biāo)注問題還是模型問題。如果是標(biāo)注問題修正標(biāo)簽如果是模型問題把這些難例復(fù)制幾份加入訓(xùn)練集或者對這些類做更強的增強。torch.no_grad() def find_hard_examples(model, dataset, device, top_k50): model.eval() hard [] for idx in range(len(dataset)): image, label dataset[idx] # dataset 返回的是增強后的圖這里為了診斷用原始圖更合適 # 簡化處理直接用增強后的圖推理 input_tensor image.unsqueeze(0).to(device) output model(input_tensor) pred output.argmax(dim1).item() if pred ! label: hard.append((idx, label, pred)) return hard[:top_k] hard_examples find_hard_examples(model, val_ds, device, top_k30) for idx, true_label, pred_label in hard_examples: print(fidx{idx} 真實{val_ds.classes[true_label]} 預(yù)測{val_ds.classes[pred_label]})這段代碼會列出驗證集里被錯分的樣本索引和類別。你可以根據(jù)索引找到原圖看看是標(biāo)注錯了還是模型確實分不出來。如果是標(biāo)注錯了改標(biāo)簽如果是模型問題把這些圖加入訓(xùn)練集或者對這類圖做針對性增強。5.3 一個提點技巧兩階段訓(xùn)練與標(biāo)簽平滑如果基線準(zhǔn)確率卡在 85% 左右上不去可以試兩階段訓(xùn)練。第一階段凍結(jié) backbone只訓(xùn)分類頭學(xué)習(xí)率設(shè)1e-3跑 5 個 epoch。第二階段解凍全部層學(xué)習(xí)率降到1e-4跑 15 個 epoch。這樣能讓分類頭先適應(yīng)數(shù)據(jù)分布再微調(diào)整個網(wǎng)絡(luò)比一上來就全量微調(diào)更穩(wěn)。另外標(biāo)簽平滑label smoothing對垃圾分類這種有歧義的類別很有效。CrossEntropyLoss(label_smoothing0.1)會讓模型不要把概率壓到 1.0對易混類別更寬容。我一般會在第二階段加上通常能提 1 到 2 個點。# 第二階段解凍全部層小學(xué)習(xí)率微調(diào) for param in model.parameters(): param.requires_grad True optimizer optim.AdamW(model.parameters(), lr1e-4, weight_decay1e-4) criterion nn.CrossEntropyLoss(label_smoothing0.1) scheduler optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max15) for epoch in range(15): train_loss, train_acc train_one_epoch(model, train_loader, criterion, optimizer, device) val_loss, val_acc evaluate(model, val_loader, criterion, device) scheduler.step() print(fstage2 epoch {epoch1}: val_acc{val_acc:.4f})標(biāo)簽平滑的0.1是經(jīng)驗值太大比如 0.3會讓模型欠擬合太小比如 0.01效果不明顯。兩階段訓(xùn)練的關(guān)鍵是第二階段學(xué)習(xí)率一定要小否則會破壞預(yù)訓(xùn)練特征。我自己的習(xí)慣是每次跑完基線先看混淆矩陣再決定是補數(shù)據(jù)、改標(biāo)注還是調(diào)模型。數(shù)據(jù)集的問題永遠(yuǎn)比模型多。希望幫到你。本文還有配套的精品資源點擊獲取