简介:本资源是一套完整的毕业设计级项目源码,面向计算机、人工智能、通信工程等专业的本科生及初学者,聚焦联邦学习中关键的安全聚合问题,通过Paillier同态加密实现客户端本地模型参数的密文上传与服务器端安全聚合,兼顾隐私保护与模型可用性。压缩包共55个文件,包含21个预训练模型文件(.model)、9个核心Python脚本(含服务端/客户端逻辑、加密模块、聚合算法实现)、4个Windows批处理启动脚本(bat),以及README.md说明文档、IDE配置文件(.iml)和证书密钥(.cer/.key)等,整体体积仅2.08MB,结构清晰、开箱即用。已有112人下载学习,代码经实际运行验证,答辩平均分达96分,配套完整目录组织与模块化设计,支持直接部署调试、二次开发拓展或作为课程设计/毕设原型快速迭代。
1. 这不是“加个加密壳就叫安全”的联邦学习玩具:一个答辩96分、真能跑通的同态加密聚合系统,专治模型上传被窃取、梯度泄露、中心服务器作恶这三类毕业设计高频翻车点
你是不是也见过这样的毕设演示:客户端本地训练完,把模型参数明文发给服务器,服务器一聚合就完事——美其名曰“联邦学习”,实则连基础通信加密都没有?更别说攻击者截获梯度后反推原始数据、或中心服务器偷偷替换聚合结果这类真实威胁。这个项目不是概念验证,而是用 Python 实实在在跑通了「Paillier 同态加密 + 安全聚合」闭环:客户端对本地梯度做同态加密后上传,服务器在密文上直接求和(不接触明文),再下发密文聚合结果,客户端各自解密还原全局模型更新。整个过程无明文梯度出域,答辩现场用act_server.bat和act_client.bat一键启停,三台模拟客户端+一台服务器,5分钟内完成一轮完整训练与聚合。它适合计算机、人工智能、信息安全方向的本科生做毕设或课程设计——不是让你从零造轮子,而是给你一套可调试、可修改、可讲清楚每行代码作用的生产级骨架。如果你正卡在“怎么让联邦学习真正防住服务器”这个关键问题上,或者导师问“你们怎么保证聚合过程不泄露用户数据”,这份源码就是你答辩时能打开 IDE 现场演示的后悔药。
2. 为什么选 Paillier 而不是 RSA 或 CKKS?从数学原理到工程落地的四层选型逻辑
2.1 同态加密选型不是“越新越好”:Paillier 在联邦聚合场景下的不可替代性
联邦学习中的安全聚合,核心诉求是「多个客户端上传的梯度向量,在服务器端做加法运算,且全程不暴露单个梯度值」。这就要求加密方案支持加法同态(ciphertext₁ ⊕ ciphertext₂ = Enc(plain₁ + plain₂)),且最好支持标量乘法同态(k ⊗ ciphertext = Enc(k × plain)),因为梯度聚合常需加权平均(如按样本数加权)。Paillier 恰好满足这两点,且密文长度可控、解密开销远低于全同态(FHE)方案。相比之下,RSA 仅支持乘法同态(Enc(a)×Enc(b)=Enc(a×b)),要实现加法得绕道 ElGamal 或做复杂编码;CKKS 虽支持近似加减乘,但浮点精度损失大、密文膨胀严重,对本科生调试极不友好。本项目选用phe(Python Homomorphic Encryption)库实现 Paillier,正是因为它封装了密钥生成、加密、同态加、标量乘、解密全流程,且底层用 C 扩展加速,避免纯 Python 实现的秒级延迟。
提示:
phe不是pyfhel或SEAL的轻量版替代,而是专为 Paillier 优化的独立实现。它的paillier.generate_paillier_keypair()生成的密钥对,私钥含 λ 和 μ(Carmichael 函数相关参数),公钥含 n 和 g(模幂基),这些在models/keys/目录下以.pkl形式保存,后续所有加密操作都依赖此密钥体系。
2.2 项目结构即运行逻辑:从src/到act_server.bat的五层调用链
项目目录不是随意堆砌,而是严格对应联邦学习的生命周期:
src/:核心逻辑层。server.py实现聚合服务器(监听端口、接收密文梯度、同态相加、广播结果);client.py实现客户端(加载本地数据、训练模型、加密梯度、发送密文、解密聚合结果);encryption.py封装phe调用,提供encrypt_vector()和decrypt_vector()接口,将向量级操作映射到底层标量加密。models/:模型与密钥层。models/base_model.py定义 PyTorch 模型骨架(如简单 CNN),models/keys/存放预生成的 Paillier 密钥(public_key.pkl,private_key.pkl),这是整个系统信任锚点——所有客户端必须用同一公钥加密,服务器用对应私钥解密。data/:数据模拟层。不含真实敏感数据,而是用torchvision.datasets.MNIST或sklearn.datasets.make_classification()生成合成数据集,通过--num_clients=3参数控制模拟客户端数量,每个客户端分配不同子集(非IID 划分已内置)。scripts/:启动胶水层。act_server.bat本质是python src/server.py --port 8080 --num_clients 3;act_client_by_num.bat则循环启动多个python src/client.py --client_id 0 --port 8080实例,实现一键多客户端部署。.idea/和.gitignore:开发环境适配层。PyCharm 配置文件确保你在 IDE 中能直接 debugclient.py,.gitignore已排除__pycache__/、models/keys/*.pkl(密钥不提交)等敏感项。
2.3 一行命令启动服务器:act_server.bat的真实执行路径与参数含义
双击act_server.bat并非黑盒操作,其内容展开如下:
@echo off cd /d "%~dp0" python src/server.py --port 8080 --num_clients 3 --max_rounds 10 --log_level INFO pausecd /d "%~dp0":切换到当前 bat 文件所在目录(即项目根目录),确保后续python src/server.py能正确找到相对路径下的模块。--port 8080:服务器监听端口,客户端默认连接此端口。若端口被占用,可改为--port 8081,同时需同步修改act_client.bat中的--port参数。--num_clients 3:声明预期连接的客户端数量。服务器会阻塞等待全部 3 个客户端注册完毕才开始第一轮聚合,避免“少一个客户端就卡死”的尴尬。--max_rounds 10:最大训练轮数。每轮包含:服务器广播当前模型 → 客户端本地训练 → 加密上传梯度 → 服务器同态聚合 → 下发密文结果。超过此轮数自动退出。--log_level INFO:日志级别。调试时可改为DEBUG查看每步密文长度、解密耗时等细节;答辩演示用INFO即可,输出精简。
注意:
server.py内部使用socketserver.TCPServer实现简易 TCP 服务,非 HTTP。这意味着你不能用浏览器访问http://localhost:8080,而必须用配套客户端连接。这种设计牺牲了 Web 友好性,换来了更低的通信开销和更清晰的密文传输边界。
2.4 客户端启动的本质:act_client.bat如何模拟分布式环境
act_client.bat内容实为:
@echo off cd /d "%~dp0" start "Client_0" python src/client.py --client_id 0 --port 8080 --data_path data/client_0/ --model_path models/base_model.pth start "Client_1" python src/client.py --client_id 1 --port 8080 --data_path data/client_1/ --model_path models/base_model.pth start "Client_2" python src/client.py --client_id 2 --port 8080 --data_path data/client_2/ --model_path models/base_model.pth pausestart "Client_X":Windows 下启动独立 CMD 窗口,每个窗口运行一个客户端进程,模拟真实分布式场景。窗口标题显示Client_0等,便于区分日志。--client_id X:客户端唯一标识。服务器用此 ID 区分不同来源的密文梯度,避免混淆。--data_path data/client_X/:每个客户端读取专属数据子集。项目已预处理 MNIST 数据,按 client_id 划分到data/client_0/、data/client_1/等目录,确保非IID(Non-IID)分布——这是联邦学习的典型挑战,也是本项目验证安全性的前提(即使数据分布偏斜,加密聚合仍有效)。--model_path models/base_model.pth:初始模型权重文件。所有客户端从同一份.pth开始训练,保证起点一致。
关键细节:客户端在
client.py中执行encryption.encrypt_vector(grad_vector)时,并非对整个梯度张量逐元素加密(那会爆炸式增长密文体积),而是先grad_vector.flatten()展平为一维数组,再对每个浮点数做量化(如int(grad * 1000)转为整数),最后调用public_key.encrypt(int_value)。量化因子1000在encryption.py中硬编码,你可根据模型梯度范围调整(如10000适应更大梯度),但需同步修改解密后的反量化系数。
3. 安全聚合的三重校验:从密文加法正确性到梯度还原保真度的全流程验证
3.1 第一重校验:密文加法是否真等于明文加法?用phe的raw_add做原子测试
同态加法的正确性是整个系统的基石。不能只信文档,必须亲手验证。在src/test_encryption.py中加入以下测试:
from phe import paillier import numpy as np # 生成临时密钥对(仅测试用) pub_key, priv_key = paillier.generate_paillier_keypair(n_length=1024) # 明文向量 plain_a = np.array([1.23, -4.56, 7.89]) plain_b = np.array([-2.34, 5.67, -8.90]) # 量化并加密 def quantize_and_encrypt(vec, pub_key, scale=1000): return np.array([pub_key.encrypt(int(x * scale)) for x in vec]) cipher_a = quantize_and_encrypt(plain_a, pub_key) cipher_b = quantize_and_encrypt(plain_b, pub_key) # 同态加法(密文相加) cipher_sum = np.array([ca + cb for ca, cb in zip(cipher_a, cipher_b)]) # 解密验证 def decrypt_and_dequantize(cipher_vec, priv_key, scale=1000): return np.array([priv_key.decrypt(cv) / scale for cv in cipher_vec]) plain_sum_decrypted = decrypt_and_dequantize(cipher_sum, priv_key) print("明文 a + b:", plain_a + plain_b) print("解密后结果:", plain_sum_decrypted) print("误差(max abs):", np.max(np.abs((plain_a + plain_b) - plain_sum_decrypted)))- 输出应显示
误差(max abs): < 1e-6,证明 Paillier 加法同态性在浮点量化后仍保持高精度。 n_length=1024是密钥长度,影响安全性与性能。项目默认2048(见models/keys/generate_keys.py),测试时用1024加速;实际部署必须用2048或更高。- 量化尺度
scale=1000是关键参数:太小(如10)导致梯度信息丢失;太大(如100000)可能超出 Paillier 模幂运算的整数范围引发溢出。本项目经实测1000对 MNIST 梯度足够。
3.2 第二重校验:服务器聚合是否真的“不碰明文”?抓包分析 TCP 流量
安全聚合的核心承诺是“服务器无法获知单个客户端梯度”。验证方法不是看代码,而是用 Wireshark 抓包:
- 启动
act_server.bat,记下 PID(任务管理器中查看)。 - 启动
act_client.bat,待所有客户端连接成功。 - 在 Wireshark 中过滤
tcp.port == 8080,捕获服务器与任一客户端的通信。 - 查看应用层载荷(右键 → “Decode As” → “Raw”),你会看到一串无法识别的二进制数据——这就是 Paillier 密文。对比明文梯度(在
client.py的print("Local grad norm:", torch.norm(grad).item())处添加日志),确认密文长度(约 256 字节/元素)与明文(8 字节 float64)无任何可读关联。
提示:Wireshark 中若看到可读 JSON 或文本,说明你误用了 HTTP 协议或未启用加密。本项目 TCP 载荷是
pickle.dumps(cipher_vector)序列化后的二进制,天然不可读。
3.3 第三重校验:全局模型更新是否收敛?用test_convergence.py监控准确率曲线
安全不能以牺牲效用为代价。项目附带src/test_convergence.py,用于验证加密聚合后模型是否仍能收敛:
import torch from models.base_model import SimpleCNN from data.mnist_loader import load_mnist_for_client # 加载预训练的非加密联邦模型(baseline) baseline_model = SimpleCNN() baseline_model.load_state_dict(torch.load("models/baseline_fedavg.pth")) # 加载本项目的加密聚合模型 encrypted_model = SimpleCNN() encrypted_model.load_state_dict(torch.load("models/encrypted_aggregation_final.pth")) # 在测试集上评估 test_loader = load_mnist_for_client(client_id=-1, train=False) # 全局测试集 def evaluate(model, loader): model.eval() correct = 0 total = 0 with torch.no_grad(): for data, target in loader: output = model(data) _, pred = torch.max(output.data, 1) total += target.size(0) correct += (pred == target).sum().item() return 100 * correct / total baseline_acc = evaluate(baseline_model, test_loader) encrypted_acc = evaluate(encrypted_model, test_loader) print(f"Baseline FedAvg accuracy: {baseline_acc:.2f}%") print(f"Encrypted aggregation accuracy: {encrypted_acc:.2f}%") print(f"Accuracy gap: {abs(baseline_acc - encrypted_acc):.2f}%") # 应 ≤ 1.5%- 项目实测
MNIST上,加密聚合模型最终准确率97.2%,非加密 FedAvg98.5%,差距1.3%,在可接受范围内。 - 若差距 > 3%,优先检查量化尺度(
scale)是否过小,或训练轮数--max_rounds是否不足(默认 10 轮对 MNIST 偏少,可增至 20)。
4. 避坑指南:答辩前必须扫清的五个“看似正常实则致命”的常见问题
4.1 现象:双击act_server.bat后窗口一闪而逝,日志没输出
原因:Python 环境未正确配置,或缺失phe、torch等依赖。bat文件执行python命令时找不到解释器,或导入失败导致异常退出。
解决:
- 在 CMD 中手动执行
python --version,确认 Python 3.8+ 已安装且在 PATH 中; - 运行
pip install -r requirements.txt(项目根目录下有此文件,若无则创建:phe==1.4.0 torch==1.13.1 scikit-learn==1.2.2); - 在
act_server.bat末尾加pause前插入echo Python path: %PATH%和python -c "import phe; print('phe OK')",定位具体失败点。
4.2 现象:客户端报错ConnectionRefusedError: [WinError 10061]
原因:服务器未启动,或端口不匹配。常见于先双击act_client.bat再启动服务器,或修改了act_server.bat的--port但忘了同步改act_client.bat。
解决:
- 严格按顺序操作:先运行
act_server.bat,待窗口显示Server started on port 8080... Waiting for 3 clients后,再运行act_client.bat; - 检查两个 bat 文件中的
--port参数是否完全一致; - 用
netstat -ano | findstr :8080查看端口占用情况,若有残留进程,用taskkill /PID <PID> /F强制结束。
4.3 现象:服务器日志显示Received gradient from client 0,但后续无聚合动作,卡在Waiting for all clients...
原因:--num_clients参数与实际启动的客户端数量不符。例如act_server.bat设为--num_clients 3,但act_client.bat只启动了 2 个客户端(如start命令被注释掉一行)。
解决:
- 核对
act_client.bat中start命令的数量,必须与--num_clients值相等; - 在
server.py的handle_client_registration()函数中,添加print(f"Registered client {client_id}, total: {len(self.clients)}")日志,实时观察注册数量; - 若需动态调整客户端数,修改
act_server.bat的--num_clients并同步更新act_client.bat中的start行数。
4.4 现象:训练多轮后准确率不上升,甚至下降,loss波动剧烈
原因:Paillier 量化引入的舍入误差在多轮累积,或学习率未适配加密场景。明文 FedAvg 常用lr=0.01,但加密后梯度精度下降,需降低学习率。
解决:
- 在
client.py的train_local_model()函数中,将optimizer = torch.optim.SGD(model.parameters(), lr=0.01)改为lr=0.005; - 检查
encryption.py中的量化尺度scale:若梯度范数普遍 > 10,则scale=1000可能不足,尝试scale=5000并重新生成密钥(models/keys/generate_keys.py); - 添加梯度裁剪:在
client.py计算梯度后插入torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0),防止大梯度放大量化误差。
4.5 现象:README.md提到“答辩评审平均分96分”,但自己运行效果差强人意
原因:忽略环境差异。原作者在PyTorch 1.13.1 + Python 3.9下调试,而你的环境可能是PyTorch 2.0+(API 变更)或Python 3.11(phe兼容性问题)。
解决:
- 创建隔离环境:
python -m venv fed_env→fed_env\Scripts\activate.bat→pip install -r requirements.txt; - 降级 PyTorch:
pip uninstall torch→pip install torch==1.13.1+cpu -f https://download.pytorch.org/whl/torch_stable.html(CPU 版); - 若
phe安装失败,改用pip install git+https://github.com/asecurityteam/phe.git@v1.4.0指定 commit。
5. 进阶技巧:如何把这套系统改成“可答辩演示的黄金 5 分钟”——从启动到展示安全性的全流程话术与操作清单
5.1 答辩演示黄金流程:5 分钟精准控制,每一秒都服务于“安全可信”这个核心论点
不要试图讲完所有代码,聚焦三个镜头式操作:
| 时间 | 操作 | 话术要点 | 预期效果 |
|---|---|---|---|
| 0:00–1:30 | 双击act_server.bat,待出现Waiting for 3 clients... | “老师请看,这是安全聚合服务器。它只暴露一个端口(8080),不存储任何客户端数据,也不接触明文梯度——所有计算都在密文上进行。” | 展示服务器纯净性,建立“中心不作恶”第一印象 |
| 1:30–3:00 | 双击act_client.bat,三窗口弹出,观察各窗口日志滚动 | “现在启动三个模拟客户端。注意每个窗口标题是 Client_0/1/2,它们加载不同数据(非IID),独立训练后,将加密后的梯度上传——这里的关键是,上传内容是密文,服务器无法解密单个梯度。” | 强调“加密上传”动作,用 Wireshark 截图佐证(提前准备) |
| 3:00–5:00 | 切换到server.py代码,定位aggregate_gradients()函数,高亮cipher_sum = sum(cipher_grads)行 | “请看服务器核心逻辑:它只做密文相加(+运算符重载),不调用任何解密函数。聚合完成后,下发密文结果,客户端各自解密——服务器永远不知道自己加的是什么,这才是真正的安全聚合。” | 直击技术本质,用代码行证明“不碰明文” |
提示:提前录制 30 秒屏幕录像(服务器启动→客户端启动→Wireshark 抓包显示密文→代码高亮
cipher_sum = sum(...)),答辩时嵌入 PPT,比口头描述有力十倍。
5.2 必备答辩话术包:把技术细节转化为评委能听懂的价值点
当被问“Paillier 和 RSA 有什么区别?”
“RSA 是乘法同态,要实现加法得把数字转成指数形式(a→g^a),效率极低且易受共模攻击;Paillier 原生支持加法同态,密文大小稳定,解密快,更适合梯度聚合这种高频加法场景。”当被问“量化会不会影响模型精度?”
“会,但可控。我们用 scale=1000 量化,相当于保留三位小数精度。实测 MNIST 上精度损失仅 1.3%,远低于联邦学习固有的 Non-IID 数据导致的 3-5% 损失——安全增益远大于精度代价。”当被问“如果服务器被黑客控制怎么办?”
“这正是本系统的设计目标。即使服务器被攻破,黑客只能拿到密文梯度和聚合结果,没有私钥就无法解密。我们把信任锚点从‘服务器可信’转移到‘密钥安全保管’,而私钥始终在客户端本地,从未上传。”
5.3 从“能跑通”到“能讲透”的最后一道工序:修改README.md的三处关键注释
原README.md过于简略,答辩前务必补充:
在“运行步骤”后增加“安全验证步骤”:
## 安全验证步骤(答辩必备) 1. 启动服务器后,用 Wireshark 抓包 `tcp.port == 8080`,确认载荷为不可读二进制(即密文); 2. 查看 `server.py` 第 127 行 `cipher_sum = sum(cipher_grads)`,确认无 `decrypt()` 调用; 3. 运行 `python src/test_convergence.py`,输出准确率差距 ≤ 1.5%。在“目录结构”中为
models/keys/添加警示:- `models/keys/`: **密钥存储目录(严禁提交到 Git)** `public_key.pkl`: 所有客户端共用的公钥,用于加密; `private_key.pkl`: 服务器私钥,**必须严格保密**,解密时使用。在“注意事项”里明确环境约束:
> ⚠️ 重要:本项目在 Python 3.9 + PyTorch 1.13.1 + phe 1.4.0 下验证通过。 > 若使用更高版本,请先测试 `phe` 兼容性(`python -c "from phe import paillier; paillier.generate_paillier_keypair()"`)。
从那以后我每次准备毕设答辩,都会强制走一遍这三步:先用 Wireshark 抓包确认密文传输,再打开server.py找sum(cipher_grads)行截图,最后跑test_convergence.py记下准确率数字——不是为了炫技,而是让“安全聚合”这个词从 PPT 里的空洞概念,变成评委亲眼所见、亲手可验的确定事实。希望帮到你。
本文还有配套的精品资源,点击获取