尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

PyTorch实战:皮肤烧伤检测从ResNet分类到U-Net分割

发布时间:2026/9/28 16:22:56

资讯中心
01
ARTICLE

PyTorch实战:皮肤烧伤检测从ResNet分类到U-Net分割

PyTorch实战:皮肤烧伤检测从ResNet分类到U-Net分割
简介这是一套面向医疗AI研究与实战的皮肤烧伤检测项目基于卷积神经网络实现烧伤范围、深度与愈合情况的自动识别适合医疗科研人员、算法工程师及深度学习初学者参考。压缩包共168个文件大小约41.69MB文件类型以Python脚本、YAML配置、Jupyter Notebook为主并包含图片、CSV结果数据、Shell脚本与Dockerfile等其中Python脚本承担模型构建与训练YAML用于定义模型参数与环境Notebook则提供分步流程教程整体覆盖从数据预处理到系统部署的完整链路。目前已有105人学习下载源码与教程配套便于快速上手。项目提供完整源码和分步教程可直接复现训练流程包含数据增强、模型调优、评估指标对比等实现细节并附有实验结果CSV与可视化图表帮助读者理解烧伤检测的工程化落地。1. 皮肤烧伤检测值不值得自己跑先把问题想明白接手皮肤烧伤检测这个方向的人十有八九是从一张惨不忍睹的临床照片开始的创面边界不规则、渗出液反光、周围皮肤红肿程度不一肉眼判断都费劲更别说让传统图像算法自动给结论。而基于深度学习的皮肤烧伤检测算法核心任务就是把“这块皮肤到底烧没烧、烧到什么程度、占体表面积多少”这类问题交给卷积神经网络去逼近。这类项目在临床辅助决策、远程分诊和教学场景里有真实需求也正因为输入是真实拍摄的创面照片它才比一般分类项目更考验数据清洗、标注一致性和模型泛化能力。如果你手头有几百张到几千张带有标注的烧伤图像想从零开始搭一个能用的深度学习检测流程这篇文章就是按这个目标写的。它适合有Python和PyTorch基础、但还没完整跑过一个医疗图像项目的开发者也适合想评估“烧伤检测算法到底能不能落地”的团队。我下面讲的不是某个神秘源码包的说明书而是这一类项目最常见的实现路线、参数设置和踩坑记录。2. 定义任务与整理数据先决定“检测”是分类、定位还是分割2.1 三种监督信号的取舍分类、检测框还是逐像素分割做深度学习检测类项目第一个要较真的是任务定义。烧伤这个词在计算机视觉里有三个层次对应三种不同的监督信号和输出形式第一层是图像级分类判断一张图里有没有烧伤、属于一度、二度还是三度第二层是目标检测用边界框把烧伤区域框出来第三层是语义分割对每个像素判断它是不是烧伤区域、属于哪个烧伤等级。很多标题里写的“检测算法”其实是分类和分割的混合体因为临床真正关心的往往是“严重程度分级”加“面积估计”这两件事分别靠分类和分割完成。我的建议是不要一上来就追边界框检测。烧伤区域的边界极不规则甚至同一片创面里不同深度区域交织在一起目标框会框进大量健康皮肤给后续面积计算带来偏差。项目起步阶段先把图像级分类跑通拿到一个可靠的分级基线如果还需要定位和面积再把分割模型加上去。这样做的另一个好处是分类标注比像素级标注便宜得多一个实习生用标注工具一天能标几百张图而像素级分割标注一张复杂的创面图可能要半小时。2.2 数据集准备与预处理路线烧伤图像数据集不太好找医院里的脱敏数据往往涉及隐私授权公开数据集又比较零散。常见的做法是先从公开皮肤图像库中筛选烧伤类别再补充自己团队采集的图像。无论来源是哪拿到原始图像后第一步永远是统一格式和清洗。把图像统一调整为统一尺寸去掉重复图、带水印的示意图、以及标注信息缺失的样本。这里有一个跟一般自然图像项目不一样的坑烧伤创面的颜色和形态与正常皮肤差异很大但渗液、结痂、纱布、消毒液染色都会制造大量假特征清洗时要把这些“伪样本”识别出来。预处理路线我一般这样走读取图像转RGB按短边比例缩放后中心裁剪到256x256或224x224再归一化到ImageNet均值方差。分类模型用224x224分割模型常用256x256或512x512越大越能保留创面纹理细节但显存占用也成倍涨。为了缓解样本不足离线做随机水平翻转、轻微旋转和颜色抖动就够了不要做太强的几何增强尤其是缩放和裁剪范围过大的话会把“小面积烧伤”切成“看起来没伤”的样本。2.3 用最小脚本把图片加载为训练张量无论后续用分类还是分割数据加载这一步是共用的。先按ImageFolder的结构整理目录再写一个标准的Dataset类。下面这个脚本是分类任务的数据读取骨架# dataset.py import os from PIL import Image from torch.utils.data import Dataset from torchvision import transforms class BurnDataset(Dataset): def __init__(self, root_dir, phasetrain): # 目录结构: root_dir/phase/类别名/图片.jpg self.phase phase self.samples [] # 每个元素是 (图片路径, 标签) for label, cls_name in enumerate(os.listdir(os.path.join(root_dir, phase))): cls_dir os.path.join(root_dir, phase, cls_name) for fname in os.listdir(cls_dir): self.samples.append((os.path.join(cls_dir, fname), label)) if phase train: self.transform transforms.Compose([ transforms.Resize((256, 256)), transforms.RandomHorizontalFlip(), transforms.ColorJitter(brightness0.2, contrast0.15), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]), ]) else: self.transform transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]), ]) def __len__(self): return len(self.samples) def __getitem__(self, idx): path, label self.samples[idx] image Image.open(path).convert(RGB) image self.transform(image) return image, label这里有两个参数值得说明。Resize((256, 256))是为了让后续CenterCrop(224)有裁切余地避免图像边缘的伪影被强行拉伸进模型视野如果图像原图比例差异很大直接Resize到正方形会改变创面的长宽形态影响分割类任务对面积的估计。ColorJitter(brightness0.2, contrast0.15)用来模拟不同光线下的拍摄条件但不能调太大否则会把烧伤红晕的颜色特征破坏掉。3. 用 PyTorch 实现烧伤检测先从分类基线做起再谈分割3.1 用 ResNet 二分类建基线训练脚本与参数说明拿到整理好的数据第一步永远是训练一个分类基线判断这个任务本身是否可学。烧伤严重程度分级可以视为多分类但如果你手里的数据只有“有无烧伤”的标注就先做二分类。多分类和二分类在代码上几乎没有差别唯一要注意的是损失函数和输出层维度。下面是一份可直接跑的训练脚本骨架基于ResNet18做二分类。选择ResNet18而不是更深的ResNet50是因为烧伤图像数据量通常不大深度网络容易过拟合而且推理速度对后续部署更友好。# train.py import torch import torch.nn as nn from torch.utils.data import DataLoader from torchvision import models from dataset import BurnDataset device torch.device(cuda if torch.cuda.is_available() else cpu) train_ds BurnDataset(./data, phasetrain) val_ds BurnDataset(./data, phaseval) train_loader DataLoader(train_ds, batch_size32, shuffleTrue, num_workers4) val_loader DataLoader(val_ds, batch_size32, shuffleFalse, num_workers4) model models.resnet18(weightsmodels.ResNet18_Weights.IMAGENET1K_V1) model.fc nn.Linear(model.fc.in_features, 2) # 二分类 model.to(device) criterion nn.CrossEntropyLoss() optimizer torch.optim.AdamW(model.parameters(), lr1e-4, weight_decay5e-4) scheduler torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max30) for epoch in range(30): model.train() total_loss, correct 0.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() total_loss loss.item() * images.size(0) correct (outputs.argmax(dim1) labels).sum().item() scheduler.step() train_acc correct / len(train_ds) print(fEpoch {epoch}: loss{total_loss/len(train_ds):.4f}, acc{train_acc:.4f})这段代码的关键参数有三个。lr1e-4是从ImageNet预训练权重微调时比较稳妥的学习率如果从头训练可以调到1e-3但烧伤数据量通常不支持从头训练。AdamW比传统Adam在医疗图像小数据上收敛更稳配合weight_decay5e-4能有效抑制过拟合。CosineAnnealingLR让学习率在30个epoch内从1e-4余弦下降到接近0相比固定学习率后期微调更充分。如果你的显存小于8GB把batch_size降到16学习率同步降到8e-5即可。3.2 升级到 U-Net 做烧伤区域分割损失函数与评估分类基线能告诉你“这张图有没有烧伤、有多严重”但临床场景通常还要求“烧伤区域到底在哪、占多大面积”。这个需求就要靠分割模型完成。常见做法是在U-Net基础上做二分类分割输入一张RGB图输出每个像素属于烧伤区域的概率图。因为烧伤区域内部往往还有深度差异更完整的方案是每个像素做多分类分割健康/一度/二度/三度但这个对标注质量要求极高建议先把烧伤区域二分类分割做稳。U-Net的PyTorch实现有很多现成版本核心结构是编码器压缩特征、解码器恢复分辨率并用skip connection融合细粒度信息。烧伤图像的特点是边界模糊、目标区域占比变化大——有的病例只有一小块水泡有的则是大面积烧伤。这时损失函数不能再只用交叉熵否则小面积烧伤区域会被整张图的“非烧伤”像素淹没。我常用的方案是Dice Loss和交叉熵按2:8加权混合# loss.py import torch import torch.nn as nn import torch.nn.functional as F class DiceCE_loss(nn.Module): def __init__(self, dice_weight0.2, ce_weight0.8): super().__init__() self.dice_weight dice_weight self.ce_weight ce_weight def forward(self, logits, targets): # logits: [B, C, H, W], targets: [B, H, W] 像素标签 ce F.cross_entropy(logits, targets) probs F.softmax(logits, dim1)[:, 1] # 取烧伤类别的概率 preds (probs 0.5).float() intersection (preds * targets.float()).sum() dice (2.0 * intersection 1.0) / (preds.sum() targets.float().sum() 1.0) dice_loss 1.0 - dice return self.ce_weight * ce self.dice_weight * dice_loss这里dice_weight和ce_weight的比例要按项目数据调整。如果烧伤面积普遍偏小把dice_weight提到0.3甚至0.4让模型更关注小目标区域如果标注噪声较大Dice Loss对边界像素的波动太敏感就降回0.1。加1.0平滑项是为了避免某个batch里完全没有烧伤目标时除零。评估分割结果时不要只看整体像素准确率因为大面积健康皮肤会让准确率虚高。我需要看IoU和Dice系数按烧伤类别单独计算。推理时输出概率图需要做后处理先用argmax得到像素类别再用开运算去掉细小噪点最后用连通域分析滤掉面积小于某个阈值比如全图0.5%的孤立区域。这一步能明显改善分割掩膜的视觉质量也有助于后续的面积统计。3.3 训练控制学习率、早停、归一化细节烧伤检测项目里我建议把早停Early Stopping和最佳模型保存作为标准配置。由于样本量小验证集指标在训练中后期往往剧烈波动不用早停的话很容易把最后一轮的过拟合模型当成最终产物。常见的做法是每个epoch结束在验证集上计算指标连续10到15个epoch没有提升就停止训练并保存验证指标最高时的权重。配合上一节的余弦退火调度器训练时长一般控制在30到50个epoch8GB显存单卡跑ResNet18大约两小时以内。归一化是另一个容易被忽略的细节。医疗图像不像自然图像那样统一由相机生成不同来源的图像可能在色调、亮度上差异很大。如果直接把原始图喂给网络模型很容易学到“亮度偏暗就是烧伤”这种伪规律。我建议在预处理阶段记录训练集的像素均值方差推理时用完全相同的归一化参数。如果项目里有红外相机或不同品牌手机拍的图归一化参数最好按设备来源分别统计或者做一个简单的白平衡预处理把图像整体色调拉回中性。注意临床场景下的模型评价必须跟使用场景绑定。如果目标是“辅助分诊”那么模型输出的是风险概率不是最终诊断评估时关注准确率和召回率的平衡如果目标是“烧伤面积估计”则要重点验证分割掩膜与医师勾画区域的重合度而不是分类准确率。4. 避坑指南烧伤检测项目里的 5 个高频翻车点4.1 翻车点一类别严重失衡模型把所有图都预测为“无烧伤”现象训练完的二分类模型在验证集上准确率高达95%但仔细一看几乎全部预测为“无烧伤”类别烧伤样本的召回率接近0。原因烧伤图像数据里健康皮肤或轻度创面样本往往远多于严重烧伤样本负样本占比可能超过90%。交叉熵损失在类别不平衡时会让网络倾向于学习“多数类更容易”的捷径输出置信度偏向负类。解决先统计训练集各类别样本数。如果正负比超过1:3不要只在损失函数上加权重优先做数据层面的处理——对少数类做离线增强生成更多角度的裁剪和翻转样本。同时把损失函数改为带类别权重的交叉熵nn.CrossEntropyLoss(weighttorch.tensor([1.0, 3.0]))其中权重按总样本数 / (类别数 * 该类别样本数)计算。另外验证时不要只看Accuracy加看每个类别的Precision和Recall必要时画混淆矩阵。4.2 翻车点二纱布、反光和阴影被当成烧伤区域现象分割模型的掩膜在正常皮肤上出现一堆零星小区域尤其集中在图像边缘和纱布边缘而真正的创面反而有漏检。原因烧伤创面常有湿润渗出液反光特性跟周围皮肤差异极大纱布和绷带的纹理也会被误认为“破损组织”。模型没有理解“烧伤”的语义只是学到了“这块颜色/纹理与周围不同”的低层特征。解决在训练数据里显式加入这些干扰样本并确保它们被标注为“非烧伤”背景。如果标注人员当时把这些区域也画成了前景那就是标注质量问题需要返工复查。另一个有效手段是在预处理里加高斯模糊或光照扰动削弱极端反光对模型的影响。对推理结果用形态学开运算去掉面积小于50像素的孤立区域也能过滤掉这类伪阳性。4.3 翻车点三模型只记住了肤色和背景换一组数据就失灵现象在自建小数据集上训练的分类模型验证集AUC能到0.95但拿到外部医院或网络公开图上测试准确率掉到0.6以下。原因这是医疗图像深度学习领域最典型的域偏移问题。采集设备的色彩风格、病人肤色、创面拍摄距离和角度都构成了模型潜在依赖的“环境特征”。烧伤检测模型的玄学之处就在于它可能通过背景床单的颜色或皮肤整体色调来分类而不是真正聚焦创面区域。解决先检查模型关注区域用Grad-CAM生成热力图看模型在推理时是否真的盯着创面。如果热力图集中在背景说明模型学到了不该学的关联。修复方法有两个方向一是做区域裁剪训练时随机从图像中裁取局部区域作为输入强制模型关注局部细节二是做色调归一化把训练集和测试集的图像统一转换到Lab色彩空间后作直方图匹配。这类问题没有一劳永逸的解法只能在每次换数据来源后重新评估。4.4 翻车点四数据泄漏训练集和验证集出现同一病人的重复图像现象验证集指标异常高比如分割Dice达到0.95但一到真实场景就明显退化。检查数据文件时发现同一个病人的不同拍摄角度照片一部分在训练集、一部分在验证集。原因很多公开烧伤数据集是按“图”而不是按“病人”划分的。同一个病人的创面在不同角度、不同时间拍摄的多张照片极其相似模型在训练时已经见过几乎相同的验证图属于典型的“黑匣子式幻觉指标”。解决处理医疗图像数据时无论如何都按“病人ID”划分训练、验证、测试集保证同一个病人的所有照片只在其中一个集合里出现。如果原始数据没有病人ID字段先对全部图像做感知哈希去重把相似度超过0.85的样本分到同一组。这是血泪经验项目组在这上面翻车的最多。4.5 翻车点五评估指标选错拿像素准确率糊弄分诊场景现象分割模型报告的像素准确率高达99%但手动查看预测掩膜创面边界处明显偏小医生认为不可用。原因像素准确率会被大面积健康皮肤“带飞”——如果烧伤区域只占图像的5%模型把所有像素都预测为健康皮肤准确率也能达到95%。临床关心的是“病人身上哪些地方烧伤了、占体表多少”这对应的是烧伤类别的IoU和Dice而不是整体准确率。解决评估分割结果时报告三个指标烧伤类别的IoU、Dice系数以及边界距离Hausdorff距离用来衡量预测边界与真实边界接近程度。如果Hausdorff距离过大说明模型对创面边缘的刻画不够精准需要增加边缘损失约束或者把输入分辨率从256提高到384。临床决策支持系统的验收底线应该是烧伤类别的召回率不低于90%同时IoU在0.75以上否则就继续优化。5. 进阶验证用连通域分析和形状因子过滤碎屑预测分割模型直接输出的掩膜几乎不可能一次就干净尤其是烧伤创面渗出液造成的反光碎片会让掩膜表面布满小孔和孤立噪点。最后一环的掩膜后处理直接决定这个项目落地时的“手感”。常见做法是组合三步条件膨胀填补小孔、连通域分析丢弃小区域、形状因子过滤狭长伪影。下面是我常用的后处理代码输入是模型输出的概率图输出是干净的二进制掩膜# postprocess.py import cv2 import numpy as np def clean_mask(prob_map, min_area_ratio0.002, kernel_size5): # prob_map: [H, W] float32范围0~1 binary (prob_map 0.5).astype(np.uint8) * 255 # 第一步开运算去噪闭运算补孔 kernel cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (kernel_size, kernel_size)) opened cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel, iterations2) closed cv2.morphologyEx(opened, cv2.MORPH_CLOSE, kernel, iterations3) # 第二步连通域分析按面积最小占比过滤 num_labels, labels, stats, _ cv2.connectedComponentsWithStats(closed, connectivity8) h, w prob_map.shape min_area int(h * w * min_area_ratio) cleaned np.zeros_like(closed) for i in range(1, num_labels): area stats[i, cv2.CC_STAT_AREA] if area min_area: cleaned[labels i] 255 # 第三步用轮廓周长面积比排除狭长伪影 contours, _ cv2.findContours(cleaned, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for cnt in contours: area cv2.contourArea(cnt) perimeter cv2.arcLength(cnt, True) if perimeter 0 and area / (perimeter ** 2) 0.01: cv2.drawContours(cleaned, [cnt], -1, 0, -1) return cleaned这段代码里min_area_ratio0.002表示面积小于整张图0.2%的连通域直接丢弃这个值适合中等分辨率图像如果图像是512x512相当于过滤掉小于约524像素的噪点。用作面积估算时要把掩膜像素数除以整张图的像素数再乘以对应部位的体表面积百分比——但这个换算系数需要医生参与标定算法工程师不要自行拍板。我现在的习惯是任何模型改动后都拿同一组“已知创面范围”的验证图重跑一遍后处理确认形态学算子的参数没有掩盖真实边界。希望这份弯路清单能帮你跳过几个大坑祝你的烧伤检测项目早日跑出可信的结果。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。