ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

COCO JSON转YOLO实战:成人小孩识别数据集训练与避坑指南

COCO JSON转YOLO实战:成人小孩识别数据集训练与避坑指南 简介这是一份面向计算机视觉学习者和算法工程师的成人与小孩识别数据集包含1738张真实场景原始图片并配套COCO JSON格式标注可直接用于目标检测、分类等模型的训练和效果评估解决缺少权威儿童/成人区分标注数据的痛点。压缩包内共1741个文件其中1738个jpg图片涵盖室内外、单人多人、不同年龄等多样场景另含3个json标注文件详细记录每张图片的类别与位置信息便于标准流程调用。资源整体约91.49MB结构简洁已按通用数据集命名方式组织方便按需筛选。目前已有1998人学习使用。对需要构建以年龄为维度的人像识别系统、或作为迁移学习基础数据集的研发人员而言这份资源能有效节省数据采集与标注时间直接进入模型设计与调参阶段。1. 成人和小孩识别数据集1738 张 COCO JSON 标注图片为什么值得跑一遍这个数据集做了一件很朴素的事把 1738 张原始图片整理成 COCO JSON 格式标注用来训练一个区分成人和小孩的检测模型公开的验证识别率是 70.9%。数字不高但正好卡在“能跑通、但还不理想”的位置最适合用来验证从数据解析、格式转换到模型训练的一整套流水线。安防摄像头、儿童防走失、商超客流统计这类场景都需要先判断画面里是成人还是儿童再做后续联动这个数据集把问题收敛到二分类标注形式又是 COCO 标准适合刚入门目标检测的人熟悉 JSON 标注结构也适合老手拿来测自己的数据清洗和难例挖掘流程。2. COCO JSON 标注解剖images、annotations、categories 三段结构拿到这个数据集我建议先别急着开训练。1738 张原始图片不算多真正花时间的不是算力而是搞清标注字段怎么读。COCO JSON 格式的标注文件通常包含三个顶级字段images、annotations、categories任何一个转格式脚本都是围绕这三个字段转的。先花十分钟把结构读懂后面做转换和排错能省下几个小时。2.1 先跑一个统计脚本数图片、数标注、看类别分布常见的交付结构是一个 images 目录加一个 COCO 标注 JSON也有把 train.json 和 val.json 分开给的变体但里面结构一样。我拿到手先看一眼文件长什么样dataset/ ├── images/ # 1738 张原始图片 │ ├── 000001.jpg │ ├── 000002.jpg │ └── ... └── annotations/ └── instances.json # COCO 格式标注真正的检查点是 JSON 里的字段是否完整不是目录长得漂不漂亮。拿 Python 读一遍import json from collections import Counter coco json.load(open(annotations/instances.json, encodingutf-8)) cats {c[id]: c[name] for c in coco[categories]} print(图片数:, len(coco[images])) print(标注数:, len(coco[annotations])) counts Counter(cats[a[category_id]] for a in coco[annotations]) print(标注分布:, dict(counts))这段脚本做三件事先从 categories 数组建立一个 id 到类别名的字典再数 images 数组的条目最后遍历所有 annotations用 category_id 统计每个类别的实例数量。关键参数是 category_id在 COCO 里是整数如果抛 KeyError说明标注文件里出现了 categories 没有定义的类别 ID这是数据集交付阶段就可能带进来的脏数据。跑完你会得到两个很有用的数字。图片数是标题里写的 1738但标注数不一定等于图片数。标注数远小于图片数说明大量图片里没有人被框出来这类样本对正样本贡献是零标注数远大于图片数说明一张图里有多个目标这是目标检测的正常形态。类别分布也很关键如果 adult 和 child 数量差了三倍以上就得提前做类别平衡不然训练出来的模型会倾向把小孩子预测成大人。2.2 images 字段file_name 和宽高是坐标换算的地基images 数组的每一项对应一张原始图片。挑一条出来看{ id: 1024, file_name: images/000001.jpg, width: 1920, height: 1080 }id 是这张图在数据集内部的编号annotations 里的 image_id 指向这里。file_name 是读取图片用的路径有的数据集存的是绝对路径有的存的是相对路径转换脚本里最好都统一处理成绝对路径避免训练时图片找不到。width 和 height 是原图的真实宽高不能随便填。后面把 COCO 的绝对像素 bbox 转成 YOLO 的归一化坐标时要用这两个数做分母宽高一旦写错所有目标框的位置都会整体偏移哪怕训练能跑最终推理结果也是错位的。2.3 annotations 字段bbox、area、iscrowd 的读写要点annotations 数组才是数据集的正文。每一条代表一个目标实例{ id: 1, image_id: 1024, category_id: 1, bbox: [320, 180, 240, 600], area: 144000, iscrowd: 0 }bbox 的四个数分别是左上角 x、左上角 y、框宽 w、框高 h单位是像素。它和很多标注工具导出的中心点格式不一样转 YOLO 时不能直接拿这四个数用要先把中心点和归一化宽高算出来。area 字段理论上等于 wh但有些数据集用多边形算面积会跟 wh 有出入如果训练脚本要用 area 过滤小目标建议自己重新算不要直接信任原始值。iscrowd 为 0 表示普通实例为 1 表示密集人群区域。成人或小孩密集聚集的场景里标注者有时会把一群人框成一个 iscrowd1 的大框这种实例在训练时要跳过因为它不是一个可学习的目标。2.4 categories 字段类别 ID 和 YOLO 的映射关系categories 数组是这个数据集的类别名册成人/小孩数据集通常就两类categories: [ {id: 1, name: adult, supercategory: person}, {id: 2, name: child, supercategory: person} ]supercategory 一般写成 person表示这两类都属于“人”这个父类。这里最大的坑是类别 ID 从 1 开始而 YOLO 的类别编号从 0 开始。转换时如果用 category_id - 1在这个例子里恰好能得到 0 和 1但换个数据集categories 的顺序一换这个减法就会错。可靠做法是重新读 categories 数组自己构造一个 {旧 id: 新 id} 映射而不是假设 id 就是 1、2、3。如果你读的是别人生成的 JSON还会碰到一种很隐蔽的问题字段名的大小写有出入。常见的是 segmentation 字段缺失或者 bbox 写成了 bndbox。转换脚本里建议做好字段校验缺哪个字段就提前报错而不是等训练跑到一半才发现。2.5 用什么打开 COCO 标注 JSON命令行校验最可靠有人问“json 用什么打开”对于几十 MB 的 COCO 文件在线 JSON viewer 很容易卡死VS Code 能看但搜索慢。我一般直接命令行校验python -m json.tool annotations/instances.json checked.json这段命令把 JSON 格式化输出到 checked.json如果文件有语法错误python -m json.tool 会在第一个坏字符处报错并指出具体行号。常见错误是文件末尾多了逗号、引号没闭合或者 JSON 数组中间混进了非 ASCII 字符。只查看某个字段时用 jqjq .categories annotations/instances.jsonjq 是 shell 里处理 JSON 最顺手的工具.categories表示取 categories 键对应的 JSON 数组。参数说明如果 jq 没安装可以用python -c import json;print(json.load(open(annotations/instances.json))[categories])替代效果一样。熟悉这两个操作后面排查标注结构会快很多。3. 把 COCO JSON 转成训练能用格式转 YOLO 与转分类目录COCO JSON 是存储格式不是训练格式。这一步不转换后面的训练脚本全都要自己造轮子。3.1 为什么建议先转 YOLO 而不是直接训练YOLO 系的训练生态最成熟YOLOv8、YOLOv5、YOLO11 都读同一套标注格式每张图对应一个 txt 文件每行写 class x_center y_center width height四个坐标全部归一化到 01。COCO JSON 是先给出全局的 images 和 annotations再通过 image_id 关联如果直接把 COCO JSON 喂给 YOLO 训练脚本自己还要写一个 DataLoader 来解析工作量和踩坑面都更大。常见做法是先把 COCO 转成 YOLO txt再按图片划分 train/val。转换脚本写一次以后换数据集都能复用。另外这套转换逻辑对 labelme 导出的 JSON、cvat 导出的 COCO 一样适用只是字段名稍有差异。3.2 COCO 转 YOLO 的 Python 脚本import json import os from collections import defaultdict coco json.load(open(annotations/instances.json, encodingutf-8)) id_to_cat {c[id]: i for i, c in enumerate(coco[categories])} img_info {img[id]: img for img in coco[images]} anns_by_img defaultdict(list) for ann in coco[annotations]: if ann.get(iscrowd, 0) 1: continue anns_by_img[ann[image_id]].append(ann) os.makedirs(labels, exist_okTrue) for img_id, anns in anns_by_img.items(): img img_info[img_id] h, w img[height], img[width] lines [] for ann in anns: x, y, bw, bh ann[bbox] x max(0, x) y max(0, y) bw min(w - x, bw) bh min(h - y, bh) if bw 0 or bh 0: continue cx x bw / 2 cy y bh / 2 cls id_to_cat[ann[category_id]] lines.append(f{cls} {cx / w:.6f} {cy / h:.6f} {bw / w:.6f} {bh / h:.6f}) base os.path.splitext(img[file_name])[0] with open(os.path.join(labels, base .txt), w, encodingutf-8) as f: f.write(\n.join(lines))逻辑说明先跳过 iscrowd1 的对象因为密集人群不适合当独立目标学然后对每个 bbox 做边界约束把 x、y 限制到不小于 0把宽高限制在图像内部避免出现负数宽度导致归一化比例错误最后一行是 YOLO 格式的完整表达。参数说明id_to_cat 用 enumerate 而不是 id-1是为了兼容类别 ID 不连续的数据集base 用os.path.splitext(img[file_name])[0]取出图片主文件名保证 images/000001.jpg 对应 labels/000001.txt。运行完检查一下 labels 目录每张有目标的图片都应该有同名 txt空 txt 表示这张图没有可用标注。3.3 如果只是做分类按 bbox 裁剪出成人/小孩图片有些任务不需要定位只想判断画面里有没有儿童。这时候可以把每个 bbox 裁剪下来按类别建文件夹import json import os from PIL import Image coco json.load(open(annotations/instances.json, encodingutf-8)) img_map {img[id]: img for img in coco[images]} cats {c[id]: c[name] for c in coco[categories]} for ann in coco[annotations]: if ann.get(iscrowd, 0) 1: continue img img_map[ann[image_id]] im Image.open(os.path.join(images, img[file_name])) x, y, w, h ann[bbox] pad 10 x0 max(0, x - pad) y0 max(0, y - pad) x1 min(img[width], x w pad) y1 min(img[height], y h pad) crop im.crop((x0, y0, x1, y1)) out_dir os.path.join(crops, cats[ann[category_id]]) os.makedirs(out_dir, exist_okTrue) crop.save(os.path.join(out_dir, f{ann[image_id]}_{ann[id]}.jpg))这段代码先把所有图片读成 PIL Image再按 bbox 坐标裁剪。pad10 是经验值成人或小孩的标注框往往紧贴身体完全不扩边会把衣服边缘裁掉pad 太大会把旁边的人也带进来。保存文件名带上 image_id 和 ann_id是为了后面回溯哪张裁剪图来自哪张原图、哪个标注实例一眼能查清楚。裁剪完之后crops/adult 和 crops/child 就是两个分类文件夹直接用 ResNet 或者 MobileNet 做二分类都可以。标题里报的 70.9% 如果指的是分类识别率那这个方案更贴近原意。3.4 划分 train/val固定随机种子按目录移动小数据集划分要格外小心验证集比例太低指标波动会很大随机种子不固定两次训练结果没法比较。我一般把 85% 的图放训练15% 放验证import random from pathlib import Path import shutil random.seed(42) image_pool list(Path(images).glob(*.jpg)) random.shuffle(image_pool) split int(len(image_pool) * 0.85) for phase, files in [(train, image_pool[:split]), (val, image_pool[split:])]: img_dir Path(fimages/{phase}) lbl_dir Path(flabels/{phase}) img_dir.mkdir(parentsTrue, exist_okTrue) lbl_dir.mkdir(parentsTrue, exist_okTrue) for img_path in files: shutil.move(str(img_path), str(img_dir / img_path.name)) label_path Path(labels) / (img_path.stem .txt) if label_path.exists(): shutil.move(str(label_path), str(lbl_dir / label_path.name))这个过程把 images/train、images/val、labels/train、labels/val 四个目录建好图片和同名标签一起移动。random.seed(42) 保证每次跑出来的划分完全一样后面调参数时指标变化只来自模型不来自数据抖动。split 用 int 取整1738 张图大约 1477 张训练、261 张验证验证集样本数接近三百已经能看出大概水平。注意这里有一个前提如果原始图片是同一个视频连续帧截出来的直接随机分 train/val 会让相邻帧同时出现在两边验证集指标虚高。更严格的划分应该按视频片段或场景分组这个在避坑章节再展开。4. 用 YOLOv8 训练成人和小孩识别参数与 70.9% 准确率怎么来的数据准备好之后训练本身反而是最机械的一步。关键是配置文件和参数别乱调。4.1 先写 data.yaml类别顺序必须和转换脚本一致YOLOv8 用 YAML 文件描述数据和类别我的写法是path: . train: images/train val: images/val nc: 2 names: 0: adult 1: childpath 是当前项目根目录train 和 val 是图片目录的路径。YOLO 会自动把路径里的 images 替换成 labels所以 labels/train、labels/val 不需要写进 YAML。names 的顺序必须和转换脚本里 enumerate(categories) 的顺序保持一致否则 adult 和 child 就反了。参数说明nc 是类别数这里必须等于 2。names 的键从 0 开始值可以换成别的语言但顺序不能动。训练前我会把标注文件里第一行打出来看一眼确认 class 号对得上。4.2 训练命令与 5 个核心参数在 1738 张的小数据集上我习惯用轻量模型起手不要上来就上 yolov8xyolo detect train \ modelyolov8n.pt \ datadata.yaml \ epochs60 \ imgsz640 \ batch16 \ lr00.01 \ patience15参数选择整理成一张表方便边跑边对照参数推荐值说明modelyolov8n.pt从 COCO 预训练权重迁移person 类别知识可以直接继承epochs5060小数据集 30 轮能收敛60 轮看波动再多容易过拟合imgsz640原图大、小目标多时640 会缩小显存够可以试 1280batch8168GB 显存从 16 起步显存不够降到 8lr00.01 或 0.005类别不平衡、数据量少时0.005 更稳patience15验证集指标连续 15 轮不升自动停防止无效等待modelyolov8n.pt 是从 COCO 上预训练过的权重它可以识别 person 这个大类只是区分不了 adult 和 child用它做初始化比从零训练收敛快很多。imgsz640 是很多小模型的默认值但这类数据集里经常有远距离小尺寸小孩缩小到 640 会把小目标细节丢掉要是 70.9% 这个数字一直上不去值得把 imgsz 提到 960 或 1280 试试代价是训练时间和显存翻倍。lr0 是最容易被忽略的玄学参数。小数据集上 lr00.01 偶尔会让 loss 在前几个 epoch 抖动0.005 反而更平滑。这不是固定的如果 0.005 收敛慢再改回 0.01 重新训一轮对比。4.3 评估时看哪几个数mAP、Precision、Recall 和报告里的 70.9%YOLO 训练完会输出 P、R、mAP50、mAP50-95。注意P 和 R 是每个类别的平均值mAP50 是 IoU 阈值 0.5 下的平均精确率均值。标题里写的 70.9% 如果是一个识别率更可能是验证集上“预测的类别是否正确”的准确率不是 YOLO 直接输出的某一个指标。比如验证集有 300 个目标模型把其中 213 个实例的类别判断对了就是 71% 左右。这几个指标的关系我用表拆开指标公式在这个场景里看什么Accuracy(TPTN)/总数成人/小孩类别判断正确比例PrecisionTP/(TPFP)框出的人里类别蒙对的占多少RecallTP/(TPFN)真实存在的成人和小孩被找回多少mAP50AP 按类别平均定位框 类别判断的综合结果所以只看准确率一件事不够。P 高 R 低说明模型把小孩漏掉了很多准确率被大量成人样本撑起来了R 高 P 低说明模型把大人频繁认成小孩在安全监控场景里会触发误报警。训练完成后一定要跑一遍混淆矩阵看清 adult 和 child 各自被认错的比例。5. 避坑COCO 数据集训练成人和小孩模型的常见问题这一节全是实际踩过的坑拿出来对照能少走弯路。5.1 category_id 对不上两个类别训练完混成一个现象训练跑完预测框全输出成 adultchild 类别一个都不出或者两个类别输出完全相同的分布。原因转换脚本里用了 category_id - 1但数据集里 adult 的 id 是 1child 的 id 是 2那没问题如果 id 是 5 和 7减 1 后变成 4 和 6而 data.yaml 里 nc2类索引直接错乱。解决不要用手写减法。用{c[id]: i for i, c in enumerate(coco[categories])}把容器 id 重新编号为 0、1。训练前打印一下 id_to_cat 字典再对比 data.yaml 的 names两张表对得上再开训。5.2 bbox 越界训练 loss 不降甚至出现 nan现象loss 曲线掉到某个水平就停住或者直接变成 nan训练继续跑结果越来越差。原因标注框的 x w 超过图片真实宽度常见于二次修改后的数据没重新裁剪或者标注工具导出的浮点坐标被四舍五入后越界。归一化坐标出现大于 1 的值模型学不到正确位置。解决转换脚本里加边界裁剪把 x、y 限制到 0把宽高限制到图像边界宽高小于 1 像素的框直接跳过。这段逻辑在每个 dataset 可能不一样但原则相同进训练集之前任何一个 bbox 必须完全落在图像内。5.3 小孩漏标严重召回率低一大截现象评估结果里 child 的 recall 只有 0.5大量真实小孩没有被框出来。原因标注人员对远距离、小尺寸的目标不敏感。一个成年人站在前面后面背景里蹲着两个小孩标注者往往只框了成年人。解决先用预训练好的 person 检测模型在全部 1738 张图上跑一遍把低置信度的候选框导出再打开 cvat 或 labelimg 人工确认。重点看那些“成人占主体、小孩在背景里”的图这类图最容易补回有效样本。补标注之后重新转 YOLO 格式child 类别的 recall 通常能涨几个点。5.4 类别不平衡成人是小孩的三倍以上现象模型容易把小孩预测成成人尤其在远距离、模糊、遮挡严重的样本上。原因训练集里 adult 类别数量明显多于 child模型只需要把多数类预测对就能拿到很低的 loss。解决先不要简单重复采样优先做按类别的数据增强。对 child 类多做一些 mosaic、平移、翻转和随机色彩抖动比复制几遍同一批图更有效。同时检查验证集里 child 的数量够不够如果验证集只有几十个 child 样本70.9% 这个数字本身的置信度就很低。5.5 验证集和训练集有重复画面识别率虚高现象训练时 mAP 很高一到新场景视频上识别率直线掉到 50% 以下。原因数据集是从同一个视频连续帧截出来的随机划分 train/val 时相邻两帧分别进了训练集和验证集。模型记住了背景纹理而不是人的特征在验证集上表现好换个场景就现原形。解决按视频片段或时间窗口划分把同一个视频的帧尽量归到同一侧。做法上可以先把文件名按视频编号分组再对视频编号做随机划分而不是对图片逐张划分。这样才能判断模型是不是真的学会了区分成人而不是背下了背景。6. 把 70.9% 往上提数据增强、难例挖掘与二次标注终于到了最有意思的环节。70.9% 在小数据集里不算差但离“能交给业务用”还差一截。我的经验是往上提升最划算的路径不是换大模型而是做难例挖掘。先用已经训好的模型去推理训练集把所有预测框按置信度排序重点关注置信度在 0.30.6 之间的样本。这些框意味着模型自己也拿不准是成人还是小孩它们往往就是数据里最有信息量的部分。把这些低置信度框对应的图片挑出来对照 COCO 标注检查你通常会发现三类问题一是标注框没画准把小孩的半截身子框进去了二是漏标模型检测到了目标但标注里没有三是极端样本比如背影、侧脸、遮挡严重这类样本本身就需要单独处理。处理完这些难例之后更新标注、重新转 YOLO 格式、再训一轮一般能往上拉 23 个点。训练完成后用混淆矩阵验证重点看 adult 被认成 child 的比例。在防走失场景里把成人误判成小孩会触发不必要的提醒但这个错还算安全真正危险的是把小孩误判成成人导致漏报。所以调整阈值或类别权重时我会刻意压低后者。我现在的习惯是拿到任何新数据集先花半天做探索性统计和标注质量检查再开训练训练完先看错图和混淆矩阵而不是急着调学习率。这个顺序用下来比盲目堆训练轮数有用得多。Handling 小数据集没有玄学就是把脏数据洗出来、难例补进去70.9% 只是一个起点跑完这一轮你手里会握着完整的标注检查工具链这才是这套数据集真正的价值。希望帮到你。本文还有配套的精品资源点击获取
返回列表