1. 自动标注这条链路,到底解决了什么痛点
做过视觉模型落地的朋友都清楚,一个目标检测或者分割项目,真正花时间的地方从来不是调模型结构,而是搞数据。标注一批几千张的图,纯手工点框、描边,一个人干一周都未必能出活,而且标注质量还参差不齐。更麻烦的是,业务需求一变,类别一改,之前标的数据可能直接作废,又得重头来一遍。这种循环一旦形成,团队基本就被拖死在数据准备阶段了。
自动标注这套东西,本质上是想把这个循环打破。它的核心逻辑是:先用一个泛化能力足够强的模型(比如开放词汇检测、分割模型)对未标注数据做一轮“预标注”,人工只需要在预标注结果上做修正和确认,而不是从零开始画。这样一来,单张图的标注耗时能从几分钟压缩到几十秒甚至几秒,而且随着修正后的数据回流去微调模型,下一轮预标注的质量会越来越高,这就是大家常说的“数据飞轮”。
我这次要拆的这条链路,是X-AnyLabeling + autodistill + Grounded-SAM的组合。这三个东西各自定位很清晰:Grounded-SAM 负责“看得懂”,它能根据文本提示(比如“person”“car”)在图上找出对应目标并给出分割掩码;autodistill 负责“批量跑”,它把教师模型的推理能力封装成流水线,自动给整个数据集打上标签;X-AnyLabeling 负责“人工兜底”,它是一个带 AI 辅助的标注客户端,能加载预标注结果,让人快速修正、补漏、改类别。
这套组合适合谁?如果你手头有一批未标注的图片,想快速搞出一个可用的检测或分割数据集;或者你已经有一个小规模标注集,想用自动标注把规模扩起来;再或者你在做垂直领域(工业质检、遥感、医疗影像)的定制模型,需要反复迭代数据——那这条链路基本就是为你准备的。哪怕你之前没接触过自动标注,只要会装 Python 环境、能跑命令行,跟着走一遍就能出结果。
下面我按实际操作的顺序,把整条链路拆开讲。先讲整体设计思路和选型理由,再讲每个环节的细节和坑,然后是完整的实操流程,最后是我踩过的那些问题和排查方法。
2. 整体方案设计与选型逻辑拆解
2.1 为什么是这三个工具的组合,而不是单用一个
很多人第一反应是:既然 Grounded-SAM 这么强,直接拿它跑一遍不就行了,为什么还要 autodistill 和 X-AnyLabeling?这个问题我一开始也想过,实际跑下来发现,单用 Grounded-SAM 有三个绕不过去的坎。
第一是批量处理能力弱。Grounded-SAM 官方给的 demo 基本是单张图推理,你要跑几千张图,得自己写循环、管理显存、处理异常,还要把结果存成标注格式。autodistill 的价值就在这里,它把“教师模型推理 + 结果转标注格式 + 数据集组织”这套流程标准化了,你只需要指定输入目录、输出目录和教师模型,它就能批量跑完,输出 COCO、YOLO 等常见格式。
第二是结果需要人工校验。Grounded-SAM 再强,也会有漏检、误检、掩码边缘不准的情况,尤其是小目标、遮挡目标、类别边界模糊的场景。如果直接把自动标注结果拿去训练,噪声会污染模型。X-AnyLabeling 就是解决这个问题的,它能直接加载 autodistill 产出的标注文件,在界面上把框和掩码显示出来,人工只需要拖拽调整、删掉误检、补上漏检,效率比从零标注高一个数量级。
第三是迭代闭环。autodistill 支持用自动标注的结果去训练一个轻量学生模型(比如 YOLO),训练完的学生模型可以反过来做下一轮预标注,或者部署到边缘设备。X-AnyLabeling 修正后的数据也可以导出,继续喂给下一轮。这三个工具串起来,才形成完整的“预标注—修正—训练—再预标注”飞轮。
2.2 Grounded-SAM 的工作原理,用大白话讲清楚
Grounded-SAM 其实是两个模型的拼接:Grounding DINO和SAM。Grounding DINO 是一个开放词汇检测模型,你给它一张图和一段文本提示(比如“cat . dog .”),它能在图上找出所有匹配这些词的目标,输出边界框。SAM(Segment Anything Model)是一个分割模型,你给它一个框或者一个点,它能把目标的精确轮廓抠出来。
所以 Grounded-SAM 的流程是:先用 Grounding DINO 根据文本找到目标框,再把框喂给 SAM 得到分割掩码。这样一来,你不需要预先定义类别、不需要训练,只要用自然语言描述你要找什么,它就能标出来。这个特性对于冷启动阶段特别有用,因为很多项目一开始根本不知道数据里有哪些类别,或者类别体系还没定下来。
但要注意,Grounding DINO 的检测精度受文本提示的写法影响很大。比如你写“person”,它可能只检出明显的人;你写“person . human . pedestrian .”,召回率会高一些,但误检也会增加。这个平衡需要根据你的数据特点去调。
2.3 autodistill 的定位:把教师模型变成标注流水线
autodistill 的核心抽象是“教师模型”和“目标模型”。教师模型负责打标签,目标模型负责学习。你调用autodistill的接口,指定教师模型(比如 GroundedSAM)和输入图片目录,它就会自动跑推理、过滤低置信度结果、转成标注格式。
它支持的教师模型很多,除了 GroundedSAM,还有 CLIP、DETIC、YOLO-World 等。目标模型支持 YOLOv8、YOLOv5、RT-DETR 等。这意味着你可以用一个大模型打标签,然后训练一个小模型部署,这在工程上非常实用。
autodistill 还有一个好处是结果格式统一。它输出的标注可以直接被 X-AnyLabeling 读取,也可以直接用于 YOLO 训练,省去了格式转换的麻烦。我试过手动把 Grounded-SAM 的输出转成 YOLO 格式,光是坐标归一化和类别映射就写了一堆代码,还容易出错。autodistill 把这些脏活都封装好了。
2.4 X-AnyLabeling 为什么比普通标注工具更适合这条链路
X-AnyLabeling 是一个基于 Qt 的标注客户端,支持检测、分割、分类、姿态等多种任务。它最大的特点是内置了 AI 辅助标注能力,可以加载 SAM、YOLO 等模型做交互式标注。但在我们这条链路里,它的核心作用是加载预标注结果并高效修正。
它支持直接打开 autodistill 输出的 COCO JSON 或 YOLO TXT,把框和掩码渲染出来。你可以用快捷键快速切换图片、删除误检、调整框、修改类别。它还支持“自动保存”,修正完直接导出,不需要额外转换。相比 Labelme、CVAT 这些工具,X-AnyLabeling 对 AI 预标注的兼容性更好,操作也更顺手。
另外,X-AnyLabeling 是跨平台的,Windows、Linux、macOS 都能跑。Linux 下如果遇到显示问题,通常和 Qt 的图形后端有关,后面我会讲怎么处理。
3. 环境搭建与核心细节解析
3.1 硬件和基础环境要求
这套链路对硬件有一定要求,主要是 Grounded-SAM 推理比较吃显存。我的测试环境是 Ubuntu 22.04 + NVIDIA RTX 3090(24GB 显存)+ CUDA 12.1 + Python 3.10。如果你显存小一些,比如 8GB,也能跑,但需要调小 batch size 或者用更小的模型变体。
基础依赖包括:
- Python 3.8 以上,推荐 3.10
- PyTorch 2.0 以上,带 CUDA 支持
- CUDA 和 cuDNN,版本要和 PyTorch 匹配
- Git、wget 等基础工具
安装 PyTorch 的时候,建议直接去官网查对应 CUDA 版本的安装命令,不要凭记忆写。我见过太多人因为 PyTorch 和 CUDA 版本不匹配,卡在torch.cuda.is_available()返回 False 上。
3.2 三个工具的安装顺序和依赖冲突处理
安装顺序建议是:先装 autodistill,再装 Grounded-SAM 相关依赖,最后装 X-AnyLabeling。原因是 autodistill 会拉取一些基础依赖,Grounded-SAM 对 transformers、segment-anything 的版本有要求,X-AnyLabeling 相对独立。
autodistill 的安装:
pip install autodistill pip install autodistill-grounded-samGrounded-SAM 的依赖:
pip install groundingdino-py pip install segment-anything这里有个坑:groundingdino-py在不同平台上的编译情况不一样。Linux 下通常没问题,Windows 下可能需要装 Visual Studio Build Tools。如果装不上,可以试试从源码编译,或者用 conda 环境。
X-AnyLabeling 的安装有两种方式:一种是直接下载预编译的安装包,另一种是从源码跑。我推荐直接用预编译包,省事。Linux 下下载 AppImage 或者 tar.gz,Windows 下下载 exe。如果要从源码跑:
git clone https://github.com/CVHub520/X-AnyLabeling.git cd X-AnyLabeling pip install -r requirements.txt python anylabeling/app.py依赖冲突主要出现在 transformers 和 tokenizers 的版本上。Grounded-SAM 可能需要较新的 transformers,而其他包可能锁定了旧版本。我的做法是单独建一个虚拟环境给 autodistill + Grounded-SAM,X-AnyLabeling 用另一个环境或者直接用预编译包,避免互相干扰。
3.3 模型权重的下载和存放位置
Grounded-SAM 需要两个权重文件:Grounding DINO 的权重和 SAM 的权重。
Grounding DINO 权重:
wget https://github.com/IDEA-Research/GroundingDINO/releases/download/v0.1.0-alpha/groundingdino_swint_ogc.pthSAM 权重(推荐用 ViT-H,精度最高,但显存占用也大):
wget https://dl.fbaipublicfiles.com/segment_anything/sam_vit_h_4b8939.pth如果显存不够,可以用 ViT-L 或 ViT-B,精度会降一些,但速度快很多。
权重文件建议放在一个固定目录,比如~/models/,然后在代码里用绝对路径引用。autodistill 的 GroundedSAM 类在初始化时可以指定权重路径,如果不指定,它会尝试自动下载,但自动下载有时候会因为网络问题失败,手动下载更稳。
3.4 文本提示的设计技巧
文本提示直接决定 Grounded DINO 的检测结果。几个实操经验:
- 类别之间用
.分隔,比如person . car . dog . - 类别名尽量用常见英文单词,避免生僻词
- 如果某个类别召回率低,可以加同义词,比如
car . automobile . vehicle . - 如果误检多,可以减少同义词,或者调低
box_threshold - 提示末尾加
.有助于模型识别边界
box_threshold和text_threshold是两个关键参数。box_threshold控制检测框的置信度阈值,默认 0.3 左右;text_threshold控制文本匹配的阈值,默认 0.25 左右。实际调的时候,先固定text_threshold,调box_threshold,观察召回和误检的平衡。
4. 完整实操流程与关键环节实现
4.1 第一步:准备未标注图片目录
把所有待标注的图片放在一个目录下,比如~/data/unlabeled/。图片格式支持 jpg、png 等常见格式。建议图片命名规范一些,避免中文和特殊字符,后面处理起来省事。
如果图片数量很大(比如上万张),建议先抽一个子集(比如 500 张)跑通流程,确认效果后再全量跑。全量跑的时候,Grounded-SAM 的推理速度大概是每张图 1-3 秒(取决于分辨率和显存),一万张图大概需要几个小时。
4.2 第二步:用 autodistill 跑批量预标注
核心代码其实很短:
from autodistill_grounded_sam import GroundedSAM from autodistill.detection import CaptionOntology from autodistill.utils import plot ontology = CaptionOntology({ "person": "person", "car": "car", "dog": "dog" }) base_model = GroundedSAM(ontology=ontology) dataset = base_model.label( input_folder="./data/unlabeled", extension=".jpg", output_folder="./data/labeled" )CaptionOntology是一个字典,key 是你要的类别名,value 是给 Grounded DINO 的文本提示。这里可以不一样,比如 key 用中文“人”,value 用英文“person”,autodistill 会自动做映射。
label方法会遍历输入目录下所有指定扩展名的图片,跑推理,然后把结果存到输出目录。输出格式默认是 COCO JSON,每张图对应一个 JSON 文件,或者一个总的 JSON,取决于配置。
跑完之后,输出目录里会有图片和对应的标注文件。你可以先用plot函数可视化几张,看看效果:
plot( image="./data/unlabeled/001.jpg", detections=dataset[0].detections )如果发现漏检严重,回去调文本提示和阈值;如果误检多,调高box_threshold。
4.3 第三步:用 X-AnyLabeling 加载预标注并修正
打开 X-AnyLabeling,选择“打开目录”,指向 autodistill 的输出目录。它会自动识别 COCO JSON 或 YOLO TXT,把标注渲染出来。
修正的时候几个高效操作:
- 用
A和D键切换上一张/下一张 - 用
Delete删除选中的框 - 拖拽框的边角调整大小
- 双击类别标签修改类别
- 用
Ctrl+S保存当前修改
如果发现某张图漏检了某个目标,可以用 X-AnyLabeling 内置的 SAM 模型手动补一个:点一下目标,SAM 会自动生成掩码,然后你选类别就行。这个功能在补漏的时候特别快。
修正完所有图片后,导出标注。X-AnyLabeling 支持导出 COCO、YOLO、VOC 等格式,选你训练时需要的格式。
4.4 第四步:用修正后的数据训练学生模型
如果你只是想得到一个可用的数据集,到第三步就结束了。但如果你想形成飞轮,可以继续用 autodistill 训练一个 YOLO 学生模型:
from autodistill_yolov8 import YOLOv8 target_model = YOLOv8("yolov8n.pt") target_model.train("./data/labeled/data.yaml", epochs=50)训练完之后,学生模型可以拿来做下一轮预标注,速度比 Grounded-SAM 快很多,精度在垂直领域上往往也更好,因为它已经学到了你的数据分布。
4.5 第五步:迭代与数据飞轮
飞轮的运转方式是:
- 用 Grounded-SAM 对新的未标注数据做预标注
- 用 X-AnyLabeling 修正
- 把修正后的数据加入训练集
- 重新训练学生模型
- 用学生模型替代 Grounded-SAM 做下一轮预标注(或者两者结合)
每一轮迭代,预标注的质量都会提升,人工修正的工作量会下降。我实测下来,第一轮修正大概要改 30%-40% 的框,第三轮之后基本降到 10% 以下。
5. 常见问题与排查技巧实录
5.1 Grounded-SAM 推理报显存不足
这是最常见的问题。解决方法:
- 换小模型:SAM 用 ViT-B 或 ViT-L,Grounding DINO 用 Swin-T
- 调小输入分辨率:Grounded-SAM 默认用 800x1333,可以改成 600x800
- 分批推理:autodistill 支持设置 batch size,调小一些
- 用 CPU 推理:慢,但能跑,适合小数据集
如果显存刚好卡在边界,可以试试torch.cuda.empty_cache()在每张图推理后清理缓存。
5.2 autodistill 输出格式和 X-AnyLabeling 不兼容
autodistill 默认输出 COCO JSON,X-AnyLabeling 是支持的。但如果你的 autodistill 版本较老,可能输出格式有差异。排查方法是先看输出目录里有什么文件,再用 X-AnyLabeling 打开试试。如果不识别,可以用 autodistill 的转换工具转成 YOLO 格式,X-AnyLabeling 对 YOLO TXT 的支持更稳定。
5.3 X-AnyLabeling 在 Linux 下打不开或界面异常
Linux 下常见的问题是 Qt 图形后端不兼容。解决方法:
export QT_QPA_PLATFORM=xcb如果还是不行,试试:
export QT_DEBUG_PLUGINS=1看具体报错。另外,AppImage 需要 FUSE 支持,如果系统没装,可以加--appimage-extract-and-run参数。
5.4 文本提示写了但检测不到目标
排查顺序:
- 确认类别名是英文常见词
- 调低
box_threshold到 0.2 试试 - 加同义词,比如
person . human . - 确认图片里确实有该目标
- 换 Grounding DINO 的权重版本试试
如果某个类别始终检测不好,可能是 Grounding DINO 对这个概念本身就不敏感,这种情况只能靠人工补标,或者换其他教师模型。
5.5 自动标注结果噪声太大,训练效果差
自动标注的噪声主要来自漏检和误检。处理策略:
- 设置置信度阈值,过滤低置信度的框
- 对面积过小的框做过滤(比如小于 10x10 像素)
- 对长宽比异常的框做过滤
- 人工修正时重点检查这些可疑框
另外,训练的时候可以用 label smoothing 或者噪声鲁棒损失函数,减轻噪声影响。
5.6 常见问题速查表
| 问题 | 可能原因 | 解决方法 |
|---|---|---|
| 显存不足 | 模型太大或分辨率太高 | 换小模型、降分辨率、分批推理 |
| 检测不到目标 | 文本提示不合适 | 调阈值、加同义词、换权重 |
| 误检太多 | 阈值太低或提示太宽泛 | 调高 box_threshold、减少同义词 |
| X-AnyLabeling 打不开 | Qt 后端问题 | 设置 QT_QPA_PLATFORM=xcb |
| 标注格式不兼容 | 版本差异 | 转成 YOLO 格式 |
| 训练效果差 | 标注噪声大 | 过滤低质量框、人工修正 |
5.7 几个我踩过的坑
第一个坑是权重路径写错。Grounded-SAM 初始化的时候如果不指定权重路径,它会尝试从网上下载,但有时候下载的权重版本不对,导致推理结果异常。后来我统一手动下载权重,用绝对路径引用,问题就没了。
第二个坑是类别映射搞混。autodistill 的CaptionOntology里,key 和 value 的对应关系要搞清楚。key 是最终标注文件里的类别名,value 是给模型的提示。我有一次把 key 写成中文,value 写成英文,结果标注文件里全是中文类别,训练的时候又得转一遍。建议一开始就统一用英文类别名。
第三个坑是X-AnyLabeling 自动保存没开。修正了几百张图,结果没保存,白干。一定要在设置里把自动保存打开,或者养成随手 Ctrl+S 的习惯。
第四个坑是图片分辨率不一致。Grounded-SAM 对分辨率敏感,如果数据集里图片分辨率差异很大,检测效果会不稳定。建议先统一 resize 到一个合理范围,比如长边 1333。
6. 一些提升效率的实操心得
6.1 预标注结果的可视化检查
在跑完 autodistill 之后,不要直接进 X-AnyLabeling 修正,先用脚本随机抽几十张图,把标注画出来看一眼。这样能快速发现系统性问题,比如某个类别整体漏检、框普遍偏大偏小等。如果问题普遍存在,先调提示和阈值重跑,比一张张修效率高得多。
可视化脚本可以用 OpenCV 或者 PIL 写,把框和类别画在图上,存到一个临时目录,快速翻看。
6.2 分批处理和断点续跑
如果数据集很大,autodistill 跑一半崩了,重跑很浪费时间。我的做法是把数据集分成多个子目录,每个子目录单独跑,跑完一个再跑下一个。这样即使某个批次出问题,也只影响那一批。
另外,autodistill 的label方法本身不支持断点续跑,但你可以自己写个简单的判断:如果输出目录里已经有对应的 JSON 文件,就跳过。这样重跑的时候只处理没跑过的图。
6.3 用学生模型加速后续轮次
第一轮用 Grounded-SAM 跑,速度慢但泛化好。第一轮修正完训练出学生模型后,第二轮就可以用学生模型做预标注,速度快十倍以上,精度在垂直领域上往往还更好。这时候 Grounded-SAM 只在学生模型不确定的图上兜底,比如置信度在 0.3-0.5 之间的图,再跑一遍 Grounded-SAM 做交叉验证。
6.4 标注质量的一致性检查
多人协作标注的时候,一致性是个大问题。X-AnyLabeling 支持导出标注,你可以写脚本统计每个类别的框数量、平均大小、位置分布,看看有没有异常。比如某个人标的“car”框普遍比别人大一圈,可能就是理解不一致,需要统一标准。
6.5 数据飞轮的启动策略
飞轮最难的是启动阶段,因为第一轮预标注质量差,人工修正工作量大,容易让人放弃。我的建议是:
- 第一轮只选 200-500 张图,不要贪多
- 类别体系尽量精简,先做核心类别
- 修正的时候优先保证召回,漏检的补上,误检的可以先留着,后面再过滤
- 第一轮训练出的学生模型不要期望太高,能到 0.5 mAP 就算成功
- 第二轮开始,预标注质量会明显提升,这时候再扩大数据量
这套链路我前后跑了三个项目,从工业零件检测到遥感目标提取,基本都能在两周内从零搞出一个可用的数据集。最耗时的部分永远是人工修正,但相比纯手工标注,效率提升至少在 5 倍以上。如果你正在被数据标注拖后腿,建议先拿 100 张图跑一遍全流程,感受一下自动标注的实际效果,再决定要不要规模化。