简介面向电池分类与目标检测的YOLOv7格式数据集专注识别9伏电池、纽扣电池和干电池三类常见电池模型实测正确识别率高达97.7%适用于电池回收、智能分拣等场景。数据来源于2030张640×640分辨率的原始图像已全部完成标注并整理为可直接读取的格式用户无需自行转换即可开始训练。压缩包共包含2000个文件其中1999个txt文件逐一存储每张图的类别编号与边界框坐标1个yaml文件定义类别名称、训练验证路径等关键参数整体大小仅65.72MB结构简洁便于快速下载、备份与迁移。目前已有48人学习该资源对于计算机视觉初学者、小型项目开发者和工业质检场景来说是一份可直接落地的数据集能显著减少数据采集与标注的时间投入也可作为算法对比与模型迭代的基准数据。1. 电池数据集三类电池检测YOLOv7 标注直接能训做工业分拣或者废旧电池回收项目时最烦的不是模型选型而是手头没有像样的数据。公开数据集里电池类别往往只有干电池一种9V 方块电池和纽扣电池基本找不到就算找到也是背景杂乱、标注不齐训出来的模型一到产线就翻车。这份电池数据集一共 2030 张原始图分辨率统一 640×640覆盖 9V 电池、纽扣电池、干电池三类标注格式是 YOLOv7 直接可用的 txt 文件官方声称正确识别率 97.7%。对刚入门目标检测、或者急着给分拣设备做原型验证的工程师来说省掉了从零采集和标注的漫长周期拿到手就能跑训练。2. 数据集结构与标注规范先搞清 2030 张图里藏着什么2.1 文件命名与目录结构拆解解压数据集后你会看到一堆像下面这种名字的 txt 文件和 jpg 图片混在一起-E9-9B-BB-E6-B1-A0-E3-83-99-E3-83-AB-E3-83-88-E3-82-B3-E3-83-B3-E3-83-99-E3-82-A2-mp4-t-9-8_jpg.rf.9fda1ea0a91ad15397f70c0257bdcaca.txt IMG_2984-MOV-t-14-6_jpg.rf.00325b5d76456e9c6d8acf53399c8350.txt IMG_0889_jpeg.rf.c74f78590a360739c070f2876fa85ffd.txt这些文件名其实是从 Roboflow 导出的标准命名格式。.rf.后面那串哈希值只是防止文件重名的随机 ID没什么实际意义。但前面那部分值得解读一下文件名里带mp4-t-9-8这种段落的是从视频里按帧抽出来的图t-9-8表示视频第 9.8 秒左右截取的一帧。这解释了为什么数据集里很多图像的背景是连续变化的传送带——原始素材应该是拍了一段电池流水的视频然后抽帧得到图像。带IMG_2984-MOV-t-14-6的说明素材有一部分是用手机或者相机录像然后同样走了抽帧流程。这种图像往往带有手持拍摄的轻微运动模糊。文件名里还有一段类似-E9-9B-BB-E6-B1-A0-E3-83-99...的十六进制编码解码后是日文「電池ベルトコンベア」也就是「电池传送带」。这说明这批数据集的原始拍摄环境大概率是一条电池装配或分拣的传送带产线。提示如果你打算自己扩充数据集建议沿用同样的命名习惯——把来源视频名、时间戳、来源标识写进文件名。后期排查 badcase 时能从文件名直接定位到原始视频片段省事很多。2.2 标注格式与类别设置每个 txt 文件和同名 jpg 一一对应内容格式是标准的 YOLO 目标检测格式class_id x_center y_center width height其中x_center、y_center、width、height都是归一化到 0~1 之间的浮点数。比如某一行是0 0.53671875 0.4427083333333333 0.10546875 0.0890625意思就是一个类别 0 的目标中心点在图像的 (0.537, 0.443) 处宽占整图的 10.5%高占整图的 8.9%。这里所有值都是相对于 640×640 图像尺寸的归一化坐标不需要你再做任何换算。需要特别注意的是类别 ID 的映射关系。数据集本身没有附带类别配置文件你需要在训练前自己确认0、1、2分别对应哪类电池。常见做法是写个小脚本统计每个 txt 里的类别分布再对照原图人工核验几个典型样本。我一般会先随机抽 20 张图用 OpenCV 把标注框画出来看一遍确认类别顺序。统计类别分布的脚本也很简单import os label_dir labels/train class_count {} for fname in os.listdir(label_dir): if not fname.endswith(.txt): continue with open(os.path.join(label_dir, fname), r) as f: for line in f.readlines(): cls_id int(line.strip().split()[0]) class_count[cls_id] class_count.get(cls_id, 0) 1 print(class_count) # 示例输出: {0: 1203, 1: 876, 2: 450}这段代码遍历训练集标签目录统计每个类别出现的总次数。如果某个类别数量明显偏少比如纽扣电池只有几百个实例那训练时就要考虑加权重或者做增广不然后面验证时这个类很容易漏检。2.3 三类电池的形态差异与识别难点从检测难度来分这三类电池并不是同一量级的对手干电池5 号/7 号圆柱体尺寸占比中等在整幅图中的特征是最明显的——金属正极帽、侧面印字、圆柱形轮廓即使有点模糊也能靠形状认出来。9V 方块电池长方体顶部有正负两个极片侧面有 6F22 之类的丝印长宽比区别于干电池也不算难。纽扣电池小圆片在 640×640 的图里可能只有二三十个像素的直径。这类目标在检测里归为小目标YOLOv7 的 PANet 结构对 16×16 以下的小目标召回率会明显下降。此外纽扣电池反光严重正上方拍摄时表面会形成高光区域导致边缘特征被吞掉。这是为什么要单独说数据集结构的原因——2030 张图看着不少但如果你把训练集、验证集按 8:1:1 划分再扣掉视频抽帧带来的相似帧真正有效的独立场景可能就六七百个。后面训练时过拟合的风险是实打实存在的需要靠增广和早停来控制。3. 复现 97.7% 识别率从数据集到 YOLOv7 训练的全流程3.1 整理目录与划分数据集拿到手的数据集如果已经是散放的 txt 和 jpg先整理成 YOLOv7 训练的标准目录结构dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ └── labels/ ├── train/ ├── val/ └── test/写一个脚本把所有图像和标注按固定的随机种子划分到上面三个子集里。这个脚本建议自己写而不是用 Roboflow 的在线划分功能因为以后你往数据集里加新图时需要保证同样的划分逻辑能复用import os import random import shutil random.seed(42) src_img_dir battery_dataset/images src_label_dir battery_dataset/labels img_files [f for f in os.listdir(src_img_dir) if f.endswith(.jpg)] random.shuffle(img_files) train_ratio, val_ratio 0.8, 0.1 train_cut int(len(img_files) * train_ratio) val_cut int(len(img_files) * (train_ratio val_ratio)) splits { train: img_files[:train_cut], val: img_files[train_cut:val_cut], test: img_files[val_cut:] } for split_name, files in splits.items(): os.makedirs(fdataset/images/{split_name}, exist_okTrue) os.makedirs(fdataset/labels/{split_name}, exist_okTrue) for img_name in files: label_name img_name.replace(.jpg, .txt) shutil.copy(os.path.join(src_img_dir, img_name), fdataset/images/{split_name}/{img_name}) shutil.copy(os.path.join(src_label_dir, label_name), fdataset/labels/{split_name}/{label_name})参数说明random.seed(42)保证每次运行划分结果一致这是复现训练结果的前提。把随机种子写死在脚本里而不是用默认值是为了后面增量训练时新旧数据集的划分不冲突。replace(.jpg, .txt)是假设所有图片都是 jpg 后缀如果你的数据里有 png 或 jpeg需要把所有后缀统一后再跑这个脚本。划分完检查一下每个子集的图片数和标注数是否一致直接数文件对不上最常发生在标注缺失或者空标注文件的情况# 统计 images/train 和 labels/train 的文件数 ls dataset/images/train | wc -l ls dataset/labels/train | wc -l注意如果 labels 里存在内容为空0 字节的 txt 文件YOLOv7 训练时遇到这类文件不会报错但那张图等于完全没有参与 loss 计算。数量少没事多的话会浪费训练资源。3.2 编写数据集配置文件YOLOv7 训练需要你准备一个data.yaml放在yolov7/data/目录下。写法如下train: dataset/images/train val: dataset/images/val test: dataset/images/test nc: 3 names: [9V_battery, AA_battery, button_cell]这里nc: 3对应三类电池。names的顺序必须和标注文件里的 class_id 一一对应。如果你前面核验发现类别映射不是这个顺序以你核验的结果为准。有个细节YOLOv7 的train.py在读取 data.yaml 时路径是相对于你执行python train.py的工作目录来解析的。所以如果你的数据集放在和yolov7/平级的battery_dataset/下建议配置文件的路径写成绝对路径或者用../dataset/images/train这种相对路径避免每次训练前都要纠结路径对不上。这是我踩过的坑——FileNotFoundError报的路径看着对其实就是相对路径基准点不对。3.3 训练命令与关键超参数选预训练权重时YOLOv7 官方提供了yolov7.pt原版和yolov7-tiny.pt轻量版。对于 2030 张图的规模我建议先用yolov7.pt做迁移学习因为它在大规模数据集上预训练过的特征提取器对电池这种工业小目标的泛化更好。tiny 版本速度快但 mAP 通常会低 3~5 个点后面调起来更费劲。python train.py --workers 4 --batch-size 16 --epochs 100 \ --data data/battery.yaml --cfg cfg/training/yolov7.yaml \ --weights yolov7.pt --device 0 --img-size 640 \ --hyp data/hyp.scratch.p5.yaml参数说明--batch-size 16如果你的显卡显存只有 8GB这个值可能是上限。显存不够时优先降 batch 而不是降--img-size因为检测器对输入分辨率更敏感。--img-size 640数据集的原始分辨率就是 640×640这里保持 640 可以避免二次缩放带来的标注偏移。不要为了省显存降到 416会牺牲小目标召回率。--hyp data/hyp.scratch.p5.yaml训练的超参数文件。默认的hyp.scratch.p5.yaml里hsv_h、hsv_s、degrees等增广参数是给通用目标检测调的遇到电池检测时degrees默认值是 0意思是模型学不到旋转不变性——因为电池姿态在产线上大多是固定的这个默认值反而是合理的。--workers 4数据加载线程数。Windows 下如果报 DataLoader 错误改为 0 或者 2 试试这是 Windows 多进程和 PyTorch 的兼容老问题。训练过程中看 loss 曲线前 20 个 epoch 如果val/obj_loss能降到 0.03 以下后面的 mAP 大概率不会太差。如果训练完发现 loss 在 60 个 epoch 后还是震荡回来看是不是学习率设得太高默认lr00.01在 batch-size 只有 16 的时候偏激进可以改成 0.005 再试。训练跑完后runs/train/exp/weights/best.pt就是验证集上 mAP 最高的权重文件后面推理和部署都用这个。4. 避坑指南复现训练时最容易翻车的四个环节4.1 类别映射搞反模型把所有电池都当成一类现象训练 loss 收敛很快但验证时算出来的混淆矩阵里对角线之外的值特别高9V 电池被识别成干电池的比例大得离谱。原因数据集的 txt 标注里class_id对应的类别和 data.yaml 里的names顺序不一致。Roboflow 导出的数据集它的类别顺序取决于你上传时的标注顺序和数据集 README 里写的「9V、纽扣、干电池」这种自然语言顺序不是一回事。解决在训练前用脚本统计类别 ID 后再人工抽 5 张图用 OpenCV 把标注框画出来对应框内物体确认class_id具体是什么。画框代码很短不要嫌麻烦跳过这步。调整好映射顺序后用第 2 章的统计脚本来回验证两次。这一步做不好后面整个训练等于白做。4.2 视频抽帧带来的相似帧导致数据泄漏现象训练集 loss 低得离谱验证集 mAP 也有 98% 以上但一到自己拍的测试图就原形毕露准确率掉到六成。原因这个数据集的很大一部分图像是从视频里逐段抽帧得到的同一段视频连续几帧里的电池位置、背景、光照几乎完全一致。如果不打乱而是按照文件名的t-9-2、t-9-4、t-9-6顺序划分数据集训练集和验证集里会混入大量「同一个场景的邻居帧」模型等于「记住」了这些图的特征而非学习了电池的通用特征。解决划分数据集之前先按文件名的mp4-t-9-8和IMG_2984-MOV-t-14-6里的视频来源 ID 做分组确保同一个来源的帧全部进入同一个子集要么全在 train要么全在 val而不是单纯随机打散。这样验证集才有真正的参考意义。4.3 纽扣电池漏检严重但干电池识别一切正常现象训练完的模型在测试图上干电池和 9V 电池框得稳稳当当但纽扣电池经常漏掉尤其是图里有多个纽扣电池叠放或者反光强烈的场景。原因纽扣电池尺寸小在 640×640 的图里可能只占 30×30 像素。YOLOv7 默认的 anchor 是针对 COCO 数据集设计的COCO 里小物体比例不高所以较小的 anchor 数量不够。另外纽扣电池表面镜面反射会使得边缘和纹理非常不清晰语义特征薄弱模型很难把它和背景区分。解决直接在原图上做 Mosaic 增广时把纽扣电池的裁剪尺寸调小一点或者自己写一个 RandomCrop 策略专门对包含纽扣电池的区域做局部放大。另一个有效做法是增加输入分辨率——如果显存允许把--img-size从 640 提到 960纽扣电池的像素面积会变为原来的 2.25 倍识别率提升明显。代价是训练时间变长显存占用变成原来的约 2 倍。4.4 验证集 mAP 很高但推理时把抓夹或传送带背景框成电池现象模型在验证集上 mAP 97%可拿到实际产线上一看机械臂的金属夹爪、传送带上的暗色胶带都会触发电池框。原因这是典型的背景过拟合。训练数据的电池基本上都出现在传送带或者桌面这类固定背景上模型学到了「和传送带颜色接近的物体就是电池」这种捷径。验证集里没有这类反例所以 mAP 不会暴露问题。解决使用--multi-scale训练让模型在不同分辨率下适应背景的尺度变化。另外推理时提高置信度阈值从默认的 0.25 提到 0.4 甚至 0.5并结合 NMS 的 IoU 阈值默认 0.45一起调整能过滤掉一部分低置信度的背景误检。如果条件允许采集一些空载没有电池的产线背景图加入训练集标注为空背景强制模型学会「没有目标」的情况。5. 评估与验证97.7% 的识别率是怎么来的怎么测才可信5.1 评估指标与日志解读YOLOv7 训练完成后runs/train/exp/目录下会生成results.txt和results.png里面记录了每个 epoch 的 box_loss、obj_loss、cls_loss、Precision、Recall、mAP0.5 和 mAP0.5:0.95。» 97.7% 这种数字在 YOLO 语境里指的通常就是验证集上的mAP0.5——也就是 IoU 阈值为 0.5 时的平均精度。只盯 mAP0.5 是不够的。我一般会把results.txt里最后 10 个 epoch 的指标取平均再单独看一眼mAP0.5:0.95。这两个指标的差值能说明很多问题指标情况含义mAP0.5 高但 mAP0.5:0.95 低模型能框住目标但框的位置不够准边界框偏差大Precision 高但 Recall 低模型很少误报但容易漏检适合保守场景Recall 高但 Precision 低模型都能框出来但误报多需要调高置信度阈值两者都高模型状态健康可以继续做部署举个例子如果你的 best.pt 在验证集上得到Precision0.983, Recall0.972, mAP0.50.977这就是「97.7% 识别率」的实际构成。精度和召回率都在 97% 以上说明模型在验证集上几乎没有系统性偏科。但别忘了第 4.2 节说的数据泄漏问题——如果划分时不按视频来源分组这个数字会虚高。5.2 用混淆矩阵定位类别间混淆训练完用val.py在测试集上跑一遍python val.py --data data/battery.yaml --weights runs/train/exp/weights/best.pt --img 640 --task test跑完后看runs/val/exp/confusion_matrix.png。对于三类电池理想情况是对角线接近 1非对角线接近 0。最容易出现的问题是纽扣电池那一行的值有一部分落到了「background」那一列——这表示有一部分纽扣电池被模型当成背景了也就是漏检。看到这个情况优先回去做 4.3 节的应对措施。混淆矩阵里如果 9V 电池和干电池之间也有一定混淆多半是数据集里这两类的形状特征在某些角度下过于相似。解决思路是回到标注层面检查是不是某些样本本身标注错误把 9V 电池误标成了干电池。数据集的 txt 文件和图片是分离的肉眼核验工作量大但为了模型上限这步偷懒不得。5.3 推理可视化与具体 badcase 分析跑完评估后还要做一步——随机抽 50 张测试集图片把检测框直接画上去逐张看。这一步不要自动化肉眼过一遍的价值在于发现指标反映不出来的问题import cv2 import torch import random model torch.hub.load(WongKinYiu/yolov7, custom, path_or_modelruns/train/exp/weights/best.pt, force_reloadTrue) img_files [test_imgs/001.jpg, test_imgs/002.jpg, test_imgs/003.jpg] for img_path in img_files: img cv2.imread(img_path) results model(img, size640) rendered results.render()[0] cv2.imwrite(fvis_{img_path.split(/)[-1]}, rendered)这段代码用 PyTorch Hub 加载 best.pt对指定图片做推理并把框画在图上保存。逐张看渲染图时重点关注几个问题框有没有把电池的金属极帽切掉、有没有把两个紧挨的电池框成同一个、反光强烈的纽扣电池上有没有出现重复框。这些细节不好量化但产线上客户的接受度和这些细节高度相关。6. 从训练到产线TensorRT 部署与模型瘦身的实用技巧模型训练好了部署阶段的取舍往往决定最终能不能用。PyTorch 推理在验证和演示阶段没问题但真到产线摄像头 30FPS 的实时场景需要在推理速度和精度之间找平衡。如果目标设备是带 NVIDIA GPU 的工控机优先把权重转成 TensorRT 引擎python export.py --weights runs/train/exp/weights/best.pt --grid --simplify --include tensorrt --device 0转换时注意--grid参数必须带上它在输出层保持原始的网格结构转出来的 TensorRT 模型在端侧部署时预处理更简单。转换完成后用同一批测试图验证 TensorRT 模型的 mAP 和原版 PyTorch 模型差异正常情况下损失不超过 0.5%但速度会快 2~3 倍。如果目标设备是无 GPU 的嵌入式平台Jetson Nano 或者 RK3588 这类那要做的不只是转格式还涉及模型剪枝和量化。torch_pruning库可以对 YOLOv7 做结构化剪枝把 3×3 卷积里贡献小的通道剪掉再微调几个 epoch。剪枝 30% 的通道mAP 损失通常能控制在 1% 以内但推理速度能提升 30% 以上。需要提醒的是剪枝和量化之后组件的验证指标全部要重置——之前测的 97.7% 是浮点精度INT8 量化通常会有 1~2% 的掉点如果掉多了就要尝试混合量化只量化权重不量化激活。我接过一个视觉分拣的小项目模型训练完在 PC 上跑得好好的转到 RK3588 上 INT8 量化后纽扣电池漏检率直接翻倍。后来单独只把检测头保留成 FP16主干做 INT8才把性能拉回来。那以后我习惯在训练阶段就保留一份未量化的浮点 best.pt每次做量化或剪枝都从这份原始权重出发而不是量化后再改——量化是不可逆的原始权重就是后悔药。数据侧也值得再投入一点如果采集到新的产线图片用 x-anylabeling 做半自动标注能省不少时间。先用当前模型跑一批预测结果人工修正错误的框和类别再把新图加入训练集做增量训练。建议每增加 200~300 张新图就重新训练一轮增量训练在 YOLOv7 里直接用--weights best.pt作为起点--epochs 30不用从头死磕。持续迭代半年后这个数据集真的是越养越顺手希望这份拆解帮到你。本文还有配套的精品资源点击获取