基于机器学习的喷码缺陷检测Python源码:MobileNetV3-Small实战与调优
简介这份资源是面向高校学生与机器学习入门者的喷码缺陷检测完整项目源码可直接用于毕业设计、课程设计或期末大作业。项目以Python实现围绕工业喷码字符的缺陷识别展开涵盖数据预处理、模型训练与评估等环节适合具备一定深度学习基础、希望快速完成课题的同学参考。压缩包共208个文件约156.86MB包含55张jpg与26张png图像样本、41个csv训练日志与指标记录、12个json配置、12个pdparams与11个pdopt模型权重、11个yml参数文件以及11个py源码脚本另附ttf字体与md说明文档结构完整、层次清晰。目前已有135人学习下载。项目已通过导师指导并获高分评价下载即用无需修改读者可据此复现训练流程、分析损失曲线与评估结果并在此基础上调整网络结构或数据增强策略快速形成可展示、可答辩的完整方案。1. 喷码缺陷检测这套源码到底能不能直接跑通喷码缺陷检测在产线上是个很实际的问题喷码机在包装袋、瓶盖、纸盒上喷印生产日期、批号、二维码喷歪了、缺笔画、重影、墨点飞溅都算缺陷。人工目检速度跟不上漏检率还高所以用机器学习做二分类或异常检测是很自然的思路。这份「基于机器学习的喷码缺陷检测python源码」是一个已经跑通并整理成毕业设计形态的项目包里面包含训练日志、逐样本的预测结果 CSV以及配套的 Python 源码。它适合两类人一类是正在做计算机毕业设计、需要一份能运行、有完整实验记录的项目另一类是刚接触工业视觉、想找一个轻量级缺陷检测流程练手的工程师。项目本身不追求 SOTA追求的是流程完整、结果可复现这一点从它保留了 VisualDL 的 scalar 日志和逐图 CSV 就能看出来。2. 从日志和 CSV 反推模型结构MobileNetV3-Small 的选型逻辑2.1 为什么是 MobileNetV3-Small 而不是 ResNet喷码缺陷检测的输入通常是产线相机拍的小图分辨率不会太大缺陷区域往往只占画面一小块。这种任务有两个特点样本量有限、推理要快。ResNet50 这类骨干在 ImageNet 上很强但参数量大在小数据集上容易过拟合部署到边缘设备也吃力。MobileNetV3-Small 是轻量级骨干里比较均衡的选择它用深度可分离卷积把计算量压下来又通过 SE 模块和 h-swish 激活补回一部分精度。项目里 VisualDL 日志文件名直接带了mobilenetv3_small说明训练时用的就是这个小模型。对于毕业设计场景这个选择的好处是训练一轮快笔记本上就能跑完答辩时演示推理也不卡。2.2 日志文件里能读出什么项目正文里列出的visualdl-scalar-output_mobilenetv3_small_vdl_log--Metrics_Training(Step)_ loss.csv是 VisualDL 导出的训练损失曲线数据。VisualDL 是百度飞桨生态里的可视化工具但它的 scalar 日志本质就是 step 和 value 两列跟 TensorBoard 的 event 文件类似。这个 CSV 的价值在于它记录了每个训练 step 的 loss你可以直接画曲线判断模型有没有收敛、有没有震荡。常见做法是用 pandas 读进来按 step 做滑动平均看趋势。import pandas as pd import matplotlib.pyplot as plt # 读取 VisualDL 导出的 scalar CSV # 注意VisualDL 导出的 CSV 可能没有表头需要根据实际列数指定 df pd.read_csv( visualdl-scalar-output_mobilenetv3_small_vdl_log--Metrics_Training(Step)_ loss.csv, headerNone, names[step, loss] ) # 按 step 排序防止导出顺序错乱 df df.sort_values(step).reset_index(dropTrue) # 滑动平均窗口 20用来平滑曲线看趋势 df[loss_smooth] df[loss].rolling(window20, min_periods1).mean() plt.figure(figsize(10, 4)) plt.plot(df[step], df[loss], alpha0.3, labelraw loss) plt.plot(df[step], df[loss_smooth], labelsmoothed loss) plt.xlabel(step) plt.ylabel(loss) plt.legend() plt.tight_layout() plt.savefig(loss_curve.png, dpi150)这段代码的逻辑很直接先把日志读成两列排序后做滑动平均。参数上window20是我一般会用的经验值太小了平滑不够太大了会把真实的下降趋势抹平。如果你发现 raw loss 抖动特别厉害先别急着调模型检查一下 batch size 是不是太小或者学习率是不是偏高。min_periods1是为了让前 19 个点也有值不然曲线开头会断一截。2.3 逐样本 CSV 的字段含义与用途正文里还有一堆picture_19.csv、picture_26.csv这样的文件这是逐张图片的预测结果导出。常见做法是每张图一个 CSV里面记录该图在每个类别上的概率或者记录真实标签和预测标签。这类文件在毕业设计里很有用你可以拿它算混淆矩阵、画 ROC 曲线、找误检样本。我一般会写个脚本把所有 CSV 合并成一张大表方便后续分析。import glob import os import pandas as pd # 匹配所有 picture_*.csv files sorted(glob.glob(picture_*.csv)) records [] for f in files: # 从文件名提取图片编号例如 picture_19.csv - 19 img_id int(os.path.basename(f).replace(picture_, ).replace(.csv, )) tmp pd.read_csv(f) tmp[image_id] img_id records.append(tmp) # 合并成一张总表 all_df pd.concat(records, ignore_indexTrue) all_df.to_csv(all_predictions.csv, indexFalse) # 快速看下每个类别的样本数分布 print(all_df.head()) print(all_df[pred_label].value_counts() if pred_label in all_df.columns else all_df.columns)这里的关键参数是glob.glob(picture_*.csv)的匹配模式如果你的文件命名不是这个规则要相应改。合并之后image_id就是每张图的唯一标识后面做误检分析时可以直接定位到具体是哪张图。注意如果 CSV 里没有表头pd.read_csv会把第一行当数据这时候要加headerNone并手动指定列名。这个坑我在第一次处理 VisualDL 导出文件时就踩过loss 列里混进了一个字符串画图直接报错。3. 把源码跑起来环境配置与训练推理全流程3.1 环境依赖与版本对齐这类毕业设计项目通常是在特定版本下跑通的直接装最新版反而容易翻车。常见做法是看项目里有没有requirements.txt有就按它来没有就根据 import 语句反推。喷码缺陷检测一般会用到 PyTorch 或 PaddlePaddle、OpenCV、Pillow、numpy、pandas、matplotlib可能还有 scikit-learn 用来算指标。我一般会先建一个干净的虚拟环境避免和系统里的包打架。# 创建虚拟环境Python 版本建议 3.8 或 3.9太新了有些老包装不上 python -m venv venv_pm # 激活环境 # Windows: venv_pm\Scripts\activate # Linux / macOS: source venv_pm/bin/activate # 安装核心依赖版本按项目实际调整 pip install torch1.13.1 torchvision0.14.1 pip install opencv-python4.7.0.72 pip install pandas matplotlib scikit-learn pillow参数说明torch1.13.1只是我常用的一个稳定版本如果你的项目用的是 Paddle就换成paddlepaddle。关键是不要盲目pip install torch装最新版因为 MobileNetV3 的预训练权重加载接口在不同版本间有过变动。如果你在加载权重时报KeyError或size mismatch八成是版本对不上。3.2 数据目录结构与标签组织喷码缺陷检测的数据集一般按类别分文件夹比如defect和normal或者更细的missing、smudge、offset。项目里没有明确给出数据目录但按常见做法我会把数据整理成 ImageFolder 能直接读的结构dataset/ train/ normal/ img_001.jpg img_002.jpg defect/ img_003.jpg img_004.jpg val/ normal/ ... defect/ ...这样用torchvision.datasets.ImageFolder就能自动生成标签不用自己写映射。注意如果两类样本数量差很多比如正常 2000 张、缺陷 200 张直接训练会让模型偏向多数类。常见做法是给 DataLoader 加WeightedRandomSampler或者在 loss 里设class_weight。这个点在毕业设计答辩时经常被问到提前处理好能加分。3.3 训练脚本的关键参数训练部分的核心是配置学习率、batch size、epoch 和优化器。MobileNetV3-Small 微调时我一般会把骨干的学习率设小一点分类头设大一点用分组参数的方式。import torch import torch.nn as nn from torchvision import models # 加载预训练 MobileNetV3-Small model models.mobilenet_v3_small(pretrainedTrue) # 替换分类头二分类输出 2 类 in_features model.classifier[3].in_features model.classifier[3] nn.Linear(in_features, 2) # 分组学习率骨干小分类头大 backbone_params [p for n, p in model.named_parameters() if classifier not in n] head_params [p for n, p in model.named_parameters() if classifier in n] optimizer torch.optim.Adam([ {params: backbone_params, lr: 1e-4}, {params: head_params, lr: 1e-3} ], weight_decay1e-4) criterion nn.CrossEntropyLoss()逻辑说明pretrainedTrue会下载 ImageNet 预训练权重如果网络不通可以提前把权重文件下好放到缓存目录。分组学习率的好处是防止预训练特征被大学习率破坏。weight_decay1e-4是常见的正则化强度如果你发现训练 loss 降得很快但验证 loss 不降可以适当加大。batch size 我一般从 16 或 32 起步显存不够就往下调但不要低于 8否则 BN 层统计量会不稳。3.4 推理与结果导出推理阶段要把模型切到 eval 模式关掉 dropout 和 BN 的更新。逐图导出 CSV 时记得把 softmax 后的概率也存下来方便后面算 AUC。model.eval() results [] with torch.no_grad(): for img_path, img_tensor in val_loader: output model(img_tensor) prob torch.softmax(output, dim1) pred torch.argmax(prob, dim1) for i in range(len(img_path)): results.append({ image: img_path[i], pred_label: pred[i].item(), prob_normal: prob[i][0].item(), prob_defect: prob[i][1].item() }) pd.DataFrame(results).to_csv(inference_result.csv, indexFalse)参数说明torch.no_grad()必须加不然显存会爆。prob_defect这一列是后续调阈值的依据——默认 argmax 相当于阈值 0.5但工业场景里漏检代价高通常会把阈值调低比如 0.3 就判缺陷宁可误报也不漏报。这个阈值怎么选要看你的 PR 曲线后面第 5 章会讲。4. 避坑与排查喷码缺陷检测里最容易翻车的五个点4.1 现象训练 loss 正常下降但验证集准确率一直 50% 左右原因最常见的是标签映射错了。ImageFolder 是按文件夹名首字母排序生成类别索引的defect和normal排出来defect是 0、normal是 1但你在推理时可能按自己以为的顺序解读导致正负颠倒。另一个可能是数据泄漏训练集和验证集里有同一张图的副本。解决打印dataset.class_to_idx确认映射关系并在划分数据前先做去重。我一般会用 MD5 对图片做一次哈希确保没有重复图跨集。4.2 现象加载预训练权重时报size mismatch for classifier原因MobileNetV3-Small 原版分类头输出 1000 类你改成了 2 类如果先加载权重再改分类头或者改完分类头又去加载原版权重就会报这个错。解决先models.mobilenet_v3_small(pretrainedTrue)再替换classifier[3]。如果你已经保存了改过头部的 checkpoint加载时用strictFalse但要注意这样会跳过不匹配的层可能掩盖其他问题。4.3 现象VisualDL 日志读进来画图报ValueError: could not convert string to float原因VisualDL 导出的 CSV 有时会在开头或结尾带一行非数值内容比如标题行或者空行。直接pd.read_csv会把这一行当数据。解决加error_bad_linesFalse旧版 pandas或on_bad_linesskip新版或者先手动打开 CSV 看一眼结构。我现在的习惯是读任何日志文件前先head -5看一下这个动作帮我省过很多时间。4.4 现象推理时 GPU 显存够但速度很慢原因没有用torch.no_grad()或者 DataLoader 的num_workers设成了 0数据加载成了瓶颈。另外如果输入图片分辨率远大于训练时的尺寸计算量会成倍增加。解决推理一定包在torch.no_grad()里num_workers设成 CPU 核数的 2 到 4 倍推理前把图片 resize 到和训练一致的尺寸。MobileNetV3-Small 在 224x224 下单张推理在 CPU 上也就几十毫秒如果慢得离谱先查这三点。4.5 现象缺陷样本召回率很低但准确率看着还行原因类别不平衡。正常样本远多于缺陷样本时模型只要全判正常就能拿到很高的准确率但缺陷一个没抓到。这是工业检测里最危险的假象。解决不要只看准确率要看召回率和 F1。训练时加WeightedRandomSampler或者用 focal loss。推理时把缺陷判定阈值从 0.5 降到 0.3 甚至 0.2用 PR 曲线找平衡点。这个点我在第一次做产线项目时吃过亏答辩时被老师追问召回率幸好提前补了。5. 进阶技巧用逐图 CSV 做阈值调优和误检归因5.1 从概率到决策PR 曲线找最佳阈值项目里导出的逐图 CSV 存了每张图的prob_defect这就是调阈值的原材料。默认 argmax 等价于阈值 0.5但工业场景里漏检和误检的代价不对称。我一般会遍历 0.1 到 0.9 的阈值算每个阈值下的精确率和召回率然后画 PR 曲线。import numpy as np import pandas as pd from sklearn.metrics import precision_recall_curve, f1_score df pd.read_csv(inference_result.csv) # 假设真实标签列叫 true_label没有的话需要从文件名或目录反推 y_true df[true_label].values y_prob df[prob_defect].values precision, recall, thresholds precision_recall_curve(y_true, y_prob) # 找 F1 最大的阈值 f1_scores 2 * precision * recall / (precision recall 1e-8) best_idx np.argmax(f1_scores) best_threshold thresholds[best_idx] if best_idx len(thresholds) else 0.5 print(fbest threshold: {best_threshold:.3f}, F1: {f1_scores[best_idx]:.3f})参数说明precision_recall_curve返回的 thresholds 长度比 precision 少 1所以索引要小心。1e-8是防止除零。如果你更看重召回率可以不取 F1 最大而是取召回率达标前提下精确率最高的点。这个阈值最终要写进推理脚本替换掉默认的 0.5。5.2 误检归因把错分样本捞出来看光有阈值还不够得知道模型错在哪。我一般会把假阳性和假阴性分别导出按概率排序然后人工看几张。样本类型定义排查方向假阳性真实正常预测缺陷看是否有反光、阴影被误认为缺陷假阴性真实缺陷预测正常看缺陷是否太细微或训练集里这类缺陷太少高置信错误概率接近 0 或 1 但判错大概率是标签错了优先复查# 捞假阴性真实缺陷但预测正常 fn df[(df[true_label] 1) (df[pred_label] 0)] fn fn.sort_values(prob_defect) # 概率越低越离谱 fn.to_csv(false_negative.csv, indexFalse) # 捞假阳性 fp df[(df[true_label] 0) (df[pred_label] 1)] fp fp.sort_values(prob_defect, ascendingFalse) fp.to_csv(false_positive.csv, indexFalse)这个动作看起来简单但非常有用。我做过的一个项目里假阳性集中在某几个批次的图片上一看原来是那批包装袋换了材质反光特性变了。这种问题不看错分样本根本发现不了。5.3 数据增强的边界别把缺陷增没了喷码缺陷检测做数据增强要特别小心。随机裁剪、旋转、翻转对正常样本没问题但对缺陷样本翻转可能把「缺笔画」变成另一种缺陷裁剪可能直接把缺陷区域裁掉。我一般只对正常样本做较强的增强缺陷样本只做轻微的亮度、对比度扰动。另外喷码是有方向性的上下翻转通常不合理左右翻转也要看具体字符。这个边界如果没把握好训练集里缺陷样本被增强得面目全非模型学到的特征就是错的。从那以后我每次做缺陷检测项目都会先把增强后的缺陷样本可视化一批出来看确认缺陷还在、还可辨认再开始训练。希望帮到你。本文还有配套的精品资源点击获取