☰
改进U-Net实现轮毂X射线缺陷实时分割:卷积下采样与Dropout实践
2026/9/30 13:11:27 网站建设 项目流程

简介:基于U-Net卷积神经网络的轮毂缺陷分割是一份面向图像处理与深度学习研究者的技术论文PDF,针对铸造轮毂X射线图像中裂纹、缩孔等缺陷难以自动检测的问题,系统提出改进型U-Net分割方案。文中将原始U-Net最大池化操作替换为卷积下采样,并引入Dropout层以提升模型稳定性,同时完成缺陷区域标注、去均值、归一化、样本扩充等数据预处理流程。实验表明,改进网络在复杂轮毂X射线缺陷识别上DICE系数达到0.8554,SSIM系数为0.9655,单张识别耗时仅3毫秒,能够满足无损检测自动化需求。资源包大小815KB,共1个PDF文件,内容覆盖数据预处理细节、网络结构改动思路、实验指标分析与作者研究背景,适合机器学习、数据建模方向学习者理解U-Net在工业质检场景中的工程化落地。当前已有141人浏览学习,是一份可直接用于技术调研、方案设计或论文写作参考的实用资料。

1. 轮毂缺陷分割这件事,U-Net 为什么能轻轻松松做到 3 ms/张

轮毂在铸造过程中最容易出两类缺陷:裂纹和缩孔。这两样东西在 X 射线图像里往往只有几个像素宽,人眼盯屏幕连续看半小时就会疲劳漏检,而传统图像处理的阈值分割又根本抓不住这类低对比度目标——背景的铸件纹理和缺陷在灰度值上几乎重合。这篇论文给出的答案是 U-Net 卷积神经网络,更具体地说,是一个把最大池化换成卷积、再加了 Dropout 的改进 U-Net。整套方案的实测结果是 DICE 系数 0.8554、SSIM 系数 0.9655,单张图像推理只要 3 ms。对一个做工业视觉的人来说,这个数字意味着它已经能满足产线上的实时无损检测节奏。这篇笔记就围绕这个论文方案来拆:从网络改哪里、数据怎么处理、训练时看什么曲线,到部署时哪些环节容易翻车,一次讲透。

2. 先把网络结构立住:把 Max-Pooling 替换成卷积,加了 Dropout,理由是什么

2.1 U-Net 的骨架:编码器、解码器、跳跃连接缺一不可

U-Net 最早是给医学细胞图像分割设计的,它的特点一句话就能说清:左边编码器逐层下采样提取语义特征,右边解码器逐层上采样恢复空间分辨率,中间用跳跃连接把编码器每一层的特征图直接拼到解码器对应层上。这个跳跃连接是精华所在——下采样会丢边缘位置信息,而分割恰恰需要精确的边缘定位,跳跃连接相当于给解码器递了一张"高清地图",让它能在恢复尺寸的同时找回细节。

论文里用的 U-Net 整体是 27 层,左边收缩路径 4 个 ConvBlock,右边扩展路径 4 个 Up-ConvBlock,输入图像是 572×572。每个下采样块里,先用一个 2×2 卷积核做步长 2 的卷积来降采样,再接两个 3×3 卷积层,每层后面跟 ReLU 激活。特征通道数从 64 开始逐层翻倍,图像尺寸逐层减半,最后缩到 28×28。上采样路径则是反卷积把图放大一倍,特征通道数减一半,同时和左边对应的特征图做拼接,然后用两个 3×3 卷积去提炼特征。最后一层是 1×1 卷积加 Sigmoid,输出一张每个像素值都在 0 到 1 之间的概率图,值越大代表该像素属于缺陷的概率越高,完成缺陷和背景的二分类。

2.2 为什么要把最大池化替换成卷积:信息保留的差异在哪

原始 U-Net 的下采样用的是最大池化,论文把它换成了步长 2 的 2×2 卷积。这个改动的实际意义有两层。第一层是特征保留:最大池化只取窗口内最大的那个值,相当于把局部区域压缩成一个"最有把握的特征点",这在分类任务里没问题,但在分割任务里会丢掉目标的空间分布细节。卷积下采样是让网络自己学权重的下采样方式,虽然在运算量上比池化略大,但对缺陷这类小目标更友好。

第二层是感受野。池化本身没有可学习的参数,下采样的信息提取能力完全靠前面那层卷积撑着;而卷积下采样多了一层可学习的特征变换,等于在下采样的同时还在做一轮特征提取。这就是论文里 Loss 曲线对比能看到改进 U-Net 收敛更快、损失更小的直接原因——网络在同样的深度下做了更多有效计算,而不是简单地对特征图做"减半处理"。

我在自己的分割项目里做过对比,卷积下采样在小目标分割上的 DICE 提升一般在 2 到 5 个百分点之间,原理就在这——它保留的细节比池化多。

2.3 Dropout 的作用机制:它为什么能治样本少带来的过拟合

轮毂缺陷图像是典型的"样本稀缺"场景——缺陷种类多、形态变化大,但工厂能提供的带缺陷 X 光图就那么几百张。数据扩充能增加训练样本量,但扩充样本之间的相关性很高,模型照样容易记住训练集特有过拟合。Dropout 的引入就是冲这个问题去的。

训练时每个 Dropout 层设定一个概率 p,论文里取 p=0.5,也就是说每个神经元有 50% 的概率在当前批次被暂时丢弃,只保留计算得到的权重,下次迭代再参与更新。这样做的效果是,每次迭代都在训练一个"瘦身版"的 U-Net,权重分配不会过度依赖某一个节点,特征提取在各个通道之间更均匀。

这里有一个很多人忽略的工程细节:Dropout 只在训练阶段生效,推理阶段要把所有神经元参与计算,输出还要乘上保留概率做补偿(或者用 inverted dropout 让训练输出直接归一化)。PyTorch 里 nn.Dropout 默认就是 inverted dropout,即训练时自动除以保留概率,推理时直接传值即可,不需要手动调整。如果你是自己手写的 Dropout 逻辑,这个补偿一定要做,否则推理输出的概率图整体偏小,阈值怎么调都不对。

2.4 复现网络改造的关键代码:卷积下采样与 Dropout 怎么落

基于 PyTorch 搭一个改进 U-Net 的收缩块和扩展块,核心代码这样写:

import torch import torch.nn as nn class ConvBlock(nn.Module): """U-Net 收缩块:步长2的2x2卷积做下采样 + 两个3x3卷积 + ReLU""" def __init__(self, in_ch, out_ch, dropout_p=0.5): super().__init__() self.down_conv = nn.Conv2d(in_ch, out_ch, kernel_size=2, stride=2) self.conv1 = nn.Conv2d(out_ch, out_ch, kernel_size=3, padding=1) self.conv2 = nn.Conv2d(out_ch, out_ch, kernel_size=3, padding=1) self.relu = nn.ReLU(inplace=True) self.dropout = nn.Dropout2d(p=dropout_p) def forward(self, x): # 卷积下采样,替代原版最大池化 x = self.down_conv(x) x = self.relu(self.conv1(x)) x = self.relu(self.conv2(x)) # 在卷积层后加 Dropout,防止过拟合 x = self.dropout(x) return x

这里 down_conv 的 kernel_size=2、stride=2,已经把原特征图尺寸减半,所以外面不需要再套池化层。注意 padding 参数:3×3 卷积我用 padding=1 保持特征图尺寸不变,这样下采样只发生在 down_conv 这一层,尺寸变化路径清晰可控。

class UpConvBlock(nn.Module): """U-Net 扩展块:反卷积上采样 + 跳跃连接拼接 + 两个3x3卷积""" def __init__(self, in_ch, skip_ch, out_ch): super().__init__() # 转置卷积:图像尺寸放大一倍,通道数减半 self.up_conv = nn.ConvTranspose2d(in_ch, out_ch, kernel_size=2, stride=2) self.conv1 = nn.Conv2d(out_ch + skip_ch, out_ch, kernel_size=3, padding=1) self.conv2 = nn.Conv2d(out_ch, out_ch, kernel_size=3, padding=1) self.relu = nn.ReLU(inplace=True) def forward(self, x, skip_feat): x = self.up_conv(x) # 跳跃连接:编码器特征图与解码器特征图在通道维度拼接 x = torch.cat([x, skip_feat], dim=1) x = self.relu(self.conv1(x)) x = self.relu(self.conv2(x)) return x

拼接时要注意 skip_feat 的空间尺寸必须和上采样后的 x 完全一致,否则 torch.cat 直接报错。如果原图尺寸不是 2 的整数次幂倍数(比如 572×572 经过 4 次下采样变成 35.75),就要在预处理阶段把人家的尺寸统一改成 2 的幂次倍数,常见做法是 padding 到 576×576,或者直接 resize 到 572×572 然后让网络结构去适配。论文的输入正好是 572,经过四次减半落到 28×28 附近,说明他们做数据的时候已经把这个尺寸问题处理掉了。

Dropout2d 和 Dropout 的区别值得注意。图像分割用的是 Dropout2d,它按通道整体丢弃而不是按单个像素丢弃,也就是让某张特征图整体失效。这样做的合理性在于相邻像素高度相关,按像素随机丢弃对卷积特征提取的破坏没有按通道丢弃大。

3. 数据预处理全流程:从 Labelme 标注人到训练样本,四步不能省

3.1 感兴趣区域提取:为什么要先切 ROI 再训练

轮毂 X 射线原图分辨率很高,直接整图送进 U-Net 会带来两个问题:一是 GPU 显存扛不住大尺寸输入,二是缺陷区域在整张图里占比太小,负样本严重压制正样本的梯度贡献。论文的做法是用 Labelme 先把缺陷区域标注出来,提取感兴趣区域(ROI)之后再做训练和测试。

在实际做的时候,我要强调一点:Labelme 标出来的多边形不只是用来做标签图的,它同时定义了 ROI 的边界。你可以用标注框先裁出 ROI,再基于裁剪后的区域去建模,这样网络输入永远是"缺陷周围一小块",正负样本比例要健康得多。

import json import numpy as np import cv2 from labelme.utils import shape_to_mask # 读取 Labelme 标注的 JSON 文件 with open('defect_001.json', 'r', encoding='utf-8') as f: label_data = json.load(f) # 读取原始图像 orig_img = cv2.imread(label_data['imagePath'], cv2.IMREAD_GRAYSCALE) # 从 Labelme 多边形标注生成二值掩码 mask = shape_to_mask( (orig_img.shape[0], orig_img.shape[1]), label_data['shapes'][0]['points'], shape_type='polygon' ).astype(np.uint8) # 根据掩码边界框裁剪 ROI ys, xs = np.where(mask > 0) x_min, x_max = xs.min(), xs.max() + 1 y_min, y_max = ys.min(), ys.max() + 1 roi_img = orig_img[y_min:y_max, x_min:x_max] roi_mask = mask[y_min:y_max, x_min:x_max]

shape_to_mask 是从 labelme 工具包自带 utils 里导出的函数,它会把你手工点出来的多边形自动转成和原图同尺寸的二值掩码。边界框裁剪用的是掩码的最小外接矩形,这样 ROI 既不会引入太多无关背景,又不会把缺陷边缘切掉。如果缺陷形状是长条形的(裂纹),建议适当外扩几个像素,避免缺陷正好压在裁剪边界上。

3.2 去均值:让模型训练更快进入收敛区间

去均值就是把图像每个像素减去整个训练集的平均像素值,让数据各维度中心化到 0 附近。这样做的直接收益是训练初期梯度下降的方向更稳定——如果原始图像的灰度分布整体偏移,网络前几层要先去适配这个偏移量,白白浪费迭代次数。

在轮毂 X 射线图像的场景下,去均值还有一个隐含好处:不同批次的 X 射线图像可能因为曝光参数不同而整体偏亮或偏暗,去均值之后,这种设备层面的灰度漂移被压制,网络学习到的特征更集中在缺陷本身的纹理结构上。

# 计算整个训练集的平均灰度 mean_val = np.mean(np.stack([roi_img.astype(np.float32) for roi_img in train_imgs])) # 去均值:每个样本减去全局均值 train_imgs_centered = [img.astype(np.float32) - mean_val for img in train_imgs]

代码里注意两点:一是必须先统一裁剪后的 ROI 尺寸再算均值,否则不同尺寸图像堆叠会报错;二是去均值操作一定要用 float32 精度,uint8 减法会出现负数截断,等于白做。论文里提到去均值之后训练不容易过拟合,机理上说,中心化后的数据让权重更新的方差更小,模型对噪声的敏感度降低。

3.3 归一化:为什么说它能加快梯度下降收敛效率

归一化是把像素值缩放到 0 到 1 或 -1 到 1 的区间。X 射线图像的原始灰度范围是 0 到 255,而 U-Net 里的 Sigmoid 输出范围是 0 到 1,如果输入不归一化,网络第一层卷积的输入尺度就是输出尺度的几百倍,梯度传播容易震荡甚至爆掉。

常见的做法是除以 255 映射到 [0,1],但更推荐按训练集的均值方差做标准化:

# Z-Score 标准化 std_val = np.std(np.stack([img for img in train_imgs_centered])) train_imgs_norm = [(img - mean_val) / std_val for img in train_imgs_centered]

这样处理之后,输入数据的分布接近标准正态分布,配合 ReLU 激活函数时,神经元的输出不会频繁落入死亡区域。归一化和去均值要配套使用:先去均值再除以标准差,这是标准的 Z-Score 流程,缺一步效果都会打折。

3.4 样本扩充:数据不够,怎么用平移翻转凑出训练量

标注好的轮毂缺陷样本往往只有一两百张,喂给深度 U-Net 肯定过拟合。论文用随机平移和水平翻转做数据扩充——这些变换不改变缺陷的本质特征,只改变缺陷在图像中的位置和朝向,正好适合 X 射线图像这类目标姿态本来就没规律的应用场景。

import random import torch from torchvision import transforms train_transform = transforms.Compose([ transforms.RandomAffine( degrees=0, # 不旋转,轮毂缺陷方向有意义 translate=(0.05, 0.05), # 随机平移最多5% scale=(0.95, 1.05), # 轻微缩放 ), transforms.RandomHorizontalFlip(p=0.5), transforms.ToTensor(), # 转Tensor并归一化到[0,1] ])

这里我特意没开旋转,因为轮毂是铸件,裂纹方向往往和应力方向相关,旋转 90 度会生成物理上不存在的缺陷样本。这个细节是论文没提但实际训练时要注意的——数据增强想当然地全开,模型训练出来的精度是虚高的,现场一测就露馅。平移范围控制在 5% 也是为了保留缺陷在 ROI 内的完整性,平移量太大会把缺陷挪出边界导致标签错位。扩充后的训练集样本量建议至少是原始标注量的 8 到 10 倍。

在这套预处理流程里,最容易忽略的是最后一步:标签图要和输入图像做完全相同的变换。很多人只对原始图做平移翻转,标签掩码保持不动,训练出的模型分割位置整体错位,Loss 始终压不下去。用上面的 torchvision.transforms 方式写,图片和掩码要分别定义两次同样的变换流程,或者封装到一个自定义 Dataset 里同步处理。

4. 模型训练与评估:硬件配置、Loss 曲线和评价指标的工程含义

4.1 训练环境与网络输入尺寸的选择逻辑

论文给出的训练环境是 Intel i9-9900K CPU、NVIDIA GeForce RTX 2080Ti GPU、64 位 Windows 7 系统。这套配置放到现在看依然是中高端的深度学习工作台,RTX 2080Ti 有 11 GB 显存,训练 572×572 输入的 U-Net 完全够用,batch size 开到 8 到 16 没有压力。

输入尺寸 572×572 的选择有讲究。太大——比如 1024×1024——显存直接吃紧,训练速度掉到不可接受。太小——比如 256×256——裂纹这类细长缺陷可能只剩两三个像素宽,分割精度必然下降。572 是一个折中值,配合四层下采样正好压缩到 28×28,既不浪费显存又保住了缺陷的结构信息。

如果要在自己的机器上复现,显存小于 8 GB 的卡把输入尺寸降到 384×384,效果差距不大,但训练速度能快一倍。

4.2 Loss 曲线对比:改进网络为什么更平更稳

论文给出了原始 U-Net(Max-pooling)和改造 U-Net(Conv)的 Loss 曲线对比:改进版损失值更小、收敛更快、曲线更平稳。这个结果和网络结构改动是直接对应的。

改进版的 Loss 下降快,是因为卷积下采样在每一步都做了一次可学习的特征提取,梯度回传路径更丰富,有效信息在每个下采样层都被保留。原始池化下采样没有参数,下采样操作不产生梯度贡献,信息提取的压力全压在后面几层卷积上,收敛自然慢。

Loss 曲线更平稳这件事,Dropout 起了很大作用。Dropout 会让训练 Loss 在初期有一定抖动(因为每次丢弃的神经元不同),但迭代够多之后反而更平滑——防止了模型在某几个异常样本上过拟合导致的大幅波动。如果你训练时发现 Loss 曲线剧烈震荡,先检查是不是 Dropout 的 p 设得太高(0.7 以上),或者学习率设得太大导致权重更新步长过猛。

4.3 评价指标怎么读:DICE 和 SSIM 分别衡量什么

论文用了两个评价指标来量化分割精度。

DICE 系数是分割任务最常用的指标,衡量预测分割区域和真实标注区域的重叠程度。公式是 2 倍交集除以两个区域面积之和,取值在 0 到 1 之间,越高越好。论文训练集平均 DICE 0.8554,这个数字在缺陷分割领域算是相当不错——因为缺陷往往是不规则的细长形状,边界像素占比高,DICE 天然不容易刷到 0.9 以上。原始 U-Net 只有 0.7932,换成卷积下采样直接提升了 6 个百分点。

SSIM 是结构相似性度量,从亮度、对比度、结构三个维度比较两幅图像。论文里 SSIM 0.9655 远高于原版 U-Net 的 0.9176,这说明改进网络输出的分割图在整体结构上和人工标注非常接近——边缘定位准,伪影少。

4.4 推理速度 3 ms/张背后的工程账

3 ms/张的识别速度是在 RTX 2080Ti 上得到的。如果不算图像 IO 和预处理,纯网络推理 3 毫秒意味着产线上即使按每秒 5 张的速度连续过料,GPU 也只用不到 2% 的算力。真正卡脖子的往往不是网络推理,而是前端的图像采集和缺陷标注的 ROI 提取耗时。

这里要泼一盆冷水:3 ms 是论文理想条件下的数据,部署到现场生产环境时,需要考虑工业相机触发、图像传输、预处理流水线带来的额外延迟。常见的做法是把 U-Net 转成 TensorRT 的 FP16 引擎,推理速度还能再快一倍,代价是 DICE 会掉 0.5 到 1 个百分点,用在对精度要求不那么极端的场景完全划算。

5. 避坑与常见问题排查:轮毂缺陷分割复现路上的五个坑

5.1 标注样本太少,U-Net 训练直接崩掉

现象:训练到第 10 个 epoch 左右,训练 Loss 降到很低,但验证集 DICE 一直在 0.3 到 0.4 徘徊,明显过拟合。

原因:标注样本只有几十张,数据扩充量撑不起整个 U-Net 的参数量。U-Net 虽然结构比大型分类网络轻量,但 27 层的参数量依然是百万级,几十张训练样本只能让网络记住训练集本身。

解决:先把样本扩充规模翻到 10 倍以上再谈训练。如果扩充后 Loss 还在 0.1 以下而验证 DICE 上不去,加大 Dropout 概率到 0.6,或者给卷积层加权重衰减(weight decay 设为 1e-4)。再不行就考虑迁移学习,用公开数据集(如医学图像分割数据集)预训练 U-Net 编码器,再冻结前几层微调轮毂缺陷数据。

5.2 标签图和输入图像尺寸不一致,Loss 恒为常数

现象:训练 Loss 不下降,稳定在某个固定值附近。

原因:代码里对输入图和掩码分别做了 resize,但尺寸参数写得不一致。比如输入图是 572×572,掩码被 resize 成 256×256,U-Net 输出的 572×572 概率图和标签做损失计算时,PyTorch 的 BCELoss 不会报错,但逐像素比较的结果基本是随机的。

解决:在 Dataset 的getitem里 return 前加断言,强制检查 img.shape 和 mask.shape 完全一致。归一化、resize、旋转这些 transform 统一封装起来,输入图和标签图走同一个 pipeline,不要分开写两套流程。模板代码:

assert img_norm.shape == mask.shape, f"Image {img_norm.shape} and mask {mask.shape} mismatch"

5.3 Sigmoid 输出后直接二值化,边缘出现大量椒盐噪声

现象:概率图阈值取 0.5 后,缺陷边缘周围出现密集的小块噪点。

原因:X 射线图像噪声较高,缺陷边缘在像素级上的灰度过渡很宽,网络在这些区域的输出概率恰好在 0.5 附近徘徊,二值化之后就成了散点。

解决:别急着上一刀切的阈值。先对概率图做一次高斯滤波(kernel 3×3 或 5×5),再取阈值 0.5,边缘噪声能消掉大半。更稳的办法是用阈值 0.3 和 0.7 做双层判定:概率大于 0.7 的像素一定标记为缺陷,小于 0.3 的一定不是,中间地带用连通域分析配合形态学开运算决定归属。现场部署时我会先用测试集统计一个最优阈值,而不是默认 0.5。

5.4 训练 Loss 一直降不下去,问题出在数据标注质量

现象:Loss 下降到 0.3 左右就卡住无法继续下降,DICE 在 0.7 上下徘徊。

原因:Labelme 标注的缺陷边界不精确。多边形标注的边界是直边,裂纹的实际边缘是连续弧线,网络在标注误差大的样本上反复学习矛盾信息,Loss 下不去。

解决:标注时用 Labelme 的 brush 工具或圆点模式做边缘精细标注,而不是粗放地画多边形。标注完成后做一轮交叉复核,至少两个人独立标注同一批图像,计算标注之间的一致性(用 DICE 衡量),低于 0.9 的样本要重新标。这类脏数据宁可丢掉也不要灌进训练集,害处比益处大得多。

5.5 现场采集图和训练图灰度分布不同,模型精度断崖式下跌

现象:训练集上 DICE 0.85,换到现场新采集的轮毂 X 光图上直接掉到 0.5。

原因:现场的 X 射线成像参数(管电压、管电流、曝光时间)和实验室采集时的设置不一致,导致图像整体灰度分布偏移。模型虽然训练时做过归一化,但归一化的均值和标准差是训练集算出来的,新图分布不同,归一化后依然对不上。

解决:部署前先采集一组现场图,统计灰度均值方差,用现场数据重新计算归一化的 mean 和 std,替换掉训练时的统计值。如果现场图像亮度波动幅度很大(超过 ±20%),建议在预处理里加一步自适应直方图均衡化,把灰度分布强制拉到统一区间。这个操作要在训练集和测试集上同步做,否则又是一次分布不匹配。

6. 落地验证技巧:用 SSIM 和 DICE 的组合判断模型是否值得上线

6.1 DICE 分数与 SSIM 分数互为参照的意义

很多人在评估分割模型时只盯 DICE,但 DICE 有一个盲区:它只看区域重叠度,不关心分割结果在结构上是否合理。举个例子,一个模型输出的缺陷区域位置、大小、形状都和标注高度重合,但边缘整体向内缩了 2 个像素,DICE 可能只从 0.9 掉到 0.85,肉眼看很难分辨,此时 SSIM 会从 0.98 掉到 0.92,直接暴露边界退化。

所以在评估模型时,我用 DICE 和 SSIM 一起看,两个指标同时下降才说明模型真的退步了,单一指标波动大概率是标注差异或阈值微调引起的。论文里的评测瞒不住任何问题:改进 U-Net 的 DICE 0.8554 和 SSIM 0.9655 是同步提升的,这比单个指标好看更有说服力。

6.2 DICE 指标的重新标定:用测试集做阈值曲线扫描

import numpy as np def dice_score(pred_prob, gt_mask, threshold=0.5): pred = (pred_prob > threshold).astype(np.uint8) intersection = np.logical_and(pred, gt_mask).sum() union = pred.sum() + gt_mask.sum() return 2 * intersection / union if union > 0 else 1.0 # 测试集上扫描阈值,找最优 thresholds = np.arange(0.3, 0.7, 0.02) dice_scores = [np.mean([dice_score(model_prob[i], gt_mask[i], t) for i in range(len(model_prob))]) for t in thresholds] best_threshold = thresholds[np.argmax(dice_scores)]

这段代码做的事情:在测试集的概率图和标注图上,从 0.3 到 0.7 扫一遍阈值,找出让平均 DICE 最高的那个阈值,替换默认的 0.5。这样的好处是阈值选择有统计依据,而不是拍脑袋定。经过扫描选出的阈值通常比 0.5 低 0.05 到 0.1,因为缺陷像素的概率值集中在 0.6 附近,取 0.5 会把边缘区域的弱信号全部滤掉,DICE 白白丢一两个点。

6.3 模型泛化能力的快速自检:留出法之外的验证设计

轮毂缺陷分割的样本量决定了你不能像公开数据集那样做五折交叉验证,太奢侈。我一般会按缺陷类型分层留出验证集:裂纹样本留 10%,缩孔样本留 10%,保证验证集里两类缺陷都有。训练完成之后,把验证集的预测结果逐张看一眼,DICE 高的看边界细节,DICE 低的看模型到底把错标当成缺陷还是漏检。这种人工复核比任何指标都更能暴露模型的实际问题——通常你会发现,DICE 低的样本集中在细碎裂纹上,属于标注本身就存在分歧的图像,模型跟不上是正常的。

从模型设计到落地这段路,我最深的体会是:U-Net 的改动幅度不用很大——一个卷积替换池化、一层 Dropout,就能换来 6 个百分点的 DICE 提升,关键是把每个改动背后的场景逻辑想清楚,再配合数据预处理把干净样本喂进去。从那以后我每次训练分割模型,都强制自己在数据集层面走一遍"标注质量复核 → 扩充前向性检查 → 分布对齐"这三步,宁可慢半天,也不能让模型在脏数据上跑轮次。这套方法在轮毂缺陷分割上验证有效,换成其他工业视觉小目标分割场景的思路也完全通用,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询