做目标检测和实例分割项目的人,十有八九都经历过标注噩梦。我前年接手一个细分场景的实例分割任务,训练集需要6000张图,每张平均4到5个目标,用多边形逐点抠掩码,熟练标注员一天最多完成80张。算下来纯手工标注要耗掉两个半月,项目周期直接被拖垮。那之后我一直在折腾自动标注,最近大半年把 X-AnyLabeling、autodistill 和 Grounded-SAM 串成了一条完整流水线,标注周期从两个月压到三四天,准确率也够用。这篇文章就把整套流程摊开讲清楚:每个工具解决什么问题、怎么装、怎么用、踩了哪些坑,方便正准备入坑自动标注的朋友直接照着走。
文章会按一条主线展开:先用 X-AnyLabeling 做小样本摸底和人工校验,再用 Grounded-SAM 做零样本自动出框出掩码,最后用 autodistill 把标注、训练、迭代串成闭环。如果你只是被某个单点工具吸引进来的,也可以直接跳到对应章节看细节。
1. 手工标注的痛在哪:三款工具各自要解决的问题
1.1 一个像素一个像素抠掩码的日子,我过够了
做检测项目,很多人觉得画框就够了,但一旦业务需要分割掩码(比如工业缺陷检测、自动驾驶感知、商品抠图),工作量是几何级上升的。一个矩形框鼠标拉两下就完事,一个多边形掩码呢?你得沿着目标边缘一个点一个点打,复杂轮廓几十个点打下来,手一抖还得回退。6000张图,每张四五个目标,这就是两三万个多边形,换成谁都会崩溃。
更要命的是标注质量不稳定。人不是机器,标注到下午容易疲劳,边框位置开始随意,掩码边缘越画越粗。团队里两个标注员对"紧贴边界"的理解还不一样,导致数据集标准漂移。后期清洗数据的时候,我发现有几百个标注框明显偏大,只能返工重标,浪费的时间比第一次标注还多。
所以我的核心诉求很明确:找一个能批量产出高质量标注,又保留人工复核入口的流程。自动标注不是要完全替代人,而是让人从"画"变成"改",把时间花在真正需要判断力的地方。
1.2 三个工具的分工:标注台、提示引擎、流水线调度
自动标注工具链里,每个工具的定位完全不同,别指望一个工具干完所有事。
X-AnyLabeling 是本地标注软件,本质是一个"带 AI 辅助的标注台"。它由 AnyLabeling 衍生而来,整合了 OpenCV、ONNX Runtime 和大量现成模型,既能手动框、画多边形,也能加载模型做 AI 预标注,然后人再去修正。它的优势在于灵活、可玩性强,适合小样本试探和最终人工质检。
Grounded-SAM 是模型组合,由 Grounding DINO 和 SAM(Segment Anything Model)拼起来。Grounding DINO 负责根据文字描述找目标框,SAM 负责在框内生成精细掩码。它最大的价值是零样本:不需要针对你的数据集训练,只要写对提示词,就能标出新类别。这是整个流程里的"AI 产能核心"。
autodistill 是流水线调度框架,它把"教师模型产出标注"和"学生模型消费标注"这两个环节抽象成了代码接口。你可以把 Grounded-SAM 挂进去当教师模型,它会自动扫描整个文件夹的图片、批量推理、输出标准格式的数据集,紧接着还能启动 YOLOv8 等学生模型的训练。它解决的不只是标注,而是"标注完怎么办"的后续动作。
三个工具的关系,类比一下就是:autodistill 是流水线本身,Grounded-SAM 是流水线上最聪明的那台机器,X-AnyLabeling 是流水线末端的人检工位。机器干粗活,人干细活。
| 工具 | 角色 | 核心能力 | 适合场景 |
|---|---|---|---|
| X-AnyLabeling | 标注客户端 | 手动标注 + 模型预标注 + 结果修正 | 小样本摸底、人工质检与修正 |
| Grounded-SAM | 零样本标注引擎 | 文本提示出框 + 分割掩码 | 新类别快速冷启动、批量自动标注 |
| autodistill | 流程编排框架 | 教师模型标注 + 学生模型训练闭环 | 大批量数据生产与模型迭代 |
2. X-AnyLabeling:不只是标注器,还是模型推理前台
2.1 安装与启动:Linux 和 Windows 各自的打开方式
X-AnyLabeling 支持 Windows 和 Linux。Windows 用户最简单的方式是去它的 GitHub Releases 页面下载打包好的可执行文件,解压后双击就行。Linux 用户尤其是服务器场景,更推荐源码运行,因为可以在没有显示器的情况下配合远程虚拟屏使用。
源码运行先准备 Python 3.8 到 3.10 的环境,克隆代码后用 pip 安装依赖,有两个依赖最容易在 Linux 上翻车:
git clone https://github.com/CVHub520/X-AnyLabeling.git cd X-AnyLabeling pip install -r requirements.txt # 如果报 libGL 相关的错,需要补系统库 sudo apt-get install libgl1 libglib2.0-0启动就是一行命令:
python main.py启动之后主界面左侧是图像列表,中间是画布,右侧是图层和标注属性面板。如果是 Windows 打包版打开没反应,大概率是杀毒软件把模型下载或临时文件拦截了,我遇到过两次,加白名单就能解决。
Linux 无头服务器上用 X-AnyLabeling 有一点要注意:它需要图形环境,没有显示器就配合 Xvfb 虚拟屏幕跑,或者在本地跑 Windows 版、远程桌面连过去。我在实验室的做法是,标注和人工复核都在本地 Windows 机器上做,批量自动标注交给 Linux 服务器上的 autodistill 完成,各用各的长处。
2.2 模型加载与 AI 辅助标注:把 SAM 和检测模型塞进标注台
X-AnyLabeling 区别于普通标注软件的关键点是模型面板。右侧工具栏里有一个模型加载区域,支持加载各类 ONNX 模型:检测模型、分割模型、SAM 系列、甚至是 Grounding DINO 的 ONNX 版本。
我的日常操作是加载一个轻量分割模型做预标注,然后在它的基础上微调。加载步骤很简单:
- 打开模型面板,切换到"Segment Anything"分类;
- 下载对应的 SAM ONNX 模型(比如 ViT-B 版,大小约 350MB);
- 加载后,用"自动分割"工具在目标上点一下,模型会生成整个目标的掩码;
- 切换到多边形工具,对生成的掩码做边缘微调。
这里有个非常实用的技巧:先放模型粗标,再人修。实测下来,SAM 在轮廓清晰的目标上表现很好,但在两个目标粘连、边界模糊的位置,给出的掩码会跨越目标边界。这时候不要完全重画,用多边形工具在错误区域附近点几个点,把不属于当前目标的区域排除掉就行,比从头画快得多。
标注格式方面,X-AnyLabeling 支持导出多种格式。我喜欢用 COCO 格式做中间存储,因为 COCO 的 JSON 结构方便后续程序读取,也方便转成别的格式。如果你最终要训 YOLO,导出为 YOLO txt 格式也可以,软件里开启了自定义 YAML 配置后就能正确导出来。
2.3 用 X-AnyLabeling 做开工前的数据摸底
很多人拿到一批新图直接上 autodistill 批量标注,我强烈不建议这么做。效率最高、最稳的路径是:先在 X-AnyLabeling 里随机抽 100 张图手动标注。
为什么要做这一步?因为自动标注的前提是"知道要标什么"。抽 100 张图手工标完,你会对三个问题心中有数:数据里包含哪些类别、哪些目标形态差异大、哪些场景模型容易翻车。比如做车辆检测,你以为只有小轿车和货车,标完才发现还有大量三轮车、特种工程车,提示词里如果不加,Grounded-SAM 就会漏得干干净净。
摸底标注还有一个作用:定义 Ontology 的类别清单。autodistill 里的 Ontology 是一份类别列表,所有自动标注都围绕这份列表展开。列表定错了,后面全部白做。我用 X-AnyLabeling 摸完底之后,会把每类的典型图片截下来存成一个参考文件夹,后续自动标注的提示词就参考这些图片来写。
3. Grounded-SAM 拆解:从一句话到一张掩码图
3.1 Grounding DINO 负责"找",SAM 负责"切"
Grounded-SAM 不是单一模型,而是两个模型的串联,理解这一点能够帮助你排查绝大多数问题。
前半段是 Grounding DINO。它是一个开放集目标检测模型,输入一张图和一段文本提示(比如 "a red car"),输出的是图中所有匹配文本描述的物体框。它的内部机制是跨模态融合:文本经过语言编码器,图像经过视觉骨干网络,二者在多层特征上做交叉注意力匹配。通俗讲,它是在"读"文字并"看图找对应区域"。
后半段是 SAM。SAM 是一个分割基础模型,输入一张图加上提示(可以是点、框或掩码),输出精确的分割掩码。Grounded-SAM 就是把 Grounding DINO 检测出的框作为提示喂给 SAM,让 SAM 在框范围内生成像素级掩码。检测负责定位,分割负责抠边,各干各的活。
这个组合在推理时的参数设置直接影响效果。框的置信度阈值建议先设 0.35 再按实测调高或调低。阈值设太高(比如 0.7)容易漏检;设太低,会多出一堆误报框,浪费显存和时间。箱扩边系数(box_threshold)也要注意,Grounding DINO 输出的框通常偏紧,SAM 需要一点外扩空间才能切出完整的掩码边缘,通常外扩 10% 左右效果比较好。
3.2 文本提示词的写法是唯一需要拼经验的地方
Grounded-SAM 是零样本模型,理论上不需要训练,但这不代表不需要调。整个系统里最影响标注质量的因素,就是提示词。
我把提示词的经验总结成几条:
第一,用英文,别用中文。Grounding DINO 的语言编码器主要基于英文语料训练,中文提示词的效果明显差一截。让模型表现稳定的做法是把类别词翻译成标准英文,比如"汽车"写成 "car" 而不是 "qiche"。
第二,类别词要短,描述别贪多。一个类别一个核心名词最稳。带修饰词容易引入歧义,比如写 "white dog" 模型确实只找白狗,但你的 Ontology 类别是 "dog",用 "white dog" 作为提示词会漏掉其他毛色的狗。
第三,多个类别之间用句号分隔,而不是逗号。autodistill 里传入 Ontology 后,内部会把类别拼接成提示词,句号分隔能减少类别间的互相干扰。我写过 "person. car. traffic light." 这种格式,效果比逗号分隔稳定。
第四,针对容易漏检的类别,可以给提示词加同义词。比如 "truck / lorry",模型对某些车型的语义理解有偏差,多一个同义词漏检率会降不少。这个技巧对特殊装备类别特别管用。
3.3 显存占用与推理速度的实测数据
Grounded-SAM 是好用,但它是资源大户。我在实验室测过一组数据,模型是 Grounding DINO 的 Swin-T 后端加 SAM ViT-B,显卡是 RTX 3090:
| 输入尺寸 | 单张推理耗时 | 显存占用 | 备注 |
|---|---|---|---|
| 512 x 512 | 约 1.2 秒 | 约 5.5 GB | 小目标容易漏检 |
| 800 x 800 | 约 2.1 秒 | 约 7.8 GB | 综合最佳 |
| 1024 x 1024 | 约 3.5 秒 | 约 10.5 GB | 适合高清小目标 |
如果你只有 8GB 显存的卡,800x800 输入尺寸是最稳妥的选择。显存不够时不要硬上大图,而是先跑 512,看漏检情况再决定是否分块推理。分块推理比较麻烦,因为分块会切断跨块的大目标,掩码对不上,我一般不建议在初版流水线里用。
推理速度还有个隐藏瓶颈:Grounding DINO 的预处理会把文本输入重复编码,类别越多,编码阶段耗时越长。几十个类别的时候耗时差异还看不出来,几百个类别时预热时间会明显拉高。所以 Ontology 里的类别尽量精简,只保留当前批次任务需要的,不要把所有历史类别一股脑塞进去。
4. autodistill:把教师模型的输出变成学生模型的养料
4.1 核心概念:Teacher、Student、Ontology
autodistill 的核心理念是"蒸馏式标注"。它把流程抽象成三个角色:Teacher(教师模型)、Student(学生模型)、Ontology(类别本体)。
Teacher 是那些能力很强、但推理成本高的基础模型,比如 Grounded-SAM、GLIP、甚至是闭源 API。它们什么都能标,但慢、贵、部署重。Student 是你要训练的目标模型,比如 YOLOv8、RT-DETR,轻量、快速、能部署到实际场景。autodistill 的工作就是让教师模型自动给数据打标注,然后学生模型在这个标注数据集上训练。
Ontology 是这个体系里我最喜欢的设计。它是一个纯类别的容器,定义方式相当简单:
from autodistill.core import Ontology # 方式一:纯类别列表 ontology = Ontology.from_classes(["person", "car", "traffic_light"]) # 方式二:类别同时指定提示词 ontology = Ontology.from_classes([ "person", "truck / lorry", "traffic_light", ])方式二非常实用,因为 Grounded-SAM 的提示词可以比类别名更丰富。你在 Ontology 层面写好提示词,后续标注、训练都基于这份定义,一处定义全局生效。换学生模型时也不用改 Ontology,这是 autodistill 设计得比较舒服的地方。
4.2 一段代码跑通自动标注与训练
autodistill 的 API 封装得相当薄,核心就几个类。下面是一段完整可跑的示例,完成"用 Grounded-SAM 标注 + 训 YOLOv8":
from autodistill_grounded_sam import GroundedSAM from autodistill_yolov8 import YOLOv8 from autodistill.core import Project, Ontology # 1. 定义类别和教师模型 ontology = Ontology.from_classes(["person", "car", "traffic_light"]) # 2. 初始化 Project:输入原始图片目录,输出标注结果 project = Project( teacher=GroundedSAM(ontology=ontology), ontology=ontology, input_folder="raw_images/", output_folder="labeled_dataset/", ) # 3. 执行批量自动标注 project.label() # 4. 在标注结果上启动学生模型训练 student = YOLOv8(ontology=ontology) student.train("labeled_dataset/dataset.yaml", epochs=80)整个流程的运行过程很直观:label() 扫描 input_folder 下所有图片,逐张调用教师模型推理,把检测框和掩码结果写入 output_folder,并在里面生成 dataset.yaml 配置。train() 只需要认准那个 yaml 文件,按正常训练走。
我最初跑的时候卡在第 2 步的初始化,查了半天发现是版本问题。autodistill 拆分成了多个独立包,核心包不包含教师模型实现,必须同时安装 autodistill 和 autodistill-grounded-sam,少一个都会在初始化时报 No module named 'autodistill_grounded_sam'。
4.3 为什么要用重模型打样、轻模型接手
有人会问:Grounded-SAM 能零样本标注,那为什么不直接用它做推理,还要训练个 YOLOv8?答案很简单,成本和速度。
Grounded-SAM 单张推理要一到三秒,显存七八个 G,部署端跑不动,推理吞吐也扛不住每秒几十帧。YOLOv8 训练好之后,一张 640 分辨率的图在 3090 上推理只要 2 到 4 毫秒,在 Jetson 上也能跑到实时,这是量级的差距。
更重要的原因是场景固化。你的业务场景里类别是固定的,用 Grounded-SAM 标注完一轮数据,相当于把通用知识"蒸馏"到了轻量模型里。轻量模型在特定场景上的表现可以反超教师模型,因为它只在你的数据分布上学,干扰更少。这个过程就是 autodistill 名称的由来:不是模型蒸馏权重,而是蒸馏标注信号。
我个人的经验是,不要指望自动标注的 Grounded-SAM 是一锤子买卖。真正高效率的生产方式是"自举迭代":第一轮用 Grounded-SAM 标注,人工校验修一轮,训练出 YOLOv8 v1;再用 v1 去标注剩余未标注图片,人工只需重点看 v1 的低置信度结果;用新数据训 v2。每轮人工参与比例递减,模型质量递增。
5. 三件套串起来:从原始图片到可训练数据集的完整流程
5.1 全流程手工拼装:五个环节缺一不可
我在这条流水线上折腾了几个月,最终固定下来的流程如下:
- X-AnyLabeling 小样本摸底,人工标注 50 到 100 张图,确定 Ontology 类别;
- 服务器部署 autodistill + Grounded-SAM,用类表批量自动标注全部原始图片;
- 把自动标注结果导入 X-AnyLabeling,按置信度排序,人工逐张复核修正;
- 复核后的数据转成目标格式,训练 YOLOv8 或 RT-DETR 等学生模型;
- 用学生模型继续标注新采集的数据,返回步骤 3,往复迭代。
第 5 步往往被人忽略,但它才是流水线真正"转起来"的关键。如果每次新增数据都回到 Grounded-SAM,成本太高;训练好的学生模型在固定场景上比 Grounded-SAM 更快更准,只是初始冷启动阶段需要 Grounded-SAM 打底。
5.2 操作实录:用"车辆与行人"数据集走一遍完整流程
拿一个具体的例子说明。假设我要做一个十字路口的车辆行人检测数据集,原始图片 3000 张。
第一步,我在 X-AnyLabeling 里随机抽 80 张手工标注。摸底发现,场景里除了 car、person,还有大量 bus 和 bicycle,而且夜间图片有反光,小目标特别多。于是 Ontology 定为 ["person", "car", "bus", "bicycle / bike"]。
第二步,写一份标注脚本调用 autodistill:
from autodistill_grounded_sam import GroundedSAM from autodistill_yolov8 import YOLOv8 from autodistill.core import Project, Ontology # 提示词按摸底结果调整:bicycle 加了同义词 ontology = Ontology.from_classes([ "person", "car", "bus", "bicycle / bike", ]) project = Project( teacher=GroundedSAM(ontology=ontology), ontology=ontology, input_folder="crossroad_imgs/", output_folder="crossroad_labeled/", ) project.label()运行后它会生成一个 dataset.yaml,类别索引顺序和 Ontology 定义一致。这里要记得:类别顺序会影响标注的 id 编号,如果后续要追加新类别,最好重新生成数据集,避免历史标注 id 错位。
第三步,把 crossroad_labeled 里的 COCO 格式结果用脚本转成 YOLO txt。X-AnyLabeling 不直接读取 COCO JSON 作为图层标注(它主要读自己的标签文件),所以我用了一个小脚本把项目的 mask 转回 labelme JSON 格式,再导入 X-AnyLabeling。有现成脚本可抄,核心逻辑是把每个目标的 bounding box 换算成归一化坐标,写入对应图片的同名 txt。
第四步也是最耗人工的环节:逐张复核。我把推理置信度低于 0.5 的图片排在前面优先检查,高置信度的抽查。3000 张图里大约有 600 张需要重点看,其余 2400 张快速扫一遍即可。
第五步,训练 YOLOv8s。3000 张图,80 epochs,3090 上大概半小时跑完。验证集 mAP50 到了 0.88,其中 car 和 person 很高,bicycle 偏低,因为自行车形态差异大,提示词二义性强,人工修正时多花了些力气。
5.3 数据质检:自动标注产出的数据集必须筛一遍
质检这步建议不要省。自动标注跟人一样会犯错,只是犯错模式不同。人容易疲劳犯错,Grounded-SAM 的错偏向以下几类:
漏检是最常见的问题。目标过小、遮挡严重、光照极端时,Grounding DINO 给出框的置信度太低,被过滤掉了。这类错误在全自动模式下很难发现,因为你看到的标注文件是"成功检出"的部分,看不到"本该有但没检出的"空缺。质检时必须结合原始图片快速目检,或者对特定小目标区域做针对性人工检查。
误检通常是语义混淆。比如 "person" 提示词可能把电线杆、服装店模特、广告牌上的人像也框出来。这类错误在巡检场景很常见,因为模型没有业务上下文,它不知道"场景里根本不该有广告牌"。
掩码粗糙则集中在边界区域。SAM 对边缘锐利的目标表现好,但遇到半透明物体、反光表面、与背景同色的目标,掩码边缘会泄漏到背景。我的质检经验是:抽样放大看掩码边缘,如果 20 个目标里超过 3 个有明显泄漏,那这一批全量跑一遍边缘修正比较值。
6. 踩坑记录与效果实测:自动标注不等于免检
6.1 我踩过的四个坑,每一个都花了不少时间
第一个坑是 Python 版本冲突。autodistill 依赖的 supervision 库和部分模型包,在 Python 3.10 以下表现最好。我一开始用 3.11 跑,装 supervision 时依赖树直接冲突,反复折腾。后来统一用 conda 建了 Python 3.9 环境,所有包一把装过。建议所有项目用同一个环境管理文件固定版本,不要东一个环境西一个环境。
第二个坑是中文提示词。我第一次用中文类别 "汽车" 跑 Grounded-SAM,漏检率高得离谱,汽车只检出来一半,轿车和 SUV 漏了不少。换成 "car" 之后立竿见影。这里提醒一下:任何零样本模型都要考虑训练语料的语言偏向,英文提示词在这类模型上通常是最稳的选择。
第三个坑是 X-AnyLabeling 的模型目录不能有中文路径。我一开始把模型放在中文目录下的"预训练模型"文件夹里,加载的时候一直报错误,换到纯英文路径就好了。Windows 上尤其容易出现这个问题,建议所有模型文件统一放在英文路径下。
第四个坑是 batch_size 和显存的关系。autodistill 的 GroundedSAM 在 label() 阶段默认是逐张处理的,但我看到网上有人改成 batch 推理来提速,结果显存直接爆掉,还把标注进程拖崩了。label() 阶段不要贪 batch,老老实实逐张推,换取稳定性。如果真觉得慢,优先调小输入尺寸而不是加 batch。
6.2 自动标注与人工复核的工作量配比:没有奇迹,但有杠杆
网上很多人宣传自动标注是"全自动无人值守",我实际用下来的感觉是:能省掉 70% 的标注工作量,但剩下的 30% 复核工作一点不能少。
拿前面 3000 张图的例子来说。纯手工标注,按一天 80 张算,需要 37 个工作日。自动标注加复核,大约 4 个工作日,其中 3 天在复核修正。听起来复核占比还是高,但总周期从 37 天降到 4 天,这是质的变化。如果有人告诉你自动标注后完全不用看,那他不是没做过大规模项目,就是数据太简单(比如单一类别、前景背景对比极其明显)。
我把自动标注定位成"杠杆工具":它放大你的人工时间,把人力从 80% 的画框时间降到 20% 的决策时间。你不需要再画框,但你需要判断"这个框对不对"。这个判断力,恰恰是模型替代不了的。
6.3 效果实测与成本估算:这笔账划算吗
最后给一组我实测的数据参考。项目背景是工业零部件检测,类别 12 类,图片 5000 张,中低复杂度场景。
| 环节 | 纯手工方案 | 自动标注流水线 | 备注 |
|---|---|---|---|
| 标注工时 | 约 55 天 | 约 6 天 | 含复核修正 |
| 掩码精度(IoU) | 约 0.90 | 约 0.86 | 修正后接近 0.89 |
| GPU 成本 | 0 | 约 120 元(云租用) | 3090 跑两天 |
| 人工成本 | 标注员 2 人 | 复核 1 人半程参与 | 大幅节约 |
这笔账的结论很清楚:自动标注不是零成本,它把成本从"纯人力"转移到了"GPU 租用 + 少量复核人力"。对于个人开发者,省下的时间完全可以覆盖 GPU 成本;对于团队,可以把标注员从重复劳动中解放出来,去做更有价值的质检和场景分析。
根据我个人的经验,这套流程最值钱的不是某一个工具,而是"小样本摸底 -> 零样本批量标注 -> 轻量模型自举"的思路转换。工具更新换代很快,今天可能是 Grounded-SAM,明天可能就有更强的视觉大模型,但这条思路是通用的:先用重模型把数据标注的冷启动成本打下来,再用轻模型把标注能力固化到自己的场景里,人工永远守在质检那一环,只做模型做不了的决定。这个定位想清楚了,自动标注就不会给你添乱,而是真正为你省时间。