✅博主简介:擅长数据搜集与处理、建模仿真、程序设计、仿真代码、论文写作与指导,毕业论文、期刊论文经验交流。
✅成品或者定制,扫描文章底部微信二维码。
(1) 光学零件表面缺陷数据采集与数据库建立
光学零件在精密加工和使用过程中容易产生多种类型的表面缺陷,这些缺陷会严重影响光学系统的成像质量和性能参数。建立完整覆盖各类缺陷形态的图像数据库是开展深度学习研究的首要前提。在数据采集环节,需要搭建专业的光学检测平台,采用高分辨率工业相机配合特定照明方案对光学零件表面进行成像。照明方式的选择对缺陷的可见性具有重要影响,通常采用暗场照明技术能够更清晰地呈现表面微小缺陷的形貌特征。
光学零件表面缺陷按照产生原因可分为制造缺陷和非制造缺陷两大类。制造缺陷主要包括划痕、麻点、崩边、气泡等类型,这些缺陷在加工过程中形成,具有相对规则的几何特征。划痕缺陷呈现为细长的线状结构,宽度和深度变化范围较大,在图像中表现为亮度异常的条带区域。麻点缺陷是分布于表面的点状凹坑,尺寸通常在微米到毫米量级,在暗场图像中呈现为离散的亮点。崩边缺陷发生在零件边缘位置,表现为不规则的缺损区域。气泡缺陷是材料内部的微小空腔在表面的投影,呈现为圆形或椭圆形的轮廓特征。非制造缺陷主要包括残痕和指印等类型,残痕是清洗或擦拭过程中残留的污渍痕迹,形状不规则且边界模糊。指印是人工操作过程中手指接触表面留下的油脂印记,呈现为特征性的指纹纹理图案。
数据采集过程中需要确保各类缺陷样本数量的相对均衡,避免严重的类别不平衡问题影响模型训练效果。对于自然发生频率较低的缺陷类型,可通过人工制造标准缺陷样本的方式进行补充。采集得到的原始图像需要经过质量筛选,剔除对焦不清、曝光异常等低质量图像。随后由专业技术人员对图像进行分类标注,建立缺陷类别与图像的对应关系。为验证标注的准确性,采用多人独立标注后取一致结果的方式,对于标注结果存在分歧的样本进行专家复核。最终建立的数据库包含数千张高质量缺陷图像,覆盖全部目标缺陷类型,为后续模型训练和评估提供可靠的数据支撑。
(2) 面向光学缺陷分类的多尺度混合卷积网络设计
光学零件表面缺陷的形态特征具有显著的多样性,同一类型缺陷在不同样本中可能呈现出差异较大的尺度和形状特征,而不同类型缺陷之间又可能存在相似的外观表现。这种类内变化大、类间差异小的特点对特征提取网络的设计提出了较高要求。为应对这一挑战,设计了基于多尺度混合卷积核的深度网络架构,通过并行使用不同尺度的卷积核来提取丰富的多尺度特征表示。
多尺度混合卷积模块的核心思想是在同一网络层内同时部署多个不同大小的卷积核,各卷积核分别负责提取特定尺度范围内的特征信息。小尺寸卷积核具有较小的感受野,能够精确捕获缺陷的局部细节特征,如划痕的边缘轮廓和麻点的中心区域。大尺寸卷积核具有较大的感受野,能够感知缺陷的整体形态结构和空间分布模式。将不同尺度卷积核提取的特征进行通道拼接,形成包含多尺度信息的综合特征表示,使网络能够适应缺陷尺度变化带来的挑战。
除多尺度混合卷积外,还设计了非对称混合卷积模块来增强网络对特定方向特征的提取能力。光学零件表面的划痕缺陷通常具有明显的方向性,沿某一方向呈现为细长的条带结构。传统的方形卷积核在各方向上具有相同的感受野范围,难以有效捕获这种各向异性的特征。非对称混合卷积模块使用长宽比不同的矩形卷积核,横向卷积核擅长提取水平方向的线性特征,纵向卷积核擅长提取垂直方向的线性特征,两者的组合能够覆盖各个方向的划痕缺陷。将非对称卷积核与标准方形卷积核并行使用,既保留对点状缺陷的检测能力,又增强对线状缺陷的识别能力。
网络的整体架构采用多阶段特征提取的方式,每个阶段由若干个混合卷积模块堆叠而成,阶段之间通过下采样操作逐步降低特征图的空间分辨率并增加通道数量。在网络末端连接全局平均池化层和全连接分类层,将提取的高级语义特征映射为各缺陷类别的预测概率。训练过程中使用交叉熵损失函数,并采用标签平滑等正则化技术防止模型过拟合。
(3) 基于结构重参数化与滤波器剪枝的模型轻量化方法
尽管多尺度混合卷积网络能够取得优异的缺陷识别精度,但其复杂的多分支结构会带来较高的计算开销,难以满足工业在线检测对实时性的要求。为解决精度与速度之间的矛盾,引入结构重参数化技术对训练完成的网络进行等效转换,在保持识别性能不变的前提下大幅提升推理速度。
结构重参数化的基本原理是利用卷积运算的线性可加性,将训练阶段的多分支结构在推理阶段合并为等效的单分支结构。以多尺度混合卷积模块为例,训练时并行使用多个不同尺寸的卷积核,推理时可将这些卷积核合并为一个较大的等效卷积核。具体做法是首先将各个小尺寸卷积核通过零填充扩展至相同的大尺寸,然后将扩展后的卷积核权重进行逐元素相加,得到等效的单一卷积核。批归一化层也可以与卷积层进行融合,将归一化操作的参数吸收到卷积核权重和偏置中。经过重参数化处理后,原本包含多个并行分支的模块被转换为单个标准卷积层,消除了多分支带来的额外内存访问和计算开销,显著提升实际推理速度。
import torch import torch.nn as nn import torch.nn.functional as F import numpy as np class MultiScaleMixedConv(nn.Module): def __init__(self, in_channels, out_channels): super(MultiScaleMixedConv, self).__init__() branch_channels = out_channels // 4 self.branch1 = nn.Conv2d(in_channels, branch_channels, 1) self.branch2 = nn.Conv2d(in_channels, branch_channels, 3, padding=1) self.branch3 = nn.Conv2d(in_channels, branch_channels, 5, padding=2) self.branch4 = nn.Conv2d(in_channels, branch_channels, 7, padding=3) self.bn = nn.BatchNorm2d(out_channels) def forward(self, x): return F.relu(self.bn(torch.cat([ self.branch1(x), self.branch2(x), self.branch3(x), self.branch4(x) ], dim=1))) class AsymmetricMixedConv(nn.Module): def __init__(self, in_channels, out_channels): super(AsymmetricMixedConv, self).__init__() branch_channels = out_channels // 3 self.branch_h = nn.Conv2d(in_channels, branch_channels, (1, 5), padding=(0, 2)) self.branch_v = nn.Conv2d(in_channels, branch_channels, (5, 1), padding=(2, 0)) self.branch_s = nn.Conv2d(in_channels, out_channels - 2 * branch_channels, 3, padding=1) self.bn = nn.BatchNorm2d(out_channels) def forward(self, x): return F.relu(self.bn(torch.cat([ self.branch_h(x), self.branch_v(x), self.branch_s(x) ], dim=1))) class M2Block(nn.Module): def __init__(self, in_channels, out_channels, use_asymmetric=True): super(M2Block, self).__init__() self.ms_conv = MultiScaleMixedConv(in_channels, out_channels) self.use_asymmetric = use_asymmetric if use_asymmetric: self.asym_conv = AsymmetricMixedConv(out_channels, out_channels) def forward(self, x): x = self.ms_conv(x) if self.use_asymmetric: x = self.asym_conv(x) return x class M2Net(nn.Module): def __init__(self, num_classes=7): super(M2Net, self).__init__() self.stage1 = nn.Sequential( M2Block(3, 64), M2Block(64, 64), nn.MaxPool2d(2, 2) ) self.stage2 = nn.Sequential( M2Block(64, 128), M2Block(128, 128), nn.MaxPool2d(2, 2) ) self.stage3 = nn.Sequential( M2Block(128, 256), M2Block(256, 256), nn.MaxPool2d(2, 2) ) self.stage4 = nn.Sequential( M2Block(256, 512), M2Block(512, 512), nn.AdaptiveAvgPool2d(1) ) self.classifier = nn.Linear(512, num_classes) def forward(self, x): x = self.stage1(x) x = self.stage2(x) x = self.stage3(x) x = self.stage4(x) x = x.view(x.size(0), -1) return self.classifier(x) def fuse_conv_bn(conv, bn): fused_conv = nn.Conv2d( conv.in_channels, conv.out_channels, conv.kernel_size, conv.stride, conv.padding, conv.dilation, conv.groups, bias=True ) w_conv = conv.weight.data.clone() w_bn = bn.weight.data.clone() running_mean = bn.running_mean.data.clone() running_var = bn.running_var.data.clone() b_bn = bn.bias.data.clone() eps = bn.eps std = torch.sqrt(running_var + eps) fused_conv.weight.data = w_conv * (w_bn / std).view(-1, 1, 1, 1) fused_conv.bias.data = b_bn - running_mean * w_bn / std return fused_conv class L1FilterPruner: def __init__(self, model, prune_ratio=0.3): self.model = model self.prune_ratio = prune_ratio def compute_filter_importance(self, conv_layer): weights = conv_layer.weight.data.abs() importance = weights.sum(dim=(1, 2, 3)) return importance def prune_layer(self, conv_layer, bn_layer, keep_ratio): importance = self.compute_filter_importance(conv_layer) num_filters = conv_layer.out_channels num_keep = int(num_filters * keep_ratio) _, indices = torch.sort(importance, descending=True) keep_indices = indices[:num_keep] new_conv = nn.Conv2d( conv_layer.in_channels, num_keep, conv_layer.kernel_size, conv_layer.stride, conv_layer.padding, bias=conv_layer.bias is not None ) new_bn = nn.BatchNorm2d(num_keep) new_conv.weight.data = conv_layer.weight.data[keep_indices] if conv_layer.bias is not None: new_conv.bias.data = conv_layer.bias.data[keep_indices] new_bn.weight.data = bn_layer.weight.data[keep_indices] new_bn.bias.data = bn_layer.bias.data[keep_indices] new_bn.running_mean.data = bn_layer.running_mean.data[keep_indices] new_bn.running_var.data = bn_layer.running_var.data[keep_indices] return new_conv, new_bn, keep_indices def train_defect_classifier(model, train_loader, epochs=50, lr=1e-3): device = torch.device("cuda" if torch.cuda.is_available() else "cpu") model = model.to(device) criterion = nn.CrossEntropyLoss(label_smoothing=0.1) optimizer = torch.optim.AdamW(model.parameters(), lr=lr, weight_decay=1e-4) scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=epochs) for epoch in range(epochs): model.train() total_loss, correct, total = 0.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() _, predicted = outputs.max(1) correct += predicted.eq(labels).sum().item() total += labels.size(0) scheduler.step() print(f"Epoch {epoch+1}: Loss={total_loss/len(train_loader):.4f}, Acc={100.*correct/total:.2f}%") if __name__ == "__main__": model = M2Net(num_classes=7) dummy_input = torch.randn(1, 3, 224, 224) output = model(dummy_input) print(f"Output shape: {output.shape}")如有问题,可以直接沟通
👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇