據(jù)集實戰(zhàn):從YOLOv8訓(xùn)練到密集小目標切片推理)
簡介這是一份專為計算機視覺與醫(yī)學圖像分析場景準備的血細胞檢測數(shù)據(jù)集面向算法工程師、研究人員及醫(yī)學生用于訓(xùn)練目標檢測與分類模型。壓縮包共1753個文件其中876個txt標簽文件與874張jpg圖像一一對應(yīng)另含yaml配置、license許可說明及md文檔整體大小約11.81MB結(jié)構(gòu)清晰便于快速上手。數(shù)據(jù)集已按train、valid、test劃分可直接用于構(gòu)建卷積神經(jīng)網(wǎng)絡(luò)在TensorFlow或PyTorch等框架下訓(xùn)練識別紅細胞、白細胞、血小板等血細胞類型并完成模型調(diào)參與泛化評估。已有150人學習下載適合在血細胞自動識別、異常檢測及醫(yī)療影像輔助診斷等課題中作為基準數(shù)據(jù)借助配套的README等說明文檔還可降低理解數(shù)據(jù)格式與標注規(guī)則的門檻對貧血、白血病等疾病的早期篩查研究具有參考價值。整體來看這份數(shù)據(jù)集的目錄組織與配套文檔也為入門者提供了低成本實踐醫(yī)學圖像識別項目的良好起點。1. 血細胞檢測數(shù)據(jù)集.zip醫(yī)學影像目標檢測里最典型的密集小目標考題你從同事手里接過來一個血細胞檢測數(shù)據(jù)集.zip解壓后不是現(xiàn)成的紅色圖片文件夾而是幾十張血涂片顯微圖像和同樣多的XML標注。這正是醫(yī)學影像自動化檢驗最常見的起點用目標檢測模型把白細胞、紅細胞、血小板的位置和類別找出來替代人工鏡檢計數(shù)。它解決的是血常規(guī)檢驗中的識別與定位問題適合做醫(yī)學影像算法、檢驗科自動化和醫(yī)療AI落地的人。反直覺的地方在于這類數(shù)據(jù)集里模型結(jié)構(gòu)反而不是瓶頸密集的小目標和極度失衡的類別分布才是真正決定你能不能跑通的因素。2. 解壓血細胞檢測數(shù)據(jù)集之前先搞清楚格式、規(guī)模和標注可信度拿到血細胞檢測數(shù)據(jù)集.zip之后我建議你先別急著 unzip 然后直接訓(xùn)練。這個任務(wù)和你平時接觸的車輛檢測、行人檢測差別很大血涂片里單個目標很小類別分布極度不均衡標注是否可靠直接決定后面所有工作的價值。我見過不少團隊在數(shù)據(jù)集上跑通了 YOLOv8卻在換一個染色批次后全線翻車問題大多出在數(shù)據(jù)準備階段。這一章先講清楚怎么判斷一個血細胞數(shù)據(jù)集能不能用、用什么姿勢解壓、解壓之后應(yīng)該先檢查什么。2.1 血細胞檢測為什么不能拿通用數(shù)據(jù)集直接練血細胞檢測的典型輸入是血涂片在顯微鏡高倍鏡下的圖像一個視野里幾十到幾百個細胞紅細胞呈雙凹圓盤狀白細胞帶核血小板尺寸更小。目標檢測模型本身不挑領(lǐng)域COCO 預(yù)訓(xùn)練權(quán)重也能遷移過來但關(guān)鍵差異在目標和分布。COCO 里最常見的 person 占幾百像素而血涂片單個紅細胞在 4000×3000 原圖里可能只有 30×30 像素血小板更小常常只有十幾個像素。直接套用通用目標檢測流程第一輪訓(xùn)練出來的模型幾乎只會把大片紅色區(qū)域當成紅細胞對白細胞則完全無視。另一個問題是類別比例。血液中白細胞、紅細胞、血小板的數(shù)量相差幾個數(shù)量級反映到標注文件里可能就是 RBC 有幾千個框WBC 只有二十幾個框。如果你不干預(yù)采樣模型在損失函數(shù)里就會被紅細胞統(tǒng)治WBC 的召回率趨近于 0。這也是為什么拿到血細胞檢測數(shù)據(jù)集.zip后最先做的是統(tǒng)計類別分布而不是馬上訓(xùn)練。我把這類檢查叫“數(shù)據(jù)體檢”30 秒就能發(fā)現(xiàn)后面可能翻車的地方。在 BDD100K 這類駕駛數(shù)據(jù)集上成立的訓(xùn)練流程到了血細胞檢測上往往要改的不只是路徑還有整套采樣策略。2.2 一個血細胞數(shù)據(jù)集 zip 解壓后通常有什么常見做法是解壓到獨立目錄我一般先把壓縮包重命名成英文小寫路徑避免后面 Shell 和 Python 在處理中文路徑時出問題。如果你接觸過 COCO2017 數(shù)據(jù)集的結(jié)構(gòu)會對 images 與 annotations 的分離模式很熟悉但血細胞檢測數(shù)據(jù)集更常見的是 VOC 風格少量是 YOLO 風格或 COCO 風格。解壓命令很簡單# Linux 下解壓血細胞檢測數(shù)據(jù)集.zip-d 指定解壓目錄 unzip blood_cell_dataset.zip -d blood_cell_dataset # 如果解壓出來的文件名是亂碼說明壓縮包在 Windows 下用 GBK 編碼打包 unzip -O CP936 blood_cell_dataset.zip -d blood_cell_dataset解壓之后目錄結(jié)構(gòu)通常是這幾種之一。VOC 風格有 JPEGImages、Annotations、ImageSetsCOCO 風格只有 images 和一個 annotations.jsonYOLO 風格已經(jīng)處理好 images 和 labels。血細胞數(shù)據(jù)集中用 LabelImg 標注的 VOC 風格最常見因為標注工具默認就是導(dǎo)出 XML。三種格式各有特點直接影響后續(xù)轉(zhuǎn)換腳本怎么寫。格式文件組織常見配套文件你需要注意的地方VOC XML一張圖對應(yīng)一個 XMLImageSets/Main/train.txt、val.txt檢查是否有缺失 XML 或多余 XMLCOCO JSON一個 JSON 索引所有圖instances_train.json、instances_val.json確認類別 id 是否連續(xù)YOLO txt一張圖對應(yīng)一個 txtimages/ 與 labels/ 同名檢查是否存在沒標注的圖如果你看到 ImageSets/Main 里有 train.txt 和 val.txt說明作者已經(jīng)幫你劃分過數(shù)據(jù)集如果沒有就得自己做劃分。VOC 的每個 XML 獨立描述一張圖腳本處理起來方便但也更容易出現(xiàn)標注缺失和格式不一致所以解壓后我第一件事是檢查是不是每個 XML 都有對應(yīng)的圖片。這一步不需要訓(xùn)練寫個小循環(huán)掃一遍就行。2.3 跑一個體檢腳本30 秒知道數(shù)據(jù)集能不能用這里給一個我常用的體檢腳本只讀 XML不做任何修改。你要改的只有路徑變量# inspect_blood_cell.py # 不要急著訓(xùn)練先統(tǒng)計類別數(shù)、框數(shù)和圖像尺寸 import glob import xml.etree.ElementTree as ET from collections import Counter annotations glob.glob(annotations/*.xml) class_counter Counter() box_count 0 image_sizes [] for ann in annotations: tree ET.parse(ann) root tree.getroot() # 部分數(shù)據(jù)集的 XML 里沒有 size 節(jié)點這里做一層保護 if root.find(size) is not None: w int(root.findtext(size/width)) h int(root.findtext(size/height)) image_sizes.append((w, h)) for obj in root.iter(object): name obj.findtext(name) if name is not None: class_counter[name] 1 box_count 1 print(樣本圖像數(shù):, len(annotations)) print(標注框總數(shù):, box_count) print(類別分布:, dict(class_counter)) if image_sizes: widths [s[0] for s in image_sizes] heights [s[1] for s in image_sizes] print(圖像寬度范圍:, min(widths), -, max(widths)) print(圖像高度范圍:, min(heights), -, max(heights))這段腳本輸出三件事樣本圖像數(shù)、標注框總數(shù)、各類別數(shù)量。如果某類別占比低于 1%后面訓(xùn)練時必須做采樣干預(yù)。如果圖像寬度從 640 到 4000 都覆蓋說明數(shù)據(jù)集來自不同采集設(shè)備訓(xùn)練時要考慮尺度變化。如果你發(fā)現(xiàn)統(tǒng)計出的類別名稱不統(tǒng)一比如 WBC、Leukocyte、白細胞混著出現(xiàn)先合并再訓(xùn)練否則模型會把這些都當成不同類別導(dǎo)致同一類細胞被拆成多類訓(xùn)練和驗證都會亂掉。2.4 類別命名不統(tǒng)一先合并再訓(xùn)練我遇到過標注里 WBC 寫成 WbC、RBC 寫成 Red Blood Cell 的情況直接在轉(zhuǎn)換腳本里加一層歸一化即可。常見做法是維護一個別名表把每個 object 的 name 先映射到標準類別再映射到類別 ID。這樣即使數(shù)據(jù)集內(nèi)部標注不一致訓(xùn)練時也不會憑空多出幾個類別。和 CCPD 車牌數(shù)據(jù)集、HRSC2016 遙感艦船數(shù)據(jù)集一樣領(lǐng)域數(shù)據(jù)集的標注字典經(jīng)常帶著作者的個人習慣不能拿到手就當標準。類別名合并這件事越早做越好晚了等你訓(xùn)練完看結(jié)果再回頭改標簽等于重來一遍。3. 把血細胞數(shù)據(jù)集轉(zhuǎn)成 YOLO 格式并跑起來轉(zhuǎn)換腳本、YAML 和訓(xùn)練參數(shù)的取舍血細胞檢測數(shù)據(jù)集.zip 里的原始標注無論是什么格式最終喂給 YOLO 系列模型的都是歸一化坐標的 txt 文件。用 YOLOv8 訓(xùn)練自己的數(shù)據(jù)集最??ㄗ〉牟皇悄P徒Y(jié)構(gòu)而是格式轉(zhuǎn)換和路徑配置。這一章給出從 VOC XML 到 YOLO txt 的轉(zhuǎn)換腳本再講訓(xùn)練配置文件怎么寫最后給出血細胞場景下幾個關(guān)鍵訓(xùn)練參數(shù)的取舍。3.1 VOC XML 轉(zhuǎn) YOLO txt轉(zhuǎn)換腳本與越界處理YOLO 格式每行五個值類別 ID、歸一化中心點 x、歸一化中心點 y、歸一化寬 w、歸一化高 h。轉(zhuǎn)換的核心是把像素坐標除以原圖寬高。聽起來簡單但血細胞標注里經(jīng)常出現(xiàn)框超出圖像邊界的情況細胞被涂片邊緣截斷時標注工具的矩形框甚至會延伸到圖外。下面的腳本做了 clamp 處理# convert_voc_to_yolo.py # 把 VOC XML 轉(zhuǎn)成 YOLO txt并在寫文件前把越界框裁剪回圖像范圍 import glob import os import xml.etree.ElementTree as ET # 類別映射必須和之后 blood_cell.yaml 里的 names 保持一致 CLASS_MAP {WBC: 0, RBC: 1, Platelets: 2} def convert(xml_dirannotations, out_dirlabels): os.makedirs(out_dir, exist_okTrue) for xml_file in sorted(glob.glob(os.path.join(xml_dir, *.xml))): root ET.parse(xml_file).getroot() w int(root.findtext(size/width)) h int(root.findtext(size/height)) stem os.path.splitext(os.path.basename(xml_file))[0] lines [] for obj in root.iter(object): cls_name obj.findtext(name) if cls_name not in CLASS_MAP: print(f跳過未知類別: {cls_name} {stem}) continue box obj.find(bndbox) x1 float(box.findtext(xmin)) y1 float(box.findtext(ymin)) x2 float(box.findtext(xmax)) y2 float(box.findtext(ymax)) # 先把坐標限制到圖片范圍內(nèi)防止 YOLO 訓(xùn)練時報標簽越界 x1 max(0, min(x1, w)) y1 max(0, min(y1, h)) x2 max(0, min(x2, w)) y2 max(0, min(y2, h)) # 再計算歸一化的中心點和寬高保留 6 位小數(shù) cx (x1 x2) / 2.0 / w cy (y1 y2) / 2.0 / h bw (x2 - x1) / w bh (y2 - y1) / h lines.append( f{CLASS_MAP[cls_name]} {cx:.6f} {cy:.6f} f{bw:.6f} {bh:.6f}\n ) with open(os.path.join(out_dir, stem .txt), w) as f: f.writelines(lines) if __name__ __main__: convert()這里有一個參數(shù)需要根據(jù)你的數(shù)據(jù)集改CLASS_MAP。統(tǒng)計腳本輸出的類別名稱是什么就映射什么。如果統(tǒng)計里有 Three 大類但你想合并成 WBC、RBC、Platelets 三類就在這一層做收斂。腳本跳過未知類別是為了避免訓(xùn)練時類別 ID 對不上導(dǎo)致 loss 異常。還有一點要說明如果數(shù)據(jù)集是 COCO JSON 格式你需要先解析 JSON 而不是 XML常見做法是先把 JSON 轉(zhuǎn)成 VOC 再跑這個腳本或者直接用 json 庫解析后生成 txt邏輯一樣只是數(shù)據(jù)源換了。越界 clamp 是容易忽略的坑。你以為加了 clamp 就安全了但 clamp 之后如果框?qū)捀咦兂?0比如 x1 和 x2 都被 clamp 到同一個值這行標注就廢了。我在后續(xù)巡檢里會再加一層過濾把寬高小于 1 像素的框直接丟掉后面章節(jié)會講。3.2 劃分數(shù)據(jù)集并編寫 blood_cell.yaml轉(zhuǎn)換完標注文件后需要把圖片和對應(yīng)的 txt 按比例分成訓(xùn)練集和驗證集。血細胞數(shù)據(jù)集規(guī)模不大用 shell 腳本就能完成。以下命令在 blood_cell_dataset 目錄下執(zhí)行# 先把所有圖片路徑打亂再按 8:2 劃分訓(xùn)練集和驗證集 find images -name *.jpg | shuf all_files.txt total$(wc -l all_files.txt) head -n $((total * 8 / 10)) all_files.txt | while read img; do stem$(basename $img .jpg) mv $img images/train/ mv labels/$stem.txt labels/train/ done # 剩余圖片進入驗證集 tail -n $((total * 8 / 10 1)) all_files.txt | while read img; do stem$(basename $img .jpg) mv $img images/val/ mv labels/$stem.txt labels/val/ done這種 shell 方式適合百張以內(nèi)的數(shù)據(jù)集幾分鐘能搞定。如果數(shù)據(jù)集上千張建議用 Python 腳本做分層劃分先把按類別分布排序確保驗證集里每一種細胞都有一定樣本否則驗證集的 mAP 會失真。劃分完成后在項目根目錄寫一個 blood_cell.yaml# blood_cell.yaml # train 和 val 指向圖片目錄YOLO 會在同目錄下找同名 txt train: /data/blood_cell_dataset/images/train val: /data/blood_cell_dataset/images/val nc: 3 names: [WBC, RBC, Platelets]這個配置文件的關(guān)鍵是 names 的順序和 CLASS_MAP 里類別 ID 必須一致。如果 WBC 在 CLASS_MAP 里是 0在 yaml 里也必須排第 0 位否則模型訓(xùn)練時會把白細胞學成紅細胞。路徑建議用絕對路徑省得每次訓(xùn)練前 cd 半天。如果你用的數(shù)據(jù)集本來就有 ImageSets/Main/train.txt也可以直接讀取那個列表去復(fù)制文件不用重新 find。3.3 血細胞訓(xùn)練的參數(shù)經(jīng)驗值準備好數(shù)據(jù)和配置文件后訓(xùn)練命令本身不復(fù)雜# 安裝 ultralytics 運行環(huán)境 pip install ultralytics # 開始訓(xùn)練參數(shù)順序不影響YOLO CLI 會解析 yolo detect train \ datablood_cell.yaml \ modelyolov8n.pt \ imgsz640 \ epochs100 \ batch16 \ patience20 \ ampFalse參數(shù)上我一般會針對血細胞場景做幾個調(diào)整。imgsz640 是速度優(yōu)先但如果原圖是 2000 像素以上640 下單個白細胞可能只剩不到 20 像素模型很難學到細節(jié)。建議第一輪用 640 跑通流程看 mAP 值再決定要不要上 1280。batch 大小完全看顯存16 在 12G 左右卡上比較安全顯存不夠就降到 8。ampFalse 是我在醫(yī)學影像上的習慣部分血細胞數(shù)據(jù)集在混合精度下會出現(xiàn) loss 變成 NaN 的情況關(guān)掉省心。epochs 和 patience 也要看數(shù)據(jù)規(guī)模。血細胞數(shù)據(jù)集的樣本數(shù)少模型可能到第 40 個 epoch 還在漲patience20 意味著 20 個 epoch 不漲就早??赡芴崆扒袛嘤?xùn)練。我會把 patience 放到 30 到 50讓它多跑一會兒。如果你發(fā)現(xiàn)訓(xùn)練曲線一直在抖先把驗證集確認一遍而不是急著調(diào)學習率。訓(xùn)練完成后權(quán)重在 runs/detect/train 目錄下best.pt 是按驗證集 mAP 保存的最佳權(quán)重后續(xù)推理都用它。4. 訓(xùn)練前的標注質(zhì)量巡檢框不靠譜mAP 就是自欺欺人血細胞檢測數(shù)據(jù)集.zip 里的標注不一定是高質(zhì)量的。很多開源數(shù)據(jù)集的標注來自半自動標注工具紅細胞密密麻麻時標注員會漏點白細胞偶爾標錯類別血小板經(jīng)常畫出一個過大的框把周圍紅細胞也包進去。這些問題如果不處理訓(xùn)練出來的模型 mAP 會虛高換一張真實涂片就露餡。這一章講怎么在訓(xùn)練前把標注質(zhì)量查一遍以及怎么處理類別不平衡和增強的邊界。4.1 把標注畫到圖上最笨但最有效的檢查方式判斷標注質(zhì)量最簡單的方法是把標注框畫回原圖肉眼掃一遍。血細胞框數(shù)量大不需要一張張全看隨機抽 30 到 50 張重點看白細胞和血小板的框。下面的腳本會把訓(xùn)練集里的每張圖和它的 YOLO txt 標注重疊輸出到 vis 目錄# draw_boxes.py import os import glob import cv2 def draw_label(img_path, label_path, out_dirvis): os.makedirs(out_dir, exist_okTrue) img cv2.imread(img_path) if img is None: print(f圖片讀不到: {img_path}) return if not os.path.exists(label_path): print(f漏標文件: {img_path}) return h, w img.shape[:2] with open(label_path) as f: for line in f: parts line.strip().split() if len(parts) ! 5: print(f格式異常: {label_path}) continue cls, cx, cy, bw, bh map(float, parts) # YOLO txt 是歸一化坐標還原時要乘回原圖寬高 x1 int((cx - bw / 2) * w) y1 int((cy - bh / 2) * h) x2 int((cx bw / 2) * w) y2 int((cy bh / 2) * h) color (0, 0, 255) if cls 0 else (0, 255, 0) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.imwrite(os.path.join(out_dir, os.path.basename(img_path)), img) for img_path in glob.glob(images/train/*.jpg): label_path labels/train/ os.path.basename(img_path).replace(.jpg, .txt) draw_label(img_path, label_path)這段腳本里有個細節(jié)容易出錯還原框坐標時用的是原圖寬高不是訓(xùn)練時的 imgsz。如果你把圖 resize 到 640 再還原坐標畫出來的框和細胞位置是對不上的。另外腳本會打印兩類問題讀不到的圖片和缺失的標注文件。讀不到的圖片說明圖片路徑和標注文件名對不上常見原因是圖片是 png 而腳本在找 jpg或者文件名里有多余空格。缺失標注文件說明這張圖被劃分進了訓(xùn)練集但作者沒標訓(xùn)練時 YOLO 會跳過它但會影響訓(xùn)練集規(guī)模和類別分布。提示巡檢的目的不是把每一張圖都畫出來看而是抽出最可能出現(xiàn)問題的類別重點看。血細胞數(shù)據(jù)集里紅細胞框偏大、血小板框重疊是最常見的兩類問題。4.2 類別不平衡當白細胞只有 20 個框統(tǒng)計腳本如果告訴你 WBC 占比不到 1%你有兩個選擇一是換 dataset二是做采樣干預(yù)。真實項目中能換數(shù)據(jù)集的場景很少大多數(shù)人只能硬著頭皮做。我一般會先做一次過采樣把白細胞樣本所在圖片在訓(xùn)練列表里重復(fù) 3 到 5 遍讓模型在每個 epoch 里有更多機會看到白細胞。訓(xùn)練列表就是 images/train 目錄下的文件你可以人工把 WBC 樣本多的圖片復(fù)制到另一個臨時目錄再軟鏈接回訓(xùn)練集也可以直接在數(shù)據(jù)增強配置里動手腳。更好的方式是做細胞級裁剪。既然白細胞框那么少干脆把每個標注框裁出來縮放到固定尺寸單獨做分類或檢測。這種方式適合那種“檢測少、分類難”的場景比如白細胞五分類你先用檢測網(wǎng)絡(luò)把所有有核細胞找出來再做分類。缺點是脫離了檢測任務(wù)的端到端流程但實際效果往往比硬訓(xùn)練一個大模型好。另一個思路是負采樣。紅細胞框太多模型訓(xùn)練時正負樣本比例嚴重失衡可以隨機丟棄一部分紅細胞框。但 YOLO 的 txt 是按圖組織的你沒法只刪一部分框而不影響同一張圖里的其他類別。所以常見的做法是挑出“只有紅細胞、沒有白細胞/血小板”的圖直接從訓(xùn)練集里挪出去一部分。這個操作要小心如果挪得太多模型對紅細胞的泛化能力也會下降。我一般先把紅細胞占比壓到 70% 以下再看 WBC 和 Platelets 的 AP 有沒有起來。4.3 數(shù)據(jù)增強的邊界染色是醫(yī)學圖像的敏感信號血涂片里有大量臨床信息藏在顏色里染料的濃度、染色時間、顯微鏡光源色溫都會讓同一類細胞呈現(xiàn)不同顏色。你想用數(shù)據(jù)增強提升泛化能力但用力過猛會讓模型學到不真實顏色導(dǎo)致?lián)Q一臺顯微鏡就翻車。YOLOv8 的數(shù)據(jù)增強可以在 yaml 里覆蓋默認參數(shù)血細胞場景我通常這樣調(diào)# blood_cell_aug.yaml # 在訓(xùn)練時通過超參數(shù)覆蓋默認增強 mosaic: 0.5 hsv_h: 0.02 hsv_s: 0.3 hsv_v: 0.3 degrees: 10 translate: 0.1 scale: 0.3mosaic 從默認的 1.0 降到 0.5是因為 mosaic 會把四張圖拼在一起細胞會被拼接縫截斷血細胞這種密集小目標對象容易學到半截特征。hsv_h 只給 0.02色調(diào)不要亂動飽和度和亮度可以稍大一點模擬不同染色深淺。degrees10 表示最多旋轉(zhuǎn) 10 度血細胞本身是圓的旋轉(zhuǎn)多一點問題不大但對方向敏感的細胞形態(tài)如血小板聚集會有影響。translate 和 scale 保持適中保證細胞不會被裁掉太多。如果你發(fā)現(xiàn)模型在驗證集上 mAP 不錯但換一個實驗室的數(shù)據(jù)集就掉點優(yōu)先檢查顏色增強是否過硬。有個土辦法把訓(xùn)練時的增強可視化出來看增強后的圖是不是已經(jīng)偏綠偏紫。如果肉眼都看出來顏色不對勁模型也不可能學到靠譜的顏色特征。醫(yī)學圖像的數(shù)據(jù)增強邊界在于“不能改變診斷特征”你增強出來的圖要能讓一個檢驗科醫(yī)生認可它仍然是正常的血涂片。5. 血細胞檢測訓(xùn)練與推理的常見問題排查這一章把我在血細胞檢測訓(xùn)練和推理里踩過的一些問題列出來每條都按現(xiàn)象到原因到解決辦法來講。這些問題不會同時出現(xiàn)但如果你照著前面的步驟做還翻車先來這里對對癥狀。5.1 解壓后文件名亂碼和中文路徑導(dǎo)致訓(xùn)練中斷現(xiàn)象解壓血細胞檢測數(shù)據(jù)集.zip 后文件列表里是一串亂碼訓(xùn)練時提示 FileNotFoundError 或 NotADirectoryError圖片讀不到標注文件也對不上。原因zip 包在 Windows 下打包時使用 GBK 編碼Linux 下 unzip 默認按 UTF-8 解壓中文文件名和目錄名就會變成亂碼。另一個隱性問題是路徑本身含中文Python 的 glob 和 OpenCV 的 imread 在部分系統(tǒng)上對中文路徑兼容不好報錯還特別隱晦。解決Linux 下用unzip -O CP936 blood_cell_dataset.zip -d blood_cell_dataset指定編碼解壓macOS 的 unzip 可能不帶 -O 參數(shù)可以用 Python 的 zipfile 庫在解壓時手動用 errorsignore 重命名。解壓后我習慣先把目錄重命名為英文mv 血細胞檢測數(shù)據(jù)集 blood_cell_dataset。所有后續(xù)腳本和訓(xùn)練命令都不要出現(xiàn)中文路徑這是給自己省時間。5.2 白細胞類別 AP 一直是 0現(xiàn)象訓(xùn)練跑完驗證集上 RBC 和 Platelets 的 mAP 都有 0.8 以上WBC 的 AP 始終是 0.000預(yù)測結(jié)果里一張白細胞都沒框出來。原因不是模型壞了而是白細胞在訓(xùn)練數(shù)據(jù)里占比太低。一個典型血涂片視野里紅細胞上萬白細胞只有幾個標注框數(shù)量相差幾個數(shù)量級。模型從隨機權(quán)重開始訓(xùn)練時幾乎每個 batch 里都沒有白細胞樣本梯度完全被紅細胞主導(dǎo)。解決先跑第 2 章的統(tǒng)計腳本確認 WBC 框數(shù)量。如果確實不到 1%做過采樣把包含 WBC 的圖片復(fù)制到訓(xùn)練列表 3 到 5 遍或者把這些圖片單獨放進一個目錄在 yaml 里多加一個數(shù)據(jù)源。還有一種做法是降低紅細胞樣本比例把只有紅細胞的圖片從訓(xùn)練集里挑出一部分挪走讓 WBC 的相對占比從 0.5% 提高到 5% 左右。訓(xùn)練完再看 WBC 的 AP如果還是 0檢查類別 ID 是否映射錯了。5.3 驗證集 mAP 高但實際場景翻車現(xiàn)象在數(shù)據(jù)集劃分的驗證集上 mAP0.5 到 0.97拿一張其他實驗室采的血涂片來測白細胞召回率直接掉一半邊界框松松垮垮。原因這是典型的域偏移。同一個數(shù)據(jù)集里的訓(xùn)練圖和驗證圖來自同一臺顯微鏡、同一個染色批次模型學到的是那套顏色和光照分布。血涂片染色深淺、顯微鏡白平衡、相機型號一變模型就懵了。這不是過擬合是數(shù)據(jù)本身就太單一。解決訓(xùn)練前把一部分來自不同條件下的圖像單獨留出來做測試集不要參與劃分。如果數(shù)據(jù)集里沒有外部圖像就在訓(xùn)練時故意增強亮度飽和度的擾動模擬不同染色條件。更專業(yè)的做法是做染色歸一化比如用 Reinhard 方法把每張圖的顏色分布對齊到標準涂片在訓(xùn)練前預(yù)處理一遍。這個操作對血細胞檢測的泛化提升非常明顯但要注意別在驗證集上也用訓(xùn)練集的統(tǒng)計參數(shù)來歸一化否則數(shù)據(jù)泄漏。5.4 Loss 為 NaN 或 CUDA OOM現(xiàn)象訓(xùn)練到第幾個 epoch 時 loss 突然變成 nan命令行直接報錯或者剛開始訓(xùn)練就提示 CUDA out of memorybatch 怎么降都不太對。原因loss 變成 nan 最常見的是三個來源一是訓(xùn)練數(shù)據(jù)里有坐標越界的標注轉(zhuǎn)換時沒 clamp歸一化后出現(xiàn)負值或極大值二是混合精度訓(xùn)練下某些算子的梯度異常尤其在醫(yī)學圖像的半精度情況下更容易出現(xiàn)三是學習率太大優(yōu)化器發(fā)散。CUDA OOM 則經(jīng)常和高分辨率 imgsz 疊加了大 batch 有關(guān)血細胞數(shù)據(jù)集如果你想用 imgsz1280batch16 在 24G 卡上都會爆。解決先用第 4 章的巡檢腳本把訓(xùn)練集里的越界框全部過濾掉確保 txt 里所有坐標都在 [0,1] 區(qū)間。然后在訓(xùn)練命令里加 ampFalse再不行把學習率從默認 0.01 降到 0.001在 yaml 里覆蓋 lr0。OOM 的解決不是一味降 batch可以先降 batch 到 8 或 4如果還爆再把 imgsz 從 1280 降到 960 或 640。血細胞的檢測精度和分辨率相關(guān)但模型訓(xùn)練不出來更虧。6. 用切片推理處理高分辨率血涂片一個讓 mAP 變實用的技巧前面訓(xùn)練時你可能為了顯存把 imgsz 設(shè)成了 640模型在驗證集上表現(xiàn)不錯但拿去推理一張 4000×3000 的血涂片直接把整圖 resize 成 640 再預(yù)測很多小細胞會消失。這里有一個我習慣用的技巧切片推理把大圖切成 640×640 的小窗逐個預(yù)測再把框映射回原圖坐標。# 高分辨率血涂片切片推理stride 小于 window 保證窗口有重疊 import cv2 from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) img cv2.imread(full_slide.jpg) H, W img.shape[:2] window, stride 640, 512 all_boxes [] for y in range(0, H, stride): for x in range(0, W, stride): crop img[y:ywindow, x:xwindow] results model(crop, conf0.25, imgsz640, verboseFalse) for box in results[0].boxes.data.tolist(): x1, y1, x2, y2, conf, cls box # 小窗坐標加上窗口偏移量映射回原圖坐標 all_boxes.append((x1 x, y1 y, x2 x, y2 y, conf, cls))這段腳本的思路很簡單滑動窗口推理重疊部分用后續(xù) NMS 合并。stride512 小于 window640同一個細胞會在相鄰窗口里出現(xiàn)兩次預(yù)測出來的兩個框位置相同需要做一次 NMS 去重。直接用 cv2.dnn.NMSBoxes 處理 all_boxes 就行或者換成 SAHI 庫它把切片、后處理都封裝好了支持 YOLOv8 接口。切片推理的關(guān)鍵參數(shù)是 window 和 stridewindow 越大單窗看到的上下文越多但也不能超過模型訓(xùn)練時的 imgsz 太多stride 越小重疊越多檢測越穩(wěn)但耗時越長。我一般先用 window640、stride512 起看漏檢情況再把 stride 降到 384。我最早做血細胞檢測時直接把 4000 像素的涂片 resize 到 640 去推理結(jié)果一個白細胞都找不到。后來翻車翻多了才意識到不是模型不行是目標在 resize 過程中被抹掉了。從那以后凡是單邊超過 1500 像素的醫(yī)學圖像我都默認走切片推理寧可多花幾秒也不再迷信強制縮放。希望這個習慣能幫到你。本文還有配套的精品資源點擊獲取