ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

用 Scikit-learn 构建回归模型:四种方式玩转线性回归与多项式回归(ML-For-Beginners 南瓜价格预测实战)

用 Scikit-learn 构建回归模型:四种方式玩转线性回归与多项式回归(ML-For-Beginners 南瓜价格预测实战) 用 Scikit-learn 构建回归模型四种方式玩转线性回归与多项式回归ML-For-Beginners 南瓜价格预测实战【免费下载链接】ML-For-Beginners12 weeks, 26 lessons, 52 quizzes, classic Machine Learning for all项目地址: https://gitcode.com/GitHub_Trending/ml/ML-For-Beginners导读本文是 ML-For-Beginners 课程「回归」章节的第三课核心内容围绕一个贯穿全课程的真实数据集——美国南瓜批发价格数据系统讲解如何使用 Python 与 Scikit-learn 构建简单线性回归、多项式回归以及引入分类特征one-hot 编码后的多特征回归模型。你将掌握从数据清洗、相关性分析到模型训练、评估RMSE 与决定系数 R²的完整闭环并亲眼看到模型质量如何从 R² ≈ 0.06 一路提升到 0.97。课程背景与数据准备本课的学习目标在前两课中我们已经了解了回归的概念并用 Matplotlib 对南瓜价格数据做过可视化。本课的重点是进入机器学习的核心环节——训练模型模型在历史数据上自动学习特征与结果之间的依赖关系从而对未见过的数据做出预测。本课将学习两种回归技术基础线性回归Linear Regression多项式回归Polynomial Regression它们背后的数学原理将帮助我们预测南瓜价格随输入数据的变化。全课程默认读者只具备最基础的数学知识因此文档中大量使用笔记、 数学小课堂、示意图等辅助工具帮助理解。数据集与清洗流程本课数据来源于仓库中的 US-pumpkins.csv课程的 notebook.ipynb 已预先完成数据加载与清洗。清洗的关键步骤与上一课 2-Data 数据可视化与分析 相同如下import pandas as pd import matplotlib.pyplot as plt import numpy as np from datetime import datetime pumpkins pd.read_csv(../data/US-pumpkins.csv) # 只保留按 bushel蒲式耳计价的记录 pumpkins pumpkins[pumpkins[Package].str.contains(bushel, caseTrue, regexTrue)] # 选取需要的列 columns_to_select [Package, Variety, City Name, Low Price, High Price, Date] pumpkins pumpkins.loc[:, columns_to_select] # 价格为低价与高价的平均值 price (pumpkins[Low Price] pumpkins[High Price]) / 2 month pd.DatetimeIndex(pumpkins[Date]).month day_of_year pd.to_datetime(pumpkins[Date]).apply(lambda dt: (dt-datetime(dt.year,1,1)).days) new_pumpkins pd.DataFrame( {Month: month, DayOfYear: day_of_year, Variety: pumpkins[Variety], City: pumpkins[City Name], Package: pumpkins[Package], Low Price: pumpkins[Low Price], High Price: pumpkins[High Price], Price: price}) # 按包装规格将价格换算为每 bushel 的单价 new_pumpkins.loc[new_pumpkins[Package].str.contains(1 1/9), Price] price/1.1 new_pumpkins.loc[new_pumpkins[Package].str.contains(1/2), Price] price*2 new_pumpkins.head()这里有两个关键细节值得注意数据筛选仅保留Package中包含 bushel 的记录因为不同包装规格的计价单位不一致无法直接比较单价换算1 1/9 bushel的包装价格除以 1.1 换算为每 bushel 单价1/2 bushel的包装价格乘以 2从而统一价格口径。清洗后得到约 415 条记录、包含 8 列的数据框样例如下IDMonthDayOfYearVarietyCityPackageLow PriceHigh PricePrice709267PIE TYPEBALTIMORE1 1/9 bushel cartons15.015.013.636364719267PIE TYPEBALTIMORE1 1/9 bushel cartons18.018.016.3636367210274PIE TYPEBALTIMORE1 1/9 bushel cartons18.018.016.3636367310274PIE TYPEBALTIMORE1 1/9 bushel cartons17.017.015.4545457410281PIE TYPEBALTIMORE1 1/9 bushel cartons15.015.013.636364其中DayOfYear年中第几天的计算表达式为day_of_year pd.to_datetime(pumpkins[Date]).apply(lambda dt: (dt-datetime(dt.year,1,1)).days)线性回归的本质最小二乘法回归直线的两个目标线性回归的目标是画出一条直线实现两件事展示变量之间的关系relationship做出预测预测一个新数据点会落在直线的哪个位置。最小二乘Least-Squares原理画这条直线最典型的方法是最小二乘回归。最小二乘指的是最小化模型中的总误差对每个数据点测量它到回归直线的垂直距离残差然后把这些距离平方后求和目标是找到使这个总和最小的那条直线。为什么要对距离平方文档给出了两个核心理由关注幅度而非方向误差 -5 应与 5 同等对待平方把所有值变为正数惩罚异常值平方给更大的误差更多权重迫使直线更贴近那些偏离较远的点。 数学小课堂直线方程这条最佳拟合线可以用方程表示Y a bXX是解释变量explanatory variableY是因变量dependent variableb是直线的斜率slopea是Y 轴截距y-intercept即X 0时Y的值。回到南瓜数据的原始问题按月份预测每 bushel 南瓜的价格在这个设定中X指价格、Y指销售月份。斜率b的计算与截距共同决定了直线形态文档中的示意图展示了完整的推导过程见 translated_images/es/slope.f3c9d5910ddbfcf9.webp 与 translated_images/es/calculation.a209813050a1ddb1.webp。课程中的观点是如果按这个模型估算大约支付 $4 时对应的时间点应该在四月份。相关性分析找出值得建模的变量相关系数另一个必须理解的概念是变量 X 与 Y 之间的相关系数Correlation Coefficient。用散点图可以快速直观地判断点整齐地排成一条线则相关性高点散布在整个图上则相关性低。好的线性回归模型应当具有较高更接近 1 而非 0的相关系数。在 notebook 中先用散点图观察Month与Price的关系然后用corr函数定量计算print(new_pumpkins[Month].corr(new_pumpkins[Price])) print(new_pumpkins[DayOfYear].corr(new_pumpkins[Price]))结果为-0.15Month与-0.17DayOfYear相关性很小。但如果用更细粒度的时间度量如DayOfYear即一年中的第几天而不是月份散点图会揭示更多细节。你可以在配套 notebook 中亲自运行验证改用 DayOfYear 后视觉上数据的相关性是否有变化发现关键线索价格聚类相关系数虽小但散点图显示价格点呈现出几个明显的簇——这暗示可能存在比日期更重要的影响因素。文档假设是南瓜品种Variety通过给每个品种分配不同颜色绘制在同一张图上验证axNone colors [red,blue,green,yellow] for i,var in enumerate(new_pumpkins[Variety].unique()): df new_pumpkins[new_pumpkins[Variety]var] ax df.plot.scatter(DayOfYear,Price,axax,ccolors[i],labelvar)结果证实品种对整体价格的影响远大于实际销售日期。用柱状图可以更直观地确认这一点new_pumpkins.groupby(Variety)[Price].mean().plot(kindbar)聚焦单一品种PIE TYPE为验证日期对价格的影响文档将注意力集中到 PIE TYPE 这一种南瓜上pie_pumpkins new_pumpkins[new_pumpkins[Variety]PIE TYPE] pie_pumpkins.plot.scatter(DayOfYear,Price)此时Price与DayOfYear的相关系数约为-0.27——说明训练预测模型是有意义的。重要提醒训练线性回归前必须保证数据干净。线性回归对缺失值很敏感因此需要删除空单元格pie_pumpkins.dropna(inplaceTrue) pie_pumpkins.info()另一种可选方案是用对应列的均值填充缺失值。方式一简单线性回归本课的训练统一使用Scikit-learn库from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_squared_error from sklearn.model_selection import train_test_split第 1 步分离特征与标签。把输入值特征与期望输出标签放入独立的 numpy 数组X pie_pumpkins[DayOfYear].to_numpy().reshape(-1,1) y pie_pumpkins[Price]注意这里必须对输入数据做reshape线性回归期望的输入是二维数组每一行对应一个特征向量。本例只有一个输入特征所以需要形状为N×1的数组N 为数据集大小。第 2 步划分训练集与测试集以便在训练后验证模型X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state0)test_size0.220% 数据作为测试集random_state0固定随机种子保证结果可复现。第 3 步训练模型核心只有两行代码lin_reg LinearRegression() lin_reg.fit(X_train,y_train)第 4 步解读模型参数。fit之后LinearRegression对象包含全部回归系数可通过.coef_属性访问。本例只有一个系数约为-0.017含义是价格随时间缓慢下降大约每天 2 美分。截距通过lin_reg.intercept_获取约为21代表年初的价格水平。在完整解决方案 solution/notebook.ipynb 中实际训练得到的值为coef_ -0.01751876、intercept_ 21.13373436并且可以直接用训练好的模型做推理例如预测程序员节第 256 天的南瓜价格lin_reg.predict([[256]]) # 输出约 16.65第 5 步用 RMSE 评估精度。在测试集上预测价格然后度量预测与期望值的接近程度。RMSE均方根误差是期望值与预测值之差的平方和的平均值的平方根pred lin_reg.predict(X_test) rmse np.sqrt(mean_squared_error(y_test,pred)) print(fRMSE: {rmse:3.3} ({rmse/np.mean(pred)*100:3.3}%))误差约为 2 个点即约17%——并不理想。第 6 步决定系数 R²score。另一个模型质量指标是决定系数score lin_reg.score(X_train,y_train) print(Model determination: , score)值为 0模型完全没利用输入数据行为等同于最差线性预测器——直接输出结果的平均值值为 1可以完美预测所有期望输出本例约为0.06非常低。最后把测试数据与回归直线画在一起直观观察回归效果plt.scatter(X_test,y_test) plt.plot(X_test,pred)结论仅用DayOfYear的简单线性回归RMSE 2.7717.2%、R² 0.07模型质量很差说明单一时间特征不足以解释价格变化。方式二多项式回归有时变量之间存在线性关系如南瓜体积越大价格越高但更多时候这种关系无法用平面或直线刻画。回顾 Date 与 Price 的关系价格很可能会波动例如冬天高、夏天低、再次回升而不是单调变化这时就该尝试多项式回归。多项式是含一个或多个变量与系数的数学表达式。多项式回归创建一条曲线来更好地拟合非线性数据。本例中如果把DayOfYear的平方项加入输入数据就能用一条抛物线拟合数据——曲线会在一年中的某个时间点出现最小值。用 Pipeline 组合处理步骤Scikit-learn 提供了便捷的pipeline API来组合不同的数据处理步骤。pipeline 是估计器estimator的链条本例中先为模型添加多项式特征再训练回归from sklearn.preprocessing import PolynomialFeatures from sklearn.pipeline import make_pipeline pipeline make_pipeline(PolynomialFeatures(2), LinearRegression()) pipeline.fit(X_train,y_train)PolynomialFeatures(2)表示包含输入数据的所有二阶多项式单个输入DayOfYear时只生成DayOfYear²如果有两个输入变量 X、Y则会生成 X²、XY、Y²也可以使用更高阶的多项式。pipeline 的用法与原始LinearRegression对象完全一致——fit之后用predict获取预测结果pred pipeline.predict(X_test) rmse np.sqrt(mean_squared_error(y_test,pred)) print(fRMSE: {rmse:3.3} ({rmse/np.mean(pred)*100:3.3}%)) score pipeline.score(X_train,y_train) print(Model determination: , score)绘制平滑逼近曲线若直接绘制无序的测试数据会得到锯齿状折线因此用np.linspace生成均匀的输入值范围来绘制平滑的逼近曲线X_range np.linspace(X_test.min(), X_test.max(), 100).reshape(-1,1) y_range pipeline.predict(X_range) plt.scatter(X_test, y_test) plt.plot(X_range, y_range)多项式回归的 RMSE 略有降低、决定系数略有提高但提升并不显著——必须考虑其他特征观察图像你会发现南瓜价格的最低点大约出现在万圣节前后你能解释这是为什么吗方式三引入分类特征one-hot 编码理想情况下我们希望用同一个模型预测不同品种南瓜的价格。但Variety列与Month等数值列不同它包含的是非数值内容这类列称为分类特征categorical。两种编码方式对比要利用品种信息必须先把它转换为数值形式即编码。文档对比了两种方案简单数值编码建立品种表用表中索引替换品种名。这对线性回归不是好方案——线性回归会把索引的真实数值乘以某个系数加到结果上。本例中索引数值与价格的关系显然是非线性的即使人为调整索引顺序也无法解决。one-hot 编码把Variety列替换为 4 个独立列每个品种一列对应品种的行该列为 1否则为 0。这样线性回归中会有4 个系数每个系数负责对应南瓜品种的起始价格更准确地说是价格增量。用 pd.get_dummies 实现 one-hotpd.get_dummies(new_pumpkins[Variety])IDFAIRYTALEMINIATUREMIXED HEIRLOOM VARIETIESPIE TYPE700001710001...............173801001739010017400100用 one-hot 品种训练线性回归只需正确初始化X和y其余代码与之前完全相同X pd.get_dummies(new_pumpkins[Variety]) y new_pumpkins[Price]在完整解决方案中作者封装了一个可复用的run_linear_regression(X, y)函数内部执行 train_test_split、fit、predict、RMSE 与 R² 打印训练结果显示均方误差与之前相当RMSE 5.2419.7%但决定系数大幅提升到约 77%0.774——验证了品种是价格的重要解释变量。用 join 组合更多特征要进一步提高精度可以同时纳入更多分类特征与数值特征如Month、DayOfYear。用join拼接出大特征矩阵X pd.get_dummies(new_pumpkins[Variety]) \ .join(new_pumpkins[Month]) \ .join(pd.get_dummies(new_pumpkins[City])) \ .join(pd.get_dummies(new_pumpkins[Package])) y new_pumpkins[Price]同时纳入City城市与Package包装类型后RMSE 降至 2.8410.5%决定系数达到0.94。方式四组合最优模型多项式 全特征要得到最好的模型可以把上面的组合数据one-hot 编码的分类特征 数值特征与多项式回归结合。文档给出了完整代码# 准备训练数据 X pd.get_dummies(new_pumpkins[Variety]) \ .join(new_pumpkins[Month]) \ .join(pd.get_dummies(new_pumpkins[City])) \ .join(pd.get_dummies(new_pumpkins[Package])) y new_pumpkins[Price] # 划分训练集与测试集 X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state0) # 配置并训练 pipeline pipeline make_pipeline(PolynomialFeatures(2), LinearRegression()) pipeline.fit(X_train,y_train) # 对测试数据预测 pred pipeline.predict(X_test) # 计算 RMSE 与决定系数 rmse mean_squared_error(y_test, pred, squaredFalse) print(fRMSE: {rmse:3.3} ({rmse/pred.mean()*100:3.3}%)) score pipeline.score(X_train,y_train) print(Model determination: , score)注意此处使用了mean_squared_error(y_test, pred, squaredFalse)直接返回 RMSE。该方案在解决方案 notebook 中的实测结果为RMSE 2.238.25%、决定系数 0.965接近 97%。四种模型效果汇总模型RMSE决定系数DayOfYear线性2.7717.2%0.07DayOfYear多项式2.7317.0%0.08Variety线性5.2419.7%0.77全特征线性2.8410.5%0.94全特征多项式2.238.25%0.97一次课程中构建了四个回归模型并把模型质量从 0.06 提升到了 97%。这组对比清晰展示了特征工程的价值时间特征贡献有限而品种、城市、包装等分类特征才是价格预测的核心信息源多项式特征在特征充足时还能再带来约 3 个百分点的提升。实践与延伸动手挑战尝试在本课 notebook 中测试不同的变量组合观察相关性如何与模型精度对应——例如只用Month、只加City、去掉Package等对比每次的 RMSE 与 R² 变化。课后作业仓库配套的 assignment.md 要求利用线性与多项式回归的知识自己找一份数据集或使用 Scikit-learn 内置数据集构建全新模型在 notebook 中解释选择该技术的原因并展示模型精度如果精度不佳要解释原因。评分标准为完整且文档完善的 notebook 为优秀Exemplary解决方案不完整为合格Adequate模型有缺陷或 bug 为待改进Needs Improvement。更多学习资源本课数据清洗与建模的完整可运行代码见 notebook.ipynb含全部实验记录与输出的是 solution/notebook.ipynb本课同样提供了 R 语言版本适合偏好 R 生态的读者回归家族中还有 Stepwise、Ridge、Lasso、Elasticnet 等重要技术值得延伸学习下一课 4-Logistic 逻辑回归 将把回归从预测数值扩展到判断类别。小结本课通过南瓜价格预测这一贯穿性案例完成了从最小二乘原理 → 相关性分析 → 简单线性回归 → 多项式回归 → one-hot 分类特征 → 全特征组合的完整进阶路径。核心收获可以归纳为三点先做 EDA 找对特征品种比日期重要得多用 train_test_split 与固定随机种子保证评估可信用 RMSE 与 R² 双指标客观度量模型——这正是一个可复用到任何回归任务的标准化工作流。【免费下载链接】ML-For-Beginners12 weeks, 26 lessons, 52 quizzes, classic Machine Learning for all项目地址: https://gitcode.com/GitHub_Trending/ml/ML-For-Beginners创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表