资讯详情 机器学习大作业全流程实战:从数据清洗到模型部署的完整指南
📅 2026/10/15 21:44:03
简介本资源为南京邮电大学通达学院《机器学习》期末大作业的完整设计报告面向修读机器学习课程、需要完成医疗保健方向系统设计任务的高校学生。报告以膀胱癌诊断为例围绕数据驱动的智能医疗系统展开涵盖数据收集、特征提取与模型选择、实验评估设计三大环节并梳理了机器学习在影像学诊断、尿细胞学检查、卡介苗治疗预后评估及术后复发预测等场景中的典型应用可作为课程作业的参考范本与写作思路来源。压缩包内仅含1个docx文档约29KB结构完整、篇幅达标便于直接查阅与借鉴。目前已有1022人学习下载适合需要快速理解医疗机器学习系统设计框架、梳理报告章节逻辑的同学参考使用。1. 从一份“机器学习大作业”说起为什么很多人卡在第一步如果你正在搜“南邮 通达 机器学习 大作业 期末 作业”大概率你手里已经拿到了一份课程要求或者正面临一个尴尬的局面课程讲的是逻辑回归、决策树、SVM但作业要求你交出一个“完整可运行、有数据、有评估、有报告”的东西。你打开搜索引擎发现满屏都是“手写数字识别”“鸢尾花分类”“波士顿房价预测”——这三个题目几乎成了机器学习大作业的“老三样”。问题不在于题目老而在于很多人把大作业做成了“调包填空题”导入 sklearn三行代码训练打印一个准确率截图交差。结果答辩时被问“你的特征怎么选的”“为什么用这个模型”“过拟合怎么判断”直接翻车。这篇笔记不针对某一份具体的作业文档而是把“机器学习期末大作业”这个任务拆成一条可复现的落地路径。适合两类人一类是刚学完课程、想用最短时间做出一个不心虚的作业另一类是有一定基础、想把作业做成能写进简历的小项目。核心思路是用一个小而完整的数据集走通“数据清洗→特征工程→模型对比→误差分析→报告呈现”的全流程而不是追求模型多高级。下面从选题、环境、代码、调参、避坑到进阶一步步讲清楚。2. 选题与数据准备别让“公开数据集”变成你的天花板2.1 大作业选题的三个现实约束课程大作业和工业项目最大的区别是时间有限、算力有限、评分标准模糊。所以选题要满足三个条件数据量在几千到几万条之间太大跑不动太小看不出问题、特征有明确的物理含义方便写报告时解释、任务类型是分类或回归聚类和强化学习在作业里很难讲清楚评估指标。常见的可靠选择包括某电商平台的用户购买行为预测二分类、某城市共享单车的租借量预测回归、某传感器采集的设备故障诊断多分类。这些数据在公开数据平台都能找到但注意不要直接用“鸢尾花”这种被用烂的数据集除非你打算在特征工程上做出差异化。我一般会建议学生先花半天时间做一件事把候选数据集的字段含义、缺失比例、类别分布列成一张表。这张表直接决定了你后面能不能写出有内容的报告。比如一个二分类数据集如果正负样本比例是 1:99那准确率就是废的必须用 F1 或 AUC如果缺失值超过 30%就要考虑是删特征还是插补。这些判断在报告里都是加分项。2.2 用 pandas 做一次“体检式”数据探查拿到数据后不要急着扔进模型。先跑一段探查代码把数据的基本情况摸清楚。下面这段代码可以直接抄改一下文件路径和列名就能用。import pandas as pd import numpy as np # 读取数据注意编码和分隔符按实际情况调整 df pd.read_csv(your_dataset.csv, encodingutf-8) # 1. 基本信息行列数、类型、内存占用 print(df.info()) # 2. 缺失值统计按列计算缺失比例 missing df.isnull().sum() / len(df) print(missing[missing 0].sort_values(ascendingFalse)) # 3. 目标变量分布分类任务看类别比例回归任务看偏度和峰度 target label # 改成你的目标列名 print(df[target].value_counts(normalizeTrue)) print(df[target].describe()) # 4. 数值特征描述均值、标准差、分位数快速发现异常值 num_cols df.select_dtypes(include[np.number]).columns print(df[num_cols].describe().T[[mean, std, min, 50%, max]]) # 5. 类别特征基数如果某一列取值超过50个考虑目标编码或直接舍弃 cat_cols df.select_dtypes(include[object]).columns for col in cat_cols: print(f{col}: {df[col].nunique()} unique values)这段代码的逻辑是先看数据整体结构再定位缺失和类别不平衡最后检查数值特征的分布和类别特征的复杂度。参数上value_counts(normalizeTrue)输出的是比例而不是计数方便判断不平衡程度describe().T转置后更易读。如果发现某一列缺失超过 40%且不是关键特征直接删掉比插补更安全如果类别特征基数过高比如用户 ID要么做频率编码要么直接排除否则独热编码后维度爆炸。2.3 划分训练集和测试集的正确姿势很多作业翻车在这一点先做了标准化或缺失值插补再划分训练测试集。这会导致数据泄露——测试集的统计信息混入了训练过程。正确顺序是先划分再在训练集上拟合预处理器然后应用到测试集。用 sklearn 的Pipeline可以强制这个顺序。from sklearn.model_selection import train_test_split from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler from sklearn.impute import SimpleImputer # 假设 X 是特征y 是标签 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy # stratify 保持类别比例 ) # 构建预处理管道先插补中位数再标准化 preprocess Pipeline([ (imputer, SimpleImputer(strategymedian)), (scaler, StandardScaler()) ]) # 在训练集上拟合在测试集上只做变换 X_train_processed preprocess.fit_transform(X_train) X_test_processed preprocess.transform(X_test)这里stratifyy在分类任务中很重要能保证训练集和测试集的类别比例一致避免某个类别在测试集中完全缺失。random_state42是为了结果可复现报告里写清楚这个种子别人才能复现你的数字。注意fit_transform只用于训练集测试集只能用transform这是铁律。3. 模型训练与对比用三行代码跑出五个基线3.1 为什么我建议先跑逻辑回归和决策树很多同学一上来就上 XGBoost 或神经网络结果调参调到崩溃报告里却说不清模型为什么有效。我的习惯是先用逻辑回归和决策树建立基线。逻辑回归的系数可以解释特征重要性决策树的树结构可以画出来直观展示分裂条件。这两个模型跑通后再上随机森林或梯度提升树做对比报告里就有了“从简单到复杂”的叙事线。下面这段代码一次性跑五个模型输出准确率、F1 和 AUC方便横向对比。from sklearn.linear_model import LogisticRegression from sklearn.tree import DecisionTreeClassifier from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier from sklearn.svm import SVC from sklearn.metrics import accuracy_score, f1_score, roc_auc_score models { LogisticRegression: LogisticRegression(max_iter1000, random_state42), DecisionTree: DecisionTreeClassifier(max_depth5, random_state42), RandomForest: RandomForestClassifier(n_estimators100, random_state42), GradientBoosting: GradientBoostingClassifier(n_estimators100, random_state42), SVM: SVC(probabilityTrue, random_state42) } results [] for name, model in models.items(): model.fit(X_train_processed, y_train) y_pred model.predict(X_test_processed) y_prob model.predict_proba(X_test_processed)[:, 1] # 正类概率 results.append({ model: name, accuracy: accuracy_score(y_test, y_pred), f1: f1_score(y_test, y_pred, averageweighted), auc: roc_auc_score(y_test, y_prob) }) import pandas as pd print(pd.DataFrame(results).sort_values(auc, ascendingFalse))参数说明max_iter1000防止逻辑回归不收敛max_depth5限制决策树深度避免过拟合n_estimators100是树的数量作业场景下 100 到 200 足够probabilityTrue让 SVM 输出概率否则算不了 AUC。注意predict_proba返回的是两列取[:, 1]才是正类概率。如果数据是多分类roc_auc_score需要加multi_classovr参数。3.2 交叉验证别让一次划分决定你的结论单次训练测试划分有随机性换一个random_state可能结果差好几个百分点。报告里只写一次结果容易被质疑。用 5 折交叉验证取平均值和标准差结论更稳。from sklearn.model_selection import cross_val_score import numpy as np # 以随机森林为例做5折交叉验证 rf RandomForestClassifier(n_estimators100, random_state42) cv_scores cross_val_score(rf, X_train_processed, y_train, cv5, scoringf1_weighted) print(fCV F1: {cv_scores.mean():.4f} (/- {cv_scores.std():.4f}))cv5表示 5 折scoringf1_weighted适合类别不平衡场景。输出格式里/-后面是标准差如果标准差超过 0.05说明模型不稳定需要检查数据划分或增加数据量。交叉验证只能在训练集上做测试集留到最后用一次否则测试集就失去了“未见数据”的意义。3.3 特征重要性让报告有“可解释性”的抓手作业评分里“可解释性”往往是拉开差距的地方。随机森林和梯度提升树都提供feature_importances_逻辑回归的系数绝对值也可以排序。下面代码把特征重要性画成横向条形图直接贴进报告。import matplotlib.pyplot as plt rf RandomForestClassifier(n_estimators100, random_state42) rf.fit(X_train_processed, y_train) importances rf.feature_importances_ feature_names X.columns # 如果用了Pipeline这里需要对应原始列名 indices np.argsort(importances)[::-1][:10] # 取前10个 plt.figure(figsize(10, 6)) plt.barh(range(len(indices)), importances[indices], aligncenter) plt.yticks(range(len(indices)), [feature_names[i] for i in indices]) plt.xlabel(Feature Importance) plt.gca().invert_yaxis() plt.tight_layout() plt.savefig(feature_importance.png, dpi150)注意如果预处理用了PipelineX.columns可能对不上因为插补和标准化不改变列数但顺序一致所以直接按原始列名取即可。如果做了独热编码列数会变需要从preprocess.named_steps里取get_feature_names_out()。这张图在报告里配合一段文字解释“为什么这个特征最重要”比单纯堆准确率有说服力得多。4. 避坑与排查大作业里最容易翻车的五个地方4.1 准确率很高但 F1 很低类别不平衡在作祟现象模型准确率 0.95但 F1 只有 0.3混淆矩阵显示少数类几乎全错。原因数据正负样本比例悬殊模型学会了“全预测多数类”就能拿高准确率。解决改用 F1、AUC 或召回率作为评估指标在模型里设置class_weightbalanced或者用 SMOTE 对少数类过采样注意只能在训练集上做。报告里要明确写出“本数据集存在类别不平衡因此不采用准确率作为主要指标”。4.2 测试集分数远低于训练集过拟合的典型信号现象训练集准确率 0.99测试集只有 0.7。原因模型太复杂树太深、特征太多或数据量太小。解决降低模型复杂度限制max_depth、减少n_estimators增加正则化逻辑回归的C调小SVM 的C调小检查是否有特征泄露比如用未来信息预测过去。如果训练集和测试集差距在 0.1 以内属于正常范围。4.3 缺失值插补后结果变差插补策略选错了现象用均值插补后模型效果反而下降。原因缺失值可能不是随机的均值插补引入了偏差。解决先分析缺失模式如果某一列缺失比例很高且与目标相关考虑把“是否缺失”作为一个新特征数值列用中位数插补抗异常值类别列用众数或单独归为“Unknown”类。SimpleImputer的strategy参数支持mean、median、most_frequent、constant按列类型分别处理更稳妥。4.4 随机种子没固定结果无法复现现象第二次运行代码准确率变了。原因train_test_split、模型初始化、交叉验证划分都依赖随机种子。解决在所有涉及随机性的地方设置random_state包括train_test_split、模型构造函数、cross_val_score。报告里写明“所有实验固定 random_state42”。这不是玄学是工程规范。4.5 报告只贴代码不写分析评分直接降档现象代码跑通了报告里全是代码块和截图没有文字解释。原因把作业当成了“代码提交”而不是“实验报告”。解决每个实验环节至少写三段话——为什么选这个方法、参数怎么定的、结果说明了什么。比如“随机森林的 F1 比逻辑回归高 0.08说明特征之间存在非线性关系树模型能自动捕捉交互项”。这种分析不需要多高深但能体现你理解了自己在做什么。5. 从作业到简历项目三个让结果更硬核的进阶技巧5.1 用 SHAP 值做单样本解释特征重要性是全局的但答辩时老师可能问“为什么这个样本被预测为正类”。SHAP 能给出每个特征对单个预测的贡献值。安装shap后几行代码就能画出瀑布图。import shap # 以随机森林为例创建解释器 explainer shap.TreeExplainer(rf) shap_values explainer.shap_values(X_test_processed[:100]) # 取前100个样本 # 对第一个样本画瀑布图 shap.plots.waterfall(shap_values[0][:, 1]) # 二分类取正类注意shap_values的维度二分类返回列表取[0]是负类[1]是正类多分类返回列表长度等于类别数。瀑布图里红色表示推高预测概率蓝色表示拉低。这张图放在报告里比任何文字都直观。5.2 用网格搜索代替手工调参手工调参靠运气网格搜索靠算力。作业场景下参数组合控制在几十组以内跑几分钟就能出结果。from sklearn.model_selection import GridSearchCV param_grid { n_estimators: [50, 100, 200], max_depth: [3, 5, 7, None], min_samples_split: [2, 5, 10] } grid GridSearchCV( RandomForestClassifier(random_state42), param_grid, cv3, scoringf1_weighted, n_jobs-1 # 用满所有CPU核心 ) grid.fit(X_train_processed, y_train) print(grid.best_params_) print(grid.best_score_)n_jobs-1表示并行跑能显著缩短时间。cv3比 5 折快适合调参阶段。拿到best_params_后用这组参数重新训练最终模型再在测试集上评估一次。报告里可以放一张“参数-得分”的热力图展示调参过程。5.3 把模型保存下来做一个最小可用的预测脚本作业交完就删代码是最大的浪费。用joblib把训练好的模型和预处理器保存成文件以后有新数据可以直接调用。import joblib # 保存模型和预处理器 joblib.dump(rf, model_rf.pkl) joblib.dump(preprocess, preprocess.pkl) # 加载并预测新样本 loaded_model joblib.load(model_rf.pkl) loaded_preprocess joblib.load(preprocess.pkl) new_data pd.DataFrame([{...}]) # 构造一条新样本 new_processed loaded_preprocess.transform(new_data) prediction loaded_model.predict(new_processed) print(f预测结果: {prediction[0]})这个脚本虽然简单但能让你在简历里写“完成了从数据预处理到模型部署的完整流程”。如果时间充裕还可以用Flask或Streamlit包一个网页界面输入特征值就能看到预测结果。我见过不少同学靠这一步把课程作业变成了面试时的演示项目。最后说一个我自己的习惯每次做完作业把代码、数据、报告、图表整理到一个文件夹里写一个README.md说明每个文件的作用和运行顺序。这个习惯看起来不起眼但等你下次需要复用代码或者面试官要看项目时你会感谢自己当初多花了这十分钟。希望帮到你。本文还有配套的精品资源点击获取