尧图精选

深度学习机场安检危险品识别:YOLO目标检测项目实战与避坑指南

🕒 发布时间:2026/10/2 14:13:06 📁 来源:尧图网络
简介这是一个面向高校深度学习、Python课程设计及毕业设计的机场安检危险品识别实战项目。项目基于卷积神经网络与Faster R-CNN目标检测框架覆盖X光图像标注、模型训练、验证与部署全流程针对刀具、爆炸物等违禁品场景提供自动识别方案可直接用于算法对比、答辩展示与课设验收。压缩包共180个文件以37个Python源码文件与63张JPG训练/测试图片为核心同时附带Cython加速模块pyx/pyd/c、4个XML标注配置、2个Qt界面文件UI以及Git工程管理配置体积仅9.72MB整体结构紧凑且目录划分清晰便于快速解压与按需查阅。已有47人学习浏览适合应届生或算法入门者快速搭建实验环境理解模型损失变化与工程化集成排错思路。1. 深度学习机场安检危险品识别一份能跑通的毕设项目拆解机场安检场景里的危险品识别本质上是一个目标检测任务。你给模型一张X光安检机图像它得在几毫秒内告诉你哪里有刀、哪里有打火机、哪里有瓶装液体。这个项目用深度学习做这件事适合正在做毕业设计或者课程设计的同学。我拆过不少这类资源最大的感受是模型选型不是难点数据和迭代流程才是拉开差距的地方。这份资源里带的权重、脚本和标注数据能让你少走两周弯路。2. 项目架构与模型选型检测框架、骨干网络和训练环境2.1 为什么危险品识别首选单阶段检测模型安检图像里危险品目标通常尺寸小、对比度低、相互遮挡多。双阶段检测器比如Faster R-CNN先提取候选区域再分类精度确实高但推理速度在安检场景里容易成为瓶颈。单阶段模型如YOLO把目标定位和分类合在一个网络里一次完成在GPU上能做到实时 inference。这个项目选用的是深度学习里的典型做法以YOLO架构为核心配合在COCO上预训练过的权重做迁移学习。实际训练时你会发现安检X光图像的风格和自然图像差异很大。预训练模型在ImageNet或COCO上见过的是自然光下的物体而X光图像是透射灰度图纹理特征完全不同。所以项目里在微调时把骨干网络的学习率调低让底层特征提取器慢慢适应安检图像域的分布而检测头部分用更高的学习率去快速拟合危险品类别。这种做法在业内是通用技巧不是这个项目独有的但它直接影响最终mAP能到多少。2.2 训练环境配置CUDA、PyTorch与依赖清单建议用Python 3.8以上PyTorch 1.10到2.x之间都行。CUDA版本跟着PyTorch走别用太新的11.8是社区里踩坑最少的组合。依赖主要是torch、torchvision、opencv-python、numpy、pillow、matplotlib、tqdm一个requirements.txt能解决。# 创建虚拟环境避免污染系统Python conda create -n safety_check python3.8 -y conda activate safety_check # 安装PyTorch注意cuda版本要和本机驱动匹配 # 可以用 nvidia-smi 看驱动支持的CUDA版本向下兼容 pip install torch1.12.0cu113 torchvision0.13.0cu113 -f https://download.pytorch.org/whl/torch_stable.html # 安装项目依赖 pip install -r requirements.txt这里有两个细节值得说。第一requirements.txt里如果直接写torch默认会装CPU版本训练速度直接差几十倍所以必须手动指定带cu后缀的安装源。第二如果机器是AMD显卡或者纯CPU环境那训练一批数据可能要分钟级建议直接换到云GPU环境或Google Colab跑别在本地死磕。2.3 数据集目录结构与标签体系这个项目的数据集目录结构遵循YOLO标准布局train和val分开每张图像对应一个同名txt标注文件每行格式是类别ID 中心点x 中心点y 宽度 高度都是归一化到0到1之间的浮点数。dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ ├── train.txt ├── val.txt └── data.yamldata.yaml里写类别数和类别名比如刀、打火机、剪刀、液体瓶、充电宝这类。拿到标注文件后我习惯先做一步校验用脚本把标注框画回图像上肉眼确认框和物体是匹配的这一步能避免后面训练时模型学了一堆错误位置特征。3. 把标注数据喂给模型VOC与YOLO格式互转脚本与样本均衡3.1 标注格式转换VOC的XML转成YOLO的txt很多公开数据集和标注工具导出的是VOC格式也就是每张图一个XML文件里面用XML标签记录目标框。而YOLO训练需要的是txt文件如果你拿到的是LabelImg导出的Pascal VOC格式需要先做一次转换。转换脚本是这类项目里最容易出错的环节错一个归一化公式所有框都会偏移。import xml.etree.ElementTree as ET import os def convert_voc_to_yolo(xml_file, class_names, output_dir): tree ET.parse(xml_file) root tree.getroot() # 读取图像尺寸 size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) # 解析所有目标框 labels [] for obj in root.iter(object): cls_name obj.find(name).text if cls_name not in class_names: continue cls_id class_names.index(cls_name) box obj.find(bndbox) x1 float(box.find(xmin).text) y1 float(box.find(ymin).text) x2 float(box.find(xmax).text) y2 float(box.find(ymax).text) # 关键YOLO用的是中心点和宽高且全部归一化 x_center (x1 x2) / 2.0 / img_w y_center (y1 y2) / 2.0 / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h # 过滤掉异常框 if x_center 0 or x_center 1 or y_center 0 or y_center 1: print(f跳过异常框: {xml_file}, {cls_name}) continue labels.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) # 写输出文件文件名和xml保持一致 xml_name os.path.basename(xml_file).replace(.xml, ) txt_path os.path.join(output_dir, xml_name .txt) with open(txt_path, w) as f: f.write(\n.join(labels))这里最关键的坑是归一化公式。很多人会直接除以图像宽高但如果你从XML里读出来的坐标是整数像素值除以宽高后得到0到1之间的浮点数没问题。但有些标注框是从裁剪后的图像生成的坐标上下文不一样直接套公式就偏了。我一般会加一个框合法性过滤中心点在图像范围外的直接跳过。3.2 数据集划分与样本均衡策略训练集和验证集的划分比例项目中默认是8:2。但安检危险品数据集普遍存在严重的类别不均衡问题。打火机样本可能只有几十张而充电宝有上千张。直接用原始分布训练模型会偏向样本多的类别小类别基本学不出来。常见做法是先统计标注文件里每个类别的目标数量对少的类别做过采样也就是把少样本的图像复制几份放进训练集。还有一种方式是把少样本的图做数据增强后再放进去效果更好一点。增强手段在安检场景里要注意分寸X光图像做马赛克增强或者随机裁剪有效做上下翻转要慎重因为危险品在传送带上有明确的朝向语义翻转后可能让模型学出错误的姿态特征。# 统计各类别样本数量决定是否需要过采样 python scripts/analyze_labels.py --labels dataset/labels/train # 输出类似knife: 152, lighter: 34, scissors: 89, battery: 1203 # 对数量低于50的类别考虑过采样或复制粘贴增强3.3 安检图像的数据增强参数设置数据增强是这类项目里性价比最高的环节。一个简单的随机仿射变换就能让模型泛化能力上一个台阶。YOLO训练时常用的增强参数包括HSV扰动、随机平移、随机缩放、Mosaic。但在安检X光图像上我建议把HSV扰动关掉或者把饱和度扰动调低。因为X光图像本身就是灰度或蓝灰色调饱和度扰动会把图像变成奇怪的颜色风格反而引入噪声。Mosaic增强在安检场景下可以用但要注意X光图像里的目标往往是叠放在一起的Mosaic把四张图拼在一起后目标之间的遮挡关系会更复杂模型收敛速度会变慢。如果显存不够大建议关闭Mosaic直接用随机平移和旋转就够了。4. 训练配置全解析超参数、迁移学习与损失曲线判读4.1 训练命令与参数含义训练脚本是这类资源的灵魂。打开train.py会发现虽然代码框架是常见的YOLO结构但超参数是调过的。默认image size是640batch size根据显存自动调整epoch数给的80。学习率用warmup加余弦退火策略初始学习率0.01warmup阶段前3个epoch线性升到0.01后面按余弦曲线降到0.0001。# 单卡训练示例批量大小根据显存调整 python train.py --data dataset/data.yaml --cfg models/yolov5s.yaml --weights yolov5s.pt --epochs 80 --batch-size 16 --img-size 640 --device 0 # 断点续训训练中断后不用从头来 python train.py --resume runs/train/exp5/weights/last.ptweights参数在迁移学习中很关键。如果从零训练yolov5s需要几万张图和几十个小时的GPU时间但加载coco预训练权重后只用几千张安检图就能在10到20个epoch内看到明显的loss下降。这也是为什么资源包里一定带着预训练权重的原因。4.2 显存占用与batch size的折中显存不够是训练环节最常见的翻车现场。显卡只有6G显存时batch size设成16很容易爆显存。这时有三个选择把img-size从640降到512、把batch-size降到8、或者开启梯度累积。梯度累积是模拟大batch的手段每两步累积一次梯度再更新参数效果上接近batch size翻倍代码里这样写accumulate 4 # 每4个batch累积一次梯度 for i, (imgs, labels) in enumerate(dataloader): loss model(imgs, labels) loss loss / accumulate # 先做归一化 loss.backward() if (i 1) % accumulate 0: optimizer.step() optimizer.zero_grad()这样做的本质是牺牲训练速度换取模型收敛稳定性。batch size太小batch normalization的统计量不稳定loss容易震荡太大又受显存限制。梯度累积正好解决这个矛盾代价是训练时间变长。4.3 训练过程的止损信号判断训练是否正常主要看两个指标loss曲线的下降趋势和验证集mAP。如果训练loss在下降但验证mAP一直是0大概率是数据集划分出了问题比如训练集和验证集图像有重叠或者标注文件位置不对。如果loss在某个epoch后突然上升通常是学习率没衰减到位模型在最优点附近震荡。我习惯每5个epoch保存一次验证集预测图直接看模型的预测框和置信度分数。光看数值不够一定要看图模型看到图里的什么特征才能判断它学偏没偏。比如如果模型把传送带边缘当成刀框出来说明数据标注时背景样本没给够需要往训练集里加一些完全不含危险品的负样本。4.4 推理脚本与置信度阈值的选择训练完以后就是推理。detect.py里有两个参数直接决定使用体验conf-thres置信度阈值和iou-thres NMS阈值。置信度阈值默认0.25实测安检场景下建议调到0.3到0.4之间。因为安检机图像噪声多模型在低置信度下会输出一大堆假阳性框安检员会被烦死。调高阈值后假阳性减少但也会漏掉一些叠在复杂背景里的小目标。python detect.py --weights runs/train/exp5/weights/best.pt --source test_images/ --conf-thres 0.35 --iou-thres 0.454.5 模型导出与部署格式训练完成后PyTorch的.pt文件在部署时不够轻量。转成ONNX格式后可以脱离PyTorch环境推理用一个opencv或者onnxruntime就能跑。导出命令是这个项目里比较容易被忽略的。python export.py --weights runs/train/exp5/weights/best.pt --include onnx --img-size 640导出的时候有个坑必须在导出前处理如果训练阶段做了自定义的anchor导出时要在export.py里确认anchor尺寸同步更新了不然ONNX模型的anchor还是默认值推理会非常慢且不准。我在这上面栽过跟头导出的模型在onnxruntime里跑框全飘了查了半天发现是anchor没同步。5. 避坑排查五个最常见的翻车现场与解决路径5.1 训练loss出现NaN现象训练进行到第几百步时loss突然变成nan之后一直不恢复。原因最常见的是学习率过大导致梯度爆炸特别是非warmup阶段直接大学习率训练时。也和数据集标注有关如果某个类别ID写错了比如标注文件里出现了类别数之外的ID模型输出层计算损失时会产生极大值。还有一种情况是归一化坐标出现负数或大于1的值模型对这些越界框的损失计算不稳定。解决方式是先用小学习率0.0001跑几个epoch看loss是否正常然后逐步升回去。同时检查所有标签txt里是否有异常ID或异常坐标值。我一般写一个校验脚本把标签文件里每个数的取值范围打印出来超过[0,1]的直接删掉或者重新标注。换精度的话AMP混合精度训练偶尔也会导致nan把amp关掉试试。5.2 验证集mAP很高但测试图效果很差现象训练结束时mAP有0.85看起来不错但拿一张新图测试各种漏检和误检。原因训练集和验证集来自同一个数据分布如果数据集划分方式不对比如同一批物品在不同角度下拍的图被同时分到训练集和验证集那验证集就变成了“开卷考试”。实际场景中的安检机图像和数据集风格差异较大时模型自然会掉点。解决方式是重新划分数据集按物品实例分组而不是按图像随机划分。如果同一件物品拍了多张图确保它只出现在训练集或只出现在验证集里不能两边都有。5.3 OpenCV读图导致颜色通道错乱现象用OpenCV读取图像后直接传给模型推理结果明显变差检测框置信度都特别低。原因OpenCV的imread读取的是BGR顺序而PyTorch训练时用的是RGB顺序。训练数据经过的是RGB通道推理时输入BGR相当于输入分布完全变了。解决方式是在推理脚本里把通道顺序转换回来img cv2.cvtColor(img, cv2.COLOR_BGR2RGB)再做归一化。这个坑在本地调试时最容易踩因为训练脚本里datasets.py已经做了转换直接跑没问题但自己写推理脚本时忘了这一步模型效果直接砍半。5.4 显存明明够却报CUDA OOM现象训练刚开始就报RuntimeError: CUDA out of memorybatch size已经改到4了还是不行。原因PyTorch的缓存分配机制会让显存碎片化。之前跑过其他模型留下的缓存没释放新训练时即使数据量不大也可能申请不到连续显存。解决方式是在训练前加一行torch.cuda.empty_cache()如果还不行直接在代码最前面设置os.environ[PYTORCH_CUDA_ALLOC_CONF] max_split_size_mb:128把显存碎片整理阈值调低可以减少碎片导致的OOM。5.5 Mosaic增强导致目标框错位现象开了Mosaic增强后训练loss下降很快但验证mAP波动剧烈权重文件时好时坏。原因Mosaic把四张图拼在一起同时对框做坐标变换。如果图像尺寸不是Mosaic模块预期的倍数或者拼接后的图像和目标框没有同步变换就会出现框和内容错位。训练集里这些错位的框被当成“正确答案”模型自然学不出干净的特征。解决方式是把Mosaic增强关掉或者仔细检查数据增强代码里框坐标是否随图像一起做了仿射变换。我记得YOLOv5较新的版本里Mosaic做得比较成熟但如果项目是旧版魔改而来建议直接关掉这个选项用简单随机平移和缩放代替效果更可控。6. 进阶验证技巧用混淆矩阵定位模型盲区再做ONNX部署自检训练结束并不等于项目完成还需要一次系统性的验证和部署自检。这部分内容和写论文、答辩的关系最大我分享两个具体操作。第一个是画混淆矩阵。用验证集跑一次推理把所有预测框和真实框做匹配统计每个类别的召回率和误检率。比如结果显示“刀”类别的召回率只有0.6说明一半的刀都被漏检了。这时去看它的预测结果图你会发现漏检的主要是那种横着放、被其他物品压在下面的刀。这类目标在数据集中很少模型没机会学。解决方式是专门收集或合成这类角度的负样本加进训练集重训。from sklearn.metrics import confusion_matrix import numpy as np # preds和trues分别是预测类别ID和真实类别ID # 注意目标检测的混淆矩阵需要先做IOU匹配不能用简单的逐类别对比 cm confusion_matrix(trues, preds, labels[0, 1, 2, 3, 4]) # 输出矩阵对角线越亮越好非对角线元素高的地方就是混淆严重区域第二个是ONNX模型的输入输出自检。导出ONNX后先做一次模型的输入输出一致性与推理前对齐验证。常见做法是用同一张图分别跑PyTorch模型和ONNX Runtime推理比较输出结果的差异是否在可接受范围内。这里的差异通常来源于模型内部的某些算子在两个框架下的计算精度不同但一般IOU偏差小于0.01都属于正常。import onnxruntime as ort import numpy as np # 创建ONNX Runtime会话 sess ort.InferenceSession(best.onnx) input_name sess.get_inputs()[0].name input_shape sess.get_inputs()[0].shape # 输入shape一般是[1, 3, 640, 640]构造一个模拟输入 dummy np.random.rand(*input_shape).astype(np.float32) output sess.run(None, {input_name: dummy}) # 确认输出张量不是空的且形状合理 print(output[0].shape)跑完这一步整个项目的完整链路就算是打通了。从那以后我每次部署模型都会强制走一遍这个流程先画混淆矩阵找盲区再用ONNX Runtime跑自检验证前后一致性最后才敢把模型交给测试组。这两个步骤加起来不超过半小时但能省掉后面来回扯皮的功夫。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →