微调LocateAnything-3B 实现超高密度的目标检测

在线体验各类最新模型,更有模型 免费Token 额度领取!
立即体验
简介: 本文介绍如何微调NVIDIA LocateAnything-3B模型,应对300+密集重叠种子的精准定位难题。依托并行框解码(PBD)与半监督Pipeline(点标注→SAM2转框→YOLO伪标→定向微调),大幅降低人工成本,实现高精度、可落地的密集目标检测方案。

微调LocateAnything-3B,实现当图像中有 300+ 个密集重叠目标、人工标注不可行时的实用方案。

假设手头有一批种子发芽托盘、谷物质检图像或植物学调查照片。每张图像包含 100–500+ 粒种子,许多彼此重叠,部分被遮挡。老板(或导师)要求模型能精确定位每一粒。

在每张图像里手动为 300 个互相重叠的目标画紧密边界框,人工标注是根本不可行。按每个框约 5 秒计算,一张图就要花 25 分钟;1000 张图下来,标注工时超过 400 小时。

本文介绍如何借助 NVIDIA 的 LocateAnything-3B——一个支持 Parallel Box Decoding(并行框解码)的视觉语言定位模型——以及一套半监督 Pipeline,将人工标注量压缩到最低。

为什么选择 LocateAnything-3B?

大多数视觉语言定位模型(如 Grounding DINO 或 OWL-ViT)采用自回归方式逐 Token 生成边界框,检测 300 个目标的速度极慢。

LocateAnything-3B 采用 Parallel Box Decoding(PBD),以并行块预测的方式一次性输出完整边界框,而非逐 Token 生成,特别适合包含数百目标的密集场景。

核心参数:

  • 基础模型:Qwen2.5–3B-Instruct 语言模型 + MoonViT 视觉编码器
  • 解码方式:并行块级预测(非自回归)
  • 许可证:NVIDIA Open Model License

总体策略

整个方案分四个阶段,采用半监督 Pipeline:

  1. 最少量的点标注(非框标注)+ SAM2 生成初始边界框
  2. 在合成数据与伪标签数据上训练轻量级检测器
  3. 对全量未标注数据集生成伪标签
  4. 使用高置信度伪标签配合密集场景专用训练策略微调 LocateAnything-3B

阶段 1:最少量标注 + 点转框

点击目标中心点的速度比画边界框快约 10 倍。标注 300 粒种子,打点只需约 5 分钟,画框则需约 25 分钟。

具体做法:用 SAM 2(Segment Anything Model 2)将每个点扩展为分割掩码(segmentation mask),再从掩码推导出边界框。

依赖安装:

 pip install sam2 torch torchvision transformers opencv-python

点转框 Pipeline:

 import cv2
import numpy as np
import torch
from sam2.build_sam import build_sam2
from sam2.sam2_image_predictor import SAM2ImagePredictor
from PIL import Image

# 加载 SAM2
checkpoint = "./checkpoints/sam2.1_hiera_large.pt"
model_cfg = "configs/sam2.1/sam2.1_hiera_l.yaml"
predictor = SAM2ImagePredictor(build_sam2(model_cfg, checkpoint))

def points_to_boxes(image_path, seed_points):
    """
    Args:
        image_path: 原始种子图像路径
        seed_points: (x, y) 元组列表——每粒种子中心点击一次
    Returns:
        boxes: [x1, y1, x2, y2] 边界框列表
    """
    image = np.array(Image.open(image_path).convert("RGB"))
    predictor.set_image(image)

    # 将点转换为 numpy 数组
    point_coords = np.array(seed_points, dtype=np.float32)
    point_labels = np.ones(len(seed_points), dtype=np.int32)  # 1 = 前景

    # 批量预测(SAM2 支持多点输入)
    masks, scores, logits = predictor.predict(
        point_coords=point_coords,
        point_labels=point_labels,
        multimask_output=False,  # 每个点输出单一掩码
    )

    # 将掩码转换为边界框
    boxes = []
    for mask in masks:
        # mask 形状:(H, W)
        ys, xs = np.where(mask > 0)
        if len(xs) == 0:
            continue
        x1, y1, x2, y2 = int(xs.min()), int(ys.min()), int(xs.max()), int(ys.max())
        boxes.append([x1, y1, x2, y2])

    return boxes, masks

# 使用示例
seed_points = [
    (120, 340), (145, 342), (180, 338),  # ... 共 300 个点
]

 boxes, masks = points_to_boxes("tray_001.jpg", seed_points)

SAM2 擅长处理边界分离。即使种子相互重叠,点击可见前景种子上的点,通常也能正确分割出该前景目标。若两粒种子严重粘连:

  • 点击最上层种子的可见中心
  • SAM2 会优先分割包含该点的目标
  • 对于被遮挡的种子,点击其可见的边缘或角点

若 SAM2 将两粒相邻种子合并为一个掩码,可将点位下移后重试,或在提取边界框前对掩码进行 watershed 分割后处理。

阶段 2:构建初始检测器

在接触 LocateAnything-3B 之前,先基于 SAM2 生成的边界框训练一个快速的 YOLO11 或 RT-DETR 模型。训练完成后可以得到一个专针对种子的检测器,运行速度达 100+ FPS,适合对数千张图像批量生成伪标签。

 from ultralytics import YOLO

# 在 SAM2 伪标签上训练 YOLO11
model = YOLO("yolo11n.pt")  # 使用 Nano 版以提升速度,可按需换用 s/m/l

# 数据格式:YOLO 需要 .txt 文件,格式为 类别 x_center y_center width height(归一化)
# 转换边界框:
def box_to_yolo_line(box, img_w, img_h):
    x1, y1, x2, y2 = box
    x_c = ((x1 + x2) / 2) / img_w
    y_c = ((y1 + y2) / 2) / img_h
    w = (x2 - x1) / img_w
    h = (y2 - y1) / img_h
    return f"0 {x_c:.6f} {y_c:.6f} {w:.6f} {h:.6f}"

# 训练
model.train(
    data="seed_pseudo_data.yaml",
    epochs=50,
    imgsz=640,
    batch=16,
    patience=10,
    augment=True,
    hsv_h=0.015, hsv_s=0.7, hsv_v=0.4,
    degrees=15, translate=0.1, scale=0.5,
    fliplr=0.5, mosaic=1.0, mixup=0.1,
 )

为什么要设计这个中间步骤?YOLO11 训练只需几分钟,每个预测结果都带有置信度分数,可按置信度过滤伪标签,也能过滤掉 SAM2 产生的明显误检(如背景杂点)。

阶段 3:大规模生成伪标签

将 YOLO11 种子检测器应用于全部未标注图像,保留高置信度预测,过滤噪声。

 import glob
import json
from ultralytics import YOLO

model = YOLO("runs/detect/seed_yolo11/weights/best.pt")
image_paths = glob.glob("raw_images/*.jpg")

pseudo_labels = []
CONF_THRESHOLD = 0.65  # 可调整——从 0.6 开始,若噪声过多可提高至 0.75
IOU_NMS_THRESHOLD = 0.5

for img_path in image_paths:
    results = model(img_path, conf=CONF_THRESHOLD, iou=IOU_NMS_THRESHOLD)

    boxes = results[0].boxes.xyxy.cpu().numpy().tolist()
    confs = results[0].boxes.conf.cpu().numpy().tolist()

    # 可选:跳过模型置信度较低的图像(平均置信度偏低)
    if len(confs) > 0 and np.mean(confs) < 0.5:
        print(f"标记为待人工复核:{img_path}")
        continue

    # 以 LocateAnything 对话格式存储
    pseudo_labels.append({
        "id": img_path.split("/")[-1].replace(".jpg", ""),
        "image": img_path,
        "conversations": [
            {
                "from": "human",
                "value": "<image>\nLocate all seeds in this image."
            },
            {
                "from": "gpt",
                "value": format_boxes_for_locateany(boxes)
            }
        ],
        "meta": {"source": "yolo_pseudo", "mean_conf": float(np.mean(confs))}
    })

def format_boxes_for_locateany(boxes):
    """将 xyxy 格式边界框转换为模型所需的特殊 Token 格式。"""
    # LocateAnything 要求坐标归一化至 [0, 999] 并使用特殊 Token 包裹
    # 具体 Token 化方式取决于 processor;此处展示逻辑结构
    lines = []
    for box in boxes:
        x1, y1, x2, y2 = box
        lines.append(f"<<box>>{int(x1)} {int(y1)} {int(x2)} {int(y2)}<<</box>>")
    return "\n".join(lines)

# 保存 recipe
with open("data/pseudo_labels.jsonl", "w") as f:
    for item in pseudo_labels:
         f.write(json.dumps(item) + "\n")

基于置信度的过滤策略:

在密集场景中,标准 NMS(Non-Maximum Suppression,非极大值抑制)可能过度抑制相互重叠的种子。替代方案如下:

 # 尺寸感知过滤:若两个框尺寸相近,则保留重叠框
# (种子大小大致均匀;若大框与小框重叠,大概率是误检)
def size_aware_filter(boxes, confs, size_tol=2.0):
    areas = [(b[2]-b[0])*(b[3]-b[1]) for b in boxes]
    median_area = np.median(areas)

    filtered = []
    for box, conf, area in zip(boxes, confs, areas):
        if area > median_area * size_tol or area < median_area / size_tol:
            continue  # 可能是误检(杂质、背景团块)
        filtered.append((box, conf))
     return filtered

阶段 4:Fine-tune LocateAnything-3B

伪标签数据已准备就绪,开始对主模型进行 Fine-tuning。

LocateAnything 使用 recipe JSON 定义数据混合比例。针对密集种子场景,有意提高密集/重叠场景的数据权重:

 {
  "datasets": [
    {
      "name": "seed_pseudo_dense",
      "path": "data/pseudo_labels.jsonl",
      "root": "data/raw_images/",
      "repeat": 3,
      "length": 15000
    },
    {
      "name": "seed_sam2_verified",
      "path": "data/human_verified_sam2.jsonl",
      "root": "data/raw_images/",
      "repeat": 8,
      "length": 2000
    },
    {
      "name": "seed_counting_captions",
      "path": "data/weak_count_captions.jsonl",
      "root": "data/raw_images/",
      "repeat": 2,
      "length": 5000
    }
  ]
 }

注意:人工核验数据的 repeat 设为 8。数量虽少,但质量更高。提高重复比例可防止模型仅学习 YOLO 的偏差。

训练脚本:

 # 脚本位置:eaglevl/train/locany_finetune_magi_stream.py

torchrun --nproc_per_node=4 \
  eaglevl/train/locany_finetune_magi_stream.py \
  --model_name_or_path nvidia/LocateAnything-3B \
  --meta_path "./recipes/seed_finetune_recipe.json" \
  --output_dir work_dirs/locany_seed_sft \
  --max_steps 10000 \
  --learning_rate 1e-5 \
  --warmup_ratio 0.1 \
  --lr_scheduler_type cosine \
  --bf16 True \
  --block_size 6 \
  --attn_implementation magi \
  --max_seq_length 8192 \
  --per_device_train_batch_size 1 \
  --gradient_accumulation_steps 8 \
  --save_steps 2000 \
  --logging_steps 10 \
  --report_to tensorboard \
   --deepspeed "deepspeed_configs/zero_stage2_config.json"

LoRA 替代方案(显存受限时):没有 4 张 A100 的话,可以走 LoRA。LocateAnything 支持

use_backbone_lora

use_llm_lora

 from transformers import AutoModel, AutoConfig

config = AutoConfig.from_pretrained(
    "nvidia/LocateAnything-3B",
    trust_remote_code=True
)

# 启用 LoRA
config.use_llm_lora = 64      # 语言模型的秩
config.use_backbone_lora = 64 # 视觉骨干网络的秩

model = AutoModel.from_pretrained(
    "nvidia/LocateAnything-3B",
    config=config,
    trust_remote_code=True,
    torch_dtype=torch.bfloat16
 )

LoRA 将可训练参数从约 38 亿压缩到约 2 亿,在单张 A100 或 2 张 RTX 4090 上即可完成 Fine-tuning。

进阶:弱监督计数辅助定位

如果连点标注都无法提供,只有图像级别的计数(例如"此托盘约有 320 粒种子"),可以通过以下方式引导定位:

 # 基于计数注意力的弱监督定位
import torch.nn.functional as F

def attention_peaks_to_points(model, processor, image_path, prompt, n_peaks=300):
    """
    利用模型中文本('seeds')与图像 patch 之间的交叉注意力,
    估计种子中心的可能位置。需要对模型进行前向 hook。
    """
    image = Image.open(image_path).convert("RGB")
    inputs = processor(text=prompt, images=image, return_tensors="pt").to("cuda")

    # 开启注意力输出的前向传播(如模型支持)
    with torch.no_grad():
        outputs = model(**inputs, output_attentions=True)

    # 提取视觉-语言注意力(层次相关,通常取最后几层)
    attn = outputs.attentions[-1]  # [batch, heads, text_tokens, image_patches]

    # 对多头取均值,并对种子相关的文本 Token 求和
    seed_token_indices = find_seed_tokens(inputs.input_ids, processor)
    seed_attn = attn[:, :, seed_token_indices, :].mean(dim=2)  # [batch, heads, patches]

    # 重塑为空间网格(MoonViT 使用合并后的 patch,448px 下约为 24x24)
    H = W = 24
    attn_map = seed_attn.mean(dim=1).reshape(H, W).cpu().numpy()

    # 寻找局部极大值
    from scipy.ndimage import maximum_filter
    local_max = (attn_map == maximum_filter(attn_map, size=3))
    peaks = np.argwhere(local_max)

    # 将 patch 索引转换为图像坐标
    img_w, img_h = image.size
    points = [(p[1] * img_w / W, p[0] * img_h / H) for p in peaks[:n_peaks]]

     return points

这些注意力极值点将作为伪点输入阶段 1 的 SAM2。不过这个结果存在一定噪声,但配合 NMS 和置信度过滤,足以为整个 Pipeline 提供初始数据(理论)。

使用 Fine-tune 后的模型进行推理

 from transformers import AutoModel, AutoProcessor
import torch

model = AutoModel.from_pretrained(
    "work_dirs/locany_seed_sft/checkpoint-10000",
    trust_remote_code=True,
    torch_dtype=torch.bfloat16
).cuda()

processor = AutoProcessor.from_pretrained(
    "nvidia/LocateAnything-3B",
    trust_remote_code=True
)

# 加载密集种子图像
image = "germination_tray_A7.jpg"

# Prompt 措辞很重要——明确说明对重叠情况的预期
prompts = [
    "Locate every single seed in this image, including overlapping ones.",
    "Detect all seeds. Do not miss any, even if they are partially covered.",
    "Count and bound each individual seed. Return all bounding boxes.",
]

best_result = None
max_boxes = 0

for prompt in prompts:
    inputs = processor(text=prompt, images=image, return_tensors="pt").to("cuda")

    with torch.no_grad():
        outputs = model.generate(
            **inputs,
            max_new_tokens=1024,  # 允许输出大量边界框
            do_sample=False
        )

    result = processor.batch_decode(outputs, skip_special_tokens=False)[0]
    n_boxes = result.count("<<box>>")

    if n_boxes > max_boxes:
        max_boxes = n_boxes
        best_result = result

print(f"检测到 {max_boxes} 粒种子")
 print(best_result)

Ensemble Prompting

对于关键应用场景(如种子质检),可用多个 Prompt 分别推理,再通过 NMS 合并结果:

 from collections import defaultdict

def merge_prompt_results(results_list, iou_thresh=0.5):
    """
    不同 Prompt 会发现不同的种子,合并它们!
    """
    all_boxes = []
    for res in results_list:
        all_boxes.extend(parse_boxes(res))  # 自行实现解析器

    # 标准 NMS 或 soft-NMS
     return weighted_boxes_fusion(all_boxes, iou_thresh)

不同 Prompt 的召回范围往往不完全重叠,融合后通常能找到更多目标。

参考资源

  • 模型:nvidia/LocateAnything-3B
  • 论文:LocateAnything: Fast and High-Quality Vision-Language Grounding with Parallel Box Decoding
  • 代码:NVlabs/Eagle/Embodied
  • SAM2:facebook/sam2.1
  • 标注工具:CVAT(支持点标注),Label Studio

总结

密集重叠场景下的目标检测,不需要消耗数百小时手工标注。点监督、SAM2 自动分割、轻量级伪标签模型、LocateAnything-3B 的并行解码——四者结合,可以构建出生产级种子检测器,与传统框标注流程相比,标注时间减少约 90%。

让廉价模型和几何方法承担繁重工作,人力精力留给验证和边界案例。

https://avoid.overfit.cn/post/d5eae73f1cfa42688a4d3e970494702e

by Yash M Gupta, PhD

目录
相关文章
|
1月前
|
人工智能 Oracle 机器人
推理 → 行动 → 观察:用 LangChain + Python 实现一个智能体循环
智能体循环(Agentic Loop)突破单次问答局限,通过“推理→行动→观察”迭代闭环,让AI能自主分解任务、调用工具、持续优化直至目标完成,是构建真正自动化智能体的核心架构。
310 9
推理 → 行动 → 观察:用 LangChain + Python 实现一个智能体循环
|
20天前
|
监控 中间件 API
【剪映小助手】音频时间线计算接口(Audio Timelines)
音频时间线计算接口用于草稿自动化中音视频时序分析,依赖requests、subprocess、Pydantic等模块,支持并发处理、流式下载与断点续传。含参数校验、错误重试及自动清理机制,OpenAPI为准。
146 12
|
21天前
|
人工智能 缓存 监控
协议兼容新方案:CC Switch本地路由实现Codex CLI接入DeepSeek全流程
在命令行AI编程场景中,Codex CLI凭借高效的代码生成、脚本编写与工程辅助能力,成为开发者轻量化开发的核心工具。但Codex CLI原生仅兼容OpenAI Responses API协议,无法直接对接DeepSeek等采用Chat Completions API的第三方模型,直接修改配置会引发404报错、参数解析失败、流式输出中断等问题。CC Switch本地路由工具通过轻量化本地代理与双向协议转换,无需修改Codex CLI源码,即可实现无感接入DeepSeek等第三方模型,彻底解决协议不兼容痛点,大幅拓展Codex CLI的模型生态与使用场景。
514 4
|
21天前
|
人工智能 IDE 开发工具
阿里云Qoder CN AI编程工具安装指南:IDE、JetBrains、VS Code插件部署方法
阿里云Qoder CN是面向开发者的AI智能编码助手,支持开箱即用的独立IDE或VS Code/JetBrains插件两种安装方式,登录即用。官网:https://t.aliyun.com/U/fEiOLV
618 0
|
20天前
|
机器学习/深度学习 安全 算法
水下目标检测数据集:11类海洋渔具及垃圾 | 目标检测
本数据集含2500张真实水下图像,涵盖11类废弃渔具(如鱼笼、绳索、渔网)与海洋垃圾(轮胎、木材等),支持YOLO等目标检测模型训练。专为应对水下光学退化、目标遮挡、尺度多变等挑战构建,标注精细、结构规范、开箱即用,助力ROV/AUV智能巡检与海洋生态治理。(239字)
128 10
|
21天前
|
人工智能 IDE API
阿里云Qoder完全使用指南:从安装配置到Agentic编码实战
本文系统介绍阿里云Qoder这一Agentic编码平台的完整使用方法。Qoder是面向软件开发的AI智能编码助手,支持桌面IDE、命令行CLI和JetBrains插件三种接入方式,可通过按量付费、Coding Plan或Token Plan团队版接入阿里云百炼大模型。文章详细讲解Qoder IDE的安装配置、模型接入凭证设置、Editor与Quest双模式切换、智能代码补全与问答交互、Cloud Agent的API调用流程,以及企业级知识库增强、MCP扩展、Skills技能等高级特性。同时分享Quest模式下的Spec驱动开发、网站搭建、原型探索三种场景实践,并给出亚信科技等企业的真实落地案例
|
21天前
|
人工智能 IDE 安全
Claude Code、Codex、Cursor、OpenCode Harness架构与执行逻辑深度对比
在AI编程工具的技术体系中,Harness是决定工具能力边界、执行效率与安全可控性的核心引擎,它是模型与开发环境之间的中间层,负责上下文管理、代码执行、权限管控、结果验证与迭代闭环。Claude Code、Codex、Cursor、OpenCode四款主流工具,因产品定位与设计理念不同,其Harness在架构、执行流程、上下文能力、权限模型、扩展机制与适用场景上存在显著差异。本文从Harness的核心定义出发,系统拆解四款工具的Harness架构、执行逻辑、关键特性与实战表现,帮助开发者理解不同工具的底层差异,选择适配自身开发流的AI编程工具。
351 0
|
21天前
|
存储 监控 安全
零基础上手阿里云百炼API:API-Key获取、配置与调用全攻略
阿里云百炼API-Key的获取与调用,核心在于“规范开通、正确创建、安全存储、精准配置”。从账号实名认证、服务开通,到密钥创建、参数配置,再到代码调用与问题排查,每一步都有明确的操作标准。遵循本文流程,可快速完成从0到1的API接入,避免新手常见踩坑。同时,严格遵守安全规范,做好密钥管理与额度监控,既能保障调用稳定性,又能降低安全风险。无论是个人开发测试,还是企业生产部署,掌握百炼API-Key全流程,都是高效使用大模型服务的基础,为后续智能体开发、自动化业务落地提供核心支撑。
362 0
|
21天前
|
人工智能 API C++
OpenCode vs Claude Code:一文看懂两者的核心区别与选型建议
本文深度解析2026年两大主流AI编程工具——闭源旗舰Claude Code与开源标杆OpenCode的本质差异:非模型之争,而是“极致体验”与“绝对自由”的产品哲学分野,助开发者精准选型。

热门文章

最新文章