
简介面向隐私保护与深度学习的交叉方向这套资源以 Python 卷积神经网络为核心解决加密云端图像在不泄露原始内容的前提下完成分类的问题。适用人群覆盖零基础入门者和进阶学习者可作为毕业设计、课程设计、大作业或初期项目立项的参照方案。压缩包共 2004 个文件主体由 1296 个 JS 脚本、356 个 Markdown 文档和 341 个 JSON 配置/数据文件组成另含少量 HTML 测试页、Python 源码与 TXT 说明整体约 148.84MB。其中 Markdown 文档适合梳理系统架构与分类流程JSON 文件便于调整参数和数据集配合前端请求脚本与测试演示页面可快速搭建交互环境直观理解加密图像分类的请求与响应机制。当前已有 116 人学习浏览通过学习目录组织与关键代码可完整掌握从模型推理到前端交互的实现链路方便二次开发成自定义系统原型。1. 加密云端图像的隐私分类让CNN在看不见明文的云端把类别算出来想象一个真实场景医院把CT影像交给云端做病灶分型合同上写着“数据不出域”影像本身却早已被传了上去。加密图像分类就是把这张图像先加密再上云让云端服务在密文上完成卷积、全连接和类别判定最后只把标签送回来。这不等同于“加密传输”传输加密只保护链路云端模型看到的还是明文而这里要解决的是模型也看不到明文还能给出正确分类。基于python卷积神经网络的加密图像分类系统难点从来不在训练一个图像分类模型而在让CNN的卷积和激活函数屈从于同态加密的运算规则。适合动手的人群正在做隐私计算应用、医工交叉项目或评估云端SaaS图像服务合规方案的技术人员。下面先讲清楚为什么CNN在密文里会“失灵”再给一套能在本地复现的最小闭环。2. 全同态推理与混合架构加密图像分类的选型依据2.1 同态加密与CKKS能在密文上做加法和乘法的黑匣子普通加密注重“解密后还原”RSA这类公钥算法虽然也有少量同态性质但运算次数非常受限拿来做CNN推理基本不可行。真正能支撑图像分类的是全同态加密Fully Homomorphic Encryption, FHE给定密文 c1、c2不需要密钥就可以算出 Enc(m1m2)、Enc(m1×m2)。CNN里的卷积、全连接、批归一化推理期本质就是乘加组合理论上都在FHE射程内。FHE的实现算法主要有BFV和CKKS两系。BFV做整数精确运算适合统计查询这类“结果必须可整除”的场景CKKS做浮点近似运算允许结果带少量可控误差而图像特征图恰恰能容忍这种近似。所以落地加密图像分类我一般优先选CKKS。用CKKS有一个概念必须先立住密文域每次乘法后噪声会膨胀系统通过 rescale 操作把噪声压回去而 rescale 需要消耗“层级level”。层级用尽之前必须解密否则误差会盖过信号。这就是为什么CKKS参数表里总有一串 coeff_mod_bit_sizes它直接决定了模型能跑多深。2.2 为什么不在密文域训练区分训练态与推理态把训练好的CNN放进密文域推理是隐私分类项目实施时的通用做法。有些团队会先问能不能把训练也搬进密文域答案是代价极大。反向传播里的梯度计算涉及除法、比较、softmaxFHE能做但一轮 epoch 跑上几个小时很正常数据增强、洗牌、Mini-batch 采样这些训练期依赖随机性的环节在密文域也会全部失效。更别说模型收敛时你需要反复看验证集曲线训练过程本身没法全黑盒。所以常见做法是明文侧训练、密文侧推理。这里要分清两个安全边界被保护的是推理时的输入图像不是模型参数。训练好的权重以明文配置形式交给云端推理节点这个环节通过部署流程和密钥管理去管控图像由客户端加密后上云云端的推理程序只见过 Enc(image)、Enc(feature_map)私钥只留在客户端手里。以这个边界为前提整个系统的落地难度会降一个数量级。顺便说一句transformer图像分类模型的自注意力核心是矩阵乘理论上也能同态实现但注意力矩阵需要 softmax密文域做除法会剧烈消耗噪声预算。目前实际跑通的隐私图像分类方案几乎都是CNN不是越来越大的transformer结构。这不是原理上禁止而是工程代价暂时不划算。2.3 混合推理架构可落地的密文分类系统数据流纯全同态推理整网所有层都在密文域论文里很常见真正部署到云上却很吃力。最不好啃的是激活函数ReLU需要比较操作同态实现要用多项式近似近似程度差一点分类精度就掉几个点。工程上因此普遍采用混合架构将网络按层拆成“线性层”和“非线性层”。卷积、全连接、平均池化保留在密文域ReLU、最大池化、softmax 这类非线性挪到客户端明文域执行。中间特征图在客户端解密、激活、再加密回云端私钥不下放。一张 32×32 灰度图在整条链路中的流转可以归纳成下表的顺序客户端明文做 padding、切块加密后上传云端用明文卷积核对密文块做内积返回密文特征图客户端解密做平方激活、平均池化、再加密送回重复这个过程直到算出最后一层 logits客户端解密取 argmax。表格里的每个节点后端开发都能对应到一段具体代码。环节数据形态归属方图像采集与预处理明文图像客户端切块与加密上传密文向量序列客户端卷积与全连接计算密文中间特征云端激活与池化解密后的明文特征短暂客户端类别判定明文标签客户端或授权业务方这套混合架构还有一个明显好处客户端不需要持有模型权重云端不需要接触明文图像双方只交换密文中间结果。就算云端日志被拖走攻击者拿到的也只是一堆没有私钥就无法还原的密文张量。3. 设计加密友好型CNN结构把ReLU换成平方激活后的关键调整3.1 为什么ReLU是密文域最先被淘汰的那一层卷积和全连接在密文域实现很直接明文权重与密文向量做内积FHE天然支持。但ReLU不一样它对每个元素做 max(0, x)涉及密文域的大小比较CKKS自身没有原生的比较算子只能用多项式近似逼近。多项式近似引入的误差会随网络深度逐层累积加密图像分类系统的精度崩盘十次有八次出在这一层。我在第一个版本里试过用泰勒展开的ReLU近似多项式阶数取低了分类准确率从基准的 94% 掉到 82%阶数取高了乘法次数增加噪声预算又不够。反复调之后结论很明确与其费劲近似ReLU不如直接换一个“天生同态友好”的激活函数。平方激活 x² 只需要一次密文乘法不涉及比较不动用旋转密钥是工程上最省事的替代品。代价是它只有单侧响应网络容量会损失一些要靠后面几层补回来。3.2 把ReLU换成平方激活训练收敛和数值范围的调整换成 x² 之后第一个要改的就是数值范围。ReLU输出与输入同量级x² 会把大于1的数值平方放大。卷积层的输出如果落在 0~2 之间经过一次平方后输出变成 0~4两层之后特征图数值就可能膨胀到几十。密文域里数值越大与明文缩放因子的比值就越难控制最终导致解密后精度急剧下降。训练时我一般做三件事第一把网络初始化标准差从默认的 xavier 缩小到 0.5 倍控制卷积输出在 -1~1 之间第二加大权重衰减让卷积核的模长偏小避免大权重把激活推向高值区第三在平方激活之前加一个可学习的线性缩放系数这个系数在明文侧训练推理时折算进前一层的权重。这个系数是一维标量解密后乘上即可不增加密文乘法次数。经过这几处调整x² 网络的收敛速度会慢一些但最终精度可以追到 ReLU 版本的 2~3 个百分点以内。3.3 一个加密友好的小型CNN示例层表与训练参数下面是我在实际项目里用过的一个加密友好网络结构输入是 32×32 单通道图像面向 10 分类。它的原则很明确卷积和全连接层全部保留激活统一为 x²池化选择平均池化并放到客户端明文侧执行。最大池化需要比较操作在密文域代价高这里弃用批归一化在推理期可以折叠进卷积权重但训练期涉及除法和均值统计我直接用权重衰减替代它的正则作用。网络层配置输出尺寸执行环境Conv13×3, 16通道, stride116×30×30云端密文域Act1x²16×30×30客户端明文域AvgPool12×2, stride216×15×15客户端明文域Conv23×3, 32通道, stride132×13×13云端密文域Act2x²32×13×13客户端明文域AvgPool22×2, stride232×6×6客户端明文域Conv33×3, 64通道, stride164×4×4云端密文域Act3x²64×4×4客户端明文域FC11024→128128云端密文域Act4x²128客户端明文域FC2128→1010云端密文域训练参数上我用 Adam学习率 1e-3批大小 128训练 60 个 epoch权重衰减 5e-4。注意学习率不能沿用 ReLU 网络常用的 1e-2 起步x² 的梯度形态与 ReLU 差异大太大的学习率在第三个 epoch 就会让损失直接震荡。图像分类数据集下载后也不要全量训练先切出 500 张图跑通加密推理链路再上全量。4. 用 Python TenSEAL 跑通密文分类最小闭环代码与参数说明4.1 环境准备Python 里的同态加密库怎么选Python 生态里做同态加密TenSEAL 是目前最顺手的库它封装了CKKS的编码、加密、内积和 rescaleAPI 风格贴近 NumPy。PySyft 也能做但它的设计更偏向多方安全计算框架把简单内积包装得比较重初次上手容易迷失在协议概念里。我的建议是先掌握 TenSEAL 再扩展到其他框架。环境安装很简单Python 3.8 以上执行pip install tenseal即可如果安装过程中卡在编译多半是系统缺少 C 构建工具链python 安装教程里关于编译器环境的部分值得提前看一眼。pip install tenseal torch安装后不要急着写业务代码先做一个 10 秒的冒烟测试生成上下文、加密一个向量、解密对比数值误差。CKKS是近似加密解密结果与明文存在 1e-3 到 1e-4 量级的误差这是正常现象如果误差到了 1e-1 以上说明参数配置不合理后面所有推理结果都不可信。4.2 明文预处理切块、编码与加密CKKS 密文本质是一个多项式向量一次能打包 8192 个实数以 8192 多项式模数为例。CNN 的滑动窗在密文域没法直接索引所以工程上常见的妥协是在明文侧把图像切块每个块加密成一个独立的 CKKS 向量云端对该向量与卷积核做内积。切块操作可以用torch.nn.functional.unfold一次完成。import torch import torch.nn.functional as F import tenseal as ts def build_context(): # 多项式模数 8192系数模数三段式60bit 输入层 40bit 中间层 60bit 输出层 ctx ts.context( ts.SCHEME_TYPE.CKKS, poly_modulus_degree8192, coeff_mod_bit_sizes[60, 40, 40, 60] ) ctx.global_scale 2**40 # 生成旋转密钥后续特征图重排或矩阵乘法会用到 ctx.generate_galois_keys() return ctx def encrypt_image_patches(image_tensor, kernel_size3, padding1): # image_tensor: [1, 1, 32, 32] patches F.unfold(image_tensor, kernel_sizekernel_size, paddingpadding) # patches shape: [1, 9, 1024]1024 是滑动窗口数量 ctx build_context() enc_patches [] for i in range(patches.shape[-1]): enc_patches.append(ts.ckks_vector(ctx, patches[0, :, i].tolist())) return enc_patches, ctx这里每个 patch 是 9 维向量加密成一个 CKKS 密文。滑动窗口总数等于输出特征图的像素数在 32×32 输入下是 1024 个。代码里的poly_modulus_degree8192决定了单次密文可打包的数据维度与安全强度越低越快但越不安全global_scale2**40是明文在编码时的放大倍数图像像素是浮点数缩放越大精度越高但噪声预算消耗也更快。4096 的模数多项式跑这个小网络性能更好但安全强度会降到 80bit 以下不建议公网部署使用。4.3 密文域卷积明文权重与密文内积切块加密完成后云端收到的是 1024 个密文向量。卷积计算变成对每个密文向量执行一次同态内积密文向量不动卷积核展开成明文向量调用dot方法。这一步是密文域的全部核心计算不涉及任何解密操作。def conv3x3_enc(enc_patches, kernel_bias_list, out_h30, out_w30): kernel_bias_list: 每个输出通道一个 (weight_flat, bias) 元组 weight_flat 长度 16 * 1 * 3 * 3 对应的展平 conv_features [] for weights, bias in kernel_bias_list: channel_out [] for enc_p in enc_patches: # 明文权重向量与密文向量做内积 val enc_p.dot(weights) channel_out.append(val bias) conv_features.append(channel_out) # 组织成 [C, H, W] 的密文张量后续交给客户端解密激活 return conv_featuresdot方法在 TenSEAL 内部会把明文向量编码进同一个 CKKS 多项式的相位位置再做一次 SIMD 式乘加然后通过 internal rescale 降低噪声。这里必须注意偏置bias不能以明文直接加在密文上——加法可以但 bias 数值过大会推高噪声最好在明文训练阶段就把bias合并进卷积核的某个常数维度。做法是给输入 patch 额外补一个固定为 1 的分量让卷积核最后一个权重等于 bias。这样偏置也变成乘法的一部分噪声控制更均匀。整个流程里我没有做旋转操作因为切块已经把空间索引关系暴露在明文侧。代价是密文数量等于输出像素数一次卷积会产生上千个密文对象。要减少密文数量可以在预处理阶段加大滑动步长或者把相邻 patch 打包进同一个 CKKS 向量——打包能充分利用 SIMD但会让代码复杂度上一个台阶。先跑通再优化是我做这类系统的习惯。4.4 客户端非线性层与二次加密混合推理循环密文卷积结束后云端把密文特征图交还客户端。客户端解密后做平方激活与平均池化再加密送回云端继续下一轮卷积。整个过程中传输的都是密文客户端私钥始终不出本地。下面的函数演示了单个卷积层之后的“解密→激活→池化→再加密”过程。def client_side_nonlinear(enc_feature, patch_h30, patch_w30, in_ch16, ctx): # 解密所有通道特征 plain_features [] for ch_data in enc_feature: channel_plain [] for val in ch_data: channel_plain.append(val.decrypt()[0]) plain_features.append(torch.tensor(channel_plain).view(1, 1, patch_h, patch_w)) x torch.cat(plain_features, dim1) # [1, C, H, W] x x ** 2 # 平方激活 x F.avg_pool2d(x, kernel_size2, stride2) # 平均池化 # 展平并重新切块加密 flat x.flatten().tolist()[0] enc_new ts.ckks_vector(ctx, flat) return enc_new注意最后一层全连接输出不需要激活客户端解密后直接取 argmax 就是分类标签。如果想要概率值就在明文侧跑一个 softmax这一段不在密文域不泄露中间特征之外的信息。整套流程跑完我得到的准确率大约是基准明文模型低 3~5 个百分点推理耗时在个人电脑上为秒级换到云端 GPU 实例后可压缩到毫秒级。5. 避坑与排查加密图像分类系统最容易翻车的五个地方5.1 像素缩放到 [0, 1] 时精度反而更差现象明文模型在 [0, 1] 归一化下训练得很好换到加密推理后准确率掉到 50% 上下接近随机猜测。排查发现密文解码的特征图数值几乎被噪声淹没。原因CKKS 的global_scale2**40是针对有效数值范围设计的图像像素在 [0, 1] 区间时卷积核均值也接近 0.01 量级乘积累加后信号幅度远低于量化噪声。解决推理链路里把输入像素调整到 [0, 16] 或 [0, 32] 范围或者把第一层卷积核整体乘上一个放大系数训练时同步调整输出 logits 对应的分类阈值要重新标定。5.2 深度超过 3 层后噪声预算断崖现象第 1、2 个卷积层解密后特征图还干净第 3 层开始特征图变成白噪声。原因每做一次密文乘法噪声按多项式模数的底数增长coeff_mod_bit_sizes[60, 40, 40, 60]中间只有两级 40bit 用于 rescale只够支撑有限次乘法。解决一是把网络改浅比如去掉一个卷积层换大卷积核二是增大系数模数把中间层改成[60, 45, 45, 45, 60]但密文体积和运算耗时同步上涨三是检查每一层是否有多余的密文与密文乘法——明文权重与密文内积只产生一层噪声密文与密文相乘会让噪声翻倍要坚决避免。5.3 程序卡在矩阵运算前Galois 密钥缺失现象4.2 节代码里不调用generate_galois_keys()程序跑到某个张量旋转或矩阵乘法时报错提示 Galois keys 不存在。原因TenSEAL 中旋转密钥不是默认生成而矩阵乘、通道重排这些操作在 CKKS 底层依赖旋转。解决在构建上下文后立即生成并把密钥序列化保存云端只需要公钥和旋转密钥私钥由客户端持有。私钥一旦在生成后丢失所有密文都无法解密这不算 bug但要在系统设计时把密钥备份纳入运维流程。5.4 最后输出层用了 softmax 导致解密值溢出现象全连接层输出正确但客户端做 softmax 时 exp 后数值爆炸返回 nan。原因密文域最后一层输出没有做 scale 控制logits 可能高达几十softmax 里的 exp(30) 在双精度浮点下已经接近溢出阈值。解决不要在密文域或解密后立刻做 softmax。把全连接输出减去最大值明文侧做这一步再对差值做 exp 和归一化或者干脆不做 softmax直接用 argmax。分类任务在乎的是排名顺序softmax 只是让输出像概率不是必需的。5.5 密钥和中间结果分离做错隐私保护沦为摆设现象系统上线后审计发现云端日志里明文中间特征图偶尔出现原因是客户端解密激活后调试时顺手把特征图写进了日志文件。原因混合架构中客户端短暂持有明文特征图这是架构设计允许的但明文数据被持久化后攻击者拿到日志就等于拿到原始图像的稀疏表征。解决明文特征图只允许驻留在内存禁止写磁盘和日志在所有调试接口后面加一层脱敏校验凡是包含明文特征图的响应体都要拦截。密码学解决不了运维纪律问题这是隐私计算项目上线前最容易忽略的一条。6. 验收加密图像分类系统从精度、噪声预算和推理耗时三个维度把关6.1 三个必测指标第一个指标是精度损失。明文模型准确率为基准加密推理准确率与它的差值控制在 5 个百分点以内才可接受。第二个指标是噪声剩余预算。解密前检查密文对象的noise_budget低于 20bit 说明参数余量不足上线后有波动就可能翻车。第三个指标是单次推理耗时。32×32 输入在网络带宽 100Mbps 的模拟环境下单次分类延迟要低于 3 秒才具备线上体验价值否则只能走异步任务队列。指标合格线说明精度损失≤5%与明文基准对比噪声预算剩余≥20bit低于 20bit 需调整参数单次推理延迟≤3s本地 CPU 参考值6.2 我常用的两个调参起点第一输入分辨率先缩到 32×32 再放大。很多隐私分类需求来自医疗影像原始图动辄 1024×1024直接加密会让 patch 数量爆炸。我会先在 32×32 上把链路跑通确认精度损失符合预期再用双线性插值下采样或裁剪推进到 128×128。第二通道数从 16 起步不要一上来就堆 64、128。密文域的计算量正比于乘积通道数通道翻倍延迟接近指数上涨。先用小通道模型拿到可接受精度再逐步加宽同时观察噪声预算是否还撑得住。做第一个加密分类版本时我在噪声预算上栽过一次网络结构和明文版完全一样只是换了激活函数结果第三层卷积直接失效特征图全是雪花点。后来才意识到问题不在激活函数本身而是我把池化也留在了密文域平均池化里的除法一样会产生噪声。从那以后我给自己定了一条规矩密文域只留乘加任何除法、比较、非多项式运算全部挪到明文侧。这个习惯后来帮我少踩了很多坑。如果你是第一次搭这类系统忘掉“端到端全密文”的执念先按混合架构跑通一条最小链路性能和安全边界都会清晰得多。希望帮到你。本文还有配套的精品资源点击获取