Generation · Code Reading · Efficient Inference
FastVAR in Infinity: What the Code Actually ChangesFastVAR 在 Infinity 代码里到底改了什么
arXiv · PDF · FastVAR Code · Paper Note
This note looks at the FastVAR implementation on top of Infinity rather than only the paper algorithm. The short version is: FastVAR does not train a new Infinity model. It swaps the text-to-image Transformer block, wraps self-attention, cross-attention, and FFN with token pruning and cached restoration, and skips the two largest scales in the Infinity inference loop.
The relevant code sits in FastVAR/Infinity/infinity/models. Compared with the original Infinity/infinity/models, two new files matter most: fastvar_basic.py and fastvar_utils.py. The first replaces the block implementation; the second implements pivotal token selection and cache-based restoration.
Where the Hook Is
- Original Infinity uses
CrossAttnBlock. In the baseline, each text-to-image block runs full self-attention, full cross-attention, and full FFN on the complete token map. - FastVAR imports
FastVARCrossAttnBlock. The FastVAR version changes the model constructor so text-to-image blocks come fromfastvar_basic.pyinstead of the originalbasic.py. - The weights remain compatible. The module names still contain the same attention, cross-attention, FFN, normalization, and adaptive modulation pieces, so this is a post-training inference-time change rather than a retraining recipe.
- The inference loop also changes. In
autoregressive_infer_cfg, the FastVAR version directly skips scales whose width is 48 or 64, implementing the paper's aggressive 100% pruning for the last two Infinity scales.
Token Selection
The selection rule is implemented in masked_previous_scale_cache. Given the current token map cur_x, the code reshapes it back to a spatial grid, applies global average pooling, and treats this average token as a low-frequency proxy.
Then it computes sum((cur_x - mean_x) ** 2) for every token and sorts scores descending. Tokens far from the global average are treated as high-frequency or detail-heavy tokens, which corresponds to edges, texture, object boundaries, and local changes.
The merge closure uses torch.gather to keep only selected token indices. That reduced sequence is what enters the expensive Transformer operations.
Where Pruning Is Enabled
- Only selected resolutions are pruned.
compute_mergecurrently checks whether the current width is in[32, 40]. - The ratios are hard-coded. Width 32 uses pruning ratio 0.4; width 40 uses 0.5. In other words, 60% or 50% of tokens are kept for those scales.
- Only later layers are pruned.
FastVARCrossAttnBlockenables pruning whenlayer_idxis inrange(3, 28). Early layers and very late layers stay intact. - Other scales use identity functions. When pruning is not enabled,
compute_mergereturnsdo_nothingfor merge, unmerge, and index lookup, so the block behaves like the original path.
Cache Restoration
After a module runs on the reduced token set, FastVAR must restore a full 2D token grid. Otherwise the next layer, the VAE-side scale transition, and the image lattice would no longer match the expected shape.
The unmerge closure takes a cached token map from an earlier scale, reshapes it to B x C x H x W, interpolates it to the current resolution with area mode, flattens it back, and scatters the newly computed selected tokens into their original positions.
In this implementation, each block keeps three caches: previous_scale_cache_self_attn, previous_scale_cache_cross_attn, and previous_scale_cache_ffn. The cache source is fixed to 24 x 24 via cached_size = [24, 24]. When the current sequence length equals 24 * 24, the block saves the outputs for later restoration.
Applied to All Three Submodules
The important engineering detail is that FastVAR does not only prune self-attention. Inside FastVARCrossAttnBlock.forward, the same pattern is applied three times:
- Self-attention. Normalize the gathered tokens, run self-attention, then unmerge with the self-attention cache.
- Cross-attention. Gather the current visual tokens before text cross-attention, run cross-attention on fewer visual queries, then restore with the cross-attention cache.
- FFN. Gather again before the feed-forward network, run the MLP on fewer tokens, then restore with the FFN cache.
This is why the speedup is not limited to attention. FFN cost also drops because the dense MLP is evaluated on a shorter sequence.
RoPE Has to Follow the Pruned Indices
One easy-to-miss detail is positional encoding. If the visual sequence is gathered, the rotary position cache must be gathered with the same indices. Otherwise the selected tokens would carry the wrong 2D positions.
FastVAR introduces apply_rotary_emb_fastvar. It slices the original RoPE cache for the current scale and, when a pruning index function is active, gathers RoPE entries with the selected token indices before applying rotation to Q and K.
Last Two Scales Are Skipped
The most aggressive part is in the autoregressive loop. For Infinity, when the current scale width is 48 or 64, the FastVAR code executes continue. That means the Transformer does not run for those scales at all.
This matches the paper's Infinity schedule {40%, 50%, 100%, 100%}. The 32 and 40 scales are partially pruned inside the block. The 48 and 64 scales are treated as fully pruned, relying on accumulated VAE latent interpolation rather than another Transformer refinement step.
Why It Speeds Up
- Sequence length becomes smaller at costly scales. Attention and FFN no longer process all tokens at width 32 and 40.
- KV cache grows more slowly. Self-attention caches fewer current-scale keys and values when pruning is active.
- The largest scales are removed from Transformer compute. Skipping width 48 and 64 avoids exactly the stages that dominate runtime and memory.
- It remains compatible with FlashAttention. Token importance is feature-based rather than attention-map-based, so FlashAttention can stay enabled.
My Take
The code is more direct than the paper description suggests. It is not an adaptive system that learns a schedule online. It is a small set of concrete inference-time decisions: prune widths 32 and 40, cache 24 x 24 layer outputs, prune only middle layers, and skip widths 48 and 64.
That directness is also the strength. FastVAR uses the coarse-to-fine structure of Infinity as an engineering prior: preserve early structure, spend compute only on high-frequency late tokens, and let cache interpolation fill stable low-frequency regions. The method is therefore easy to graft onto an existing Infinity checkpoint, but the exact scale list, cache scale, and ratios are backbone-specific knobs rather than universal constants.
这篇笔记看的是 FastVAR 在 Infinity 上的代码实现,而不只是论文算法。简短结论是:FastVAR 没有重新训练一个 Infinity,也没有换 VAE 或文本编码器。它主要替换了文生图 Transformer block,在 self-attention、cross-attention 和 FFN 前后包上 token 剪枝与 cache 恢复,并且在 Infinity 推理循环里直接跳过最后两个最大尺度。
相关代码集中在 FastVAR/Infinity/infinity/models。和原始 Infinity/infinity/models 对比,最关键的是新增的 fastvar_basic.py 和 fastvar_utils.py。前者替换 block 实现,后者实现关键 token 选择和基于 cache 的恢复。
入口改在哪里
- 原始 Infinity 使用
CrossAttnBlock。 baseline 里每个文生图 block 都对完整 token map 跑 self-attention、cross-attention 和 FFN。 - FastVAR 引入
FastVARCrossAttnBlock。 FastVAR 版本在模型构造阶段把文生图 block 换成fastvar_basic.py里的实现,而不是原始basic.py。 - 权重仍然兼容。 模块里仍保留 attention、cross-attention、FFN、norm 和 adaptive modulation 等同类组件,所以这是训练后推理路径改造,而不是重新训练。
- 推理循环也被改了。 在
autoregressive_infer_cfg里,FastVAR 版本对宽度为 48 或 64 的尺度直接continue,对应论文里 Infinity 最后两个尺度 100% pruning 的设置。
如何选择 token
token 选择逻辑在 masked_previous_scale_cache。给定当前 token map cur_x 后,代码先把序列 reshape 回空间网格,再做 global average pooling,把平均 token 当成低频近似。
接着对每个 token 计算 sum((cur_x - mean_x) ** 2) 并按分数降序排序。离全局平均越远的 token,被认为越像高频细节 token,比如边缘、纹理、物体边界和局部变化区域。
merge 这个闭包用 torch.gather 只保留选中的 token。后续昂贵的 Transformer 运算只看到这段更短的序列。
在哪些地方启用剪枝
- 只剪特定分辨率。
compute_merge当前只检查宽度是否在[32, 40]中。 - 剪枝比例是硬编码。 宽度 32 的 pruning ratio 是 0.4,宽度 40 是 0.5。也就是这两个尺度分别保留约 60% 和 50% token。
- 只剪中后段层。
FastVARCrossAttnBlock里只有当layer_idx属于range(3, 28)时才启用剪枝。最前面几层和最后几层保持完整。 - 其他情况走 identity。 不满足剪枝条件时,
compute_merge返回do_nothing,所以 block 行为退化为原始全量路径。
如何用 cache 补回去
一个模块在短序列上跑完之后,FastVAR 必须恢复完整二维 token 网格。否则下一层、VAE 侧尺度转换和图像 lattice 都会对不上。
unmerge 闭包会拿到早期尺度的 cache,把它 reshape 成 B x C x H x W,用 area 插值到当前尺度,再 flatten 回序列,最后把刚刚真正计算过的 token 用 scatter_ 放回原来的位置。
在这个实现里,每个 block 维护三份 cache:previous_scale_cache_self_attn、previous_scale_cache_cross_attn 和 previous_scale_cache_ffn。cache 来源固定为 24 x 24,由 cached_size = [24, 24] 指定。当当前序列长度等于 24 * 24 时,block 会保存对应输出供后续恢复使用。
三类子模块都被剪了
一个重要工程细节是,FastVAR 不只剪 self-attention。在 FastVARCrossAttnBlock.forward 里,同样的 merge -> forward -> unmerge 模式被用了三次:
- Self-attention。 先对 gather 后的 token 做 norm 和 self-attention,再用 self-attention cache 还原。
- Cross-attention。 视觉 token 先 gather,作为更少的 query 去和文本 token 做 cross-attention,然后用 cross-attention cache 还原。
- FFN。 进入 MLP 前再次 gather,只在更短序列上跑 FFN,最后用 FFN cache 还原。
所以 FastVAR 的收益不只来自 attention。FFN 也因为序列变短而少算了一大块 dense MLP。
RoPE 也要跟着索引走
一个容易漏掉的点是位置编码。如果视觉序列被 gather 了,rotary position cache 也必须用同一组 index gather。否则被选中的 token 会带错二维位置。
FastVAR 新增了 apply_rotary_emb_fastvar。它先切出当前尺度的原始 RoPE cache;当剪枝 index 函数有效时,再用选中的 token index 去 gather RoPE 条目,然后再把 rotary embedding 应用到 Q 和 K 上。
最后两个尺度直接跳过
最激进的部分在 autoregressive 推理循环里。对于 Infinity,当当前尺度宽度是 48 或 64 时,FastVAR 代码直接执行 continue,也就是这些尺度完全不跑 Transformer。
这对应论文里的 Infinity schedule {40%, 50%, 100%, 100%}。32 和 40 尺度在 block 内部分剪枝;48 和 64 尺度则视为完全剪枝,依赖已经累积的 VAE latent 插值,而不是再做一次 Transformer refinement。
为什么会快
- 昂贵尺度的序列变短。 宽度 32 和 40 时,attention 和 FFN 不再处理所有 token。
- KV cache 增长变慢。 剪枝启用时,自注意力只为保留的当前尺度 token 追加 key/value。
- 最大尺度被移除出 Transformer 计算。 跳过宽度 48 和 64,避开了最耗时、最吃显存的阶段。
- 仍然兼容 FlashAttention。 token 重要性来自 feature 分数,而不是 attention map,所以不影响 FlashAttention 的使用。
我的理解
代码比论文描述更直接。它不是一个在线学习 schedule 的自适应系统,而是一组明确的推理时决策:剪 32 和 40,缓存 24 x 24 的层输出,只剪中间层,跳过 48 和 64。
这种直接性也是它的优点。FastVAR 把 Infinity 的 coarse-to-fine 生成结构当成工程先验:早期结构不省,后期只把计算花在高频 token 上,稳定的低频区域用 cache 插值补。它很容易接到已有 Infinity checkpoint 上,但具体剪哪些尺度、用哪个 cache 尺度、剪多少比例,都是和 backbone 绑定的工程旋钮,而不是放之四海皆准的常数。