尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

半自动标注实战:YOLO预标注与人工复核闭环指南

半自动标注实战:YOLO预标注与人工复核闭环指南 简介这份资源面向目标检测方向的学习者与工程实践者提供一套基于YOLO系列的半自动标注数据集代码用于缓解大规模数据标注耗时耗力的问题。其核心思路是先人工标注少量样本训练出初版模型再用该模型对剩余图像进行预标注最后人工复核修正误检从而显著降低标注成本。压缩包共5个文件以2个Python脚本为主另含1个yaml数据集配置、1个使用说明txt及1个系统隐藏文件整体约7KB体积轻量、便于快速部署。脚本中已预留图片路径、xml输出路径、模型权重与数据配置等可修改参数读者只需按说明调整后运行即可完成批量预标注同时附带仅推理检测的脚本方便单独验证模型效果。目前已有523人学习下载适合希望搭建半自动标注流程、提升数据集制作效率的开发者参考使用。1. 半自动标注到底省了什么从 auto_label.py 到人工复核的闭环标注一百张图还能靠耐心标注一万张就只能靠工程。目标检测项目里真正拖慢进度的往往不是训练而是把原始图片变成 YOLO 格式标签这一步。纯手工拉框一天能出三百张就算手快纯自动伪标签又容易把漏检和误检直接固化进数据集越训越偏。半自动标注的思路很直接先用一个已经能跑的 YOLOv 系列模型对未标注图片做推理把预测框写成 YOLO txt再让人只做「删、改、补」三件事。它解决的是标注吞吐量问题适合手上有少量已标注数据、想快速扩充数据集的目标检测从业者也适合刚接触 YOLOv8 训练自己数据集、不想一上来就被标注劝退的新手。核心脚本通常叫 auto_label.py它不神秘本质就是推理加格式转换。2. 半自动标注的工程链路模型推理、阈值过滤与 YOLO txt 落盘2.1 为什么选 YOLOv 系列做预标注模型半自动标注对预标注模型的要求和最终上线模型不一样。上线模型追求精度极限预标注模型追求「召回优先、速度够快、格式好接」。YOLOv 系列在这三点上比较均衡推理速度快单卡批量跑几千张图不需要等太久输出直接是框加类别加置信度转 YOLO txt 只涉及坐标归一化生态成熟YOLOv5、YOLOv8 到 YOLOv11 的推理接口基本一致换版本成本低。常见做法是拿一个在 COCO 或自有小数据集上训过的权重当起点如果目标类别和预训练类别重合比如人、车、常见动物直接零样本预标注就能用如果类别是自定义的比如鸟类目标检测的数据集里那些细分类别就得先手工标几百张训一个初版模型再用它去预标注剩下的。这里有个选型判断预标注模型的 mAP 不需要很高但召回要尽量高。因为漏检的框人工补起来费时误检的框人工删起来很快。所以推理时置信度阈值要压低通常设 0.15 到 0.25让模型多报一些把筛选压力交给人工。这和最终评估模型时设 0.5 阈值的逻辑正好相反很多人第一次做半自动标注会在这里翻车直接沿用评估阈值结果预标注框少得可怜人工补框比从头标还累。2.2 auto_label.py 的最小可运行实现下面这段代码是半自动标注脚本的骨架基于 ultralytics 的 YOLO 接口。它做四件事加载模型、遍历图片、推理、把结果写成和图片同名的 txt。代码里保留了置信度和 IoU 两个关键参数方便你按自己的数据调。# auto_label.py # 依赖: pip install ultralytics opencv-python tqdm from pathlib import Path from ultralytics import YOLO import cv2 from tqdm import tqdm # 参数区这三个值决定预标注框的多少 MODEL_PATH yolov8n.pt # 预标注权重可换成自己训练的 best.pt IMG_DIR datasets/raw/images LABEL_DIR datasets/raw/labels CONF_THRES 0.20 # 预标注阶段压低保召回 IOU_THRES 0.45 # NMS 阈值密集目标可调到 0.5~0.6 model YOLO(MODEL_PATH) IMG_DIR Path(IMG_DIR) LABEL_DIR Path(LABEL_DIR) LABEL_DIR.mkdir(parentsTrue, exist_okTrue) img_paths list(IMG_DIR.glob(*.jpg)) list(IMG_DIR.glob(*.png)) for img_path in tqdm(img_paths): img cv2.imread(str(img_path)) if img is None: continue h, w img.shape[:2] results model.predict(img, confCONF_THRES, iouIOU_THRES, verboseFalse) lines [] for r in results: for box in r.boxes: cls_id int(box.cls[0]) x1, y1, x2, y2 box.xyxy[0].tolist() # YOLO 格式要求归一化中心点 宽高 cx (x1 x2) / 2.0 / w cy (y1 y2) / 2.0 / h bw (x2 - x1) / w bh (y2 - y1) / h # 过滤越界框避免写入非法标签 if bw 0 or bh 0: continue lines.append(f{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) txt_path LABEL_DIR / (img_path.stem .txt) txt_path.write_text(\n.join(lines), encodingutf-8)逻辑说明model.predict返回的是像素坐标的 xyxy 框YOLO 训练要的是归一化后的中心点加宽高所以中间必须做一次坐标变换。cx、cy、bw、bh四个值都除以了图像宽高范围落在 0 到 1 之间。参数方面CONF_THRES控制预标注框数量设低了框多但误检多设高了框少但漏检多建议从 0.2 起步看一批结果再调。IOU_THRES影响重叠框的合并密集小目标场景调高到 0.5 以上避免相邻目标被 NMS 误删。MODEL_PATH换成自己训练的best.pt时类别 id 会和训练时的data.yaml对齐这点在后续人工复核时要特别注意别把类别号对错了。2.3 批量推理的显存与速度控制几千张图一次性喂进去显存容易爆。ultralytics 的predict支持streamTrue配合batch参数可以边推理边写盘不用把所有结果堆在内存里。常见做法是把batch设成 8 或 16具体看显卡显存8G 显存跑 yolov8n 用 16 比较稳跑 yolov8l 就降到 4。如果图片分辨率很高比如遥感目标检测里常见的 4000 像素大图建议先切图再预标注否则模型输入被缩放到 640 后小目标基本消失预标注框会漏掉一大片。切图可以用滑动窗口重叠 20%切完的图分别推理再把框映射回原图坐标这一步在 auto_label.py 里加一个坐标偏移量就能实现。速度上单张 1080p 图片在 3060 上跑 yolov8n 大约 10 毫秒一万张图纯推理不到两分钟瓶颈通常在读盘和写盘。把图片放在 SSD 上标签写盘用追加模式能再快一截。如果只是做预标注不需要保存可视化结果saveFalse默认就是关的别手贱打开否则每张图多写一个 jpg磁盘很快就满。3. 人工复核环节把预标注框变成可用标签的三个动作3.1 复核工具的选择与标签格式对齐预标注生成的 txt 不能直接拿去训练必须过一遍人工。复核工具常见的有 labelImg、Label Studio、CVAT以及 ultralytics 自带的标注界面。选哪个主要看两点能不能直接读 YOLO txt能不能批量切换图片。labelImg 老牌但界面简陋Label Studio 支持多人协作但部署重CVAT 功能全但学习成本高。如果只是个人或小团队labelImg 够用打开labels目录对应的 txt框会自动显示人工只需要拖拽调整、删除误检、补画漏检。这里有个格式坑不同工具对 YOLO txt 的类别 id 起始值理解不一样。YOLO 官方是从 0 开始但有些工具默认从 1 开始导入导出时会整体偏移一位。复核前先拿一张有已知类别的图验证确认框的类别名和你的data.yaml对得上再批量开工。否则标了几百张才发现类别全错那真是血泪经验。3.2 复核时的优先级先删误检还是先补漏检人工复核的时间要花在刀刃上。我的习惯是分两遍第一遍快速扫只删明显误检比如把背景纹理当成目标的框这类框留着会教坏模型第二遍再逐张补漏检尤其是小目标和遮挡目标。为什么先删后补因为误检框会干扰视线让你误以为某个区域已经标过了补漏检时容易跳过。先删干净画面清爽补漏检的效率会高不少。补漏检时注意框的紧贴度。预标注框通常比较准但漏检的目标需要手工拉拉的时候别太松背景留太多会让模型学到无关特征。对于鸟类目标检测这类细长目标框的宽高比要尽量贴合身体不要把翅膀外的天空也框进去。复核完一批随机抽十张可视化检查用下面的脚本把标签画回图上肉眼确认没有越界框和类别错位。# check_label.py 可视化复核结果 import cv2 from pathlib import Path IMG_DIR Path(datasets/raw/images) LABEL_DIR Path(datasets/raw/labels) CLASSES [bird, person] # 换成你的类别列表 for txt in LABEL_DIR.glob(*.txt): img_path IMG_DIR / (txt.stem .jpg) img cv2.imread(str(img_path)) if img is None: continue h, w img.shape[:2] for line in txt.read_text().strip().splitlines(): if not line: continue c, cx, cy, bw, bh line.split() cx, cy, bw, bh map(float, (cx, cy, bw, bh)) x1 int((cx - bw / 2) * w) y1 int((cy - bh / 2) * h) x2 int((cx bw / 2) * w) y2 int((cy bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, CLASSES[int(c)], (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imwrite(fcheck_vis/{txt.stem}.jpg, img)这段脚本把归一化坐标还原成像素坐标画框并写上类别名。CLASSES列表的顺序必须和训练时data.yaml里的names完全一致否则显示的类别名会张冠李戴。跑完抽几张看重点检查有没有框跑到图像外面、有没有同一个目标被标了两次、有没有类别明显不对的。确认无误后这批标签就可以和图片一起进训练流程了。3.3 迭代预标注用复核后的数据反哺模型半自动标注不是一次性的。第一轮预标注加人工复核后你得到了一批高质量标签拿这批数据去微调预标注模型第二轮预标注的质量会明显提升人工复核的工作量随之下降。这个循环跑两到三轮通常能把人工工作量压到纯手工的百分之二十到三十。具体做法是把复核后的images和labels按 8:2 分训练验证用 yolov8n 或 yolov8s 训 50 到 100 轮得到新的best.pt替换auto_label.py里的MODEL_PATH对剩余未标注图片再跑一遍。注意每轮都要保留一份原始预标注结果方便对比模型进步了多少也方便回滚。4. 半自动标注避坑从坐标越界到类别错位的五条排查记录4.1 现象训练时报「Label class out of range」原因预标注模型输出的类别 id 超出了data.yaml里names的长度。常见于用 COCO 预训练权重直接跑自定义数据集COCO 有 80 类你的data.yaml只有 3 类模型预测出 id 为 45 的框写进 txt 后训练器直接报错。解决在auto_label.py里加一层类别过滤只保留cls_id len(CLASSES)的框。或者更彻底用自己数据训练过的权重做预标注类别自然对齐。过滤代码就一行if cls_id len(CLASSES): continue。4.2 现象标签文件是空的但图上明明有目标原因置信度阈值设太高或者模型输入尺寸太小导致小目标没被检出。前者常见于直接沿用 0.5 的评估阈值后者常见于高分辨率遥感图直接缩放推理。解决把CONF_THRES降到 0.15 到 0.2同时把imgsz调大比如从默认 640 调到 1280。如果显存不够改用切图推理再映射回原图。切图时窗口重叠 20%避免目标被切在边界上漏检。4.3 现象框的位置整体偏移或宽高反了原因坐标变换时把宽高和中心点算错或者读图时用了 cv2 的 BGR 顺序但宽高取反。YOLO 格式是cx cy w h不是x1 y1 x2 y2顺序写错会导致框乱飞。解决用check_label.py可视化验证肉眼确认框贴合目标。如果整体偏移检查w和h有没有写反如果框大小不对检查有没有漏除图像宽高。归一化后的值必须在 0 到 1 之间出现大于 1 的值说明坐标没除对。4.4 现象密集场景下相邻目标被合并成一个框原因NMS 的 IoU 阈值设太低两个挨得近的目标框重叠度超过阈值被当成重复框删掉一个。解决把IOU_THRES从 0.45 调到 0.55 甚至 0.6让 NMS 更宽容。如果目标确实非常密集比如人群检测可以考虑用 soft-NMS但 ultralytics 默认接口不支持需要自己改后处理。更简单的办法是预标注阶段不依赖 NMS把iou设成 0.7让框多留一些人工复核时再删。4.5 现象复核后的标签训练效果反而比预标注前差原因人工复核时引入了系统性偏差比如补漏检时框拉得太松或者删误检时把难样本也删了导致训练集分布偏移。另一个可能是复核后的标签和图片没有一一对应比如图片重命名后 txt 没跟着改。解决复核后随机抽 20 张做可视化检查确认框的紧贴度和类别正确。训练前用脚本校验图片和标签文件名是否一一对应数量是否一致。如果效果仍然差回滚到上一版标签对比两版差异定位是哪一类操作引入的问题。5. 把预标注阈值调成自适应一个提升复核效率的小技巧固定置信度阈值有个问题不同图片的难度不一样简单图片框少复杂图片框多人工复核的节奏被打乱。我后来改成一个自适应策略先跑一遍低阈值推理统计每张图的框数量如果某张图框数超过 50说明场景密集自动把该图的阈值上调到 0.35减少误检如果框数少于 3说明可能漏检把阈值下调到 0.1多报一些。这个逻辑不复杂在auto_label.py里加一个两遍推理就能实现。# 自适应阈值片段 def adaptive_conf(img, model, base_conf0.2): # 第一遍低阈值探数量 r model.predict(img, conf0.1, iou0.5, verboseFalse)[0] n len(r.boxes) if n 50: return 0.35 # 密集场景收紧 elif n 3: return 0.10 # 疑似漏检放宽 return base_conf这个技巧在鸟类目标检测数据集上效果比较明显因为鸟群图片框数波动大自适应阈值能让每张图的预标注框数量落在人工复核的舒适区间。参数上50和3这两个分界值需要按你的数据分布调可以先统计一批图的框数直方图取 80 分位和 20 分位作为分界。跑完自适应预标注后人工复核的时间大概能再省一成多虽然不多但积少成多。另一个习惯是每轮预标注都保留一份auto_label的日志记录每张图的框数和使用的阈值。这样当某张图复核时发现漏检严重能回溯是阈值问题还是模型问题。日志用简单的 csv 就行字段包括图片名、框数、阈值、耗时。别小看这个日志模型迭代几轮后它能帮你判断预标注质量是在提升还是停滞。最后说个我自己的教训半自动标注的瓶颈从来不在脚本而在复核标准的一致性。同一个人上午和下午的标注尺度都可能不一样更别说多人协作。所以复核前一定要定一份简短的标注规范写清楚框的紧贴度、遮挡目标怎么标、最小目标尺寸是多少。规范不用长一页纸就够但能省下后面返工的大量时间。希望帮到你。本文还有配套的精品资源点击获取
返回列表