当前位置: 首页 > java >正文

多模态模型如何处理任意分辨率输入——Tiling与Packing技术详解

1 背景

过去,在应用Vision Transformer(ViT)模型处理实际的任务时,我们往往会将图片分辨率resize到某一固定尺寸再送入模型。这样做有几大好处:

  • 训练和推理时图片的尺寸一致,特征尺度相对一致,易于泛化
  • 固定分辨率便于batch推理。

但对于一个通用的多模态模型而言,这个方案并不适合。首先,“通用”代表模型所面临的task很多,而不同的task所关注的图片特征是不一致的。仅在OCR任务上动态分辨率就有较大意义,如行级别的OCR和页面级别的OCR,如果都用同一尺寸的分辨率去做,显然是不合理的,不仅有计算浪费,resize导致的字符长宽比失真亦或padding造成无关像素的引入都可能引起精度的下降。

那么自然会引发一个思考,为什么我们不能用图片动态分辨率去做推理呢?在cnn-era,动态分辨率推理是个常规操作,但ViT-era,却往往固定分辨率做推理。

回答这个问题前,不妨回顾一下ViT的各个组件:

  • patchify: H × W × C → L × d H \times W \times C \rightarrow L \times d H×W×CL×d (这个模块不限制图片分辨率,不同分辨率生成的image token数 L L L不同)
  • image token加入位置编码。(位置编码通常需要预先定义最大分辨率窗口,这个模块可能会依赖图片分辨率。不过可以通过插值,亦或用可外推的位置编码方案来解决)
  • transformer block (这个模块不依赖图片分辨率,理论上只要显存够,多少分辨率都能work)

可见ViT本身是支持动态分辨率推理的,ViT架构不是Any-resolution的瓶颈所在。但不同分辨率意味着不能组batch,从而导致并行效率低,不利于训练和并行推理。

因此,比较棘手的是如何解决any-resolution的并行推理问题。

2 Method

目前多模态处理多分辨率输入主要有两种方案:

  • Tiling。核心思路是预设一个默认分辨率,称之为tile,随后将图片切分成子图,每个子图的shape与tile一致,再在batch维度做拼接。tiling还会将原图也resize到tile的shape,和子图拼接在一起,以保留全局信息。该方法用一种相对暴力的方式,以fixed shape的ViT实现了动态分辨率的推理。
  • Packing 。packing的做法最早源于NLP的sequence pack(《Efficient Sequence Packing without Cross-contamination: Accelerating Large Language Models without Impacting Performance》)。核心思路是将多个分辨率的图片特征经过patchify后,在sequence维度进行拼接,通过引入block diagonal mask来避免跨图像token计算attention问题,从而实现并行。

下节将相对详细的对上述两种方式进行介绍。

2.1 Tiling

Tiling是解决Any Resolution的一种常用方案,经典工作如UReader, Llava Next, InterVL1.5, BLIP-3等。其核心思路为:先预先定义tile的shape,如(384,384),随后设置一些尺度模版,这里不妨设置为4种,分别为,1:1, 1:2, 2:1, 2:2。有了tile的shape和尺度模板,就可以得到预设的分辨率,如下表所示:

tile为384x384时不同尺度对应的预设分辨率

尺度模板预设分辨率
1:1384x384
1:2384x768
2:1768x384
2:2768x768

给定一张图片,映射到最相近的预设分辨率,随后进行切分384x384的子图,随后将原图也resize成(384x384)在batch维度上拼接到一起输入到ViT中,以此达到并行。具体流程如下图所示:

step1: 将图片转为tiles

在这里插入图片描述

step2:batch化并行推理

在这里插入图片描述

tiling本质是:将图片先映射到预设分辨率,在切成固定尺寸的子图。其本质上仍然是一个固定形状的tokenization机制。

优点:

  • 能够并行推理不同分辨率的图片
  • 能直接应用开源在固定分辨率上训练的ViT,仅微调其他部分。

缺点:不同tile间的特征交互不足,会牺牲部分全局信息,会适合那些空间局部性强的任务。

2.2 Packing

ViT中的packing技术最先起源于google于2023年发表的论文:《Patch n’ Pack: NaViT, a Vision Transformer for any Aspect Ratio and Resolution》。用packing技术能对不同分辨率的图片进行组batch,并行推理

首先定义一组图片

B = { I k ∣ k = 1 , 2 , ⋯ , m } B = \{I_k|k=1, 2, \cdots , m\} B={Ikk=1,2,,m}, 其中 I k ∈ R H k × W k × 3 I_k \in \mathbb{R}^{H_k \times W_k \times 3} IkRHk×Wk×3。经过patchify后得到 S = { s k ∣ k = 1 , 2 , ⋯ , m } S = \{s_k|k=1, 2, \cdots , m\} S={skk=1,2,,m}, 其中 s k ∈ R L k × d s_k \in \mathbb{R}^{L_{k}\times d} skRLk×d, L k = W k s × H k s L_k = \frac{W_k}{s} \times \frac{H_k}{s} Lk=sWk×sHk

通过patchify,我们将图片转为了sequence。这些sequence的长度可能不同。随后加上各自的position embedding,从而得到包含位置信息的序列 P = { p k ∣ k = 1 , 2 , ⋯ , m } P = \{p_k|k=1, 2, \cdots , m\} P={pkk=1,2,,m},其中 p k ∈ R L k × d p_k \in \mathbb{R}^{L_{k}\times d} pkRLk×d

我们当然可以通过padding的方式将这些sequence补到统一长度,在引入attention mask来实现并行推理,如下图所示:

sequence padding的计算示意图

但这样做不够高效,因为padding位置的token产生的计算都是无效的。

packing的思路更为直接,为什么要组batch呢?扩大sequence的长度照样可以提升提升训练效率!

packing核心做法:

  • 将所有图片的sequence在sequence维度上进行拼接,从而得到一个超长的sequence即, x = C a t ( [ p 1 , ⋯ , p k ] , d i m = 0 ) , x ∈ R ∑ L k × d x = \mathrm{Cat}([p_1, \cdots, p_k], \mathrm{dim} = 0), x\in \mathbb{R}^{\sum L_k \times d} x=Cat([p1,,pk],dim=0),xRLk×d.
  • 随后将 x x x送入到transformer block中(这是个超长的序列)
  • 最后在将结果进行分离: [ p 1 ′ , ⋯ , p k ′ ] = S p l i t ( T r a n s f o r m e r ( x ) , d i m = 0 ) [p'_1, \cdots, p'_k] = \mathrm{Split}(\mathrm{Transformer}(x), \mathrm{dim} = 0) [p1,,pk]=Split(Transformer(x),dim=0),得到各自的结果。

packing的计算示意图。示意图中采用加性位置编码,直接与sequence embedding相加,实际尝试中也可用乘性位置编码,如2D-ROPE
这里要注意直接这样做是不等价的!因为transformer的Attention层存在token mixture的运算,若不做约束会导致attention跨图像计算,导致语意混淆。因此需要引入block-diagonal attention mask 来确保attention仅在相同图片的token间计算,以保证计算等价性。

在这里插入图片描述

packing技术只需修改Attention的计算公式为:

A t t e n t i o n = S o f t m a x ( Q K T d + M ) V \mathrm{Attention} = \mathrm{Softmax}(\frac{QK^T}{\sqrt{d}} +\mathcal{M})V Attention=Softmax(d QKT+M)V

M \mathcal{M} M为block diagonal attention mask。

3 One More Thing

packing的原理简单、有效。笔者补充几点实践中值得注意的细节。

3.1 降低block-diagonal attention mask显存占用

在packing naive的实现中,block-diagonal attention mask是一个 R ∑ L k × ∑ L k \mathbb{R}^{\sum L_k \times \sum L_k} RLk×Lk的稀疏矩阵,矩阵大小为所有图片sequence长度和的平方,空间复杂度为 O ( N 2 ) \mathcal{O}(N^2) O(N2)

实际的工程实现中不会真的去创建这个大矩阵,而是只需记录每个sequence的起始位置,根据位置信息进行segment-aware attention。如下述xformers的伪代码(不考虑位置编码):

import torch
import torch.nn as nn 
from xformers.ops.fmha import memory_efficient_attention 
from xformers.ops.fmha.attn_bias import BlockDiagonalMask img_ls = [img1, img2, ...]  # , each item shape like (1, 3, H_i, W_i)
patch_conv = nn.Conv2d(3, d, stride, stride)patch_embedding_ls: list[torch.Tensor] = [patch_conv(img) for img in img_ls]
# each item of list shape like (1, d, H_i/s, W_i/s)# generate block diagonal attention mask
packing_mask = BlockDiagonalMask.from_seqlens([p.shape[-2] * p.shape[-1] for p in patch_embedding_ls])# cat all patch_embedding
x = torch.cat([i.flatten(-2).transpose(-1, -2) for i in patch_embedding_ls], dim=-2).contiguous()  # packing# calculate attention
bsz, seq_len, d = x.size()
num_head = 2
head_dim = d // num_head q = to_q(x).view(bsz, seq_len, num_head, head_dim)
k = to_k(x).view(bsz, seq_len, num_head, head_dim)
v = to_v(x).view(bsz, seq_len, num_head, head_dim)
# xformers requires (B, S, H, D) layout
attn_out = memory_efficient_attention(q, k, v, packing_mask)  #

3.2 多模态特征融合的注意事项

Packing能实现不同分辨率图片的并行处理,得到长度不一的图片序列特征。多模态模型需要将图片特征融入到语言模型,常见的融合方式有两种:

  • Self-Attention融合(Causal LM with Image Prefix)。将图片序列特征与文本token按照特定的模板进行拼接(如<img>token1...tokenN</img><text>question</text>)。这种方式原生支持不同长度的图片序列特征,只需通过padding对齐输入层序列长度。但需注意position id和attention mask的准确设置。(如Qwen2.5-VL,KIMI-VL, Pixtral-12B)

    这类方法,由于图片token作为context,同等显存规模下,上下文窗口更短。优势:图片特征和文本特征交互更紧密,无需更改LLM架构。劣势:这种方法一般都会联合token compression技术。以降低context的长度。

  • **Cross-Attention融合。**在语言模型的transformer block中引入cross attention层,接受图片特征作为context来做多模态特征的融合。实践中,为了兼顾计算效率和交互深度,可以sample某些block添加cross attention, 如LLama3flamingo是每4层插入一次cross-attention的特征交互。若batch内图片长度不同不一,可通过padding对齐,以便并行运算。同样需要适当的mask控制,以避免无效token干扰attention score。优势:不占用context的长度。劣势:需要更改语言模型的架构。

目前基于LLM的多模态模型多以第一种方案为主,其特性决定了在认知推理上有更大优势。若任务偏向细粒度感知(如OCR类任务)第二种方案也有较多paper使用(如donut, nougat等等 )。当然第一类方案也适用细粒度感知类任务(如较新的smoldocling, GoT),只是其在context上有所限制。

3.3 仅做Vision Encoder的packing还不够高效

Packing能解决vision encode部分训练和推理的高效并行计算。但多模态训练任务的监督对象是LLM的response。

通常,不同training sample的response长度不同。常规的做法使用padding的方式进行序列长度的对齐以便组batch。但如果一个batch中序列长度的方差较大,会导致很多无效的计算。

由于LLM通常用teacher forcing的方式训练,因此在训练阶段,我们仍然可以用packing的方法做并行。或如文献《Dataset Decomposition: Faster LLM Training with Variable Sequence Length Curriculum》所述,可以预先对数据进行分桶,来减少一个batch中的序列方差, 这个方式需配合动态batch的机制(短序列用更大的batch size),以更为高效的利用训练资源。

4. 小结

本文详细介绍了目前多模态任务中常用的两种处理任意分辨率的技术tiling,packing。若有疏漏错误之处,敬请指出。

http://www.xdnf.cn/news/6936.html

相关文章:

  • CentOS 下 FTP 与 NFS 服务深度解析:从基础配置到实战应用
  • css 中 content: “\e6d0“ 怎么变成图标的?
  • 2000 元以下罕见的真三色光源投影仪:雷克赛恩Cyber Pro1重新定义入门级投影体验
  • 南航无人机大规模户外环境视觉导航框架!SM-CERL:基于语义地图与认知逃逸强化学习的无人机户外视觉导航
  • STM32F10xx 参考手册
  • ALIENTEK精英STM32F103开发板 实验0测试程序详解
  • 信息安全的基石:深入理解五大核心安全服务
  • NPN、PNP三极管的应用
  • 企业级电商数据对接:1688 商品详情 API 接口开发与优化实践
  • Pandas 掌握Matplotlib基础绘图①
  • 6to4、6over4的类比解释
  • MAUI之XAML标记扩展
  • Linux:计算机的层状结构
  • .NET 中管理 Web API 文档的两种方式
  • 指定elf文件dwarf 版本以及查看dwarf版本号
  • C++ 蓝桥 STEMA 真题模拟测试卷二
  • 程序中断方式好题分享
  • 日志系统**
  • 蓝桥杯11届国B 答疑
  • Redis内存管理深度解析
  • LeetCode --- 156双周赛
  • JAVA的常见API文档(上)
  • 高频面试题(含笔试高频算法整理)基本总结回顾110
  • 角点特征:从传统算法到深度学习算法演进
  • 电子电路:什么是色环电阻器,怎么识别和计算阻值?
  • React学习(二)-变量
  • Docker常见命令解读
  • Vue.js---watch 的实现原理
  • SpringSecurity授权、认证
  • 数据库blog1_信息(数据)的处理与效率提升