我的老同学晓芸在一家名叫“安心保”的保险公司做精算师。每天清晨,她的团队要为数万位客户做理赔风险评估,客户的保单信息却像病历一样敏感,绝不能暴露给第三方。于是公司定制了一台“魔法玻璃柜”:客户的资料被锁进透明隔间,柜壁上嵌着两只特制手套,评估员把手伸进去,便能在不打开柜门的情况下翻页、勾选、计算、盖章——数据全程不出柜子,评审却照常完成。这台玻璃柜,正是 CKKS 同态加密方案的化身。今天这篇文章,我们跟着晓芸把这台柜子的原理拆开:它如何把浮点数装进密文(编码打包),如何在“起雾”(噪声)中保持清醒(重缩放),又如何把整个神经网络翻译成一套只含乘加的手势(网络映射),最终回答工程师最关心的那个问题——在真实的 CNN 与 Transformer 推理里,它到底准不准、快不快、能不能用。

岗位关联

掌握 CKKS,你就能设计“数据可用不可见”的隐私推理服务——在金融、医疗等强监管行业,这正成为 AI 从业者最具溢价的能力之一。

目录
  1. 可视化演示
  2. CKKS 基础认知
  3. 编码与打包
  4. 密钥与噪声
  5. 网络映射适配
  6. 性能精度分析
  7. 典型应用场景
  8. 工具与生态
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

动态展示CKKS方案的密文计算流水线:编码加密、SIMD并行运算、乘法深度管理与精度损耗,支持调节乘法深度观察精度变化。

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

CKKS 基础认知

让我们从“安心保”公司的故事说起。这家公司每天要为成千上万位客户做理赔风险评估,但客户的资料绝不能被外人看到。于是公司造了一台“魔法玻璃柜”:客户的资料被锁进透明隔间,柜壁上有两只特制手套,评估员把手伸进手套,就能在不开柜门的情况下翻阅、计算、盖章。每一位客户的数据都像锁进了玻璃柜,而 CKKS 方案,正是这台玻璃柜的设计蓝图。

在密码学里,这种“不开柜门也能计算”的技术叫同态加密。传统的同态方案要么只能做加法(如 Paillier),要么只支持整数运算(如 BGV、BFV),面对神经网络里铺天盖地的浮点数,要么算不了,要么精度崩盘。2017 年,Cheon、Kim、Kim 与 Song 四位学者提出了 CKKS 方案——它允许密文直接承载浮点向量,并承诺“近似正确”:解密得到的结果不是精确值,而是真实值加上一个可以忽略的小噪声。

为什么“近似”反而更好用?因为神经网络本身就是容错的:激活函数的微小偏差、权重的舍入扰动,都不会让分类结果从“猫”变成“狗”。CKKS 把这种容错性用在了刀刃上——它用一个小噪声换取了对浮点数的完整支持。解密时,用私钥对密文做内积,再模 q,就能得到“明文 + 噪声”的组合。只要噪声远小于明文信号,推理结论就和明文计算几乎一致。

把公式翻译回玻璃柜:m 是客户资料里的真实数字,e 是厚手套留在纸面上的淡淡指纹。指纹再多,也不会把“赔偿 5000 元”看成“赔偿 5 万元”。CKKS 正是用这层“可容忍的模糊”,换来了“全程不解密”的安全承诺——这正是隐私推理的起点。

m^=⟨c,sk⟩ mod q=m+e\displaystyle \hat{m} = \langle \mathbf{c}, \mathbf{sk} \rangle \bmod q = m + e
解密结果由明文项 m 和噪声项 e 组成,噪声足够小时,近似值就是可用值。
  1. 密文与私钥密文 $\mathbf{c}=(c_0,c_1)$ 是两个多项式,私钥 $\mathbf{sk}=(1,s)$ 也是一个多项式,两者做内积。
    ⟨c,sk⟩=c0+c1⋅s\langle \mathbf{c}, \mathbf{sk} \rangle = c_0 + c_1 \cdot s
  2. 模 q 归约对结果取模 $q$,把数值拉回多项式环的标准范围内,相当于柜门的锁具规则。
     mod q\bmod q
  3. 噪声项分离等号右边拆成明文 $m$ 与噪声 $e$,只要 $\lVert e \rVert$ 远小于 $\lVert m \rVert$,近似就成立。
    m+e≈mm + e \approx m

编码与打包

玻璃柜最聪明的地方在于隔间。安心保的柜子不是一间小屋,而是一整面像蜂巢一样的抽屉墙:8 个、16 个、甚至上万个隔间。评估员把 8 份不同客户的资料分别放进 8 个隔间,然后只做一套手势——同时给 8 份资料盖章。这套“一套手势服务多个对象”的功夫,在计算世界里叫 SIMD(单指令多数据)。

落在 CKKS 上,这个“隔间”就是多项式环 $R = \mathbb{Z}[X]/(X^N + 1)$ 上的位置。N 是多项式的度数,通常取 2 的幂(如 8192、16384)。一个 N 次多项式有 N 个系数,可以塞进 N/2 个复数。编码器 Encode 的工作,就是把这 N/2 个复数写进一个多项式;解码器是它的逆过程。这一步做完,“8 份资料”就变成了“一页纸”——一个多项式,正好装进一个密文。

写进去的时候要放大:每个浮点数先乘上缩放因子 Δ(通常取 2 的 40 次方量级),再量化成整数,这样小数部分在模运算里才不容易丢。求值还原时,把本原单位根代入多项式,就能把各个隔间的内容取回来。这套编码最漂亮的性质是:多项式相加、相乘,恰好等于向量逐分量相加、相乘。于是评估员的一只手,真的能同时算完成千上万个客户的风险分。

把公式翻译回蜂巢墙:Δ 是柜子里的“放大镜”,把 0.37 这种小数点后的数字放大成大整数,免得在柜内取整时被磨掉;ζ 是隔间的门牌号,告诉评估员哪份资料放在哪一格。一次手势、成百上千份资料——这就是 CKKS 能给神经网络推理提速的第一个秘密。

m(X)=Encode(z)⟹m(ζj)≈Δ⋅zj,  j=0,1,…,N/2−1\displaystyle m(X) = \mathrm{Encode}(\mathbf{z}) \quad\Longrightarrow\quad m(\zeta_j) \approx \Delta \cdot z_j,\; j = 0,1,\dots,N/2-1
一个多项式密文同时承载 N/2 个浮点数,这就是 SIMD 打包的数学本质。
  1. 向量到多项式Encode 把复数向量 $\mathbf{z}$ 映射成一个多项式 $m(X)$,整个向量被压缩进单个密文。
    z⟼m(X)\mathbf{z} \longmapsto m(X)
  2. 单位根求值把本原单位根 $\zeta_j$ 代入 $m(X)$,第 j 个隔间的内容就会被取回。
    m(ζj)≈Δ⋅zjm(\zeta_j) \approx \Delta \cdot z_j
  3. 缩放因子$\Delta$ 把浮点数放大为整数,用来抑制量化取整的相对误差。
    Δ=240  量级\Delta = 2^{40}\; \text{量级}
  4. 槽位遍历j 从 0 扫到 N/2-1,对应柜子里的所有隔间,一次运算全部更新。
    j=0,1,…,N/2−1j = 0,1,\dots,N/2-1

密钥与噪声

玻璃柜有个天生的毛病:用久了会起雾。起初雾很淡,数字还看得清;可每做一次乘法,雾就浓一层。等雾浓得看不见数字,评估员就只能干瞪眼。这团“雾”,在密码学里叫噪声,是 CKKS 工程实践里最令人头疼的对手。

噪声从哪里来?两个来源:第一是加密时故意掺入的随机扰动,为了保护明文,必须“先弄脏再上锁”;第二是每轮乘法与舍入带来的累积误差。假设两个密文的噪声分别是 $e_1$、$e_2$,对应明文的量级是 $m_1$、$m_2$,相乘以后噪声大约变成三项之和:$e_1 m_2$、$e_2 m_1$ 和 $e_1 e_2$。前两项和对方的“音量”成正比——信号越大,灰越大。

控制雾气的办法叫重缩放(Rescale):每次乘法之后,把密文的每个系数除以 Δ 再取整,同时把模数从 q 缩到 q/Δ。这相当于打开换气扇,把“浓雾”和“刻度尺”一起按比例压缩,让信号与噪声重新拉开距离。每一次重缩放,就消耗一级“滤芯”——专业术语叫一个 Level。滤芯数量 L 由加密参数预先定死,所以工程师必须精打细算每一层消耗几根滤芯。滤芯耗尽还想继续算?那就得用自举(Bootstrap):在柜内自动更换整套滤芯,代价是极其昂贵的额外计算。

把公式念成故事:“雾 = 对方的灰 + 自己的灰 + 灰乘灰”。乘法让雾以乘积速度膨胀,重缩放则像定期开窗透气,把雾和标尺一起吹薄。滤芯预算 L,就是你的网络能在柜内走多深的“深度计”——密文世界里,网络的深度敏感度远超明文世界。

emult≈e1m2+e2m1+e1e2,c′=⌊Δ−1c⌉ mod q′\displaystyle e_{\text{mult}} \approx e_1 m_2 + e_2 m_1 + e_1 e_2, \qquad \mathbf{c}' = \left\lfloor \Delta^{-1} \mathbf{c} \right\rceil \bmod q'
乘法让噪声叠加放大,重缩放把噪声和刻度一起压缩,同时消耗一级层级预算。
  1. 乘法噪声主导项两个密文相乘,噪声变成三项之和,前两项与对方明文量级成正比,是主要威胁。
    e1m2+e2m1+e1e2e_1 m_2 + e_2 m_1 + e_1 e_2
  2. 重缩放操作每个系数乘以 $\Delta^{-1}$ 后取整,把乘积的“小数点”挪回原位,噪声也按比例压低。
    c′=⌊Δ−1c⌉\mathbf{c}' = \left\lfloor \Delta^{-1} \mathbf{c} \right\rceil
  3. 模数缩减模数从 q 缩小到 q',相当于换掉一根滤芯,剩余层级减一。
    q′=q/Δq' = q / \Delta

网络映射适配

现在,评估员要面对真正的挑战:把理赔评审流程搬进玻璃柜。评审流程里有加减乘除,也有“如果分数高于 60 就通过”这种带判断的动作。好消息是,神经网络里九成的计算——权重矩阵与输入的乘积——本来就是一堆加法和乘法;坏消息是,判断动作在密文里没法直接做。

先看好消息。全连接层 $\mathbf{y} = \mathbf{W}\mathbf{x} + \mathbf{b}$ 中,每个输出 $y_j$ 等于权重行与输入列的内积。在 CKKS 里,输入被编码进一个密文,权重是明文常数,于是 $y_j$ 的密文就是权重乘以输入密文再逐项求和、加上偏置密文。整套操作除了乘法就是加法,恰好是 CKKS 的舒适区;又因为 SIMD 打包,所有输出能用同一套手势同时算完。卷积层同理:把卷积核展开成 Toeplitz 矩阵,卷积就变成了矩阵乘。

再看坏消息。ReLU 是一个“如果”函数:x 大于 0 时输出 x,否则输出 0。密文没有“如果”——你不可能让评估员在柜里判断一个看不见的数是不是大于 60。工程上的标准答案是用多项式去“伪装”它:比如 $\mathrm{ReLU}(x) \approx x/2 + x^2/4$,要求输入落在某个对称区间内。这个近似只需要一次密文自乘,代价小、效果也不错。Softmax 里的指数函数同理,用多项式或 2 的幂逼近。于是整条网络被翻译成:线性层(乘加)+ 多项式激活(自乘)+ 归一化(乘加),全部落在 CKKS 的能力圈内。

再来看激活函数的伪装公式:

把公式读作手势:评估员先对每个隔间做“加权求和加偏置”(线性手势),再做一个“温和修正”(激活近似手势),最后盖章输出。每一步手势都很朴素,但组合起来就能完成完整的理赔判定——CNN、Transformer 里复杂的张量运算,就这样被拆解成玻璃柜里一套接一套的乘加操练。

Enc(yj)=∑k=1dwjk⋅Enc(xk)+Enc(bj)\displaystyle \mathrm{Enc}(y_j) = \sum_{k=1}^{d} w_{jk}\cdot \mathrm{Enc}(x_k) + \mathrm{Enc}(b_j)
每个输出神经元 = 权重与加密输入的加权和,再叠加上加密偏置,全部由乘加实现。
  1. 线性项$\frac{x}{2}$ 保证在 x=0 附近有正斜率,让近似曲线不塌陷。
    x2\frac{x}{2}
  2. 平方项$\frac{x^2}{4}$ 只需一次密文自乘,在 [[0, C]] 区间内为曲线提供“拐弯”的弧度。
    x24\frac{x^2}{4}
  3. 定义域约束要求 $|x| \le C$,超出范围的输入,近似误差会迅速放大。
    ∣x∣≤C|x| \le C

性能精度分析

第一台玻璃柜上线后,工程师们的办公桌上多了两份报表:一份叫“评估准不准”,另一份叫“一分钟能处理几单”。这两份报表总是互相拉扯——想要更准,就要更厚的隔音棉(更大的安全参数),而更厚的棉让手势更笨重、速度更慢。

精度的衡量指标叫相对误差 ReLE:把密文推理的输出和明文推理的输出放在一起,算它们之间的 L2 距离再除以明文的 L2 范数。实验室里常见的结论是:在 MNIST、CIFAR 这类数据集上,明文分类准确率 95% 时,CKKS 推理大约在 93%~94%,ReLE 通常能压在 5% 以内。损失主要来自三个环节:编码取整(浮点变整数丢了尾巴)、重缩放舍入(每一次除法取整都磨掉一点精度)、激活近似(多项式毕竟不是真正的 ReLU)。

性能上,CKKS 的核心计算是多项式环上的乘法,用数论变换(NTT)加速后单次乘法复杂度为 O(N log N)。听着很贵,但别忘了:一次乘法同时算了 N/2 个槽位,摊到每个数据上,成本立刻被稀释了几千倍。实测中,轻量 CNN(如 LeNet)在单核 CPU 上单次推理延迟约 1~5 秒,批量打包后吞吐量能达到每秒几十次;而 Transformer 因为注意力矩阵乘法层层堆叠、激活多项式阶数更高,单次推理延迟往往要数十秒到数分钟。性能账单写在三行:层级预算太浅、密文体积膨胀、以及与现有推理框架的集成开销。

把报表翻译回玻璃柜:ReLE 就是“雾的浓度计”,5% 是雾的警戒线,过了线客户就会投诉“数字看不清”;吞吐量则是“每分钟过单数”,取决于隔间数量(SIMD 宽度)、手势灵巧度(NTT 加速)和滤芯预算(层级 L)。工程师的日常,就是在这张报表上找一个让老板和客户都满意的平衡点。

ReLE(x^,x)=∥x^−x∥2∥x∥2\displaystyle \mathrm{ReLE}(\hat{\mathbf{x}}, \mathbf{x}) = \frac{\lVert \hat{\mathbf{x}} - \mathbf{x} \rVert_2}{\lVert \mathbf{x} \rVert_2}
相对误差 ReLE 把密文输出与明文输出的差距归一化,是衡量隐私推理精度的标准尺子。
  1. 分子 L2 范数密文输出 $\hat{\mathbf{x}}$ 与明文参考 $\mathbf{x}$ 的逐元素差,先平方和再开方。
    ∥x^−x∥2=∑i(x^i−xi)2\lVert \hat{\mathbf{x}} - \mathbf{x} \rVert_2 = \sqrt{\sum_i (\hat{x}_i - x_i)^2}
  2. 分母 L2 范数明文参考向量自身的尺度,用来归一化,让误差可以跨模型比较。
    ∥x∥2=∑ixi2\lVert \mathbf{x} \rVert_2 = \sqrt{\sum_i x_i^2}
  3. 工程阈值实践中通常以 ReLE 小于 0.05 作为隐私推理可用的判据。
    ReLE<0.05\mathrm{ReLE} < 0.05

典型应用场景

几个月后,玻璃柜从实验室正式搬进机房。柜子有三种型号:第一种是“卷积柜”,服务车险图像定损;第二种是“注意力柜”,服务医疗险的复杂核保;第三种是“联邦柜”,让几家再保险公司在不交换原始保单的情况下联合建模。

卷积柜对应 CNN 推理。输入图像加密后进柜,卷积核展开成矩阵乘,ReLU 用多项式近似,逐层算到输出。典型效果:在隐私保护图像分类(如医学影像病灶筛查)中,精度损失约 1~2 个百分点,延迟从毫秒级涨到秒级,但换来的是“影像不出院”的安全承诺。

注意力柜对应 Transformer 推理。难点有三个:QK 转置是大矩阵乘、注意力头数多导致乘法深度深、Softmax 的指数必须近似。实验数据显示,BERT 级别模型在 CKKS 上完成一次前向推理需要分钟级延迟,精度下降约 3~5 个百分点(取决于多项式阶数)。联邦柜则把 CKKS 用在训练侧:各机构加密梯度后上传,服务器在密文上做加权平均聚合,全程看不到任何一家的原始梯度。

三种柜型的故事其实是同一个道理:CKKS 解决的不是“能不能算”,而是“在什么代价下算”。车险图像批次大、单价低,适合 SIMD 批量摊薄成本;医疗核保数据贵、对延迟不敏感,愿意为安全多等几秒。把合适的柜子派给合适的业务,正是隐私计算工程师的核心手艺。

S=Btenc+teval+tdec\displaystyle S = \frac{B}{t_{\mathrm{enc}} + t_{\mathrm{eval}} + t_{\mathrm{dec}}}
吞吐量 = 每批打包的样本数除以加密、评估、解密三段总耗时,评估通常占 90% 以上。
  1. 批大小 B一次 SIMD 打包处理的样本数,由多项式度数决定,通常是数千到数万。
    B=#slotsB = \#\text{slots}
  2. 三段耗时加密、同态评估、解密各占一段,评估阶段几乎总是主导项。
    teval≫tenc+tdect_{\mathrm{eval}} \gg t_{\mathrm{enc}} + t_{\mathrm{dec}}
  3. 吞吐量单位时间完成的批次数,直接衡量隐私推理服务的并发能力。
    S=BttotalS = \frac{B}{t_{\text{total}}}

工具与生态

造柜子不能靠手工作坊。安心保最终采购了一批“标准柜型”:开放的设计图纸、经过审计的出厂参数、还有配套的维修手册——这正是开源加密库和硬件加速器在做的事情。

软件层面,Microsoft SEAL、HElib、OpenFHE 是三大主流库,其中 OpenFHE 对 CKKS 支持最全面;TenSEAL 把 CKKS 封装成了接近 NumPy 和 PyTorch 的接口,适合快速搭建隐私推理原型。硬件层面,由于 NTT 是热点算子,GPU 和 FPGA 上的专用 NTT 引擎能把多项式乘法提速 10~100 倍,让 CKKS 从“学术玩具”走向“生产可行”。

部署时要回答四个问题:安全强度定多少(参数 n 与 log q 的关系,通常要求 128-bit 安全级别)、滤芯预算留几根(L 由 q 与 q0 的关系决定)、密文膨胀如何承受(每个密文数 MB,内存与带宽都要规划)、以及和现有推理框架怎么接(ONNX 或 PyTorch 前端 + 密文后端)。未来方向包括可编程自举(把任意函数烧进一次自举)、加密感知的神经架构搜索,以及把量化和剪枝搬进同态域。

采购单上那几行参数,就像给玻璃柜选的“轮胎型号”——柜体尺寸 n 决定能装多少隔间,滤芯根数 L 决定能算多深,锁具模数 log q 决定安全等级,缩放因子 Δ 决定每根滤芯的过滤精度。选对了参数,玻璃柜才能在安全、精度、速度之间稳稳地行走。

L=⌊log⁡Δqq0⌋\displaystyle L = \left\lfloor \log_{\Delta} \frac{q}{q_0} \right\rfloor
初始模数 q 被拆成 L 级,每级对应一根“滤芯”,层级数就是密文可承受的乘法深度上限。
  1. 模数分解初始模数 q 由基础模数 $q_0$ 乘以缩放因子的 L 次幂构成,每个 Δ 对应一根滤芯。
    q=q0⋅ΔLq = q_0 \cdot \Delta^{L}
  2. 对数换算对 q 与 $q_0$ 的比值取以 Δ 为底的对数,得到层级总数。
    log⁡Δqq0\log_{\Delta} \frac{q}{q_0}
  3. 向下取整层级必须取整数,余量被舍去,以保证每一步模运算都留有安全边界。
    ⌊⋅⌋\lfloor \cdot \rfloor

AI 应用场景

医疗影像隐私推理

阶段:模型推理(Inference)

解决的问题:医院有影像数据,AI 公司有训练好的病灶筛查模型,双方互不信任。传统方案要么把影像明文发给公司(泄露患者隐私),要么把模型部署到医院(泄露模型权重)。CKKS 方案让医院加密影像、在 AI 公司侧直接推理、拿回密文结果,模型和影像都各安其位。

具体案例:某三甲医院与 AI 公司合作肺癌 CT 筛查,用 TenSEAL 封装后的 CNN 对加密影像推理,精度从明文 96.2% 降至 94.8%,单张影像延迟约 3 秒,满足非实时筛查需求;影像全程不出医院。

金融风控黑盒评分

阶段:模型服务(Serving)

解决的问题:中小银行想用头部平台的信用评分模型,但既不愿把客户明文数据送出,平台也不愿开源模型权重。CKKS 把客户数据加密后发送给平台,平台用明文权重在密文上计算评分,返回密文分数,客户自行解密——双方都只交出“最小必要信息”。

具体案例:某消费金融公司接入反欺诈评分模型:每笔申请加密后推理,端到端延迟 1.2 秒,误拒率增加 0.3 个百分点,通过率几乎不变;平台方也无法还原任何单笔申请的特征。

联邦学习梯度聚合

阶段:训练与聚合(Training & Aggregation)

解决的问题:多个机构联合训练时,梯度本身包含敏感信息,明文聚合的服务器一旦被攻破就会泄露各机构数据分布。CKKS 加密梯度后上传,服务器在密文上做加权平均聚合,更新只以密文形式存在,即使服务端被攻破也无法还原单机构梯度。

具体案例:某慢病预测联盟用 CKKS 聚合多家医院梯度,相比明文联邦学习,模型精度下降约 0.5%,通信量增大数倍但可接受,成为首个通过伦理审查的跨院联邦训练项目。

Python 演示

python63 行
import mathimport random random.seed(42)  class MiniCKKS:    """迷你CKKS: 演示SIMD打包、近似运算、噪声与重缩放."""     def __init__(self, scale=1024, noise_std=5.0, level=6):        self.scale = scale        self.noise_std = noise_std        self.level = level     def encrypt(self, vec):        code = [round(v * self.scale) for v in vec]        return [c + random.gauss(0, self.noise_std) for c in code]     def decrypt(self, cipher):        return [v / self.scale for v in cipher]     def add(self, c1, c2):        return [a + b for a, b in zip(c1, c2)]     def add_plain(self, cipher, vec):        return [a + round(v * self.scale) for a, v in zip(cipher, vec)]     def mul_plain(self, cipher, w):        return [w * a for a in cipher]     def square(self, cipher):        if self.level <= 0:            raise RuntimeError("层级耗尽, 需要自举Bootstrap")        self.level -= 1        return [a * a / self.scale for a in cipher]  def relu_sq(v):    return v / 2 + v * v / 4  def main():    sim = MiniCKKS()    x = [0.7, -0.3, 1.0, -0.5, 0.8, -0.2, 0.4, 0.9]    w = [0.5, -0.8, 0.3, 0.6, -0.4, 0.2, -0.9, 0.7]    b = 0.1    ref = [relu_sq(w[i] * x[i] + b) for i in range(8)]     c = sim.encrypt(x)    lin = sim.add_plain(sim.mul_plain(c, w), [b] * 8)    act = sim.add(sim.mul_plain(lin, 0.5), sim.square(lin))    got = sim.decrypt(act)     num = math.sqrt(sum((g - ref[i]) ** 2 for i, g in enumerate(got)))    den = math.sqrt(sum(r * r for r in ref))    print("明文参考:", " ".join("%.4f" % r for r in ref))    print("密文输出:", " ".join("%.4f" % g for g in got))    print("相对误差ReLE: %.4f" % (num / den if den else 0.0))    print("剩余层级(滤芯):", sim.level)  if __name__ == "__main__":    main()
目的:用纯 Python 模拟一个简化版 CKKS,演示“8 个浮点数打包进一个密文 → 加权求和 → 多项式 ReLU 近似 → 解包还原”的完整链路,并输出相对误差。
  1. code = [round(v * self.scale) for v in vec]编码:把浮点向量放大成整数,模拟“放大镜 Δ”
  2. return [c + random.gauss(0, self.noise_std) for c in code]加密:注入高斯噪声,模拟“先弄脏再上锁”
  3. return [w * a for a in cipher]标量乘:权重作为明文直接叠加到每个槽位
  4. return [a * a / self.scale for a in cipher]自乘加重缩放:每平方一次消耗一个层级并压噪
  5. lin = sim.add_plain(sim.mul_plain(c, w), [b] * 8)用乘加组合计算 W·x + b 的密文
  6. act = sim.add(sim.mul_plain(lin, 0.5), sim.square(lin))用 x/2 + x²/4 拼出 ReLU 近似
  7. got = sim.decrypt(act)解包得到近似明文,与参考值对比
  8. print("相对误差ReLE: %.4f" % (num / den if den else 0.0))输出精度指标,验证“雾气浓度”

常见误区

误区我以为 CKKS 和普通加密一样,私钥一解,出来的数字必须和原来一模一样,稍微差一点就是方案有问题。

正解CKKS 的设计哲学就是“近似”:解密得到 m + e,e 是小噪声。它把“精确”换成了“足够精确”。就像戴手套写钢笔字,笔画略有抖动但别人完全能认出写的是什么。神经网络本身有容错性,1~2 个百分点的精度损失通常完全可用。

为何常见:教科书里的加密方案(AES、RSA)都强调可逆精确,初学者会默认“解密必须精确”才是安全方案。

误区我以为 Level 和噪声是同一个东西,重缩放也是想用几次用几次,等于可以无限精确。

正解Level 是硬预算:每一根滤芯用一次少一根,用光必须自举。噪声是连续变量:每做一次乘法涨一截。重缩放同时做两件事——压噪声、扣滤芯。滤芯是天花板,噪声是实时水位,两者相关但绝不是一回事。

为何常见:教程常把“层级预算”和“噪声增长”画在同一张图上,概念相近又同步变化,初学者极易混淆。

误区ReLU 不就是 max(x, 0) 吗?既然能算加法和乘法,为什么不能直接算 ReLU?

正解max 依赖“比较大小”,而比较依赖明文的值,密文里看不到值就没法判断。唯一办法是把 ReLU 近似成多项式,比如 x/2 + x²/4。这个近似是精度损失的固定来源之一。

为何常见:神经网络教程从不说“ReLU 需要多项式近似”,因为在明文世界里它是免费原语;切换到密文世界,“免费”变成了“昂贵且不精确”。

误区既然自举能刷新噪声,那网络想多深就多深,成本无所谓。

正解自举的计算开销通常是普通密文乘法的数十到数百倍,实现复杂且会引入额外延迟。工程上的共识是:优先设计低深度网络,把滤芯预算用满,只在关键节点才用一次自举。

为何常见:论文摘要里“自举可实现任意深度计算”这句话被过度简化,忽略了背后巨大的工程代价。

面试题

Q1. CKKS 方案与 BGV、BFV 等经典方案最核心的区别是什么?

单选题 · easy

  • A.CKKS 支持整数精确运算
  • B.CKKS 支持浮点近似运算← 正确答案
  • C.CKKS 只能做加法运算
  • D.CKKS 不需要任何密钥

答案:B。CKKS 的核心特征是“近似算术”:解密得到明文加小噪声,而不是精确相等。BGV、BFV 支持整数精确运算,CKKS 专为浮点设计,用小误差换取完整的加乘能力。就像戴厚手套写字,字迹略抖但可辨认。

Q2. 在 CKKS 编码中,缩放因子 Δ 的主要作用是什么?

单选题 · mid

  • A.降低方案的安全强度
  • B.放大浮点并抑制舍入误差← 正确答案
  • C.压缩密文的存储体积
  • D.加速多项式的乘法速度

答案:B。Δ 把浮点小数放大成整数再进行环运算,使量化取整造成的相对误差被压到很小。它不降低安全性、不压缩密文、也不加速 NTT。就像把 0.37 放大成大整数再运算,尾巴上的误差就看不见了。

Q3. 在 CKKS 中,控制噪声增长的有效方法有哪些?

多选题 · mid

  • A.执行重缩放操作← 正确答案
  • B.执行自举刷新噪声← 正确答案
  • C.无限增加缩放因子
  • D.压缩密文乘法次数← 正确答案

答案:ABD。重缩放每次乘法后压一轮噪声,自举把噪声彻底刷新到初始水平,减少乘法次数从源头控制噪声增长。只增大缩放因子并不能改变噪声增长速度,反而更快耗尽模数空间。滤芯 + 换气扇 + 少开火,三招齐下才能让柜子长期运转。

Q4. 用 CKKS 计算 ReLU 激活函数的常规做法是什么?

单选题 · mid

  • A.在密文中比较大小判断正负
  • B.用多项式近似拟合 ReLU 函数← 正确答案
  • C.解密后执行再重新加密
  • D.用查表法预存所有结果

答案:B。密文不支持“比较大小”这类依赖明文的分支操作,解密再加密破坏保密性,查表法对连续输入不可行。多项式只由加法和乘法组成,是唯一常规途径。就像评估员不能“看着数字决定”,只能用固定手势模拟“如果”。

Q5. SIMD 打包对神经网络推理的最大好处是什么?

单选题 · easy

  • A.增强密文噪声规模
  • B.一次计算多个明文槽位← 正确答案
  • C.消除密钥交换流程
  • D.替代全部激活函数

答案:B。SIMD 把整个向量塞进一个密文,一次运算更新所有隔间,等价于把单样本推理变成批量推理。噪声、密钥、激活都不受它影响。这就是玻璃柜的“一套手势盖多个章”。

Q6. 影响 CKKS 密文推理精度的因素包括哪些?

多选题 · hard

  • A.编码取整舍入误差← 正确答案
  • B.重缩放除法舍入误差← 正确答案
  • C.激活函数多项式近似误差← 正确答案
  • D.私钥排列顺序偏移

答案:ABC。编码把浮点量化成整数(丢尾巴),重缩放每次除法取整(继续磨),激活多项式本身就是系统性偏差。私钥长度影响安全性,不直接影响推理精度。三种误差对应“放大镜、换气扇、模拟手势”各自的瑕疵。

Q7. 在 CKKS 中,“层级 Level”的含义是什么?

单选题 · hard

  • A.剩余可用乘法次数预算← 正确答案
  • B.明文向量的长度规格
  • C.神经网络隐藏层的数量
  • D.密钥生成算法的轮数

答案:A。一个 Level 代表一次可进行的密文乘法预算;每次乘法后重缩放消耗一级。它与向量长度、网络层数、密钥轮数无关。滤芯根数 = 层级,网络能走多深由滤芯决定。

Q8. 以下哪项是 CKKS 最典型的应用场景?

单选题 · easy

  • A.对密文图像做隐私保护推理← 正确答案
  • B.对明文文本做关键词检索
  • C.对密文数据进行无损压缩
  • D.对明文字段做格式校验

答案:A。CKKS 的招牌场景是“数据加密外包、模型明文参与”的隐私推理,如加密医疗影像做病灶筛查。明文检索、无损压缩、格式校验都不需要同态加密,直接明文处理更高效。

Q9. 关于 CKKS 在 Transformer 推理中的挑战,下列说法正确的有哪些?

多选题 · extreme

  • A.Softmax 含指数需多项式近似← 正确答案
  • B.注意力乘法消耗层级预算← 正确答案
  • C.模型权重必须为密文参与
  • D.输出可跳过重缩放直接复用

答案:AB。Softmax 的指数不是多项式,必须用近似;自注意力的大量矩阵乘法会快速消耗 Level。常见隐私推理场景中模型权重作为明文参与,无需加密;密文乘法后必须重缩放,不能跳过。对应“注意力柜”中的三个难点。

Q10. 自举 Bootstrap 在 CKKS 中的本质作用是什么?

单选题 · mid

  • A.刷新噪声并恢复计算精度← 正确答案
  • B.压缩密文尺寸节省存储
  • C.转换明文模型为密文
  • D.扩展密钥生成的轮数

答案:A。自举通过一轮复杂的同态解密过程,把噪声压回初始水平,从而“续命”,但计算开销极大。它不减小密文、不转换模型、也不改变密钥轮数。相当于在柜内自动更换整套滤芯,代价是耗时极长。