尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于YOLO的表面缺陷检测与可视化监管系统实战

基于YOLO的表面缺陷检测与可视化监管系统实战 简介面向深度学习与机器视觉方向的毕业设计这套源码提供了基于深度学习的表面缺陷检测与可视化监管系统的完整实现适合Python开发者、高校本科生及研究生参考可快速搭建缺陷检测模型并对检测结果进行可视化监管。压缩包共241个文件包含Python源码、模型权重pth、yaml/json配置文件、pickle数据文件、ui界面及html/css/js前端资源还有大量bmp/png/jpg实验图像总计约163.69MB目录结构清晰便于按模块阅读和复用。已有661人学习下载。项目代码完整下载即可运行内容涵盖模型训练、推理检测、监控可视化等环节另附ipynb示例、说明文档与训练日志可帮助理解缺陷检测流程、模型调参与系统部署思路适合作为高分毕业设计项目直接参考或二次开发。1. 拿到这个源码包先别急着解压它到底解决哪一类质检难题表面缺陷检测是工业质检里的老问题人工肉眼在流水线上看金属表面划痕、麻点、脏污看久了眼睛就花漏检率直线上升。用深度学习做自动检测本质上是目标检测问题计算机在图像里圈出缺陷的位置和类别。而可视化监管系统是把检测结果、统计报表、实时报警放到Web页面上让车间管理者不用一直盯着屏幕也能掌握整条产线的情况。这个源码包如果拆开看无非就是训练端、检测服务端、展示端三块但它恰好覆盖了深度学习毕业设计最容易出彩的两个点能跑模型能看结果。它适合三类人毕设选了表面缺陷检测方向的学生、想快速在本地跑通一套检测demo的工程师、还有准备用python做质检方向预研的技术人员。拿到源码的第一件事不是解压运行而是先把技术路线看懂否则连python环境配置都能把你劝退。2. 深度学习系统骨架模型选型、数据流与跑通前的环境配置2.1 缺陷检测模型选型为什么几乎所有毕设都落在YOLO系表面缺陷检测在工业现场大致分为两类任务第一类是判断这个工件有没有缺陷第二类是找出缺陷在哪。如果只需要前者用ResNet、EfficientNet做一个二分类就够了但毕业设计通常要展示定位能力因为缺陷位置对后续工艺改进很重要。目标检测恰好能同时输出类别和位置所以CNN目标检测模型成了主流。那Faster R-CNN、SSD、YOLO那么多为什么大家最终都选了YOLO我给你的回答不复杂就是均衡。Faster R-CNN精度高但慢Mask R-CNN能做像素级分割但训练成本高YOLO系列在速度和精度之间找到平衡点而且社区资料多到爆炸。下面这个表格是我平时选型的参考不是标准答案但能帮你理解差距。模型推理速度精度显存占用实现难度适合场景YOLOv5/v8快中高中等低产线实时检测、毕设首选Faster R-CNN慢高高中精度优先、不追求实时Mask R-CNN慢高分割高高需要像素级缺陷轮廓EfficientDet中中中中算力受限我一般会选YOLOv5而不是最新版原因很朴实v5的坑基本都被人踩过了任何报错都能在站内搜到答案。等把v5这条路走通再去换v8也不是难事数据配置和训练流程一脉相承。如果有同学想做点创新的可以在YOLOv5的neck或loss上动手脚但要确保自己看得懂答辩时最怕的是用了一个别人都没见过的网络结构还没法解释。这里要插一句如果你在查资料时看到“点云图金属表面缺陷检测”这个词那是另一个分支用激光或结构光获取三维点云再分析缺陷数据获取成本极高普通毕设拿不到那么贵的数据。绝大部分毕业设计用的都是2D图像数据集比如NEU-DET带钢表面缺陷数据集包含裂纹、夹杂、斑块、麻点、氧化铁皮压入、划伤六类缺陷。先把2D目标检测吃透3D不过是换一种输入模态特征提取和检测的核心思路是一样的。2.2 数据流与目录结构写代码前先把架子搭对这类毕业设计源码包我经手过好几个核心模块通常是固定的训练脚本、模型定义、检测服务、Web前端。它们之间的数据流是这样的生产线相机抓拍一张原始图像图像送到检测服务服务用训练好的权重做推理得到缺陷类别、置信度和边框坐标然后把坐标画到图上结果写入数据库前端每秒钟拉一次最新结果并渲染。所以在你急着跑demo之前先把目录结构理顺。我一般会这样规划这也是最不容易返工的一种排列defect_system/ ├── data/ │ ├── images/ # 原始缺陷图像 │ ├── annotations/ # VOC格式XML标注 │ ├── labels/ # 转换后的YOLO txt标注 │ └── defect.yaml # 数据集配置文件 ├── models/ │ └── best.pt # 训练好的权重 ├── scripts/ │ ├── voc2yolo.py # 标注格式转换 │ └── train.sh # 训练脚本 ├── web/ │ ├── app.py # Flask检测服务 │ ├── templates/ │ └── static/ ├── uploads/ # 接收上传图片 └── requirements.txt注意data和models放同级目录不要嵌套太深。之前见过有人把标注文件放在桌面路径下训练脚本读不到最后排查半天居然是路径分隔符写成了反斜杠在Windows下能跑换到Linux全挂。关于路径有一条铁律项目里所有路径要么写相对路径要么基于项目根目录拼接不要写死绝对路径。因为答辩时要在老师电脑上跑你的/Users/yourname/xxx到别人机器上就是灾难。目录结构确定之后你还会看到一些公开的源码会在data目录之外单独放一个runs目录这是YOLO默认的输出目录用来存放每次训练的日志和权重。不要手欠去删后面分析训练结果靠它。2.3 环境配置最容易让新手在第一步翻车的环节很多下载这个源码包的同学第一反应是双击运行然后被一连串ModuleNotFoundError打懵。这很正常因为表面缺陷检测的深度学习环境不是装了python就行你需要PyTorch、OpenCV、NumPy、Pandas这一串版本还互相咬合。我按python安装教程的思路给你整理了一个我反复用过的环境配置顺序照着做能少踩一半坑conda create -n defect python3.8 -y conda activate defect pip install torch1.13.1cu117 torchvision0.14.1cu117 --extra-index-url https://download.pytorch.org/whl/cu117 pip install -r requirements.txt先解释第一个命令conda创建名为defect的独立环境python指定3.8。我知道Python已经出到3.12了但深度学习生态不全都跟得上很多依赖库的wheel包还是针对3.8到3.10用3.8最稳妥。如果你机器上连conda都没有先去装Anaconda这个过程本身不难但要注意安装路径不能带空格否则后面一些底层库会莫名其妙报错。第二个命令是安装GPU版PyTorchcu117表示CUDA 11.7。如果你的电脑是纯CPU没有NVIDIA显卡那就把--extra-index-url那段去掉但训练速度会慢到让你怀疑人生。这套命令适合LinuxWindows下把conda activate换成activate defect其余不变。注意pip安装时如果提示certificate verify failed多半是公司网络拦截临时加--trusted-host pypi.org --trusted-host download.pytorch.org可以绕过。第三个命令里的requirements.txt如果源码包里没有就自己建一个。最少需要opencv-python、numpy、pandas、flask、pyyaml、tqdm如果走YOLOv5训练还要加matplotlib、seaborn。别一股脑pip install xxx以后重装环境你就知道什么叫后悔药没处买。装完环境之后先跑一段验证代码确认torch能调用设备再继续往下走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版本第二行如果是False后面训练会慢到无法忍受但也能跑只是100个epoch可能要几十小时第三行能看见显卡型号。我之前遇到一个用户装好后torch.cuda.is_available()是True但训练时还是走CPU最后发现是train.py里硬编码了devicecpu这种低级错误在源码包里并不少见。提示环境配置失败时不要急着把整个环境删掉重来先用pip list看已安装的包再对照requirements逐个排查。这一环节本身就是深度学习项目的基本功过了这一关后面的流程才算顺畅。3. 训练缺陷检测模型标注转换、训练参数与loss曲线怎么看3.1 把VOC标注转成YOLO格式转换脚本与四个边界坑表面缺陷检测数据集最常见的格式是Pascal VOC也就是一张图片对应一个XML文件XML里面是目标框的左上和右下坐标。而YOLO系列训练要求每个目标一行文本类别索引、归一化中心点x、中心点y、归一化宽度w、归一化高度h。如果标注格式不对训练能跑但loss永远不收敛这是第一个大坑。源码包里如果有voc2yolo.py你也要自己检查一遍逻辑。如果没有下面这段脚本可以直接复用我几乎每个类似项目都靠它起步import os import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, out_dir, class_names): os.makedirs(out_dir, exist_okTrue) tree ET.parse(xml_path) root tree.getroot() img_w float(root.find(size/width).text) img_h float(root.find(size/height).text) lines [] for obj in root.findall(object): name obj.find(name).text.strip() if name not in class_names: continue cls class_names.index(name) box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) xmin max(0, xmin) ymin max(0, ymin) xmax min(img_w, xmax) ymax min(img_h, ymax) w xmax - xmin h ymax - ymin if w 0 or h 0: continue x_center (xmin xmax) / 2 / img_w y_center (ymin ymax) / 2 / img_h w_norm w / img_w h_norm h / img_h lines.append(f{cls} {x_center:.6f} {y_center:.6f} {w_norm:.6f} {h_norm:.6f}) out_path os.path.join(out_dir, os.path.basename(xml_path).replace(.xml, .txt)) with open(out_path, w, encodingutf-8) as f: f.write(\n.join(lines)) if __name__ __main__: xml_dir data/annotations out_dir data/labels class_names [crazing, inclusion, patches, pitted_surface, rolled_in_scale, scratches] for xml_file in os.listdir(xml_dir): if xml_file.endswith(.xml): voc_to_yolo(os.path.join(xml_dir, xml_file), out_dir, class_names)脚本逻辑分四步解析XML读取图片尺寸遍历每个object把左上右下坐标转成中心点宽高写txt文件。注意我加了三个关键动作name.strip()清掉类别名两边的空白坐标越界夹取宽高小于等于0的直接跳过。这三个动作不是为了好看而是实际数据集里一定会遇到的问题。四个边界坑一个个说第一类别名前后的空白字符。XML里经常出现namepatches/name旁边多一个换行或制表符直接进class_names.index会抛ValueError。所以在取name后立刻strip。第二负坐标和越界坐标。缺陷紧贴图像边缘时人工标注可能标出负的xmin或超过边界的xmax不加处理会让归一化坐标落在0到1外面训练时目标被忽略。夹取之后小数精度损失可以忽略不计。第三图片宽高和XML里的size对不上。有些数据集在发布前压缩过图片XML里存的却是原始分辨率。转换前抽三张图用OpenCV读一下实际宽高再和XML里的size对比不一致就先统一图片分辨率。这个坑我曾吃过800x600的图标注写成了1024x768训练出来mAP只有0.2当时还怀疑是模型问题查了一天。第四空标注文件。部分图片确实没有缺陷XML里没有object转换后txt是空文件这是合法的YOLO允许空标注。但不要为了凑样本给这些图乱标一个框会让模型学到“没有缺陷的图也有缺陷”误检率直线上升。3.2 训练命令与参数batch_size、learning rate、epochs不是拍脑袋定的标注转换完检查一下data/labels目录确认每个XML都有对应的txt且非空文件每行正好五个数字。确认无误后写一个data/defect.yamltrain: data/images val: data/images nc: 6 names: [crazing, inclusion, patches, pitted_surface, rolled_in_scale, scratches]如果训练集和验证集要分开就把train和val分别写成两个目录路径。很多源码包偷懒train和val指向同一目录这样验证mAP虚高答辩时一到真实数据就露馅。我一般按7:1:2划分成train、val、testtest完全不参与训练只用来做最终评估。接着是训练命令以YOLOv5为例python train.py \ --data data/defect.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --device 0 \ --workers 4 \ --patience 15参数说明--img是训练输入尺寸缺陷检测建议至少640因为很多缺陷只有几十像素再小会被下采样抹掉。--batch根据显存来我用8G显存跑yolov5sbatch 16刚好再往上就会显存溢出。--device 0指定显卡CPU训练就改成--device cpu但百来个epoch可能要跑一两天不太现实。--workers是读取线程数Windows下建议设0或2设大了经常报DataLoader worker崩溃。训练过程中怎么看有没有问题主要看每轮输出的box_loss、cls_loss、obj_loss和验证集的mAP。深度学习CNN不是黑匣子loss曲线会告诉你趋势box_loss前20轮快速下降之后缓慢收敛这是正常如果loss反复震荡完全不降优先怀疑学习率太大或者数据集里有大量错误标注。如果train_loss降到很低但val_loss往上走就是过拟合解决办法是增加数据增强、降低模型复杂度或者提前停下。训练完成后模型目录runs/train/exp里会生成best.pt和last.pt。记住一条用best.pt做后续推理它是验证集上表现最好的权重last.pt是最后一轮的通常比best差。我之前图省事直接用last.pt漏检率肉眼可见变高后来老老实实改回best.pt。3.3 在验证集上先自评不要拿训练集截图当成果训练完不是马上接Web系统先跑一次验证脚本把每张图的预测结果画出来。YOLOv5自带val.pypython val.py \ --data data/defect.yaml \ --weights runs/train/exp/best.pt \ --img 640 \ --conf-thres 0.25 \ --iou-thres 0.45 \ --save-txt \ --save-conf这里--conf-thres是置信度阈值只有模型判定概率超过0.25的框才会保留。阈值越低越容易把噪点当缺陷阈值越高越容易漏掉真实缺陷。--iou-thres是NMS去重阈值同一个缺陷如果预测出多个重叠框IoU超过0.45的会被合并。这两个参数在后面的监管系统里也会用到建议测试时就固定下来不要训练时用一组部署时又换一组那样实验数据完全不可比。验证集自评的目的是生成带框图片然后你一个人看不要只看mAP。缺陷检测里mAP高但漏检的案例太多了特别是小目标。把预测结果图一张张翻过去重点关注裂纹这种细长缺陷如果断断续续只检测出中间一段说明模型学到了局部特征没学到整体连贯性后续可以靠提高推理分辨率或切patch解决。4. 可视化监管系统用Flask把检测模型包装成Web应用4.1 写一个最小的检测服务模型只加载一次可视化监管系统拆开来看就三个部分一个检测服务、一个网页、一个数据库。检测服务的任务是把训练好的模型权重暴露成HTTP接口前端传图片过去后端返回检测结果。这里最容易犯的错是每次请求都torch.load一次模型直接导致页面卡死。正确做法是在Flask应用启动时把模型加载到内存全局变量之后所有请求共享。下面这个app.py是我常用的骨架你可以直接对照源码包找对应部分import os import cv2 import torch import numpy as np from flask import Flask, request, jsonify, render_template from werkzeug.utils import secure_filename app Flask(__name__) app.config[UPLOAD_FOLDER] uploads os.makedirs(app.config[UPLOAD_FOLDER], exist_okTrue) # 模型全局加载只做一次 model torch.hub.load(ultralytics/yolov5, custom, pathmodels/best.pt, force_reloadTrue) model.conf 0.25 # 置信度阈值 model.iou 0.45 # NMS阈值 model.classes [0, 1, 2, 3, 4, 5] # 只检测这六类缺陷 app.route(/) def index(): return render_template(index.html) app.route(/detect, methods[POST]) def detect(): f request.files[image] if image in request.files else None if f is None: return jsonify({error: no image}), 400 path os.path.join(app.config[UPLOAD_FOLDER], secure_filename(f.filename)) f.save(path) img cv2.imread(path) results model(img) detections [] for *xyxy, conf, cls in results.xyxy[0].tolist(): detections.append({ box: [round(v, 2) for v in xyxy], confidence: round(conf, 4), class: int(cls) }) return jsonify({detections: detections, count: len(detections)}) if __name__ __main__: app.run(host0.0.0.0, port5000, debugFalse)说明几个关键点。model.conf和model.iou是YOLOv5推理时的默认阈值这里提前设好避免每次调用都传参。model.classes是类别过滤如果你的源码包只训练了两类就改成对应索引列表不设的话会输出所有类别。secure_filename会把文件名里可能存在的路径分隔符去掉防止有人通过文件名上传路径穿越文件。图像读取使用cv2.imread但它有个老毛病Windows下读中文路径会返回None。如果你的上传目录或图片名是中文需要在保存后改用cv2.imdecode(np.fromfile(path, dtypenp.uint8), cv2.IMREAD_COLOR)我后续避坑章里再详细说。接口写完启动服务就是python web/app.py默认跑在5000端口。你可以用curl测一下curl -X POST -F imagetest.jpg http://127.0.0.1:5000/detect如果返回的JSON里有count字段说明检测链路已经通了。这一步是整个可视化系统的地基地基没打牢后面画什么图表都是空的。4.2 前端可视化轮询接口、画框与报警不要在浏览器里跑模型检测服务就绪后前端要解决三件事把图片显示在页面、把检测框画上去、把缺陷统计展示出来。看起来复杂实际上用Flask内嵌模板加原生JavaScript就能做不需要前端框架。Vue和ECharts可以加但会增加演示时讲不清的风险我建议先把最原始方式跑通至少它能回答“可视化是怎么实现的”。先写一个最简的index.html模板用Canvas画框video idcamera autoplay muted/video canvas idoverlay/canvas div idalarm styledisplay:none;color:red;检测到缺陷请查看/div script const canvas document.getElementById(overlay); const ctx canvas.getContext(2d); async function poll() { const res await fetch(/latest); const data await res.json(); if (!data.image_base64) return; const img new Image(); img.onload () { ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.drawImage(img, 0, 0); data.detections.forEach(d { ctx.strokeStyle #ff0000; ctx.lineWidth 2; const [x1, y1, x2, y2] d.box; ctx.strokeRect(x1, y1, x2 - x1, y2 - y1); ctx.fillStyle #ff0000; ctx.fillText(缺陷${d.class} ${d.confidence}, x1, y1 - 5); }); }; img.src data:image/jpeg;base64, data.image_base64; } setInterval(poll, 1000); /script这段代码里/latest接口返回最近一帧的base64图像和检测框列表。前端每秒钟拉一次用Canvas把检测框叠加在图片上。data.detections里每一项的box是[x1, y1, x2, y2]直接用于画矩形。如果你要接摄像头实时画面就把video标签加进来用getUserMedia拿本地视频但要注意浏览器安全策略必须通过HTTP或HTTPS访问。轮询间隔设1秒够用设100毫秒会让Flask开发服务器疯狂刷日志CPU占用拉满而且浏览器同时发多个请求还会阻塞。想做实时视频流可以走WebSocket加MJPEG流但毕设演示不需要反而越复杂越容易出问题。后端要把图像转成base64我在app.py里补上这个接口import base64 detections_db [] app.route(/latest) def latest(): if not detections_db: return jsonify({image_base64: None, detections: []}) last detections_db[-1] ret, buf cv2.imencode(.jpg, last[image]) b64 base64.b64encode(buf.tobytes()).decode(utf-8) return jsonify({image_base64: b64, detections: last[detections]})这里用全局列表detections_db充当最近一帧的缓存实际项目中要加线程锁因为Flask多线程下两个请求同时修改列表可能错乱。更规范的做法是用queue.Queue扮演生产消费模型一个后台线程不停从摄像头读帧并推理把结果放进队列前端接口从队列取最新结果。这个设计的优势是检测吞吐量和Web展示解耦摄像头帧率再高前端只看最新一帧。源码包里如果只有一个同步的/detect接口我建议你改成这种后台轮询模式答辩时老师问“你的系统能不能接实时产线”你能答得上来就赢了一半。4.3 结果落库与统计面板可视化监管的核心是数据闭环检测结果不能只显示在网页上必须落库。不要用MySQL毕设用SQLite足够零配置文件型数据库交源码时把数据库一起给就行。我习惯在启动时执行建表import sqlite3 def init_db(): conn sqlite3.connect(defect.db) conn.execute( CREATE TABLE IF NOT EXISTS defects ( id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, class TEXT, confidence REAL, image_path TEXT ) ) conn.commit() conn.close()每次检测到缺陷时除了在前端显示还向表里写一条记录。统计面板就有了数据源可以用最直接的方式在Flask里查数据库再渲染到模板app.route(/stats) def stats(): conn sqlite3.connect(defect.db) rows conn.execute( SELECT class, COUNT(*) FROM defects GROUP BY class ).fetchall() conn.close() return render_template(stats.html, rowsrows)这段代码本身不复杂但体现了可视化监管系统的本质模型输出的一条条检测记录经聚合后变成辅助决策的信息。比如哪种缺陷出现最多、今天比昨天增加了多少、哪个班次漏检率上升。把这些做成柱状图和折线图就贴合了“可视化监管”题目的要求。如果你想用ECharts把rows转成JSON传给JS绑定到chart.setOption里去。但先把数据闭环打通再谈图表这个顺序不能反。5. 表面缺陷检测系统避坑手册环境、显存、小目标和中文路径5个真实翻车现场下面这些坑我全都踩过有的在换电脑演示时现场暴露有的在训练跑到一半时翻车。每一条都按现象、原因、解决三个步骤写方便你直接对照。5.1 torch.cuda.is_available()返回False显卡却明明存在现象按教程装完torch跑print(torch.cuda.is_available())输出False但设备管理器里NVIDIA显卡正常显示。原因90%的情况是torch装成了CPU版本。很多人在默认PyPI源里执行pip install torch拿到的就是CPU版。剩下10%是显卡驱动太旧或者CUDA版本与驱动不兼容。解决先看torch版本号后面有没有cu。pip show torch输出Version: 1.13.1cu117表示带CUDA如果只有1.13.1那就是CPU版。重装命令pip install torch1.13.1cu117 torchvision0.14.1cu117 --extra-index-url https://download.pytorch.org/whl/cu117装完再验证。如果还是False去显卡厂商官网更新驱动驱动版本要大于CUDA所需的最低版本。这一步通过之后后面训练阶段的“玄学”问题能少一大半因为很多libcudnn报错都源于torch与CUDA版本错配。5.2 CUDA out of memory先调参数再考虑加钱现象训练第一个epoch还没跑完就报RuntimeError: CUDA out of memory. Tried to allocate 128.00 MiB而且每次的报错都差不多。原因一是batch_size设得太大8G显存跑yolov5s batch 16没问题但跑yolov5x batch 32就是作死二是显存被其他进程占用训练时开着十几个浏览器标签页显存早就不够了。解决把batch_size降到8或者4同时把--img从640降到512。这两个参数对显存的影响是乘法的图像尺寸缩一半特征图面积缩小显存占用明显下降。如果还不够在训练命令里加--workers 0减少数据加载线程然后运行nvidia-smi看是否有其他进程占用显存有就清掉。还有一个trick训练前在脚本里加torch.cuda.empty_cache()释放PyTorch没回收的中间张量。5.3 小缺陷总是检测不到模型看见的和你看的不是一个尺度现象模型整体mAP超过0.85但检测细小裂纹总是漏或者把一条连续裂纹检测成断断续续的三段。原因缺陷目标在640x640输入下可能只有十几个像素经过YOLO的下采样到特征图上只剩一两个格子特征信息基本丢了。这是目标检测的通病不是你的代码有问题。解决第一个方案是把训练和推理的img尺寸提高到1280但显存占用会翻倍小于8G不建议。第二个方案是切patch把原图切成512x512的块逐块检测再合并结果小目标相对尺寸变大检测率会明显提升代价是单帧推理时间从30ms涨到120ms毕设现场完全能接受。第三个方案是数据增强里加随机裁剪让模型在训练时多看到局部特征。我在做金属表面麻点检测时用切patch方案把小目标漏检率从18%降到了6%。5.4 中文路径让程序直接停摆OpenCV和PyTorch都读不了现象代码在英文路径下跑得好好的放到D:\缺陷检测\项目源码\下报FileNotFoundError但文件明明存在。原因Windows控制台默认编码是GBKOpenCV底层调用系统API传中文路径会转换失败。PyTorch的DataLoader也沿用了类似限制它期望纯ASCII路径。解决最省事的是项目路径和所有文件名全部用英文这是我在每个环境配置教程里都强调的事。如果必须用中文图像读取改为cv2.imdecode(np.fromfile(path, dtypenp.uint8), cv2.IMREAD_COLOR)保存图像改为cv2.imencode(.jpg, img)[1].tofile(path)。模型权重路径如果含中文也尽量复制到英文目录torch.load对中文路径的支持各版本不一致没必要赌运气。5.5 Flask页面一直转圈别在请求函数里加载模型现象第一次访问检测页面等了好久才出来第二次直接超时浏览器小圆圈一直转。原因代码把torch.load(best.pt)写进了app.route(/detect)端点内部每次请求都重新加载模型。torch.load一次要好几秒如果还从远程hub下载权重那就不是几秒而是几分钟。解决把模型加载放到全局作用域在启动时一次完成。如果用的torch.hub.load第一次会自动联网下载依赖之后就会走本地缓存。这里还有一个隐蔽的坑Flask的debug模式会自动reload代码一改动整个进程重启全局变量重新初始化模型重新加载也会造成卡顿。所以我建议调试时用debugTrue方便观察但演示时一定改成debugFalse。如果推理耗时超过一两秒最好把检测请求放到后台线程用队列把结果传回接口避免开发服务器的单进程阻塞所有请求。这5条都属于“不会让模型变复杂但能让系统从不可用变可用”的工程细节。把代码以外的这些问题解决掉你才有资格去谈模型优化。6. 进阶把准确率再往上顶的三个手段与最终验证训练结束不等于能交差下面三个手段按性价比排序。第一个是调阈值不增加任何计算成本第二个是TTA用时间换准确率第三个是加二次分类专门压误报。6.1 用验证集网格搜索置信度阈值model.conf0.25是YOLO默认值但不一定适合你的缺陷分布。表面缺陷检测中漏检的代价通常比误检大所以可以把阈值降到0.1让模型把不确定目标也框出来再在验证集上数一下多出来的框到底是真缺陷还是误检。我常用的做法是让conf从0.05按步长0.05跑到0.5每组记下precision和recall选一个业务上能接受的平衡点固定到Web服务里。这个过程半小时就能跑完但效果往往比调半天网络结构都明显。6.2 推理时开TTA用时间换准确率YOLOv5的val.py和detect.py都支持--augment推理时对图像做缩放翻转等变换再融合多个预测结果能提升一点mAP尤其对小目标友好。代价是速度变为原来的2到4倍但毕设演示完全够用。这一步只加参数不碰代码python val.py \ --data data/defect.yaml \ --weights runs/train/exp/best.pt \ --img 640 \ --conf-thres 0.1 \ --iou-thres 0.45 \ --augment6.3 误报多加一个轻量二次分类网络如果模型把纹理阴影当成缺陷不要急着调阈值可以训练一个ResNet18输入是检测框裁剪出来的图像块输出“真缺陷”或“假缺陷”。检测服务先跑YOLO得到候选框裁剪后送给分类网络只保留被判定为真缺陷的框。这个方案能砍掉大部分误报而且几乎不影响召回。我上一个项目用这个办法误报率从15%压到2%。最终验证我的习惯是把测试集图片全部预测一遍把预测错误的图打印到一个文件夹一张张看看是标注错了还是模型确实看不到。这个习惯帮我少交了好几次答辩返工希望帮到你。本文还有配套的精品资源点击获取
返回列表