HybridToken-VLM: Hybrid Token Compression for Vision-Language Models

Paper: arXiv:2512.08240 Code: jushengzhang/HybridToken-VLM Code reference: main @ 29486cb (2026-03-15) Authors: Jusheng Zhang, Xiaoyang Guo, Kaitong Cai, Qinhan Lv, Yijia Fan, Wenhao Chai, Jian Wang, Keze Wang Affiliations: Sun Yat-sen University, Princeton University, Snap Inc. Year: 2025

1. Motivation (研究动机)

1.1 背景:VLM 视觉 token 的计算瓶颈

现代 VLM (如 LLaVA) 使用 ViT-L/14 将图像编码为 个 patch token,输入 LLM 后注意力计算复杂度为 ,对于 ,单层即需约 浮点运算,迅速耗尽 GPU 显存。

1.2 现有方案的两难困境

压缩范式代表方法核心限制
连续压缩 (Pooling/Aggregation)VoCo-LLaMA, Q-Former语义稀释 (Semantic Dilution): ,高频纹理噪声主导熵 ,淹没离散语义
离散量化 (VQ Codebook)VQ-VAE, MoVQ粒度间隙 (Granularity Gap): $I(z_d; D) \le \log
结构化剪枝 (Token Pruning)ToMe, FastV, PDrop结构坍塌 (Structure Collapse): 去除 token 破坏拓扑先验,低 token 区间性能骤降

核心洞察: 单 token 连续瓶颈无法同时编码离散语义 和连续细节 ——这是一个表征容量冲突 (Capacity Conflict),而非简单的信息丢失问题。

2. Idea (核心思想)

在压缩前注入少量离散语义锚点,使高层语义在瓶颈前就被显式保留,连续通道则专注于细粒度细节,两者通过解纠缠注意力掩码融合为单个混合 latent。

Figure 1 解读: 对比三种压缩策略。(a) VoCo-LLaMA 将 576 个 patch 直接压缩为 1 个 <voco> token,丢失语义结构;(b) HTC-VLM 在 576 个连续 patch 前添加 4 个 MGVQ 离散语义 token,形成 580 token 混合序列,再通过 Disentanglement Mask 压缩为 1 个 <voco> token,同时保留语义和视觉细节。

3. Method (方法)

3.1 整体架构

Figure 2 解读: HTC-VLM 完整架构对比。(a) Pooling 方法直接平均/池化视觉 embedding,丢失空间信息;(b) VoCo-LLaMA 用 Vision Encoder + Media Projector 压缩为单 token;(c) HTC-VLM (本文) 包含双通道:连续通道 (CLIP ViT-L → Linear Projector → 576 patch embeddings) 和离散通道 (MGVQ → 2-layer MLP → 4 discrete tokens),拼接后经 Disentanglement Attention Mask 压缩为 <voco> token,最终输入 LLaVA 进行推理。

3.2 双通道设计

连续通道:细粒度细节

: CLIP ViT-L/14 (frozen) : 线性投影 ,对齐 LLM embedding 空间 保留高维流形上的纹理梯度、姿态变化等连续特征

离散通道:高层语义锚点

: MGVQ 量化器 (Multi-Group VQ), codebook, groups, 16x 下采样 : 量化特征 : 论文 Table 6 / Eq. 4 写作 2-layer MLP (, GELU 激活) released code 中的 LlavaLlamaForCausalLM.projector 实际为 14112 -> 16384 -> 16384,再 reshape 为 输出 : 4 个离散语义 token / semantic anchors

论文公式与 released code 实现差异:论文文字把离散 projector 记为 main@29486cbllava/model/language_model/llava_llama_1stg.py 实际创建 nn.Sequential(nn.Linear(14112, 16384), nn.GELU(), nn.Linear(16384, 16384)),而 llava/model/llava_arch.py 将输出 reshape 为 4 个 4096 维 discrete embeddings。笔记后续伪代码以 released code 为准,同时保留论文表格中的配置以便对照。

3.3 融合与 Disentanglement Bottleneck

混合序列构建

关键设计:离散 token 放在连续 patch 前面 (Pre-fusion),利用 autoregressive transformer 的 “Prompting Effect”,使语义锚点作为后续 patch 处理的语义上下文。

Disentanglement Attention Mask

三个关键约束:

  1. 视觉 token 互相独立: 内部 token 之间 时注意力为 ,防止 oversmoothing
  2. 信息瓶颈强制: 文本 token 不能直接 attend 到原始视觉 patch,只能通过 <voco> 获取视觉信息
  3. Star-Graph 拓扑: 信息流仅从 单独流向 <voco>,形成星形图结构

<voco> Latent 提取

最终 transformer 层对应 <voco> 位置的隐状态即为压缩后的混合 latent

3.4 训练目标

基于变分推断框架,训练目标可分解为 ELBO:

实际训练 loss:

近似展开为:

梯度动力学: 增强语义聚类 (最大化 ), 细化细节表征空间。

3.5 伪代码(基于 released code)

Code reference: main @ 29486cb (2026-03-15) — pseudocode and mapping based on this commit

3.5.1 MGVQ 离散语义通道

class MGVQQuantizer(nn.Module):
    def __init__(self, ckpt="./llava/model/MGVQ/checkpoints/mgvq_f16c32_g8.pt"):
        super().__init__()
        self.model = MGVQ_HF(
            Namespace(
                vq_model="mgvq-f16c32",
                vq_ckpt=ckpt,
                codebook_size=16384,
                codebook_dim=32,
                codebook_groups=8,
                groups_to_use=8,
                ds_rate=16,
            )
        )
        state = torch.load(ckpt, map_location="cpu")
        self.model.load_state_dict(state["model"], strict=True)
        self.model.eval().requires_grad_(False)
 
    def forward(self, image_tensor):
        # released code receives CLIP-normalized images, denormalizes to [0, 1],
        # then maps to [-1, 1] before MGVQ encode.
        x = imagenet_denorm(image_tensor)
        x = x * 2.0 - 1.0
        quant, codebook_usage, _ = self.model.encode(x)
        # quant: [B, C, H', W'] -> [B, 1, 14112]
        quant = quant.permute(0, 2, 3, 1).view(image_tensor.shape[0], 1, -1)
        return quant.contiguous().to(device=image_tensor.device, dtype=image_tensor.dtype)

这段对应 llava/model/MGVQQuantizer.py:MGVQ checkpoint 被严格加载,模型设为 eval 且冻结,因此离散语义通道不是 end-to-end 更新 codebook,而是把外部 MGVQ tokenizer 当作固定 semantic anchor generator。

3.5.2 离散 projector 与连续 patch 拼接

class LlavaLlamaForCausalLM(...):
    def __init__(self, config):
        self.image_quantizer = None
        self.projector = nn.Sequential(
            nn.Linear(14112, 16384),
            nn.GELU(),
            nn.Linear(16384, 16384),
        )
 
def build_hybrid_image_features(model, images):
    # Continuous branch: LLaVA vision tower + mm_projector -> [576, 4096]
    image_features = model.encode_images(images)
 
    # Discrete branch: frozen MGVQ + projector -> [B, 1, 16384] -> [B, 4, 4096]
    discrete_embeds = model.projector(model.image_quantizer(images))
    discrete_embeds = discrete_embeds.view(
        discrete_embeds.size(0),
        -1,
        image_features[0].size(-1),
    )
 
    # Pre-fusion: 4 discrete semantic anchors are prepended before 576 continuous patches.
    hybrid_features = []
    for idx, continuous_patches in enumerate(image_features):
        semantic_anchors = discrete_embeds[idx]
        hybrid_features.append(torch.cat([semantic_anchors, continuous_patches], dim=0))
    return hybrid_features, discrete_embeds.shape[1]

这段对应 llava/model/llava_arch.pyimage_features = self.encode_images(images) 产生连续 patch;discrete_embeds = self.projector(self.image_quantizer(images)) 产生离散 anchor;torch.cat([de, cf], dim=0) 实现 pre-fusion。这里的 discrete_id_nums 是 4,最终形成 580 个视觉 token。

3.5.3 <voco> token 注入与训练样本构造

def preprocess_multimodal_conversation(conversation, tokenizer, has_image=True):
    if has_image:
        # released code fixes the number of <voco> tokens to 1.
        maybe_voco_str = "".join(["<voco>" for _ in range(1)])
        prompt = "<image>\n" + maybe_voco_str + "\n" + (
            conversation.replace("<image>", "").replace("\n", "")
        )
        input_ids = tokenizer_image_token(prompt, tokenizer, return_tensors="pt")
        return input_ids
    return tokenizer(conversation, return_tensors="pt").input_ids
 
def attach_quantizer_and_train(model, tokenizer, training_args):
    tokenizer.add_special_tokens({"additional_special_tokens": ["<voco>"]})
    model.resize_token_embeddings(len(tokenizer))
    voco_token = tokenizer.additional_special_tokens_ids[-1]
 
    model.image_quantizer = MGVQQuantizer()
    model.image_quantizer.model.to(device=model.device, dtype=model.dtype)
    freeze_quantizer(model, enable=True)
    return voco_token

这段对应 llava/train/train_compress.py:训练 preprocessing 明确把 <image>\n<voco>\n 放在对话开头;训练函数添加 <voco> special token、resize embedding、挂载 MGVQQuantizer() 并冻结 image_quantizer.* 参数。

3.5.4 Star-Graph / Disentanglement mask

def reverse_cumsum(x):
    return x + torch.sum(x, dim=-1, keepdims=True) - torch.cumsum(x, dim=-1)
 
def make_mask_post_last_voco(inputs, voco_token):
    return (reverse_cumsum(inputs == voco_token) >= 1)
 
def make_mask_pre_first_voco(inputs, voco_token):
    return ((inputs == voco_token).cumsum(-1) >= 1)
 
def make_voco_mask_llava(inputs, voco_token):
    pre_voco_mask = make_mask_post_last_voco(inputs, voco_token)[:, None, None]
    post_voco_mask = make_mask_pre_first_voco(inputs, voco_token)[:, None, None]
    pre_voco_time_mask = pre_voco_mask.permute((0, 1, 3, 2))
    mask = torch.where(pre_voco_time_mask, pre_voco_mask, post_voco_mask)
    has_voco = (inputs == voco_token).any(-1)[:, None, None, None]
    return torch.where(has_voco, mask, True)

这段对应 llava/model/language_model/llava_llama_1stg.py:代码先在 prepare_inputs_labels_for_multimodal 里记录 <voco> 相对序列尾部的位置,再在 LLaMA forward 中把这些位置标成 32000 并调用 make_voco_mask_llava。实现层面不是单独构造论文 Algorithm 3 那样的显式矩阵,而是在 4D attention mask 上叠加一层 voco-specific mask。

3.5.5 Code-to-paper mapping

Paper ConceptReleased source fileKey symbol / behavior
连续视觉通道 llava/model/llava_arch.pyencode_images(images) + mm_projector(image_features)
MGVQ 离散 tokenizer llava/model/MGVQQuantizer.pyMGVQQuantizer.forward, self.model.encode(x), checkpoint mgvq_f16c32_g8.pt
离散 projector llava/model/language_model/llava_llama_1stg.pyself.projector = nn.Sequential(nn.Linear(14112,16384), GELU, nn.Linear(16384,16384))
Pre-fusion [v_d; V]llava/model/llava_arch.pynew_cf = torch.cat([de, cf], dim=0)
<voco> special tokenllava/train/train_compress.pytokenizer.add_special_tokens({"additional_special_tokens": ["<voco>"]})
对话模板中插入 <voco>llava/train/train_compress.pyconversations = [f"<image>\\n{maybe_voco_str}\\n" + ...]
voco attention maskllava/model/language_model/llava_llama_1stg.pymake_voco_mask_llava, attention_mask = attention_mask + attention_mask_voco
训练入口llava/train/train.pyimports train_compress and calls train_compress(attn_implementation="sdpa")
训练 launcherscripts/finetune_voco_llama.sh8×A100-style deepspeed include, Vicuna-7B, CLIP ViT-L/14-336

3.6 信息论分析

Theorem 1 (Semantic-Detail Capacity Conflict): 对于纯连续压缩,当 有界时, as increases,因为高频细节 () 的 Entropy Domination 饱和了有限容量。

Theorem 2 (Disentangled Sufficiency): 混合先验 降低了 ,使得 的连续容量可专用于编码细节 :

混合 latent 作为联合分布 的充分统计量。

4. Experimental Setup (实验设置)

4.1 训练配置:论文表格 vs released launcher

训练配置必须区分论文 Table 6 与 released code。论文给的是实验配置摘要;仓库里真正可执行的 launcher 是 scripts/finetune_voco_llama.sh,并且 README 里写的 bash scripts/finetune.sh 与仓库当前文件名不一致。

项目论文 Table 6released code / launcher
Vision encoderCLIP-ViT-L/14-336px, frozen--vision_tower openai/clip-vit-large-patch14-336
Continuous projectorLinear 1024 -> 4096llava/model/llava_arch.pymm_projector,launcher 用 --mm_projector_type mlp2x_gelu
Discrete quantizerMGVQ, K=16384, G=8MGVQQuantizer(vq_ckpt="./llava/model/MGVQ/checkpoints/mgvq_f16c32_g8.pt")
Discrete projector论文写 14112 -> 8192 -> 4096code 写 14112 -> 16384 -> 16384 后 reshape 成 4 x 4096
LLM backboneLLaVA-1.5 / Vicuna-7B-v1.5--model_name_or_path lmsys/vicuna-7b-v1.5
视觉序列长度4 + 576 = 580 visual tokenstorch.cat([de, cf], dim=0)<voco> 作为额外 special token
训练数据visual instruction tuning--data_path ./playground/data/llava_v1_5_mix665k.json
图像根目录COCO/GQA/OCR-VQA/TextVQA/VG--image_folder ./playground/data
GPU8× NVIDIA A100 80GBdeepspeed --include localhost:0,1,2,3,4,5,6,7
Epochs未在 Table 6 明说--num_train_epochs 2
Per-device train batchTable 6 给 global batch 256--per_device_train_batch_size 8
Gradient accumulationTable 6 写 1launcher 写 --gradient_accumulation_steps 2
Learning rateTable 6 写 2e-4 cosinelauncher 写 --learning_rate 2e-5 + --lr_scheduler_type cosine
Weight decayTable 6 写 0.1launcher 写 --weight_decay 0.
WarmupTable 6 写 1,500 stepslauncher 写 --warmup_ratio 0.03
Precisionbfloat16--bf16 True, --tf32 True
Context length未在 Table 6 明说--model_max_length 2048
Save strategy未在 Table 6 明说--save_strategy steps, --save_steps 50000, --save_total_limit 1

论文公式与 released code 实现差异:训练超参也存在 paper-vs-code gap。若复现实验,应优先以 scripts/finetune_voco_llama.sh 为准记录实际运行命令;若引用论文报告的 2e-4 / global batch 256 / weight decay 0.1 / warmup 1500,需要明确它来自论文 Table 6,而不是当前 released launcher。

4.2 数据集与评测协议

训练侧 README 要求准备的视觉数据源包括: COCO train2017 GQA images OCR-VQA images TextVQA train_val_images VisualGenome VG_100KVG_100K_2 released launcher 使用 LLaVA instruction mix: ./playground/data/llava_v1_5_mix665k.json 该文件名表明 instruction tuning mix 规模为 665k;note 只把它作为 released code 的数据路径,不额外推断各子集精确样本数。 主实验评测覆盖 7 个 visual understanding benchmark: GQA:视觉问答 / compositional reasoning,报告 accuracy。 VQAv2:通用视觉问答,报告 accuracy。 MMBench:综合多模态能力,报告 benchmark score。 MME:perception/cognition 综合分数,数值量纲与 accuracy 不同。 POPE:hallucination / object existence 判断。 SEED-Bench:多模态理解综合 benchmark。 ScienceQA-Image:图像条件科学问答。

论文还补充使用: MME 的 16 个 test samples 做 <voco> attention heatmap。 S-10 / D-10 probing tasks 检查 z_vocov_d\bar{V} 的语义与细节可解码性。 GQA/VQAv2 token-budget sweep 比较 1→576 tokens 下的 retention 曲线。

4.3 Baselines 与指标

Baseline / variant压缩范式作用
Upper Bound576 visual tokens不压缩 LLaVA-style full visual tokens
Lower Boundno image / weak visual inputretention 归一化的下界
Q-Formerlearnable query aggregation连续 latent 聚合代表
Average Poolingpooling最简单 continuous compression
Token Merging / ToMemerging / pruning结构化 token reduction
FastV / PDroppruning / dropping低预算 token selection
SparseVLMsparse visual token selection中等 token budget baseline
VoCo-LLaMAone-token continuous <voco>最关键的 continuous-only 1-token 对照
Discrete-onlyMGVQ discrete tokens only测试纯离散量化是否损失细节
HTC-VLMhybrid latent4 discrete semantic anchors + 576 continuous patches + 1 <voco>

平均 retention 的定义是 per-benchmark 归一化:

论文报告的 Avg. 是跨 benchmark retention 的平均值;因此 MME 这类非百分制分数不会直接与 GQA/VQAv2 accuracy 简单求均值。

5. Experimental Results (实验结果)

5.1 主实验:单 token 压缩 (580:1)

模型TokensGQAVQAv2MMBenchMMEPOPESEEDSQAAvg(%)
Upper Bound57661.177.764.01487.285.057.966.5100%
Q-Former151.163.451.71079.777.347.262.757.2%
Avg. Pool152.965.055.51210.379.150.362.264.1%
VoCo-LLaMA157.471.857.91241.481.548.866.381.0%
HTC-VLM (ours)1 hybrid57.672.460.01265.282.849.867.787.2%
Lower Bound137.741.222.3617.353.936.960.70%

核心发现: HTC-VLM 在所有 7 个 benchmark 上全面超越 VoCo-LLaMA (+6.2% 平均保持率),尤其在语义密集型任务 (MMBench +2.1, SQA +1.4) 和细节敏感型任务 (POPE +1.3, MME +23.8) 上均有显著提升。

5.2 多 Token Budget 对比

BudgetHTC-VLMVoCo-LLaMASparseVLMFastVToMe
64 tok89.8%78.0%89.3%72.0%71.1%
128 tok93.8%92.2%96.7%85.1%81.9%
192 tok94.2%90.7%99.1%86.4%88.9%

HTC-VLM 在全 token 范围内保持最平滑的性能衰减曲线,尤其在极端压缩 (1-4 token) 区间远超所有基线。

Figure 4 解读: Performance vs. Token Budget 曲线。HTC-VLM (蓝色实线) 在整个 1-576 token 范围内保持最平滑的衰减,尤其在 1-32 token 极端压缩区间仍保持稳定,而 VoCo-LLaMA 在 16 token 以下急剧下降。结构化剪枝方法 (ToMe, FastV, PDrop) 表现为阶梯式跳变,因为它们只支持特定粒度 ({64, 128, 192})。

5.3 注意力分析与语义解耦验证

Figure 3 解读: <voco> token 的注意力热力图对比。左图为 HTC-VLM,前 4 列对应离散语义 token,显示 <voco> 对离散锚点的注意力权重显著高于后续 image patch tokens,验证了离散锚点的语义引导作用。右图为 VoCo-LLaMA,注意力在所有 16 个 image patch 上分布均匀,缺乏语义聚焦。

5.4 表征探测 (Representation Probing)

表征类型Detail 任务 (D-10)Semantic 任务 (S-10)
离散 25.44%20.83%
连续 27.19%26.67%
混合 30.70%26.67%

混合 latent 在两类任务上均取得最优,验证了压缩机制同时保留语义和细节的能力。

5.5 消融实验

Figure (Table 4) 解读: 消融实验总结页面,包含三组关键消融。(1) Hybrid vs. Non-Hybrid: 纯离散 33.3%、纯连续 81.0%、混合 87.2%,证明混合表征的必要性。(2) 离散 token 数量: 最优 (87.2%), 反而下降到 84.6%,说明过多离散 token 引入冗余。(3) 融合策略: Pre-fusion (离散在前) 最优 87.2%,Post-fusion 和 Mean fusion 均为 84.6%。

关键消融结果

消融维度配置Retention
Hybrid vs. Non-HybridDiscrete-Only (441 tok)33.3%
Continuous-Only (576 tok)81.0%
HTC-VLM (4+576 tok)87.2%
离散 token 数 1 / 2 / 4 / 883.9 / 84.9 / 87.2 / 84.6
融合策略Pre-fusion / Post / Mean87.2 / 84.6 / 84.6
Anchor 类型Random Patches / Top-k Attn / K-Means / MGVQ82.5 / 85.1 / 85.8 / 87.2
Mask 拓扑Full Graph / Star Graph85.4 / 87.2

5.6 推理效率

模型Latency (ms)Throughput (img/s)Memory (GB)
Vanilla LLaVA (576 tok)4752.128.3
SparseVLM (64 tok)1655.615.8
VoCo-LLaMA (1 tok)5219.411.1
HTC-VLM (1 tok)5418.711.6

HTC-VLM 延迟仅比 VoCo-LLaMA 多 2ms (MGVQ encoder ~6ms,可与 ViT 并行隐藏) 相比 Vanilla LLaVA 加速 7.9x,相比 SparseVLM 加速 8.8x MGVQ 额外成本可忽略:轻量 CNN,~6ms,远小于 ViT 的 28.5ms

5.7 MGVQ Codebook 消融

配置GQATextVQAAvg. Retention
G=4, K=819255.166.382.4%
G=4, K=1638456.067.484.1%
G=8, K=819256.868.585.2%
G=8, K=16384 (ours)57.669.787.2%
G=8, K=3276857.469.486.9%

最佳配置 ,更大 codebook () 引入训练不稳定,性能反降。

5.8 总结与思考

5.8.1 核心贡献

  1. 表征分析: 首次从信息论角度揭示单 token 连续瓶颈的 “Semantic-Detail Capacity Conflict”,证明纯连续压缩的根本局限性 (Theorem 1)
  2. 混合解纠缠框架: 提出 discrete semantic anchors + continuous detail carriers 的双通道设计,通过 Disentanglement Attention Mask 实现 Star-Graph 拓扑的信息融合
  3. SOTA 单 token 压缩: 580:1 压缩比下 87.2% 性能保持率,全面超越 VoCo-LLaMA (81.0%),且推理效率几乎相同

5.8.2 局限性

仅支持单图: 未探索多图/视频场景,temporal cues 可能与混合 token 设计有交互 外部 VQ tokenizer: MGVQ 是预训练的外部模型,非端到端联合学习,可能限制适应性 固定压缩比: 所有图像统一压缩为 1 token,缺乏自适应机制

5.8.3 启发与延伸

语义锚点思想的通用性: 离散 token 作为 “meta-instruction” 引导压缩的思路可迁移到视频 VLM 的时序压缩——每帧用少量离散 token 锚定关键语义,连续通道保留运动细节 与 KV Cache 压缩的关系: Disentanglement Mask 的 Star-Graph 设计本质上限制了 attention 的有效范围,类似于 StreamingLLM 的 attention sink 机制,可能可以结合 可变压缩率: 结合 token budget 实验 (Table 2),HTC-VLM 从 1 到 576 token 平滑扩展的特性暗示了一个自适应 token 分配策略的可能——根据图像复杂度动态选择压缩率