标注这件事,做过的都懂——模型效果好不好,八成看数据;数据好不好,八成看标注。可标注偏偏是最费人力的环节,一张图框几个目标,一天下来眼睛都花了,标注团队的成本还居高不下。这两年"自动标注"和"数据飞轮"被反复提起,核心思路其实很朴素:用大模型先把框和掩码粗标出来,人工只做审核和修正,把重复劳动压到最低。我最近把X-AnyLabeling + autodistill + Grounded-SAM这条链路完整跑了一遍,从环境部署到批量出标、再到回流训练,踩了不少坑,也总结出一套相对稳的流程。这篇就把整套东西拆开讲清楚,包括为什么这么选型、每一步的意图是什么、哪些地方最容易翻车。不管你是刚接触自动标注的新手,还是已经在做数据闭环的老手,应该都能从里面捞到点能直接抄的东西。
1. 为什么是这三个工具凑成一条链路
1.1 自动标注的本质是"用模型生产训练数据"
先把概念捋直。自动标注不是让模型替你干活就完事了,它的本质是用一个大而全的模型,去生产一个小而专的模型所需要的训练数据。你最终要部署的往往是一个轻量检测或分割模型,它需要几千上万张标注图;而人工标这些图成本极高。于是思路变成:先用一个泛化能力强的模型(比如 Grounded-SAM 这种开放词汇的检测+分割组合)把数据粗标出来,人工只做修正,修正后的数据拿去训练你的小模型,小模型上线后再把难例回流,形成"数据飞轮"。
这条链路里,三个工具各司其职:
- Grounded-SAM:负责"从零生成标注"。它把 Grounding DINO 的开放词汇检测能力和 SAM 的分割能力拼在一起,你给一段文字提示(比如 "person. car. dog."),它就能把图里对应的目标框出来并分割出掩码。这是自动标注的"发动机"。
- autodistill:负责"把发动机装到流水线上"。它是一个蒸馏框架,把大模型(基础模型)的输出转成目标模型能吃的标注格式,并且封装了批量推理、格式转换、训练目标模型的流程。你不用自己写一堆胶水代码。
- X-AnyLabeling:负责"人工审核和修正"。它是一个带 AI 辅助的标注客户端,内置了 SAM、YOLO 等模型,支持自动预标注、一键分割、快捷键操作,人工审核效率比传统标注工具高一大截。
三者串起来就是:Grounded-SAM 批量出粗标 → autodistill 做格式转换和蒸馏训练 → X-AnyLabeling 做人工精修和难例回流。这个组合的好处是每一环都能单独替换,不会把你锁死在某一个工具上。
1.2 和"直接用 SAM 点一下"的区别在哪
很多人会问:X-AnyLabeling 里不是自带 SAM 吗,我直接点一下不就自动标注了,为什么还要绕 Grounded-SAM 和 autodistill?
区别在于规模和自动化程度。X-AnyLabeling 内置的 SAM 交互式分割,适合"少量图片、人工点提示"的场景,你点一下它分一个,本质还是半自动。而 Grounded-SAM 是文本驱动的,你写一次提示词,它能对成百上千张图批量跑,全程不需要人点。autodistill 则把"批量跑"这件事工程化了,还能直接接着训练。
所以选型逻辑很清楚:
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 几十张图,快速标 | X-AnyLabeling 内置 SAM | 交互式,即点即用 |
| 几千张图,类别固定 | Grounded-SAM + autodistill 批量 | 文本驱动,全自动 |
| 类别多、需精修 | 三者串联 | 批量粗标 + 人工精修 |
| 要持续迭代 | 加数据飞轮 | 难例回流再训练 |
我实测下来,纯人工标 1000 张图(每张 5 个目标)大概要 2-3 天;用 Grounded-SAM 批量粗标 + X-AnyLabeling 精修,同样工作量能压到半天以内,而且标注一致性更好——因为粗标阶段是同一个模型出的,风格统一,人工只需要改错。
1.3 数据飞轮到底转的是什么
"数据飞轮"这个词被说烂了,但落到实操上,它转的是这么一圈:
- 用基础模型(Grounded-SAM)对未标注数据批量出粗标;
- 人工在 X-AnyLabeling 里精修,得到高质量标注集;
- 用 autodistill 拿这批数据训练一个轻量目标模型;
- 目标模型上线推理,把置信度低或预测错的样本挑出来(难例挖掘);
- 难例回到第 1 步,重新粗标、精修、训练。
每转一圈,目标模型在难例上的表现就提升一点,需要的标注量越来越少。关键在于第 4 步的难例挖掘,这是飞轮能转起来的核心——如果只是无脑标新数据,那叫堆数据,不叫飞轮。
2. 环境部署:三个工具各自的坑
2.1 Grounded-SAM 的依赖是最重的一环
Grounded-SAM 本质是两个模型的组合,依赖比较重。核心是 Grounding DINO 和 SAM 两个权重,加上它们的推理代码。我建议用 conda 单独建环境,别和主环境混:
conda create -n grounded-sam python=3.10 -y conda activate grounded-samPython 版本我锁在 3.10,实测 3.11 和 3.12 在部分 CUDA 扩展上会出问题。然后是 PyTorch,一定要按你的 CUDA 版本装,别直接pip install torch,那样装到的可能是 CPU 版:
# 以 CUDA 11.8 为例 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118装完先验证:
import torch print(torch.cuda.is_available()) # 必须是 True print(torch.version.cuda) # 确认 CUDA 版本如果这里是 False,后面所有推理都会退化成 CPU,慢到没法用。这是第一个大坑,很多人卡在这还以为模型有问题。
权重文件要提前下好,Grounding DINO 的groundingdino_swint_ogc.pth和 SAM 的sam_vit_h_4b8939.pth(或更轻的 vit_b/vit_l)。权重放哪、怎么加载,代码里要写对路径,否则会报找不到文件的错。
2.2 autodistill 的安装和模型选择
autodistill 本身很轻,它是个"调度层",真正干活的是它背后挂的基础模型和目标模型。安装:
pip install autodistill pip install autodistill-grounded-sam # Grounded-SAM 作为基础模型 pip install autodistill-yolov8 # YOLOv8 作为目标模型注意 autodistill 的插件是按"基础模型"和"目标模型"分开装的,你要用哪个就装哪个。我第一次装的时候只装了autodistill,跑起来报找不到 GroundedSAM,才发现插件没装。
目标模型我选 YOLOv8,理由是它训练快、部署方便、社区资料多。如果你要做分割,可以换成 YOLOv8-seg;要做分类,autodistill 也支持。选型上不用纠结,先用最熟的跑通,再换。
2.3 X-AnyLabeling 的源码运行与快捷键
X-AnyLabeling 有打包好的可执行文件,直接下载就能用。但如果你要改代码、加自定义模型,就得从源码跑。源码运行大致是:
git clone https://github.com/CVHub520/X-AnyLabeling.git cd X-AnyLabeling pip install -r requirements.txt python anylabeling/app.py在 PyCharm 里跑的话,把anylabeling/app.py设为入口,工作目录设成项目根目录,解释器选你装好依赖的那个环境。常见报错是 Qt 相关的库缺失,Linux 下补libxcb系列,Windows 下一般是 PyQt5 版本冲突,按 requirements 里的版本锁死基本能解决。
快捷键是提效的关键,我常用的几个:
| 快捷键 | 功能 |
|---|---|
| W | 创建矩形框 |
| E | 创建多边形 |
| Ctrl+I | 导入 AI 预标注结果 |
| Ctrl+J | 切换下一张 |
| D / A | 下一张 / 上一张 |
| Ctrl+S | 保存 |
| Del | 删除选中标注 |
把Ctrl+I用熟,配合 Grounded-SAM 导出的预标注文件,审核速度能翻倍。
3. 用 Grounded-SAM 批量出粗标
3.1 提示词怎么写才准
Grounded-SAM 是文本驱动的,提示词直接决定召回率。几个实操经验:
- 用英文,类别之间用点号分隔:
person. car. bicycle.这种格式是 Grounding DINO 的标准输入,末尾的点号别漏。 - 类别名要具体:写
dog比写animal准得多,写red apple比写fruit准。开放词汇模型对具体名词的响应更好。 - 别一次塞太多类别:我试过一张图塞 20 个类别,召回率明显下降,模型会顾此失彼。建议单次不超过 8-10 个类别,类别多就分批跑。
- 同义词可以都写上:比如
car. automobile. vehicle.,能提高召回,但会带来重复框,后面去重。
阈值也要调。Grounding DINO 有box_threshold和text_threshold两个参数,前者控制框的置信度门槛,后者控制文本匹配门槛。默认 0.3/0.25 偏保守,召回不够就降到 0.2,误检多就升到 0.4。这个没有万能值,得拿几十张图试出来。
3.2 批量推理脚本的骨架
单张推理跑通后,批量就是套个循环。核心逻辑:
import os import cv2 import torch from groundingdino.util.inference import load_model, load_image, predict from segment_anything import sam_model_registry, SamPredictor # 加载模型 grounding_dino = load_model("GroundingDINO_SwinT_OGC.py", "groundingdino_swint_ogc.pth") sam = sam_model_registry["vit_h"](checkpoint="sam_vit_h_4b8939.pth") sam.to(device="cuda") predictor = SamPredictor(sam) TEXT_PROMPT = "person. car. bicycle." BOX_THRESHOLD = 0.3 TEXT_THRESHOLD = 0.25 image_dir = "images/" output_dir = "prelabels/" os.makedirs(output_dir, exist_ok=True) for name in os.listdir(image_dir): path = os.path.join(image_dir, name) image_source, image = load_image(path) boxes, logits, phrases = predict( model=grounding_dino, image=image, caption=TEXT_PROMPT, box_threshold=BOX_THRESHOLD, text_threshold=TEXT_THRESHOLD, ) # 用框作为 SAM 的提示,生成掩码 predictor.set_image(image_source) # ... 对每个 box 调 predictor.predict,得到 mask # 保存为 X-AnyLabeling 能读的 json这里有个关键点:SAM 的输入提示用 Grounding DINO 出的框,框越准,掩码越准。如果框本身就偏,掩码会跟着偏。所以框阈值别设太低,宁可漏检也别引入一堆垃圾框。
3.3 输出格式要提前对齐 X-AnyLabeling
Grounded-SAM 默认输出的是它自己的格式,要转成 X-AnyLabeling 能直接导入的格式。X-AnyLabeling 支持多种格式,我一般转成它原生的 JSON,结构大致是:
{ "version": "2.4.0", "flags": {}, "shapes": [ { "label": "person", "points": [[x1, y1], [x2, y2]], "shape_type": "rectangle" } ], "imagePath": "xxx.jpg", "imageHeight": 1080, "imageWidth": 1920 }如果是分割掩码,shape_type用polygon,points放多边形顶点。掩码转多边形可以用cv2.findContours加approxPolyDP简化,顶点别太多,否则文件巨大还卡。
提示:坐标一定要和图片尺寸对应,X-AnyLabeling 是按原图坐标渲染的。如果你在推理时缩放了图片,记得把坐标还原回去,否则框会整体偏移。
4. 用 autodistill 把粗标变成训练集
4.1 autodistill 的工作流拆解
autodistill 的用法很简洁,核心就三步:定义基础模型、定义目标模型、启动蒸馏。
from autodistill_grounded_sam import GroundedSAM from autodistill.detection import CaptionOntology from autodistill_yolov8 import YOLOv8 # 1. 定义本体:文本提示到类别名的映射 ontology = CaptionOntology({ "person": "person", "car": "car", "bicycle": "bicycle", }) # 2. 基础模型 base_model = GroundedSAM(ontology=ontology) # 3. 对未标注图片批量打标 dataset = "unlabeled_images/" base_model.label(input_folder=dataset, output_folder="labeled_dataset/") # 4. 训练目标模型 target_model = YOLOv8("yolov8n.pt") target_model.train("labeled_dataset/", epochs=50)CaptionOntology是核心,它把"文本提示"和"最终类别名"对应起来。左边是喂给 Grounded-SAM 的提示词,右边是你数据集里的类别名。这个映射写对了,后面类别就不会乱。
4.2 本体设计:类别映射的讲究
本体设计看着简单,其实很影响结果。几个原则:
- 提示词用英文,类别名按你的规范来:比如提示词
"traffic light",类别名可以是"traffic_light",保持你数据集命名一致。 - 一个类别对应一个提示词,别一对多:如果你写
{"car": "vehicle", "automobile": "vehicle"},两个提示词映射到同一个类别,autodistill 会合并,但可能产生重复框,不如直接写{"car. automobile.": "vehicle"}让 Grounded-SAM 内部处理。 - 类别名别用空格和特殊字符:训练时容易出问题,用下划线。
我踩过一个坑:本体里类别名写成了中文,结果训练时 YOLOv8 的类别文件编码出问题,报了一堆乱码。后来统一改成英文小写下划线,再没出过事。
4.3 蒸馏训练的参数与验证
autodistill 调 YOLOv8 训练时,参数可以透传。几个关键参数:
| 参数 | 建议值 | 说明 |
|---|---|---|
| epochs | 50-100 | 数据少就多跑几轮 |
| imgsz | 640 | 和推理尺寸一致 |
| batch | 按显存 | 8G 显存用 8-16 |
| patience | 20 | 早停,防过拟合 |
训练完一定要验证,别直接上线。autodistill 训练完会输出权重,拿几张没参与训练的图跑一下,看 mAP 和实际效果。我一般会留 10% 的数据做验证集,autodistill 的label阶段可以只标训练集,验证集单独标或者人工标,这样评估才准。
注意:自动标注出来的数据是有噪声的,直接拿去训练,模型会学到这些噪声。所以精修这一步不能省,尤其是要上线的模型。
5. 在 X-AnyLabeling 里做人工精修
5.1 导入预标注并批量审核
把 autodistill 或 Grounded-SAM 输出的 JSON 放到图片同目录(或指定目录),在 X-AnyLabeling 里用Ctrl+I导入。导入后每张图会显示预标注的框和掩码,你只需要:
- 检查框是否贴合目标,偏了就拖动调整;
- 检查类别是否正确,错了就改标签;
- 检查有没有漏标,漏了就补;
- 检查有没有误标,多了就删。
熟练之后一张图几秒钟。关键是批量审核时保持节奏,别在一张图上纠结太久,先把明显的错误改掉,细节留到第二轮。
5.2 用内置模型补标和纠错
X-AnyLabeling 内置了 SAM 和 YOLO,遇到预标注漏掉的目标,可以直接用内置模型补。比如漏了一个人,用 SAM 点一下就能分割出来。这个功能在精修阶段特别有用,比手动画多边形快得多。
另外它支持"自动标注"模式,选一个内置模型对当前图跑一遍,作为预标注的补充。我一般先用 Grounded-SAM 的预标注打底,再用内置模型查漏补缺。
5.3 快捷键与审核效率的实战技巧
审核效率全靠快捷键。除了前面列的基础键,几个提效技巧:
- 用
D快速翻页,眼睛扫一遍,有问题的才停下来改; - 标签用数字键快速切换,X-AnyLabeling 支持给标签绑快捷键,把高频类别绑到 1-9;
- 善用复制粘贴,相似目标可以复制框再微调;
- 分组审核,先集中改类别错误,再集中调框位置,减少上下文切换。
我实测下来,一个熟练的标注员用这套流程,审核速度能到纯手工标注的 3-5 倍,而且因为粗标统一,标注一致性明显更好。
6. 数据飞轮:难例回流与迭代
6.1 难例挖掘的几种策略
飞轮能不能转,全看难例挖得准不准。常用策略:
- 低置信度样本:目标模型推理时置信度在 0.3-0.6 之间的,说明模型拿不准,值得回流;
- 预测与粗标不一致:目标模型和 Grounded-SAM 结果差异大的,往往是难例;
- 类别混淆样本:模型把 A 类预测成 B 类的,重点回流;
- 场景边缘样本:夜间、遮挡、小目标等,主动收集。
我一般用低置信度 + 不一致两个策略组合,召回难例的效果最好。
6.2 回流数据的再标注与再训练
挖出来的难例,回到第 3 步重新用 Grounded-SAM 粗标,再进 X-AnyLabeling 精修,然后并入训练集重新训练。注意别把旧数据丢了,新老数据一起训,防止灾难性遗忘。如果新数据量很大,可以按比例混合,比如新数据占 30%-50%。
再训练时学习率可以调小一点,因为是在已有模型基础上微调。YOLOv8 支持从已有权重继续训,把model指向上一轮的best.pt即可。
6.3 飞轮转起来的判断标准
怎么知道飞轮转起来了?看两个指标:
- 同样精度下,需要的人工精修量在下降:说明模型越来越准,人工越来越省;
- 难例的占比在下降:说明模型覆盖的场景越来越全。
如果转了几圈这两个指标都没变化,那说明要么难例挖得不对,要么数据分布没变,得回头检查。
7. 踩过的坑和几条硬经验
7.1 坐标和尺寸对不上导致框全偏
这是最常见的坑。Grounded-SAM 推理时如果对图片做了 resize,输出的框坐标是缩放后的,直接存成标注就会整体偏移。解决办法是记录缩放比例,把坐标还原到原图尺寸。我一般统一不缩放,或者缩放后严格还原,别偷懒。
7.2 类别映射写错导致训练全乱
本体映射写错,训练出来的模型类别全是错的。建议在label阶段完成后,先抽查几张标注图,确认类别名和框都对,再进训练。这一步花五分钟,能省几小时返工。
7.3 显存不够的降级方案
Grounded-SAM 的 SAM 用 vit_h 很吃显存,8G 卡跑大图容易 OOM。降级方案:
- 换 vit_b 或 vit_l 的 SAM 权重,精度略降但显存友好;
- 减小输入分辨率;
- 分批推理,别一次加载太多图。
我日常用 vit_l,精度和显存比较平衡。
7.4 自动标注不是万能药
最后说句实在话:自动标注能省大量重复劳动,但它替代不了人工判断。模型没见过的新类别、模糊边界、遮挡严重的目标,还是得人来。把自动标注当成"高级草稿",人工做"终审",这个定位摆正了,整条链路才跑得顺。
这套流程我前后迭代了三四版,从最开始单张跑 Grounded-SAM,到后来 autodistill 批量蒸馏,再到 X-AnyLabeling 精修加飞轮,每一步都是被实际问题逼出来的。工具会更新,但"基础模型粗标 + 人工精修 + 难例回流"这个思路是稳的。你要是刚开始搭,建议先用几十张图把三个工具各自跑通,再串起来,别一上来就上大规模,那样出问题很难定位。