1. 自动标注这条链路到底解决了什么痛点做过视觉项目的人都有一个共识模型效果的上限往往不是被网络结构卡住的而是被标注数据卡住的。一个目标检测或者分割任务前期花在拉框、描边、分类上的时间经常占到整个项目周期的六七成。更难受的是当你辛辛苦苦标完一批数据、训练完一版模型发现漏检的类别还得补标于是又回到标注工具里重复劳动。这个循环一旦转起来人就变成了流水线上的一颗螺丝。自动标注要解决的就是这个循环。它的核心思路是用已有的基础模型先跑一遍推理把结果作为预标注导入标注工具人工只做修正和确认而不是从零开始画。这样一来单张图的标注时间可以从几分钟压缩到几十秒甚至几秒。而X-AnyLabeling、autodistill、Grounded-SAM这三个东西组合起来恰好能覆盖预标注生成—人工精修—模型迭代的完整闭环也就是大家常说的数据飞轮。我先把这三个角色说清楚不然后面容易混。X-AnyLabeling是一个带 AI 辅助能力的标注客户端它本身集成了 SAM、YOLO 等模型可以在标注界面里一键出框、一键分割支持导出多种格式。Grounded-SAM是 GroundingDINO 加 SAM 的组合前者负责用文字描述找到目标框后者负责把框变成精细掩码也就是说你输入cat它就能把图里所有猫框出来并分割。autodistill则是一个编排框架它把用大模型给无标注数据打伪标签、再训练小模型这件事标准化了你只要指定基础模型和目标模型它帮你把中间流程串起来。这三者拼在一起的价值在于Grounded-SAM 负责零样本的粗标注autodistill 负责把粗标注转成可训练的数据集并蒸馏出轻量模型X-AnyLabeling 负责人工介入的精修和格式导出。适合谁来参考如果你手头有一批没标注的图、想快速起一个检测或分割任务或者你已经在用 LabelImg、Labelme 觉得效率太低这套流程值得花一个下午搭起来。哪怕你只是想了解自动标注的原理跟着走一遍也能把概念落地。2. 三个工具的分工与选型逻辑2.1 为什么不是随便挑一个用很多人第一反应是我直接用 Grounded-SAM 跑完不就行了为什么还要 autodistill 和 X-AnyLabeling。这个问题我踩过。Grounded-SAM 的输出是零样本的它对常见类别效果不错但对细分类别、密集小目标、遮挡严重的场景召回和精度都不稳定。你直接拿它的结果当训练集训出来的模型会继承它的偏差越训越偏。所以它只能当第一遍粗筛不能当最终答案。autodistill 存在的意义是把粗筛结果变成可迭代的数据资产。它的工作方式是用 Grounded-SAM 这类基础模型对无标注图片生成伪标签然后拿这些伪标签去训练一个 YOLO 或者 DETR 之类的小模型。小模型在特定数据分布上微调后精度往往能超过基础模型本身而且推理速度快几十倍。这就是知识蒸馏在标注场景的落地。X-AnyLabeling 则是人机协同的入口。自动标注不可能 100% 正确人工必须能高效地检查和修正。X-AnyLabeling 的优势是它把模型推理内置到了标注界面里你按一个快捷键就能让当前图重新推理或者对某个框做 SAM 精修不用在命令行和标注工具之间来回切。这个体验差异在实际标注几千张图的时候非常明显。2.2 三者的能力对照工具核心能力输入输出在流程中的位置Grounded-SAM文本提示零样本检测分割图片文字描述框掩码伪标签生成autodistill伪标签编排与模型蒸馏无标注图片集训练好的小模型数据飞轮引擎X-AnyLabelingAI辅助标注与格式导出图片预标注精修后的标注文件人工精修与交付这张表建议存下来后面配置环境的时候对照着看能少走很多弯路。选型上我的建议是如果你的类别是 COCO 里常见的八十类Grounded-SAM 直接跑效果就够用如果是工业缺陷、医学影像这种专有类别Grounded-SAM 的文本提示要写得非常具体而且必须经过 autodistill 蒸馏才能用如果团队里有人工标注资源X-AnyLabeling 一定要配上它是保证数据质量的下限。2.3 环境依赖的坑要先说这三个工具对环境的敏感度不一样。Grounded-SAM 依赖 PyTorch、GroundingDINO 的自定义 CUDA 算子、segment-anything 的权重安装过程最容易出问题。autodistill 相对干净它本质是 Python 包编排但会拉一堆依赖。X-AnyLabeling 是带 GUI 的它对 PyQt 版本、显卡驱动、CUDA 版本都有要求而且不同版本对 Python 版本的支持不一样。我实测下来比较稳的组合是Python 3.10、PyTorch 2.1 配 CUDA 11.8、GroundingDINO 用官方仓库编译、segment-anything 用 pip 装、X-AnyLabeling 用官方 release 的打包版本而不是源码跑。为什么 X-AnyLabeling 建议用打包版因为源码跑的时候 PyQt 和 onnxruntime 的版本冲突非常常见打包版已经把这些依赖锁死了省心。如果你非要用源码跑比如要改界面那一定要单独建一个虚拟环境别和 Grounded-SAM 的环境混用。注意Grounded-SAM 和 X-AnyLabeling 不要装在同一个 conda 环境里。前者要编译 CUDA 算子后者要特定版本的 PyQt两者对 numpy、protobuf 的版本要求经常打架混装的结果就是两个都跑不起来。3. Grounded-SAM 伪标签生成实操3.1 环境搭建的关键步骤先建一个独立环境我习惯用 condaconda create -n grounded-sam python3.10 -y conda activate grounded-sam pip install torch2.1.0 torchvision0.16.0 --index-url https://download.pytorch.org/whl/cu118PyTorch 装完先验证一下 CUDA 是否可用这一步别跳过import torch print(torch.cuda.is_available()) print(torch.version.cuda)如果输出 False后面所有推理都会退到 CPU速度慢到没法用。确认可用之后再装 GroundingDINO。GroundingDINO 需要编译一个 CUDA 扩展所以要先确认本机有 nvccnvcc --version没有的话要装对应版本的 CUDA Toolkit。编译命令是git clone https://github.com/IDEA-Research/GroundingDINO.git cd GroundingDINO pip install -e .编译过程如果报 no kernel image is available基本是 CUDA 架构不匹配需要在 setup.py 里把-gencode参数改成你显卡对应的算力比如 3090 是 8.64090 是 8.9。这个坑我遇到过两次第一次折腾了一晚上才定位到。segment-anything 就简单多了pip install githttps://github.com/facebookresearch/segment-anything.git然后下载三个权重文件GroundingDINO 的groundingdino_swint_ogc.pth、SAM 的sam_vit_h_4b8939.pth。权重文件加起来好几个 G建议提前下好放本地别在代码里现下。3.2 文本提示怎么写才准Grounded-SAM 的效果高度依赖文本提示prompt的写法。这里有个反直觉的点不是写得越详细越好而是要贴近模型训练时的语言分布。GroundingDINO 训练数据里的类别描述大多是简短名词短语所以你写 person 比写 a human being standing in the scene 效果好得多。几个实操经验类别之间用英文句点分隔比如person . car . dog .末尾的点别漏它起到分隔符作用。同义词可以都写上比如bottle . flask .能提升召回。不要写颜色、位置、状态这类修饰模型对这类词不敏感反而干扰。如果某个类别总是漏检试试换更通用的上位词比如 sedan 换成 car。阈值方面box_threshold控制框的置信度text_threshold控制文本匹配的置信度。默认 0.3 和 0.25 对大多数场景够用。如果发现框太多太杂把 box_threshold 提到 0.4如果漏检严重降到 0.2 试试。这两个参数没有万能值要拿几张典型图试出来。3.3 批量推理脚本单张图跑通之后写个批量脚本处理整个文件夹。核心逻辑是遍历图片、调用 Grounded-SAM、把结果存成 COCO 或者 YOLO 格式。下面是我常用的一个精简版import os import cv2 import torch import numpy as np from groundingdino.util.inference import load_model, load_image, predict from segment_anything import sam_model_registry, SamPredictor TEXT_PROMPT person . car . dog . BOX_THRESHOLD 0.3 TEXT_THRESHOLD 0.25 model load_model(GroundingDINO/groundingdino/config/GroundingDINO_SwinT_OGC.py, weights/groundingdino_swint_ogc.pth) sam sam_model_registry[vit_h](checkpointweights/sam_vit_h_4b8939.pth) sam.to(cuda) predictor SamPredictor(sam) img_dir images out_dir pseudo_labels os.makedirs(out_dir, exist_okTrue) for name in os.listdir(img_dir): path os.path.join(img_dir, name) image_source, image load_image(path) boxes, logits, phrases predict( modelmodel, imageimage, captionTEXT_PROMPT, box_thresholdBOX_THRESHOLD, text_thresholdTEXT_THRESHOLD) predictor.set_image(image_source) h, w, _ image_source.shape for box, phrase in zip(boxes, phrases): xyxy box * torch.Tensor([w, h, w, h]) masks, _, _ predictor.predict(boxxyxy.numpy()[None, :], multimask_outputFalse) # 这里把 masks 和 phrase 存成你要的格式 print(fdone: {name})这段代码里有个细节值得说box * torch.Tensor([w, h, w, h])是把归一化坐标还原成像素坐标GroundingDINO 输出的是归一化的 cxcywh用之前一定要转。我第一次写的时候忘了转框全挤在左上角排查了半天。3.4 伪标签的质量控制伪标签不是生成完就能用的必须做一轮质量过滤。我的做法是按置信度排序把低于阈值的框直接丢掉。对面积过小的框比如小于 20x20 像素做过滤这类框大多是噪声。对重叠度极高的同类框做 NMS避免一个目标出多个框。抽样人工看 50 到 100 张统计漏检和误检的比例决定要不要调 prompt 或阈值。这一步花的时间不多但能避免把垃圾数据喂给下游。我见过有人跳过这步结果蒸馏出来的模型精度还不如直接用 Grounded-SAM白忙一场。4. autodistill 把伪标签变成可用模型4.1 autodistill 的工作机制autodistill 的设计很巧妙它把基础模型和目标模型解耦了。基础模型负责打标签目标模型负责学习。你只需要写几行代码指定两者中间的格式转换、数据集划分、训练循环它都帮你处理了。它的核心抽象是BaseModel和TargetModel。比如用 GroundingDINO 当基础模型、YOLOv8 当目标模型from autodistill_grounding_dino import GroundingDINO from autodistill_yolov8 import YOLOv8 base_model GroundingDINO(ontologyontology) target_model YOLOv8(yolov8n.pt) target_model.train(./dataset, epochs50)这里的ontology是本体定义说白了就是类别列表和它们的文本描述。autodistill 用这个 ontology 去调基础模型把每个类别的伪标签生成出来。4.2 ontology 的定义技巧ontology 是 autodistill 里最需要花心思的地方。它决定了基础模型用什么词去找目标。定义方式有两种简单的是直接给类别名列表复杂的是用Ontology类做层级定义。from autodistill.ontology import Ontology ontology Ontology( images./unlabeled_images, detections_ontology[ person, car, traffic light ] )如果类别之间有包含关系比如车辆下面有轿车卡车可以用层级结构这样基础模型先找大类再细分召回会更好。但层级太深也会让 prompt 变复杂一般两层就够了。我踩过的一个坑是ontology 里的类别名要和后续 X-AnyLabeling 里的标签名完全一致包括大小写和空格。不一致的话导入预标注的时候标签会对不上得手动映射很烦。所以定义 ontology 的时候就把最终标签体系定死。4.3 蒸馏训练的参数选择autodistill 默认用 YOLOv8 当目标模型这个选择是合理的因为 YOLOv8 训练快、部署方便、社区支持好。模型大小从 n 到 x 有五个档选哪个取决于你的精度要求和部署环境。模型参数量推理速度适用场景yolov8n3.2M最快边缘设备、实时检测yolov8s11.2M快一般服务器部署yolov8m25.9M中精度要求较高yolov8l43.7M慢离线高精度yolov8x68.2M最慢精度优先我的建议是先用 yolov8n 跑一轮看精度能不能接受。如果差得多再往上换。因为伪标签本身有噪声用太大的模型反而容易过拟合噪声。epochs 一般设 50 到 100配合早停。学习率用默认的 0.01 起步如果 loss 震荡就降到 0.001。训练完之后一定要在留出的验证集上评估别只看训练 loss。伪标签训练的模型验证集精度和训练集精度差距往往比较大这个差距就是伪标签噪声的体现。4.4 数据飞轮的迭代节奏autodistill 真正的价值在于迭代。第一轮用 Grounded-SAM 生成伪标签、训练 YOLOv8然后用这个 YOLOv8 去推理新的无标注数据把高置信度的结果加入训练集再训一轮。每轮迭代模型对专有类别的识别能力都会提升因为它在逐步拟合你的数据分布。这个循环跑三轮左右精度通常能接近全人工标注的水平。但要注意每轮加入的新数据要控制比例别一次性加太多低质量样本。我的做法是每轮只加入置信度高于 0.7 的预测且人工抽检确认无误后才入库。5. X-AnyLabeling 人工精修与导出5.1 安装与模型配置X-AnyLabeling 官方提供了各平台的打包版本直接下载解压就能用。如果你要用源码跑环境是conda create -n xanylabeling python3.10 -y conda activate xanylabeling pip install -r requirements.txt python anylabeling/app.py源码跑的时候最容易出问题的是 onnxruntime 和 PyQt5 的版本。requirements 里锁的版本不一定适配你的系统如果启动报 DLL 加载失败多半是 onnxruntime 的问题换成onnxruntime-gpu或者降版本试试。模型配置在设置里的模型选项卡。X-AnyLabeling 支持导入自定义的 ONNX 模型你可以把 autodistill 训出来的 YOLOv8 导出成 ONNX 放进去。导出命令yolo export modelruns/detect/train/weights/best.pt formatonnx opset12opset 用 12 兼容性最好。导出后在 X-AnyLabeling 里新建一个模型配置指定模型路径、输入尺寸、类别列表就能在标注界面里用这个模型做预标注了。5.2 快捷键与高效标注流程X-AnyLabeling 的快捷键设计得挺顺手熟练之后效率提升明显。常用的几个D/A下一张 / 上一张CtrlI用当前模型对整图推理CtrlJ对选中的框做 SAM 精修CtrlS保存Delete删除选中的标注高效流程是这样的打开一张图先CtrlI让模型出预标注然后快速扫一遍框对的留着框错的删掉或调整漏的补上。对于分割任务选中框按CtrlJSAM 会把框变成精细掩码比手描快太多。有个技巧是批量推理。X-AnyLabeling 支持对整个文件夹做批量预标注你晚上挂机跑第二天来精修能省不少时间。批量推理的入口在AI 标注菜单里选好模型和文件夹就行。5.3 导出格式与下游对接X-AnyLabeling 支持导出 COCO、YOLO、VOC、Labelme 等多种格式。导出前要确认类别映射正确尤其是从预标注导入的标签有时候会带上模型输出的原始类别名需要统一成你的标签体系。导出 YOLO 格式的时候注意它生成的是images和labels两个文件夹加一个classes.txt。labels 里是归一化的class_id cx cy w h。如果你要做分割导出的是多边形点集格式和检测不一样下游训练脚本要对应改。注意导出前先备份原始工程文件。X-AnyLabeling 的工程文件是 json 格式导出操作有时会覆盖我吃过一次亏几百张图的标注差点丢了。6. 常见问题与排查速查6.1 环境类问题现象可能原因解决方向torch.cuda.is_available() 为 False驱动或 CUDA 版本不匹配重装对应 CUDA 版本的 torchGroundingDINO 编译报错缺 nvcc 或算力不匹配装 CUDA Toolkit改 gencodeX-AnyLabeling 启动闪退PyQt 或 onnxruntime 冲突用打包版或单独建环境SAM 推理 OOM显存不足换 vit_b 权重或缩小输入尺寸6.2 效果类问题伪标签漏检严重怎么办先检查 prompt 写法换成更通用的词。再降 box_threshold 到 0.2。如果还不行说明基础模型对你的类别确实不熟这时候要么换基础模型要么老老实实人工标一批种子数据用种子数据微调基础模型再生成伪标签。蒸馏后模型精度反而下降大概率是伪标签噪声太大。回去做质量过滤把低置信度、小面积、重叠的框清掉。另外检查训练集和验证集有没有数据泄漏伪标签生成和评估用了同一批图会导致虚高。X-AnyLabeling 导入预标注后标签错乱检查类别名是否完全一致包括大小写。X-AnyLabeling 是按字符串匹配的差一个字符就匹配不上。6.3 几个独家避坑经验第一Grounded-SAM 的推理结果一定要存原始输出别只存可视化图。后面调阈值、换格式都要用到原始框和掩码重新跑一遍很费时间。第二autodistill 训练时把workers设小一点设太大在有些系统上会卡死。我一般设 4。第三X-AnyLabeling 的自动保存间隔默认比较长建议在设置里改成 1 分钟防止意外崩溃丢数据。第四整个流程的中间产物伪标签、ONNX 模型、工程文件按日期分文件夹存迭代几轮之后你会感谢自己做了这件事。7. 关于数据飞轮的一点个人体会这套流程我前后搭过三次每次都有新的坑但每次跑通之后的收益都很实在。最开始我执着于把 Grounded-SAM 的阈值调到完美后来发现没必要因为下游还有 autodistill 和人工精修两道关粗标注的容错空间比想象中大。真正决定最终质量的是人工精修那一步以及迭代的轮数。另外别指望一次就把类别体系定死。实际项目里类别经常要增删所以 ontology 和标签体系要设计得容易扩展。我现在的习惯是留一个其他类兜底遇到暂时归不了类的目标先扔进去后面再细分。最后分享一个小技巧如果你手头有少量已标注数据别浪费用它们去评估每一轮蒸馏模型的精度画出精度随迭代轮数的曲线。这条曲线能告诉你什么时候该停止迭代也能在汇报的时候拿出有说服力的证据。数据飞轮转起来之后你会发现标注这件事从体力活变成了调参活这才是它最大的价值。