:基于5700張西瓜病數(shù)據(jù)集的遷移學(xué)習(xí)訓(xùn)練)
簡介西瓜病識別圖像分類數(shù)據(jù)集是一套面向圖像分類入門與農(nóng)業(yè)病害識別應(yīng)用的高質(zhì)量標(biāo)注資源適合深度學(xué)習(xí)初學(xué)者與研究者快速搭建分類模型。資源共2000個文件含1998張JPG圖片覆蓋花葉病毒、健康、炭疽病和霜霉病四個類別已劃分訓(xùn)練集與測試集并按類別分目錄存放另附JSON標(biāo)注文件和Python可視化腳本可快速查看圖像與標(biāo)簽。壓縮包約156.94MB結(jié)構(gòu)清晰下載后即可接入PyTorch/TensorFlow訓(xùn)練。目前已有59人瀏覽學(xué)習(xí)。作者還提供CNN分類網(wǎng)絡(luò)改進的配套專欄結(jié)合資源中的訓(xùn)練集與可視化腳本讀者可完成從數(shù)據(jù)處理、模型訓(xùn)練到精度優(yōu)化的完整流程適合課程設(shè)計、科研預(yù)實驗或項目練手。1. 拿到約5,700張已標(biāo)注的西瓜病識別數(shù)據(jù)集先別急著開訓(xùn)做圖像分類的同行應(yīng)該都有體會找一個“已標(biāo)注、能直接用”的垂直領(lǐng)域數(shù)據(jù)集有多難。西瓜病識別這個方向尤為典型——公開數(shù)據(jù)集里大多是葉片病害果實表面的病斑識別要么樣本少要么標(biāo)注只給到病害大類。手上這份正好反過來約5,700張數(shù)據(jù)已經(jīng)完成標(biāo)注類目落在西瓜果實常見病害和正常果上拿來跑圖像分類模型基本不需要再做標(biāo)注層面的返工。這篇文章想和你聊清楚三件事這5,700張適合做什么量級的任務(wù)、從標(biāo)簽解析到訓(xùn)出第一個模型的完整路徑、以及真正會讓結(jié)果翻車的邊界條件。適合誰看做課程設(shè)計、搞論文實驗、或者想練手垂直領(lǐng)域分類落地的工程師這個規(guī)模不會大到讓你等訓(xùn)練等到下班也不會小到訓(xùn)完毫無說服力。2. 先讀標(biāo)注再談訓(xùn)練目錄結(jié)構(gòu)與標(biāo)簽解析2.1 數(shù)據(jù)集最常見的兩種組織方式這類已標(biāo)注圖像分類數(shù)據(jù)集拿到手先看目錄不要急著寫訓(xùn)練腳本。常見做法有兩種第一種是images目錄存放全部圖片另配一個JSON或CSV文件記錄文件名和類別第二種是直接按類別建子目錄每個文件夾名就是標(biāo)簽。兩種組織方式?jīng)Q定了后面的數(shù)據(jù)讀取代碼完全不一樣。約5,700張的規(guī)模不算大但如果你一開始就假設(shè)錯了標(biāo)簽格式后面的清洗步驟全要返工。判斷方式很簡單打開根目錄看有沒有標(biāo)注文件。有JSON或者CSV就走映射方案純文件夾結(jié)構(gòu)就走目錄讀取方案。下面這段代碼用Python同時兼容兩種方案省得你反復(fù)改import os import json import csv from collections import Counter dataset_root ./watermelon_disease # 方案A有標(biāo)注文件JSON/CSV if os.path.exists(os.path.join(dataset_root, annotations.json)): with open(os.path.join(dataset_root, annotations.json), r, encodingutf-8) as f: ann json.load(f) # 常見格式之一{images: [{file_name: 001.jpg, label: anthracnose}], categories: [...]} items [(d[file_name], d[label]) for d in ann[images]] elif os.path.exists(os.path.join(dataset_root, labels.csv)): with open(os.path.join(dataset_root, labels.csv), r, encodingutf-8) as f: reader csv.DictReader(f) items [(row[file_name], row[label]) for row in reader] # 方案B按類別子目錄組織 else: items [] for class_name in os.listdir(dataset_root): class_dir os.path.join(dataset_root, class_name) if not os.path.isdir(class_dir): continue for fname in os.listdir(class_dir): if fname.lower().endswith((.jpg, .jpeg, .png)): items.append((os.path.join(class_name, fname), class_name)) counter Counter(label for _, label in items) print(f總樣本數(shù): {len(items)}) for label, cnt in counter.most_common(): print(f{label}: {cnt})這段代碼的邏輯是先探測標(biāo)注文件再兜底讀目錄最后統(tǒng)計類別分布。參數(shù)說明annotations.json和labels.csv是常見的兩種標(biāo)注文件名實際以你拿到的文件名為準(zhǔn)如果叫別的名字就把os.path.exists里的路徑改掉。encodingutf-8在Windows下最好加上否則中文類別名容易讀成亂碼。跑完這段你會得到兩個關(guān)鍵信息類別數(shù)和各類別樣本量——這兩個數(shù)字直接決定后續(xù)要不要做重采樣。2.2 把類別分布打出來看第一輪坑就藏在里面統(tǒng)計結(jié)果不要只看總數(shù)要把每個類別的數(shù)量打印出來。約5,700張聽上去挺均衡實際垂直領(lǐng)域數(shù)據(jù)集的類別分布往往偏得離譜健康果可能占一半某種病害只有三百張。如果某類占比低于5%直接進訓(xùn)練的話模型大概率把這類全部忽略。下面這個表是這類數(shù)據(jù)集常見的分布形態(tài)具體數(shù)字以你拿到手的實際統(tǒng)計為準(zhǔn)類別典型占比風(fēng)險提示健康果40%-50%數(shù)量充足注意與病斑果的特征區(qū)分炭疽病15%-25%病斑特征明顯容易學(xué)偏到果皮反光疫病10%-15%與日灼病在顏色上容易混淆日灼病8%-12%顏色偏淺黃和健康果亮度接近其他/少見病害5%-10%樣本太少需要過采樣或者干脆合并類別看到少數(shù)類只有兩三百張的時候先別急著刪。圖像分類里有一種做法叫類別合并——把癥狀相似、樣本都少的病害合并成一個“其他病害”類。約5,700張的數(shù)據(jù)規(guī)模合并后每類至少能有500張以上訓(xùn)練才穩(wěn)。這一步的取舍會在最終精度上體現(xiàn)得非常明顯我后面講避坑章節(jié)時會再展開。3. 數(shù)據(jù)清洗與劃分約5,700張里藏著多少重復(fù)和錯標(biāo)3.1 去重腳本哈希比對一步到位已標(biāo)注數(shù)據(jù)集不等于干凈數(shù)據(jù)集。垂直領(lǐng)域數(shù)據(jù)集經(jīng)常是從多個來源湊來的同一個果實的不同角度照片、同一張圖被縮放過兩次、甚至同一張圖在不同類目下各出現(xiàn)一次這些情況都不少見。約5,700張的規(guī)模人工翻一遍不現(xiàn)實寫腳本做哈希比對是標(biāo)準(zhǔn)做法。import hashlib from collections import defaultdict def file_md5(path, chunk_size8192): h hashlib.md5() with open(path, rb) as f: while chunk : f.read(chunk_size): h.update(chunk) return h.hexdigest() hash_map defaultdict(list) duplicates [] for file_path, label in items: # items 來自上一節(jié)的解析結(jié)果 full_path os.path.join(dataset_root, file_path) if not os.path.exists(full_path): continue digest file_md5(full_path) hash_map[digest].append((file_path, label)) for digest, file_list in hash_map.items(): if len(file_list) 1: duplicates.extend(file_list) print(f疑似重復(fù)文件組數(shù): {len([v for v in hash_map.values() if len(v) 1])}) for dup in duplicates: print(dup)這段腳本用MD5做文件級去重全等重復(fù)同一文件復(fù)制改名一定會被抓到。參數(shù)說明chunk_size8192是分塊讀取的緩沖區(qū)大小約5,700張圖片全量算一遍MD5也就幾秒到十幾秒。抓到重復(fù)文件后建議保留其中標(biāo)注信息更詳細(xì)的那條另一條直接從items里剔除。但要注意MD5只能抓全等重復(fù)。同一張圖被人為加了水印、調(diào)了亮度、改了尺寸MD5就對不上了。這時候要用感知哈希pHash或者直接用圖像縮放后做像素差比對。約5,700張的規(guī)模下pHash全量比對是秒級到分鐘級的事值得跑一遍import cv2 import numpy as np def perceptual_hash(image_path, size(32, 32)): img cv2.imread(image_path, cv2.IMREAD_GRAYSCALE) img cv2.resize(img, size, interpolationcv2.INTER_AREA) # 用離散余弦變換的低頻分量做特征 img_float np.float32(img) / 255.0 dct cv2.dct(img_float) # 取左上角8x8低頻塊去掉第一個直流分量 low_freq dct[:8, :8].flatten()[1:] median_val np.median(low_freq) return (low_freq median_val).astype(np.uint8) # 對items抽樣計算兩兩比較漢明距離距離小于閾值視為疑似重復(fù)這段不做全量兩兩對比——約5,700張兩兩比對的復(fù)雜度是O(n^2)沒必要。常見做法是抽樣幾百張做快速篩查確認(rèn)數(shù)據(jù)源風(fēng)格差異大不大。如果這步發(fā)現(xiàn)大量近重復(fù)圖我一般會寫一個窗口式的增量去重把漢明距離小于10的圖片打印出來人工二次確認(rèn)。注意去重和后面的數(shù)據(jù)劃分是兩件事先去重再劃分順序不能反過來。3.2 按“來源組”而不是“單張圖”劃分訓(xùn)練驗證集劃分訓(xùn)練集、驗證集、測試集約5,700張的常規(guī)比例是7:1.5:1.5也就是訓(xùn)練約3,990張驗證和測試各約855張。但真正影響模型可信度的不是比例而是劃分的單位。如果你拿到的是多角度拍攝的數(shù)據(jù)同一顆西瓜出現(xiàn)在多個文件里隨機按單張圖去劃分訓(xùn)練集和驗證集里會出現(xiàn)同一個果實的相似照片驗證集精度虛高落地實測立刻打回原形。正確做法是先看文件名或者目錄結(jié)構(gòu)里有沒有“來源分組”的信息。有的數(shù)據(jù)集文件名類似field1_20230912_001.jpg前半段就是分組鍵有的按采集批次分了子目錄。按組劃分再隨機抽組代碼如下import random from collections import defaultdict group_items defaultdict(list) for file_path, label in items: # 按文件名前綴分組常見分隔符是 _ 或 - group_key file_path.split(_)[0] group_items[group_key].append((file_path, label)) groups list(group_items.keys()) random.seed(42) random.shuffle(groups) train_groups groups[: int(len(groups) * 0.7)] val_groups groups[int(len(groups) * 0.7) : int(len(groups) * 0.85)] test_groups groups[int(len(groups) * 0.85) :] train_items [item for g in train_groups for item in group_items[g]] val_items [item for g in val_groups for item in group_items[g]] test_items [item for g in test_groups for item in group_items[g]] print(f訓(xùn)練集: {len(train_items)}, 驗證集: {len(val_items)}, 測試集: {len(test_items)})這段代碼的核心邏輯是先提取分組鍵對組做隨機化再按組劃分。參數(shù)說明split(_)[0]是常見的分組鍵提取方式實際要以文件名的命名規(guī)則為準(zhǔn)可能是前兩個字段、也可能是中間某段看了文件名格式再定。random.seed(42)固定隨機種子保證實驗可復(fù)現(xiàn)。如果文件名看不出分組信息退而求其次的做法是檢查圖片的采集時間戳或EXIF信息但這步比較費工時不是所有數(shù)據(jù)集都具備條件。4. 訓(xùn)練第一個模型用預(yù)訓(xùn)練圖像分類模型做遷移學(xué)習(xí)4.1 為什么約5,700張不要從零訓(xùn)練在圖像分類這個任務(wù)上約5,700張數(shù)據(jù)做從零訓(xùn)練是筆虧本買賣。ResNet從零訓(xùn)到收斂需要百萬級數(shù)據(jù)支撐5,700張連正則化都壓不住模型直接過擬合到訓(xùn)練集。現(xiàn)在主流的圖像分類模型都在遷移學(xué)習(xí)路徑上用ImageNet預(yù)訓(xùn)練權(quán)重做初始化凍結(jié)前幾層只微調(diào)后面幾層和分類頭。這是垂直領(lǐng)域小數(shù)據(jù)集的標(biāo)準(zhǔn)打法也是工業(yè)界最穩(wěn)的做法。模型怎么選ResNet18和EfficientNet-B0是兩類代表ResNet系列結(jié)構(gòu)簡單、顯存占用小、在CPU上也能做推理EfficientNet-B0精度略高且參數(shù)更少但對輸入分辨率更敏感默認(rèn)的224x224輸入表現(xiàn)不錯。如果你只想快速在西瓜病識別上跑通流程ResNet18足夠在意的指標(biāo)是Top-1準(zhǔn)確率而不是推理速度就上EfficientNet-B0。至于ViT這類強Transformer架構(gòu)在約5,700張規(guī)模下預(yù)訓(xùn)練權(quán)重的作用沒有CNN陣營那么強容易在微調(diào)后期精度爬不上去。import torch import torch.nn as nn import torchvision.models as models from torch.utils.data import Dataset, DataLoader from torchvision import transforms from PIL import Image class WatermelonDataset(Dataset): def __init__(self, items, dataset_root, transformNone): self.items items # [(file_path, label), ...] self.root dataset_root self.transform transform self.class_to_idx self._build_class_index() def _build_class_index(self): unique_labels sorted(set(label for _, label in self.items)) return {label: idx for idx, label in enumerate(unique_labels)} def __len__(self): return len(self.items) def __getitem__(self, idx): file_path, label self.items[idx] img Image.open(os.path.join(self.root, file_path)).convert(RGB) if self.transform: img self.transform(img) return img, self.class_to_idx[label] transform_train transforms.Compose([ transforms.Resize((256, 256)), transforms.RandomCrop(224), transforms.RandomHorizontalFlip(p0.5), transforms.ColorJitter(brightness0.2, contrast0.2, saturation0.2), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]), ]) transform_eval transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]), ]) model models.resnet18(weightsmodels.ResNet18_Weights.IMAGENET1K_V1) num_classes len(set(label for _, label in items)) 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) train_loader DataLoader( WatermelonDataset(train_items, dataset_root, transform_train), batch_size32, shuffleTrue, num_workers4 ) val_loader DataLoader( WatermelonDataset(val_items, dataset_root, transform_eval), batch_size32, shuffleFalse, num_workers4 ) criterion nn.CrossEntropyLoss() optimizer torch.optim.Adam(model.parameters(), lr3e-4) for epoch in range(20): 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() # 每個epoch后驗證 model.eval() correct, total 0, 0 with torch.no_grad(): 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 correct / total print(fEpoch {epoch1:02d} | Loss: {running_loss/len(train_loader):.4f} | Val Acc: {val_acc:.4f})這段代碼是完整的訓(xùn)練骨架幾個關(guān)鍵參數(shù)值得細(xì)說models.ResNet18_Weights.IMAGENET1K_V1加載ImageNet預(yù)訓(xùn)練權(quán)重。這里用帶權(quán)重的枚舉寫法不推薦pretrainedTrue這種舊方式因為新版torchvision已經(jīng)把參數(shù)名改了。model.fc nn.Linear(model.fc.in_features, num_classes)替換最后一層全連接輸出維度改成實際類別數(shù)。分類頭的初始化會用默認(rèn)方式不需要額外處理。batch_size32約5,700張訓(xùn)練集一個epoch大約125個batch20個epoch是2,500次迭代在單張消費級顯卡上幾分鐘就能跑完一輪實驗。lr3e-4遷移學(xué)習(xí)的典型學(xué)習(xí)率區(qū)間是1e-4到5e-43e-4是一個安全中間值。如果發(fā)現(xiàn)Loss震蕩劇烈降一個數(shù)量級到3e-5再試。幾個訓(xùn)練時的注意點ColorJitter對西瓜病識別很敏感——過度調(diào)飽和度會讓病斑顏色失真建議把saturation調(diào)低到0.1RandomCrop配合Resize是先放大到256再隨機裁剪到224相當(dāng)于引入輕微的尺度擾動對病斑大小變化有抑制作用。訓(xùn)練過程中盯著驗證集Loss如果驗證Loss在第10個epoch之后不降反升大概率是過擬合了有兩種處理提前停止或者把學(xué)習(xí)率降到1e-5繼續(xù)跑幾個epoch。4.2 類別不均衡時的損失函數(shù)調(diào)整如果第2章的統(tǒng)計結(jié)果顯示少數(shù)類占比很低直接用CrossEntropyLoss會讓模型偏向多數(shù)類。這時候可以給損失函數(shù)加類別權(quán)重權(quán)重和樣本量成反比from collections import Counter class_counts Counter(label for _, label in train_items) total sum(class_counts.values()) class_weights torch.tensor( [total / (len(class_counts) * class_counts[label]) for label in sorted(class_counts.keys())], dtypetorch.float32 ).to(device) criterion nn.CrossEntropyLoss(weightclass_weights)這個權(quán)重公式的含義是每個類的權(quán)重 總樣本數(shù) / (類別數(shù) × 該類樣本數(shù))。樣本量少的類別權(quán)重放大樣本量多的類別權(quán)重縮小。比如某類只有300張權(quán)重算出來是約3.2另一類有2,000張權(quán)重約0.48訓(xùn)練時模型會“更在意”樣本少的類。加了類別權(quán)重之后驗證集指標(biāo)不要只看整體準(zhǔn)確率要看每一類的召回率。約5,700張這個規(guī)模小類可能只有幾十個樣本整體準(zhǔn)確率85%但小類召回率為0的情況在西瓜病識別里太常見了我下面會把這個問題單獨放進避坑清單。5. 避坑指南西瓜病識別訓(xùn)練里最常見的四個翻車現(xiàn)場5.1 驗證集精度高實際測試卻翻車做圖像分類的人一定見過這個場景訓(xùn)練時驗證集精度一路爬到92%拿到新的西瓜圖片一測識別結(jié)果完全不對。發(fā)現(xiàn)這類問題要先去查數(shù)據(jù)劃分方式。之前說過如果同一來源同一顆西瓜、同一塊地、同一天拍攝的近重復(fù)圖片被拆進了訓(xùn)練集和驗證集驗證時模型見過了近似圖像成績自然虛高。解決方法是回到第3章的按組劃分思路重新生成三個集合后再訓(xùn)練。另外一個隱蔽原因是測試圖片的來源分布和訓(xùn)練集差異太大比如訓(xùn)練集全是田間拍攝測試卻用了倉庫里的照片光照、背景、拍攝距離全部變化模型見過的特征對不上。處理手段是手動把測試集里識別錯誤的圖挑出來看確認(rèn)是特征分布漂移還是模型本身學(xué)錯了。5.2 病斑太小模型學(xué)的是果皮反光不是病斑西瓜病斑在整張圖中的占比往往很小尤其是早期病斑可能只有十幾個像素。模型在這種數(shù)據(jù)上學(xué)到的特征可能是果皮的高光區(qū)域、葉片的綠色背景、甚至照片角落的水印而不是真正的病斑紋理。我遇到過一版模型看起來精度很高用熱力圖一看關(guān)注的全是果實邊緣的反光區(qū)域。解決辦法有三個一是先做目標(biāo)裁剪把果實區(qū)域從背景中分離出來再送入分類模型常見做法是先用目標(biāo)檢測器定位果實或者人工裁剪后重新訓(xùn)練二是把輸入分辨率從224提高到384給病斑多留一些像素三是用Grad-CAM這類可視化工具逐張檢查發(fā)現(xiàn)學(xué)錯特征就去調(diào)整預(yù)處理或者換模型。在約5,700張的規(guī)模下優(yōu)先嘗試后兩種方案成本最低見效最快。5.3 少數(shù)類完全學(xué)不動訓(xùn)練結(jié)束時少數(shù)類準(zhǔn)確率依然是0的問題多數(shù)來自訓(xùn)練過程中模型從未把該類的loss降到有效范圍。排查手段是在每個epoch打印loss per class如果某個類平均loss一直不下降就是沒學(xué)進去。原因通常有兩個一是類別權(quán)重加得不夠把權(quán)重調(diào)大一倍再試二是該類的訓(xùn)練樣本之間差異太大病斑形態(tài)、角度、光照各不相同模型通過有限的50張樣本學(xué)不到穩(wěn)定的類別特征這時候需要檢查標(biāo)注是否有誤把同類樣本鋪開人工過一遍。如果標(biāo)注沒問題另一個方向是把類別合并同類病害合并成一個大類雖然細(xì)粒度信息丟了但至少模型在這個類上有區(qū)分度。5.4 訓(xùn)練過程中Loss出現(xiàn)NaN約5,700張的小數(shù)據(jù)集上用Adam優(yōu)化器Loss出現(xiàn)NaN的概率不大但也不是沒有。常見誘因是學(xué)習(xí)率過大導(dǎo)致梯度爆炸或者輸入圖像中有損壞的圖片文件截斷的JPEG數(shù)據(jù)讀取時返回了空張量。一個排查思路是先把學(xué)習(xí)率降到1e-5跑幾個epoch看是否復(fù)現(xiàn)不復(fù)現(xiàn)就是學(xué)習(xí)率問題另一個做法是所有訓(xùn)練圖片做一遍完整性校驗——用PIL打開并轉(zhuǎn)換RGB拋異常的圖片直接從items里剔除。另外使用混合精度訓(xùn)練AMP時也要注意小數(shù)據(jù)集上精度縮放策略設(shè)置不當(dāng)也會導(dǎo)致NaN通常把torch.cuda.amp.GradScaler的init_scale參數(shù)降低即可。6. 最后的進階技巧用Grad-CAM驗證模型到底在看什么地方訓(xùn)練完模型先別急著驗收精度指標(biāo)。我現(xiàn)在的習(xí)慣是花半小時做一輪可視化歸因這個動作幫我躲開了至少三次誤報。Grad-CAM能生成熱力圖標(biāo)記出模型做決策時關(guān)注的圖像區(qū)域這對病斑類任務(wù)非常關(guān)鍵——判斷模型看的是“病斑”還是“背景”一目了然。from pytorch_grad_cam import GradCAM from pytorch_grad_cam.utils.model_targets import ClassifierOutputTarget from pytorch_grad_cam.utils.image import show_cam_on_image import matplotlib.pyplot as plt model.eval() cam_model GradCAM(modelmodel, target_layers[model.layer4[-1]]) # 隨便挑一張測試集里的病斑圖 img_path, true_label test_items[0] img Image.open(os.path.join(dataset_root, img_path)).convert(RGB) input_tensor transform_eval(img).unsqueeze(0).to(device) target [ClassifierOutputTarget(class_idx)] grayscale_cam cam_model(input_tensorinput_tensor, targetstarget)[0] visualization show_cam_on_image( np.float32(np.array(img.resize((224, 224)))) / 255.0, grayscale_cam, use_rgbTrue ) plt.imshow(visualization) plt.axis(off) plt.show()這段代碼把熱力圖疊加在原始圖像上邏輯說明target_layers[model.layer4[-1]]選擇ResNet最后一個卷積塊作為特征提取層病斑的位置信息在這一層仍然保留ClassifierOutputTarget指定要看哪個類別的激活區(qū)域——傳入錯誤類別就能看到模型在判斷這個類時看的是哪里。如果熱力圖中心偏離病斑區(qū)域、落在果皮反光或背景上說明模型沒學(xué)到該學(xué)的特征。配合一個簡單的量化檢查手動框出測試圖像上的病斑區(qū)域用標(biāo)注框或者簡單摳圖計算熱力圖高亮區(qū)域和病斑區(qū)域的IoU。如果IoU低于0.3這張圖的預(yù)測結(jié)果就要打上問號。用這個方法在約5,700張的驗證集或測試集上抽100張左右做一次抽樣檢查基本就能判斷模型學(xué)的是“西瓜病”還是“西瓜反光”了。這個習(xí)慣幫我篩掉過很多表面高精度、實際不可用的模型權(quán)重。希望幫到你。本文還有配套的精品資源點擊獲取