
Zipformer 面试复习教程为什么 Zipformer 要做多尺度?不同 Stack 到底改变了什么?Downsample / Upsample 为什么不会把信息直接“压没”?Block 为什么比 Conformer 更复杂?Attention 为什么可以复用?CTC / RNN-T / Pruned RNN-T 在哪里接入?Streaming 时真正缓存的是什么?1. 核心概念1.1 Zipformer 是什么Zipformer 是一个面向语音识别的高效 Encoder 架构,最核心的变化可以概括成一句话:不要让整个语音 Encoder 始终在同一个时间分辨率上计算。Conformer 通常经过前端 subsampling 后,在整个 Encoder 中保持相对固定的时间分辨率;Zipformer 则采用类似 U-Net 的多尺度结构,让不同 Encoder Stack 在不同时间分辨率上建模。原始 Zipformer 论文中的典型结构是:输入声学特征 100 Hz │ │ Conv-Embed ↓ 50 Hz │ ├── Stack 1 : 50 Hz │ ↓ ├── Stack 2 : 25 Hz │ ↓ ├── Stack 3 : 12.5 Hz │ ↓ ├── Stack 4 : 6.25 Hz │ ↓ ├── Stack 5 : 12.5 Hz │ ↓ └── Stack 6 : 25 Hz │ ↓ Output 25 Hz原论文明确把这种结构称为 U-Net-like encoder,并指出中间 Stack 工作在更低的 frame rate;同时不同 Stack 可以使用不同 embedding dimension,中间 Stack 通常采用更大的维度。因此 Zipformer 真正重要的不是某几个特殊模块,而是三个思想:Temporal Multi-Scale:不同 Stack 使用不同时间分辨率。Efficient Block:一次计算 Attention Weights,供多个模块复用。Residual / Bypass-based Information Preservation:多尺度压缩后仍通过上采样和可学习融合保留原始高分辨率信息。1.2 先把 Tensor 维度彻底分清楚ASR Encoder 中最重要的一个抽象通常是:X∈RB×T×D X \in \mathbb{R}^{B \times T \times D}X∈RB×T×D其中:BBB:batch sizeTTT:时间帧数量,也就是 sequence lengthDDD:每一帧的 feature / embedding dimension例如:X.shape = [B, T, D] = [16, 250, 384]意味着:16 个 utterance 每个 utterance 有 250 个时间位置 每个时间位置对应 384 维表示这里一定要建立一个非常牢固的认识:T 是“有多少帧”,D 是“每一帧有多少个特征通道”。它们不是同一个概念。例如:t1 → [d1 d2 d3 ... d384] t2 → [d1 d2 d3 ... d384] t3 → [d1 d2 d3 ... d384]可以把它理解成:时间轴 t1 t2 t3 ... tT 每个时间点 │ └── 一个 D 维向量因此 Zipformer 中:Downsample主要改变的是:T ↓而:Feature Projection / convert_num_channels主要改变的是:D这两个维度必须在脑子里分开。2. 为什么需要 Zipformer2.1 Conformer 的问题不是“效果不好”,而是计算预算没有被合理分配Conformer 最大的优势是:Self-Attention 建模全局依赖Convolution 建模局部模式因此非常适合 ASR。但语音存在一个特殊事实:不同层、不同阶段并不一定需要同样密集的时间采样。例如:50 Hz: | | | | | | | | | | | | | | | |这种高时间分辨率适合:较细粒度的声学变化音素边界短时局部变化但是到了 Encoder 的中间层,模型更关心:更长时间范围的上下文音节、词、短语级关系更稳定的语义表示那么继续对每一个细粒度时间位置做完整 Self-Attention,会产生大量重复计算。2.2 Self-Attention 最大的问题是时间复杂度随 T 二次增长Self-Attention 的核心是:Q=XWQ,K=XWK,V=XWV Q=XW_Q,\quad K=XW_K,\quad V=XW_VQ=XWQ,K=XWK,V=XWV然后:A=Softmax(QK⊤dk) A=\operatorname{Softmax}\left(\frac{QK^\top}{\sqrt{d_k}}\right)A=Softmax(dkQK⊤)其中:QK⊤∈RT×T QK^\top \in \mathbb{R}^{T \times T}QK⊤∈RT×T所以 Attention Score Matrix 的规模直接与T2T^2T2相关。可以粗略理解为:AttentionCost∼O(T2D) \text{Attention Cost} \sim O(T^2D)AttentionCost∼O(T2D)因此如果把时间长度降低一半:T→T2 T \rightarrow \frac{T}{2}T→2T那么 Attention 的主要二次项大约变成:T2→T24 T^2 \rightarrow \frac{T^2}{4}T2→4T2也就是说:时间长度减半,不是只省一半计算,而是 Attention 的二次项大约省到四分之一。这就是 Zipformer 为什么要 aggressively downsample 中间 Stack。3. Zipformer 的整体结构3.1 从输入一路追踪 Tensor假设输入是 100 Hz 的声学特征:X0∈RB×T×80 X_0 \in \mathbb{R}^{B \times T \times 80}X0∈RB×T×80例如:16 kHz audio ↓ 80-dim fbank ↓ [B, 1000, 80]经过前端 Conv-Embed 后,原论文典型设计把时间分辨率降低一半:100 Hz ↓ 50 Hz也就是:[B, 1000, 80] ↓ [B, 500, D1]原始论文明确说明 Conv-Embed 将 100Hz 降到 50Hz。之后进入多个 Stack:T ↓ Stack 1 50 Hz 500 Stack 2 25 Hz 250 Stack 3 12.5 Hz 125 Stack 4 6.25 Hz 63 Stack 5 12.5 Hz 125 Stack 6 25 Hz 250注意这里还有第二个变化:T 在变化 D 也可以变化所以更准确地看是:Stack 1: [B, 500, D1] Stack 2: [B, 250, D2] Stack 3: [B, 125, D3] Stack 4: [B, 63, D4] ...其中 Zipformer 的设计倾向于:越到中间 T 越小 D 越大这并不是巧合。3.2 为什么 T 越小,D 反而可以更大?这是 Zipformer 非常值得面试时讲出来的设计思想。假设两个 Stack:Stack A: T = 500 D = 384 Stack B: T = 125 D = 768Stack B 的时间位置只有四分之一。所以虽然每个位置的表示更宽,但是整个 Tensor 中元素数量:Stack A:500×384=192000 500 \times 384 = 192000500×384=192000Stack B:125×768=96000 125 \times 768 = 96000125×768=96000实际上只有原来的一半。更关键的是 Attention:Stack A:O(5002×384) O(500^2 \times 384)O(5002×384)Stack B:O(1252×768) O(125^2 \times 768)O(1252×768)即使 D 增大,T 的平方下降仍然带来了巨大收益。所以 Zipformer 的核心设计逻辑可以概括成:把计算预算从“时间长度”重新分配到“特征表达能力”。也就是:高时间分辨率 ↓ 少一点 feature depth 低时间分辨率 ↓ 多一点 feature depth这也是为什么原论文指出不同 Stack 使用不同 embedding dimension,而中间 Stack 使用更大的维度。4. Multi-Scale 到底是怎么工作的4.1 Downsampling 不是简单x[:, ::2]这是一个非常容易产生错误理解的地方。最简单的下采样当然可以:x=x[:,::2]但这样实际上只是:丢掉一半帧例如:x1 x2 x3 x4 x5 x6 ↓ ↓ ↓ x1 x3 x5信息损失会比较直接。Zipformer 的设计不是简单丢帧,而是对多个时间位置做一个可学习聚合。4.2 最基本的 Attention Downsample以ds=2ds=2ds=2为例:x1 x2 → y1 x3 x4 → y2 x5 x6 → y3最简单的思想是:yi=a1x2i+a2x2i+1 y_i=a_1x_{2i}+a_2x_{2i+1}yi=a1x2i+a2x2i+1并且:a1+a2=1 a_1+a_2=1a1+a2=1通常可以通过 softmax 让权重满足:[a1,a2]=Softmax(q) [a_1,a_2]=\operatorname{Softmax}(q)[a1,a2]=Softmax(q)这样模型就不是机械平均,而是在训练过程中学习:前一个时间位置重要? 后一个时间位置重要?原始论文把它描述为:例如 downsample factor 为 2 时,通过两个可学习 scalar weight 对相邻两帧做加权聚合;之后在低时间分辨率上进行 Zipformer block 建模。4.3 Downsample 的本质假设输入:X∈RT×Din X \in \mathbb{R}^{T \times D_{in}}X∈RT×Din经过 factorsss的 Downsample 后:Xd∈R⌈T/s⌉×Dout X_d \in \mathbb{R}^{\lceil T/s\rceil \times D_{out}}Xd∈R⌈T/s⌉×Dout所以它完成两件事情:时间维: T → T / s 特征维: Din → Dout因此 Downsample 可以同时改变:Temporal Resolution + Feature Dimension这两个变化完全是独立的。5. Upsampling 为什么存在Downsample 后:T = 500变成:T = 250Encoder 在 250 个时间位置上进行了大量计算。但是下一层或者最终输出可能仍然需要对应到:T = 500因此必须把低分辨率结果重新对齐到高分辨率时间轴。最简单的 Upsample 是:y1 → y1 y1 y2 → y2 y2 y3 → y3 y3也就是:Xu[2i]=Xd[i],Xu[2i+1]=Xd[i] X_u[2i]=X_d[i],\quad X_u[2i+1]=X_d[i]Xu[2i]=Xd[i],Xu[2i+1]=Xd[i]这看起来很“粗糙”,但这里有一个关键认识:Upsample 的目的不是恢复被 Downsample 丢掉的原始信息。因为如果真的做了信息压缩:x1 x2 ↓ y单凭一个yyy,理论上就不可能完全恢复x1,x2x_1,x_2x1,x2。所以 Zipformer 的 Upsample 主要任务是:把低时间分辨率表示重新扩展到高分辨率坐标系,然后和高分辨率信息做融合。这也是 Bypass 存在的原因。6. Bypass / Residual:Zipformer 为什么敢大胆下采样假设 Stack 输入:x_orig经过:Downsample ↓ Zipformer Blocks ↓ Upsample ↓ x_low如果直接:output = x_low那么原始高分辨率信息会被大量压缩。Zipformer 采用 Bypass 把两者融合:y=(1−c)⊙x+c⊙z y=(1-c)\odot x+c\odot zy=(1−c)⊙x+c⊙z其中:xxx:Stack 输入zzz:经过 Downsample → Encoder → Upsample 的结果ccc:可学习的 channel-wise scalar⊙\odot⊙:element-wise multiplication原论文明确给出了这个形式,并指出 Bypass 会学习每个 channel 应该保留多少原始输入、注入多少新表示。直观理解:原始高分辨率信息 ──────┐ ├── Bypass → output 低分辨率深层信息 → Upsample ─┘于是一个 Stack 并不是:“把高分辨率信息压缩掉,再恢复回来。”而是:“在低分辨率上做一套便宜而深的计算,同时保留原来的高分辨率路径。”这个区别非常重要。7. Cross-Scale Representation:不同尺度之间如何交流Zipformer 的多尺度不是几个完全独立的 Encoder。整体关系更接近:High Resolution │ ↓ Stack 1 │ ↓ Downsample │ ↓ Stack 2 │ ↓ Downsample │ ↓ Stack 3 │ ↓ ... │ ↑ Upsample │ ↑ Stack 5 │ ↑ Stack 6所以一个 Stack 在低分辨率上学到的信息,会继续传入后续 Stack。同时每一个 downsampled stack 又通过 Bypass 保留自己的高分辨率输入。因此信息实际上有两条路径:高分辨率路径 ─────────────────────────────────────→ 低分辨率路径 ↓ ↓ ↓ ↑ ↑ ↑这就是所谓的:Multi-Scale Representation而不是简单的:多个不同采样率的模型并排运行。8. Feature Dimension 的变化这一部分和前面的 Temporal Resolution 必须分开理解。8.1 D 代表什么在:X∈RB×T×D X \in \mathbb{R}^{B \times T \times D}X