
简介面向机器学习初学者与实践者的分类与回归实战项目包聚焦监督学习两大核心任务。资源以波士顿房价预测为回归案例系统演示线性回归、岭回归等算法从数据预处理、特征选择到模型训练、交叉验证与性能评估的完整流程同时配套酒店数据集分类项目涵盖逻辑回归、决策树等常用算法对比便于体会分类与回归在问题定义、模型选择及评价指标上的差异。压缩包共7个文件包括2个可运行的Jupyter Notebook、2个CSV数据集、2个HTML结果预览和1份项目说明文档整体大小2.23MB。Notebook 内含代码与输出结果可直接修改运行HTML 文件可快速浏览分析过程docx 文档补充项目背景与操作说明。已有986人学习下载既能满足课程实验、毕业设计参考也可作为入门机器学习的动手训练素材。1. 机器学习实战项目分类与回归两份数据集把监督学习的底子一次打牢做机器学习这行最怕的不是模型跑不出来而是拿着数据不知道从哪里下手。这份「机器学习实战项目——分类回归.zip」解决的就是这个问题它用波士顿房价回归和 INN Hotels Group 酒店预订分类两个真实数据集把监督学习里最核心的两类任务完整走了一遍。压缩包里不是散落的代码而是一套能跑通的 Notebook、数据集和项目说明文档适合刚学完机器学习理论、想动手验证的初学者也适合需要快速回顾完整建模流程的从业者。数据预处理、特征选择、模型训练、评估指标这些环节在这两个项目里都能找到对应的实际操作不是空讲概念。2. 拆开压缩包先看清两个任务分别是什么、用什么模型、怎么评估2.1 文件构成与用途对照拿到压缩包后第一件事不是急着跑代码而是先把文件清单过一遍搞清楚每个文件是干什么的。这个习惯能避免后面拿错数据集、看错 Notebook 的低级错误。文件类型用途Boston.csv数据集波士顿房价回归实验的数据包含 14 个特征列和 1 个目标列Learners_Notebook_Boston_house_price.ipynbNotebook波士顿房价回归的完整建模代码从导入数据到模型评估Regression.html文档回归项目 Notebook 的静态导出版没装 Jupyter 也能直接看结果Project Description - Machine Learning.docx文档项目说明讲清了两个任务的背景、目标和评估要求Classification.html文档分类项目 Notebook 的静态导出版INNHotelsGroup.csv数据集INN Hotels Group 酒店预订数据用于分类建模LearnerNotebook-Project_Classification_ML.ipynbNotebook分类任务的完整代码包含数据检查和建模过程回归的 Notebook 我一般直接拿 Jupyter Notebook 打开HTML 版本是给不想装环境的人预览用的。两个 CSV 数据集需要用 pandas 读取做过数据处理的人对这个流程不会陌生。2.2 分类与回归任务定义上的本质差别分类和回归同属监督学习但目标变量类型完全不同。回归预测的是连续数值比如波士顿房价数据集里的 MEDV中位房价它是一个连续的浮点数模型输出的是一个数值。分类预测的是离散类别比如 INN Hotels Group 数据里判断客人是否会取消预订输出是 0 或 1属于二分类问题。这个区别直接决定了评估指标的选择。回归任务看 MSE、RMSE、R²分类任务看准确率、精确率、召回率和 F1。很多初学者在这两个任务之间切换时容易把评估指标混着用比如拿准确率去评价回归模型这没有任何意义。项目里两个 Notebook 是分开写的正好体现了这种差异做第一遍的时候建议按顺序先回归后分类理解会顺畅很多。2.3 两套数据集的字段与目标变量说明波士顿房价数据集来自 1978 年的波士顿郊区字段包括犯罪率CRIM、住宅用地比例ZN、非零售商业用地比例INDUS、查尔斯河虚拟变量CHAS、一氧化氮浓度NOX、平均房间数RM、房龄AGE等 13 个特征目标变量是 MEDV中位房价单位千美元。这个数据集经典在于特征之间有明显的共线性比如 DIS到就业中心距离和 NOX一氧化氮浓度往往负相关这种结构适合拿来练习正则化模型观察 Lasso 和 Ridge 如何处理共线性。INN Hotels Group 数据集字段包括预订类型、入住日期、取消标志等最核心的目标列是取消标志通常为 0 或 1。这个数据的价值在于类别通常不平衡酒店预订里取消的订单比例往往只有两三成这意味着不能只看准确率要看召回率和 F1。3. 波士顿房价回归完整跑通线性回归到正则化模型的链路3.1 数据清洗与特征检查拿到 Boston.csv 后第一步永远是数据体检。用 pandas 读进来先看形状、缺失值、数据类型再对目标变量做分布检查。波士顿房价数据集本身比较干净但练习时故意制造缺失值、添加异常值也是常见做法目的是练熟处理套路。import pandas as pd import numpy as np # 读取波士顿房价数据 boston pd.read_csv(Boston.csv) # 查看数据形状和列名 print(boston.shape) print(boston.columns.tolist()) # 检查缺失值 print(boston.isnull().sum()) # 目标变量分布检查 import matplotlib.pyplot as plt plt.hist(boston[MEDV], bins30, edgecolorblack) plt.xlabel(MEDV (千美元)) plt.ylabel(频数) plt.show()这段代码完成三件事确认数据规模、检查缺失值、观察目标变量分布。MEDV 的分布如果不是标准的正态分布后续做线性回归时可能需要对目标做对数变换。波士顿房价数据里存在少数 MEDV 被截断到 50 的情况直方图会在右侧出现一个尖峰这是 1978 年数据采集时的上限截断属于数据集本身的结构特点建模时要么保留要么删除截断样本看你要解决什么问题。3.2 特征相关性分析与数据集划分建模前需要看特征与目标的相关系数这是特征选择的依据。波士顿房价数据里有几个特征与 MEDV 的相关性非常明显比如 RM平均房间数正相关最强LSTAT低收入人口比例负相关明显。# 计算特征与目标变量的相关系数 corr boston.corr()[MEDV].sort_values(ascendingFalse) print(corr) # 划分训练集和测试集 from sklearn.model_selection import train_test_split X boston.drop(MEDV, axis1) y boston[MEDV] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) print(f训练集规模: {X_train.shape}, 测试集规模: {X_test.shape})参数说明test_size0.2表示留出 20% 的数据做测试random_state42固定随机种子保证每次运行划分结果一致方便复现实验。训练集规模约 404 条测试集约 101 条样本量不算大后面调参时交叉验证比单次划分更稳。做特征选择时我一般不会只凭相关系数删列而是结合领域知识和模型反馈。波士顿数据里 DIS 和 NOX 有强共线性两个都放进去会让线性回归的系数估计变得不稳定但岭回归可以缓解这类问题。相关系数矩阵可以用可视化进一步确认不过新手阶段看排序结果就够了。3.3 线性回归、岭回归与 Lasso对比它们的实际表现波士顿房价最经典的做法是用线性回归作为基线然后对比岭回归Ridge和 Lasso。Ridge 适合处理特征共线性Lasso 能做特征选择。三者用 sklearn 调用非常简单但要注意线性回归需要先标准化特征否则量纲差异大会影响系数解释和正则化效果。from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LinearRegression, Ridge, Lasso from sklearn.metrics import mean_squared_error, r2_score # 特征标准化 scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test) # 线性回归 lr LinearRegression() lr.fit(X_train_scaled, y_train) y_pred_lr lr.predict(X_test_scaled) # 岭回归 ridge Ridge(alpha1.0) ridge.fit(X_train_scaled, y_train) y_pred_ridge ridge.predict(X_test_scaled) # Lasso lasso Lasso(alpha0.1) lasso.fit(X_train_scaled, y_train) y_pred_lasso lasso.predict(X_test_scaled) # 评估 for name, y_pred in [(LinearRegression, y_pred_lr), (Ridge, y_pred_ridge), (Lasso, y_pred_lasso)]: mse mean_squared_error(y_test, y_pred) rmse np.sqrt(mse) r2 r2_score(y_test, y_pred) print(f{name}: RMSE{rmse:.2f}, R2{r2:.3f})这段代码展示了三个模型的训练和评估。StandardScaler的fit_transform和transform分开调用是因为测试集用的是训练集的均值和标准差不能重新计算这是个高频出错点。Ridge 的alpha1.0控制正则化强度alpha 越大系数压缩越狠Lasso 的alpha0.1做的是 L1 惩罚会把不重要特征的系数压到 0天然做特征选择。三个模型跑完后对比 RMSE 和 R² 的差别。常见结果有两种一是三个模型表现接近说明线性假设基本成立二是 Ridge 比线性回归好说明数据存在共线性。这时候要看 Lasso 的系数哪种特征被压成了 0就知道哪些变量对房价预测贡献不大。3.4 残差分析判断模型是否漏掉了非线性关系评估回归模型不能只看 R²还要画残差图。残差是 y_test 减去 y_pred如果残差随机分布在 0 值附近说明模型拟合合理如果呈现明显的曲线形状说明存在非线性关系线性模型没有捕捉到。# 计算残差 residuals y_test - y_pred_lr # 画残差图 plt.scatter(y_pred_lr, residuals, alpha0.6) plt.axhline(y0, colorred, linestyle--) plt.xlabel(预测值) plt.ylabel(残差) plt.title(线性回归残差图) plt.show()残差图中如果出现漏斗形状即随着预测值增大残差方差也在增大说明存在异方差性这时可以考虑对目标变量做对数变换。波士顿房价截断到 50 的样本会让残差图右侧出现一条竖线这就是数据结构的痕迹。遇到这种情况我的做法是把残差分析结果写进实验笔记作为模型局限性的说明而不是急着换模型。4. INN Hotels Group 分类把预订取消预测做成一个可落地的二分类模型4.1 从业务问题到分类任务目标列怎么定义INN Hotels Group 数据集做的是酒店预订取消预测核心问题是一个客人订了房间之后会不会取消。这是一个典型的二分类问题目标列为是否取消取值为 0未取消或 1取消。处理这种数据第一步是看类别分布。import pandas as pd # 读取酒店数据 hotels pd.read_csv(INNHotelsGroup.csv) # 查看目标列分布 cancel_counts hotels[IsCanceled].value_counts(normalizeTrue) print(cancel_counts) # 查看缺失值 print(hotels.isnull().sum()) # 查看数据类型 print(hotels.dtypes.value_counts())如果取消类别占比低于 30%就是不平衡数据集直接建模会出问题。酒店取消场景里这很常见反反复复都是如此。value_counts(normalizeTrue)返回占比方便一眼看出不平衡程度。另外数据里的类型分布值得注意有些列是数值型有些是类别型类别型特征需要做编码。常见的做法是 LabelEncoder 处理有序类别OneHotEncoder 处理无序类别。酒店数据里的房间类型、预订渠道这些字段都属于无序类别用独热编码。4.2 特征工程日期字段拆解与数值特征标准化酒店预订数据的特征工程核心是挖掘日期字段。入住日期、预订日期这两个字段如果直接用原始格式喂给模型是不行的需要拆成年月日、星期几、提前预订天数等。这些衍生特征往往比原始特征更有预测力。from sklearn.preprocessing import StandardScaler, OneHotEncoder import pandas as pd # 假设数据里有预订日期 ArrivalDate 和入住日期 # 这里以常见的日期拆解为例 hotels[BookingDate] pd.to_datetime(hotels[BookingDate]) hotels[ArrivalDate] pd.to_datetime(hotels[ArrivalDate]) # 提前预订天数 hotels[LeadTime] (hotels[ArrivalDate] - hotels[BookingDate]).dt.days # 入住月份和星期几 hotels[ArrivalMonth] hotels[ArrivalDate].dt.month hotels[ArrivalDayOfWeek] hotels[ArrivalDate].dt.dayofweek # 数值特征标准化 num_cols [LeadTime, ArrivalMonth, ArrivalDayOfWeek] scaler StandardScaler() hotels_scaled scaler.fit_transform(hotels[num_cols])LeadTime是提前预订天数这个特征在酒店取消预测里非常关键一般预订时间越早取消概率越高。ArrivalMonth捕获季节性夏季和节假日的取消行为差别很大。ArrivalDayOfWeek看周末和工作日的差异。日期拆解本身是个熟能生巧的活不同业务场景拆出来的特征也不同但这一套组合在酒店场景里是通用的。类别特征的独热编码需要特别小心它会把一列变成多列训练集和测试集必须用同一个编码器。我后面避坑章会专门讲这个问题这里先记住一个原则fit只能在训练集上调用一次。4.3 基线模型与类别不平衡处理分类问题不要一上来就上复杂模型先跑一个逻辑回归作为基线让心中有底。但酒店取消数据类别不平衡时直接跑逻辑回归会被多数类主导预测结果偏向把所有样本判为 0。这时候需要做两步处理调整类别权重换评估指标。from sklearn.model_selection import train_test_split from sklearn.linear_model import LogisticRegression from sklearn.metrics import classification_report, confusion_matrix # 准备特征和目标 X_h hotels.drop(IsCanceled, axis1) y_h hotels[IsCanceled] X_train_h, X_test_h, y_train_h, y_test_h train_test_split( X_h, y_h, test_size0.2, random_state42, stratifyy_h ) # 使用类别权重平衡 log_reg LogisticRegression(class_weightbalanced, max_iter1000) log_reg.fit(X_train_h, y_train_h) y_pred_h log_reg.predict(X_test_h) # 输出分类报告 print(classification_report(y_test_h, y_pred_h))class_weightbalanced让 sklearn 根据类别频率自动调整权重少数类的错分代价提高模型不会一味偏向多数类。stratifyy_h保证训练集和测试集里取消与未取消的比例和原始数据一致这点在不平衡数据集上必须做否则随机划分可能导致测试集里取消样本极少评估失真。max_iter1000是因为特征经过独热编码后维度增加逻辑回归默认迭代次数可能不够收敛报 warning 时可以这样调。classification_report 里要重点看少数类类别 1的精确率、召回率和 F1这三个值比准确率诚实得多。4.4 随机森林上场对比逻辑回归在非线性特征上的差距逻辑回归是线性模型如果特征与目标之间存在非线性关系它的表达能力有限。随机森林能捕捉非线性交互而且天然处理类别特征虽然编码后效果更好。作为第二个模型随机森林主要用于对比上限。from sklearn.ensemble import RandomForestClassifier # 随机森林分类器 rf RandomForestClassifier( n_estimators100, max_depth8, random_state42, class_weightbalanced ) rf.fit(X_train_h, y_train_h) y_pred_rf rf.predict(X_test_h) print(classification_report(y_test_h, y_pred_rf)) print(confusion_matrix(y_test_h, y_pred_rf)) # 特征重要性排序 feature_importance pd.Series( rf.feature_importances_, indexX_train_h.columns ).sort_values(ascendingFalse) print(feature_importance.head(10))n_estimators100控制树的棵数一般 100 到 200 之间足够。max_depth8限制单棵树深度防止过拟合。特征重要性输出能看到 LeadTime 是否排在前面这会验证之前做特征工程时对提前预订天数的判断。如果随机森林的 F1 比逻辑回归高不少说明数据里确实有非线性关系如果差不多线性模型就够了复杂模型反而增加部署成本。这个判断直接影响后续方向是深入调随机森林的参数还是转向 XGBoost、LightGBM或者干脆用逻辑回归上线。机器学习项目里的模型选择本质上是收益和成本的权衡。5. 实战避坑分类与回归项目里我踩过的六个坑5.1 数据泄漏把目标变量的衍生信息当成了特征现象训练时 R² 高达 0.95测试集表现也很好但主观上一看特征列表就发现不对——某个特征本质上和目标值有直接计算关系。原因数据预处理时不小心把包含目标信息的列留在了特征里。波士顿房价数据集里没有这个问题但自建特征时很容易犯。比如把 MEDV 的均值或某个分桶结果作为特征加进去模型直接从特征里读答案。解决建模前用X.columns核对特征列表确保没有目标变量或目标变量的直接变换列。另外可以在数据清洗阶段把明显冗余的列剔除比如 ID 列、和目标完全重复的列。做特征工程时多留个心眼加特征前问自己这个特征是否包含了答案5.2 标准化时把缩放器在训练集和测试集上分别 fit 了现象模型训练正常但测试集预测结果离谱数值波动巨大。原因训练时对 X_train 做了scaler.fit_transform测试时对 X_test 又单独做了一次scaler.fit_transform导致两个数据集的均值和标准差不同分布完全错位。新手经常因为图省事把两行代码写成一样的。解决统一用训练集的缩放器。训练集上fit_transform测试集上只调用transform。这个坑在回归和分类项目里都会出现一旦犯过后续再做数据预处理时我会强制自己检查这两行代码是不是同一个对象。5.3 类别不平衡时硬套 accuracy模型看似很准实则没用现象酒店取消数据里 80% 的订单未取消模型全部预测为 0准确率直接 80%看起来不错但对取消订单的召回率是 0业务上完全不可用。原因accuracy 在类别不平衡场景下会被多数类主导。分类项目里如果只看准确率很容易产出一个实际没有价值的模型。解决换成 precision、recall、F1 和 confusion matrix。分类项目 Notebook 里应该输出 classification_report 而不是只报 accuracy。对于酒店取消预测通常更关心召回率——漏掉一个取消订单意味着房间空置成本很高。5.4 独热编码后训练集和测试集列数不一致现象模型训练完预测时报错提示测试集的特征数量与训练集不一致。原因训练集里某个类别出现的值测试集里没有出现或者相反。独热编码器在训练集上 fit 后固定了列名集合测试集经过同样的编码器处理时如果出现了训练集中没见过的类别会报错或生成新的列。解决OneHotEncoder 设置handle_unknownignore它在遇到未知类别时输出全零列而不是报错。另外建议用一个 ColumnTransformer 统一处理数值和类别特征它能保证完整的数据处理流程作为同一个 pipeline 被复用训练和推理时不会出现列错位。5.5 交叉验证和测试集混在一起调参调出了虚假分数现象调参时交叉验证分数一直在涨感觉模型越调越好最后在测试集上一跑分数远低于预期。原因调试过程中反复用同一个测试集评估效果要么是random_state没固定导致每次划分的训练集和测试集不同要么是拿测试集参与了调参测试集已经不再“未见”泛化能力被高估。解决先把测试集锁死交叉验证只用在训练集内部。调参时用 GridSearchCV 加cv5找到最优参数后在单独的测试集上做一次最终验证。从那以后我每次跑实验都会用版本号记录数据划分方式和随机种子调参变量再多也不会自己骗自己。6. 一个值得养成的习惯训练前先把基线跑出来再谈调参动手做分类或回归项目之前我强烈建议先跑一个最简单的基线模型再用结果调整后续策略。两个项目里最直接的基线分别是线性回归和逻辑回归先把它们跑通得到一组原始分数后面的模型才有比较对象。以波士顿房价为例基线模型跑出来 R² 如果是 0.7 左右这说明线性假设对当前数据基本适用。如果 R² 只有 0.4那第一个要查的就不是调参而是数据有没有预处理问题、特征是不是选错了。模型调优有个铁律先保证数据对再调参数。具体操作上我一般会用 sklearn 的Pipeline把标准化、特征编码、模型训练串成一个完整流程这样既省去了反复写代码的麻烦也避免漏掉某一步处理。from sklearn.pipeline import Pipeline from sklearn.compose import ColumnTransformer from sklearn.preprocessing import StandardScaler, OneHotEncoder # 数值列和类别列分开处理 num_features [LeadTime, ArrivalMonth, ArrivalDayOfWeek] cat_features [RoomType, MarketSegment] preprocessor ColumnTransformer( transformers[ (num, StandardScaler(), num_features), (cat, OneHotEncoder(handle_unknownignore), cat_features) ] ) # 把预处理和模型串成 pipeline pipeline Pipeline(steps[ (preprocessor, preprocessor), (classifier, RandomForestClassifier( n_estimators100, max_depth8, class_weightbalanced, random_state42 )) ]) # 直接用 pipeline 训练和预测 pipeline.fit(X_train_h, y_train_h) y_pred pipeline.predict(X_test_h)Pipeline 的价值在于把整个处理链锁在一个对象里。在线预测时不用手动记住标准化、编码、模型这三步的操作顺序一个pipeline对象从头到尾搞定也不会有中间步骤被遗忘的风险。测试集验证时数据泄漏的可能性被降到最低。先用逻辑回归拿基线然后用 Pipeline 把随机森林串起来比较两者的 F1 分数和混淆矩阵确认复杂模型带来的收益是否值得。如果随机森林比逻辑回归的 F1 只提升了不到两个百分点我会选择逻辑回归——部署简单解释性强业务方也容易接受。机器学习不是越复杂的模型越好是在给定资源条件下找到最合用的方案。这个习惯帮我少走了很多弯路希望帮到你。本文还有配套的精品资源点击获取