模型简介
模型特点
模型能力
使用案例
license: apache-2.0 tags:
- 多模态
- 视觉语言
- 视频理解
- 空间推理
- 视觉空间认知
- llava
- qwen
- llava-video datasets:
- nkkbr/ViCA-322K
- nkkbr/ViCA-thinking-2.68k language:
- en library_name: transformers pipeline_tag: video-text-to-text model_name: ViCA-7B base_model: lmms-lab/LLaVA-Video-7B-Qwen2 model-index:
- name: ViCA-7B
results:
- task:
type: 视觉问答
dataset:
name: VSI-Bench
type: vsi-bench
metrics:
- type: 得分 value: 60.56 name: 平均分 verified: false
- type: MRA value: 68.81 name: 物体计数
- type: MRA value: 57.01 name: 绝对距离
- type: MRA value: 79.17 name: 物体尺寸
- type: MRA value: 75.14 name: 房间尺寸
- type: 准确率 value: 58.45 name: 相对距离
- type: 准确率 value: 42.56 name: 相对方向
- type: 准确率 value: 34.54 name: 路径规划
- type: 准确率 value: 68.77 name: 出现顺序
- task:
type: 视觉问答
dataset:
name: VSI-Bench
type: vsi-bench
metrics:

ViCA-7B:视觉空间认知助手
您可能对我们的另一个项目ViCA2也感兴趣。请参考以下链接:
概述
ViCA-7B是一款专为室内视频环境中的视觉空间推理而微调的视觉语言模型。基于LLaVA-Video-7B-Qwen2架构构建,它使用我们新提出的ViCA-322K数据集进行训练,该数据集强调结构化空间标注和基于指令的复杂推理任务。
ViCA-7B在VSI-Bench上实现了最先进的性能,超越了专有模型如GPT-4o和Gemini-1.5 Pro,以及更大的开源基线模型。
ViCA-7B为室内视频的开源多模态空间推理设定了新标准,使其成为具身AI和机器人应用的强有力候选者。
图1:ViCA-7B与其他模型在VSI-Bench上的性能对比。
模型架构与训练策略
ViCA-7B基于LLaVA-NeXT框架构建,使用Qwen2-7B作为语言主干,SigLIP作为视觉编码器。
关键训练特性
-
固定长度视觉标记化
每个视频均匀采样为64帧,每帧编码为210个视觉标记,每个示例总共13,440个视觉标记。这种固定长度设计确保跨批次的内存使用一致和优化稳定。 -
通过轻量级投影器实现多模态对齐
一个简单的基于MLP的投影器将视觉嵌入映射到语言嵌入空间,在训练和推理过程中实现视频内容与文本提示的有效融合。 -
使用DeepSpeed进行高效分布式训练
训练在8×NVIDIA H100 80GB GPU上使用DeepSpeed ZeRO-3 Offload进行,完整参数和优化器状态跨设备分区。此设置支持大批量并最小化GPU内存开销。 -
混合精度计算(fp16)
我们采用**混合精度训练(fp16)**来加速计算并减少内存使用,而不影响准确性。这与ZeRO-3分区结合,进一步提高了训练可扩展性。
训练持续55小时,涵盖基础和复杂空间推理子集。
训练动态
图2:ViCA-7B微调期间的训练损失、学习率计划和梯度范数曲线。 这些曲线展示了在DeepSpeed ZeRO-3设置下的稳定优化过程和平滑收敛。
数据集
ViCA-7B在两个互补的数据集上进行了微调:
-
ViCA-322K:
一个大规模数据集,涵盖基础空间推理任务(如物体距离、尺寸、计数、出现顺序)和涉及自然语言问题和场景理解的复杂空间推理任务。该数据集构成了模型空间推理能力的核心。 -
ViCA-thinking-2.68k:
一个专注于指令调优的数据集,用于增强模型生成逐步推理痕迹的能力,然后输出最终答案。这支持更可解释和认知对齐的响应生成。
详情请参阅上述链接的各个数据集页面。
评估:VSI-BENCH基准
图3:ViCA-7B与基线模型在VSI-Bench上的定量比较。ViCA-7B在数值和多项选择题任务中均实现了最佳整体性能。
CSR数据的影响
配置 | 平均得分 |
---|---|
仅基础数据 (281K) | 55.35 |
完整数据含CSR (322K) | 60.56 |
CSR(复杂空间推理)提升了泛化能力并加速学习,在中间检查点(如50%到55%时+2.02)表现显著提升。
数据规模与性能
性能在**5%→60%数据使用量之间显著提升。超过80%**后,改进趋于平稳,表明数据集与模型容量匹配良好。
图4:ViCA-7B在不同训练数据规模(从5%到100%)下的性能。完整数据集(含复杂空间推理,CSR)始终优于仅基础配置。值得注意的是,CSR增强模型在50%到55%之间显示出+2.02的得分跳跃,在完整规模下最终性能增益为+4.75。超过80%后性能趋于平稳,表明数据集与模型容量匹配良好。
中间检查点与评估输出
为支持详细分析和可复现性,我们提供两组在训练数据每5%增量时保存的中间检查点。这些模型训练了单个周期,有助于理解性能随训练进展的演变。
我们还发布了每个检查点对应的原始评估输出(如.json
预测文件)。
用于生成这些输出的评估脚本可在我们的GitHub仓库中找到。
完整数据集(ViCA-322K:基础+CSR)
此系列对应完整训练集,包括基础空间推理和复杂空间推理(CSR):
数据使用 | 检查点 | 数据使用 | 检查点 |
---|---|---|---|
5% | nkkbr/ViCA-5p |
55% | nkkbr/ViCA-55p |
10% | nkkbr/ViCA-10p |
60% | nkkbr/ViCA-60p |
15% | nkkbr/ViCA-15p |
65% | nkkbr/ViCA-65p |
20% | nkkbr/ViCA-20p |
70% | nkkbr/ViCA-70p |
25% | nkkbr/ViCA-25p |
75% | nkkbr/ViCA-75p |
30% | nkkbr/ViCA-30p |
80% | nkkbr/ViCA-80p |
35% | nkkbr/ViCA-35p |
85% | nkkbr/ViCA-85p |
40% | nkkbr/ViCA-40p |
90% | nkkbr/ViCA-90p |
45% | nkkbr/ViCA-45p |
95% | nkkbr/ViCA-95p |
50% | nkkbr/ViCA-50p |
100%(本仓库) | nkkbr/ViCA |
原始评估输出可在此处获取此处。
仅基础子集(ViCA-322K:基础)
此系列仅使用ViCA-322K的基础空间推理子集进行训练,不含任何CSR示例:
数据使用 | 检查点 | 数据使用 | 检查点 |
---|---|---|---|
5% | nkkbr/ViCA-base-5p |
55% | nkkbr/ViCA-base-55p |
10% | nkkbr/ViCA-base-10p |
60% | nkkbr/ViCA-base-60p |
15% | nkkbr/ViCA-base-15p |
65% | nkkbr/ViCA-base-65p |
20% | nkkbr/ViCA-base-20p |
70% | nkkbr/ViCA-base-70p |
25% | nkkbr/ViCA-base-25p |
75% | nkkbr/ViCA-base-75p |
30% | nkkbr/ViCA-base-30p |
80% | nkkbr/ViCA-base-80p |
35% | nkkbr/ViCA-base-35p |
85% | nkkbr/ViCA-base-85p |
40% | nkkbr/ViCA-base-40p |
90% | nkkbr/ViCA-base-90p |
45% | nkkbr/ViCA-base-45p |
95% | nkkbr/ViCA-base-95p |
50% | nkkbr/ViCA-base-50p |
100% | nkkbr/ViCA-base |
原始评估输出可在此处获取此处。
源数据检查点
虽然完整的ViCA-322K数据集由我们整理,但底层视频和相关元数据来自三个不同的室内视频数据集:
为更好地理解每个数据源对模型性能的贡献,我们在ViCA-322K的子集上微调了ViCA-7B,这些子集仅使用来自每个源的数据。对于每个子集,我们提供从可用数据的**10%到100%**以10%增量训练的检查点。
还提供了所有检查点对应的原始评估输出(如.json
预测)。
仅ARKitScenes检查点
数据使用 | 检查点 | 数据使用 | 检查点 |
---|---|---|---|
10% | nkkbr/ViCA-ARKitScenes-10p |
60% | nkkbr/ViCA-ARKitScenes-60p |
20% | nkkbr/ViCA-ARKitScenes-20p |
70% | nkkbr/ViCA-ARKitScenes-70p |
30% | nkkbr/ViCA-ARKitScenes-30p |
80% | nkkbr/ViCA-ARKitScenes-80p |
40% | nkkbr/ViCA-ARKitScenes-40p |
90% | nkkbr/ViCA-ARKitScenes-90p |
50% | nkkbr/ViCA-ARKitScenes-50p |
100% | nkkbr/ViCA-ARKitScenes |
原始评估输出:ARKitScenes结果
仅ScanNet++检查点
数据使用 | 检查点 | 数据使用 | 检查点 |
---|---|---|---|
10% | nkkbr/ViCA-ScanNetPP-10p |
60% | nkkbr/ViCA-ScanNetPP-60p |
20% | nkkbr/ViCA-ScanNetPP-20p |
70% | nkkbr/ViCA-ScanNetPP-70p |
30% | nkkbr/ViCA-ScanNetPP-30p |
80% | nkkbr/ViCA-ScanNetPP-80p |
40% | nkkbr/ViCA-ScanNetPP-40p |
90% | nkkbr/ViCA-ScanNetPP-90p |
50% | nkkbr/ViCA-ScanNetPP-50p |
100% | nkkbr/ViCA-ScanNetPP |
原始评估输出:ScanNet++结果
仅ScanNet检查点
数据使用 | 检查点 | 数据使用 | 检查点 |
---|---|---|---|
10% | nkkbr/ViCA-ScanNet-10p |
60% | nkkbr/ViCA-ScanNet-60p |
20% | nkkbr/ViCA-ScanNet-20p |
70% | nkkbr/ViCA-ScanNet-70p |
30% | nkkbr/ViCA-ScanNet-30p |
80% | nkkbr/ViCA-ScanNet-80p |
40% | nkkbr/ViCA-ScanNet-40p |
90% | nkkbr/ViCA-ScanNet-90p |
50% | nkkbr/ViCA-ScanNet-50p |
100% | nkkbr/ViCA-ScanNet |
原始评估输出:ScanNet结果
额外探索
时间指令
在提示中包含64帧时间戳会略微降低性能,表明模型未能利用时间对齐,且受指令冗长性的负面影响。
图5:在VSI-Bench上添加显式帧时间戳(64个值)会降低模型性能,表明无法利用时间对齐且对提示长度敏感。
更多帧
将输入从64帧增加到128帧会使视觉标记数量翻倍(13,440→26,880),但无性能提升,凸显了对固定标记长度的过拟合和架构的灵活性不足。
图6:64帧与128帧输入的对比。尽管视觉标记数量翻倍,性能保持不变,表明对固定长度输入的过拟合和对可变长度序列的适应能力有限。
潜在应用
ViCA-7B支持广泛的基于空间的多模态应用:
- 室内导航助手
- 机器人规划和空间查询
- 智能房间布置和AR布局分析
- 具身AI代理的场景理解
已知限制
- 有限的时间推理:时间指令未被有效利用
- 帧缩放问题:模型期望固定输入长度
- 无深度/点云:仅支持RGB视频输入
- 零样本泛化良好,但非任务无关
推理
以下是使用ViCA-7B回答VSI-Bench问题的可运行示例。
# 此推理脚本改编自:
# https://huggingface.co/lmms-lab/LLaVA-Video-7B-Qwen2
# pip install git+https://github.com/LLaVA-VL/LLaVA-NeXT.git
from llava.model.builder import load_pretrained_model
from llava.mm_utils import get_model_name_from_path, process_images, tokenizer_image_token
from llava.constants import IMAGE_TOKEN_INDEX, DEFAULT_IMAGE_TOKEN, DEFAULT_IM_START_TOKEN, DEFAULT_IM_END_TOKEN, IGNORE_INDEX
from llava.conversation import conv_templates, SeparatorStyle
from PIL import Image
import requests
import copy
import torch
import sys
import warnings
from decord import VideoReader, cpu
import numpy as np
import json
from tqdm import tqdm
import os
warnings.filterwarnings("ignore")
def load_video(video_path, max_frames_num,fps=1,force_sample=False):
if max_frames_num == 0:
return np.zeros((1, 336, 336, 3))
vr = VideoReader(video_path, ctx=cpu(0),num_threads=1)
total_frame_num = len(vr)
video_time = total_frame_num / vr.get_avg_fps()
fps = round(vr.get_avg_fps()/fps)
frame_idx = [i for i in range(0, len(vr), fps)]
frame_time = [i/fps for i in frame_idx]
if len(frame_idx) > max_frames_num or force_sample:
sample_fps = max_frames_num
uniform_sampled_frames = np.linspace(0, total_frame_num - 1, sample_fps, dtype=int)
frame_idx = uniform_sampled_frames.tolist()
frame_time = [i/vr.get_avg_fps() for i in frame_idx]
frame_time = ",".join([f"{i:.2f}s" for i in frame_time])
spare_frames = vr.get_batch(frame_idx).asnumpy()
# import pdb;pdb.set_trace()
return spare_frames,frame_time,video_time
pretrained = 'nkkbr/ViCA'
model_name = "llava_qwen"
device = "cuda"
device_map = "auto"
tokenizer, model, image_processor, max_length = load_pretrained_model(pretrained, None, model_name, torch_dtype="bfloat16", device_map=device_map) # 添加任何其他你想在llava_model_args中传递的内容
model.eval()
from datasets import load_dataset
vsi_bench = load_dataset("nyu-visionx/VSI-Bench")
vsi_bench = vsi_bench['test']
data_curr = vsi_bench[1000]
video_path = f"[视频路径]"
max_frames_num = 64
video,frame_time,video_time = load_video(video_path, max_frames_num, 1, force_sample=True)
video = image_processor.preprocess(video, return_tensors="pt")["pixel_values"].cuda().to(torch.bfloat16)
video = [video]
conv_template = "qwen_1_5"
# time_instruciton = f"视频持续{video_time:.2f}秒,从中均匀采样了{len(video[0])}帧。这些帧位于{frame_time}。请回答与此视频相关的以下问题。"
time_instruciton = ""
question = DEFAULT_IMAGE_TOKEN + f"\n{time_instruciton}\n\n"
question += f"这些是视频的帧。\n\n"
question += f"问题:{data_curr['question']}\n"
if data_curr['options'] is not None:
question += '\n'.join(data_curr['options']) + "\n"
question += f"直接从给定选项中选择字母回答。\n"
else:
question += f"请用一个词或短语回答问题。\n"
print(f"提示:\n{question}")
conv = copy.deepcopy(conv_templates[conv_template])
conv.append_message(conv.roles[0], question)
conv.append_message(conv.roles[1], None)
prompt_question = conv.get_prompt()
input_ids = tokenizer_image_token(prompt_question, tokenizer, IMAGE_TOKEN_INDEX, return_tensors="pt").unsqueeze(0).to(device)
cont = model.generate(
input_ids,
images=video,
modalities= ["video"],
do_sample=False,
temperature=0,
max_new_tokens=1024,
)
text_outputs = tokenizer.batch_decode(cont, skip_special_tokens=True)[0].strip()
print(repr(text_outputs))










