资讯动态

Grounding DINO实战指南:从零样本检测到微调部署

发布时间:2026/10/17 8:39:01 来源:尧图企业网站定制
提到 Grounding DINO我先说句实话这可能是目前开源世界里把“用一句话检测任意目标”这件事做得最顺手的模型之一。就算你训练数据里从来没有某个类别只要在提示词里把类别写清楚它就能把目标框给你画出来。这个能力放在两三年前得靠一堆手工标注加复杂训练流程才能勉强接近现在一个开源模型就搞定了。我前后在好几个项目里用过它做训练和推理今天把这套流程里我认为最有价值的部分完整的整理出来从环境搭建到微调训练再到推理部署一步步说清楚希望能帮你少走点弯路。1. 环境准备与工具选型1.1 硬件要求与版本选型先说硬件。Grounding DINO虽然是开放集检测但本质还是Transformer架构的检测模型对显存的要求不算低。我的建议是推理阶段一张 8GB 显存的卡就能跑但batch size只能开到1多张图会非常吃力。实测 8GB 显存跑 Swin-T 主干单张 640 分辨率图片的推理峰值显存大约 4-5GB基本是压线通过。训练阶段微调强烈建议 24GB 以上比如 3090、4090 或 V100、A100。如果只有 16GB 显存也不是不能跑但batch size只能开 1还要配合梯度累积训练效率会低不少。如果只是做小数据集的微调比如几百张图一张 24GB 的卡其实就够了不一定非要上多卡。版本选型也是一个关键决策。我踩过的坑是不要无脑拉最新代码。Grounding DINO 官方仓库更新不算频繁社区fork版本反而更活跃但选fork又容易引入别人改过的逻辑出了问题很难排查。我的做法是固定官方主仓库的某一个commit配合对应版本的模型权重绝不混用。依赖版本方面核心是 PyTorch 和 torchvision 的匹配其次要特别注意CUDA版本建议用 11.8 或 12.xPyTorch 对应安装即可Python用 3.8-3.10 都行我用 3.9 遇到的坑最少transformers库版本建议 4.18 或 4.21太新的话BertModel接口变化可能导致模型加载失败einops、yaml、opencv-python、scipy这些常规依赖直接装最新版问题不大1.2 环境搭建实操我用的是 conda 管理环境整个安装流程如下conda create -n groundingdino python3.9 conda activate groundingdino pip install torch1.13.1 torchvision0.14.1 --index-url https://download.pytorch.org/whl/cu117 pip install -r requirements.txtrequirements.txt 里的核心依赖大致是matplotlib opencv-python transformers4.21.1 einops scipy timm yaml装完之后可以快速验证环境是否正常import groundingdino print(ok)能正常导入就说明依赖基本没问题。如果 import 时报错九成是某个依赖版本冲突按报错往上定位即可不要盲目重装。1.3 权重文件准备Grounding DINO 的权重分为预训练权重和微调权重官方提供了好几种主干对应的权重。最常用的是 Swin-T 和 Swin-B模型变体主干网络权重大小推理精度显存占用Swin-TTiny~700MB适中较低Swin-BBase~1.2GB较高较高权重文件需要和配置文件对应比如swint_oogc.pth就要配合groundingdino_swint_oogc.py使用如果配错加载时会出现 key 不匹配的报错。很多新手第一次加载模型就报一长串 missing keys 和 unexpected keys其实主要原因就是权重和配置文件不匹配。下载权重时如果网络不稳定可以先用下载工具拉下来放到本地目录之后手动指定路径。我在代码里通常这样加载from groundingdino.util.inference import load_model, load_image model load_model( config_pathconfigs/groundingdino_swint_oogc.py, weights_pathweights/groundingdino_swint_oogc.pth )load_model内部会自动读取配置文件中的模型结构然后加载权重。只要这两者的名字和结构对得上基本不会出错。2. 推理零样本检测的正确用法2.1 推理脚本核心参数推理是绝大多数人接触 Grounding DINO 的第一步。官方提供了一套很早就能跑通的推理脚本我贴一个我自己改好的版本from groundingdino.util.inference import load_model, load_image, predict # 加载模型 model load_model( config_pathconfigs/groundingdino_swint_oogc.py, weights_pathweights/groundingdino_swint_oogc.pth ) # 加载图片 image_source, image load_image(test.jpg) # 推理 boxes, logits, phrases predict( modelmodel, imageimage, captionperson. car. tree., box_threshold0.35, text_threshold0.25 )这里有两个核心参数box_threshold目标框的置信度阈值值越低检测到的目标越多但误检也会增加。我一般先用 0.35如果发现漏检就降一点如果发现误检多就升一点。text_threshold文本匹配的置信度阈值只有超过这个值文本与视觉特征的匹配才会被保留。默认 0.25 通常够用但如果文本提示词比较复杂可以适当调高。predict函数返回三个东西boxes每个目标的归一化坐标格式是 [x1, y1, x2, y2]范围是 0 到 1logits目标框的置信度越大越可靠phrases匹配到的文本提示词对应每个框属于哪个类别搭好推理流程后可以顺手做一个可视化脚本把检测结果画出来看import cv2 for box, phrase in zip(boxes, phrases): x1, y1, x2, y2 box * [image_source.shape[1], image_source.shape[0], image_source.shape[1], image_source.shape[0]] cv2.rectangle(image_source, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2) cv2.putText(image_source, phrase, (int(x1), int(y1) - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2) cv2.imwrite(result.jpg, image_source)2.2 文本提示词设计经验很多人忽略了提示词对 Grounding DINO 的影响。OpenCLIP 的文本编码器对提示词非常敏感这点我实验过很多次。提示词设计有几个关键经验用英文表达不要用中文。虽然 Grounding DINO 的文本分支用的是多语言模型但实测英文提示的效果比中文稳定得多。类别之间用句号隔开不要用逗号也不要一口气写完。比如person. car. tree.的效果比person, car, tree好。提示词不要太长。默认max_tokens256如果提示词超过 256 个 token后面的内容会被截断容易导致漏检。类别名尽量用常见词汇。检测“椅子”写chair不要写seating furniture后者会让模型困惑。我记得有一次做工业场景检测我需要检测传送带上的“破损纸箱”一开始提示词写的broken cardboard box效果很差很多完好的箱子也被框出来。后来我改成cardboard box.把“破损”这种修饰词去掉只保留物体本名效果立刻好了很多。“破损”和“完好”是属性不是类别Grounding DINO 擅长检测物体类别而不是判断属性。真正判断属性需要靠细粒度分类模型或者后续加一层分类网络。2.3 打破搜索瓶颈文本提示词越简单越好结合前面说的这里想再强调一次Grounding DINO 的本质是“用文本引导视觉定位”不是“用文本来理解场景”。你给它red apple.它大概率能框出苹果但区分红苹果和青苹果它做不到。你在做提示词设计的时候应该先想清楚你要检测的是“名词”而不是“形容词名词”。如果需要区分同一物体的不同状态还是老老实实把状态微调成一个新的类别或者用分类模型补一刀。我做一个光伏组件缺陷检测项目时最初试图用cracked solar panel.去检测有裂纹的组件效果一塌糊涂裂纹附近的框非常不稳定。后来我改成检测solar panel.然后接一个 ResNet 分类器去判断面板是否损坏整个系统才稳下来。这条经验估计能帮你省下至少一周的调试时间。3. 训练从零开始微调的完整流程3.1 数据准备COCO 格式与标注转换Grounding DINO 的训练数据格式是 COCO 格式也就是一个 JSON 标注文件 一组图片。这个格式是目标检测领域最通用的格式之一很多标注工具如 labelme、x-anylabeling都支持导出成 COCO 或类似格式。COCO 格式标注的核心字段如下{ images: [ { id: 1, file_name: img_001.jpg, width: 1280, height: 720 } ], annotations: [ { id: 1, image_id: 1, category_id: 0, bbox: [100, 50, 200, 300], area: 60000, iscrowd: 0 } ], categories: [ { id: 0, name: person } ] }这里有几个容易出错的点bbox的格式是 [x, y, width, height]不是 [x1, y1, x2, y2]category_id不需要连续但需要和categories里的 id 对应iscrowd必须为 0否则训练时会跳过该目标图片宽高必须和实际图片尺寸一致宽高写错会导致 scale augmentation 出问题我自己写过一个从 labelme 到 COCO 的转换脚本核心逻辑是import json import os from labelme.utils import shape_to_mask import numpy as np def labelme_to_coco(img_dir, json_dir, output_json): images [] annotations [] annotation_id 1 categories [{id: 0, name: person}] cat_id_map {person: 0} for img_name in os.listdir(img_dir): img_id len(images) 1 height, width cv2.imread(os.path.join(img_dir, img_name)).shape[:2] images.append({ id: img_id, file_name: img_name, width: width, height: height }) json_path os.path.join(json_dir, img_name.replace(.jpg, .json)) with open(json_path, r, encodingutf-8) as f: labelme_data json.load(f) for shape in labelme_data[shapes]: points np.array(shape[points], dtypenp.float32) x1, y1 np.min(points, axis0) x2, y2 np.max(points, axis0) x, y, w, h x1, y1, x2 - x1, y2 - y1 annotations.append({ id: annotation_id, image_id: img_id, category_id: cat_id_map[shape[label]], bbox: [x, y, w, h], area: w * h, iscrowd: 0 }) annotation_id 1 with open(output_json, w, encodingutf-8) as f: json.dump({images: images, annotations: annotations, categories: categories}, f, ensure_asciiFalse)这个脚本虽然简陋但跑通小数据集已经够用了。如果你的数据是用其他工具标注导出的只要最终是标准 COCO 格式Grounding DINO 都能直接吃进去。3.2 配置文件解读与关键修改Grounding DINO 的训练配置分为两部分模型结构配置groundingdino_swint_oogc.py和训练配置GroundingDINO_SwinT_OGC.py。模型结构配置里的参数比较固定训练阶段基本不用动。真正需要改的是训练配置打开来主要看这几个参数# 数据相关 TRAIN_DATASETS [coco_train_grounding] EVAL_DATASETS [[coco_val_grounding]] # batch size 与优化器 SOLVER dict( AMPTrue, OPTIMIZER_NAMEAdamW, BATCH_SIZE8, BASE_LR0.0001, WEIGHT_DECAY0.05, CLIP_GRADIENTSdict(ENABLEDTrue, CLIP_TYPEnorm, CLIP_VALUE1.0), MAX_ITER100000, CHECKPOINT_PERIOD5000, )这些参数怎么调其实很有讲究BASE_LR微调时建议用1e-5到5e-5不要再高。Grounding DINO 的预训练权重已经很强了学习率太大容易把文本对齐能力冲掉导致模型“忘掉”开放集泛化能力。BATCH_SIZE每卡 batch size × 卡数。8GB 显存开 116GB 开 2-424GB 开 4-8。如果 batch size 开得小需要配合GRADIENT_ACCUMULATION模拟更大的 batch否则 BN 统计会不稳定。MAX_ITER注意这里不是 epoch 而是 iteration取决于数据集大小。一般小数据集 5000-20000 迭代就够了我做过一次 300 张图的微调5500 迭代效果就很好。3.3 训练启动与监控训练启动的命令是python train.py \ --config_file configs/GroundingDINO_SwinT_OGC.py \ --output_dir logs/groundingdino_tune \ --opts MODEL.WEIGHTS weights/groundingdino_swint_oogc.pth如果你的显存不够可以在命令行里追加覆盖python train.py \ --config_file configs/GroundingDINO_SwinT_OGC.py \ --output_dir logs/groundingdino_tune \ --opts MODEL.WEIGHTS weights/groundingdino_swint_oogc.pth \ SOLVER.BATCH_SIZE 2 \ SOLVER.AMP True训练过程中要重点看几个指标Loss_Total总损失初期在 2-3 附近很正常慢慢应该降到 1 以下Loss_GIoU边界框 GIoU 损失这个值越快下降说明定位学得越快Loss_L1L1 回归损失同样应该缓慢下降iter_time单次迭代耗时如果超过 1s说明 batch 可能开大了我习惯用 log 里每 500 迭代打一次的信息做简单监控。如果你用多卡分布式训练可以用 tensboardpip install tensorboard tensorboard --logdir logs/然后在浏览器里看 loss 曲线。看到Loss_Total持续波动不下去十有八九是学习率太大或者数据标注有问题先停下来查数据不要干等。4. 训练中的关键组件理解与超参调优4.1 损失函数的组成与微调重点Grounding DINO 的训练损失主要由三部分组成分类损失可类比为“法向量分类”计算文本-区域匹配的质量L1 损失直接回归 bbox 的 [x, y, width, height] 与真实框的绝对误差GIoU 损失通过对两个框之间的交集与最小闭包区域求关系评估框的重叠程度训练时这三部分的比例不是写死的在配置文件里有LOSS_WEIGHT可以调整MODEL dict( LOSS_WEIGHTdict( LOSS_CLASS1.0, LOSS_BOX5.0, LOSS_GIou2.0, ) )几个调优经验如果调的是纯定位问题对已知目标类别进行更准的检测可以把LOSS_CLASS稍微调低比如 0.5把LOSS_BOX调高到 5-8让模型更关注框的位置和大小如果调的是新类别模型没见过需要学新概念LOSS_CLASS要保持 1.0 以上否则文本对齐能力学不好GIoU 损失不要调太高否则模型会为了“框得准”而牺牲分类置信度最终检测精确率下降4.2 冻结策略与分阶段微调微调 Grounding DINO 最常用的技巧是分阶段解冻第一阶段只解冻交叉注意力层其他层全部冻结。这样模型会把更多精力放在“理解文本和视觉的对应关系”上而不是从头学特征提取。这一步迭代数不需要太多1000-2000 就够。第二阶段解冻骨干网络的最后两层让模型适应新的数据分布。这一步是收益最大的因为你的数据如果和预训练数据的分布差异很大比如从自然场景换到工业场景微调骨干会让模型显著加速收敛。第三阶段可选全部解冻用很小学习率微调 3000-5000 迭代精修整个模型。如何实现分段微调在代码里通过过滤参数名来冻结对应层def freeze_module(model, module_name): for name, param in model.named_parameters(): if module_name in name: param.requires_grad False比如冻结文本分支freeze_module(model, language_encoder)只让视觉分支和交叉注意力层可训练for name, param in model.named_parameters(): if backbone in name or transformer in name: param.requires_grad True else: param.requires_grad False注意了冻结之后 optimizer 里要过滤掉requires_gradFalse的参数否则 optimizer 会对冻结参数计算梯度虽然不会更新但浪费显存trainable_params [p for p in model.parameters() if p.requires_grad] optimizer torch.optim.AdamW(trainable_params, lr1e-5)4.3 蒸馏实战大模型带小模型如果你要微调一个更大的模型像 Swin-B 甚至 Swin-L可以直接训练但如果最终目标是部署到边缘设备我强烈建议你用蒸馏方案用大模型当 teacher用小的 Swin-T 当 student把大模型的知识蒸馏给小模型。蒸馏的核心是把 teacher 输出的检测结果当 soft label。Grounding DINO 是开放集检测teacher 的文本-视觉匹配分数可以看成一种软标签student 学着贴近 teacher 的分数分布而不仅仅是二元“目标/非目标”。蒸馏损失定义大致如下def distillation_loss(student_logits, teacher_logits, temperature3.0): # 将 logits 除以温度再进行 KL 散度对齐 soft_student F.log_softmax(student_logits / temperature, dim-1) soft_teacher F.log_softmax(teacher_logits / temperature, dim-1) return F.kl_div(soft_student, soft_teacher, reductionbatchmean) * temperature ** 2温度的作用是“软化”teacher 的输出分布。温度越高分布越平滑student 学到的信息越多。但如果温度太高噪声也会被放大我推荐 3 到 5 之间。做蒸馏时 teacher 的输出不需要反传梯度可以先用torch.no_grad()把它固定住这样反而能省显存。实际试验中同样的 Swin-T 结构有蒸馏比没蒸馏在复杂场景上的 mAP 大概能提升 2-3 个百分点。值不值得折腾取决于你的精度要求。5. 常见问题与排查实录5.1 显存不够怎么办这是一定会遇到的。24GB 显存看着不小但 Grounding DINO 训练起来照样可能爆显存。我的处理顺序是先度小 batch size配合梯度累积SOLVER.BATCH_SIZE 2 \ SOLVER.GRADIENT_ACCUMULATION 4这样等效 batch size 还是 8但单次前向只算 2 张图。再开混合精度SOLVER.AMP True混合精度能把显存占用降低一半左右而且现代 GPU 上由于 Tensor Core 加速速度不降反升我是强烈建议训练阶段就开 AMP 的。如果还爆就降低图片分辨率。Grounding DINO 的默认输入分辨率是 800×500 左右实际可以降到 640×480检测精度损失在可接受范围。千万不要用缩小输入分辨率的方式硬塞测试这会改变数据的宽高比对 Transformer 的位置编码影响很大。要改就改配置文件里的INPUT.MIN_SIZE_TRAIN和MAX_SIZE_TRAIN让数据增强逻辑一起跟着变。5.2 检测不到目标这是最容易让人头大的问题。检测不到先不要怀疑模型和代码按顺序排查第一步检查图片加载。我见过至少三次load_image读进来的是灰度图或者转成了 0-1 的 float 数组但模型期望的是 0-255 的 BGR 图像。如果你自己写数据接口注意保持通道顺序一致。第二步检查提示词格式。captionperson, car. tree.这种混用标点的写法最容易出问题。全部用句号隔开。第三步降低置信度阈值。默认box_threshold0.25如果目标很小或者特征不明显直接降到 0.2 试试。但要注意阈值越低误检越多输出结果里可能有一堆垃圾框。第四步换一张更简单的图测试。如果简单的图能检测出来复杂图不行那不是模型问题是数据分布问题如果简单的图也检测不出来那就要检查权重和配置文件了。5.3 训练不收敛训练不收敛表现形式是 loss 一直在一个区间震荡怎么都不下降。根据我带的几个项目经验最常见的原因是学习率过大。微调阶段1e-4已经很大了降到1e-5试试。数据处理问题。比如 bbox 坐标越界、宽高为 0、类别标签和 categories 不匹配都会让 loss 卡住下不去。同一张图被用了两种数据增强方式导致某些 batch 的输入混乱。我的排查方法很简单先用别人训练好的权重和官方配置跑一次确认代码没问题然后用官方权重做 init在随机 100 张图上做一轮短训练如果 100 张图的简单任务都不收敛那就是配置或者数据处理的问题。6. 推理优化与部署效能6.1 半精度与批量推理推理阶段如果只是单张图跑速度无所谓。但如果是做批量推理比如对一批图片做检测就需要注意效率。先用 FP16 跑推理可以把显存占用减半速度提升 30%-50%model model.half().cuda() image image.half().cuda()然后开 batch inference。Grounding DINO 的输入是(B, C, H, W)的四维张量多张图按同一长宽比缩放到同样尺寸后可以拼成一个 batch 一起跑def preprocess_for_batch(image_paths, size(800, 500)): tensors [] for path in image_paths: _, image load_image(path) image F.interpolate(image.unsqueeze(0), sizesize, modebilinear).squeeze(0) tensors.append(image) return torch.stack(tensors)batch size 先设 8然后根据显存波动调整。8GB 显存建议 batch size 2-316GB 建议 4-8。6.2 TensorRT 加速如果需要更极致的推理性能可以考虑 TensorRT 方案。TensorRT 的优化核心是多层融合、算子调度和低精度量化通常能把 Transformer 类的模型推理速度提升 2-3 倍。基本流程是把 PyTorch 模型导出为 ONNX用 TensorRT 的 ONNX parser 转换出 engine在 Python 里加载 engine 做推理Grounding DINO 导出 ONNX 时最麻烦的一点是它模型内部有一些动态尺寸的算子比如集合丢失或 shape 重排这些在导出时容易报错。我当时的做法是固定输入分辨率把动态尺寸变成静态尺寸虽然灵活性降低但导出非常干净。如果你打算部署到 CPU 设备或者边缘设备建议优先把目标聚焦到 Swin-T 模型而不是 Swin-BSwin-B 在边缘设备上即使做了 TensorRT 也很难跑流畅。6.3 部署经验小结部署 Grounding DINO 时有几个实用性的经验权重文件不要打包在 docker 镜像里每次更新模型就要重建镜像很烦。建议挂载外部存储。提示词做配置化别写死在代码里。运营人员可能随时要调整检测类别每次改代码不是长久之计。推理结果尽量保留原始置信度不要只输出 0/1 标签。下游业务系统可以根据置信度做二次过滤给业务留弹性空间。把检测框的坐标从相对坐标转成绝对坐标或者在保存结果时保存归一化的相对坐标避免不同分辨率下定位偏差这一点很容易被忽略。7. 项目复盘一份完整经验清单最后从项目管理的角度做一个复盘因为每次做 Grounding DINO 项目我发现最后的瓶颈往往不是模型本身而是流程安排和数据质量。第一个关键决策是工具链的标准化。如果团队内部有人用 labelme、有人用 x-anylabeling、有人直接用 Python 脚本画框光格式转换就能消耗好几天。建议在项目启动时立刻固定标注工具统一导出 COCO 格式并且在第一天就写好数据校验脚本检查每一张图的 bbox 是否越界、宽高是否为正、类别是否有缺失。第二个决策是训练和推理分开走。不要等训练全部跑完才部署推理而是第一天就做好推理脚本用预训练权重跑通后续训练出的模型只需要替换权重路径。这样能并行推进模型迭代一版测一版。第三个决策是预留模型评估阶段。微调了几轮光看看 loss 曲线是不够的要在验证集上算 mAP 或 mAP50还要挑出一批典型图片人工查看检测效果。搞目标检测这行的资深经验是指标只反映整体水平人工看检测结果才能发现“模型把所有椅子都圈进去但漏了人”这种离散但致命的错误。第四个决策是把数据扩充做好再训模型。我见过很多同学拿几百张图直接开训效果不理想就开始调参调学习率其实问题是过拟合数据增强不够、图片太单一模型根本没有见过足够的变化。先把数据扩充这一环做扎实往往是省时省力的方法。最后再分享一条实操心得Grounding DINO 微调时不要过度追求“把所有类别都学到了”。它本质是个开放集模型天生擅长“泛化”擅长“把没见过的类别通过文本引导也框出来”。如果你有一个非常具体的类别且样本足够多把该类别的数据单独训一个专门的检测器精度上限通常会比 Grounding DINO 微调更高。所以我的建议是Grounding DINO 当“泛化利器”用处理牛毛一样多的动态类别当一个品类非常固定且需要高精度时可以把它拔出来的候选框交给小分类模型去兜底。这种“开放集检测 特定分类器”的组合是我在半数项目里的最终落地方案。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑