
简介面向目标检测算法学习与落地验证的生猪检测数据集采集自猪圈摄像头真实监控场景单类别pig图像以YOLOV5标准目录格式整理训练集929张、验证集136张可直接接入YOLOv5等框架训练无需额外转换。所有RGB图片分辨率覆盖640至1080单张图像包含多个目标适合研究密集目标场景下的检测性能。资源共2000个文件主要由1066个txt标签文件、933张jpg图像及1个py可视化脚本组成压缩包约97.43MBtxt为YOLO格式标注py脚本可随机读取一张图片并绘制边界框后保存便于快速检验标注质量。此外数据已按datasets-images-train与datasets-images-val分好目录训练验证划分明确。目前已有609人学习使用适合目标检测初学者、生猪养殖智能化项目开发者作为训练素材。1. 猪圈摄像头下的生猪检测一份YOLOv5目录格式数据集拿来就能训做猪只检测的人和做行人检测的人踩的坑很不一样猪圈摄像头架得高、光线差、猪又互相贴着挤目标密集且遮挡严重标起来费劲训起来也容易翻车。这份数据集是真实猪圈摄像头拍的 RGB 截图分辨率在 640 到 1080 之间已经按 YOLOv5 目录格式整理成训练集 929 张、验证集 136 张只标一个类别 pig每张图上的猪头数量普遍很多天生就是练密集目标检测的料。对刚跑通 YOLOv5、手里缺真实场景数据的人来说它不用改目录、不转格式、不配标签直接就能喂给 train.py对老手来说它又能用来测 anchor、测 NMS 参数、测模型在小目标密集场景下的上限。下面我把目录结构、标签内容、可视化脚本和训练链路一起拆开讲。2. 数据集拆解目录结构、标签格式与可视化脚本2.1 目录结构与文件命名YOLOv5 标准 Layout先看目录怎么摆。拿到手解压后datasets 下面直接是 images 和 labels 两个大区各自再分 train、val。这是 YOLOv5 最认的布局不用额外写转换脚本符合 ultralytics 仓库对数据目录的约定。datasets/ ├── images/ │ ├── train/ │ │ ├── vlcsnap-2022-01-09-19h41m40s412_png.rf.5888b784a5b4f702503b2ad1620f25fd.jpg │ │ ├── vlcsnap-2022-01-08-18h26m24s718_png.rf.6d71b521ce80b3938d7ee6b8c0ea9760.jpg │ │ └── ... 共 929 张 │ └── val/ │ ├── vlcsnap-2022-01-09-19h41m43s620_png.rf.fa9060b3f23b6ce36fe653452c14d375.jpg │ └── ... 共 136 张 └── labels/ ├── train/ │ ├── vlcsnap-2022-01-09-19h41m40s412_png.rf.5888b784a5b4f702503b2ad1620f25fd.txt │ └── ... 共 929 个 └── val/ └── ... 共 136 个每个图片文件对应的 txt 文件在 labels 目录下的同名子目录里扩展名换一下。文件名保留了原始视频帧的命名vlcsnap 开头说明是从 VLC 播放器截帧出来的后面的日期时间就是帧的时刻。这个信息很有用train 和 val 里都有不同日期的帧说明划分时不是只按某一晚的连续帧来切的时间分布是打散的能降低相邻帧高度相似带来的过拟合风险这是很多随手整理的数据集做不到的。2.2 标签 txt 内容与类别映射打开任意一个 txt一行代表一个目标共 5 个字段0 0.541406 0.486111 0.090625 0.158333 0 0.227344 0.441667 0.070313 0.119444 0 0.689844 0.695833 0.079688 0.180556字段含义依次是类别编号、归一化中心 x、归一化中心 y、归一化宽度 w、归一化高度 h。注意这里的坐标全部除了图像宽高范围在 0 到 1 之间和 YOLOv5 的 letterbox 处理直接兼容不需要自己再归一化。类别编号是 0对应附带 txt 类别文本文件里的 pig。整个数据集只有这一个类别所以单类别训练时不需要改任何类别数。有个细节值得确认每张图的 txt 行数是不是和图上猪头数一致。多标、漏标在密集场景很常见。我一般会先统计一下每张 txt 的行数分布看看有没有 0 行文件——如果 train 里有空 txt训练时这张图会被当成背景图影响不大但如果 val 里有空 txt做 mAP 评估时它会拉低召回率容易被误判成模型漏检其实是标注缺失。这份数据集的简介里没提空标签情况到手后建议先扫一遍。2.3 可视化脚本怎么把边界框画回图上项目附带了一个可视化 py 文件作用是把某张图片和它对应的 txt 标签读进来画上边界框后输出到当前目录。脚本不用改就能跑入口通常是传入图片路径脚本自动在同目录下找同名 txt。以下是这类脚本的核心逻辑我按常见实现补全了一份import cv2 import sys from pathlib import Path def draw_boxes(img_path, out_dir.): img cv2.imread(str(img_path)) if img is None: raise ValueError(f无法读取图片: {img_path}) h, w img.shape[:2] txt_path Path(img_path).with_suffix(.txt) if not txt_path.exists(): print(f警告: 找不到标签 {txt_path}) return boxes [] with open(txt_path, r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue cls, cx, cy, bw, bh (float(x) for x in parts) x1 int((cx - bw / 2) * w) y1 int((cy - bh / 2) * h) x2 int((cx bw / 2) * w) y2 int((cy bh / 2) * h) boxes.append((x1, y1, x2, y2)) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, pig, (x1, max(0, y1 - 5)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) out_path Path(out_dir) / fvis_{Path(img_path).stem}.jpg cv2.imwrite(str(out_path), img) print(f已保存: {out_path}共 {len(boxes)} 个目标) if __name__ __main__: if len(sys.argv) ! 2: print(用法: python visualize.py 图片路径) sys.exit(1) draw_boxes(sys.argv[1])逻辑说明读取图片后先拿宽高再把 txt 里的归一化中心点和宽高反算成像素坐标。x1、y1 是左上角x2、y2 是右下角用 max 防止类别文本在画布上方越界。这一步就是可视化脚本的全部核心没有任何玄学就是坐标反变换。参数说明out_dir控制输出目录默认当前目录颜色 (0,255,0) 是绿色 BGR适合在暗色猪圈背景上显示字体大小 0.6 对 640 分辨率合适如果是 1080 图片可以调到 0.8。运行方式python visualize.py datasets/images/train/vlcsnap-2022-01-09-19h41m40s412_png.rf.xxx.jpg脚本会在当前目录生成 vis_ 开头的新图。如果一次性想检查很多张可以加一层循环遍历某个目录下所有图片批量生成预览图这个在最后一章细说。3. 把数据集接到 YOLOv5 训练链路从配置 yaml 到跑通 train.py3.1 数据集 yaml 怎么写YOLOv5 官方仓库训练时需要一个 yaml 文件声明 train 和 val 路径以及类别名。这份数据集的目录名是 datasets我一般把 yaml 放在 YOLOv5 工程根目录下# pig.yaml train: datasets/images/train val: datasets/images/val nc: 1 names: [pig]train 和 val 指向图片目录即可YOLOv5 会自动去对应 labels 目录找 txt它默认把 images 替换成 labels。这里有个新手常犯的错path 参数不是必须的如果你写了绝对路径务必保证路径里没有中文和空格。数据集本身是相对路径友好的直接放在 YOLOv5 工程目录下最省事。参数说明train、val 字段除了相对路径也可以用绝对路径但绝对路径换机器后就要改nc 是类别数这里固定 1names 列表的顺序要和 txt 里的类别编号对应只有 0 号所以列表里放一个 pig。写完 yaml 后可以用一行代码快速验证python -c import yaml; print(yaml.safe_load(open(pig.yaml)))能正常输出字典说明 yaml 语法没问题再进下一步。实际使用中我还会顺手检查一下图片数量是否和标签数量一致防止 images 或 labels 里多出孤儿文件。孤儿文件会让训练时报错提示找不到对应标签。3.2 训练命令与参数选择因为数据集只有 1065 张图929 训练 136 验证属于小型数据集训练参数不能照搬 COCO 那套。我先给一份能直接跑的基线命令python train.py --data pig.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 100 --workers 4 --cache ram --device 0逻辑说明--weights yolov5s.pt表示用 s 模型预训练权重做迁移学习小数据集从 COCO 预训练开始收敛更快也更稳--cache ram把图片一次性加载进内存避免每个 epoch 重复读磁盘这个数据集总共 98MB完全放得下能显著减少训练时间--img 640是输入分辨率和数据集图片原始尺寸的下限一致不需要强行拉高。参数说明batch 要看显存16 适用于 8GB 左右显卡如果显存小就降到 8epochs 100 对 1 类别小数据集基本够用我看 loss 曲线到 60 轮左右会稳定workers 在 Windows 上建议设 0否则有时会卡数据加载device 0 是第一块 GPU没有 GPU 就改成 cpu但训练速度会慢十倍以上。训练过程中要盯两个指标box_loss和obj_loss。如果 obj_loss 震荡明显优先检查是否开了多尺度训练。YOLOv5 默认开了--multi-scale对分辨率不统一的数据集有好处但也可能让小目标样本在缩放时变得过小。这份数据集图片分辨率在 640 到 1080开多尺度没问题但如果发现后期 mAP 不稳可以把这个参数关掉固定 img 尺寸训练。3.3 验证与测试流程训练结束后官方会自动跑一次 val在 runs/val/exp 目录下生成混淆矩阵和 PR 曲线。如果想手动对验证集再做一次评估用 val 命令python val.py --data pig.yaml --weights runs/train/exp/weights/best.pt --img 640 --task val --verbose这个命令输出的 mAP0.5 和 mAP0.5:0.95 是关键指标。对单类别密集场景单看 mAP0.5 容易自我感觉良好我会重点看 mAP0.5:0.95它和框的定位精度强相关猪只密集时边界框稍微偏一点就可能在 IoU 0.5 时重叠拉低分数。--verbose参数会打印每一张图的检测结果方便快速定位哪些图漏检严重。注意 val.py 里的--task val用的是 yaml 里的 val 路径如果目录结构没问题不需要额外改代码。测试视频或新图片时用 detect.pypython detect.py --weights runs/train/exp/weights/best.pt --source test_pig_video.mp4 --conf 0.25 --iou 0.45 --save-txt这个命令会输出带框的视频帧和 txt 检测结果。我习惯把检测 txt 和标签 txt 放在同一目录下用脚本算一下每张图的误检和漏检数量比肉眼盯着视频效率高得多。4. 密集小目标场景的调参思路anchors、imgsz 与置信度阈值4.1 为什么默认 anchor 不理想YOLOv5 默认 anchor 是按 COCO 80 类目标统计出来的从几像素的小目标到几百像素的大目标都有。猪圈摄像头拍到的猪只在 640 分辨率下单个目标宽度往往只有 40 到 120 像素属于中小目标偏多而且猪身体互相贴在一起先验框的形状如果太方正回归起来就吃力。我拿到数据集后第一件事是统计所有标签的宽高比分布看它偏向横条还是竖条。写个临时脚本import glob import numpy as np txts glob.glob(datasets/labels/train/*.txt) wh [] for t in txts: with open(t) as f: for line in f: parts line.split() if len(parts) 5: w, h float(parts[3]), float(parts[4]) wh.append([w, h]) wh np.array(wh) print(目标数量:, len(wh)) print(宽度中位数:, np.median(wh[:, 0])) print(高度中位数:, np.median(wh[:, 1])) print(宽高比中位数:, np.median(wh[:, 0] / wh[:, 1])) print(面积中位数:, np.median(wh[:, 0] * wh[:, 1]))这个统计能直接指导要不要用--anchor auto重新生成 anchor。YOLOv5 训练时加--anchor auto它会在前 50 轮用 k-means 统计当前数据集的先验框替换掉默认值。对这份数据我建议开启因为单类别目标形状高度一致自动生成的 anchor 会比 COCO 默认值精确得多。注意如果训练中途发现 anchor 变化导致 loss 突然跳一下那是正常的k-means 重新聚类后模型需要重新适应只要后面能降下来就没问题。4.2 改 anchor 与输入尺寸修改输入尺寸是另一条路。数据集图片是 640 到 1080说明摄像头截图的分辨率不是统一值。训练时--img 960能保留更多小目标细节但显存占用会变大batch 就要相应减小。我的经验是先跑 640 基线看漏检集中在哪些尺寸的目标如果小目标漏检多再试 img 960batch 从 16 降到 8。同时可以手动把自动生成的 anchor 写进模型 yaml。YOLOv5 的 yolov5s.yaml 里 anchor 定义是这样的格式anchors: - [10,13, 16,30, 33,23] # P3/8 - [30,61, 62,45, 59,119] # P4/16 - [116,90, 156,198, 373,326] # P5/32每行三个 anchor分别对应 8 倍、16 倍、32 倍下采样特征图。你可以在训练完一轮后去 runs/train/exp 目录下看生成的 anchor 文件用 YOLOv5 提供的 k-means 脚本重新得到新 anchor再替换进来训第二轮。这里有个坑anchor 顺序必须从小到大排列否则训练会震荡Loss 出现明显的锯齿状。我通常在--anchor auto跑完一轮后从 runs/train/exp/anchors 目录取回 anchor 值写死到 yaml 里再固定--noautoanchor训练这样能复现结果也方便调参对比。4.3 后处理 NMS 与 conf 阈值模型训练好以后推理时的置信度阈值和 NMS 的 IoU 阈值对密集目标影响非常大。猪只互相遮挡同一个猪可能会被预测出多个重叠框默认 NMS IoU 阈值 0.45 会把重叠的猪框合并掉导致少检设高了又会把本应合并的框留下来出现大量假阳。我的习惯是从 0.25 置信度开始看效果猪只特征明显真目标的 conf 通常都在 0.5 以上。python detect.py --weights runs/train/exp/weights/best.pt --source test_images/ --conf 0.25 --iou 0.45 --save-txt--save-txt会把检测结果保存成 txt方便对比标签。密集场景下我一般把--iou调到 0.5让 NMS 更积极合并框减少同一目标重复框。如果发现漏检优先降--conf而不是降--iou因为降 iou 会把相邻猪合并损失真实目标。这个调参顺序不要反。还有一个技巧在 detect.py 前加一行--agnostic-nms对不同类别不做区分直接 NMS单类别场景下这个参数没有意义但如果你后续加了第二类比如人开启它反而会把猪和人叠在一起时误合并所以我一般保持默认。5. 避坑与常见问题七个真实翻车点5.1 标签坐标归一化与图像尺寸不匹配现象用可视化脚本画框发现框和猪的位置错位有的框偏向画面一侧。原因txt 的坐标是归一化的反算像素时必须乘图片真实宽高。如果图片是从视频截帧后又被缩放过的宽度高度变了但 txt 没有重新归一化就会错位。常见于手动截图后直接拿来训练。解决先确认每张图片的实际分辨率再检查 txt 里的坐标值是否全部在 0 到 1 之间。如果有大于 1 的值说明是像素坐标混进来了需要重新归一化。对这份数据集由于它本身就是从 VLC 截帧并同步生成的标注理论上不会出现但下载到其他数据集时这个坑必查。我的脚本里加了一个断言for v in (cx, cy, bw, bh): assert 0.0 v 1.0, f坐标超出范围: {line}5.2 训练集和验证集重叠导致指标虚高现象训练 loss 正常下降验证 mAP 高达 0.98但到实际猪圈里跑检测漏检惨不忍睹。原因train 和 val 来自同一个视频片段相邻几帧的图像信息几乎一致模型相当于见过答案。很多公开数据集按帧连续划分前后 5 帧被分进两个集合就出现这个现象。解决拿到数据集后要检查相同时间戳的帧是否同时出现在 train 和 val。这份数据集文件名里带日期时间可以脚本提取时间戳计算 train 和 val 文件名的时间戳集合交集。如果交集比例高就要手动打散重建验证集。我一般会保留时间隔离同一秒内的帧只能出现在一个划分里。示例脚本from pathlib import Path import re def get_ts(name): m re.search(r(\d{4}-\d{2}-\d{2}-\d{2}h\d{2}m\d{2}s\d{3}), name) return m.group(1) if m else None train_ts {get_ts(p.name) for p in Path(datasets/images/train).glob(*.jpg)} val_ts {get_ts(p.name) for p in Path(datasets/images/val).glob(*.jpg)} print(交集:, len(train_ts val_ts))如果交集不为零就要重新划分验证集。常见做法是按时间戳排序后每 10 帧取 1 帧进 val其余进 train保证验证集和训练集在时间上不重合。5.3 摄像头截图噪点多导致漏检现象模型在验证集 mAP 还行但在夜间或强逆光截图上漏检率明显上升。原因猪圈摄像头画质差、红外补光、灰尘遮挡很多截图有大量噪点。数据增强里的 mosaic 和 hsv 变化能缓解一部分但本质问题还是训练集中这类低照度样本比例不够。解决把可视化脚本跑一遍挑出低亮度样本用--cache训练时观察它们的 loss 是否偏高。如果确实低照度样本少可以把数据集的图像做亮度抖动增强或者用简单的 gamma 校正复制一批样本。注意这份数据集的图片分辨率到 1080说明有部分帧是高清源混入低清摄像头截图时模型会偏向学习高清纹理容易忽略低清图的细节。我通常会在训练集里手动混入 10% 的模糊副本让模型对清晰度差异不那么敏感。5.4 类别编号错位单类别也要检查重复标注现象训练日志显示 nc1但可视化时发现某些框画到了疑似猪以外的东西上比如食槽、人腿。原因数据标注粒度不统一有些标注员把露出的猪头标了有些把半截身体也标了。单类别数据集也会出现语义不一致模型学到的特征就混乱。解决用可视化脚本随机抽 200 张图人工过一遍重点看面积特别小和特别大的框。如果发现同类目标框选范围差异过大考虑统一标注规范或者用清洗脚本去掉面积小于 0.01 的框。密集场景下小框更有可能是误标。我常用的面积过滤area bw * bh if area 0.005: print(疑似过小框:, img_name, line)5.5 可视化脚本路径分隔符与中文目录问题现象在 Windows 下运行脚本提示找不到 txt 或保存路径报错。原因脚本里用了 Path 拼接但 Windows 下如果参数带了中文绝对路径OpenCV 的 imread 和 imwrite 对非 ASCII 路径支持不稳定会静默失败返回空图或写入失败。解决把脚本和图片放在同一个纯英文路径下运行如果必须用中文路径就用cv2.imdecode先读取字节再解码。这类问题在 Linux 服务器上基本不存在但在本机预览时会翻车。我的习惯是可视化脚本里统一用 pathlib输出路径指定为绝对英文路径问题就少一半。5.6 训练时类别权重失衡单类别也会失灵现象训练到后期预测框大量集中在画面中央边缘的猪很难检出。原因猪圈摄像头通常是俯视画面中心是食槽区域猪聚集多边缘走道猪少。模型学到的位置先验会偏向中心边缘目标被当作背景。这和类别无关是空间分布不均衡导致的。解决在训练时开启 mosaic 增强它会把四张图拼在一起打乱空间分布缓解位置先验。如果开了 mosaic 还不够可以手动检查 val 中边缘目标的召回率单独统计中心区域和边缘区域的检测结果。常见做法是在标注脚本里按坐标把目标分成 center 和 edge 两组分别计算 AP定位问题。5.7 多尺度训练导致 val 指标不稳定现象同一份权重跑两次 valmAP 差 2 个百分点以上。原因YOLOv5 的 val.py 在推理时也会随机选择尺度取决于--img和--augment参数如果没固定--img尺寸结果会有波动。解决评估时务必固定--img并且不加--augment。我通常在提交结果前用同一命令连续跑三次 val取中位数作为最终指标。如果三次波动超过 0.5%说明训练还没收敛或 val 集太小。这份数据集的 val 只有 136 张波动稍大是正常的看趋势不要看单次数值。6. 用可视化脚本做数据体检三个小技巧让标注质量看得见6.1 抽检策略按目标密度分层抽样均匀随机抽图容易漏掉极端情况。我习惯先把训练集图片按 txt 行数分成三档1 到 3 个目标的稀疏图、4 到 8 个的普通图、9 个以上的密集图每档各抽 20%而不是全量随机。这样能同时看到稀疏场景的误检和密集场景漏检。6.2 在脚本里加一列统计输出每张图的框数在可视化脚本的写图之前插入一行统计把所有框数打印出来print(f{img_path.name}: {len(boxes)} pig(s))如果发现某张图框数明显低于肉眼可见的猪数量就是漏标及时回补。这个检查比看 mAP 数字更直观mAP 是汇总值单图标注质量问题会被平均掉。6.3 批量生成预览图用缩略图给整个集合作体检单张逐图看效率低我写了一个批量版本把每张图缩到 320 宽画框后拼成 4x4 网格生成一张总预览图。这样几百张图很快就能扫完。代码思路是循环调用 draw_boxes最后用 numpy 的 hstack 和 vstack 拼接不额外依赖库。从那以后我每次拿到新数据集都强制走一遍「分层抽检 → 统计框数 → 拼图预览」这三步再决定要不要进训练管道。视觉上过一遍比任何指标都让人安心。希望帮到你。本文还有配套的精品资源点击获取