阿里国际Ovis2系列模型开源:多模态大语言模型的新突破
Ovis是阿里巴巴国际化团队提出的新型多模态大模型架构,通过巧妙地将视觉和文本嵌入进行结构化对齐,为解决模态间嵌入策略差异这一局限性提供了方案。
01.背景
Ovis是阿里巴巴国际化团队提出的新型多模态大模型架构,通过巧妙地将视觉和文本嵌入进行结构化对齐,为解决模态间嵌入策略差异这一局限性提供了方案。Ovis2作为Ovis系列模型的最新版本,相较于前序1.6版本,在数据构造和训练方法上都有显著改进。它不仅强化了小规模模型的能力密度,还通过指令微调和偏好学习大幅提升了思维链(CoT)推理能力。值得一提的是,Ovis2引入了视频和多图像处理能力,并增强了多语言能力和复杂场景下的OCR能力,这些进步显著提升了模型的实用性。
Ovis2现已开源1B、2B、4B、8B、16B和34B六个版本,各个参数版均达到同尺寸SOTA,为不同应用场景提供了丰富的选择。其中,Ovis2-34B在权威评测榜单OpenCompass上展现出了卓越的性能。在多模态通用能力榜单上,Ovis2-34B位列所有开源模型第二,以不到一半的参数尺寸超过了诸多70B开源旗舰模型。在多模态数学推理榜单上,Ovis2-34B位列所有开源模型第一,并且在其他尺寸上也展现出出色的推理能力。这些成绩不仅证明了Ovis架构的有效性,也展示了开源社区在推动多模态大模型发展方面的巨大潜力。
我们坚信,开源是推动AI技术进步的关键力量。通过公开分享Ovis研究成果,我们期待与全球开发者共同探索多模态大模型的前沿,激发更多创新应用。
代码:
https://github.com/AIDC-AI/Ovis
模型:
https://modelscope.cn/collections/Ovis2-1e2840cb4f7d45
Demo:
https://huggingface.co/spaces/AIDC-AI/Ovis2-16B
arXiv:
https://arxiv.org/abs/2405.20797
02.模型架构
多模态大模型通常基于预训练的LLM和视觉模块构建。这两个模块采用不同的嵌入策略:文本嵌入是从LLM的嵌入查找表中索引得到的,其中文本词表的每个“单词”通过独热文本token映射到一个嵌入向量。相比之下,视觉嵌入通常由视觉编码器经MLP连接器投影后以非结构化方式直接生成。这种模态间嵌入策略的结构性差异可能会限制模型的整体性能。为了克服这一潜在的局限性,我们提出了一种名为Ovis的创新架构,旨在实现结构化的嵌入对齐,进一步推动开源多模态大模型领域的发展。
Ovis架构由三个关键组件构成:视觉tokenizer、视觉嵌入表和LLM。
1. 视觉tokenizer:首先,Ovis将输入图像分割成多个图像块 (patch),利用视觉Transformer提取每个图像块的特征表示。随后,通过视觉头层将每个图像块的特征匹配到各个“视觉单词”上,得到其在整个视觉词表上的概率分布,即概率化的视觉token。
2. 视觉嵌入表:类似于LLM中的文本嵌入表,Ovis的视觉嵌入表存储着每个视觉单词对应的嵌入向量。由于视觉token中的每个元素代表采样某个视觉单词的概率,因此视觉token的嵌入向量自然地定义为所有视觉单词嵌入向量的加权平均,权重即为对应的概率。
3. LLM:Ovis使用LLM的文本tokenizer将输入文本转化为独热文本token(独热token也是概率化token的退化情形),并根据文本嵌入表查找到每个文本token对应的嵌入向量。最后,Ovis将所有视觉嵌入向量与文本嵌入向量拼接起来,送入LLM中进行处理,生成文本输出,完成多模态任务。
03.训练策略
Ovis2采用了四阶段的训练策略,以充分激发其多模态理解能力。
Stage 1A:VET预训练。在这一阶段,我们冻结了大部分LLM和ViT参数,仅对ViT的最后一层、视觉头和视觉嵌入词表进行训练。此阶段多模态数据不使用对话模版,直接在图像后接上文本。这一阶段的主要目标是学习视觉特征到嵌入的转化,并优化视觉嵌入以激发文本信息。
Stage 1B:视觉理解增强。冻结LLM,训练视觉模块,提升最大子图切分数。数据格式和Stage 1A一致。通过增大子图切分数和逐步开放ViT,我们提高了视觉模块的特征提取和转化能力,增强了高分辨率图像理解、多语言和OCR能力。
Stage 2:对话指令对齐。冻结LLM,训练视觉模块。使用对话形式的视觉Caption数据。此阶段旨在使视觉嵌入对齐LLM的对话格式,为后续的指令训练奠定基础。
Stage 3:多模态指令训练。训练整个Ovis,包含视觉模块与LLM。训练数据包含多模态(纯文本、单图、多图、视频)指令数据。经过前两个阶段的训练,Ovis已经具备较强的视觉理解能力,本阶段的目标是提升Ovis在多种模态下对用户指令的遵循能力。
Stage 4:偏好学习。训练整个Ovis,包含视觉模块与LLM,并进一步提升最大子图切分数。训练数据包含多模态(纯文本、单图、多图、视频)偏好数据。通过直接和CoT采样合成偏好数据,提升模型输出质量和COT能力;同时增大子图切分数,进一步提升视觉高分处理能力。
04.视频能力增强
为了在有限的视觉上下文中提升视频理解能力,我们开发了一种创新的关键帧选择算法——。的核心是****基于帧与文本的相关性、帧之间的组合多样性以及帧的序列性来挑选最有用的视频帧。具体流程如下:
Step 1:高维条件相似度计算 我们利用预训练的视觉语言模型(VLM),将视频帧和问题语句映射到同一空间中,并通过条件多核高斯函数(CMGK)在无限维的再生核希尔伯特空间(RKHS)中计算帧之间的条件相似度。该相似度条件于帧与文本的相关性,既考虑了帧与问题的相关性,也兼顾了帧之间的组合多样性,为后续选帧提供了基础。
Step 2:行列式点过程(DPP) DPP最初用于描述泡利不相容中两个费密子的量子态互斥性,后来被广泛应用于机器学习中以衡量元素之间的组合多样性。在上一步的条件相似度矩阵上应用DPP算法,确保选出的帧既与问题相关,又具有丰富的组合多样性,从而最大化信息量。
Step 3:马尔可夫决策过程(MDP) 为了让选帧过程符合视频的序列性,我们扩展了原始的序列行列式点过程(SeqDPP)算法。将视频分割成多个片段,然后在每个片段内应用DPP,同时考虑前一个片段的选帧结果。从而在保持连续性的同时,确保局部片段内的列表式多样性。将该过程建模成马尔可夫决策过程(MDP),通过动态规划算法,在高效的时间复杂度内确定每个片段的最优选帧数量。
MDP3算法论文:MDP3: A Training-free Approach for List-wise Frame Selection in Video-LLMs
05.性能评测
下面是Ovis2在OpenCompass多模态评测榜单的评测结果,Ovis2系列6个尺寸模型在多个Benchmark同级别均取得了SOTA。
下面是在多模态模型数学推理榜单的评测结果,综合评分开源第一。
Ovis2在多项视频榜单上也取得了领先性能:
06.典型示例
小尺寸模型通用能力
大尺寸模型通用能力
大尺寸模型推理能力
07.模型推理
在魔搭社区的免费GPU算力上推理Ovis 4B版本
安装依赖
!pip install flash-attn==2.7.0.post2 --no-build-isolation
推理代码
import torch
from PIL import Image
from modelscope import AutoModelForCausalLM
# load model
model = AutoModelForCausalLM.from_pretrained("AIDC-AI/Ovis2-4B",
torch_dtype=torch.bfloat16,
multimodal_max_length=32768,
trust_remote_code=True).cuda()
text_tokenizer = model.get_text_tokenizer()
visual_tokenizer = model.get_visual_tokenizer()
# single-image input
image_path = './data/example.jpg'
images = [Image.open(image_path)]
max_partition = 9
text = 'Describe the image.'
query = f'<image>\n{text}'
## cot-style input
# cot_suffix = "Provide a step-by-step solution to the problem, and conclude with 'the answer is' followed by the final solution."
# image_path = '/data/images/example_1.jpg'
# images = [Image.open(image_path)]
# max_partition = 9
# text = "What's the area of the shape?"
# query = f'<image>\n{text}\n{cot_suffix}'
## multiple-images input
# image_paths = [
# '/data/images/example_1.jpg',
# '/data/images/example_2.jpg',
# '/data/images/example_3.jpg'
# ]
# images = [Image.open(image_path) for image_path in image_paths]
# max_partition = 4
# text = 'Describe each image.'
# query = '\n'.join([f'Image {i+1}: <image>' for i in range(len(images))]) + '\n' + text
## video input (require `pip install moviepy==1.0.3`)
# from moviepy.editor import VideoFileClip
# video_path = '/data/videos/example_1.mp4'
# num_frames = 12
# max_partition = 1
# text = 'Describe the video.'
# with VideoFileClip(video_path) as clip:
# total_frames = int(clip.fps * clip.duration)
# if total_frames <= num_frames:
# sampled_indices = range(total_frames)
# else:
# stride = total_frames / num_frames
# sampled_indices = [min(total_frames - 1, int((stride * i + stride * (i + 1)) / 2)) for i in range(num_frames)]
# frames = [clip.get_frame(index / clip.fps) for index in sampled_indices]
# frames = [Image.fromarray(frame, mode='RGB') for frame in frames]
# images = frames
# query = '\n'.join(['<image>'] * len(images)) + '\n' + text
## text-only input
# images = []
# max_partition = None
# text = 'Hello'
# query = text
# format conversation
prompt, input_ids, pixel_values = model.preprocess_inputs(query, images, max_partition=max_partition)
attention_mask = torch.ne(input_ids, text_tokenizer.pad_token_id)
input_ids = input_ids.unsqueeze(0).to(device=model.device)
attention_mask = attention_mask.unsqueeze(0).to(device=model.device)
if pixel_values is not None:
pixel_values = pixel_values.to(dtype=visual_tokenizer.dtype, device=visual_tokenizer.device)
pixel_values = [pixel_values]
# generate output
with torch.inference_mode():
gen_kwargs = dict(
max_new_tokens=1024,
do_sample=False,
top_p=None,
top_k=None,
temperature=None,
repetition_penalty=None,
eos_token_id=model.generation_config.eos_token_id,
pad_token_id=text_tokenizer.pad_token_id,
use_cache=True
)
output_ids = model.generate(input_ids, pixel_values=pixel_values, attention_mask=attention_mask, **gen_kwargs)[0]
output = text_tokenizer.decode(output_ids, skip_special_tokens=True)
print(f'Output:\n{output}')
显存占用:
08.模型微调
使用ms-swift对Ovis2-2B进行微调。ms-swift是魔搭社区官方提供的大模型与多模态大模型训练部署框架。
ms-swift开源地址:
https://github.com/modelscope/ms-swift
在这里,我们将展示可运行的微调demo,并给出自定义数据集的格式。
在开始微调之前,请确保您的环境已准备妥当。
# pip install git+https://github.com/modelscope/ms-swift.git
git clone https://github.com/modelscope/ms-swift.git
cd ms-swift
pip install -e .
微调脚本如下:
CUDA_VISIBLE_DEVICES=0 \
swift sft \
--model AIDC-AI/Ovis2-2B \
--dataset 'AI-ModelScope/LaTeX_OCR#2000' \
--train_type lora \
--torch_dtype bfloat16 \
--num_train_epochs 1 \
--per_device_train_batch_size 1 \
--per_device_eval_batch_size 1 \
--learning_rate 1e-4 \
--lora_rank 8 \
--lora_alpha 32 \
--target_modules all-linear \
--freeze_vit true \
--gradient_accumulation_steps 16 \
--eval_steps 100 \
--save_steps 100 \
--save_total_limit 2 \
--logging_steps 5 \
--max_length 2048 \
--output_dir output \
--warmup_ratio 0.05
训练显存占用:
如果要使用自定义数据集进行训练,你可以参考以下格式,并指定`--dataset <dataset_path>`。
{"messages": [{"role": "user", "content": "浙江的省会在哪?"}, {"role": "assistant", "content": "浙江的省会在杭州。"}]}
{"messages": [{"role": "user", "content": "<image><image>两张图片有什么区别"}, {"role": "assistant", "content": "前一张是小猫,后一张是小狗"}], "images": ["/xxx/x.jpg", "/xxx/x.png"]}
{"messages": [{"role": "system", "content": "你是个有用无害的助手"}, {"role": "user", "content": "<image><image><image><image>视频中是什么"}, {"role": "assistant", "content": "视频中是一只小狗在草地上奔跑"}], "images": ["/xxx/xxx/1.jpg", "/xxx/xxx/2.jpg", "/xxx/xxx/3.jpg", "/xxx/xxx/4.jpg"]}
训练完成后,使用以下命令对训练后的权重进行推理:
提示:这里的`--adapters`需要替换成训练生成的last checkpoint文件夹。由于adapters文件夹中包含了训练的参数文件`args.json`,因此不需要额外指定`--model`,swift会自动读取这些参数。如果要关闭此行为,可以设置`--load_args false`。
CUDA_VISIBLE_DEVICES=0 \
swift infer \
--adapters output/vx-xxx/checkpoint-xxx \
--stream false \
--max_batch_size 1 \
--load_data_args true \
--max_new_tokens 2048
推送模型到ModelScope:
CUDA_VISIBLE_DEVICES=0 \
swift export \
--adapters output/vx-xxx/checkpoint-xxx \
--push_to_hub true \
--hub_model_id '<your-model-id>' \
--hub_token '<your-sdk-token>'
点击链接阅读原文,直达模型合集~
更多推荐
所有评论(0)