规范嵌入与逆规范嵌入

2 阅读1分钟

CKKS 中 规范嵌入(Canonical Embedding)逆规范嵌入(Inverse Canonical Embedding) 机制


一、 核心痛点:为什么需要规范嵌入?

在同态加密(基于 Ring-LWE 假设)的世界里,存在一个严峻的“数据格式不兼容”问题:

  1. 密码学世界的语言是多项式: CKKS 所有的加密、解密和同态运算,都必须在一个被称为“分圆环(Cyclotomic Ring)”的特定数学结构中进行: Rq=Zq[X]/(XN+1)R_q = \mathbb{Z}_q[X]/(X^N+1)

    这意味着,底层密码机只能处理形如 m(x)=c0+c1x+c2x2++cN1xN1m(x) = c_0 + c_1x + c_2x^2 + \dots + c_{N-1}x^{N-1} 的多项式(阶数为 NN,系数对 qq 取模)。

  2. 现实世界的数据是向量: 在机器学习或统计分析中,我们要处理的数据是一维数组(向量),比如由浮点数组成的 z=[z1,z2,,zk]T\vec{z} = [z_1, z_2, \dots, z_k]^T

规范嵌入的作用,就是作为“完美翻译官”,把数据向量 z\vec{z} 打包编码进一个多项式 m(x)m(x) 的系数中,并在计算完成后再把结果解码还原出来。 并且,它利用了多项式环的代数同构特性,赋予了同态加密强大的 SIMD(单指令多数据流) 并行计算能力。

二、 数学基石:特殊的“取值点”(单位根)

为了在多项式和向量之间建立映射,我们需要确定一组“锚点”。

因为多项式所在的环是对 XN+1X^N + 1 取模的,所以我们寻找方程 XN+1=0X^N + 1 = 0 在复数域 C\mathbb{C} 上的所有根。

根据欧拉公式,这 NN 个根被称为分圆根(Roots of Unity),记为 ζ1,ζ2,,ζN\zeta_1, \zeta_2, \dots, \zeta_N

它们的数学表达式为(假设 M=2NM=2N):

ζk=ei(2k1)πN\zeta_k = e^{\frac{i \cdot (2k-1)\pi}{N}}

核心性质:NN 个根在复平面上均匀分布在单位圆上,并且它们天然是成对的共轭复数。也就是说,如果存在一个根 ζ\zeta,必定存在另一个根 ζˉ\bar{\zeta}


三、 完整过程:数学推导与矩阵公式

数据在 CKKS 中的流动,首先经历**逆规范嵌入(编码)进入密文世界,计算完成后,再经历规范嵌入(解码)**回到现实世界。

1. 编码过程:逆规范嵌入 (σ1\sigma^{-1}) —— 从向量求多项式

这是 CKKS 数据输入时(加密前)真正执行的操作。

我们的目标是:给定一个已知的数据向量 z=[z1,z2,,zN]T\vec{z} = [z_1, z_2, \dots, z_N]^T,求出一个未知的多项式 m(x)=c0+c1x++cN1xN1m(x) = c_0 + c_1x + \dots + c_{N-1}x^{N-1}

满足的条件是:当把单位根 ζi\zeta_i 代入 m(x)m(x) 时,计算结果恰好等于向量中的元素 ziz_i

步骤 A:构建并求解线性方程组

NN 个根代入多项式,我们得到 NN 个方程:

m(ζk)=c0+c1ζk+c2ζk2++cN1ζkN1=zk(对 k=1,,N)m(\zeta_k) = c_0 + c_1\zeta_k + c_2\zeta_k^2 + \dots + c_{N-1}\zeta_k^{N-1} = z_k \quad (\text{对 } k = 1, \dots, N)

将这组方程写成极其优雅的矩阵乘法形式:

[1ζ1ζ12ζ1N11ζ2ζ22ζ2N11ζNζN2ζNN1]V (范德蒙德矩阵)×[c0c1cN1]c (未知的多项式系数)=[z1z2zN]z (你的业务数据)\underbrace{ \begin{bmatrix} 1 & \zeta_1 & \zeta_1^2 & \dots & \zeta_1^{N-1} \\ 1 & \zeta_2 & \zeta_2^2 & \dots & \zeta_2^{N-1} \\ \vdots & \vdots & \vdots & \ddots & \vdots \\ 1 & \zeta_N & \zeta_N^2 & \dots & \zeta_N^{N-1} \end{bmatrix} }_{V \text{ (范德蒙德矩阵)}} \times \underbrace{ \begin{bmatrix} c_0 \\ c_1 \\ \vdots \\ c_{N-1} \end{bmatrix} }_{\vec{c} \text{ (未知的多项式系数)}} = \underbrace{ \begin{bmatrix} z_1 \\ z_2 \\ \vdots \\ z_N \end{bmatrix} }_{\vec{z} \text{ (你的业务数据)}}

左侧由单位根组成的矩阵被称为 范德蒙德矩阵 (Vandermonde Matrix,记作 VV)

因为所有根 ζk\zeta_k 互不相同,矩阵 VV 是可逆的。我们只需两边左乘逆矩阵,即可求出多项式系数:c=V1×z\vec{c} = V^{-1} \times \vec{z}

步骤 B:满足“实数系数约束”(为何只能打包 N/2N/2 个数据?)

密码学 LWE 算法有一个物理限制:多项式 m(x)m(x) 的系数 cjc_j 必须全都是实数(R\mathbb{R}

这里引入一个基础代数定理:实系数多项式对于共轭复数输入,必然产生共轭复数输出。

证明如下: m(ζˉ)=j=0N1cj(ζˉ)j=j=0N1cjζj=j=0N1cjζj=m(ζ)m(\bar{\zeta}) = \sum_{j=0}^{N-1} c_j (\bar{\zeta})^j = \sum_{j=0}^{N-1} \overline{c_j \zeta^j} = \overline{\sum_{j=0}^{N-1} c_j \zeta^j} = \overline{m(\zeta)}

还记得前面说的吗?我们的取值点 ζ\zeta 都是成对的共轭复数。如果我们在根 ζ1\zeta_1 处强行塞入数据 z1z_1,那么在它的共轭根 ζˉ1\bar{\zeta}_1 处,输出的结果身不由己地变成了 zˉ1\bar{z}_1z1z_1 的复数共轭)。

  • 工程限制: 你的目标向量 z\vec{z} 不能完全乱填。它的前 N/2N/2 个槽位可以填你真实的业务数据;但后 N/2N/2 个槽位,必须严格填入前一半数据的共轭镜像!
  • 结论: 一个最高次幂为 N1N-1 的多项式,至多只能独立打包 N/2N/2 个数据

步骤 C:使用 FFT 加速运算(工程奇迹)

如果直接计算 V1×zV^{-1} \times \vec{z},矩阵乘法的复杂度是 O(N2)O(N^2)。当 N=32768N=32768 时,这种计算慢如蜗牛。

但是,因为 VV 的元素是呈周期性分布的分圆根,这个特定的矩阵求逆运算,在数学上等价于对向量做一次 逆离散傅里叶变换(IDFT)

通过使用 逆快速傅里叶变换(IFFT)算法,计算复杂度瞬间从 O(N2)O(N^2) 降到了 O(Nlog2N)O(N \log_2 N),让原本需要几分钟的编码过程在毫秒级内完成。

2. 解码过程:正向规范嵌入 (σ\sigma) —— 从多项式求向量

这是 CKKS 数据输出时(解密后)执行的操作。

当你在密文状态下完成了机器学习预测,解密后得到了一个结果多项式 mres(x)m_{res}(x)。此时你只需顺向执行一遍操作,把数据提取出来。

根据规范嵌入的定义,就是将 NN 个单位根代入多项式求值:

σ(mres)=(mres(ζ1),mres(ζ2),,mres(ζN))\sigma(m_{res}) = (m_{res}(\zeta_1), m_{res}(\zeta_2), \dots, m_{res}(\zeta_N))

写成矩阵形式,就是计算正向的范德蒙德乘法:

zres=V×cres\vec{z}_{res} = V \times \vec{c}_{res}

同理,工程上这等价于做一次 离散傅里叶变换(DFT),使用 FFT 算法极速提取出前 N/2N/2 个元素,就是你的最终预测结果。


四、 终极魔法:SIMD 与环同构的代数证明

费这么大劲折腾矩阵和多项式,根本原因是它能带来 SIMD(单指令多数据流),即“算一次多项式乘法,等于同时算几千次浮点数乘法”。

下面是用代数理论对这一奇迹的严格证明。

假设多项式 A(x)A(x)B(x)B(x) 分别编码了向量 a\vec{a}b\vec{b}。(A(ζ)=aA(\zeta)=\vec{a}B(ζ)=bB(\zeta)=\vec{b}

加法并行:

C(x)=A(x)+B(x)C(x) = A(x) + B(x)

代入 ζk\zeta_kC(ζk)=A(ζk)+B(ζk)=ak+bkC(\zeta_k) = A(\zeta_k) + B(\zeta_k) = a_k + b_k。 (向量按位独立相加)

乘法并行(最精妙之处):

在密码学环中,多项式相乘必须对 XN+1X^N+1 取模:

C(x)=A(x)B(x)(modXN+1)C(x) = A(x) \cdot B(x) \pmod{X^N+1}

根据多项式带余除法定理,必定存在一个商多项式 Q(x)Q(x) 使得:

A(x)B(x)=Q(x)(XN+1)+C(x)A(x) \cdot B(x) = Q(x)(X^N+1) + C(x)

现在,我们把单位根 ζk\zeta_k 代入这个等式左右两边:

A(ζk)B(ζk)=Q(ζk)(ζkN+1)+C(ζk)A(\zeta_k) \cdot B(\zeta_k) = Q(\zeta_k)(\zeta_k^N+1) + C(\zeta_k)

见证奇迹的时刻: 因为 ζk\zeta_k 是方程 XN+1=0X^N+1=0 的根,所以 ζkN+1=0\zeta_k^N+1 = 0 永远成立!

中间那一坨复杂的 Q(ζk)(ζkN+1)Q(\zeta_k)(\zeta_k^N+1) 直接灰飞烟灭,变成了 00

等式瞬间化简为:

C(ζk)=A(ζk)B(ζk)=akbkC(\zeta_k) = A(\zeta_k) \cdot B(\zeta_k) = a_k \cdot b_k

证明完毕:两个长达 NN 维的多项式在环内执行 1 次取模乘法,其效果在规范嵌入下,完美的等价于背后 N/2N/2 个独立数据进行了并行的按位乘法!


五、 CKKS 规范嵌入完整生命周期总结

我们梳理出数据在 CKKS 中的一生:

  1. 准备与镜像: 提取 N/2N/2 个真实业务浮点数,求其复数共轭补充到后 N/2N/2 个位置,拼成长度为 NN 的向量 z\vec{z}
  2. 编码 (σ1\sigma^{-1}): 求解 c=V1z\vec{c} = V^{-1} \vec{z}(工程上通过 IFFT 加速),将向量转化为拥有实数系数的未加密多项式 m(x)m(x)
  3. 缩放取整: 将实数系数 cic_i 乘以巨大的 Scale 因子 Δ\Delta(如 2402^{40})并四舍五入,变成符合密码学要求的整数多项式
  4. 加密与密态计算: 送入 LWE 密码机加密,利用上述证明的 SIMD 同构特性,在密文状态下只需执行 1 次多项式运算,即完成成千上万条数据的批量计算。
  5. 解密与重缩放: 密钥解密还原出结果多项式,除以 Δ\Delta 恢复原本的精度级别。
  6. 解码 (σ\sigma): 求解 zres=Vcres\vec{z}_{res} = V \vec{c}_{res}(工程上通过 FFT 加速),将单位根代入多项式,顺利提取出最终的业务计算结果。

六、计算案例

在处理 Transformer 或大语言模型(LLM)时,输入的数据通常是多个 Token,且每个 Token 都有一个多维的 Embedding(特征向量)。

如果在现实工程中,你要加密处理 2 个 Token,且每个 Token 的 Embedding 是 2 维的,同态加密工程师会使用一个极其巧妙的行业标准优化技巧:复数打包(Complex Packing)

6.1 行业技巧:如何把 2D 向量塞进 CKKS?

在上一节我们讲过,CKKS 因为复数共轭的限制,最多只能提供 N/2N/2 个数据槽位。 如果采用“平铺打包”(把数字排成一排),我们需要 4 个槽位来放这 4 个数字。

但由于 CKKS 天然原生支持复数运算,业界更聪明的做法是:将一个 2D 特征向量 [x,y][x, y],直接映射为一个复数 x+yix + yi

  • Token 1 的特征[1.0,2.0][1.0, 2.0]     \implies 编码为复数 z1=1.0+2.0iz_1 = 1.0 + 2.0i
  • Token 2 的特征[3.0,4.0][3.0, 4.0]     \implies 编码为复数 z2=3.0+4.0iz_2 = 3.0 + 4.0i

这样做的好处是绝大的: 2 个 Token,无论特征维度如何,只需要占用 2 个复数槽位!所以我们依然可以使用极小的迷你多项式环(N=4N=4,容量为 2)。

业务目标: 我们要在密文下,给 Token 1 的特征乘以注意力权重 2.0,给 Token 2 乘以权重 3.0

  • 期望结果:Token 1 变成 [2.0,4.0][2.0, 4.0];Token 2 变成 [9.0,12.0][9.0, 12.0]

下面是见证魔法的详细推导。 (参数设定:N=4N=4,模数 X4+1X^4+1,取值点 r1=0.707+0.707ir_1 = 0.707 + 0.707ir2=0.707+0.707ir_2 = -0.707 + 0.707i,缩放因子 Δ=100\Delta = 100

6.2 第一步:准备数据与镜像 (Data Preparation)

  • 输入向量 A(Token特征): z1=1+2iz_1 = 1+2i, z2=3+4iz_2 = 3+4i

    为了满足多项式系数是实数的物理定律,向量后半段必须是前半段的共轭:

    完整向量:zToken=[1+2i,3+4i,34i,12i]T\vec{z}_{Token} = [1+2i, \quad 3+4i, \quad 3-4i, \quad 1-2i]^T

  • 输入向量 B(Attention权重): 我们要把权重也写成复数(虚部为0)。

    完整向量:zWeight=[2+0i,3+0i,30i,20i]T\vec{z}_{Weight} = [2+0i, \quad 3+0i, \quad 3-0i, \quad 2-0i]^T

6.3 第二步:逆规范嵌入 (编码为多项式)

计算机底层快速解范德蒙德方程组,寻找多项式 M(x)=c0+c1x+c2x2+c3x3M(x) = c_0 + c_1x + c_2x^2 + c_3x^3

这里我为你手工解出了精确的解析解(代入上述 r1,r2r_1, r_2 等根):

1. Token 特征多项式 MA(x)M_A(x)

计算得出:

MA(x)=2.0+1.414x1.0x2+2.828x3M_A(x) = 2.0 + 1.414x - 1.0x^2 + 2.828x^3

(把 r1=0.707+0.707ir_1 = 0.707+0.707i 代入这个实数多项式,结果等于 1.0+2.0i1.0 + 2.0i!这也证明了实系数多项式能生成复数!)

2. 权重多项式 MW(x)M_W(x)

计算得出:

MW(x)=2.50.3535x+0x2+0.3535x3M_W(x) = 2.5 - 0.3535x + 0x^2 + 0.3535x^3

6.4 第三步:缩放与取整 (变身密码学数据)

乘以 Δ=100\Delta = 100 并去掉小数,变成可被加密的纯整数多项式:

  • MA(x)=200+141x100x2+283x3M_A'(x) = 200 + 141x - 100x^2 + 283x^3

  • MW(x)=25035x+0x2+35x3M_W'(x) = 250 - 35x + 0x^2 + 35x^3

    随后,系统将它们加上噪声,加密成谁也看不懂的密文。


6.5 第四步:密文并行计算 (SIMD 乘法爆发)

在完全加密的状态下,计算机直接将这两个乱码多项式相乘。底层等价于发生以下运算: C(x)=MA(x)×MW(x)(modX4+1)C(x) = M_A'(x) \times M_W'(x) \pmod{X^4+1}

1. 暴力展开相乘(产生 16 个项并合并):

  • 常数项: 200×250=50000200 \times 250 = 50000
  • xx 项: (200×35)+(141×250)=7000+35250=28250x(200 \times -35) + (141 \times 250) = -7000 + 35250 = 28250x
  • x2x^2 项: (141×35)+(100×250)=493525000=29935x2(141 \times -35) + (-100 \times 250) = -4935 - 25000 = -29935x^2
  • x3x^3 项: 200×35+(100×35)+283×250=7000+3500+70750=81250x3200 \times 35 + (-100 \times -35) + 283 \times 250 = 7000 + 3500 + 70750 = 81250x^3
  • x4x^4 项: (141×35)+(283×35)=49359905=4970x4(141 \times 35) + (283 \times -35) = 4935 - 9905 = -4970x^4
  • x5x^5 项: (100×35)=3500x5(-100 \times 35) = -3500x^5
  • x6x^6 项: (283×35)=9905x6(283 \times 35) = 9905x^6

2. 模 X4+1X^4+1 代数消元(降维打击): 因为密码学规定 x4+1=0x^4 + 1 = 0,所以 x4=1x^4 = -1, x5=xx^5 = -x, x6=x2x^6 = -x^2。代入化简超高次项:

  • 4970x4+4970-4970x^4 \Rightarrow +4970 (并入常数项)
  • 3500x5+3500x-3500x^5 \Rightarrow +3500x (并入 xx 项)
  • 9905x69905x29905x^6 \Rightarrow -9905x^2 (并入 x2x^2 项)

3. 最终结果多项式:

  • 常数:50000+4970=5497050000 + 4970 = 54970
  • xx 项:28250+3500=31750x28250 + 3500 = 31750x
  • x2x^2 项:299359905=39840x2-29935 - 9905 = -39840x^2
  • x3x^3 项:81250x381250x^3

得出的密文计算结果: MRes(x)=54970+31750x39840x2+81250x3M_{Res}'(x) = 54970 + 31750x - 39840x^2 + 81250x^3


6.6 第五步:解密与重缩放

我们拿私钥解开密文,得到上面的结果。此时因为经历了乘法,Scale 膨胀成了 100×100=10000100 \times 100 = 10000。 将所有系数除以 1000010000 恢复浮点数:

MFinal(x)=5.497+3.175x3.984x2+8.125x3M_{Final}(x) = 5.497 + 3.175x - 3.984x^2 + 8.125x^3

6.7 第六步:正向规范嵌入 (解码提取 Embedding)

最激动人心的时刻到了。刚才我们只是将两个多项式做了一次纯粹的代数相乘,现在我们来看看,把单位根代进去,能提取出那两个 Token 的 2D 特征吗?

提取 Token 1 结果(代入 r1=0.707+0.707ir_1 = 0.707 + 0.707i): (注意:r12ir_1^2 \approx i, r130.707+0.707ir_1^3 \approx -0.707 + 0.707i) MFinal(r1)=5.497+3.175(0.707+0.707i)3.984(i)+8.125(0.707+0.707i)M_{Final}(r_1) = 5.497 + 3.175(0.707+0.707i) - 3.984(i) + 8.125(-0.707+0.707i)

  • 实部 (Token 1 的维度1): 5.497+2.2445.744=1.9975.497 + 2.244 - 5.744 = 1.997 \approx 2.02.0
  • 虚部 (Token 1 的维度2): 2.244i3.984i+5.744i=4.004i2.244i - 3.984i + 5.744i = 4.004i \approx 4.04.0

    \implies 结果解码为 [2.0,4.0][2.0, 4.0]。完全匹配业务预期 ([1.0,2.0]×2.0)([1.0, 2.0] \times 2.0)

提取 Token 2 结果(代入 r2=0.707+0.707ir_2 = -0.707 + 0.707i): (注意:r22ir_2^2 \approx -i, r230.707+0.707ir_2^3 \approx 0.707 + 0.707i) MFinal(r2)=5.497+3.175(0.707+0.707i)3.984(i)+8.125(0.707+0.707i)M_{Final}(r_2) = 5.497 + 3.175(-0.707+0.707i) - 3.984(-i) + 8.125(0.707+0.707i)

  • 实部 (Token 2 的维度1): 5.4972.244+5.744=8.9975.497 - 2.244 + 5.744 = 8.997 \approx 9.09.0
  • 虚部 (Token 2 的维度2): 2.244i+3.984i+5.744i=11.972i2.244i + 3.984i + 5.744i = 11.972i \approx 12.012.0

    \implies 结果解码为 [9.0,12.0][9.0, 12.0]。完全匹配业务预期 ([3.0,4.0]×3.0)([3.0, 4.0] \times 3.0)

(注:出现 1.9971.9974.0044.004 的微小误差,正是因为我们在第三步使用了极小的 Scale Δ=100\Delta=100。在真实的 CKKS 中,Δ\Delta 通常是 2402^{40},算出来的结果会是 2.0000000012.000000001,完美等价于浮点数精度。)