加密模型推理:在加密数据上运行模型的方案

FreeGuideOnline 最新 2026-06-29

bash pip install pyfhel==3.4.3 torch numpy


### 2.2 生成密钥与上下文
```python
from Pyfhel import Pyfhel

HE = Pyfhel()
ckks_params = {
    'scheme': 'CKKS',   # 支持浮点近似
    'n': 2**14,         # 多项式模数多项数
    'scale': 2**30,     # 缩放因子
    'qi_sizes': [60, 30, 60]  # 模数链
}
HE.contextGen(**ckks_params)
HE.keyGen()
HE.relinKeyGen()        # 重线性化密钥,乘法后需要
HE.rotateKeyGen()       # 用于向量化旋转

2.3 模型准备与参数提取

我们假设一个极简的两层全连接网络(明文训练好):

import torch
import torch.nn as nn

class TinyModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(8, 4)
        self.fc2 = nn.Linear(4, 2)
    def forward(self, x):
        x = torch.relu(self.fc1(x))
        x = self.fc2(x)
        return x

model = TinyModel()
# 假设已有权重
weight1 = model.fc1.weight.data.numpy()
bias1   = model.fc1.bias.data.numpy()
weight2 = model.fc2.weight.data.numpy()
bias2   = model.fc2.bias.data.numpy()

2.4 数据加密(客户端)

用户将自己的数据加密:

# 假设输入是一个8维向量
input_data = np.array([0.5, -0.2, 1.1, -0.9, 0.3, 0.7, -1.2, 0.0])

# 编码并加密为CKKS密文(需要批量编码,这里简化为单样本)
# 实际中通常将多个样本打包到同一个密文中以提高效率(batch axis)
ct_input = HE.encodeFrac(input_data)  # 先编码为多项式
ct_input = HE.encryptFrac(ct_input)   # 再加密得到密文对象

2.5 同态线性层运算

服务器持有模型权重(明文),直接在密文上执行矩阵乘法+偏置。CKKS支持密文*明文,以及密文加法。

# 第一层:Y = X·W + b
def encrypted_linear(ct_x, weight, bias, HE):
    # 权重每一行与密文向量做内积,需要先转置处理
    output_cts = []
    for i in range(weight.shape[0]):  # 输出维度
        # 密文乘以明文向量:通过逐元素乘法然后求和
        row_sum = None
        for j in range(weight.shape[1]):
            # 将权重[i,j]作为明文标量,与密文第j个元素相乘
            # Pyfhel中,密文乘明文标量:HE.multiply_plain(ct, pt)
            pt_val = weight[i,j]
            term = HE.multiply_plain(ct_x[j], pt_val)  # ct_x是密文向量的第j个槽位提取? 
            # 注意:以上为概念演示,实际需要使用槽位旋转与向量化。
            if row_sum is None:
                row_sum = term
            else:
                row_sum += term
        # 加上偏置
        row_sum += bias[i]
        output_cts.append(row_sum)
    return output_cts  # 此时每个元素仍为密文

说明:真正的实现需利用CKKS的SIMD特性,通过旋转不同槽位高效完成向量内积。为了清晰,这里简化了操作。

2.6 非线性激活的近似

ReLU函数在FHE中不能直接计算,需要用多项式近似,如 f(x) = 0.5x + 0.25x^2(简单低阶)或更精确的 0.15625x^2 + 0.5x + 0.28125(二阶)以适应定义域。

def poly_relu(ct, HE):
    # 平方
    ct_sq = HE.multiply(ct, ct)        # 密文平方
    HE.relinearize(ct_sq)              # 重线性化减小密文规模
    # 多项式: 0.25*x^2 + 0.5*x
    ct_025 = HE.multiply_plain(ct_sq, 0.25)
    ct_05  = HE.multiply_plain(ct, 0.5)
    ct_res = ct_025 + ct_05
    return ct_res

2.7 结果解密

将最后层的密文返回客户端:

# 客户端解密
result_ct = final_ct[0]  # 假设单输出
result_pt = HE.decryptFrac(result_ct)[:2]  # 解码前2个槽位
print("解密预测结果:", result_pt)