:從預處理到U-Net訓練與避坑)
簡介一套面向醫(yī)學影像分割任務的數據集聚焦超聲乳腺良性區(qū)域分割適用于計算機視覺與醫(yī)學圖像處理方向的學習者、研究者既可作為入門分割模型訓練與驗證的基礎數據也適合用來做算法對比與效果評估。壓縮包內共877個文件其中875個PNG格式文件圖像與掩碼、1個txt類別文件和1個Python可視化腳本整體大小約88.01MB目前已有958人下載學習。數據包含訓練集與測試集訓練集約300張圖像及對應mask測試集約130張按images與masks目錄組織可直接接入常見分割流程數據采用背景、良性乳腺兩類標注classes文件提供類別說明??梢暬_本可隨機抽取一張原始圖、GT圖及兩者疊加效果并保存便于快速直觀檢查標注質量與模型輸出。整體來看數據集與可視化腳本共同為乳腺超聲圖像分割相關的算法復現、論文實驗或課程設計提供了扎實的數據與工具支撐適合入門至中高級實踐者使用。1. 醫(yī)學圖像分割數據集一份超聲乳腺良性病灶數據到底能幫你做什么拿到一份帶訓練集、測試集和標簽的超聲乳腺良性圖像分割數據集第一件事不是急著跑模型而是先搞清楚它和自然圖像分割數據有什么不同。B超圖像噪聲大、邊界模糊良性病灶與周圍組織灰度接近分割任務比純檢測更依賴像素級標注質量。接下來我會從數據集結構、標簽格式、訓練/測試劃分講到最常用的 U-Net 落地腳本再列出真實使用中踩過的五個坑最后給一套可以復現的驗證套路。這套流程適合剛入手醫(yī)學圖像分割的研究生、算法工程師也適合想拿公開數據集驗證預處理和訓練管線的從業(yè)者。2. 超聲乳腺良性分割數據集的目錄與標簽拿到手先做三件事拿到一個醫(yī)學圖像分割數據集尤其是超聲影像數據直接開訓是翻車高發(fā)區(qū)。超聲圖像不像CT或MRI那樣有標準DICOM頭信息公開數據集的目錄命名、掩碼格式甚至像素值定義都可能不同。我的習慣是先做三件事確認訓練集和測試集是否完全分開確認標簽是掩碼還是多邊形坐標確認圖像和標簽能否一一對應。這三件事決定了后面的預處理代碼怎么寫也決定了評測分數是否可信。2.1 訓練集和測試集為什么必須分開數據劃分的底層邏輯分割模型的目標是學會“看”病灶區(qū)域而不是“認出”某張?zhí)囟▓D片。如果訓練集和測試集存在重疊模型在測試時直接匹配文件名或像素模板Dice會虛高到不真實。對于超聲乳腺數據集這個問題尤其隱蔽同一患者的圖像可能連文件名都不同但病灶區(qū)域來自同一次掃描背景紋理幾乎一樣。按文件名查重只是第一道防線按患者ID查重才是關鍵。如果數據集沒有提供患者ID也可以通過文件名中的case編號或病人編號推斷。下面這段腳本會掃描訓練集和測試集的圖像文件名輸出重合列表from pathlib import Path train_img_dir Path(data/train/images) test_img_dir Path(data/test/images) train_names {p.name for p in train_img_dir.glob(*.png)} test_names {p.name for p in test_img_dir.glob(*.png)} overlap train_names test_names print(train count:, len(train_names)) print(test count:, len(test_names)) print(overlap count:, len(overlap)) for name in sorted(overlap)[:10]: print(overlap:, name)這段代碼用set求交集train_names和test_names分別是兩個目錄下的文件集合。輸出重合數量后即使沒有重合也要再按文件名前綴分組檢查比如文件名是case_001_frame_01.png那么所有case_001開頭的圖像應該只出現在一個集合里。如果同一病例的幀散落在兩邊就要手動調整劃分或者改用后面第5章會講的按患者分折交叉驗證。再補一段按患者ID檢查的代碼import re def patient_id(filename): match re.match(r(case[_-]?\d), filename) return match.group(1) if match else filename train_patients {patient_id(name) for name in train_names} test_patients {patient_id(name) for name in test_names} print(patients in both sets:, train_patients test_patients)這里用正則case[_-]?\d提取病例號。實際命名可能不是這個模式需要先print幾個文件名看一眼再寫正則。注意如果提取不出來寧可手動生成一個patient_id映射表也不要跳過這一步。對醫(yī)學圖像分割數據集而言按患者劃分比按圖像劃分更能反映模型在真實新病人上的表現。2.2 標簽格式識別掩碼是 PNG、JSON 還是 NIfTI決定預處理怎么寫標題里的“標簽”兩個字看著簡單落地時差異很大。超聲乳腺分割數據集常見三種標簽格式二值PNG掩碼、JSON多邊形坐標、NIfTI體數據。PNG掩碼可以直接用OpenCV/PIL讀取JSON需要把邊界點填充成掩碼NIfTI需要處理放射學坐標方向。拿到數據集后先用一段腳本統(tǒng)計標簽目錄的擴展名避免用錯解析器。from pathlib import Path from collections import Counter label_dir Path(data/train/labels) ext_counter Counter(p.suffix.lower() for p in label_dir.iterdir() if p.is_file()) print(ext_counter) for p in sorted(label_dir.glob(*))[:5]: print(p.name)這段腳本用Counter統(tǒng)計標簽目錄里所有文件后綴能立刻看出是.png還是.npy還是.nii.gz。注意.nii.gz的實際文件名后綴是.gz如果直接統(tǒng)計.suffix會得到.gz所以最好用p.name.endswith((.nii.gz,))或先用.with_suffix()再判斷。打印前5個文件名稱是為了確認命名規(guī)律給后面的文件名對齊做準備。如果標簽是PNG下一步檢查掩碼的像素值。很多數據集的背景是0前景是255但也有用1表示前景的。訓練時如果直接把255當成類別索引會把一個前景像素拆成兩個類別。讀掩碼后先打印唯一值import cv2 import numpy as np mask cv2.imread(data/train/labels/case_001.png, cv2.IMREAD_GRAYSCALE) print(mask shape:, mask.shape) print(unique values:, np.unique(mask))這里用cv2.imread的IMREAD_GRAYSCALE強制按灰度讀避免三通道干擾。如果輸出只有[0 255]訓練時要除255再當成背景/前景如果輸出是[0 1]直接轉long類型即可。如果還有中間值如128說明掩碼可能帶邊界標注或類別權重要回到數據集說明確認。超聲乳腺良性分割一般只需要背景和病灶兩類出現128時要小心處理。如果標簽是JSON常見結構是{filename: ..., regions: [{shape_attributes: {points: [...]}}]}。這種情況不能直接送進模型要先做一次離線轉換把多邊形填充成掩碼import json import cv2 import numpy as np with open(annotation.json) as f: ann json.load(f) mask np.zeros((height, width), dtypenp.uint8) for region in ann.get(regions, []): points region[shape_attributes][points] pts np.array(points, dtypenp.int32).reshape(-1, 1, 2) cv2.fillPoly(mask, [pts], 255)這段代碼用cv2.fillPoly把多邊形頂點填充成白色掩碼。height和width必須與原始B超圖像一致通??梢愿鶕D像尺寸獲取。轉換后的掩碼要和原始圖像放在同一個目錄層級方便Dataset類讀取。2.3 目錄組織檢查清單用一段腳本驗證文件名、尺寸和類別在寫訓練腳本前我會先用一個檢查腳本把所有潛在問題都暴露出來。你需要確認四點圖像和標簽文件一一對應、圖像和標簽尺寸一致、掩碼類別只有前景和背景、訓練集和測試集沒有文件重合。下面這段腳本會遍歷圖像目錄對每張圖找到同名標簽并檢查 shapefrom pathlib import Path import cv2 import numpy as np img_dir Path(data/train/images) mask_dir Path(data/train/labels) img_files sorted(img_dir.glob(*.png)) mask_files sorted(mask_dir.glob(*.png)) print(images:, len(img_files), masks:, len(mask_files)) for img_path in img_files: mask_path mask_dir / img_path.name if not mask_path.exists(): print(missing mask:, img_path.name) continue img cv2.imread(str(img_path), cv2.IMREAD_GRAYSCALE) mask cv2.imread(str(mask_path), cv2.IMREAD_GRAYSCALE) if img.shape ! mask.shape: print(shape mismatch:, img_path.name, img.shape, mask.shape)這組代碼用同名配對方式檢查img_files按字典序排序后再通過mask_dir / img_path.name構造掩碼路徑。這樣不會因為排序方式不同出現錯位。如果打印出missing mask或shape mismatch需要先用離線腳本修復數據而不是在訓練時靠報錯去猜。檢查結果可以用下面這張表記錄檢查項期望值失敗動作圖像數量與掩碼數量完全一致補齊缺失文件或重命名圖像和掩碼尺寸每個樣本 pair 相等先統(tǒng)一裁剪/縮放再入 train掩碼唯一值0 和 255 或 0 和 1訓練前歸一化到類別索引訓練/測試文件交集無按患者ID重新劃分這張表是我在實際處理B超數據集時的檢查模板。超聲乳腺圖像四周通常有大片黑色背景如果發(fā)現圖像尺寸不統(tǒng)一不要盲目resize先用固定比例中心裁剪去掉黑邊再用cv2.resize統(tǒng)一。掩碼尺寸不一樣時更危險說明標注和原圖可能不是從同一個預處理流程出來的需要回到原始數據確認。3. 用這份數據集跑通 U-Net預處理、訓練腳本與參數選擇把目錄和標簽檢查做完就可以進入正題了。醫(yī)學圖像分割數據集最常見的落地路徑是用 U-Net 結構訓練自己的數據集。這里我會給一個最小可用流程灰度圖預處理、圖像與標簽對齊、訓練循環(huán)、曲線解讀。超聲乳腺良性病灶本身邊界偏模糊模型不需要特別復雜先跑通基線比換大模型更重要。3.1 從 B 超圖像到模型輸入灰度圖歸一化與裁剪超聲圖像通常不是標準照片而是灰度強度圖。直接套用 ImageNet 的三通道均值和標準差沒有意義常見做法是對整張圖做最大最小值歸一化或者除以255。更穩(wěn)的做法是先統(tǒng)計數據集的灰度分布把均值附近的范圍映射到0-1而不是讓個別高亮偽影主導整個輸入。另一個重要的預處理是裁剪。B超圖像四周經常有設備信息、刻度尺、純黑邊框這些區(qū)域不參與診斷卻會讓模型在前期把注意力放在背景結構上。我一般用閾值法先找到超聲扇形區(qū)域再做歸一化和縮放。下面這段函數展示了一個可復用的預處理def preprocess_image(image_path, target_size(256, 256)): img cv2.imread(image_path, cv2.IMREAD_GRAYSCALE) img img.astype(np.float32) # 去掉四周黑色背景 non_bg img 5 coords np.argwhere(non_bg) y0, x0 coords.min(axis0) y1, x1 coords.max(axis0) 1 img img[y0:y1, x0:x1] # 歸一化到 0~1 img (img - img.min()) / (img.max() - img.min() 1e-6) img cv2.resize(img, target_size, interpolationcv2.INTER_CUBIC) return imgnon_bg img 5的5是灰度閾值用來區(qū)分超聲回聲區(qū)和純黑背景。如果圖像整體很暗可以降到2如果包含強噪聲點可以提到10。np.argwhere得到所有非背景像素坐標取最小和最大值得到裁剪框。歸一化時用img.max() - img.min()做分母加1e-6防止全黑圖除零。最后 resize 到256x256這是一個速度和精度平衡的尺寸如果顯存夠大用512x512能讓邊界更清楚。標簽掩碼要執(zhí)行同樣的裁剪和resize但插值方法必須改成最近鄰否則會引入不屬于任何一類的中間灰度值def preprocess_mask(mask_path, target_size(256, 256)): mask cv2.imread(mask_path, cv2.IMREAD_GRAYSCALE) # 使用同一個裁剪框這里簡化為直接 resize mask cv2.resize(mask, target_size, interpolationcv2.INTER_NEAREST) mask (mask 127).astype(np.int64) return mask注意這里的裁剪框要和圖像預處理完全一致。實際代碼里我通常把裁剪坐標從preprocess_image返回再傳給preprocess_mask避免各自算一遍導致錯位。3.2 標簽讀取與數據對齊防止圖像和掩碼錯位的檢查點很多剛開始用醫(yī)學圖像分割數據集的人會在 Dataset 類里用os.listdir分別讀圖像和掩碼再按下標訪問。這是最容易錯位的寫法因為兩個目錄的排序結果可能不一樣。更穩(wěn)的做法是只遍歷圖像目錄然后用圖像的文件名拼接掩碼路徑。下面是一個標準 PyTorch Dataset 寫法from torch.utils.data import Dataset from pathlib import Path import cv2 import torch class UltrasoundDataset(Dataset): def __init__(self, image_dir, mask_dir, target_size(256, 256)): self.image_paths sorted(Path(image_dir).glob(*.png)) self.mask_dir Path(mask_dir) self.target_size target_size for p in self.image_paths: assert (self.mask_dir / p.name).exists(), fmissing {p.name} def __len__(self): return len(self.image_paths) def __getitem__(self, idx): image_path self.image_paths[idx] mask_path self.mask_dir / image_path.name image preprocess_image(str(image_path), self.target_size) mask preprocess_mask(str(mask_path), self.target_size) image_tensor torch.from_numpy(image).unsqueeze(0).float() mask_tensor torch.from_numpy(mask).long() return image_tensor, mask_tensorself.image_paths使用sorted(Path.glob(*.png))掩碼路徑直接用同名拼接。assert在初始化時快速失敗如果缺掩碼會立刻報錯而不是訓練到中間才發(fā)現。torch.from_numpy(image).unsqueeze(0)給灰度圖補上通道維得到(1, H, W)。掩碼轉成long是 PyTorch 交叉熵損失要求的整數標簽類型。3.3 一個最小可運行的 U-Net 訓練腳本PyTorch模型結構建議先用經典 U-Net編碼器三層、解碼器三層通道數從32開始。下面這段訓練循環(huán)聚焦在數據加載、損失函數和優(yōu)化器配置上import torch from torch.utils.data import DataLoader from unet_model import UNet device torch.device(cuda if torch.cuda.is_available() else cpu) train_dataset UltrasoundDataset(data/train/images, data/train/labels) train_loader DataLoader(train_dataset, batch_size8, shuffleTrue, num_workers2) model UNet(in_channels1, out_channels2).to(device) optimizer torch.optim.Adam(model.parameters(), lr1e-4) loss_fn torch.nn.CrossEntropyLoss() for epoch in range(50): model.train() running_loss 0.0 for images, masks in train_loader: images, masks images.to(device), masks.to(device) output model(images) loss loss_fn(output, masks) optimizer.zero_grad() loss.backward() optimizer.step() running_loss loss.item() avg_loss running_loss / len(train_loader) print(fepoch {epoch 1:02d} loss {avg_loss:.4f})UNet(in_channels1, out_channels2)里in_channels1對應灰度超聲圖out_channels2對應背景和病灶兩個類別。CrossEntropyLoss的輸出要求是(N, C, H, W)掩碼是(N, H, W)正好匹配。batch_size8是在256x256輸入、顯存8GB左右的經驗值如果出現OOM先降到4或2。lr1e-4對醫(yī)學圖像小數據集比默認的1e-3更穩(wěn)避免剛開始幾步就把分割權重帶偏。如果掩碼只有0和255需要在preprocess_mask里做(mask 127)再轉LongTensor如果已經是0和1也要確認沒有浮點噪聲。類別索引從0開始CrossEntropyLoss會自動忽略索引為負的值所以不要給掩碼加背景掩蔽以滿足形狀要求。注意unet_model模塊可以用你自己實現的 U-Net 替換關鍵是輸入通道為1、輸出通道為2且輸出尺寸與輸入一致。3.4 訓練曲線解讀Dice 不漲、Loss 震蕩時先調什么訓練跑起來之后不能只看loss。醫(yī)學圖像分割更常用Dice系數來衡量因為良性和背景之間像素極度不平衡時交叉熵loss可能一直很低但Dice也不漲。下面這段代碼在驗證集上計算Dicedef dice_score(pred, target): pred (pred 0).float() # 預測屬于病灶類的概率最大 intersection (pred * target).sum() return (2.0 * intersection 1e-6) / (pred.sum() target.sum() 1e-6)pred是模型輸出的類別索引這里把預測病灶像素和二值掩碼逐像素相乘求和。加1e-6是為了處理兩張全空掩碼時除零的問題。如果訓練loss下降但Dice在0.1左右徘徊優(yōu)先檢查兩個方向一是數據增強是否過強比如隨機旋轉90度把超聲扇形方向打亂二是掩碼是否在resize時被最近鄰插值破壞尤其是病灶區(qū)域小的時候。如果loss來回震蕩把學習率從1e-4降到3e-5同時把batch size調小通常能讓曲線穩(wěn)定下來。還有一種常見情況是驗證loss下降但Dice不降這時需要看模型是否把所有像素都預測為背景。打印預測圖的唯一值如果只有0沒有1說明類別不平衡已經把訓練推到了平凡解。可以改用Dice Loss或Focal Loss先把背景類權重降低。4. 數據集使用避坑五個讓分割模型翻車的真實場景醫(yī)學圖像分割數據集的坑不在網絡結構而在數據管道。很多問題看起來像玄學最后定位都是讀取、劃分或預處理不一致。下面五條按“現象、原因、解決”展開方便照著排查。4.1 灰度圖像被當成三通道輸入U-Net 輸入通道數設成3現象模型能跑通loss也在下降但驗證集上預測結果基本是全黑或全圖。 原因cv2.imread默認會按三通道讀圖代碼又沒顯式指定IMREAD_GRAYSCALE于是灰度B超圖變成三通道的重復矩陣。模型第一層in_channels設成3確實能訓練但學到的三通道信息完全冗余邊界特征被削弱。 解決統(tǒng)一在數據讀取時加cv2.IMREAD_GRAYSCALE并打印img.shape確認輸出是(H, W)而不是(H, W, 3)。模型第一層in_channels1如果要用預訓練模型遷移也建議把權重在輸入層做均值折疊而不是簡單地把圖復制三通道。img cv2.imread(data/train/images/case_001.png) print(img.shape) # 如果是 (512, 512, 3)說明沒走灰度讀取這段檢查會立刻暴露問題?;叶菳超圖被讀成三通道后三個通道完全相同模型等于在重復特征上做卷積參數量和計算量都白白增加。4.2 掩碼前后景比例懸殊損失函數被背景主導現象訓練準確率很高但Dice只有0.2分割結果覆蓋了一整片灰度接近的區(qū)域。 原因良性病灶在超聲圖像里通常只占幾萬像素背景占了幾十萬像素。交叉熵損失會把絕大多數梯度花在背景上模型學到的就是把大塊暗區(qū)判為背景偶爾碰中一個病灶像素就能把準確率拉高。 解決訓練損失改成Dice Loss或Focal Loss。Dice Loss直接優(yōu)化區(qū)域重疊對類別不平衡更穩(wěn)定。如果數據集里病灶區(qū)域太小還可以在預處理階段按包含前景的裁剪框切patch讓每個訓練樣本里前景占比不低于某個閾值。def dice_loss(pred, target): pred torch.softmax(pred, dim1)[:, 1] smooth 1.0 intersection (pred * target).sum() return 1 - (2 * intersection smooth) / (pred.sum() target.sum() smooth)這個dice_loss直接取模型輸出中病灶類別的概率與二值掩碼計算重疊。smooth防止除零也避免訓練初期梯度過大。替換CrossEntropyLoss后模型會更關注少數類像素。4.3 訓練集和測試集來自同一病例指標虛高現象訓練時Dice到0.95測試時換一組數據就掉到0.5。 原因數據集劃分是按文件名隨機分的但同一患者的多個超聲幀出現在兩個集合里。因為同一患者的設備參數、探頭角度、病灶形態(tài)高度相似模型記住的是這個病人的背景紋理而不是通用病灶特征。 解決拿到數據先做患者級分組同一患者的所有圖像只能出現在訓練集或測試集不能跨集合。如果標題的數據集沒有提供患者ID也可以用文件名中的case編號提取。更穩(wěn)妥的做法是用第5章的按患者分折交叉驗證反復評估模型穩(wěn)定性。train_patients {patient_id(p.name) for p in train_names} test_patients {patient_id(p.name) for p in test_names} shared train_patients test_patients if shared: print(患者級泄露:, shared)這段代碼把2.1里的患者檢查邏輯明確放在訓練前執(zhí)行。出現共享患者時寧可減少訓練集規(guī)模也不能保留跨集合的病例。4.4 裁剪黑邊導致標簽坐標沒同步現象預測掩碼在圖像邊緣出現固定偏移評測時邊界區(qū)域出現一排假正或假負。 原因預處理函數對圖像先裁剪再resize但標簽掩碼在另一段代碼里直接resize沒有同步裁剪框。圖像縮放到目標尺寸時失去了一部分邊緣掩碼卻保留了完整尺寸兩者自然錯位。 解決圖像和掩碼必須共用同一個裁剪框。把preprocess_image里計算出的(y0, y1, x0, x1)返回給preprocess_mask掩碼先裁剪再resize。掩碼的resize插值統(tǒng)一用INTER_NEAREST配合代碼塊里的assert檢查尺寸一致。def crop_resize_pair(image, mask, threshold5): non_bg image threshold coords np.argwhere(non_bg) y0, x0 coords.min(axis0) y1, x1 coords.max(axis0) 1 image_crop cv2.resize(image[y0:y1, x0:x1], (256, 256), interpolationcv2.INTER_CUBIC) mask_crop cv2.resize(mask[y0:y1, x0:x1], (256, 256), interpolationcv2.INTER_NEAREST) return image_crop, mask_cropcrop_resize_pair把圖像和掩碼的裁剪框統(tǒng)一到同一個y0,x0,y1,x1掩碼用INTER_NEAREST不會產生新灰度值。這個函數應作為訓練管線的唯一入口避免兩處各寫一套。4.5 測試時沒有做與訓練時相同的預處理現象離線驗證Dice還行部署到新環(huán)境后指標明顯下降。 原因訓練腳本里的預處理寫在train.py測試腳本里重新寫了一份歸一化順序、裁剪閾值或resize尺寸有細微差別。超聲圖像的灰度范圍很敏感差一個閾值就可能讓病灶邊界改變。 解決把預處理函數抽成獨立模塊訓練和推理都從同一個模塊導入。然后在固定一張圖像上跑訓練管線和推理管線比較輸出數組是否完全一致。我習慣加一段單元測試比較同一個輸入在兩次調用后的numpy array是否相等。arr1 preprocess_image(data/train/images/case_001.png) arr2 preprocess_image(data/train/images/case_001.png) assert np.array_equal(arr1, arr2)這段測試只能驗證函數自身確定性更關鍵的是確認推理腳本沒有重新編寫一份“看起來差不多”的預處理。如果訓練和推理的灰度閾值、目標尺寸、插值方式不一致測試集上的微小偏差會在邊界像素上被放大。5. 在單獨一次劃分之外交叉驗證與分割指標的人工復核技巧5.1 用按患者分組的交叉驗證替代單次隨機劃分如果數據集包含訓練集和測試集直接用固定劃分訓練即可。但超聲乳腺數據規(guī)模通常不大單次劃分對隨機種子太敏感。我會在大致確認固定劃分之外額外做一次五折按患者交叉驗證用下面這段代碼生成折標簽from sklearn.model_selection import KFold import numpy as np patient_ids np.array(sorted({patient_id(p.name) for p in image_paths})) kf KFold(n_splits5, shuffleTrue, random_state42) for fold, (train_idx, val_idx) in enumerate(kf.split(patient_ids)): train_patients patient_ids[train_idx] val_patients patient_ids[val_idx] print(fold, len(train_patients), len(val_patients))KFold按患者ID列表分折而不是按圖像分折。這樣每一折的驗證集都來自模型沒見過的患者評估結果更接近真實新數據上的效果。random_state42固定隨機種子保證每次實驗可比較。5.2 把預測掩碼和原始 B 超圖像疊起來做人工抽檢Dice和IoU只是數值不能告訴你模型在哪個解剖位置犯錯。我會在每輪驗證后隨機抽5張測試圖像把預測掩碼和真實掩碼分別疊加在原始B超圖上轉成彩色對比圖保存。如果模型總把低回聲區(qū)域整片當成病灶數值上看不出來但可視化會非常明顯。overlay cv2.cvtColor(img_bgr, cv2.COLOR_GRAY2BGR) overlay[pred_mask 1] (0, 0, 255) # 紅為預測 overlay[true_mask 1] (0, 255, 0) # 綠為真實 cv2.imwrite(fcheck_fold{fold}.png, overlay)紅色表示預測為病灶、綠色表示真實標注紅綠重疊會變成黃??吹酱笃t色區(qū)域時說明模型把灰度相近的腺體組織誤判成了病灶需要回到訓練數據里檢查是否有類似的錯誤標注。這個人工抽檢習慣幫我校準了很多看似合理、實則結構不完整的模型。我自己的教訓是第一次跑這份數據時只看了Dice覺得0.87很不錯抽檢才發(fā)現模型把探頭陰影也劃進了病灶。后來把可視化抽檢加進訓練循環(huán)問題立刻暴露。希望幫到你。本文還有配套的精品資源點擊獲取