• 正文
  • 相关推荐
申请入驻 产业图谱

十分钟读论文 | SigLIP-HD:不加大图像,也能让视觉编码器看清细节

09/10 16:58
282
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

转载自公众号:敢敢AUTOHUB

0. 简介

SigLIP-HD  面向多模态大模型(MLLM)里的视觉编码环节,处理的是一个被主流路线绕过去的矛盾:想让模型看清文档、图表、场景文字这类细粒度内容,几乎所有工作都在往「喂更大的图」上加码,代价是切图、多次前向、成倍膨胀的视觉 token 和额外的 token 压缩模块。它没有继续在分辨率上堆料,而是提出一套由细到粗的监督(fine-to-coarse supervision):让一个冻结的教师在多尺度图像(512²+1024²)上算出高质量特征,再逼一个学生在中等分辨率 512² 的单张图上把这套特征学过来。方法建立在目前很强的 SigLIP 2-So400m/16-512px 编码器上,训练只在 4.5M 张原始图像上跑,不用任何人工或合成的细粒度标注。

从实验看,最值得关注的是它在完全相同的推理预算下把 DocVQA 从 56.0 提到 59.6、ChartQA 从 61.6 提到 65.2、HRBench 从 43.5 提到 48.3,且能作为即插即用的替换件直接换进现有 SigLIP 2 流水线。下面结合论文与仓库代码,重点拆解三件事:好特征到底长什么样、由细到粗的监督怎么落地成训练循环、以及为什么最简单的 L1 损失反而赢了。代码已经在 Github开源

论文地址:https://arxiv.org/html/2607.09488v1
GitHub项目链接:https://github.com/LiheYoung/SigLIP-HD

1. 为什么又要在「分辨率」这件事上折腾

1.1 人眼能在中等分辨率读懂,AI 为什么不行

先看一个反差鲜明的事实。把一张原生 1722px 高的文档图缩到 512px,字确实糊了,但人类依然能准确读出上面写了什么。换句话说,人的视觉系统在中等分辨率下就已经具备细粒度感知能力,不需要凑到原生大图才看得清。可当前的多模态大模型走的却是相反的路:早期工作(LLaVA 系列)粗暴地把图缩到 336² 这种固定低分辨率,近期的 Qwen2.5-VL、LLaVA-OneVision 则干脆保留原生分辨率。前者看不清细节,后者算力开销巨大。SigLIP-HD 的出发点就是这句反问——在简单加大图像之前,我们真的把模型在标准分辨率下的感知潜力榨干了吗?

1.2 现有三条路线,每条都有硬伤

提升 MLLM 视觉表征,业界大致有三条路,每条都不便宜。第一条是从头预训练更强的视觉编码器(DINOv2、Perception Encoder),靠更好的算法、更多数据、更大模型堆出来,动辄百万 GPU 小时、十亿级数据,绝大多数研究者玩不起。第二条是多编码器融合(Cambrian-1、Eagle),CLIP 擅长图文对应、纯视觉模型擅长细节,理论上互补,但实测收益有限,甚至出现负增益。第三条也是最主流的一条,直接加大输入分辨率,从固定放大到保留原生分辨率,稳步换来更强的 OCR 能力。这里的关键是,第三条虽然有效,却把复杂度全压在了推理侧:图必须被切成小块(tile)去匹配预训练分辨率,一次前向变成多次前向,视觉 token 成倍膨胀,还得再挂 resampler、pixel unshuffle 这类压缩模块给大模型减负,整个框架越叠越重。SigLIP-HD 的核心判断是:这条路走反了方向——与其在推理时加大图,不如在训练时把大图的知识蒸馏进标准分辨率的特征里。

2. 什么才是「好特征」:动手前先做四组探路实验

2.1 选谁当底座:SigLIP 2-So400m/16-512px

方法建立在 SigLIP 2 的 So400m/16-512px 版本上。这个模型 429M 参数,吃一张 512² 的图,在 patch size 16 下吐出 32²=1024 个视觉 token。作者先做了一组横向对比:在 LLaVA-1.5-7B 协议下,SigLIP 2 的这个 512px 版本在 DocVQA、TextVQA 这类 OCR 场景上明显强于 CLIP-L/14-336px(DocVQA 22.4 对 32.2),主要就赢在分辨率更大。所以后续实验主要用它当底座。这里要厘清的是,选底座不是拍脑袋,而是先确认「哪个现成编码器在目标场景上已经够强」,再在它上面做增量。

2.2 用几个尺度算「教师特征」最划算

在设计由细到粗的监督之前,必须先回答一个前置问题:要蒸馏的那份高质量特征,到底该用几个图像尺度算出来? 作者把 SigLIP 2-512px 喂进不同的多尺度组合——单 512²、512²+1024²、512²+1024²+1536²、再加 2048²、以及单独的 1024²、1536²。为了公平,高分辨率特征都插值回 32² token,多尺度特征取平均,保证所有设置的 token 数完全一致。

结论很清楚:在 token 数相同的前提下,引入高分辨率图(512² → 512²+1024²)能提升 12 个 benchmark 里的 10 个,OCR 和图表任务尤其明显。但收益会饱和——继续加到 2048² 那一档反而变差,说明盲目加大分辨率是次优的。另一个发现是单独一个高分辨率视图普遍不如多尺度视图,这印证了全局视野不可或缺。这里值得注意,MMBench 这类偏语义的 benchmark 甚至不喜欢细粒度特征,这也解释了后面为什么要坚持保留基础尺度。

2.3 高分辨率图怎么推理、多尺度特征怎么融合

还有两个工程细节需要探路。其一,一张 1024² 的大图怎么喂给 512px 的模型? 作者对比了三种:不重叠滑窗、半重叠滑窗、以及直接插值位置编码。结果有点反直觉——半重叠滑窗(在密集预测任务里通常更好)在这里反而掉点,插值位置编码也不如滑窗。作者推测是重叠区域 token 分布不一致、或位置编码冲突所致。最终最佳实践仍是不重叠滑窗其二,多尺度特征怎么融合? 对比了「插值+平均」「插值+通道拼接」「pixel unshuffle+拼接」三种,最简单的插值+平均反而最好。这个结论直接决定了后面训练框架的形态:平均之后特征维度和基础尺度完全一样,省掉了后续对齐所需的投影头。

一句话理解:探路实验给出三条硬结论——用 512²+1024² 两个尺度、不重叠滑窗切图、插值后取平均。这三条后面会原封不动搬进训练框架。

3. 整体框架:一路推理造教师,一路训练学学生

3.1 两分支结构与输入输出

SigLIP-HD 的框架简单到可以一句话说清:一个冻结的教师分支产出高质量特征,一个可训练的学生分支去逼近它。 教师和学生共享同一份 SigLIP 2 预训练权重,架构、输入输出完全一致,中间不加任何投影模块。学生吃一张 512² 的图,产出 32² 个 token,记它的特征图为;教师则在多尺度图上算出融合后的高质量目标特征。优化目标就是在 patch 级别把对齐到。

整个前向过程在 meta_arch.py 的 forward 里完成,代码结构几乎和上面这段话一一对应:先算学生在 512² 上的特征,再对每个教师尺度分别做切图推理、插值回 32×32、堆叠取平均,最后把学生特征和教师平均特征一起送进对齐损失。可以边读边对照第 3.1 节图 2 的两条分支,代码里的每一步都能在框架图上找到位置。

# siglip_hd/train/meta_arch.py
def forward(self, img_stu, img_tea):
    # 学生:吃 512² 图,取指定层的隐藏态作为特征
    stu_feat = self.student(
        img_stu, output_hidden_states=True
    ).hidden_states[self.cfg.student.select_layer]
    H_feat_stu = int(math.sqrt(stu_feat.shape[-2]))   # 32

    # 教师:对每个尺度(512, 1024)分别推理
    tea_feat_list = []
    for tea_img_size in self.tea_img_sizes:
        img_tea_local = F.interpolate(
            img_tea, (tea_img_size,) * 2, mode="bilinear", align_corners=True)
        tea_feat = self._infer_teacher(img_tea_local)
        # 把高分辨率特征插值回 32×32,和学生对齐
        tea_feat = F.interpolate(
            tea_feat, (H_feat_stu,) * 2, mode="bilinear", align_corners=True)
        tea_feat = tea_feat.reshape(
            tea_feat.shape[0], tea_feat.shape[1], -1).permute(0, 2, 1)
        tea_feat_list.append(tea_feat)

    tea_feat_avg = torch.stack(tea_feat_list).mean(dim=0)   # 插值 + 平均
    loss = self.criterion(stu_feat, tea_feat_avg)
    return loss

这段代码把第 2 节的探路结论落成了工程。self.tea_img_sizes 来自配置里的 [512, 1024],循环里对每个尺度先把输入图 resize 到目标大小、再调 _infer_teacher 做切图推理,拿回的特征统一插值到 32×32,这样两个尺度的特征形状完全一致。最后 torch.stack(...).mean(dim=0) 就是「插值+平均」——注意它平均的是已经对齐到同一形状的特征,所以输出维度和学生一模一样,这正是省掉投影头的关键。

3.2 训练与推理的关键不对称

这套框架最值得玩味的是它的不对称性:教师看的是多尺度大图,学生看的是单张中等图;教师只在训练时出现,推理时彻底消失。 训练结束后,学生就是最终交付的 SigLIP-HD 编码器,它的结构和 IO 与原始 SigLIP 2 完全相同。这意味着部署时用户不需要改任何切图、多次前向、token 压缩的逻辑,只要把 vision tower 的 checkpoint 路径从 SigLIP 2 换成 SigLIP-HD,就能白嫖到更强的感知能力。这里的关键是,所有额外的多尺度计算都被隔离在训练阶段,推理预算一分钱没多花。

4. 教师分支拆解:切图、推理、拼回、平均

4.1 为什么必须切图而不是直接吃大图

教师要在 1024² 上产出特征,但 SigLIP 2 是在 512² 上预训练的。直接把 1024² 喂进去,要么得插值位置编码(第 2.3 节已证明会掉点),要么就得切图。SigLIP-HD 选了切图:把 1024² 的图按 512² 的窗口不重叠地切成 2×2=4 块,每块单独过一遍教师,再把 4 块特征按空间位置拼回一张完整的高分辨率特征图。这样每一块都落在模型熟悉的 512² 分辨率上,位置编码不失真,代价是一张 1024² 图要跑 4 次前向。这也是作者不加更多尺度的原因——加个 3× 尺度就要多跑 9 次前向,性价比急剧下降。切图前向的次数随尺度倍率平方增长,可以写成:

其中  是相对基础尺度的倍率,。(1024²)要 4 次前向,(1536²)要 9 次,(2048²)要 16 次。这个平方增长就是「多尺度不能无限加」的算力根源——每多加一档尺度,教师的前向开销不是线性而是平方级往上翻,而第 2.2 节又证明了收益早就饱和了,所以两尺度是性价比的甜点区。

4.2 代码透视:_infer_teacher 的切图与拼回

切图-拼回这段逻辑是整个教师分支里最绕的部分,值得逐行看。核心是用 torch.chunk 在高、宽两个维度把大图切成网格小块,摊平成一个大 batch 一次性过教师,再用 reshape + permute 把每块吐出来的 token 按原始空间位置拼回一张完整的高分辨率特征图。绕点全在维度顺序上,下面的注释会标出每一步张量形状怎么变。

# siglip_hd/train/meta_arch.py
@torch.no_grad()
def _infer_teacher(self, img):
    B, H = img.shape[0], img.shape[2]
    assert H % self.tea_base_size == 0
    grid_num = H // self.tea_base_size          # 1024 // 512 = 2

    # 先按高、再按宽切成 grid_num × grid_num 个 512² 小块
    chunks_height = img.chunk(grid_num, dim=2)
    grids = [chunk.chunk(grid_num, dim=3) for chunk in chunks_height]
    grids_flat = [grid for batch_chunk in grids for grid in batch_chunk]
    img_grid = torch.stack(grids_flat, dim=1)
    img_grid = img_grid.reshape(-1, 3, self.tea_base_size, self.tea_base_size)

    # 所有小块一次性过教师(冻结、no_grad)
    tea_feat = (
        self.teacher(img_grid, output_hidden_states=True)
        .hidden_states[self.cfg.teacher.select_layer].detach())

    # 把每块的 token 按原始空间位置拼回完整特征图
    N_len, C = int(math.sqrt(tea_feat.shape[-2])), tea_feat.shape[-1]
    tea_feat = tea_feat.reshape(B, grid_num, grid_num, N_len, N_len, C)
    tea_feat = tea_feat.permute(0, 5, 1, 3, 2, 4)
    tea_feat = tea_feat.reshape(B, C, grid_num * N_len, grid_num * N_len)
    return tea_feat

这段代码的精髓在最后那个 permute(0, 5, 1, 3, 2, 4)。切完之后特征的维度是 (B, 行块, 列块, 块内行, 块内列, C),直接 reshape 会把「块」和「块内」的顺序搞乱,拼出来的特征图是错位的。permute 先把通道 C 提到前面,再交替排列「行块-块内行」和「列块-块内列」,这样 reshape 成 (B, C, grid_num×N_len, grid_num×N_len) 时,空间位置才是连续正确的。这里的关键是整个方法用 @torch.no_grad() 和 .detach() 双重保证教师不参与梯度——教师只是个特征生成器,一分梯度都不该流回去。

难点提示(切图拼回是怎么回事):可以把它想成拼图。1024² 的大图被裁成 2×2 四块拼图,每块单独让「熟悉 512² 的教师」看清楚、记下特征,再按原来的位置摆回桌面拼成完整画面。permute 就是确保你没把左上角的拼图块摆到右下角去。

4.3 工程细节:teacher 与 student 共享同一份预训练权重

一个容易忽略的细节是,MetaArch 初始化时教师和学生都从 cfg.teacher.arch / cfg.student.arch 加载,而配置里两者都是 google/siglip2-so400m-patch16-512。也就是说,教师和学生的起点是同一个模型,区别只在教师被冻结、看多尺度大图,学生可训练、看单张中图。这和传统知识蒸馏很不一样——传统蒸馏往往是大教师教小学生,这里是「同一个模型的多尺度集成版」教「它自己的单尺度版」。作者把这种做法归纳为一句话:不依赖任何外部模型的知识,纯粹释放当前模型自身的潜力。

4.4 小结:教师分支的三步一循环

回头看整个教师分支,逻辑其实收敛成一个稳定的三步循环:切图、推理、拼回,外面再套一层「多尺度取平均」。切图保证每一块都落在模型熟悉的 512² 上、位置编码不失真;推理阶段用 no_grad 加 detach 双重冻结,确保教师只当特征生成器;拼回靠那个精心设计的 permute 把 token 摆回正确空间位置。这三步对每个尺度各跑一遍,再把插值到同一形状的特征沿尺度维度平均,就得到了学生要模仿的目标。这里的关键是,整条链路没有任何可训练参数、没有投影头、没有额外损失,纯粹是「拿现成的模型把大图看仔细,再压成标准分辨率的特征」,这也是它能即插即用的根本原因。

5. 学生分支与对齐损失:最严格的 L1 反而赢了

5.1 特征对齐的三种候选损失

学生要学教师,就得有个「学得像不像」的度量。业界给过好几种选择:EVA 用余弦相似度损失,AM-RADIO 用余弦相似度加 smooth L1 的组合。SigLIP-HD 把这些和最朴素的 L1 损失一起对比,发现三者平均表现非常接近,都超过了 SigLIP 2 基线,但最简单也最严格的 L1 略微胜出,于是选它作最终损失。核心问题在于:余弦相似度只约束方向、不约束模长,smooth L1 在小误差处会变软,而 L1 对每一个 patch、每一个通道的数值偏差都同等严格地惩罚——对「精确复刻教师特征」这个目标而言,越严格越好。把三种损失写在一起对比就一目了然:

余弦损失分母做了归一化、只看方向而丢掉了模长信息,smooth L1 在误差小于 1 时退化成平方项、对小误差的惩罚明显变软,只有 L1 对任意大小的误差都保持线性、同等严格的惩罚。正是这份「不打折扣」让它在特征复刻任务上略胜一筹,消融里 L1 拿到 54.6,比余弦的 54.3 和余弦+smooth L1 的 54.2 都高。

难点提示(三种损失差在哪):想象老师批改抄写作业。余弦损失像只看「字的形状对不对」,不管写得深浅;smooth L1 像对小错睁一只眼闭一只眼,错得离谱才重罚;L1 则是一笔一划对照标准答案,差多少扣多少分。要让学生一模一样地复刻教师,最较真的 L1 反而最合适。

5.2 代码透视:一个极简的 FeatAlignLoss

对应的损失实现短到几乎没有多余逻辑,一个 nn.Module 把 L1、smooth L1、余弦三种损失都收进来,用配置里的 patch_type 字符串切换,默认就是 l1。这种写法的好处是消融实验时只改一行配置就能切换损失函数,不必动模型代码,也方便别人一键复现论文里的三种损失对比。代码越薄,越说明方法的强度来自设计而非工程堆料。

# siglip_hd/loss/feat_align.py
class FeatAlignLoss(nn.Module):
    def __init__(self, patch_type="l1"):
        super().__init__()
        self.patch_type = patch_type

    def forward(self, pred, target):
        # pred / target: (B, N, C)
        target = target.to(pred.dtype)
        if self.patch_type == "l1":
            return F.l1_loss(pred, target)
        elif self.patch_type == "smooth_l1":
            return F.smooth_l1_loss(pred, target)
        elif self.patch_type == "cosine":
            return (1.0 - F.cosine_similarity(pred, target, dim=-1)).mean()
        else:
            raise ValueError(f"Unknown patch_type: {self.patch_type}")

这段代码把 target 先转成和 pred 一样的 dtype,是为了配合 BFloat16 训练——教师在 no_grad 下算出来的特征可能和学生的自动混合精度类型不完全一致。默认走 l1 分支,直接调 F.l1_loss,对 (B, N, C) 三个维度全部求平均绝对误差。这里值得注意,损失是在 patch 级别(N 个 token)逐个算的,不是把整图特征拉平成一个向量比对——patch 级监督才能逼学生把每个空间位置的细节都学到位,这也是它区别于 CLIPSelf 那种区域级粗监督的地方。

5.3 数据预处理:学生图与教师图的分流

一个常被忽略的工程点是,学生和教师吃的其实是同一张原图的两种预处理版本。数据集里用两个 SiglipImageProcessor,一个把图 resize 到学生尺寸 512²,另一个 resize 到教师的最大尺寸 1024²,同一张图同时产出 img_stu 和 img_tea 两个张量送进 batch。这样保证学生和教师看的是像素级完全对应的同一内容,只是分辨率不同,蒸馏对齐才有意义。

# siglip_hd/data/dataset.py
self.transform_stu = SiglipImageProcessor.from_pretrained(
    "google/siglip2-so400m-patch16-512")
self.transform_stu.size = {"height": cfg.student.img.size,
                           "width": cfg.student.img.size}         # 512

tea_max_img_size = max(omegaconf.OmegaConf.to_object(cfg.teacher.img.size))
self.transform_tea = SiglipImageProcessor.from_pretrained(
    "google/siglip2-so400m-patch16-512")
self.transform_tea.size = {"height": tea_max_img_size,
                           "width": tea_max_img_size}             # 1024

def __getitem__(self, idx):
    while True:
        try:
            img = Image.open(self.img_paths[idx]).convert("RGB")
            img = ImageOps.exif_transpose(img)
            img_stu = self.transform_stu.preprocess(img, return_tensors="pt")["pixel_values"][0]
            img_tea = self.transform_tea.preprocess(img, return_tensors="pt")["pixel_values"][0]
            break
        except Exception:
            idx = random.choice(range(len(self.img_paths)))
    return {"img_stu": img_stu, "img_tea": img_tea, "img_path": img_path}

这里的关键是教师图只 resize 到最大尺度 1024²,中间尺度(512²)在 forward 里由 F.interpolate 现场下采样得到,不必在数据侧存多份。那个 while True + 随机换 idx 的写法是针对 4.5M 原始图像里难免的坏图——读失败就随机抓另一张顶上,保证 dataloader 不会因为个别损坏文件卡死。数据是纯图像、无任何标注,这也印证了论文说的「purely exploring cheap raw images for self-improvement」。

6. 训练循环:BFloat16、梯度裁剪、余弦调度

6.1 一个标准但克制的训练 step

训练主循环没有花哨设计,就是取出学生图和教师图、在 BFloat16 autocast 下前向、反向、裁剪梯度、手动更新学习率。之所以要专门拎出来看,是因为里面几个不起眼的细节直接关系到蒸馏能不能稳定收敛——冻结教师、梯度裁剪、手动调度,每一个都在防止不同的坑。

# siglip_hd/train/train.py
for i, sample in enumerate(trainloader):
    if i == cfg.optim.iters_per_epoch:
        break
    optimizer.zero_grad()
    img_stu = sample["img_stu"].cuda()
    img_tea = sample["img_tea"].cuda()

    with torch.autocast(device_type="cuda",
                        dtype=torch.bfloat16 if cfg.bf16 else torch.float32):
        loss = model(img_stu, img_tea)

    loss.backward()
    grad_norm = torch.nn.utils.clip_grad_norm_(
        model.parameters(), max_norm=cfg.optim.grad_clip)   # grad_clip = 3
    optimizer.step()

    iters = epoch * cfg.optim.iters_per_epoch + i + 1
    lr = lr_schedule[iters]
    optimizer.param_groups[0]["lr"] = lr

这里有三个值得点名的工程选择。其一grad_clip=3 的梯度裁剪,蒸馏早期学生和教师特征差距大、loss 梯度可能很陡,裁剪能防止个别 batch 把权重带飞。其二,学习率是手动按 iters 从余弦调度表里取值再写回 param_groups,而不是用 PyTorch 的 scheduler 对象,这样 warmup 和衰减的控制更透明。其三find_unused_parameters=True(在 DDP 包装那行)——因为教师被冻结、不产生梯度,DDP 需要知道有一部分参数不参与反向,否则会报错。

6.2 训练配置:90K 迭代、512 batch、34 小时

把配置和论文对齐看,超参非常克制:AdamW,初始学习率 5e-5,weight decay 0.04,余弦调度带 4K 步 warmup,总 batch size 512。论文说训练 90K 迭代,在 32 张 A100 上用 BFloat16 只需 34 小时。对比第一条路线动辄百万 GPU 小时的从头预训练,这套后训练框架的成本几乎可以忽略。这里的关键是,它不改架构、不加模块、不要标注,就是一个轻量的 post-training 阶段,这正是它宣称「resource-constrained deployment」友好的底气。

工程价值:34 小时 / 32 卡 A100 意味着一个中等规模实验室一两天就能复现。相比之下,从头训一个视觉编码器的门槛是它的成千上万倍。SigLIP-HD 把「提升视觉表征」从一件富人游戏变成了普通研究者能上手的事。

7. 训练目标:一个 patch 级 L1,就这么简单

7.1 损失公式

把前面的代码抽象成数学,目标函数干净得只有一行,这也是这篇论文最想传达的气质:方法本身极其朴素,强度来自「学什么」而不是「用多复杂的损失学」。设学生特征为 ,教师融合特征为 ,两者都是同样形状的三维张量,第一维是 batch、第二维是 patch 数(等于一千零二十四)、第三维是通道数。整个训练就是最小化这两份特征逐元素的平均绝对误差,没有第二项、没有正则项、没有温度也没有权重:

这个教师融合特征不是凭空来的,它由两个尺度的教师特征取平均得到,正好对应第三节代码里把两尺度特征堆叠后沿尺度维度求均值那一步。记基础尺度特征为基础项、把高分辨率尺度的特征下采样之后记为下采样项,融合公式同样只有一行加权平均,而且权重被固定为两者各占一半,既不偏袒细节也不偏袒全局,具体形式如下:

这里  是 512² 图的特征, 是 1024² 图切图拼回的特征, 表示双线性插值到 (即 )。整个训练目标没有任何辅助损失、没有权重系数、没有温度超参,就是让学生的每个 patch 特征在 L1 意义下逼近教师两尺度平均后的特征,简单到几乎没有可调的旋钮,这也是作者刻意追求的「neat、universal、efficient」。

7.2 反直觉现象:1:1 平均,比偏重高分辨率更好

融合权重是个特别容易想歪的地方。直觉上既然目标是提升细粒度感知,是不是该给带细节的高分辨率特征更大权重?作者专门做了消融来回答:把基础尺度和高分辨率的比例从 1:1 调到 1:2(偏重高分辨率)或 2:1(偏重基础尺度),结果两个都不如 1:1 平均,等权重在几乎所有 benchmark 上都是最优。

最优

让人意外的是,即便在 DocVQA 这种极度依赖细节的 benchmark 上,加大高分辨率权重依然掉点(DocVQA 从 34.7 跌到 32.6)。这里要厘清的是,它证明了基础尺度图在捕捉全局内容上不可替代——高分辨率给细节,基础尺度给全局,两者等权重才能既看清字又不丢上下文。第 2.2 节里 MMBench 不喜欢细粒度特征的现象,在这里得到了呼应。

8. 兼容模式:既能替换标准分辨率,也能挂上 AnyRes

8.1 与现有 MLLM 流水线的兼容闭环

作者对方法的适用边界很坦诚:SigLIP-HD 不打算完全取代原生分辨率路线,因为有些视觉细节在下采样后确实彻底丢了。但它的好处是与现有实践完全兼容——无论你用下采样分辨率还是原生分辨率,底层都依赖一个预训练视觉编码器,把这个编码器换成 SigLIP-HD 即可。这就形成一个很干净的闭环:训练时用多尺度蒸馏把知识压进标准分辨率特征,部署时按现有流水线原样使用,一行 checkpoint 路径的修改就完成升级。

8.2 AnyRes 模式下依然拉开差距

具体到 AnyRes(保留原生分辨率)策略,作者从里选最接近的分辨率,最大 token 数设为。结果是:AnyRes 本身已经把基线大幅拔高了,但 SigLIP-HD 还能在这个更强的基线上继续拉开差距,OCR 任务尤其明显——ChartQA 从 63.9 再提到 67.4(+3.5)。这意味着由细到粗的监督学到的东西,和 AnyRes 带来的分辨率增益是正交的、可叠加的,而不是互相替代。换句话说,即使你已经在用原生分辨率,换上 SigLIP-HD 仍然有额外收益。

9. 总结

SigLIP-HD 的核心贡献不是再造一个视觉编码器,而是证明了在标准分辨率下,通过让中等图特征模仿多尺度大图特征,就能免费换到细粒度感知能力。DocVQA +3.6、ChartQA +3.6、HRBench +4.8,且推理成本一分未涨、只需改一行 checkpoint 路径、34 小时就能训完——这三点合起来,足以让「分辨率蒸馏」进入下一代 MLLM 视觉编码器的候选清单。

相关推荐