ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

医学影像深度学习多任务流水线:ResNet+UNet+DeepLabV3+YOLOv5实战

医学影像深度学习多任务流水线:ResNet+UNet+DeepLabV3+YOLOv5实战 医学影像类的深度学习毕业设计卡住学生的往往不是算法原理而是不知道如何把 ResNet、UNet、DeepLabV3、YOLOv5 这些模型组合到一个项目里。单独跑一个分类、分割或检测模型都不难难的是用 PyTorch 搭建一条能说明白的多任务链路先由 ResNet 判断影像是否异常再由 UNet 或 DeepLabV3 对病灶区域做像素级分割最后由 YOLOv5 输出目标的位置框和类别。下面以医学影像为场景从环境配置、数据组织、单模型训练到多任务流程集成给出完整复现思路。整个方案以课设和毕设场景为主先用小规模公开数据集跑通再逐步扩大数据量不建议一开始就追求高精度。1. 先搞清楚四个模型在医学影像流程里分别负责什么1.1 多任务不是堆模型而是一条分析流水线医学影像分析任务通常可以拆成三个粒度图像级是否存在异常、像素级病灶区域在哪、框级病灶位置和类别。用一个模型同时输出这三个结果并不现实常见的做法是让多个模型各管一段组成一条分析流水线。毕设中最容易出现的问题是“四个模型都训练出来却不知道它们之间怎么配合”。要避免这种局面应该先定义流程再定义模型。一个常用的流程是ResNet 对输入影像做分类判断当前图像是否包含疑似病灶。UNet 或 DeepLabV3 对包含疑似病灶的图像做语义分割得到像素级病灶区域。YOLOv5 在原始图像上做目标检测得到病灶位置框和类别。这个流程的好处是每一阶段都能独立训练、独立评估、独立替换。后续写论文时分类、分割、检测可以分别作为三个对比实验也可以组合成端到端流水线作为综合实验。1.2 四个模型各司其职分类、分割、检测的分工与选型四个模型虽然都用卷积神经网络但解决的问题粒度不同。可以用下面这张表快速理解模型任务类型输出形式典型用途ResNet图像分类类别概率判断影像是否存在病灶UNet语义分割与输入等大的 mask勾勒不规则病灶边界DeepLabV3语义分割与输入等大的 mask多尺度病灶分割与 UNet 做对比YOLOv5目标检测边界框、类别、置信度定位离散病灶并标注类别选择这四个模型并不是随意的。ResNet 是迁移学习最稳妥的 backbone预训练权重容易获取UNet 是医学分割的经典基线结构直观、小数据集也能训DeepLabV3 在分割精度和多尺度特征表达上更强和 UNet 做消融对比很有说服力YOLOv5 则是工程成熟度最高的检测方案之一数据格式、训练脚本和部署资料都比较完整。理解这个分工后后面的环境搭建和代码实现会更有指向性所有代码都不需要真的写一个“四合一”结构而是先把四条分支分别跑通最后再串成流水线。2. 环境准备与公共目录设计先对齐 PyTorch、CUDA 和数据集结构2.1 版本选型与安装方式医学深度学习项目对框架版本比较敏感尤其是 PyTorch、CUDA、torchvision 三者必须匹配。建议新建独立的 conda 环境不要在系统 Python 环境里直接安装。下面是常见的学习环境配置范围组件推荐范围说明Python3.8 - 3.113.10 兼容性较好PyTorch1.13 或 2.x优先使用官方稳定版本torchvision与 PyTorch 同源版本用于 ResNet、DeepLabV3 预训练模型CUDA11.8 或 12.1根据显卡驱动选择YOLOv5最新 releaseclone 仓库后按 requirements 安装创建环境并按需安装基础依赖conda create -n meddl python3.10 conda activate meddl pip install numpy pandas matplotlib opencv-python pillow tqdm scikit-learn albumentationsPyTorch 的安装命令建议在官方 get-started 页面生成常见组合如下pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118安装完成后先用一小段代码确认 GPU 可用import torch print(torch.__version__) print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0) if torch.cuda.is_available() else CPU only)如果这里torch.cuda.is_available()返回False不要直接开始训练先排查驱动、CUDA 版本和 PyTorch 安装源是否匹配。这个问题会在后面的排查章节展开。2.2 统一数据集组织方式医学影像公开数据集的原始目录往往很乱有的按病人编号存放有的把多种类型图片混在一个文件夹。直接加载容易出错先统一目录结构会更可控。一种适合毕设的组织方式如下data/ med_data/ classification/ train/ normal/ abnormal/ val/ normal/ abnormal/ test/ normal/ abnormal/ segmentation/ images/ train/ val/ masks/ train/ val/ detection/ images/ train/ val/ labels/ train/ val/ data.yaml分类任务按类别建子目录可以直接使用torchvision.datasets.ImageFolder也可以写自定义 Dataset。分割任务要求图像和 mask 一一对应文件名建议保持一致例如image_001.png对应mask_001.png。检测任务需要把 YOLO 格式的标签文件放在labels对应目录中。这个目录结构可以不是最终形态但至少要在项目开始时就固定下来。后面每个模型的数据加载代码都围绕同一套路径避免训练到一半发现数据进错模型。2.3 项目目录结构与公共工具函数整体工程目录建议按照功能拆分medical_multi_task/ config.py datasets/ classification_dataset.py segmentation_dataset.py models/ unet.py deeplab.py scripts/ train_resnet.py train_unet.py train_deeplab.py train_yolov5.sh predict_pipeline.py outputs/ classification/ segmentation/ detection/ checkpoints/项目根目录放一个config.py管理公共路径和超参数避免每个脚本里重复写死路径。config.py示例from pathlib import Path DATA_ROOT Path(data/med_data) OUTPUT_ROOT Path(outputs) CHECKPOINT_ROOT Path(checkpoints) CLASSIFICATION_TRAIN DATA_ROOT / classification / train CLASSIFICATION_VAL DATA_ROOT / classification / val SEG_IMAGE_TRAIN DATA_ROOT / segmentation / images / train SEG_MASK_TRAIN DATA_ROOT / segmentation / masks / train DETECTION_DATA_YAML DATA_ROOT / detection / data.yaml再把固定随机种子和指标计算放到公共函数中。随机种子对于毕设很重要否则每次训练结果不同论文里的实验记录很难解释。一个简化版如下import random import numpy as np import torch def set_seed(seed42): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) torch.cuda.manual_seed_all(seed) def dice_coef(pred, target, smooth1e-6): pred torch.sigmoid(pred) pred (pred 0.5).float() intersection (pred * target).sum() return (2.0 * intersection smooth) / (pred.sum() target.sum() smooth) def iou_score(pred, target, smooth1e-6): pred torch.sigmoid(pred) pred (pred 0.5).float() intersection (pred * target).sum() union pred.sum() target.sum() - intersection return (intersection smooth) / (union smooth)这些函数会在后面的分类、分割流程中反复使用。3. 用 ResNet 完成病灶分类先判断影像是否异常3.1 数据加载与迁移学习分类任务的目标是输入一张影像输出“正常”或“异常”。医学影像有些是单通道灰度图但 torchvision 的预训练模型按 RGB 三通道输入设计所以加载时统一转成 RGB。自定义 Dataset 示例import os from PIL import Image from torch.utils.data import Dataset class ClassificationDataset(Dataset): def __init__(self, data_dir, transformNone, classes(normal, abnormal)): self.paths [] self.labels [] self.classes classes self.class_to_idx {name: idx for idx, name in enumerate(classes)} for label_name in classes: label_dir os.path.join(data_dir, label_name) if not os.path.isdir(label_dir): continue for file in sorted(os.listdir(label_dir)): if file.lower().endswith((.png, .jpg, .jpeg)): self.paths.append(os.path.join(label_dir, file)) self.labels.append(self.class_to_idx[label_name]) self.transform transform def __len__(self): return len(self.paths) def __getitem__(self, idx): image Image.open(self.paths[idx]).convert(RGB) if self.transform: image self.transform(image) return image, self.labels[idx]数据集很小的时候不建议上来就训练完整 ResNet而是用 torchvision 自带的预训练权重做迁移学习。加载方法如下import torch.nn as nn from torchvision import models model models.resnet50(weightsmodels.ResNet50_Weights.DEFAULT) num_features model.fc.in_features model.fc nn.Linear(num_features, 2)为什么只改最后一层fc因为 ImageNet 预训练模型已经学到了大量通用视觉特征比如边缘、纹理、形状。病灶图像和自然图像在最底层特征上是相似的保留这些权重可以大幅减少训练轮次和小样本下的过拟合。如果数据集很大、任务差异也很大才考虑微调更多层或全部层。3.2 分类训练与评估训练脚本核心逻辑如下import torch from torch import nn, optim from torchvision import transforms set_seed() transform transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]), ]) train_ds ClassificationDataset(data/med_data/classification/train, transformtransform) val_ds ClassificationDataset(data/med_data/classification/val, transformtransform) train_loader torch.utils.data.DataLoader(train_ds, batch_size32, shuffleTrue, num_workers2) val_loader torch.utils.data.DataLoader(val_ds, batch_size32, shuffleFalse, num_workers2) criterion nn.CrossEntropyLoss() optimizer optim.Adam(model.parameters(), lr1e-4) scheduler optim.lr_scheduler.ReduceLROnPlateau(optimizer, modemin, patience3, factor0.5) device torch.device(cuda if torch.cuda.is_available() else cpu) model model.to(device) for epoch in range(30): model.train() total_loss 0.0 for images, labels in train_loader: images images.to(device) labels labels.to(device) optimizer.zero_grad() outputs model(images) loss criterion(outputs, labels) loss.backward() optimizer.step() total_loss loss.item() model.eval() val_loss 0.0 correct 0 total 0 with torch.no_grad(): for images, labels in val_loader: images images.to(device) labels labels.to(device) outputs model(images) loss criterion(outputs, labels) val_loss loss.item() preds outputs.argmax(dim1) correct (preds labels).sum().item() total labels.size(0) scheduler.step(val_loss / len(val_loader)) print(fepoch {epoch 1}, train_loss: {total_loss / len(train_loader):.4f}, fval_loss: {val_loss / len(val_loader):.4f}, acc: {correct / total:.4f})评估阶段不要只看 accuracy医学影像常见类别不均衡accuracy 很容易失真。建议同时输出混淆矩阵和classification_reportfrom sklearn.metrics import classification_report, confusion_matrix all_preds [] all_labels [] model.eval() with torch.no_grad(): for images, labels in val_loader: images images.to(device) preds model(images).argmax(dim1).cpu().numpy() all_preds.extend(preds.tolist()) all_labels.extend(labels.numpy().tolist()) print(confusion_matrix(all_labels, all_preds)) print(classification_report(all_labels, all_preds, target_names[normal, abnormal]))ResNet 这一层的输出会作为流水线的第一个门控。如果分类结果判断为异常才继续进入分割和检测如果判断为正常可以提前结束减少后续计算量。这个策略在论文里可以写成“两阶段级联推理”。4. UNet 与 DeepLabV3两条分割路线都要能跑通4.1 UNet 的结构理解与最小实现UNet 是医学分割最常见的 baseline。它的核心思路是编码器逐层下采样提取语义信息解码器逐层上采样恢复空间分辨率同时通过跳跃连接把编码器的细节特征传给解码器。这样的设计对小目标、边界不规则的病灶比较友好。一个能用于二分割的最小 UNet 实现如下import torch import torch.nn as nn import torch.nn.functional as F class DoubleConv(nn.Module): def __init__(self, in_ch, out_ch): super().__init__() self.block nn.Sequential( nn.Conv2d(in_ch, out_ch, kernel_size3, padding1), nn.BatchNorm2d(out_ch), nn.ReLU(inplaceTrue), nn.Conv2d(out_ch, out_ch, kernel_size3, padding1), nn.BatchNorm2d(out_ch), nn.ReLU(inplaceTrue), ) def forward(self, x): return self.block(x) class Down(nn.Module): def __init__(self, in_ch, out_ch): super().__init__() self.block nn.Sequential( nn.MaxPool2d(2), DoubleConv(in_ch, out_ch), ) def forward(self, x): return self.block(x) class Up(nn.Module): def __init__(self, in_ch, out_ch): super().__init__() self.up nn.ConvTranspose2d(in_ch, in_ch // 2, kernel_size2, stride2) self.conv DoubleConv(in_ch, out_ch) def forward(self, x1, x2): x1 self.up(x1) diff_y x2.size()[2] - x1.size()[2] diff_x x2.size()[3] - x1.size()[3] x1 F.pad(x1, [diff_x // 2, diff_x - diff_x // 2, diff_y // 2, diff_y - diff_y // 2]) x torch.cat([x2, x1], dim1) return self.conv(x) class UNet(nn.Module): def __init__(self, in_channels1, num_classes1): super().__init__() self.inc DoubleConv(in_channels, 64) self.down1 Down(64, 128) self.down2 Down(128, 256) self.down3 Down(256, 512) self.down4 Down(512, 1024) self.up1 Up(1024, 512) self.up2 Up(512, 256) self.up3 Up(256, 128) self.up4 Up(128, 64) self.outc nn.Conv2d(64, num_classes, kernel_size1) def forward(self, x): x1 self.inc(x) x2 self.down1(x1) x3 self.down2(x2) x4 self.down3(x3) x5 self.down4(x4) x self.up1(x5, x4) x self.up2(x, x3) x self.up3(x, x2) x self.up4(x, x1) return self.outc(x)这里面最需要注意的细节是输入尺寸。由于编码器中有多次 MaxPool输入图像的长宽最好能被 2 的幂整除比如 512、256。否则在上采样拼接时特征图尺寸可能相差 1 个像素虽然上面的代码做了 pad但容易带来不必要的对齐问题。4.2 DeepLabV3 基于 torchvision 的实现差异DeepLabV3 的强项在于使用空洞卷积扩大感受野并通过 ASPP 模块捕获多尺度信息。torchvision 中提供的deeplabv3_resnet50是 DeepLabV3 的官方实现结构上可以作为 DeepLabV3 思路的参考。如果毕设题目明确要求复现 DeepLabV3 的 decoder 细节更严谨的做法是参考原论文补上 decoder 模块或者使用 mmsegmentation 这类现成开源框架。用 torchvision 快速搭建分割模型的代码from torchvision import models import torch.nn as nn num_classes 1 model models.segmentation.deeplabv3_resnet50( weightsmodels.segmentation.DeepLabV3_ResNet50_Weights.DEFAULT ) in_channels model.classifier[4].in_channels model.classifier[4] nn.Conv2d( in_channels, num_classes, kernel_size(1, 1), stride(1, 1) )关键点有两个预训练模型输入是 RGB 三通道。如果医学影像原始是灰度图要转换成三通道后再输入最简单的做法是加载时用image.convert(RGB)。替换classifier最后一层后输出的out键才是分割结果。forward 返回的是一个 OrderedDict训练取model(image)[out]。使用示例output model(images)[out]4.3 分割训练、Dice 和 IoU 评估分割训练同样使用 Dataset 组织数据。分割标签建议是单通道 0/1 mask网络输出是一个和原图等大的 logits 矩阵。二分类分割可以用BCEWithLogitsLoss损失函数如下criterion nn.BCEWithLogitsLoss()训练循环内需要把 mask 形状对齐images images.to(device) # [B, C, H, W] masks masks.to(device) # [B, 1, H, W] outputs model(images)[out] # DeepLabV3 loss criterion(outputs, masks.float())UNet 的输出没有[out]直接用outputs即可。验证阶段计算 Dice 和 IoUdef validation(model, val_loader, device): model.eval() total_dice 0.0 total_iou 0.0 count 0 with torch.no_grad(): for images, masks in val_loader: images images.to(device) masks masks.to(device) outputs model(images) if isinstance(outputs, dict): outputs outputs[out] total_dice dice_coef(outputs, masks.float()).item() total_iou iou_score(outputs, masks.float()).item() count 1 return total_dice / count, total_iou / count分割结果的可视化也要保存下来否则论文里没有配图。保存方式很简单把预测 mask 和原图叠加import cv2 import numpy as np def save_overlay(image_path, mask, save_path): image cv2.imread(image_path) mask (mask 0.5).astype(np.uint8) * 255 overlay image.copy() overlay[mask 0] (0, 0, 255) result cv2.addWeighted(image, 0.6, overlay, 0.4, 0) cv2.imwrite(save_path, result)这里把预测 mask 区域在图像上标红用于人工检查模型是否真的找到了病灶区域。5. 用 YOLOv5 完成病灶检测给出位置和类别框5.1 YOLOv5 环境与数据标注格式YOLOv5 单独使用独立仓库脚本更方便先克隆仓库并安装依赖git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt如果自动下载权重缓慢可以手动把yolov5s.pt下载后放到项目根目录。YOLOv5 的数据格式和分类、分割都不同标签不是整图 mask而是每个目标一行class x_center y_center width height其中x_center、y_center、width、height都是相对图片宽高的归一化数值范围在 0 到 1 之间。标注时可以用 labelImg 或 Roboflow 生成类别编号从 0 开始。目录结构如下detection/ images/ train/ img_001.jpg val/ img_002.jpg labels/ train/ img_001.txt val/ img_002.txt data.yaml注意图片文件名和标签文件名必须一致否则 YOLOv5 找不到对应标签。5.2 data.yaml 与训练命令data.yaml是最容易写错的地方。修改后的配置示例如下train: /absolute/path/to/detection/images/train val: /absolute/path/to/detection/images/val nc: 2 names: [normal, nodule]nc表示类别数量names中的顺序要和标注时的类别编号一致。train和val建议写绝对路径避免相对路径带来的问题。训练命令python train.py \ --img 640 \ --batch 16 \ --epochs 100 \ --data /path/to/detection/data.yaml \ --weights yolov5s.pt \ --device 0参数说明参数含义说明--img输入图像尺寸默认 640显存小可改 512--batchbatch size根据显存调整OOM 时调小--epochs训练轮次小数据集 100 轮足够观察趋势--weights预训练权重yolov5s.pt速度最快精度适中--device设备编号0 表示第一张 GPUCPU 写cpu如果数据集很小可以加一个--cache ram参数把图片缓存到内存加快读取速度。显存不足时先降--batch不要一上来就把--img降到 320因为医学小病灶在低分辨率下可能丢失细节。5.3 检测指标 mAP 的读取方式训练结束时YOLOv5 会在验证集上计算两个核心指标mAP0.5IoU 阈值取 0.5 时的平均精度。mAP0.5:0.95IoU 阈值从 0.5 到 0.95 每隔 0.05 计算一次后取平均更严格。毕设写结果时建议两个都写并用不同模型做对比。检测结果的可视化图片保存在runs/detect或runs/val目录下可以直接用于论文配图。6. 多任务流水线集成分类、分割、检测如何串起来6.1 推荐流程先分类再分支三个模型单独跑通后多任务流水线不是简单地把三个模型叠在一起而是设计一个能说明业务逻辑的调用顺序。推荐流程result classify(image) if result abnormal: mask segment(image) boxes detect(image) else: mask None boxes []这样做的原因有三点医学影像中正常样本可能占多数先分类可以过滤掉大量不需要精细分析的图像降低整体推理耗时。“先分类判断有没有病灶再分割定位区域再检测输出类别”这条链路符合医生阅片的基本逻辑在答辩时容易被理解。如果某一级模型效果不好可以单独替换不需要重写整套代码。6.2 推理脚本与结果可视化多任务推理脚本可以组织成一个predict_pipeline.py。核心思路是加载三个训练好的模型对同一张输入图像依次调用import torch import cv2 import numpy as np class MedicalInferencePipeline: def __init__(self, cls_ckpt, seg_ckpt, det_weights, devicecuda): self.device torch.device(device if torch.cuda.is_available() else cpu) self.cls_model self.load_classifier(cls_ckpt) self.seg_model self.load_segmentor(seg_ckpt) self.det_model torch.hub.load( ultralytics/yolov5, custom, pathdet_weights, force_reloadTrue ) def run(self, image): abnormal self.classify(image) if not abnormal: return {abnormal: False, mask: None, boxes: []} mask self.segment(image) boxes self.detect(image) return {abnormal: True, mask: mask, boxes: boxes}实际项目中模型加载函数需要和训练脚本保持一致比如分类模型要加载model.fc相同的结构分割模型要使用相同的 UNet 或 DeepLabV3 定义。可视化时将分割 mask 用半透明色叠加到原图同时把检测框画在图上。最终可以输出一张三通道结果图左边是原图中间是分割结果右边是检测结果。这一步做完整个多任务流水线就有了可演示的界面毕设的功能演示部分基本完整。7. 常见问题与排查路径7.1 环境与训练阶段的问题下面表格整理了最容易遇到的几类问题问题现象常见原因检查方式处理建议torch.cuda.is_available()为 FalseCUDA 驱动和 PyTorch 版本不匹配运行nvidia-smi对比 PyTorch 需要的 CUDA 版本重新安装匹配的 PyTorch 版本UNet forward 尺寸不匹配输入尺寸不是 2 的整数次幂打印每个层输出 shape输入统一 resize 到 512 或 256DeepLabV3 forward 报通道错误输入是 1 通道灰度图检查image.shape加载时convert(RGB)YOLOv5 训练 loss 为 nan学习率过大或标签越界查看日志和 data.yaml调小学习率检查标签坐标是否在 0-1训练指标很高但测试很差数据划分不严谨检查训练集和验证集是否包含同一病人切片按病人 ID 分组划分分割预测全为背景或全为前景类别严重不均衡或增强过度查看 val 输出 mask使用加权损失或调整预测阈值排查顺序建议从输入数据开始先确认读到的图像和标签是正确的再检查模型结构最后才怀疑优化器和超参数。很多问题不是模型写错而是数据加载时标签错位或归一化不一致。7.2 医学影像特有的数据泄漏问题医学影像项目最常见的隐藏问题是数据泄漏。举例来说一个病人的 CT 序列包含 100 张切片如果随机按文件拆分成训练集 80 张、验证集 20 张那么同一个病人的切片可能同时出现在训练集和验证集中。模型可能在验证集上表现很好因为它在训练阶段已经见过这个病人的大部分特征而不是真正学会了泛化。解决办法是按病人 ID 分组而不是按文件随机划分。使用GroupShuffleSplit可以按组划分from sklearn.model_selection import GroupShuffleSplit groups sample_df[patient_id].values split GroupShuffleSplit(n_splits1, test_size0.2, random_state42) for train_idx, val_idx in split.split(sample_df, groupsgroups): train_samples sample_df.iloc[train_idx] val_samples sample_df.iloc[val_idx]这个细节非常值得写进论文的数据处理部分。很多评审老师会专门检查训练集和验证集是否按病人隔离提前处理好会加分。8. 最佳实践与毕设扩展方向8.1 训练与提交前检查清单一套可操作的检查清单如下是否固定了随机种子保证实验可复现。训练集、验证集、测试集是否严格独立尤其是同一病人的多张切片是否被拆分到不同集合。数据增强是否只用于训练集验证集和测试集是否只用基础预处理。归一化参数是否在训练集上计算并统一应用到所有集合。图像尺寸是否按模型要求统一分类使用 224分割使用 512检测使用 640。模型保存是否只保存state_dict而不是直接保存整个 model 对象。评估指标是否覆盖 accuracy、Dice、IoU、mAP 中与任务对应的指标。预测结果是否保存了可视化图片方便论文配图和答辩展示。代码中的路径是否全部是相对路径或通过 config 管理避免换机器后无法运行。是否记录了每个实验的配置文件防止复现时忘记超参数。生产环境还需要额外考虑日志保存、监控训练曲线、定期备份 checkpoint、异常中断后的断点续训。毕设大部分情况下用不到这么重但至少模型权重和训练日志要单独归档。8.2 从毕设到论文与技术报告的扩展点四个模型都跑通后毕设的深度可以通过以下方向继续提升消融实验UNet 替换 backbone、DeepLabV3 替换 ASPP、YOLOv5 替换不同规模的 s/m/l 权重比较精度和速度。多任务编码器共享在分类和分割之间共享 ResNet 编码器让一个 backbone 同时输出分类和分割结果对比多分支结构和独立模型的差异。数据增强策略加入随机旋转、翻转、弹性形变、对比度调整评估医学小数据集上的稳定性。边缘设备部署把训练好的 YOLOv5 模型导出为 ONNX 或 TensorRT部署到边缘设备作为工程实践章节。类别不均衡处理对分割任务使用 Dice Loss 或 Focal Loss对比BCEWithLogitsLoss的表现。最后的建议是先把分类、分割、检测三个模型各自用最小数据集跑通再增加数据量和模型复杂度。不要一开始就把所有改进点同时加进去否则出了错误很难定位。保留一份能稳定复现的简单版本再在它基础上逐步优化整个项目推进会顺很多。
返回列表