THOR:纯 HE 非交互式 Transformer 推理
ACM CCS 2025:对角线表示 + 紧凑打包,在最贵的两个 CC-MM 场景把旋转次数压到 BOLT 的 2%–5%,BERT-base 128 token 单卡 10 分钟;但一层里 56% 时间在 bootstrapping。
0. 速览
一句话定位:THOR 是非交互式(non-interactive)纯 HE 的 Transformer 推理系统——服务器持明文权重、activation 全程 CKKS 密文,客户端只做一次上传、一次下载,中间零轮通信;它把矩阵乘法改写成「对角线逐元素乘 + 旋转累加」,再用 c = 16 的紧凑打包与分块/BSGS 两套调度把旋转次数压下来,把 BERT-base 128 token 的端到端延迟从 NEXUS 的 2.7 小时压到单卡 602 s(10.04 分钟)。
四个数字(完整出处见 §3):
| 指标 | 数值 | 条件 |
|---|---|---|
| 端到端延迟 | 602.26 s ≈ 10.04 min | BERT-base,128 token,单张 A100 |
| bootstrapping 占用 | 337.86 s,56.10% | 同上,12 层合计 |
| 矩阵乘法加速 | PC-MM 5.3×、CC-MM 36.0× | 均对 BOLT,单算子微基准 |
| 一层边界的密文数 | 8 个(n/c = 128/16) | 每个 encoder 层的输入输出 |
读法:这篇论文只做一件事——把**旋转(= key-switch)**砍下来。它不引入新密码假设、不换编码理论、不动威胁模型,全部收益来自「同一个数学恒等式的更好排布」。而砍完之后剩下的时间 56% 是 bootstrapping,这个比例本身就是全文最有信息量的结论:纯 HE 推理的下一战场已经不在矩阵乘法,而在 bootstrap。
1. 问题与定位
1.1 三条路线,三种"贵"法
| 路线 | 代表 | 卡在哪 | 代价 |
|---|---|---|---|
| MPC / 秘密分享 | Iron、BumbleBee、BOLT | 每层都要交互,通信量 ∝ 层数 | BOLT 跑 BERT-base 端到端通信 25.74 GB |
| 纯 HE"替换" | THE-X、Zimerman、Powerformer | 只评到 BERT-tiny;行式(row-wise)编码与多头注意力的数据布局不匹配 | THE-X 甚至不报告运行时间 |
| 纯 HE"系统" | NEXUS(NDSS 2025) | 第一个完整的 BERT-base,但每查询 2.7 小时 | 47 × 128 = 6016 次 bootstrap |
纯 HE 在原理上极其诱人:0 轮交互,通信量只等于密文尺寸(与层数无关),服务器不需要为每一层准备掩码、不需要在线生成相关随机数。代价是三条硬约束同时压过来:
- 槽位有限:
N = 2^16→s = 2^15 = 32768个 CKKS 槽;而一个128 × 768的 activation 有 98304 个数,装不进一个密文。 - 乘法层级有限:13 层,而
n = 128的对角线乘法天然就要log n级以上的Mult/Rot链。 - 旋转最贵:每次旋转都要一次 key-switch,且每个旋转量都需要一个预先上传的评估密钥。
作者押的注写得很直白:「对角线表示 + 紧凑打包」,外加一条不起眼但影响全局的工程决定——把转置全部赶到明文侧。
1.2 与 BOLT 的路线分歧(同一篇论文里最值得记的一句判断)
BOLT 是 MPC-HE 混合框架(BFV leveled HE + 秘密分享重置噪声),它的首要优化目标是压低乘法深度,好让 BFV 不做 bootstrapping。THOR 的判断是:BOLT 为了"免 bootstrap"付的学费(更多 key-switch、更重的密文×密文协议)比 bootstrap 本身还贵。所以 THOR 反过来——接受 bootstrapping,把矩阵乘法做成"旋转极少"的形状,只在非线性操作真正需要的地方买 bootstrap。这个取舍是全文的骨架,§2.4 与 §5.4 会反复回到它。
1.3 五条贡献的成色
| 贡献 | 论文的说法 | 我的评级 |
|---|---|---|
c = 16 紧凑打包 | 沿 token 维度两级打包,槽用率 75%,层边界 8 个密文 | 真贡献,可迁移到任何 CKKS Transformer |
| DiagBlock PC-MM | 旋转 864 → 224 | 真贡献,但收益随 d 增大,属结构性的"改摆放" |
| BSGS CC-MM | 旋转减半 + lazy relin/rescale | 增量贡献:BSGS 是经典套路,搬到"掩码侧"才是新的 |
| 两阶段 Softmax | 3 次 bootstrap 而非 8 次 | 全文最实用的单项,直接换来 2.64× |
| 端到端 BERT-base 10 分钟 | 首个可用的纯 HE BERT-base | 系统贡献,但真正的主语是 bootstrapping(56%) |
2. 方法解剖
2.1 系统架构与威胁模型
两方:客户端(数据所有者,持密钥)、服务器(模型所有者,持明文权重)。协议是单轮的:客户端上传密文 → 服务器算完整个网络 → 返回密文 logits → 客户端解密。
| 资产 | 谁持有 | 是否受保护 |
|---|---|---|
| 客户端输入 token / embedding | 客户端 | 是(CKKS 语义安全) |
| 模型权重(除 embedding 表外) | 服务器,明文 | 否 |
| embedding 表 | 客户端(23.8M 参数,占 BERT-base 21.8%) | 不适用(不得不在客户端) |
| 输出 logits | 客户端解密 | 是 |
三条明确的 non-goal:不保护模型权重(不是 model-private inference)、不防恶意服务器(半诚实 only)、不涉及训练/fine-tune。安全论证只有一句:CKKS 基于 RLWE 的语义安全;而 CKKS 的密钥恢复攻击不适用,因为解密结果只回到密钥持有者手里。
一个容易被忽略的后果:模型权重明文意味着服务器可以任意预计算——所有旋转掩码、对角线编码、Q^T = W_Q^T X^T 里的 W_Q 都可以在客户端不知道的情况下算好。THOR 几乎所有"便宜"都来自这个不对称。
2.2 一个恒等式撑起全篇
设 A ∈ R^{n×m}、B ∈ R^{m×n}、C = AB,n ≥ m 且 m | n。记 U_k(A) 为把 A 的第 k 条上对角线拉直成行向量,L_ℓ(B) 为第 ℓ 条下对角线拉直。
命题 3.6(对角线乘法恒等式)
L_r(AB) = Σ_{ℓ∈[m]} ρ^r( U_{ℓ−r}(A) ) ⊙ L_ℓ(B) r = 0, 1, …, m−1
其中 ρ 是「向右循环移位 r 格」(= 一次密文旋转),
U_{ℓ−r}(A) 的下标按模 m 解释,⊙ 是逐槽乘法。读法:C 的第 r 条下对角线 = 把 A 的各条上对角线错开 r 格后,与 B 对应的下对角线逐元素相乘再求和。整个 AB 就是"m 条对角线各自一次旋转 + m 次逐元素乘 + 一次求和"。
图 1:对角线乘法,n = 3。每种颜色是一条对角线;每一行是一次 ρ^r(U) ⊙ L,三行相加得到输出密文的一条对角线。
这个恒等式本身不新(Gazelle 之后的对角线打包都用它),THOR 的贡献在于围绕它做排布。理解后续所有优化的关键,是先记住 CKKS 里四类运算的相对价格:
运算 符号 价格 为什么
加法 + 免费 无噪声增长、无密钥
密文×明文 PMult 便宜 一次乘法 + rescale(rescale 可用 lazy 省)
密文×密文 Mult 贵 tensor product 后必须 relinearize(一次 key-switch)
旋转 ρ 最贵 每次都要 key-switch;旋转量决定密钥集合,必须预上传全篇只有一个优化目标:砍旋转(以及它附带的 key-switch)。 论文从头到尾没有试图省加法,也没有试图省乘法——因为加法本来免费,乘法远比旋转便宜。
2.3 四个成本杠杆(外加两个小杠杆)
四个杠杆都在同一个式子上动手,但省的资源不同:
| 杠杆 | 手法 | 省掉 | 量级(代入 d=768, n=128, c=16) |
|---|---|---|---|
| ① u/L 互换 | 转置搬到明文侧 | 密文旋转(投影步骤全部消失) | 从 n² 次降到 0 |
| ② c=16 紧凑打包 | interlace + concat | 密文个数 ÷ c、槽浪费 | 层边界 8 个密文,槽用率 0.39% → 75% |
| ③ DiagBlock PC-MM | 切 n×n 子块 | 旋转(O(d) → O(n)) | 864 → 224,3.9× |
| ④ BSGS CC-MM | 延迟旋转、改转掩码 | 旋转 ÷2,relin/rescale 次数 | mn/c → mn/(2c) |
| 加餐 A | complexification | 密文×明文次数 ÷2 | 8 个实数 → 4 个复数 |
| 加餐 B | lazy relinearization / rescaling | relin 与 rescale 次数 | 见 §4.4 |
杠杆 ①:把转置赶到明文侧
X ∈ R^{n×d} 在客户端就转置成 X^T ∈ R^{d×n} 上传,于是:
Q^T = W_Q^T · X^T 明文 × 密文,零旋转
K^T, V^T 同理 W 由服务器离线转好,客户端永远不转密文
需要"激活的转置"时 用 U/L 互换视角代替真转置(Lemma 3.3)
真要做一次密文转置 一次旋转 + 索引翻转,代价约 n 次旋转(Lemma 3.4)Lemma 3.3 给出关键的免费关系 U_k(A) = L_k(A^T)(索引不变,只换视角),Lemma 3.4 给出 L_ℓ(A) = ρ^{−m+ℓ}( U_{m−ℓ}(A) )(一次旋转 + 翻转 ℓ ↔ m−ℓ)。整个推理因此在"转置态"下运行:只有 X^T 进、X^T 出,W 的形状由服务器预先转好。这是全篇最便宜也最容易被抄漏的一招——它把转置的代价从"每层 n² 次旋转"变成"零"。
杠杆 ②:c = 16 的紧凑打包
对角线的长度只有 n,而槽有 32768 个——直接用会用掉 0.39%,浪费 99.6%。THOR 做两级打包:
第一级 interlace:把 H 个 head 沿对角线方向交织
L̄_ℓ(A)[t] = L_ℓ( A^{(t mod H)} )[ ⌊t/H⌋ ] t ∈ [nH]
第二级 concat:把 c 块儿拼进一个密文
L̂_j(A) = ( L̄_{cj} | L̄_{cj+1} | … | L̄_{c(j+1)−1} ) ∈ R^s
约束:s = c·n·H,c = ⌊s/(nH)⌋ 向下取 2 的幂
代入 n=128, H=12, s=2^15 → c ≤ 21.3 → c = 16结果:一个 encoder 层的输入/输出边界只需要 8 个密文(n/c),槽用率 75%;而 c 块之间还可以用"内部旋转"(internal rotation,不耗 key-switch 的槽内移位)互相取用,进一步省掉跨块旋转。c 不是自由参数:它由 s/(nH) 定死,且 Table 1 显示旋转数随 c 单调下降,所以真正的天花板是槽数 s(以及更大 N 带来的密文变慢)。
杠杆 ③:DiagBlock PC-MM
直接套命题 3.6 做 R^{d×d} × R^{d×n},有 d = 768 条对角线,每条一次旋转 → 旋转数与 d 成正比,而 d 恰恰是最大的那个维度。THOR 把它切块:把 A 划成 d/n 个 n×n 子块,对每个子块用一次 n 条对角线的乘法,再用"块内对角化"把子块之间的贡献合并。代价表(Table 1):
| 方法 | 旋转 | 密文×明文 |
|---|---|---|
| Basic(直接对角线) | d + 2d/c = 864 | 2d/c + dn/c = 6240 |
| DiagBlock(本文) | n + 2d/c = 224 | d/c + dn/c = 6192 |
关键结构(Table 1 原式):Basic 是 d + 2d/c,DiagBlock 是 n + 2d/c;代入 d=768, n=128, c=16 得 864 → 224(3.9×)。DiagBlock 把与 d 成正比的那一项从 d 降到 n(子块边长,与 d 无关),代价是乘法的 2d/c 项几乎不变(6240 → 6192)——省的是旋转,不是乘法。注意比值并非 c 倍:两边都保留 2d/c 项,d 很大时比值趋近 c/2 + 1 = 9,所以 d 越大收益越大(FFN 的 d=3072 时是 3456 → 512,6.75×),但天花板是 9× 而不是 16×。
把 A 切成 d/n 个 n×n 子块 A^{(j)},输出 C 也切成 d/n 个块
C^{(j)} = Σ_{i} DiagBlock( A^{(i)}, B^{(i,j)} )
每个子块内只做一次「n 条对角线」的乘法
⇒ 旋转 = n(块内) + 2d/c(块间对齐)
代入 d=768, n=128, c=16:864 → 224(3.9×);d 增大时趋近 c/2+1 = 9×杠杆 ④:BSGS CC-MM
QK^T 与 αV 是两处密文×密文。朴素做法每算一条对角线都要"先旋转再乘",2mn/c 次旋转。BSGS(baby-step giant-step)把它拆成两步:baby step 负责 ⌈√m⌉ 个小旋转,giant step 用 ⌈√m⌉ 个大跨步合成,旋转数降到约 2√m;THOR 的变体是把旋转从数据搬到掩码上——数据各份只做 n/c 次内部旋转,掩码侧预先 d/c 次旋转即可,整体 mn/c → mn/(2c),并且 relinearize/rescale 的次数也同步减半。
朴素 CC-MM:对每个 (i, j) 先把两份密文都转到位再相乘
⇒ 旋转 2mn/c 次,relinearize 也随之翻倍
BSGS: 把下标拆成 小步 b 与大跨步 g
小步:只对 B 的副本做「槽内旋转」(不耗 key-switch)
大步:只对 A 的掩码做 ρ^{gG},共 d/c 次
⇒ 旋转 mn/c(÷2),relin/rescale 同步减半图 5:CC-MM 的 (a) 朴素做法与 (b) BSGS 优化。左列 ct_A 的副本数从 6 份降到 4 份(d = 4 的玩具例子),右侧累加时只需两次 ρ^-4 即可得到 ct_C0;掩码侧预先旋转,避免了每个乘积都转一次数据。
注意一个反直觉的结论:四个杠杆没有一个在省"乘法层级"。层级只由非线性逼近的深度决定(GELU 就要 11 层,总预算 13 层),所以 bootstrapping 根本躲不掉——这直接决定了 §2.4 与 §5.4 的判断。
2.4 非线性层:成本不在算子,在 bootstrap
| 函数 | 做法 | 代价 | 关键数字 |
|---|---|---|---|
| Softmax | 两阶段:① 对 δ^0 与 δ^1 两种平移分别做 exp 多项式近似;② 迭代校正(每次乘一个残差修正项,2 次乘法) | 3 次迭代 = 3 次 bootstrap;38 次 Mult + 21 次 Rot | δ0 = 11.805、δ1 = 2 |
| LayerNorm | 均值 → 方差 → 用 Goldschmidt 迭代 + aSOR 求 1/√σ | 每层 2 个(LN1、LN2),各 1 次 bootstrap | 常数 k_i = 2/(1+ε_i) |
| GELU | f2 ∘ f1 两段复合多项式(degree 31 与 degree 27),Paterson–Stockmeyer 求值 | 39 次 Mult,深度 11 | NEXUS 是 56 次 Mult、深度 14 |
| Tanh | 两段 degree-15 多项式(pooler 用) | 少量 | 只为分类头 |
这是整篇论文最该记住的一组对比(Table 5,对 256 个维度 128 的 softmax):
| 指标 | Cho et al. 的旧法 | THOR | 对比 |
|---|---|---|---|
| 迭代(bootstrap)次数 | 7(8 次 btp) | 3(3 次 btp) | 2.3× / 2.7× |
| 密文乘法 / 旋转 | 53 / 49 | 38 / 21 | — |
| 运行时间 | 9.779 s | 3.704 s | 2.64× |
| 最坏精度损失 | −16.956 bits | −14.189 bits | THOR 少 2.77 bits |
作者自己点破了机制:两种方法的 softmax"本身"都不到总时间的 3%,2.64× 全部来自把 8 次 bootstrap 降到 3 次。 所以"优化非线性"在这套系统里的正确翻译是"优化 bootstrap 次数",而不是"优化多项式次数"。这也解释了为什么 THOR 宁可多花 2.8 bits 精度也要少 5 次 bootstrap。
2.5 一层 encoder 的完整数据流
图 6:一层 encoder 的数据流。蓝色 = PC-MM(密文×明文),橙色 = CC-MM(密文×密文),白框 = 逐元素或行式非线性;左侧虚线框外标注 L× 表示重复 L 层。
输入是 X^T ∈ R^{d×n}(不是 X),全程转置存储。CC-MM 只有两处:QK^T 与 αV;PC-MM 有四个调用点(QKV 投影、W_O 投影、FFN 的 W_1 与 W_2),图里画成 6 个蓝框是因为 Q/K/V 各画一个——若按"注意力内 PC-MM 两处、全层含 FFN 共 4 处"的说法,两者并不矛盾,只是数的粒度不同。
| 步骤 | 操作 | 类型 | 每层耗时 (s) | 占端到端 |
|---|---|---|---|---|
| Q/K/V 投影 | 3 × PC-MM | 密文×明文 | 4.15 | 8.3% |
| 注意力分数 | 12 head × CC-MM | 密文×密文 | 1.35 | 2.7% |
| Softmax | 逐元素 + bootstrap | 非线性 | 1.29 | 2.6% |
| αV | 12 head × CC-MM | 密文×密文 | 1.09 | 2.2% |
| 输出投影 | PC-MM | 密文×明文 | 2.29 | 4.6% |
| LayerNorm 1 | 行式 | 非线性 | 0.59 | 1.2% |
| FC1 | PC-MM | 密文×明文 | 4.15 | 8.3% |
| GELU | 逐元素 | 非线性 | 2.45 | 4.9% |
| FC2 | PC-MM | 密文×明文 | 4.10 | 8.2% |
| LayerNorm 2 | 行式 | 非线性 | 0.34 | 0.7% |
| 算子小计 | — | — | 21.81 | 43.4% |
| bootstrapping 均摊 | — | — | 28.16 | 56.1% |
| 一层合计 | — | — | 49.97 | 100% |
(每层值 = 论文 Table 6 的 12 层合计 ÷ 12;占端到端 = 该项 12 层合计 ÷ 602.26。)
值得单独记的两个实现细节:注意力分数的 K^T 其实不是转置——由 Lemma 3.4,服务器只需对 K 的密文做 1 次旋转 + 索引翻转就得到 L(K^T),然后直接把 16 份 Q^T 拷进去(Algorithm 4);u/L 视角在整个 attention 里来回切换,这就是"对角线表示"在实际代码里的样子。
2.6 论文概念 ↔ 代码对照
作者开源了 BERT 版实现,几个关键函数与论文的对应关系如下(路径相对 related_work/thor/code/src/thor/):
| 论文概念 | 代码位置 | 备注 |
|---|---|---|
| DiagBlock PC-MM | linear.py:13 pt_ct_matmul(mode='block_diag_1') | 多个 mode 正好对应 Table 1 的几行 |
| 并行对角线副本 | linear.py:43 parallel_diagonal_pt_ct_mult | 生成 m 份 E_i(A) |
| 密文转置 | linear.py:64 transpose_upper_to_lower | Lemma 3.4 的实现 |
| 槽内旋转 | linear.py:155 rotate_internal | internal rotation,不耗 key-switch |
| 掩码 / 旋转副本预计算 | linear.py:105、linear.py:194 | make_rotated_copies、pre_encode_masks |
注意力分数 QK^T | bert.py:248 calculate_attention_score | 第一处 CC-MM |
αV | bert.py:458 calculate_attention_context | 第二处 CC-MM |
| 层边界(8 个密文) | bert.py:54 ThorBert.forward | 还带 max_layer_batch 做层批处理 |
| 分类头 | bert.py:810 ThorBertClassifier | Tanh + 两个线性层 |
看代码能确认一件论文没写清的事:旋转密钥是预先枚举好的(旋转量集合报数 55 个),所以"优化旋转"在工程上等价于"优化密钥集合",而这一点在 §5.4 会成为复现的负担。
3. 关键数字表
| 指标 | 数值 | 条件 | 出处 |
|---|---|---|---|
| 端到端延迟 | 602.26 s ≈ 10.04 min | BERT-base,128 token,单 A100,服务器侧计时 | Table 6, §7.3.1 |
| bootstrapping | 337.86 s,56.10% | 同上;摊到每层 ≈ 28.16 s | Table 6 |
| 线性层(PC-MM) | 205.52 s,34.13% | 同上 | Table 6 |
| 非线性层 | 56.18 s,9.33% | Softmax 15.46 + GELU 29.42 + LN 11.18 | Table 6 求和 |
| 全层算子合计 | 261.7 s,43.4% | 12 层相加 | Table 6 |
| 每层算子 | 21.81 s | 261.7 ÷ 12(本笔记除得) | Table 6 推算 |
| PC-MM 加速 | 5.3× vs BOLT,11.2× vs Powerformer | R768×768 × R768×128 | Abstract, §7.2.1, Fig 7 |
| CC-MM 加速 | 36.0× vs BOLT,9.7× vs Powerformer | 12 × (R64×128 × R128×128) | Abstract, §7.2.1 |
| DiagBlock 旋转 | 864 → 224(3.9×) | d=768, n=128, c=16 | Table 1 |
| DiagBlock PMult | 6240 → 6192 | 同上 | Table 1 |
| CC-MM 旋转 | 2mn/c → mn/c(÷2) | 同上 | Table 2 |
| MHA key-switch 次数 | Case 1/2/3/4 = 192/460/492/122(BOLT 297/24468/21420/177) | 四种注意力布局 | Table 4 |
| Softmax 对比 | 3 次迭代/3 btp vs 7/8;3.704 s vs 9.779 s(2.64×) | 256 个 R^128 | Table 5 |
| Softmax 精度 | 最坏 −14.189 bits(旧法 −16.956) | 同上 | Table 5 |
| GELU | 39 Mult,深度 11(旧法 56 Mult,深度 14) | R128×3072 | §5.2 |
| 层边界密文数 | 8(= n/c = 128/16) | 每层输入输出 | §6.2.2 |
| CKKS 参数 | N = 2^16,s = 2^15,128-bit 安全,13 个乘法层级 | 全实验统一 | §7.1 |
| 打包参数 | c = 16(c ≤ s/(nH) = 21.3 取 2 的幂) | H = 12, n = 128 | §4.5.2 |
| 评估密钥 | 112 个(旋转 55 + 其他 57),约 GB 级 | 代码统计,非论文正文 | 实现侧 |
| 精度损失 | MRPC 85.29 → 84.80;RTE 72.20 → 71.12;SST-2 91.51 → 90.71 | 全密文 vs 明文微调 | Table 7 |
| BOLT 通信 | 25.74 GB | BERT-base 端到端 | §1 对比段 |
| NEXUS | ≈ 2.7 h / query(≈ 9720 s) | BERT-base,本文未实测只引用 | §7.3.1 引用 |
4. 与同系列论文的关系
(a)在综述 ./01-隐私保护Transformer推理综述.mdx 里的位置:综述把解法分成 MPC-based 与 HE-based 两条主线,THOR 落在 HE-based 的非交互支——与 NEXUS 同一格(半诚实、单轮、只保护输入),与 BOLT 不同格(BOLT 是混合框架,按层交互,且同时保护模型)。按综述的"优化手段"分类,THOR 属于**改摆放(packing)而不是改数学(approximation)**的那一类:线性层零精度损失,非线性层才赔精度(见 §3 的精度表)。
(b)对 BOLT ./02-BOLT-MPC与HE混合推理.mdx:BOLT 是被超越的基线,也是方法论上的反面教材。BOLT 用 BFV leveled HE + 秘密分享重置噪声、按列打包密文 + Gazelle 对角线打包明文、outer-product 表示 + partial rotation 做密文×密文;它的设计目标(压深度、免 bootstrap)与 THOR(接受 bootstrap、压旋转)正好相反。THOR 在 Table 4 的四个 MHA 布局上把 key-switch 次数从 297/24468/21420/177 压到 192/460/492/122,在单算子上拿到 5.3×/36.0× 的加速。
(c)对 Odin ./04-Odin-THEMIS-LLM同态推理.mdx:Odin 把同一条纯 HE 路线从 BERT 推到 Llama-3-8B 解码器。THOR 刻意回避的三件事——causal mask、KV cache、自回归增量解码——正是 Odin 的全部难点;而 Odin 又反过来把 THOR 当成基线(报 4.51× 加速,分母是一个 1651.9 s 的"THOR-style"实现,不可审计)。这条血统线可以记成一句话:Iron/BOLT 是"混合 + 交互"支,NEXUS 是"纯 HE 首秀",THOR 负责把纯 HE 的算术做对,Odin 负责把它推到 decoder 和大模型。
5. 局限与可追问点
5.1 任务面:这是分类器,不是语言模型
只做 BERT 的掩码语言理解,没有 causal mask、没有 KV cache、不是自回归。序列长度硬上限 128 token,而 BERT 本身支持 512;论文没有报告把长句截断到 128 对下游精度的影响——这是一个没有代价声明就做了的取舍。生成式任务(逐 token 解码)完全不在射程内,而这恰恰是 2025 年之后所有人真正关心的场景。
5.2 模型面:权重不保密,且客户端要持 21.8% 的模型
服务器持明文权重 → 模型机密性为零。与 BOLT 的 2PC 相比这是少保护了一个资产换来的速度,不能用"同样安全"来类比。更实际的问题是 embedding 表(23.8M 参数,约 BERT-base 的 21.8%)必须放在客户端:客户端要本地做一次 embedding 查表 + 前向,因此 602 s 里不含这部分;而如果换成大模型,embedding 层动辄 10 亿参数,这条"客户端替服务器分担一点"的假设立刻破产。论文讨论了 PIR 作为出路但没有实现。
5.3 安全面:半诚实、无正确性验证、泄漏序列长度
恶意服务器返回任意两个数,客户端解密后无从分辨——正确性在定义里而不是在协议里。此外 attention mask 是明文编码的,n_tokens 直接泄漏给服务器;padding 也无法隐藏真实长度。所有"半诚实 only"的 HE 推理论文都有这个问题,但 THOR 因为"权重明文"而额外多了一层:服务器甚至可以针对特定输入做模型侧的侧信道。
5.4 系统面:真正的瓶颈是 bootstrap,而且它读不到
56.10% 的端到端时间在 bootstrapping 上,而仓库里的 bootstrap 实现被 PyArmor 混淆——全文最大的开销项既不可审计也不可复现。这直接压制了"再优化矩阵乘法能提速多少"的空间:即使把剩下 43.4% 全变成 0,也只有 1.76×。反过来,任何 bootstrap 层面的改进(更少的层、更好的稀疏化、换 scheme)才是数量级的机会。另外,602 s 不含:客户端 embedding 前向、加密、112 个评估密钥(其中 55 个是旋转密钥,GB 级)的上传、解密与后处理;论文也没给通信量的完整账(只给了对比段里 BOLT 的 25.74 GB)。
5.5 实验面:三个对比的公平性问题
- "旋转次数压到 BOLT 的 2%–5%"只在两个 CC-MM case 成立:Table 4 里 Case 2 是 460/24468 ≈ 1.9%、Case 3 是 492/21420 ≈ 2.3%;而两个 PC-MM case 只到 BOLT 的 65%(192/297)与 69%(122/177)。加速比 5.3×/36× 与 key-switch 比例不是一回事——BOLT 单次 key-switch 的代价与 THOR 不同(BFV vs CKKS、参数不同),所以这两个数字不能互相印证。
- NEXUS 的 2.7 小时是引用值不是实测值:Table 6 只有 THOR 自己是实测,其他都来自原文或论文自己的重算。
- 精度归因是干净的,但只有 BERT 一个模型:Table 7 的分列(明文 / 仅 GELU 近似 / GELU+LN 近似 / 全密文)显示近似本身几乎不掉点(个别任务甚至略升),约 1 个百分点的下降来自 CKKS 噪声——这个归因站得住,但只在一个 1.1 亿参数的模型上验证过。
5.6 五个可追问的问题
c从 16 提到 32 需要N翻倍(s ≥ 49152),密文尺寸和乘法成本都会涨——净收益到底正不正?论文没给这条曲线。- 如果把 BERT 换成一个
d = 4096的 7B 模型:DiagBlock 的旋转数n + 2d/c仍随d线性增长,只是斜率从1 + 2/c降到2/c;13 层乘法预算还够吗?需要多少次 bootstrap? - bootstrap 的 PyArmor 混淆是不是"不可复现"的遮羞布?有没有独立实现的 CKKS bootstrap 能给出同量级时间?
- 服务器持明文权重下,能否用"权重预计算 + 客户端只发一次密文"的框架做模型保密(例如权重也加密但用不同密钥)?代价是多少?
- 论文的 5.3×/36× 是单算子微基准,端到端加速相对 NEXUS 是 16×(2.7 h → 10 min);这个 16× 里有多少来自算法、多少来自 GPU 实现与参数选择?论文没有拆。
6. 自测题(答案紧跟)
Q1. 为什么 THOR 要写成 Q^T = W_Q^T X^T,而不是先算 Q = X W_Q 再转置?
A:因为明文转置免费(Lemma 3.3),而密文转置要做约 n 次旋转(Lemma 3.4)。把转置永久地赶到明文侧之后,整个网络在"转置态"下运行:输入 X^T、输出 X^T,所有投影都是"明文权重 × 密文激活",旋转需求归零。这是全篇最便宜的一招,也是最难在复现时想到的一招。
Q2. c = 16 是怎么定的?能不能无脑取大?
A:c ≤ s/(nH) = 2^15/(128 × 12) ≈ 21.3,取不超过它的最大 2 的幂 → 16。c 越大,层边界的密文数 n/c 与 DiagBlock 的旋转数 n + 2d/c 都单调下降,所以在 Table 1 的代价模型里"越大越好";真正的天花板是槽数 s——想取 32 就得把 N 翻倍,密文尺寸和每次乘法的成本都会跟着涨。所以 c 不是调出来的,是算出来的。
Q3. Softmax 只占端到端 2.6%,为什么它值得单独写一节?
A:因为它的代价不在算子而在它触发的 bootstrap。Table 5 显示新旧两法的 softmax"本身"都不到 3% 时间,2.64× 的加速全部来自把 8 次 bootstrap 降到 3 次。在这套系统里,"优化非线性"的正确翻译是"优化 bootstrap 次数",而不是"优化多项式次数"——这也是为什么作者愿意拿 2.8 bits 精度去换 5 次 bootstrap。
Q4. DiagBlock 省的是乘法还是旋转?量级多少?
A:旋转。d=768, n=128, c=16 时从 864 降到 224(3.9×),而密文×明文只从 6240 降到 6192(0.8%)。机制是公式从 d + 2d/c 变成 n + 2d/c:把 O(d) 的那一项换成子块边长 n;因为两边都保留 2d/c,比值随 d 增大趋近 c/2 + 1 = 9,FFN(d=3072)时是 6.75×。
Q5. 如果客户端想知道服务器算得对不对,THOR 给了什么?
A:什么都没给。半诚实假设下正确性只是定义的一部分,不是可验证属性;恶意服务器返回任意两个数,客户端解密后无从分辨。可验证推理(verifiable inference)是另一条独立的研究线,THOR 明确不做。
Q6. 602 s 里哪一部分是作者自己也没法解释的?
A:bootstrapping 的 337.86 s(56.10%)。它的实现被 PyArmor 混淆,仓库里读不到;而 112 个旋转密钥、GB 级的 setup/上传、客户端的 embedding 前向与解密都不在这 602 s 里。换句话说,这个"10 分钟"是服务器在线计算时间,不是端到端用户体验时间。
7. 原文定位索引
| 主题 | 章节 / 表号 | 页 | paper.txt 行号 |
|---|---|---|---|
| 动机、三条路线、"25.74 GB" | §1, §1.2 | p.1–2 | L36–L122 |
| 贡献三条 | §1.1 | p.2 | L76–L99 |
| MPC / HE 相关工作 | §1.2.1–1.2.3 | p.2–3 | L123–L155 |
| CKKS 基础与原语(Add/PMult/Mult/Rot 的代价) | §2.1 | p.3 | L158–L190 |
| 定义与引理(U/L 视角、Lemma 3.3–3.5) | §3.1 | p.3 | L163–L188 |
| 命题 3.6(对角线恒等式) | §3.2 | p.4 | L189–L218 |
| 紧凑打包(两级 interlace/concat) | §4.1, Fig 2 | p.4–5 | L231–L284 |
| PC-MM 与 DiagBlock、Table 1 | §4.2, Fig 3 | p.5–6 | L285–L374 |
| CC-MM 与 BSGS、Table 2 | §4.3, Fig 4–5 | p.6–8 | L375–L530 |
| 优化(lazy relin/rescaling、复数化) | §4.4 | p.8 | L531–L537 |
| 复杂度对比、Table 4 key-switch 次数 | §4.5, Table 3–4 | p.9 | L538–L627 |
| Softmax(Algorithm 3、Table 5) | §5.1 | p.9–10 | L619–L658 |
| LayerNorm / GELU / Tanh | §5.2 | p.10 | L691–L698 |
| 威胁模型与安全论证 | §6.1 | p.10–11 | L670–L690 |
Q^T = W_Q^T X^T 的转置策略 | §6.2.1 | p.11 | L693–L712 |
| 逐层流程 + Fig 6 | §6.2.2 | p.11 | L713–L772 |
| CKKS 参数(N/s/层级) | §7.1 | p.11–12 | L762–L778 |
| Table 6 端到端计时 | §7.3.1 | p.12 | L779–L800 |
| 单算子微基准(Fig 7) | §7.2 | p.12 | L801–L812 |
| Table 7 精度(G/G-LN/G-LN-S 四列) | §7.3.2 | p.13 | L857–L900 |
| 结论与未来工作 | §8 | p.13 | L903–L929 |
相关阅读
评论
评论区尚未配置:部署 Twikoo 后端后,将 envId 填入 lib/site.ts。