游乐游手机版
首页/AI教程/文章详情

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

时间:2026-06-26 16:14
针对图像中密集、重叠目标的检测难题,提出了一种半监督微调方案。该方法基于LocateAnything-3B模型及其并行解码的高效特性,通过少量点标注转化为边界框,训练轻量检测器生成伪标签,最终微调主模型。该流程极大降低了人工标注成本,实现了对成百上千个目标的快速精准定位。

想象一下这样一个场景:你手头有一批种子发芽托盘、谷物质检图像,或者植物学调查的样本照片。每张图像里密密麻麻挤着100到500粒种子,它们互相重叠、彼此遮挡,而你的老板或导师要求模型能精准定位每一粒。这事儿听起来就让人头大,不是吗?

问题在于,要在每张图像里手动给三百个互相重叠的目标画精确的边界框,人工标注这条路根本走不通。算一笔账就清楚了——标注一个框大约5秒钟,处理一张图就要花掉25分钟;要是有一千张图,光是标注就得耗上超过400个小时,这显然不现实。

那么,有没有更聪明的解法?本文就来聊聊如何借助NVIDIA的LocateAnything-3B——一个支持并行框解码的视觉语言定位模型——搭配一套半监督流水线,把人工标注量压缩到最低。

为什么选择LocateAnything-3B?

市面上大多数视觉语言定位模型,像Grounding DINO或OWL-ViT,走的是自回归路线,一个Token一个Token地生成边界框。如果要检测300个目标,速度会慢到让人难以忍受。而LocateAnything-3B采用了一种叫并行框解码的技术,一次性并行输出完整的边界框,而不是逐Token生成。这就让它在处理成百上千个目标的密集场景时,效率优势非常明显。

它的核心构成如下:

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

整体策略:四步走

整个方案分为四个阶段,本质上是一条半监督流水线:

  • 先做最少量点标注,而非框标注
  • 用SAM2从点生成初始边界框
  • 在合成数据和伪标签数据上训练一个轻量检测器
  • 对全部未标注数据生成伪标签,再用高置信度的伪标签配合特定训练策略微调LocateAnything-3B

阶段一:最少量标注——点转框

点击目标的中心点,速度比画边界框快大约10倍。标注300粒种子,打点只需大概5分钟,而画框需要25分钟左右。这笔账怎么算都划算。

具体操作是这样的:用SAM 2(Segment Anything Model 2)把每个点击点扩展成分割掩码,再从掩码反推出边界框。

先安装依赖:

pip install sam2 torch torchvision transformers opencv-python

点转框的流水线实现如下:

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把两粒相邻种子合并成一个掩码,试着把点位稍微下移再试一次,或者在提取边界框前对掩码做一次分水岭分割后处理。

阶段二:构建初始检测器

在正式接触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产生的明显误检,比如背景里的杂点。

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

接下来,把训练好的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": "Locate 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"<>{int(x1)} {int(y1)} {int(x2)} {int(y2)}<<>")
    return "".join(lines)

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

在密集场景中,标准NMS(非极大值抑制)可能会过度抑制那些互相重叠的种子。这里有个替代方案:采用尺寸感知的过滤策略。如果两个框的尺寸相近,就保留重叠的框。因为种子的大小大致均匀,若一个大框和一个小框重叠,大概率是误检。

\

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

阶段四:微调LocateAnything-3B

伪标签数据准备就绪,接下来就是对主模型进行微调了。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 \
    --sa ve_steps 2000 \
    --logging_steps 10 \
    --report_to tensorboard \
    --deepspeed "deepspeed_configs/zero_stage2_config.json"

如果显存紧张,可以用LoRA替代。LocateAnything支持use_backbone_lorause_llm_lora参数。LoRA可以把可训练参数从大约38亿压缩到约2亿,这样一来,单张A100或两张RTX 4090上就能完成微调。

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
)

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

如果连点标注都无法提供,只有图像级别的计数(比如"这个托盘大约有320粒种子"),那该怎么办?可以通过以下方式引导定位:

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和置信度过滤,理论上足够为整个流水线提供初始数据。

使用微调后的模型进行推理

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("<>")
    if n_boxes > max_boxes:
        max_boxes = n_boxes
        best_result = result

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

集成提示:对于关键应用场景,比如种子质检,可以用多个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://developer.aliyun.com/article/1742641
上一篇阿里云OSS从零到一完全对接指南 开通配置SDK集成与成本优化 下一篇空间组学解析肿瘤异质性课前准备
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
Windows Docker Desktop RabbitMQ生产级部署完整指南
AI教程 · 2026-06-29

Windows Docker Desktop RabbitMQ生产级部署完整指南

前言 在 Windows 本地开发环境中,直接安装 RabbitMQ 确实颇为周折:需要单独配置 Erlang 运行环境、手动管理环境变量、服务启停全凭手工操作。更令人困扰的是,版本兼容冲突、端口占用、环境不一致等问题层出不穷。笔者见过不少开发者为搭建环境就得耗费整整半天时间。 相比之下,借助 Do

AI搜索重构制造业采购逻辑的阿里云企业级GEOCMS优化实践
AI教程 · 2026-06-29

AI搜索重构制造业采购逻辑的阿里云企业级GEOCMS优化实践

先分享一个切实感受。过去两年,我们与福建制造企业合作较为频繁,发现一个非常突出的现象:超过80%的企业官网,产品参数仍然存放在PDF或图片中。AI爬虫?根本无法抓取。这些企业技术实力不弱、资质证照齐全、应用案例也丰富,但在AI搜索这一全新战场上,它们几乎处于隐身状态。 一、一个正在发生的行业变化 A

阿里云Token Plan团队版功能价格与省钱购买指南
AI教程 · 2026-06-29

阿里云Token Plan团队版功能价格与省钱购买指南

阿里云百炼近期推出了名为“Token Plan 团队版”的全新服务,这一服务专为企业与开发者量身打造,定位为AI大模型订阅平台。通过引入Credits作为统一计量单位,将文本生成、图像生成等多模态AI能力纳入单一计费体系,同时无缝兼容主流AI编程工具及智能体(Agent)生态系统。其核心亮点包括:全

阿里云物联网.NET Core客户端位置信息上报
AI教程 · 2026-06-29

阿里云物联网.NET Core客户端位置信息上报

阿里云物联网平台的位置服务并非一个完全独立的功能模块。位置信息可包含二维坐标与三维坐标,而位置数据的来源本质上是借助设备属性进行上传。换言之,若要让设备上报位置,您需先将其视为一个普通属性进行处理。 1)添加二维位置数据 操作过程十分简洁。进入数据分析 → 空间数据可视化 → 二维数据,点击添加,将

年阿里云服务器选型配置与网站部署全攻略
AI教程 · 2026-06-29

年阿里云服务器选型配置与网站部署全攻略

2026年,阿里云服务器生态已高度成熟,形成了清晰的轻量应用服务器与ECS云服务器两大产品阵营。无论你是计划搭建个人博客、企业官网,还是运营电商平台、进行应用开发,基本都能找到理想的解决方案。本指南将从服务器选型、配置选择、部署流程到安全运维,系统梳理2026年最实用的操作要点,帮助你少走弯路,让网