多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

BP神经网络实战:大学生消费预测模型从数据到部署

BP神经网络实战:大学生消费预测模型从数据到部署 简介这份资源面向本科及以上阶段、需要完成课程设计或数据建模实践的学习者围绕城乡大学生月平均消费数据用MATLAB实现BP神经网络预测模型帮助理解神经网络从数据加载、训练到结果输出的完整流程。压缩包共4个文件约323KB包含2个m脚本文件、1个xls数据表和1个doc实验报告脚本可直接运行并附有注释数据表提供建模所需的消费样本报告文档则梳理了实验背景与结果分析便于对照代码理解建模思路。目前已有65人学习下载。读者可据此掌握BP网络的参数设置、训练过程与预测评估方法并在此基础上替换数据或调整网络结构扩展至其他回归预测场景适合作为入门神经网络与MATLAB编程的实践参考。1. 大学生消费预测这件事为什么用 BP 神经网络能跑通月初生活费到账一周内花掉一半月底靠泡面续命——这个场景几乎每个大学生都经历过。把这件事做成一个可预测的模型就是「大学生消费预测」要解决的问题给定一个学生的性别、年级、月生活费、是否兼职、消费习惯等特征预测他下个月的消费金额或消费等级。它适合两类人一类是刚学完 BP 神经网络、想找一个完整数据集练手的学生另一类是想把预测结果用于校园贷风控、助学金精准发放、校园商户备货的从业者。为什么选 BP 神经网络而不是线性回归或 XGBoost线性回归只能拟合线性关系而消费行为里「生活费越高、消费增速反而放缓」这种非线性拐点很常见XGBoost 精度往往更高但需要调参经验对新手不友好。BP 神经网络结构简单、可解释性够用、代码量小是入门预测任务里性价比最高的选择。这一章先把「这是什么、能解决什么、适合谁」讲清楚后面几章带你从数据到模型完整跑一遍。2. 数据从哪来、怎么洗大学生消费数据集的字段与预处理2.1 一份能用的消费数据集该有哪些字段网上流传的大学生消费数据集质量参差不齐很多只有几百条、字段还残缺。一份能支撑 BP 神经网络训练的消费数据至少要包含三类字段人口属性性别、年级、专业类别、经济属性月生活费、是否兼职、兼职收入、家庭月收入档位、行为属性月消费总额、餐饮占比、购物占比、娱乐占比、是否使用分期。目标字段通常是「下月消费总额」或「消费等级低/中/高」。字段类型决定了预处理方式。性别、是否兼职这类二值字段做 0/1 编码年级、专业类别这类无序多分类字段做独热编码月生活费、消费总额这类连续字段做归一化。这里有个血泪经验很多人直接把原始金额丢进网络结果训练 loss 一直震荡不收敛就是因为量纲差异太大——生活费可能是 2000餐饮占比是 0.3网络会把大数值字段当成主导特征。2.2 用 pandas 做缺失值处理和归一化import pandas as pd import numpy as np from sklearn.preprocessing import MinMaxScaler # 读取数据假设文件为 student_consumption.csv df pd.read_csv(student_consumption.csv) # 1. 缺失值处理数值字段用中位数填充类别字段用众数填充 num_cols [monthly_allowance, part_time_income, total_consumption] cat_cols [gender, grade, major_type, use_installment] for col in num_cols: df[col] df[col].fillna(df[col].median()) for col in cat_cols: df[col] df[col].fillna(df[col].mode()[0]) # 2. 独热编码把无序类别字段展开 df pd.get_dummies(df, columns[grade, major_type], drop_firstTrue) # 3. 归一化只对连续字段做0/1 字段保持原样 scaler MinMaxScaler() df[num_cols] scaler.fit_transform(df[num_cols]) # 4. 划分特征和标签 X df.drop(columns[total_consumption]).values y df[total_consumption].values.reshape(-1, 1) print(特征维度:, X.shape, 标签维度:, y.shape)这段代码的逻辑是「先补缺、再编码、后归一」。fillna用中位数而不是均值是因为消费数据里高消费的极端值会拉高均值中位数更稳。get_dummies的drop_firstTrue是为了避免独热编码后的多重共线性虽然神经网络对共线性不敏感但少一列能减少参数量。MinMaxScaler把连续字段压到 [0,1]注意 scaler 要保存下来预测新样本时得用同一个 scaler 变换否则结果全错。参数上num_cols和cat_cols必须按你实际数据集的列名改别照抄。如果目标字段是消费等级而不是金额y要做 LabelEncoder 转成 0/1/2输出层神经元数也要从 1 改成 3。3. 用 PyTorch 搭一个能收敛的 BP 神经网络3.1 网络结构怎么定层数、神经元数、激活函数BP 神经网络结构图里最常见的是「输入层-隐藏层-输出层」三层结构。对于大学生消费预测这种特征维度在 10~30 之间的任务隐藏层设 1~2 层就够了每层神经元数取输入维度的 1~2 倍。层数堆太多几百条数据根本喂不饱直接过拟合。激活函数隐藏层用 ReLU输出层看任务预测金额用恒等函数不加激活预测等级用 Softmax。损失函数对应选 MSE 或 CrossEntropyLoss。优化器用 Adam学习率 0.001 起步这是最不容易翻车的组合。3.2 完整训练代码与关键参数说明import torch import torch.nn as nn from torch.utils.data import TensorDataset, DataLoader from sklearn.model_selection import train_test_split # 转成 tensor X_tensor torch.FloatTensor(X) y_tensor torch.FloatTensor(y) # 划分训练集和测试集8:2 X_train, X_test, y_train, y_test train_test_split( X_tensor, y_tensor, test_size0.2, random_state42) train_ds TensorDataset(X_train, y_train) train_loader DataLoader(train_ds, batch_size32, shuffleTrue) # 定义 BP 网络 class BPNet(nn.Module): def __init__(self, input_dim): super().__init__() self.net nn.Sequential( nn.Linear(input_dim, 64), nn.ReLU(), nn.Linear(64, 32), nn.ReLU(), nn.Linear(32, 1) # 回归任务输出 1 维 ) def forward(self, x): return self.net(x) model BPNet(X_train.shape[1]) criterion nn.MSELoss() optimizer torch.optim.Adam(model.parameters(), lr0.001) # 训练循环 for epoch in range(200): model.train() epoch_loss 0 for xb, yb in train_loader: pred model(xb) loss criterion(pred, yb) optimizer.zero_grad() loss.backward() optimizer.step() epoch_loss loss.item() if (epoch 1) % 20 0: print(fEpoch {epoch1}, Loss: {epoch_loss/len(train_loader):.6f})逻辑说明nn.Sequential把层按顺序串起来前向传播自动完成。batch_size32是经验值数据量小可以调到 16数据量大调到 64。shuffleTrue必须开否则每轮喂入顺序一样梯度更新会走偏。训练 200 轮是保守估计实际看 loss 曲线连续 20 轮不降就可以停。参数上lr0.001是 Adam 的甜点值loss 震荡就降到 0.0005收敛太慢就升到 0.002。隐藏层 64 和 32 这两个数可以改但别设成 512 这种大数几百条数据撑不住。random_state42固定划分保证每次跑结果可复现。4. 模型评估与调参别只看 loss 下降就以为成了4.1 回归任务该看哪些指标训练 loss 下降不代表模型能用。消费预测是回归任务要看 MAE平均绝对误差、RMSE均方根误差和 R²。MAE 告诉你平均预测偏了多少块钱最直观RMSE 对大误差惩罚更重能暴露极端预测R² 看模型解释了多大比例的方差低于 0.5 基本说明特征没选对。from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score model.eval() with torch.no_grad(): pred_test model(X_test).numpy() # 注意预测值和真实值都是归一化后的要反归一化才能看真实金额 pred_real scaler.inverse_transform(pred_test) y_real scaler.inverse_transform(y_test.numpy()) mae mean_absolute_error(y_real, pred_real) rmse np.sqrt(mean_squared_error(y_real, pred_real)) r2 r2_score(y_real, pred_real) print(fMAE: {mae:.2f} 元, RMSE: {rmse:.2f} 元, R2: {r2:.4f})这里有个容易翻车的点scaler.inverse_transform只能对当初归一化的那些列做逆变换而total_consumption在预处理时被归一化了所以能直接逆变换。如果你把标签单独归一化就得单独存一个标签的 scaler别混用。4.2 调参的三个方向与边界调参优先动这三个隐藏层神经元数、学习率、batch_size。神经元数从 32 试到 128看测试集 MAE 什么时候最低学习率在 0.0005~0.005 之间扫batch_size 在 16~64 之间试。别一次改多个参数否则不知道是哪个起了作用。边界在哪如果测试集 MAE 已经接近月消费的标准差说明模型没学到东西回去检查特征工程如果训练集 MAE 远低于测试集就是过拟合加 Dropout 层或减神经元。数据量低于 300 条时任何调参都是玄学先把数据凑够再说。5. 避坑与排查消费预测模型最常见的 5 个翻车现场现象一loss 变成 NaN。原因通常是学习率太大或输入里有异常值比如生活费填了 999999。解决先把学习率降到 0.0001再用df.describe()检查每个字段的最大最小值把超过 3 倍标准差的离群点剔除或截断。现象二训练集准确率 99%测试集一塌糊涂。典型过拟合。原因可能是隐藏层神经元太多、训练轮数太多、数据量太小。解决加nn.Dropout(0.3)把训练轮数砍一半或者用早停验证集 loss 连续 10 轮不降就停。现象三预测值全部集中在均值附近。模型没学到特征输出了一条水平线。原因多半是特征和标签相关性太弱或者归一化时把标签也压得太狠。解决先算一下特征和目标字段的相关系数低于 0.1 的特征直接删掉检查标签归一化后的分布如果全挤在 0.5 附近说明原始数据本身方差就小。现象四换一批数据预测结果全错。忘了保存训练时的 scaler 和编码器。新数据的字段顺序、编码方式必须和训练时完全一致。解决用joblib.dump把 scaler 和get_dummies后的列名存下来预测前先对齐列。现象五MAE 看着不错但业务上没法用。比如 MAE 是 200 元但学生月消费才 1500 元误差率 13%对风控来说太高。解决把回归改成分类预测「低/中/高」三档用准确率和召回率评估业务上更好落地。6. 让预测结果真正能用从模型输出到业务决策的最后一公里模型跑出 MAE 之后很多人就停了。但一个消费预测模型要真正产生价值得解决「预测出来给谁用、怎么用」的问题。我一般会做两件事一是把预测结果分档二是做特征重要性分析。分档很简单把预测金额按分位数切成三档对应「低消费、中消费、高消费」输出一个类别标签。这样校园商户备货时看档位就行不用理解具体金额。特征重要性分析用置换重要性permutation importance逐个打乱某个特征的值看 MAE 涨多少涨得越多说明该特征越重要。我实测下来「月生活费」和「餐饮占比」通常排前两位而「性别」几乎没影响——这意味着做风控时不用在性别上做区分避免合规风险。from sklearn.inspection import permutation_importance def score_model(model, X, y): model.eval() with torch.no_grad(): return mean_absolute_error(y, model(X).numpy()) result permutation_importance( estimatormodel, XX_test, yy_test.numpy(), scoringscore_model, n_repeats10, random_state42) for i in result.importances_mean.argsort()[::-1]: print(f特征 {i}: 重要性 {result.importances_mean[i]:.4f})这段代码的scoring传的是自定义函数因为 sklearn 默认的评分器不认 PyTorch 模型。n_repeats10表示每个特征打乱 10 次取平均次数越多越稳但越慢数据量小的时候 5 次也够。最后一个习惯每次训练完把「数据版本、特征列表、超参数、测试集 MAE」记到一个表格里。我吃过亏调了三天参发现还不如第一版结果第一版的配置没存只能重跑。这个习惯比任何调参技巧都值钱。希望帮到你。本文还有配套的精品资源点击获取
返回列表