手拿刀棒识别检测系统:YOLOv5训练、评估与部署全流程

发布时间:2026/10/2 13:05:00
手拿刀棒识别检测系统:YOLOv5训练、评估与部署全流程 简介这份资源面向计算机视觉入门与进阶开发者提供一套基于YOLOv5算法的手拿刀棒识别检测完整工程可用于安防场景下的危险物品检测学习与二次开发。压缩包共79个文件约41.19MB包含17个Python源码文件、23个pyc编译文件、17个yaml配置、3个pt权重文件以及训练脚本、推理脚本、Dockerfile、依赖清单和说明文档另附7张jpg与4张png可视化图片。资源中给出迭代200次的训练结果包含loss下降曲线、Recall召回率曲线、Precision精确度曲线及mAP等评估指标曲线模型拟合较好检测类别为“刀”和“棒子”两类。目前已有455人学习浏览读者可据此快速复现训练与推理流程理解数据配置、模型权重加载、指标分析与结果可视化的完整链路适合作为课程设计、毕业设计或算法练手的参考方案。1. 手拿刀棒识别到底在识别什么从一次误报到可交付系统地铁安检口、校园门口、厂区周界摄像头每天都在拍但真正需要人盯的只有那几秒——有人手里握着刀或者棒状物。手拿刀棒识别检测系统要解决的就是这件事在视频流里定位人体手部区域判断手里是否握有刀具、棍棒这类危险物品并给出类别和置信度。它属于计算机视觉与目标检测的交叉场景主流实现是 YOLOv5 加 Python 推理服务交付物通常包含源码、模型文件、评估指标曲线和使用说明。这套东西适合两类人一类是安防、园区、工地做智能化改造的工程师需要快速搭出可演示的原型另一类是计算机视觉入门的学习者想拿一个完整项目跑通训练、评估、部署全流程。但我要先说清楚一个反直觉结论这个任务最难的不是模型选型而是数据定义和误报控制。刀和棒在低分辨率画面里可能只有几十个像素和手机、雨伞、工具把手极易混淆如果一上来就堆网络深度最后大概率得到一个“训练指标好看、现场疯狂报警”的黑匣子。所以这篇笔记按“先立住任务边界再动手复现最后讲坑”的顺序展开把源码、模型文件、评估曲线和使用说明这四样东西串成一条能落地的路径。2. 任务拆解与数据准备手拿刀棒识别为什么不能直接套通用检测2.1 先定义“手拿”而不是“有刀”通用目标检测数据集里刀可能出现在桌面、货架、腰间但本系统的业务定义是“手部持握”。这个限定直接决定标注策略只标人手与刀棒同时出现的区域还是分别标手和刀棒再靠空间关系判断常见做法是采用两级思路——第一级检测人体或手部第二级在候选区域内检测刀棒最后用 IoU 或中心点距离做关联。这样做的好处是负样本更干净模型不会因为背景里一把静态刀具就报警。代价是推理链路变长对实时性有要求时要做裁剪和批处理。我一般会把标注规范写成三条硬规则第一刀棒可见长度低于 30 像素不标避免噪声标签第二手部遮挡超过一半但刀棒清晰可见时仍标刀棒并标注“遮挡”属性第三玩具刀、塑料棒单独设子类不要和真实危险物混在一个类里。这三条看起来琐碎但后面评估曲线能不能解释、误报能不能压下来全靠这里。2.2 数据格式转换把 VOC 和 COCO 统一成 YOLO 格式拿到手的原始数据往往是 VOC XML 或 COCO JSON而 YOLOv5 训练需要每张图对应一个 txt每行是class x_center y_center width height且坐标要归一化到 0 到 1。下面这段脚本处理 VOC 转 YOLO同时生成训练集和验证集的索引文件。import os import xml.etree.ElementTree as ET import random # 类别映射顺序必须和 data.yaml 里的 names 一致 CLASS_MAP {knife: 0, stick: 1, hand: 2} VOC_DIR raw_annotations IMG_DIR raw_images OUT_DIR yolo_dataset VAL_RATIO 0.2 def convert_box(size, box): # VOC 是 xmin,ymin,xmax,ymax转成归一化中心点加宽高 dw, dh 1.0 / size[0], 1.0 / size[1] x (box[0] box[1]) / 2.0 y (box[2] box[3]) / 2.0 w box[1] - box[0] h box[3] - box[2] return x * dw, y * dh, w * dw, h * dh def convert_annotation(xml_path, out_txt): tree ET.parse(xml_path) root tree.getroot() size root.find(size) w, h int(size.find(width).text), int(size.find(height).text) lines [] for obj in root.iter(object): name obj.find(name).text if name not in CLASS_MAP: continue cls_id CLASS_MAP[name] bnd obj.find(bndbox) box (float(bnd.find(xmin).text), float(bnd.find(xmax).text), float(bnd.find(ymin).text), float(bnd.find(ymax).text)) # 注意 convert_box 参数顺序是 xmin,xmax,ymin,ymax x, y, bw, bh convert_box((w, h), box) lines.append(f{cls_id} {x:.6f} {y:.6f} {bw:.6f} {bh:.6f}) with open(out_txt, w) as f: f.write(\n.join(lines)) def main(): os.makedirs(f{OUT_DIR}/labels/train, exist_okTrue) os.makedirs(f{OUT_DIR}/labels/val, exist_okTrue) os.makedirs(f{OUT_DIR}/images/train, exist_okTrue) os.makedirs(f{OUT_DIR}/images/val, exist_okTrue) xml_files [f for f in os.listdir(VOC_DIR) if f.endswith(.xml)] random.shuffle(xml_files) split int(len(xml_files) * (1 - VAL_RATIO)) for i, xml_file in enumerate(xml_files): subset train if i split else val stem os.path.splitext(xml_file)[0] convert_annotation(os.path.join(VOC_DIR, xml_file), f{OUT_DIR}/labels/{subset}/{stem}.txt) # 图片用软链接或复制这里示意复制 src_img os.path.join(IMG_DIR, stem .jpg) if os.path.exists(src_img): import shutil shutil.copy(src_img, f{OUT_DIR}/images/{subset}/{stem}.jpg) if __name__ __main__: main()逻辑说明convert_box负责坐标归一化convert_annotation逐对象读取并过滤未知类别main完成随机划分和目录创建。参数上VAL_RATIO控制验证集比例小数据集建议 0.15 到 0.2CLASS_MAP的键必须和后续data.yaml的names完全一致否则训练时类别索引错位评估曲线会莫名其妙地差。运行前确认 XML 里的size字段真实存在有些标注工具导出的宽高是 0会导致除零错误。2.3 data.yaml 与超参数yolov5 超参数怎么设才不玄学YOLOv5 的训练入口是train.py核心配置在data.yaml和hyp.yaml。data.yaml至少包含train、val、nc、names四项。手拿刀棒场景类别少nc通常为 2 到 4不要为了“以后扩展”预留空类空类会让损失计算出现无正样本的尴尬。# data.yaml train: ./yolo_dataset/images/train val: ./yolo_dataset/images/val nc: 3 names: [knife, stick, hand]超参数方面我一般先跑默认hyp.scratch-low.yaml重点调三个lr0初始学习率、anchors锚框、mosaic增强概率。小目标多时把anchors重新聚类命令是python utils/autanchor.py --data data.yaml。mosaic开到 1.0 能提升泛化但如果你的刀棒样本本身很少过度拼接会让小目标更碎建议降到 0.5 到 0.7。lr0默认 0.01微调预训练模型时改成 0.001 更稳。这些参数没有绝对最优判断标准是看results.csv里val/obj_loss是否震荡震荡就降学习率不降就加 warmup。3. 训练、评估与模型文件从 results.csv 到可解释的指标曲线3.1 启动训练与断点续训环境配置是很多人翻车的第一站。Python 建议 3.8 到 3.10PyTorch 按显卡 CUDA 版本装不要直接pip install torch拉最新版容易和 torchvision 版本打架。装完用python -c import torch; print(torch.cuda.is_available())验证返回 False 就先解决驱动别急着训练。# 单卡训练batch 根据显存调8G 显存建议 8 到 16 python train.py --img 640 --batch 16 --epochs 100 \ --data data.yaml --weights yolov5s.pt \ --hyp data/hyp.scratch-low.yaml --project runs/train --name knife_stick # 断点续训指定 last.pt python train.py --resume runs/train/knife_stick/weights/last.pt参数说明--img 640是输入分辨率手部小目标多可以提到 960但显存和速度会成倍变化--weights yolov5s.pt用官方预训练权重做迁移数据量少于 5000 张时强烈建议--batch不是越大越好batch 过大而学习率没同步放大收敛会变慢。训练日志里重点看mAP0.5和mAP0.5:0.95前者宽松后者严格两者差距大说明定位精度不够可能是标注框不紧或锚框不匹配。3.2 评估指标曲线怎么读训练结束后runs/train/knife_stick/下会有results.csv、confusion_matrix.png、PR_curve.png、F1_curve.png。这些就是标题里说的评估指标曲线。results.csv每行是一个 epoch列包括train/box_loss、val/box_loss、metrics/mAP_0.5等。用下面这段代码把关键曲线画出来方便写报告或排查。import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/train/knife_stick/results.csv) df.columns [c.strip() for c in df.columns] # 列名可能带空格 fig, axes plt.subplots(1, 2, figsize(12, 4)) axes[0].plot(df[epoch], df[train/box_loss], labeltrain_box) axes[0].plot(df[epoch], df[val/box_loss], labelval_box) axes[0].set_xlabel(epoch); axes[0].set_ylabel(box_loss); axes[0].legend() axes[1].plot(df[epoch], df[metrics/mAP_0.5], labelmAP0.5) axes[1].plot(df[epoch], df[metrics/mAP_0.5:0.95], labelmAP0.5:0.95) axes[1].set_xlabel(epoch); axes[1].set_ylabel(mAP); axes[1].legend() plt.tight_layout(); plt.savefig(metrics_curve.png, dpi150)逻辑说明左图看损失是否收敛右图看精度是否还在涨。如果train/box_loss持续下降而val/box_loss抬头就是过拟合需要加数据或加正则。mAP0.5到 0.85 以上、mAP0.5:0.95到 0.5 以上在自建小数据集上算可用。注意results.csv的列名在不同 YOLOv5 版本里可能略有差异读之前先打印df.columns确认别硬编码。3.3 模型文件导出与体积权衡训练产出的best.pt是 PyTorch 权重部署时常见做法是导出 ONNX 或 TorchScript。导出命令python export.py --weights runs/train/knife_stick/weights/best.pt \ --include onnx --img 640 --batch 1 --simplify--simplify会调用 onnx-simplifier 精简计算图减少冗余节点。导出后检查输入输出名字ONNX 推理时预处理必须和训练一致letterbox 填充、BGR 转 RGB、归一化到 0 到 1。很多人导出后精度掉一大截八成是预处理没对齐。模型文件大小方面yolov5s 的 pt 大约十几 MBONNX 相近量化成 INT8 能再降一半但小目标召回可能下降是否量化要用验证集实测别只看体积。4. 推理部署与使用说明把模型跑成能演示的服务4.1 本地推理最小闭环拿到源码和模型文件后先跑通单张图片推理确认环境和权重没问题再上视频流。YOLOv5 自带detect.py但实际项目里我建议自己写一个薄封装方便加业务逻辑比如只保留手部附近的刀棒框。import cv2 import torch import numpy as np model torch.hub.load(ultralytics/yolov5, custom, pathruns/train/knife_stick/weights/best.pt) model.conf 0.35 # 置信度阈值 model.iou 0.45 # NMS IoU 阈值 def infer_frame(frame): results model(frame) df results.pandas().xyxy[0] # 只保留刀和棒过滤手部类别 df df[df[name].isin([knife, stick])] for _, row in df.iterrows(): x1, y1, x2, y2 int(row[xmin]), int(row[ymin]), int(row[xmax]), int(row[ymax]) cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.putText(frame, f{row[name]} {row[confidence]:.2f}, (x1, y1 - 8), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) return frame cap cv2.VideoCapture(0) while True: ret, frame cap.read() if not ret: break frame infer_frame(frame) cv2.imshow(knife_stick_detect, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()逻辑说明torch.hub.load直接加载本地权重model.conf和model.iou是推理阶段最常调的两个参数。conf调低召回高但误报多调高则漏检多安防场景一般 0.3 到 0.4 起步再根据误报率微调。results.pandas()把检测结果转成 DataFrame方便按类别过滤。这段代码跑通后换成 RTSP 流或视频文件只需改cv2.VideoCapture参数。4.2 使用说明里该写什么一份能让人照着复现的使用说明至少包含四块环境依赖清单、数据目录结构、训练命令、推理命令。环境依赖要写死版本区间比如torch1.8,2.0不要只写“安装 PyTorch”。数据目录结构用树形文字描述让读者知道images和labels必须同级且文件名对应。训练和推理命令给出可直接复制的完整行包括相对路径。最后补一段“常见报错”比如AssertionError: No labels found通常是路径写错或 txt 为空CUDA out of memory就降 batch 或 img。使用说明不是论文越具体越省事。4.3 跨平台部署的取舍有人问树莓派 5 上能不能部署自己训练的 YOLOv5 模型。可以但要有预期yolov5s 在树莓派上纯 CPU 推理大概几帧到十几帧取决于分辨率和线程数。常见做法是导出 ONNX 后用 onnxruntime 跑或者用 NCNN 进一步优化。如果一定要上边缘设备建议把输入分辨率降到 416 或 320并接受小目标召回下降。跨平台部署的坑集中在算子支持上某些自定义算子导出 ONNX 后不被 onnxruntime 识别导出时加--simplify能解决一部分解决不了就换 TorchScript。别指望一套权重在所有平台都满速先明确帧率底线再决定模型尺寸。5. 避坑与排查手拿刀棒识别最常见的五类翻车5.1 现象训练 loss 正常但 mAP 一直是 0原因data.yaml里的names顺序和标注时的类别索引不一致或者验证集路径下没有对应图片。解决打印data.yaml和某个 label txt 的第一列确认类别 id 在nc范围内再检查val路径是否指向真实存在的目录YOLOv5 对空验证集不会报错只会静默给 0。5.2 现象现场误报率极高墙上挂的刀具也报警原因训练数据里混入了大量“静态刀”负样本模型学到的是“刀”而不是“手拿刀”。解决回到标注规范只保留手部持握样本静态刀具作为背景负样本但不给标签推理阶段加手部检测做前置过滤只有手部框附近的刀棒框才输出。5.3 现象导出 ONNX 后精度明显下降原因预处理不一致训练时用了 letterbox 和 RGB 归一化推理时直接 resize 且没转通道。解决把 YOLOv5 的datasets.py里 letterbox 逻辑抄到推理代码确保填充灰边、缩放比例、通道顺序完全一致导出时加--simplify并用同一张图对比 pt 和 onnx 的输出框。5.4 现象小目标刀棒漏检严重原因输入分辨率太低或者锚框尺寸不匹配。解决把--img提到 960 做一次对比训练用autanchor.py重新聚类锚框如果还不行考虑切片推理把大图裁成重叠小图分别检测再合并代价是速度下降。5.5 现象训练到一半显存溢出原因batch或img设得太大或者mosaic增强在后期产生超大拼接图。解决降batch到 8 或 4开--multi-scale时注意范围别太宽YOLOv5 支持--cache ram加速但会占内存小内存机器改成--cache disk或不用缓存。6. 把评估曲线用起来一个判断模型能不能上线的土办法最后一章不讲新工具讲一个我反复用过的判断习惯不要只看最终 mAP而是把results.csv里最后 20 个 epoch 的val/box_loss和metrics/mAP_0.5拉出来算两个数——损失的标准差和 mAP 的斜率。损失标准差大说明训练不稳定上线后同一场景不同时段表现会飘mAP 斜率还没趋近于零说明再训还能涨别急着导出。这两个数用 pandas 两行就能算tail df.tail(20) loss_std tail[val/box_loss].std() map_slope (tail[metrics/mAP_0.5].iloc[-1] - tail[metrics/mAP_0.5].iloc[0]) / 20 print(floss_std{loss_std:.4f}, map_slope{map_slope:.5f})经验阈值loss_std小于 0.02 且map_slope小于 0.001我才认为这个模型可以拿去现场试。否则要么继续训要么回头查数据。这个办法不高级但帮我省过很多次“指标好看、现场翻车”的后悔药。另外评估曲线里的confusion_matrix.png值得单独看如果knife大量被分到background说明漏检多优先补小目标样本如果background被分成knife说明误报多优先加负样本。模型文件、源码、曲线、使用说明这四样东西缺了曲线就等于没有验收标准缺了使用说明就等于把复现成本转嫁给别人。我自己踩过的最大坑是早期只存了best.pt没存results.csv后来想复现指标只能重训。所以从下一个项目开始我习惯把runs/train/整个目录打包曲线和权重放一起谁接手都能看懂。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询