ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

学生成绩预测实战:线性回归、SVM与神经网络模型对比与实现

学生成绩预测实战:线性回归、SVM与神经网络模型对比与实现 简介一份面向Python数据分析与机器学习学习者的学生成绩预测项目资料围绕线性回归、支持向量机SVM与神经网络三种算法清晰演示如何构建、训练并评估预测模型。压缩包共14个文件包含4个Python脚本、5个npy数据文件和5个txt文本文件整体大小约12.25MB脚本覆盖数据转换、模型训练及工具函数npy文件提供可直接加载的特征与成绩数据txt文件则作为说明与结果参考目录结构便于定位。已有360人浏览学习。项目完整涵盖数据预处理、缺失值、异常值、特征选择、标准化、交叉验证以及MSE、R²等评价指标通过实际数据对比三种模型在不同场景下的适用性与差异。读者可基于脚本和数据分析各算法特点快速用于课程设计、实验复现或作为入门机器学习的练习素材。1. 学生成绩预测这件事为什么线性回归、SVM 和神经网络要放在一起跑接到学生成绩预测这个任务时大多数人第一反应是直接上一个神经网络。但真拿到数据就会发现问题没那么简单原始文件是txt和npy混着存的特征分散在借阅记录、门禁记录、消费记录三个维度里成绩标签单独一个文件处理起来比建模本身更费时间。这份资源好就好在它把完整链路都给你了——从data_convert.py做数据转换到worker.py做特征工程再到三类模型分别建模对比。适合正在做课程设计、准备机器学习作业、或者想快速走一遍「数据→训练→评估→结论」全流程的从业者。我的建议是线性回归先打底SVM 验证小样本表现神经网络最后上这样每一步的收益和坑都能看清楚。2. 数据包拆解与预处理txt 和 npy 混存的原始数据怎么变成可训练样本2.1 五个数据文件各是什么特征如何对齐压缩包里能看到catalog.npy、borrow.npy、access.npy、consumption.npy、score.npy五个npy文件同时还有对应的txt原始文件。按命名和常规课程设计的数据组织习惯这五个文件分工很明确catalog是课程目录对应学生选课信息和课程编号borrow是图书馆借阅记录能反映学习投入程度access是门禁考勤记录反映到课率consumption是食堂消费记录常规做法是把它作为生活习惯的代理特征score是成绩也就是我们要预测的标签值。特征对齐是所有后续工作的前提。常见做法是以学生ID和课程ID作为联合主键把借阅、考勤、消费三张表横向拼接每一行代表「某个学生在某门课上的完整行为特征」成绩作为最后一列。这里有个容易踩的坑——npy数组的保存顺序不一定和txt文本的行顺序一致所以不能直接按下标拼接必须先检查每一行对应的ID是否相等。我一般会先把ID列单独抽出来做校验确认无误后再合并特征。2.2 data_convert.py 的转换逻辑txt 转 npy 时参数怎么设data_convert.py的作用是把你拿到的原始txt数据转成npy格式。不要小看这一步txt里的数据经常是字符串夹杂空行还有列宽不一致的情况。下面这段代码是这个转换脚本最常见的核心写法import numpy as np def txt_to_npy(txt_path, npy_path, dtypefloat): 读取指定txt文件并转为npy :param txt_path: 原始txt路径列之间用空格或制表符分隔 :param npy_path: 输出npy路径 :param dtype: 转换后的数据类型默认float rows [] with open(txt_path, r, encodingutf-8) as f: for line in f: line line.strip() if not line: continue # 跳过空行 parts line.split() # 如果第一列是字符串ID单独处理这里以纯数值数据为例 rows.append([float(p) for p in parts]) arr np.array(rows, dtypedtype) np.save(npy_path, arr) print(f已转换 {txt_path} - {npy_path}, 形状: {arr.shape}) return arr # 示例调用 txt_to_npy(catalog.txt, catalog.npy)这里有几个参数需要注意。dtypefloat是因为后续模型训练时sklearn 和 tensorflow 都要求数值型输入如果原始数据里有整数形式的ID列建议单独提出来用int保存不要混在特征矩阵里做标准化。encodingutf-8是中文环境下的常规选择如果你打开txt看到乱码改成gbk一般能解决。2.3 worker.py 到底做了什么拼接特征、标准化、划分数据集worker.py是特征工程的主脚本也是整个资源里最值得仔细读的一个文件。它通常承担三件事把五个npy文件按主键合并成一个特征矩阵、对特征做标准化、划分训练集和测试集。下面是我根据这个资源的数据结构还原出的核心逻辑import numpy as np from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler # 1. 加载五个npy文件 catalog np.load(catalog.npy) borrow np.load(borrow.npy) access np.load(access.npy) consumption np.load(consumption.npy) score np.load(score.npy) # 2. 横向拼接特征假设四者行数已对齐 # 常见做法catalog里存课程信息borrow/access/consumption是行为特征 X np.hstack([catalog, borrow, access, consumption]) y score.ravel() # 把成绩展平成一维 # 3. 标准化SVM对量纲极其敏感这一步不能省 scaler StandardScaler() X_scaled scaler.fit_transform(X) # 4. 划分训练集和测试集固定随机种子方便复现 X_train, X_test, y_train, y_test train_test_split( X_scaled, y, test_size0.2, random_state42 ) print(f特征矩阵形状: {X_scaled.shape}) print(f训练集样本数: {X_train.shape[0]}, 测试集样本数: {X_test.shape[0]})这段代码透露了几个判断第一特征拼接用的是np.hstack说明是横向拼接而不是纵向堆叠第二标准化用StandardScaler因为SVM的RBF核函数对特征尺度非常敏感不标准化的话数值大的特征会主导距离计算第三random_state42是固定随机种子确保每次跑出来的结果一致这在写实验报告和对比模型时太重要了。3. 三条模型路线逐个落地线性回归、SVM、神经网络怎么调才不翻车3.1 线性回归先打底sklearn LinearRegression 的用法与基线价值线性回归是这批模型里最朴素也最稳的一个。它的价值不在于精度最高而在于给你一个「最差也能到什么程度」的基线。如果线性回归的R²已经到0.6那SVM和神经网络至少要超越这个数才有继续调参的意义。用sklearn实现线性回归只需要几行代码但要注意两点一是必须检查特征之间有没有严重的多重共线性二是线性回归对标准化并不强制但做了之后系数更容易解释。from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_squared_error, r2_score import numpy as np # 训练线性回归模型 lr LinearRegression() lr.fit(X_train, y_train) # 预测并计算指标 y_pred_lr lr.predict(X_test) mse_lr mean_squared_error(y_test, y_pred_lr) r2_lr r2_score(y_test, y_pred_lr) print(f线性回归 MSE: {mse_lr:.4f}) print(f线性回归 R²: {r2_lr:.4f}) # 输出特征系数看哪些特征对成绩影响大 coefs lr.coef_ for i, c in enumerate(coefs): print(f特征{i} 系数: {c:.4f})参数说明LinearRegression()默认用最小二乘法求解没有正则项。如果特征数量多或者特征之间存在相关性我一般会换Ridge或Lasso分别对应L2和L1正则。R²是决定系数最理想是1如果出现负数说明模型比直接猜均值还差这时候不要急着调参回去检查数据泄露或特征顺序问题。3.2 SVM 用 SVR 而不是 SVC核函数、C 值和 epsilon 的选取逻辑很多人拿到成绩预测任务第一反应是拿SVC做分类把成绩离散成几个档位。但这份数据的标签是连续分数本质上是回归问题应该用SVR。这是最容易搞混的一个点。SVR的核心思想是在回归线两侧设定一个间隔带宽度由 epsilon 控制间隔带内的样本不计入损失只有落在带外的点才贡献误差。C 值是误分类惩罚系数C越大意味着越不能容忍误差容易过拟合C越小模型越平滑但可能欠拟合。from sklearn.svm import SVR from sklearn.metrics import mean_squared_error, r2_score # SVR 模型RBF核函数C和epsilon需要根据数据规模调整 svr SVR(kernelrbf, C10.0, epsilon0.1, gammascale) svr.fit(X_train, y_train) y_pred_svr svr.predict(X_test) mse_svr mean_squared_error(y_test, y_pred_svr) r2_svr r2_score(y_test, y_pred_svr) print(fSVR MSE: {mse_svr:.4f}) print(fSVR R²: {r2_svr:.4f})参数说明kernelrbf是默认选择适合非线性关系gammascale表示gamma值由数据规模自动计算等价于 1/(特征数*X的方差)如果换成固定值比如0.01要自己根据特征分布调整C10是经验初始值数据量小可以试着调低到1数据量大可以上调到100epsilon0.1表示预测值在真实值上下0.1分范围内都不计算损失这个值太小会导致模型过度关注细微波动。SVR有一个明显的缺点——在大样本上训练很慢几千条样本尚可接受如果数据量上万建议考虑LinearSVR。3.3 神经网络用 keras 搭前馈网络结构、激活函数与过拟合控制神经网络在这三个模型里最灵活但也最容易失控。对成绩预测这种数据量通常只有几百到几千条的任务网络结构一定要克制。我的建议是输入层接一到两个隐藏层每层神经元数量控制在16到64之间输出层用一个神经元做回归激活函数用线性激活。import tensorflow as tf from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Dense, Dropout from tensorflow.keras.optimizers import Adam # 构建前馈神经网络 model Sequential([ Dense(64, activationrelu, input_shape(X_train.shape[1],)), Dropout(0.2), # 防止过拟合随机丢弃20%的神经元 Dense(32, activationrelu), Dense(1, activationlinear) # 回归任务输出层用线性激活 ]) model.compile( optimizerAdam(learning_rate0.001), lossmse, metrics[mae] ) # 训练模型validation_split 从训练集里再切出10%做验证 history model.fit( X_train, y_train, epochs100, batch_size32, validation_split0.1, verbose0 ) # 预测并评估 y_pred_nn model.predict(X_test).ravel() mse_nn mean_squared_error(y_test, y_pred_nn) r2_nn r2_score(y_test, y_pred_nn) print(f神经网络 MSE: {mse_nn:.4f}) print(f神经网络 R²: {r2_nn:.4f})参数说明Dense(64, activationrelu)是第一层64个神经元ReLU激活函数Dropout(0.2)在每个batch训练时随机丢弃20%的神经元这是小数据量下对抗过拟合最有效的招learning_rate0.001是Adam优化器的常规初始值如果loss震荡不降把learning_rate降到0.0001试试batch_size32是梯度下降每次迭代用的样本数数据量小的时候可以调到16。训练轮数100是一个起点正确的做法是观察history里的验证集loss一旦验证loss连续多个epoch不降甚至回升就说明开始过拟合了应该用早停法提前终止。4. 三模型对比与避坑评价指标怎么选五个常见翻车现场4.1 评价指标的选择MSE、MAE、R²各看什么场景模型跑完不能只看一个指标。MSE、MAE和R²描述的是模型不同侧面的表现论文和课程设计里一般要求全部给出。指标公式含义判断标准适用场景MSE预测值与真实值差的平方的平均越小越好但受异常值影响大需要放大较大误差惩罚时MAE预测值与真实值差的绝对值的平均越小越好更抗异常值希望误差解释直观时R²模型解释的方差占总体方差的比例越接近1越好负数说明模型失效衡量模型整体拟合优度时实际操作中我一般会以R²作为主指标因为它不受成绩绝对分数范围的影响比较三个模型时最直观。MSE作为辅助因为它能放大那些「预测离谱」的样本。如果MSE很大但MAE很小说明有少量异常样本被预测得很差这时候要去看是不是特征里有脏数据而不是急着换模型。4.2 五个学完就忘的踩坑记录现象、原因、解决第一条坑R²直接出现负数。现象是模型跑完R²是负的代码也没报错。原因通常是两类一是训练集和测试集没有做标准化就进入SVM或神经网络模型学到了错误的尺度关系二是特征矩阵里有数据泄露比如把成绩本身或跟成绩强相关的列混进了特征里。解决方法是先用StandardScaler做标准化然后逐一检查特征列把score相关列剔除。第二条坑SVM训练慢到怀疑人生。现象是同样的数据线性回归几秒跑完SVR跑了十分钟还没结束。原因是RBF核的SVR需要计算两两样本间的核矩阵复杂度是O(n²)样本量到5000以上就会明显变慢。解决方法是数据量大时优先LinearSVR如果必须用RBF先做PCA降维减少特征数或者直接抽样训练再评估。第三条坑神经网络loss死活不降。现象是训练N轮loss一直停留在同一水平甚至直接变成NaN。原因常见是学习率设得太大导致梯度爆炸或者输入数据没有归一化到0-1或标准正态分布。解决方法是把learning_rate从0.001往下调试试再检查输入是否经过StandardScaler这两步能解决90%的问题。第四条坑预测值全挤在均值附近。现象是模型预测出来的成绩范围很窄比如真实成绩分布在50到95之间预测结果全在70左右。原因是特征与成绩的相关性太弱模型只能学会预测平均值。这不是bug是特征信息量不够的信号。解决方法是回到特征工程检查是不是拼接时把主要特征搞丢了或者把catalog里的课程难度信息作为特征补进去。第五条坑npy文件读出来维度对不上。现象是np.load的时候报维度错误或者四个npy的行数不一致np.hstack直接报错。原因通常有两个一是txt转npy时某些空行没有被正确跳过导致行数多出来二是原始数据里有合并单元格或重复ID导致样本数不一致。解决方法是先用print(arr.shape)逐个查看每个npy的维度再核对各文件记录数不一致时回去改data_convert.py的过滤逻辑。我每次都会在转换脚本里加打印语句这是最快的定位方式。5. 从能跑到能交代交叉验证与固定实验状态的两个习惯模型能跑出数字只是第一步答辩或写报告时别人问「你这个结果稳不稳」才是检验功底的时刻。我自己的习惯是确定最优模型后用五折交叉验证重跑一遍把每折的R²都打出来看波动幅度。单次划分训练测试集很容易被随机性影响可能这次分到的测试集简单R²偏高下次分到的测试集难R²直接掉了0.2。交叉验证的本质是把数据切五份轮流拿其中一份做测试剩下做训练最后算平均分和标准差。具体做法是这样from sklearn.model_selection import cross_val_score from sklearn.svm import SVR # 五折交叉验证评估SVR svr_cv SVR(kernelrbf, C10.0, epsilon0.1, gammascale) scores cross_val_score(svr_cv, X_scaled, y, cv5, scoringr2) print(f五折R²: {scores}) print(f平均R²: {scores.mean():.4f} ± {scores.std():.4f})如果交叉验证的平均分和你单次划分的测试集分数差距超过0.1就说明前面那次结果有运气成分需要重新审视模型和特征。神经网络的交叉验证稍微麻烦一点因为每次训练本身就有随机性我一般会先固定np.random.seed(42)和tf.random.set_seed(42)让结果可复现再做两层循环外层五折交叉验证内层记录每折的MSE和R²。从那以后我每次跑这种多模型对比的实验都会强制走一遍固定随机种子、交叉验证、指标全打印这三个步骤。看似多花了三分钟但换来的是结果经得起别人反复追问。最后提醒一句这份资源的目录结构里数据转换、特征工程、模型训练都是分开的建议按data_convert.py - worker.py - 建模脚本的顺序执行能少走很多弯路。希望帮到你。本文还有配套的精品资源点击获取
返回列表