前言
Hi大家好,我叫延捷,是一名计算机视觉算法工程师,也是叉烧的老朋友了。 我们计划发布一系列关于多模态大模型的文章,帮助大家快速、精准地了解多模态大模型的前世今生,并且深 入各个多模态大模型领域优秀的工作,希望能给大家一个脉络性的盘点,一起学习,共同进步。
Introduction
上一期我们介绍了在多模态大模型领域相当火热Gemini系列,分享了Google在多模态大模型领域的一些经典工作,并且也会介绍笔者非常喜欢的一个工作Mini-Gemini。本期则会详细给大家介绍“小而美”的MiniCPM-V工作。同样地我并不会过多列举一些不必要的论文细节和指标,而是会着重讲述:
- “心路历程”:一个系列工作逐步发展的路径,作者是如何根据当前工作的缺点一步步优化的,并且会总结出每篇工作的highlight,在精而不在多;
- “数据细节”:各个工作中对数据处理的细节,包括但不限于数据的收集,采样时的分布,如何清洗/重建noisy数据,如何进行数据预处理,视频抽样的方案等,这些对算法工程师来说是同样重要的一环;
- “前人肩膀”:各个工作中隐藏着一些非常值得盘的消融实验,站在前人的肩膀上,使用这些已有的消融实验结论,不仅能帮助我们更好地理解论文,更能在实际工作中少做些不必要的实验and少走弯路。
同样相信大家会从本文中收获不少。
MiniCPM-V
《MiniCPM-V: A GPT-4V Level MLLM on Your Phone》(2024)
MiniCPM-V其实也是一个多模态大模型的系列,跟LLaVA、Qwen-VL等不同,MiniCPM-V系列的设计哲学是在性能和效率之间实现良好的平衡,主打的方向是少参数、推理快速、可端侧运行,旨在用更少的参数达到那些大参数模型的效果。由于本篇论文的主角是MiniCPM-Llama3-V 2.5(为方便下面笔者就简称为MiniCPM-V 2.5),并且MiniCPM-V的其他系列也没有释放paper,这里我们就从MiniCPM-V 2.5入手,介绍当今2B和8B模型领域的强力好手。 MiniCPM-V 2.5的核心依然是使用了当下最流行、最有效的Visual Encoder + Projector + LLM的经典架构。笔者认为MiniCPM-V 2.5的核心亮点有三:一是在图像输入处理端,AnyRes技术的基础上,参考LLaVA-UHD的工作,精细化地使用了自适应视觉编码(Adaptive Visual Encoding),使得输入图像的切分子图能与视觉编码器的预训练尺寸更好地适配,并且尽可能地减少resize对原始图像高宽比的改变,降低因为图像扭曲导致的失真,保持整个图像信息分布的一致性;二是在tokenizer端,使用Spatial Schema把各个子图像的img tokens进行包装和区分;三是在训练端,在pre-traning中也采用了分阶段的训练策略,递进式地进行预训练。下面我们就来详细介绍MiniCPM-V 2.5这个工作。 模型结构方面,Visual Encoder采用了SigLIP,Projector采用了跟Qwen-VL系列一样的单层cross-attention结构,基于固定数量的img queries来压缩视觉编码器得到的visual tokens,LLM则使用了经典的LLaMA-3
MiniCPM-V 2.5模型架构图,Projector层采用了和Qwen-VL类似的当层cross-attn压缩visual tokens,图像输入端采用自适应视觉编码尽可能保持了原始的高宽比
MiniCPM-V 2.5中的Projector层具体使用的是叫Perceiver Resampler的结构,笔者认为这个结构应该是类似Flamingo提到的Perceiver Resampler结构,并且跟Qwen-VL一样仅使用了单层cross-attention来进行视觉信息的压缩&归纳,在MiniCPM-V 2.5中使用64/96个learnable queries对应2.4B/8B模型
在Flamingo中提出了Perceiver Resampler的架构,使用cross-attention抽象出需要的视觉特征,MiniCPM-V中固定为单层cross-attn
训练策略方面,采用Pre-Traning,Supervised Fine-Tuning,RLAIF-V的三阶段策略; A. 一阶段Pre-Training:主要用来对齐视觉编码层和图文对齐层,在Pre-Training阶段MiniCPM-V 2.5区分了3个子阶段来精细化训练,在下面Highlights我们将详细介绍; B. 二阶段SFT:主要食用高质量数据(包括人类标注数据和模型生成数据)来进一步提升模型的对话回答能力,具体来说二阶段SFT中会训练所有的参数,并且使用自适应视觉编码;在数据方面分为两类数据,第一类是专注于增强模型的基础识别能力的QA/caption数据集,响应长度较短,第二类是用来增强模型生成详细响应和提高遵循指令能力的数据集,包含了长响应以及复杂交互的数据集;在整个SFT中,会把这两部分数据串联后依次输入模型进行训练,可以简单认为就是先训练第一类,后训练第二类;这也比较符合大多MLLM在SFT阶段的训练思路:优先训练常识能力,指令跟随和复杂交互最后训练; C. 三阶段RLAIF-V:主要为了降低模型生成幻觉,采用基于强化学习的技术来训练的,具体来说使用较高的温度对同一个输入生成多样性的回答,再用LLM提取每个回答的子声明(atomic claims),再用固定的MLLM来进行yes/no判别,最后归纳子声明为每个回答打分,这样就可以对一个输入生成一组<好回答&坏回答>,最后用DPO技术基于此进行偏好学习;
MiniCPM-V 2.5中RLAIF-V的详细流程图
Highlights:
- 自适应视觉编码(Adaptive Visual Encoding):在传统ViT模型中,需要对图片输入的尺寸进行固定,但是这种固定尺寸的resize会带来两个弊端:一是会丢失许多图像细节信息,尤其对小目标检测和OCR等任务很不友好;二是直接改变了图像输入的长宽比,对整体图像信息的分布也是造成了破坏;但若把原图直接放入ViT中,会造成patch数量过多,transformer计算复杂度太大,那么直接的想法就是使用LLaVA-NeXT提出的AnyRes技术来进行图像切分,核心是保证切分后图片跟ViT预训练尺寸的一致性。基于以上几点可以总结出来,我们需要解决的问题是:A、尽量不要改变输入图像的高宽比和像素值;B、切分后每个子图片尺寸尽量和ViT预训练尺寸接近。下面我们会拆分出每一步并且给出伪代码;同时我们也给出了再LLaVA-UHD中关于使用自适应视觉编码对图像进行处理的消融实验,证实了该策略的有效性
- 第一步,若图像像素大于448448,就进行切分,否则按当前高宽比resize到448448
# 图像编码器的预训练尺寸vit_width = 448vit_height = 448# ViT的patch\_size, 一般都默认为14patch_size = 14_, height, width = input_image.shape# 若图片的像素小于448*448, 则按当前高宽比resize到448*448if width*height < vit_width*vit_height: ratio = width / height height = int(vit_height / math.sqrt(ratio)) width = int(height * ratio)# 保证图片能被patch\_size整除else: width = max(round(width / patch_size) * patch_size, patch_size) height = max(round(height / patch_size) * patch_size, patch_size)
- 第二步,计算大致的切分次数,再遍历得到所有可能的候选切分方案
# 最大的切分次数, MiniCPM-V 2.5的论文中使用的是9max_crop_num = 9# 计算大致的切分次数N, 候选切分次数为[N-1,N,N+1]slice_base = min(width*height // vit_width*vit_height, max_crop_num)slice_num_list = [slice_base-1, slice_base, slice_base+1]# 遍历得到所有可能的切分方案, [2,4], [1,8], [8,1], [3,3],......candidate_grid = []for slice_num in slice_num_list: for x in range(1, slice_num+1): if slice_num % x == 0: y = slice_num // x candidate_grid.append([x, y])
- 第三步,根据得到的所有候选切分方案,找到切分后子图的高宽比和图像编码器ViT预训练尺寸高宽比差异最小的方案
# 图像编码器ViT预训练尺寸高宽比vit_ratio = vit_width / vit_heightbest_grid = [1, 1]best_error = math.inffor grid in candidate_grid: cur_grid_width = width // grid[0] cur_grid_height = height // grid[1] # 当前切分方案的子图高宽比和ViT预训练高宽比的误差, 越小越好 cur_error = abs(log(cur_grid_width / cur_grid_height) - log(vit_ratio)) if cur_error < best_error: best_grid = grid best_error = cur_error
- 第四步,根据最佳切分方案得到小图的尺寸,计算小图最佳尺寸,进而计算最终大图尺寸,最后把大图resize,于是完整所有自适应视觉编码的准备工作,最后按照得到的最佳切分方案进行crop即可
# 最佳切分方案width_grid, height_grid = best_grid# 最佳切分方案的小图尺寸、高宽比sub_width = width / width_gridsub_height = height / height_gridsub_ratio = sub_width / sub_height# 让小图的 高*宽 尽可能接近 448*448, 并且保持高宽比不变sub_height = vit_height / math.sqrt(sub_ratio)sub_width = sub_height * sub_ratio# 保证小图的高宽都能被patch\_size整除sub_width = max(round(sub_width / patch_size) * patch_size, patch_size)sub_height = max(round(sub_height / patch_size) * patch_size, patch_size)# 最后反推出原图需要resize的尺寸refine_width = sub_width * width_gridrefine_height = sub_height * height_gridrefine_image = input_image.resize((refine_width, refine_height))# 最后还要留存一份原始图像resize到切分子图尺寸的结果, 也要放到图像编码器中ori_resize_image = input_image.resize((sub_width, sub_height))
MiniCPM-V 2.5自适应视觉编码结构图,核心就是尽可能少改变高宽比,并且保证切图后的子图跟视觉编码器ViT预训练高宽比尽量一致
对比LLaVA-NeXT中提出的AnyRes,可以看出自适应视觉编码更加灵活、多样,能够更好地处理一些高宽比较为极端的输入(比如文档图片,屏幕截图等)
自适应视觉编码与LLaVA-NeXT中AnyRes中实现方案的对比
LLaVA-UHD中关于自适应视觉编码的消融实验,证实了该精细化图像处理策略的有效性
- Spatial Schema:由于在上述自适应视觉编码策略中,把图像切成了若干子图,而在tokenizer中常规的方式是把子图的视觉编码tokens直接按顺序拼接在一起,这样有个弊端是模型无法获取各个子图直接的相对的2维位置关系(只能感知1维顺序关系)。为了优化这个问题MiniCPM-V 2.5同样参考LLaVA-UHD的方案:
- 给每个子图的视觉编码tokens前后增加与<\slice>包裹(但是MiniCPM-V的技术文档中却说是用<im_start>和<im_end>来包装,笔者认为这两周方案应该差异不大,还是看实际实验效果);
- 对位于不同行的子图中间,加入"\n"token来进行间隔区分,一定程度引入了子图之间的2维位置关系;
- 对位于不同列的子图中间,加入","token来进行间隔区分(这是LLaVA-UHD中提到的,MiniCPM-V 2.5的论文中并没有提到);
LLaVA-UHD中关于Spatial Schema自适应视觉编码的消融实验,证实了添加简单的token来引入子图之间2维位置关系的有效性
- MiniCPM-V 2.5的Pre-Training的训练细节:在MiniCPM-V 2.5中,在Pre-Training中也做到了分阶段的训练,具体如下:
- 一阶段只训练Projector层perceiver resampler,冻结了视觉编码器和LLM的参数,并且把输入图像的尺寸固定到了224*224,数据则是从captioning数据集中随机采样了200M条;
- 二阶段只训练视觉编码器SigLIP,冻结了Projector和LLM的参数,并且把输入图像的尺寸拓展到了448*448,数据同样从captioning数据集中随机采样了200M条;
- 三阶段训练训觉编码器SigLIP和Projector层perceiver resampler,冻结了LLM的参数,并且使用动态视觉编码策略来处理图像,数据则是使用了除去上面400M条的所有captioning数据+OCR+Knowledge数据;
MiniCPM-V 2.5中用到的数据集分布
笔者认为MiniCPM-V 2.5中在Pre-Training使用的训练策略,有点类似warmup的思路,逐步增加输入图像的分辨率,这种从粗粒度到细粒度的训练思路还是挺值得借鉴的。 同样在数据处理方面MiniCPM-V 2.5的paper也给出了几个相当务实的操作,具体如下:
- Caption Rewriting:使用LLM来进行对那些低质量图文对进行改写,这个现在基本上也是所有MLLM处理noisy的captioning数据的标准操作;
- Data Packing:文中提到不同的数据源可能数据的长度差异巨大,可能会导致不同batches之间真实tokens有较大的差异,从而造成内存/显存浪费,为了解决这个问题,使用了data packing策略,把多个样本打包成单一的一个序列,并且对末尾的样本做截断,保证每个输入模型样本的真实长度一致。为了降低不同样本之间的干扰,还修改了position ids和引入attention mask来规避干扰。
- Multilingual Generalization:为了解决小众语言收集数据的难度,MiniCPM-V 2.5采用的方案是在Pre-Training只用英文+中文进行预训练,在SFT阶段收集轻量但高质量的多语言数据来进行fine-tuning,这也基本上是标准操作了。
Reference