:從訓練到SAHI切片推理)
簡介這份資源面向計算機相關(guān)專業(yè)的畢業(yè)設(shè)計學生、課程設(shè)計及期末大作業(yè)需求者提供基于UNet與UNet兩種網(wǎng)絡(luò)結(jié)構(gòu)實現(xiàn)細胞圖像醫(yī)學圖像分割的完整Python源碼。項目經(jīng)導師指導并獲評審99分認可代碼完整可運行適合作為項目實戰(zhàn)練習或小白入門參考。壓縮包共48個文件以44個Python腳本為核心涵蓋模型定義、訓練、預(yù)測、評估與數(shù)據(jù)處理等模塊另含Dockerfile、requirements.txt、readme.md及.gitignore等輔助文件整體約95KB結(jié)構(gòu)清晰便于按模塊查閱。目前已有181人學習下載。讀者可從中獲取UNet與UNet的模型搭建細節(jié)、Dice評分與數(shù)據(jù)加載實現(xiàn)、切片預(yù)測與后處理流程以及完整的訓練與推理腳本幫助快速理解醫(yī)學圖像分割項目的工程組織方式并在此基礎(chǔ)上完成自己的畢設(shè)或課程設(shè)計任務(wù)。1. 從一張細胞切片說起這套 UNet/UNet 源碼到底能跑出什么細胞圖像分割這件事真正上手過的人都知道難點從來不是把網(wǎng)絡(luò)搭出來而是讓模型在邊界粘連、細胞核重疊、染色不均的圖上還能把每個實例分開。這份基于 UNet 和 UNet 的細胞圖像醫(yī)學圖像分割 Python 源碼核心就是解決這個問題它把語義分割的經(jīng)典雙 backbone 實現(xiàn)、SAHI 切片推理、Dice 評估、數(shù)據(jù)加載和訓練腳本打包在一起拿到手就能跑通從訓練到預(yù)測的完整鏈路。適合正在做計算機相關(guān)專業(yè)畢設(shè)、課程設(shè)計、期末大作業(yè)的學生也適合想拿醫(yī)學圖像分割練手語義分割實戰(zhàn)的從業(yè)者。目錄里unet/、sahi/、scripts/、utils/四個模塊分工明確train.py、predict.py、evaluate.py、slicePredict.py四個入口覆蓋了訓練、整圖預(yù)測、指標評估和切片預(yù)測不是那種只丟一個模型文件讓你自己拼的殘缺包。我拆這類源碼包有個習慣先看入口腳本數(shù)量再看依賴文件。這份有requirements.txt和Dockerfile說明作者考慮過環(huán)境復(fù)現(xiàn)不是本地跑通就完事。下面按「先搞懂結(jié)構(gòu) → 再跑通訓練 → 再處理大圖推理 → 再避坑 → 最后調(diào)優(yōu)」的順序拆每一步都落到能抄的命令和參數(shù)上。2. 拆開源碼包UNet 與 UNet 的模塊分工和選型邏輯2.1 為什么同時保留 UNet 和 UNet 兩條路UNet 的結(jié)構(gòu)是編碼器-解碼器加跳躍連接編碼器逐層下采樣提特征解碼器逐層上采樣恢復(fù)分辨率跳躍連接把淺層的高分辨率細節(jié)直接送到對應(yīng)解碼層。這個設(shè)計在細胞圖像上特別管用因為細胞邊界屬于高頻信息下采樣幾次就容易丟跳躍連接相當于給解碼器留了一條「后悔藥」通道。UNet 在 UNet 基礎(chǔ)上做了嵌套密集跳躍連接把原來一條直的跳躍連接改成多層嵌套的密集塊中間還加了深監(jiān)督。直白說UNet 讓解碼器每一層都能同時看到不同深度的編碼特征對細胞核重疊、邊界模糊的圖分割精度通常比原版 UNet 高一截代價是參數(shù)量和顯存占用上去了。這份源碼把兩者都留著實際是個很務(wù)實的選擇小數(shù)據(jù)集、顯存緊張時用 UNet 快速驗證追求指標、顯存夠用時切 UNet。unet/unet_model.py里通常用參數(shù)控制走哪條分支unet/unet_parts.py放的是 DoubleConv、Down、Up、OutConv 這些基礎(chǔ)塊unet/__init__.py負責對外暴露模型類。你換模型不用改訓練腳本只改一個配置項這是這套代碼結(jié)構(gòu)最舒服的地方。2.2 目錄結(jié)構(gòu)與各文件職責把包解開后核心文件大致是這樣分工的路徑職責unet/unet_model.pyUNet / UNet 主體網(wǎng)絡(luò)定義unet/unet_parts.py卷積塊、下采樣、上采樣等基礎(chǔ)組件utils/data_loading.py數(shù)據(jù)集類負責讀圖、配對 mask、增強utils/dice_score.pyDice 系數(shù)計算評估核心指標utils/dataprocess.py數(shù)據(jù)預(yù)處理切片、歸一化等train.py訓練入口含優(yōu)化器、學習率、checkpointpredict.py整圖推理入口slicePredict.py大圖切片推理入口evaluate.py加載權(quán)重算 Dice 等指標sahi/切片推理與后處理處理超大分辨率圖scripts/輔助腳本如標注轉(zhuǎn)換、批量處理sahi/這個目錄值得單獨說。SAHI 是 Slicing Aided Hyper Inference 的思路把一張超大圖切成帶重疊的小塊分別推理再把結(jié)果拼回去。醫(yī)學細胞圖像動輒幾千乘幾千像素直接整圖塞進網(wǎng)絡(luò)顯存扛不住切片推理是常見做法。sahi/slicing.py管切sahi/postprocess管拼sahi/predict.py管調(diào)度sahi/utils和sahi/model.py提供底層支持。這套組合不是隨便堆的是針對大圖場景專門設(shè)計的。2.3 環(huán)境準備與依賴安裝先確認 Python 版本這類分割項目一般吃 PyTorch建議 3.8 到 3.10。裝依賴前先看requirements.txt別直接pip install -r一把梭先掃一眼有沒有版本釘死。# 建獨立環(huán)境避免污染全局 conda create -n cellseg python3.9 -y conda activate cellseg # 先裝 PyTorch版本按自己 CUDA 來這里給的是常見組合 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 再裝項目其余依賴 pip install -r requirements.txt邏輯說明PyTorch 單獨裝是因為requirements.txt里往往只寫torch不寫 CUDA 版本直接裝可能拉到 CPU 版訓練慢到懷疑人生。參數(shù)上cu118對應(yīng) CUDA 11.8你要按nvidia-smi顯示的驅(qū)動支持版本改。裝完跑一句python -c import torch; print(torch.cuda.is_available())返回 True 才算環(huán)境對了。如果不想折騰環(huán)境Dockerfile是另一條路docker build -t cellseg:latest . docker run --gpus all -it -v $(pwd):/workspace cellseg:latest bash--gpus all把 GPU 透進容器-v把當前目錄掛進去這樣訓練產(chǎn)物落在宿主機上容器刪了也不丟。常見坑是宿主機沒裝 nvidia-container-toolkit--gpus會報錯這個后面避坑章節(jié)細說。3. 跑通訓練與評估從數(shù)據(jù)加載到 Dice 指標的完整鏈路3.1 數(shù)據(jù)組織與 data_loading 的讀取約定訓練能不能跑起來八成卡在數(shù)據(jù)格式上。utils/data_loading.py里的 Dataset 類一般約定圖像和 mask 分目錄存放文件名一一對應(yīng)。典型結(jié)構(gòu)是data/ images/ cell_001.png cell_002.png masks/ cell_001.png cell_002.pngmask 通常是單通道前景為 1 或 255背景為 0。讀的時候要確認data_loading.py里用的是Image.open(...).convert(L)還是直接讀 RGB如果 mask 被當三通道讀進來后面算 loss 維度就對不上。我一般先寫個幾行的小腳本驗證配對import os from PIL import Image img_dir, mask_dir data/images, data/masks imgs sorted(os.listdir(img_dir)) for name in imgs[:5]: img Image.open(os.path.join(img_dir, name)) mask Image.open(os.path.join(mask_dir, name)).convert(L) print(name, img.size, mask.size, mask.getextrema())邏輯說明getextrema()返回 mask 的像素值范圍正常應(yīng)該是(0, 255)或(0, 1)。如果返回(0, 0)說明 mask 全黑標簽有問題如果圖像和 mask 尺寸不一致訓練時 resize 會引入錯位必須提前對齊。這一步花兩分鐘能省掉后面幾小時的 debug。3.2 訓練腳本的關(guān)鍵參數(shù)train.py是主入口跑之前先看清楚它接受哪些參數(shù)。常見做法是 argparse 暴露學習率、batch size、epoch、模型類型、數(shù)據(jù)路徑。python train.py \ --data_dir ./data \ --model unetpp \ --epochs 100 \ --batch_size 8 \ --learning_rate 1e-4 \ --val_percent 10 \ --save_dir ./checkpoints參數(shù)說明--model在unet和unetpp之間切對應(yīng)第 2 章說的兩條分支--batch_size受顯存限制8 是 8G 顯存的保守值顯存夠可以往上加--learning_rate用 1e-4 是分割任務(wù)的常見起點太大 loss 震蕩太小收斂慢--val_percent劃出驗證集比例用來算 Dice 監(jiān)控過擬合。訓練過程中重點盯兩個信號訓練 loss 持續(xù)下降但驗證 Dice 不漲說明過擬合該加增強或減模型復(fù)雜度loss 直接不降先查學習率和數(shù)據(jù)歸一化。3.3 Dice 評估與 evaluate.py 的用法分割任務(wù)光看 loss 不夠Dice 系數(shù)才是能寫進報告里的指標。utils/dice_score.py一般實現(xiàn)的是 soft Dice公式是2 * |X ∩ Y| / (|X| |Y|)對預(yù)測概率和標簽逐像素算。evaluate.py加載訓練好的權(quán)重在驗證集或測試集上跑一遍python evaluate.py \ --model ./checkpoints/best.pth \ --model_type unetpp \ --data_dir ./data \ --output_dir ./eval_results邏輯說明--model指向 checkpoint--model_type必須和訓練時一致否則加載權(quán)重會因結(jié)構(gòu)不匹配報錯。評估完一般會輸出每張圖的 Dice 和平均值平均值低于 0.7 就要回頭看數(shù)據(jù)質(zhì)量和訓練輪數(shù)。注意 Dice 對類別極不平衡敏感細胞圖像里前景占比小的時候Dice 比準確率更能反映真實分割效果這也是這套代碼選 Dice 而不是 accuracy 的原因。4. 大圖切片推理SAHI 模塊怎么處理幾千像素的細胞圖4.1 為什么整圖推理會翻車細胞病理切片分辨率經(jīng)常是 2000×2000 甚至更大直接 resize 到網(wǎng)絡(luò)輸入尺寸比如 512×512細胞會被壓成幾個像素邊界信息全丟分割結(jié)果糊成一團。不 resize 直接整圖送進去顯存又扛不住。這是醫(yī)學圖像分割最典型的矛盾SAHI 切片推理就是解這個矛盾的。思路很直接把大圖按固定窗口切成帶重疊的小塊每塊單獨推理再把每塊的預(yù)測結(jié)果按位置拼回原圖重疊區(qū)域做融合。重疊是為了避免細胞正好被切在窗口邊界上導致漏檢融合是為了讓邊界過渡自然。4.2 slicePredict.py 的切片參數(shù)python slicePredict.py \ --model ./checkpoints/best.pth \ --model_type unetpp \ --source ./data/test_large \ --slice_size 512 \ --overlap 0.25 \ --output_dir ./slice_results參數(shù)說明--slice_size是切片窗口大小要和訓練時的輸入尺寸對齊訓練用 512 推理也用 512否則尺度不一致精度掉得厲害--overlap是重疊比例0.25 表示相鄰切片重疊 25%重疊太小邊界細胞會被切斷太大推理時間成倍漲0.2 到 0.3 是常見區(qū)間--source指向待推理的大圖目錄。跑完在--output_dir里能看到拼好的整圖 mask。4.3 后處理與拼接邏輯sahi/postprocess里的拼接不是簡單覆蓋常見做法是對重疊區(qū)域做加權(quán)平均或取最大值。加權(quán)平均讓邊界更平滑取最大值更保守、不容易漏。sahi/slicing.py負責生成切片坐標sahi/prediction.py管單塊推理sahi/annotation.py可能涉及結(jié)果轉(zhuǎn)標注格式。如果你要接自己的后處理改postprocess里的融合函數(shù)就行不用動推理主流程。提示切片推理的耗時和切片數(shù)量成正比slice_size減半切片數(shù)大約翻四倍。調(diào)試階段先用小圖驗證拼接邏輯對不對再上全量數(shù)據(jù)別一上來就幾千張圖跑通宵。5. 避坑與排查這套源碼跑不起來時先看這幾條5.1 顯存不足報 CUDA out of memory現(xiàn)象訓練剛起步或第一個 epoch 就拋RuntimeError: CUDA out of memory。原因batch size 太大、UNet 參數(shù)量比 UNet 高、輸入尺寸沒降下來三者疊加最容易爆。解決先把--batch_size降到 2 或 4 試還不行就把輸入尺寸從 512 降到 256再不行換--model unet。另外確認沒有別的進程占著顯存nvidia-smi看一眼有殘留進程就 kill 掉。梯度累積是另一個思路小 batch 多步累積等效大 batch但這份代碼不一定內(nèi)置要自己加。5.2 mask 與圖像尺寸或通道不匹配現(xiàn)象訓練時報維度錯誤或者 loss 算出來是 nan。原因圖像是 RGB 三通道m(xù)ask 被讀成三通道或者圖像和 mask 尺寸不一致resize 時用了不同插值方式導致錯位。解決在data_loading.py里強制 mask 走.convert(L)轉(zhuǎn)單通道圖像和 mask 用同一套 resize 參數(shù)mask 用最近鄰插值Image.NEAREST避免引入中間灰度值。改完先用 3.1 的驗證腳本確認配對無誤。5.3 Docker 里 GPU 不可用現(xiàn)象容器內(nèi)torch.cuda.is_available()返回 False。原因宿主機沒裝 nvidia-container-toolkit或者docker run時漏了--gpus all。解決宿主機裝好 nvidia-container-toolkit 并重啟 docker 服務(wù)運行容器時確認帶上--gpus all。驗證方法是在容器里跑nvidia-smi能看到顯卡列表就對了。這個坑在云服務(wù)器上尤其常見鏡像里裝了 CUDA 不代表容器能訪問 GPU。5.4 評估指標虛高但預(yù)測圖很爛現(xiàn)象evaluate.py報的 Dice 有 0.9但predict.py出來的 mask 肉眼看著一塌糊涂。原因驗證集和訓練集分布太接近或者 mask 里背景占比極高Dice 被背景拉高也可能是評估時用了和預(yù)測不同的預(yù)處理。解決確認評估和預(yù)測走的是同一套預(yù)處理和同一組權(quán)重把預(yù)測結(jié)果疊在原圖上肉眼檢查別只信數(shù)字如果背景占比過高考慮改用前景 Dice 或 IoU 單獨統(tǒng)計。指標是參考不是結(jié)論醫(yī)學圖像尤其要肉眼復(fù)核。5.5 訓練 loss 不下降現(xiàn)象跑了十幾個 epochloss 幾乎不動。原因?qū)W習率設(shè)太大導致震蕩或者數(shù)據(jù)沒歸一化或者標簽全是背景。解決先把學習率降到 1e-5 試檢查data_loading.py里有沒有做歸一化圖像像素一般要除以 255 或按均值和方差標準化用 3.1 的腳本確認 mask 不是全黑。這三條排查完八成能找到原因。6. 進階調(diào)優(yōu)把 Dice 從 0.7 推到 0.85 的幾個實操技巧訓練跑通只是起點真正拉開差距的是調(diào)優(yōu)。我拿這套代碼調(diào)細胞分割時有幾個動作是穩(wěn)定有效的。第一是數(shù)據(jù)增強。細胞圖像對旋轉(zhuǎn)、翻轉(zhuǎn)、彈性形變不敏感但對顏色抖動敏感因為染色批次不同顏色差異大。在data_loading.py里加隨機旋轉(zhuǎn) 90 度、水平垂直翻轉(zhuǎn)、輕微顏色抖動通常能漲幾個點。注意 mask 只能跟著做幾何變換不能做顏色變換。第二是損失函數(shù)。純 Dice loss 在邊界上梯度弱純交叉熵在類別不平衡時偏向背景。常見做法是 Dice BCE 加權(quán)組合權(quán)重各 0.5 起步再調(diào)。如果train.py里只用了單一損失可以自己加一項改動量不大。第三是學習率調(diào)度。固定學習率后期收斂慢加個余弦退火或 ReduceLROnPlateau驗證 Dice 幾個 epoch 不漲就降學習率往往能再擠出一兩個點。第四是模型選擇。小數(shù)據(jù)集上 UNet 不一定贏 UNet參數(shù)量大反而容易過擬合。我的習慣是先用 UNet 跑個 baseline再上 UNet 對比用驗證 Dice 說話別默認復(fù)雜模型一定好。第五是切片推理的重疊融合策略。如果發(fā)現(xiàn)拼接邊界有接縫把--overlap從 0.25 提到 0.4或者把融合從取最大值改成加權(quán)平均邊界會順很多代價是推理時間。驗證調(diào)優(yōu)有沒有效別只看最終 Dice要看驗證集 Dice 曲線是否更平穩(wěn)、預(yù)測圖邊界是否更貼合。我一般會固定一個測試子集每次改動都在這上面跑一遍對比避免被隨機性誤導。從那以后我每次拿到新的分割源碼包都強制先跑通最小閉環(huán)——三張圖訓練、一張圖預(yù)測、算一次 Dice——確認鏈路通了再上全量數(shù)據(jù)。這套 UNet/UNet 細胞分割源碼的結(jié)構(gòu)清晰入口齊全按上面的順序拆下來從環(huán)境到訓練到切片推理能完整走一遍。希望幫到你。本文還有配套的精品資源點擊獲取