上一篇文章中通过与Google AI Stduio中的Gemini 3 pro 进行对话学习,大致了解了《Homomorphic Multiple Precision Multiplication for CKKS and Reduced Modulus Consumption》中的降低精度与减少模数消耗的思路,关键在于通过将密文拆分为高低位,在Tensor2中隐式rescale掉一部分scale。
核心探讨:朴素 RNS-CKKS vs Mult² 方案
Q1: 关于朴素方法的思考
问题:
我发现这个方法和另一种更朴素的方式: 将层数翻倍,每层消耗两个模数,这样似乎也能用上 28bits 的高性能 NTT? 这和目前这个方法的优劣对比如何?
回答: 这是一个非常深刻且直击本质的问题!你提出的这个“朴素方法”(Naive Approach),其实正是传统 RNS-CKKS 在不得不使用小素数时的标准做法。
我们将这两种方法命名为:
- 朴素方法 (Naive Approach): 强行把两个 28-bit 素数捆绑在一起,作为一层(每层消耗 2 个素数,Scaling Factor )。
- Mult² 方法 (Our Paper): 使用 High/Low 结构,每层逻辑上消耗 1 个 28-bit 素数(但利用算法结构达到 的效果)。
它们都能完美适配你的 28-bit GPU NTT。但是,Mult² 的核心优势在于“降维打击”。
以下是详细的优劣对比:
1. 核心差异:模数消耗率
这是决定胜负的关键。
- 朴素方法:
- 为了维持 56-bit 精度,你必须在 Rescale 时同时扔掉 2 个 28-bit 素数。
- 消耗速度: 56 bits / Level。
- Mult² 方法:
- 利用 的隐式缩放,你在 Rescale 时只扔掉 1 个 28-bit 素数。
- 消耗速度: 28 bits / Level。
影响: 假设你的硬件或安全参数限制了你的最大模数总量 (比如由环维数 决定)。
- Mult² 可以跑的深度(层数)是朴素方法的 2 倍。
2. 真正的性能杀手:环维数 的选择
这才是 Mult² 真正快的原因。同态加密的性能主要取决于环维数 (4096 vs 8192 vs 16384…)。 每翻倍,计算量大约增加 2-4 倍,内存增加 2 倍。
假设我们要跑一个 深度为 10 的 56-bit 精度电路。
场景 A:朴素方法
- 总模数需求:。
- 安全约束:为了支持 600 bits 的模数且保持 128-bit 安全性,你查表发现必须使用 。
- 代价: 你的 GPU 必须跑 32k 点的 NTT。
场景 B:Mult² 方法
- 总模数需求:。
- 安全约束:300 bits 的模数非常小,只需要 就能保证 128-bit 安全。
- 优势: 你的 GPU 只需要跑 16k 点的 NTT。
- 性能红利:
- 减半,NTT 速度提升 2 倍以上。
- 密文尺寸(虽然 Mult² 有一对密文)总体上可能更小或持平。
- Key Switching Key 的大小大幅减小。
3. 计算复杂度与工程复杂度
这里是朴素方法唯一的赢面。
| 维度 | 朴素方法 (Naive) | Mult² 方法 | 点评 |
|---|---|---|---|
| 单次乘法开销 | 低 (1次 Tensor, 1次 Relin, 1次 Rescale) | 高 (3次 Tensor, 2次 Relin, 2次 Rescale) | 逻辑上 Mult² 算得更多。 |
| 工程实现难度 | 极低 (现有代码改改参数即可) | 高 (需要重写 Evaluator 逻辑) | 朴素方法开发最快。 |
| 误差稳定性 | 高 (标准 CKKS 误差) | 中 (低位误差积累,需定期 Recombine) | 朴素方法更省心。 |
4. 综合判定:你应该选哪个?
这取决于你的 GPU 算力瓶颈 和 应用场景需求。
情况一:如果你追求极致性能 (Performance Critical)
选 Mult²。 虽然 Mult² 单次乘法步骤多(大概慢 2-3 倍),但它允许你把 降一档(比如从 32k 降到 16k)。
- 的降低带来的全系统加速(KeyGen, Enc, Dec, Add, Mult, Rot)通常能抵消掉 Mult² 算法本身的开销,并且在同样的硬件上能跑更深的网络。
情况二:如果你只是想跑通流程,或者电路很浅 (Development Efficiency)
选朴素方法。 如果你的电路只有 3-4 层,总模数本来就不大(比如 200 bits),无论用哪种方法 可能都是 。
- 这种情况下,Mult² 的“省模数”优势发挥不出来,反而因为算法步骤多而变慢。
- 朴素方法实现简单,不易出错,且完美利用你的 28-bit NTT。
总结建议
既然你已经手握 高性能 28-bit GPU NTT:
-
第一阶段(MVP): 先实现 朴素方法。
- 把 SEAL 的 60-bit 素数逻辑改成“每层用 2 个 28-bit 素数”。
- 这能让你快速验证 GPU Kernel 的正确性,跑通整个同态加密流程。
- 这时候你的系统就是一个标准的 RNS-CKKS,只是积木变小了。
-
第二阶段(SOTA): 如果你发现 太大导致显存爆了或者速度不够快,再升级到 Mult²。
- 这时候你是在“优化算法”,而不是在“调试环境”。
- 利用 Mult² 降低 ,进一步榨干 GPU 性能。
一句话总结: 朴素方法是**“用数量换精度”(费模数,简单)。 Mult² 方法是“用结构换空间”**(省模数,复杂,能降维)。
BOB: 还有个问题是Mult² 方法精度较低,对吧?
是的,你的理解非常准确。
相比于“朴素方法”(即标准 CKKS 逻辑),Mult² 方法的精度确实会更低,或者说它的精度衰减速度更快。
但这个“低”是在可控范围内的。为了帮你做决策,我们需要量化一下这个“低”到底意味着什么,特别是在你的 28-bit 系统 下。
1. 为什么精度会低?(误差来源)
-
朴素方法 (Standard CKKS):
- 数学上是“精确”的模运算(除了 Rescaling 的舍入误差)。
- 它计算了所有的交叉项,没有丢弃任何信息。
- 误差来源: 主要是 Rescaling 引入的舍入噪声(Rounding Noise)。这是由 scaling factor 决定的,通常很稳定。
-
Mult² 方法:
- 核心原罪: 它是“近似”计算。在 Tensor 步骤中,它直接扔掉了 (低位 低位)。
- 虽然这一项很小,但它终究不是 0。
- 累积效应: 随着层数加深,(低位部分)会以 每层 +1 bit 的速度变大。你扔掉的那个“垃圾”也会越来越大。
- 结果: 这一项带来的误差会叠加在 Rescaling 噪声之上。
2. 低多少?(论文数据)
论文在 6.2 节的对比实验中给出了具体数据:
- 朴素方法(Standard): 最终精度 31.3 bits。
- Mult² 方法: 最终精度 31.0 bits。
解读: 在做了适当的维护(重组)后,Mult² 只比标准方法损失了 0.3 bits 的精度。对于大多数应用(如机器学习推理),这点损失几乎可以忽略不计。
3. 对你的 28-bit 系统的致命影响(Critical Warning)
但是! 论文用的是 60-bit 的素数,而你用的是 28-bit 的素数。这对 Mult² 的精度稳定性是巨大的挑战。
请看这个对比:
场景 A:论文环境 (60-bit Primes)
- 素数 bits。
- 低位初始大小 bits。
- 安全余量(Headroom): 低位可以从 30 bits 长到 60 bits 才会溢出/爆炸。
- 容忍度: 你有 30 bits 的空间来容忍“每层 +1 bit”的增长。所以论文说“6 层重组一次”就够了。
场景 B:你的环境 (28-bit Primes)
- 素数 bits。
- 低位初始大小 bits。
- 安全余量: 低位只能从 14 bits 长到 28 bits。
- 容忍度: 你只有 14 bits 的空间!
风险: 如果你在 28-bit 系统上跑 Mult²,只要稍微跑深一点(比如 10 层),低位误差可能就直接顶到了 24-25 bits。 这时候你再扔掉 ,扔掉的就不是“灰尘”,而是“半块砖头”了。这会导致精度雪崩式下降。
4. 结论与建议
回到你的架构选择:
如果你选择 Mult² 方法(为了省模数/降维): 你必须付出额外的代价来维持精度:
- 更频繁的重组(Recombine): 也许每 3~4 层 就要做一次 RCB-DCP,把低位误差清零。这会增加计算开销。
- 更精细的参数调优: 你可能需要把 设得比 小一点(例如 bits, bits),给低位留出更多缓冲空间。
如果你选择朴素方法(为了稳):
- 精度无忧: 它对“低位增长”免疫,因为根本没有“低位”这个概念,所有项都算进去了。
- 开发简单: 不需要写复杂的重组逻辑。
最终建议: 考虑到你是第一次将 28-bit NTT 接入 SEAL,我强烈建议你先实现“朴素方法”。
- 它能直接验证你的 GPU Kernel 是否工作正常。
- 它的精度行为符合预期,调试难度低。
- 等这一步跑通了,如果你发现显存不够用或者速度还想再快,再去挑战 Mult² 这个“高难度副本”。