设想你受邀设计一场万人“同好交流大会”的规则。最老实的做法是让所有人都互相打一遍招呼——一万人就是一万乘一万场对话,哪怕每人只说三秒钟,这场大会也要办上一年。标准 Transformer 干的就是这件事:每个 token 都要和序列里的每一个 token 计算注意力分数,序列一长,显存和算力就以平方级速度爆炸。 Reformer 是这场大会的“精明主办方”,它改了两条规则。第一条:入场时每位来宾做一次“兴趣指纹”测试,按你的兴趣方向发一个颜色的手环,同色的人坐在同一个区域。你不需要满场跑,把同区的人聊透就行——这是局部敏感哈希(LSH)注意力。第二条:大厅的留言板只有巴掌大,记不下所有对话,但主办方发现交流过程是“可逆”的,只需记每个人离开时的最终状态,需要细节时倒放录像反推;小吃摊的烤箱也很小,面包只能一炉一炉地烤——这是可逆层与分块前馈。 这篇文章就带你拆解这两个“省”字妙招背后的数学与工程细节,并把它们的代价与适用场景讲明白。

岗位关联

理解 Reformer 的 LSH 注意力与分块可逆层,是掌握长序列 Transformer 显存优化方案的必修课——面试高频考点,工程实战常备。

目录
  1. 可视化演示
  2. 注意力瓶颈
  3. 哈希分桶原理
  4. LSH注意力机制
  5. 可逆层设计
  6. 前馈分块
  7. 复杂度总账
  8. 局限与取舍
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示神经网络前向传播、损失计算和反向传播梯度更新的完整过程,动态演示信号在各层间的正向传递与梯度反向回传。

本节配有交互动效(进入视口后自动加载,约 3KB)。

前向传播 输入信号经权重矩阵线性变换后通过激活函数逐层传递 每层计算:z=Wx+b,a=σ(z),σ为激活函数 输出层产生预测值,用于与真实标签比较计算损失 a^(l) = σ(W^(l) × a^(l-1) + b^(l))

注意力瓶颈

假设大会最初的规则是“见面礼制”: 每位来宾都必须和所有其他来宾握手并自我介绍一次。一万人就是将近五千万次握手,大厅再大也塞不下这种社交强度。Transformer 的 self-attention 正是这个“见面礼制”——每个 token 都要去“认识”序列里的每个其他 token。

在 Transformer 中,每个 token 有“查询向量 Q”和“钥匙向量 K”。注意力机制让每个查询去询问所有钥匙:我们俩匹配吗?匹配分数就是查询和钥匙的内积。所有分数拼成一个 n 行 n 列的矩阵,这就是注意力矩阵。n 是序列长度:BERT 时代的 n=512,矩阵只有 26 万格;但当 n 涨到 8192 时,单层单头就是 6700 万格,而 Transformer 往往有几十层、十几个头,逐层保存激活,显存直接爆炸:

显然,“每人都认识所有人”只有在几百人的小聚会才可行。序列一旦变长,这种平方级的社交成本就不可接受了。Reformer 主办方的第一刀,就是砍掉“全员握手”——用一个巧妙的测试,让大家只和“同频”的人深聊,这就是下一节的 LSH。

Attention(Q,K,V)=softmax(QK⊤dk)V\displaystyle \text{Attention}(Q,K,V)=\text{softmax}\left(\frac{QK^\top}{\sqrt{d_k}}\right)V
通俗说:每个 token 拿着自己的问题去问所有 token 的答案,匹配度越高,最终输出里占的权重越大。
  1. 内积打分查询矩阵 Q 与键矩阵 K 的转置相乘,得到 n×n 的相似度得分矩阵,第 i 行第 j 列表示第 i 个 token 对第 j 个 token 的关注候选强度。
    QK⊤∈Rn×n\displaystyle QK^\top\in\mathbb{R}^{n\times n}
  2. 缩放稳定除以键向量维度 d\_k 的平方根,避免维度变大后点积方差增大、softmax 进入饱和区导致梯度消失。
    QK⊤dk\displaystyle \frac{QK^\top}{\sqrt{d_k}}
  3. 概率归一softmax 把每一行分数变成总和为 1 的注意力权重,相当于“分配给每个 token 的注意力预算”。
    softmax(⋅)\displaystyle \text{softmax}(\cdot)
  4. 加权汇聚用归一化后的权重对值矩阵 V 做加权求和,得到每个 token 的最终输出。
    softmax(⋅) V\displaystyle \text{softmax}(\cdot)\,V

哈希分桶原理

主办方在入口摆了一台“兴趣指纹机”: 每位来宾要回答一组关于兴趣方向的问题,机器据此算出你的“兴趣方向角”,然后发给你对应颜色的手环。同色的人大概率兴趣相近,可以坐在一起深聊。

局部敏感哈希(LSH)就是这台机器的数学化身:它是一族特殊的哈希函数,核心性质是——两个向量越相似,被分到同一个桶的概率越高。这和普通哈希(均匀打散,越像越要分开)正好相反。正是这个性质,让“只算桶内注意力”成为一个合理的近似。

Reformer 用的是“角度 LSH”。把每个 token 的向量想象成高维空间里的一支箭,先乘一个随机旋转矩阵 R,把箭头转到一组随机“指南针”方向上,再比较它在所有方向(正负都算)上的投影大小,取投影最大的那个方向作为桶号:

这个函数妙在:两支箭头方向越接近,投影到各随机方向的大小越相近,取到同一个 argmax 的概率就越高。翻译成大会语言:兴趣方向几乎相同的人,大概率拿到同一个颜色的手环;而方向差异大的人偶尔也会撞组,但概率低得多——这就是“局部敏感”的含义。

h(x)=arg⁡max⁡([xR ; −xR])\displaystyle h(x)=\arg\max\left(\left[xR\ ;\ -xR\right]\right)
通俗说:给每位来宾发一个指南针,你往哪个方向“冲劲”最大,就把你分到那个方向的组。
  1. 随机旋转投影x 是 token 的向量表示,R 是 d×b 的随机矩阵,xR 得到 b 个方向上的投影分量,相当于把高维向量“压扁”到 b 个随机方向上观察。
    xR∈Rb\displaystyle xR\in\mathbb{R}^{b}
  2. 正负方向拼接把 xR 和 -xR 首尾拼接,同时覆盖每个轴的正反两个方向,候选桶数翻倍为 2b。
    [xR ; −xR]∈R2b\displaystyle [xR\ ;\ -xR]\in\mathbb{R}^{2b}
  3. 取最强方向argmax 返回分量最大的下标,这个下标就是桶号 h(x),也就是手环颜色。

LSH注意力机制

拿到手环颜色后,主办方让同色的人坐在同一区域,并且把每个区域再分成固定人数的圆桌。你不需要满场跑,把同桌的人聊透就够了。

LSH 注意力在拿到每个 token 的桶号后做三件事:第一,按桶号排序,让同桶 token 在序列上相邻;第二,把排序后的序列切成固定大小的块;第三,在每一块内部,只允许同桶 token 互相注意。为什么不直接按桶边界切?因为严格按桶切会让块大小参差不齐、还有空桶问题,GPU 并行效率很低;“排序+分块”则让同桶 token 必然相邻,块大小又完全规整。掩码实现如下:

排序之后,同桶的 token 在注意力矩阵上聚成沿对角线的“块”,块外全部被掩码清空——这正是可视化演示里那张块对角矩阵。为了让“聊得来却在不同组”的朋友不丢失,Reformer 还会用多套独立的问题做多轮测试,每轮各自分组、算一遍注意力再取平均,这就是多轮哈希。总复杂度随之从 O(n²) 降到约 O(n×m×n​\_r),其中 m 是桶内平均候选数,n​\_r 是哈希轮数。

AttentionLSH(Q,K,V)=softmax(QK⊤dk+M)V,Mij={0,h(qi)=h(kj)−∞,否则\displaystyle \text{Attention}_{\text{LSH}}(Q,K,V)=\text{softmax}\left(\frac{QK^\top}{\sqrt{d_k}}+M\right)V,\quad M_{ij}=\begin{cases}0,& h(q_i)=h(k_j)\\ -\infty,& \text{否则}\end{cases}
通俗说:在注意力分数表上,不同手环颜色的格子直接盖上“作废”章,softmax 之后它们对输出没有任何贡献。
  1. 全局打分仍然先算所有 token 对的相似度得分,保证数学上还是标准注意力的骨架。
    QK⊤dk\displaystyle \frac{QK^\top}{\sqrt{d_k}}
  2. 掩码矩阵M 的每个元素只有两个取值——同桶为 0,不同桶为 -∞,加到分数上相当于把“跨组对话”永久屏蔽。
    Mij={0,h(qi)=h(kj)−∞,否则\displaystyle M_{ij}=\begin{cases}0,&h(q_i)=h(k_j)\\-\infty,&\text{否则}\end{cases}
  3. 归一化加和softmax 把同桶分数归一化,再乘 V 得到输出。由于 -∞ 处的权重为 0,实际有效的注意力只存在于桶内。

可逆层设计

第二道坎是“留言板”。大厅只有一张巴掌大的留言板,记不下上万人的每一句对话。主办方想:如果交流过程可以“倒放”,那我只需记每个人离开时的最终状态就够了。

标准 Transformer 训练时确实要“记笔记”: 每一层的前向激活都要保存下来,反向传播才有的算。层数 L、头数 h、序列长度 n 一起乘,内存根本扛不住。Reformer 把“记笔记”改成“拍电影”——只拍结果,过程靠倒放。可逆残差把每层输入对半切成两路 X₁ 和 X₂:

这样一来,前向传播只需把最后一层的输出记在“留言板”上,中间每一层反向时“倒放”一遍就能算出来。标准 Transformer 要存 L 份逐层激活,可逆层只需要 1 份,层数越深省得越多。特别提醒:省的是激活值,不是模型参数——那些权重矩阵该占多少还占多少。

Y1=X1+Attention(X2),Y2=X2+FeedForward(Y1)\displaystyle Y_1=X_1+\text{Attention}(X_2),\qquad Y_2=X_2+\text{FeedForward}(Y_1)
X2=Y2−FeedForward(Y1),X1=Y1−Attention(X2)\displaystyle X_2=Y_2-\text{FeedForward}(Y_1),\qquad X_1=Y_1-\text{Attention}(X_2)
通俗说:前向是“先加注意力再加前馈”,反向就是“先减前馈再减注意力”,完全对称,所以不用存中间结果。
  1. 输入分流把上一层的输出按特征维对半切开,得到 X₁ 和 X₂,注意力只吃 X₂ 这一路。
    [X1; X2]=X\displaystyle [X_1;\ X_2]=X
  2. 注意力残差对 X₂ 做注意力计算,结果加在 X₁ 上得到 Y₁。
    Y1=X1+Attention(X2)\displaystyle Y_1=X_1+\text{Attention}(X_2)
  3. 前馈残差对 Y₁ 做前馈网络,结果加在 X₂ 上得到 Y₂。
    Y2=X2+FeedForward(Y1)\displaystyle Y_2=X_2+\text{FeedForward}(Y_1)
  4. 先倒推 X₂反向时手里只有 Y₁、Y₂,先用 Y₂ 减去 FeedForward(Y₁),就还原出 X₂。
    X2=Y2−FeedForward(Y1)\displaystyle X_2=Y_2-\text{FeedForward}(Y_1)
  5. 再倒推 X₁用还原出的 X₂ 计算注意力,从 Y₁ 中减去,得到 X₁。
    X1=Y1−Attention(X2)\displaystyle X_1=Y_1-\text{Attention}(X_2)

前馈分块

即使留言板够用,烤箱也不够大。大厅小吃摊的烤箱一次只能烤四片面包,大批量烤会挤爆台面。主办方干脆规定:一炉一炉来,烤完一盘端走一盘。

Transformer 里的前馈网络 FFN 也面临同样的“台面危机”: 它把每个 token 的向量从 d 维映射到中间维度 d\_ff 再映射回来,而 d\_ff 往往是隐藏层的 4 倍(如 512→2048)。一次性处理整条序列时,中间激活要占 n×d\_ff 的显存。Reformer 的做法朴素却有效——把序列沿长度方向切成 c 块,算完一块释放一块:

为什么敢这样切?因为 FFN 对每个 token 是独立运算的,块与块之间没有任何信息交换,所以这是“精确分块”而非近似。它和 LSH 的“近似分组”有本质区别——LSH 牺牲了一点质量换效率,分块则一分钱质量都不牺牲,只是把显存峰值摊平。

FFN(x)=max⁡(0, xW1+b1)W2+b2,Y=[FFN(C1), FFN(C2), …, FFN(Cc)]\displaystyle \text{FFN}(x)=\max(0,\,xW_1+b_1)W_2+b_2,\qquad Y=\left[\text{FFN}(C_1),\,\text{FFN}(C_2),\,\ldots,\,\text{FFN}(C_c)\right]
通俗说:面包一次只能烤四片,那就一炉一炉烤,烤完的放回盘里,最终结果和整炉一起烤一模一样。
  1. 线性升维输入 x 乘 W₁ 加 b₁,从 d 维升到 d\_ff 维,相当于“临时多出几倍的工作面”。
    xW1+b1∈Rdff\displaystyle xW_1+b_1\in\mathbb{R}^{d_{ff}}
  2. ReLU 激活max(0, ·) 把负数清零,给模型引入非线性。
    max⁡(0, xW1+b1)\displaystyle \max(0,\,xW_1+b_1)
  3. 线性降维再乘 W₂ 加 b₂ 回到 d 维,完成一次完整的 FFN。
    max⁡(0, xW1+b1)W2+b2\displaystyle \max(0,\,xW_1+b_1)W_2+b_2
  4. 逐块拼接把序列切成 C₁…C\_c 共 c 块,每块独立过 FFN,最后按原顺序拼接,结果与整段一起算完全一致。
    Y=[FFN(C1), …, FFN(Cc)]\displaystyle Y=[\text{FFN}(C_1),\,\ldots,\,\text{FFN}(C_c)]

复杂度总账

大会结束后,主办方要算一笔总账:新规则到底省了多少?

把三招合起来看显存构成:

对比标准 Transformer 每层每头要存 n×n 的分数、L 层激活各留一份,Reformer 把显存随长度的增长从平方级拉到近似线性,这是它能处理几万 token 长序列的根本原因。但账本上有一行小字必须看清:省显存 ≠ 省算力。LSH 哈希投影、桶排序、多轮重复计算注意力、可逆层反向重算前向,这些都是额外的 FLOPs。当序列只有几百 token 时,这些开销远大于省下来的部分,直接用标准注意力反而更快更省。

所以这场大会的账本结论是:人越多(序列越长),新规则越划算;人少的小型聚会,老规矩“全员握手”反而简单高效。

MReformer=O(n⋅m⋅nr)+O ⁣(n⋅dffc)+O(n⋅d)\displaystyle M_{\text{Reformer}}=O(n\cdot m\cdot n_r)+O\!\left(\frac{n\cdot d_{ff}}{c}\right)+O(n\cdot d)
通俗说:显存账单上只有三大项——桶内注意力分数、分块后的 FFN 临时区、唯一一份层输出,每一项都随序列长度近似线性增长。
  1. 注意力项桶内注意力只存同桶分数,量级约为 n×m,再乘轮数 n\_r。
    O(n⋅m⋅nr)\displaystyle O(n\cdot m\cdot n_r)
  2. 前馈项FFN 临时激活从 n×d\_ff 降为每块的 (n/c)×d\_ff。
    O ⁣(n⋅dffc)\displaystyle O\!\left(\frac{n\cdot d_{ff}}{c}\right)
  3. 可逆项可逆层只需保存最后一层输出 O(n·d),而不是 L 层各存一份。
    O(n⋅d)\displaystyle O(n\cdot d)

局限与取舍

再精明的主办方也有遗憾。分组是“大概率”而不是“保证”: 总有聊得来的两个人被分到不同色区,这种跨桶的依赖在注意力中被永久屏蔽了。此外,搞多轮测试要租更多场地(算力),调手环颜色数量、轮数、每桌人数也需要大量试错。

多轮哈希能把召回率补到多高,取决于单轮碰撞概率 p₁ 和轮数 n\_r:

大会办得值不值,取决于你图什么:要极致质量、序列也不长,那就保留老规则;要在一张卡上啃下十万 token 的长文,那就接受分组近似和额外算力,用 Reformer 把“不可能”变成“可行”。这,就是它的取舍之道。

P命中=1−(1−p1)nr\displaystyle P_{\text{命中}}=1-(1-p_1)^{n_r}
通俗说:单轮测试没分到一起的概率是 1-p₁,多轮都没分到一起的概率就是它的 n\_r 次方,一减就是“至少有一轮撞上”的概率。
  1. 单轮碰撞概率两个相似向量在单独一轮哈希中落入同一桶的概率 p₁。
    p1=P(h(q)=h(k))\displaystyle p_1=P(h(q)=h(k))
  2. 全没撞上n\_r 轮独立测试都没同桶的概率为 (1-p₁) 的 n\_r 次方。
    (1−p1)nr\displaystyle (1-p_1)^{n_r}
  3. 至少命中一次用 1 减去上面的值,得到至少一轮同桶的概率,轮数越多越接近 1。
    1−(1−p1)nr\displaystyle 1-(1-p_1)^{n_r}

AI 应用场景

超长合同文档的一体化审查

阶段:模型训练与推理部署

解决的问题:法律合同动辄几万字、数万 token,传统 Transformer 要么截断(丢失跨页条款关联),要么因注意力矩阵 O(n²) 直接显存溢出。Reformer 的 LSH 注意力把注意力矩阵压成稀疏块对角,分块 FFN 与可逆层再压激活缓存,单卡就能喂入全文。

具体案例:某法务科技公司要审查一份 6 万 token 的并购合同,排查“定义条款”与“违约责任”之间的跨页引用。原先模型只能分段处理再人工拼接;换用 Reformer 后全文一次送入,关键条款的跨段引用召回明显改善,显存占用从 48GB 降到 24GB。

长用户行为序列建模

阶段:特征工程与模型训练

解决的问题:推荐系统里用户一年的点击序列长达数千到数万项,标准注意力无法整段建模。LSH 把“兴趣方向相近”的行为 item 分进同桶,注意力自然聚焦在同类兴趣的交互上,序列可以更长、信号更完整。

具体案例:短视频推荐场景中,将用户最近 2 万条观看行为编码后送入 Reformer 做序列编码,在同样一块 A100 上 batch size 从 8 提到 24,离线 AUC 提升 0.8%。

整本书级的长文本生成

阶段:推理部署与缓存优化

解决的问题:自回归生成 10 万 token 小说时,每一步都要参考整个历史,注意力缓存平方级增长。Reformer 让新 token 只回看历史中“同桶”的小范围位置,兼顾上下文广度与显存。

具体案例:开源社区用 Reformer 在 TPU 上做 64k 长度的小说续写演示,显存比同长度标准 Transformer 降低一个量级;代价是生成质量对桶大小敏感,需要针对语料调参。

Python 演示

python70 行
import numpy as np  def angular_hash(x, R):    """角度 LSH: 向量在随机方向的投影,正负拼接后取 argmax 作为桶号。"""    scores = np.concatenate([x @ R, -(x @ R)], axis=-1)    return scores.argmax(axis=-1)  def masked_attention(q, k, v, mask=None):    """缩放点积注意力,可选整数掩码(掩码中 -1e9 表示屏蔽)。"""    d = q.shape[-1]    scores = q @ k.T / np.sqrt(d)    if mask is not None:        scores = scores + mask    weights = np.exp(scores - scores.max(axis=-1, keepdims=True))    weights = weights / weights.sum(axis=-1, keepdims=True)    return weights @ v  def lsh_attention(x, n_buckets, seed=0):    """LSH 注意力: 哈希分桶 -> 排序 -> 桶内掩码注意力。"""    rng = np.random.default_rng(seed)    n, d = x.shape    R = rng.normal(size=(d, n_buckets // 2))    q = x @ rng.normal(size=(d, d))    k = x @ rng.normal(size=(d, d))    v = x @ rng.normal(size=(d, d))    h = angular_hash(x, R)    order = np.argsort(h)    q, k, v, h = q[order], k[order], v[order], h[order]    same = h[:, None] == h[None, :]    mask = np.where(same, 0.0, -1e9)    out = masked_attention(q, k, v, mask)    inv = np.empty_like(order)    inv[order] = np.arange(n)    pairs = int(same.sum())    print("LSH 分桶后只计算 %d/%d 对注意力 (%.1f%%)"          % (pairs, n * n, pairs / (n * n) * 100))    return out[inv]  def chunked_ffn(x, d_ff, chunks):    """分块前馈: 沿序列方向分块计算,峰值显存约为整体的 1/chunks。"""    rng = np.random.default_rng(1)    d = x.shape[-1]    w1, b1 = rng.normal(size=(d, d_ff)), rng.normal(size=d_ff)    w2, b2 = rng.normal(size=(d_ff, d)), rng.normal(size=d)    n = len(x)    out = np.empty_like(x)    for i in range(chunks):        s = n * i // chunks        e = n * (i + 1) // chunks        hidden = np.maximum(0, x[s:e] @ w1 + b1)        out[s:e] = hidden @ w2 + b2    return out  def main():    rng = np.random.default_rng(42)    x = np.concatenate([        rng.normal(loc=i * 3, scale=0.5, size=(16, 8)) for i in range(4)    ])    out = lsh_attention(x, n_buckets=4)    y = chunked_ffn(x, d_ff=32, chunks=4)    print("输出形状:", out.shape, y.shape)  if __name__ == "__main__":    main()
目的:用 NumPy 从零实现 Reformer 的两大核心技巧——LSH 分桶掩码注意力与分块前馈,直观对比“只算桶内分数”和“逐块算 FFN”带来的显存收益。
  1. scores = np.concatenate([x @ R, -(x @ R)], axis=-1)对应公式 [xR; -xR],把向量投影到随机方向并同时考虑正负,相似向量会得到相近的分布。
  2. return scores.argmax(axis=-1)取冲劲最大的方向下标作为桶号,方向相近的向量大概率同桶。
  3. order = np.argsort(h)按桶号排序,让同桶 token 在序列上相邻,这是块对角化的前提。
  4. same = h[:, None] == h[None, :]构造 n×n 布尔矩阵,标记所有“同桶 token 对”。
  5. mask = np.where(same, 0.0, -1e9)把不同桶的位置填 -1e9,softmax 后这些位置的权重自动归零。
  6. weights = weights / weights.sum(axis=-1, keepdims=True)数值稳定的 softmax 归一化,保证每行权重和为 1。
  7. hidden = np.maximum(0, x[s:e] @ w1 + b1)分块后只对当前块做一次升维 + ReLU,临时矩阵只占整段的 1/chunks。
  8. out[s:e] = hidden @ w2 + b2逐块降维写回,块之间互不影响,最终拼接结果与整体计算完全一致。

常见误区

误区我看 Reformer 论文说复杂度从 O(n²) 降到了 O(n),那它肯定又快又省,以后长序列都用它准没错。

正解复杂度记号省的是“注意力分数的数量”,不是全部成本。哈希投影、桶排序、多轮重复计算、可逆层反向重算都是新增的算力开销。序列只有几千 token 时,这些额外开销可能超过省下的部分;当序列长到几万 token,固定成本被摊薄,收益才真正显现。一句话:省显存是真的,省算力看场合。

为何常见:看到 O(n²)→O(n×m) 的记号,直觉上以为是全面提速,忽略了复杂度的常数项和 LSH 引入的工程开销。

误区“可逆”听起来像压缩,那 Reformer 的参数肯定比普通 Transformer 少一半,显存自然省一半。

正解可逆层省的是“前向传播过程中每层要缓存的激活值”,这是训练时反向传播的原材料;模型参数(W₁、W₂ 这些矩阵)的数量完全没变。收益体现在训练阶段——标准模型要存 L 层激活,可逆模型只存最后一层输出,反向时逐层倒推重算。纯推理如果不做反向传播,这项收益基本不存在。

为何常见:把“激活显存”和“参数显存”混为一谈,以为可逆等于信息压缩、参数减少。

误区LSH 是近似的,那分块 FFN 肯定也是近似,块切小了质量会下降,我都不敢用。

正解分块 FFN 是数学上精确等价的。FFN 对每个 token 独立运算,没有跨 token 交互,先算第 1 到 100 个 token 再算第 101 到 200 个,拼起来和一次性全算结果一模一样。它只改变显存峰值的时间分布,不改变任何数值。真正有近似性的是 LSH 分桶,不要把两者混为一谈。

为何常见:因为 LSH 和分块经常一起出现,人容易把“近似”这个标签一锅端地贴给所有省内存手段。

误区复杂度公式里桶内候选数 m 越小越省,那把桶数调大,每组人少一点,不是更省吗?

正解桶太细会把本来相似的向量切散:两个高度相关的 token 一旦被分到不同桶,注意力直接归零,长程依赖就断了。桶大小要和序列长度、多轮轮数一起调,Reformer 论文里桶大小是超参数,需要在验证集上调出来。空桶、边界截断也要额外处理,桶数绝不是越大越好。

为何常见:只看复杂度公式的直觉,忽略了“分组质量”这个约束条件。

面试题

Q1. Reformer 主要针对标准 Transformer 的哪两大痛点?

单选题 · easy

  • A.显存和算力随序列长度平方暴涨← 正确答案
  • B.参数过多与训练样本要求过高
  • C.推理速度慢且无法并行计算
  • D.梯度消失与过拟合难以控制

答案:A。标准注意力的矩阵是 n×n,序列一长,显存与算力都按平方级上涨,这正是 Reformer 最想解决的两个痛点。B 的参数量、C 的并行性(Transformer 反而擅长并行)、D 的优化问题都是真实存在但并非 Reformer 的靶心。

Q2. 局部敏感哈希 LSH 最核心的性质是什么?

单选题 · easy

  • A.相似的向量大概率落入同一个桶← 正确答案
  • B.任意向量都映射成固定长度编码
  • C.哈希值可以反向还原出原始向量
  • D.桶的编号会随训练轮次不断更新

答案:A。LSH 的核心是“局部敏感”: 两个向量越相似,被分到同一桶的概率越高;反之概率骤降。这正是 Reformer 敢只算桶内注意力的前提。B 是普通哈希/编码的特性,C 与哈希单向性矛盾,D 混淆了哈希与模型参数更新。

Q3. Reformer 中分块 FFN 带来的效果是什么?

单选题 · easy

  • A.减小中间激活的显存峰值← 正确答案
  • B.减少模型参数的存储总量
  • C.降低注意力分数的计算量
  • D.让哈希函数收敛速度更快

答案:A。FFN 的中间激活维度通常是隐藏层的 4 倍,分块后任意时刻只保留一块的临时激活,峰值显存下降,但总计算量和参数都不变。B/C/D 分别把分块的收益错安到参数、注意力和哈希头上。

Q4. LSH 注意力中按桶号排序后再分块,主要目的是什么?

单选题 · mid

  • A.让同桶 token 相邻且块长固定← 正确答案
  • B.让不同桶 token 完全随机打散
  • C.让注意力矩阵变成严格对角阵
  • D.让哈希冲突概率降为零

答案:A。按桶排序后同桶 token 聚合到相邻位置,再切成固定大小的块,既保证块形状规整便于并行,又让桶内注意力落在块内。B 与排序目的相反;C 太绝对——块内还会包含相邻桶的部分 token,并非严格对角;D 不现实,LSH 本来就是概率性分组。

Q5. 可逆残差层反向传播时,输入 X₁ 和 X₂ 是如何得到的?

单选题 · mid

  • A.利用逆推公式从输出逐层倒算← 正确答案
  • B.从硬盘重新加载训练数据
  • C.对梯度做一次随机采样模拟
  • D.用另一组参数再跑一遍前向

答案:A。可逆层只保存输出 Y₁、Y₂,反向时先用 X₂ = Y₂ − FFN(Y₁) 倒推 X₂,再用 X₁ = Y₁ − Attention(X₂) 倒推 X₁,完全不需要缓存中间激活。B/C/D 都没有利用“可逆”这一结构特性。

Q6. 下列关于 Reformer 省显存的说法,正确的是?

单选题 · hard

  • A.省的是激活值而非模型参数量← 正确答案
  • B.省的是参数矩阵的存储空间
  • C.推理时省钱效果比训练时更大
  • D.梯度本身会被压缩得更加紧凑

答案:A。可逆层和分块省的都是前向激活的缓存——训练反向传播需要它们,所以收益主要在训练阶段。参数矩阵和梯度大小都不变。C 说反了:纯推理若不做反向,可逆层重算的收益有限;D 的梯度压缩不是 Reformer 的机制。

Q7. 多轮哈希为什么能提高注意力的质量?

单选题 · mid

  • A.多轮互补,降低相似向量被拆散的概率← 正确答案
  • B.每轮都会让桶的数量自动翻一倍
  • C.多轮可以显著降低特征的维度数
  • D.多轮会把所有 token 放进同一个桶

答案:A。单轮哈希可能让两个相似 token 恰好被分到不同桶,多轮用多套独立哈希各分一次、各算一遍注意力再平均,大大增加“至少一轮撞上”的概率(见 P=1−(1−p₁)^n\_r)。B/C/D 都不是多轮哈希的机制。

Q8. 与标准 Transformer 相比,Reformer 引入了哪些额外开销?

多选题 · hard

  • A.每层都要计算哈希和排序操作← 正确答案
  • B.多轮哈希使注意力重复多次← 正确答案
  • C.可逆层反向要重算一遍前向← 正确答案
  • D.模型参数量随层数线性翻倍

答案:ABC。哈希投影、桶排序、多轮注意力、反向重算都是 Reformer 新增的算力成本,这也是“省显存不省算力”的由来。D 错误:参数量由隐藏维度决定,可逆层并不增加参数。

Q9. 哪些场景更适合选择 Reformer 而非标准注意力?

多选题 · mid

  • A.序列长度达到数万级的训练任务← 正确答案
  • B.显存受限但需要完整的长上下文← 正确答案
  • C.超短序列的高频小模型推理场景
  • D.需要严格捕捉跨桶全局依赖的任务

答案:AB。Reformer 的平方级降维收益只在长序列上才能盖过哈希等额外开销,短序列用标准注意力更简单高效;C 正是它的劣势场景。D 中“跨桶依赖”恰是 LSH 近似的薄弱点,不应选 Reformer。

Q10. 关于 LSH 注意力中“分桶”与“分块”的关系,正确的理解有?

多选题 · extreme

  • A.分块保证每块大小固定,便于硬件并行← 正确答案
  • B.严格按桶边界切分会产生空桶和大小不均← 正确答案
  • C.分块后同一块内可能出现相邻桶的 token← 正确答案
  • D.分块能够彻底消除哈希冲突带来的影响

答案:ABC。排序后严格按桶边界切分,桶大小不均、可能为空,GPU 并行效率低,所以 Reformer 改成分块近似——块大小固定,代价是块边界可能截断同一个桶,或把相邻桶的 token 包进来。D 说反了:分块只是工程折中,哈希冲突与边界截断依然存在。