联邦学习实战:跨校成绩预测与Streamlit可视化平台搭建 简介该资源面向计算机、人工智能、数据科学等专业的学生与开发者提供一套基于联邦学习的高校学生成绩预测完整研究方案可用于毕业设计、课程设计或初期项目立项演示。项目采用Python实现涵盖FedProx、Ditto、APFL、FedRep等多种联邦学习算法并借助Streamlit搭建了简易可视化平台便于直观查看训练过程与预测结果。压缩包共55个文件以18个py源码、28个pyc编译文件、7个csv数据集及1个png混淆矩阵图、1个md说明文档为主整体约2.25MB结构清晰便于按模块阅读与二次开发。资源内代码均经测试运行成功功能正常。目前已有272人学习下载读者可从中获取完整的联邦学习建模流程、多算法对比实验代码、样本数据与评估结果适合作为实战练习、算法复现与论文写作的参考素材。1. 联邦学习做成绩预测为什么“数据不出校”比模型精度更难搞定高校学生成绩预测这件事单校数据量小、特征维度高、分布差异大直接把几所学校的数据汇总到一台服务器上训练精度确实能上去但学籍、成绩、行为记录这些数据的跨校流转在合规层面基本走不通。联邦学习给出的思路是模型参数可以走原始数据不动各校在本地训练、只上传梯度或权重由聚合端合并成全局模型。这个标题对应的方案就是把这套机制落到成绩预测任务上再用 Streamlit 搭一个能看、能点、能演示的可视化平台。它适合两类人一类是想把联邦学习从论文公式拉到可运行代码的在校开发者另一类是手里有多校脱敏数据、想验证“不集中数据能不能训出可用模型”的工程同学。下面按我实际搭这套东西的顺序讲重点放在能复现的步骤和参数上。2. 成绩预测任务怎么拆成联邦学习能吃的形状2.1 先定标签和特征别急着上联邦框架成绩预测的标签通常有两种口径一种是回归直接预测某门课的期末百分制分数另一种是分类把学生划成“及格/不及格”或“优良中差”几档。我一般优先做二分类因为联邦场景下各校样本量差异大回归的 MSE 对分布偏移太敏感分类的 AUC 和 F1 更容易横向比较。特征侧常见的是三类历史成绩类前序课程均分、学分绩点、行为类出勤、作业提交率、平台登录频次、静态类专业、年级、入学方式。这里有个血泪经验行为类特征在不同学校的采集口径差别极大A 校有登录频次B 校可能只有考勤直接对齐会引入大量缺失。稳妥做法是先做特征交集只保留各参与方都有的字段缺失率超过 40% 的列直接砍掉。标签泄漏是另一个高频翻车点。比如用“本学期总评成绩”去预测“本学期期末成绩”两者高度相关模型 AUC 能到 0.99但上线毫无意义。构造特征时必须严格按时间切分用第 1 到第 N-1 周的记录预测第 N 周之后的结果任何在预测时点之后才产生的字段都不能进特征表。2.2 联邦学习的三种聚合路线成绩预测该选哪种联邦学习落地时主要有三条路线。横向联邦Horizontal FL适合各校特征相同、样本不同的情况这也是成绩预测最常用的设定每所学校是一个 client本地数据不出校只上传模型更新。纵向联邦Vertical FL适合同一批学生、不同机构持有不同维度数据比如学校有成绩、某平台有行为记录但需要样本对齐工程复杂度高。联邦迁移FedTransfer适合各校特征空间差异大、只有少量重叠的场景成绩预测里用得少。对成绩预测我建议从横向联邦的 FedAvg 起步。原因是各校特征经过交集对齐后基本一致样本按学生自然分布在不同 client聚合逻辑清晰。选型时还要确认一件事各校标签分布是否严重倾斜。如果 A 校不及格率 5%、B 校 30%FedAvg 的简单加权平均会被大样本 client 主导需要引入按类别重加权或 FedProx 这类带近端项的算法。2.3 用 PyTorch 写一个最小可跑的本地训练器联邦学习的 client 端本质就是一个普通训练脚本区别在于数据不落盘到中心、训练完只导出参数。下面是一个二分类成绩预测的本地训练器骨架输入是各校自己的 CSV输出是 state_dict。import torch import torch.nn as nn import pandas as pd from torch.utils.data import DataLoader, TensorDataset # 成绩预测用的轻量 MLP输入维度按特征数调整 class GradeNet(nn.Module): def __init__(self, in_dim): super().__init__() self.net nn.Sequential( nn.Linear(in_dim, 64), nn.ReLU(), nn.Dropout(0.3), # 防过拟合小样本校尤其需要 nn.Linear(64, 32), nn.ReLU(), nn.Linear(32, 2) # 二分类及格/不及格 ) def forward(self, x): return self.net(x) def load_local_data(csv_path, feature_cols, label_col): df pd.read_csv(csv_path) # 缺失值用列均值填充联邦场景下不能跨校统计只能本地填 df[feature_cols] df[feature_cols].fillna(df[feature_cols].mean()) X torch.tensor(df[feature_cols].values, dtypetorch.float32) y torch.tensor(df[label_col].values, dtypetorch.long) return TensorDataset(X, y) def local_train(model, loader, epochs5, lr1e-3): opt torch.optim.Adam(model.parameters(), lrlr) loss_fn nn.CrossEntropyLoss() model.train() for _ in range(epochs): for xb, yb in loader: opt.zero_grad() loss loss_fn(model(xb), yb) loss.backward() opt.step() return model.state_dict()这段代码里三个参数最关键。Dropout(0.3)不是随便写的小样本学校几百条记录很容易过拟合dropout 低于 0.2 时验证集 AUC 会明显掉。epochs5是本地轮数联邦里叫 local epoch设太大 client 会跑偏、聚合后震荡设太小全局收敛慢5 到 10 是常见起点。lr1e-3配合 Adam如果某校样本量特别小可以降到 5e-4 减少单轮漂移。注意缺失值填充必须在本地做不能把各校均值汇总后再回填那等于泄露了分布信息。3. 把 FedAvg 聚合端和 Streamlit 平台接起来3.1 聚合端的加权平均逻辑与通信轮次控制聚合端要做的事很明确收集各 client 的 state_dict按样本量加权平均再把新全局模型发回去。加权是关键样本多的学校话语权大但也不能让小学直接被淹没所以常见做法是样本量开方后再归一化缓解极端不平衡。import torch import copy def fed_avg(global_model, client_states, client_sizes): # 按样本量开方加权避免大 client 完全主导 weights [s ** 0.5 for s in client_sizes] total sum(weights) weights [w / total for w in weights] new_state copy.deepcopy(global_model.state_dict()) for key in new_state.keys(): new_state[key] sum( w * cs[key].float() for w, cs in zip(weights, client_states) ) global_model.load_state_dict(new_state) return global_model通信轮次round一般设 20 到 50。轮次太少全局模型没学到跨校共性太多则通信开销大且可能过拟合到某些 client。我通常每轮记录全局模型在各方验证集上的 AUC画一条曲线当连续 5 轮 AUC 波动小于 0.005 就提前停。client_sizes必须用各校真实训练样本数不能用测试集数量否则加权失真。3.2 Streamlit 页面要展示哪四块内容可视化平台不是把训练日志堆上去就行得让看的人三秒内明白联邦到底带来了什么。我一般放四块第一块是数据概览各校样本量、正负样本比例、特征缺失率用表格加柱状图第二块是训练过程每轮全局 AUC、Loss 曲线以及各 client 的本地指标对比第三块是模型对比集中式训练把数据合到一起和联邦训练的指标并排直观看出精度损失有多大第四块是单样本预测输入一个学生的特征输出及格概率让非技术同学也能点着玩。import streamlit as st import pandas as pd import plotly.express as px st.set_page_config(layoutwide) st.title(联邦学习成绩预测平台) # 第一块各校数据概览 st.header(参与方数据概览) summary pd.read_csv(client_summary.csv) # 列学校代号,样本数,不及格率 st.dataframe(summary) fig px.bar(summary, x学校代号, y样本数, color不及格率) st.plotly_chart(fig, use_container_widthTrue) # 第二块训练曲线 st.header(联邦训练过程) history pd.read_csv(round_metrics.csv) # 列round,global_auc,global_loss st.line_chart(history.set_index(round)[[global_auc, global_loss]])client_summary.csv和round_metrics.csv由训练脚本每轮追加写入Streamlit 只负责读和画不要把训练逻辑塞进页面回调里否则刷新一次重训一次演示时必翻车。use_container_widthTrue让图表自适应投影演示时不会出现横向滚动条。3.3 本地跑通的最小命令与目录约定整套东西的目录我习惯这样放data/下按client_1.csv、client_2.csv命名models/存每轮全局模型logs/存指标 CSVserver.py是聚合入口client.py是本地训练入口app.py是 Streamlit 页面。跑通顺序是先分别跑各 client 生成初始参数再跑 server 聚合最后起页面。# 1. 各校本地训练一轮导出参数 python client.py --data data/client_1.csv --out models/c1_round0.pt python client.py --data data/client_2.csv --out models/c2_round0.pt # 2. 聚合端加载各校参数执行 FedAvg python server.py --clients models/c1_round0.pt models/c2_round0.pt \ --sizes 800 1200 --rounds 30 --out models/global.pt # 3. 启动可视化平台 streamlit run app.py --server.port 8501--sizes传的是各校训练样本数顺序必须和--clients一致这是最容易错的地方顺序反了加权就反了全局模型会偏向错误的一方。--rounds 30是上限实际会按早停条件提前结束。端口用 8501 是 Streamlit 默认演示机如果被占用就换 8502别去改代码里的硬编码。4. 联邦成绩预测最容易翻车的五个地方4.1 各校特征列名不一致对齐后全是 NaN现象聚合后全局模型 AUC 只有 0.5 左右查各校本地验证却正常。原因A 校列名是attendance_rateB 校是attend_rate合并时按列名对齐结果两边互相填 NaN填充后又都变成均值特征失效。解决在数据准备阶段强制一份feature_schema.json所有 client 按同一份 schema 重命名列缺列直接报错退出不要静默填充。4.2 标签定义不统一及格线各校不同现象联邦模型在 A 校表现好、B 校差。原因A 校 60 分及格B 校某课程 50 分就算过标签口径不一致全局模型学到一个折中的决策边界两边都不讨好。解决联邦启动前先对齐标签生成规则统一用百分制 60 分或统一用绩点阈值规则写进项目说明各校按同一份脚本重新打标。4.3 本地 epoch 设太大全局模型震荡不收敛现象全局 AUC 曲线上下跳几十轮都不稳。原因local epoch 设成 20 甚至 50每个 client 在本地过拟合自己的数据分布聚合时方向互相抵消。解决把 local epoch 降到 5 左右配合学习率 1e-3如果还不稳就上 FedProx在本地损失里加近端项约束 client 不要跑太远。4.4 用测试集样本量做聚合权重现象某校明明数据少但全局模型明显偏向它。原因聚合时client_sizes误传了测试集数量而测试集是人为平衡过的导致权重失真。解决权重必须用训练集真实样本数训练脚本里把len(train_loader.dataset)直接传给聚合端不要手工填。4.5 Streamlit 页面每次刷新都重训模型现象演示时页面卡死CPU 跑满。原因把训练逻辑写在了页面顶层或按钮回调里Streamlit 每次交互都重跑整个脚本。解决训练和展示彻底分离训练产物落盘成.pt和.csv页面只读文件如果确实要页面触发训练用st.cache_resource包住模型加载并且只加载不重训。5. 让联邦成绩预测从能跑变成能信验证与调参的收尾技巧模型能跑通只是第一步真正决定这套方案值不值得投入的是你能不能证明“联邦的精度损失在可接受范围内”。我的习惯是固定一个基线把所有 client 数据合到一起做集中式训练得到一个上界 AUC然后看联邦模型能到它的百分之多少。经验上横向联邦在成绩预测这类表格任务上AUC 损失通常在 0.02 到 0.05 之间如果损失超过 0.1基本可以判定是特征对齐或标签口径出了问题而不是算法本身不行。验证时别只看全局 AUC要按 client 拆开看。全局指标好看但某个小学掉得厉害说明加权策略或本地 epoch 需要调。我一般会做一张对比表把集中式、FedAvg、FedProx 三种设定在各校验证集上的 AUC 和 F1 并排列出谁在什么条件下更稳一目了然。设定全局 AUC小学 A AUC大学 B AUC通信轮次集中式基线0.860.840.87不适用FedAvg0.820.760.8430FedProx0.830.790.8430调参顺序我建议这样先固定 local epoch5、lr1e-3 跑一轮 FedAvg看各校差距差距大就把聚合权重从样本量改成开方加权还不行再上 FedProx把近端项系数从 0.01 开始试太大模型会退化成各校独立训练。通信轮次不用盲目加早停条件设好省下来的时间拿去做特征工程收益比多跑二十轮大得多。最后一个具体技巧把每轮的全局模型和各校本地模型都存下来页面里加一个“回放”下拉框能切换查看不同轮次的效果。演示时这比任何文字说明都有说服力评审的人自己点几下就明白联邦到底在干什么。我自己踩过最深的坑是早期没存中间轮次结果要对比时只能重训白白多花一晚上。希望帮到你。本文还有配套的精品资源点击获取