
ML-For-Beginners 回归课程实战用 Scikit-learn 以四种方式构建南瓜价格回归模型【免费下载链接】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 课程第 3 课线性回归为核心使用美国南瓜批发价数据集2-Regression/data/US-pumpkins.csv系统讲解机器学习回归建模的完整链路从最小二乘法的数学原理、相关系数分析到用 Scikit-learn 训练简单线性回归、多项式回归再到通过 one-hot 编码引入品种、城市、包装等分类特征最终把模型决定系数从 0.07 提升到 0.97。读完本文你将掌握在 Jupyter Notebook 中完成数据清洗 → 相关性探索 → 特征工程 → 模型训练与评估 → 结果对比这一标准回归建模流程的实战能力。上图为线性回归与多项式回归的拟合形态对比直线只能表达单调关系而曲线能拟合先降后升等非线性趋势。课程定位与前置知识线性回归用于预测一个数值型结果例如房价、温度、销售额。它的核心思路是找到一条能够最好地刻画输入特征与输出之间关系的直线。本课强调先理解概念本身再向更高级的回归技术逻辑回归、ARIMA 等延伸。动手之前你需要具备掌握本课程前两课的内容对南瓜价格数据集的结构有所了解会用 Matplotlib 做基本可视化能在 Visual Studio Code 中为2-Regression/3-Linear/notebook.ipynb配置 Python 内核并运行依赖库pandas、numpy、matplotlib、scikit-learn解决方案 Notebook2-Regression/3-Linear/solution/notebook.ipynb使用了 Python 3.7/3.9 内核可作对照参考。数据准备从原始 CSV 到new_pumpkins我们带着三个业务问题来加载数据什么时候买南瓜最划算一箱迷你南瓜大概什么价格应该买半蒲式耳篮装还是 1 1/9 蒲式耳箱装上一课清洗后的数据只保留约 400 个秋季数据点按蒲式耳标准化了价格。本课在 Notebook 中预加载了数据并绘制了月份-价格的初始散点图。为了得到更多信息继续清洗数据。关键代码如下出自 notebook.ipynbimport pandas as pd import matplotlib.pyplot as plt import numpy as np from datetime import datetime pumpkins pd.read_csv(../data/US-pumpkins.csv) # 只保留按蒲式耳计价的记录 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}) # 按包装规格折算为单位蒲式耳价格 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数据框价格为每蒲式耳的均价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.636364DayOfYear年内第几天通过日期差计算得到day_of_year pd.to_datetime(pumpkins[Date]).apply(lambda dt: (dt-datetime(dt.year,1,1)).days)线性回归的数学基础最小二乘法如课程第 1 课所述线性回归的目标是画出一条直线用于展示变量关系呈现输入变量与输出之间的依赖做出预测准确预测新数据点相对这条线会落在哪里。绘制这类直线最典型的方法是最小二乘回归Least-Squares Regression。所谓最小二乘指把模型的总误差降到最小对每个数据点测量其与回归线的垂直距离称为残差residual然后把这些距离平方并求和最终找到让这个和最小的那条线。为什么要对残差做平方两个主要原因只关心大小、不关心方向把 -5 的误差和 5 的误差同等对待平方让所有值都为正惩罚离群点平方会放大较大误差的权重迫使回归线更贴近偏离较远的点。 数学速览这条最佳拟合线line of best fit可以用方程表达Y a bXX是解释变量Y是因变量。b是直线斜率a是 y 轴截距即X 0时Y的取值。先计算斜率b对应到南瓜数据按月份预测每蒲式耳南瓜价格X代表价格Y代表销售月份。再计算Y的值如果价格约 4 美元那么对应月份就是 4 月April计算回归线的数学过程必须同时体现斜率与截距——截距即X 0时Y所处的位置。相关性分析找对特征比急着训练更重要相关系数Correlation Coefficient描述 X 与 Y 之间的关联强度。用散点图可以快速目测数据点排列成规整直线则相关性高散乱分布则相关性低。一个优秀的线性回归模型其相关系数应当接近 1 而非 0。课程在这里安排了一个动手检验运行配套 Notebook观察月份-价格散点图凭视觉判断相关性高低再换用更细的粒度——DayOfYear年内第几天——相关性是否发生变化先用corr函数量化相关性print(new_pumpkins[Month].corr(new_pumpkins[Price])) print(new_pumpkins[DayOfYear].corr(new_pumpkins[Price]))在解决方案 Notebook 中的实际输出为-0.14878293554077535 -0.16673322492745407相关系数很小Month约 -0.15DayOfYear约 -0.17。但散点图中似乎存在几簇明显的价格团可能对应不同的南瓜品种。为了验证这个假设把每个品种用不同颜色画出——通过给scatter传递ax参数可以在同一张图上叠加所有品种的点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)调查结果指向一个重要结论品种Variety对总价的影响比销售日期更大。用柱状图可以直观看到各品种均价的差异new_pumpkins.groupby(Variety)[Price].mean().plot(kindbar)接下来只聚焦单一品种 PIE TYPE观察日期对价格的影响pie_pumpkins new_pumpkins[new_pumpkins[Variety]PIE TYPE] pie_pumpkins.plot.scatter(DayOfYear,Price)此时再计算Price与DayOfYear的相关系数约为-0.27解决方案 Notebook 实测输出-0.2669192282197318——说明训练预测模型是有意义的。训练线性回归模型前必须保证数据干净线性回归对缺失值敏感应删除空单元格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第一步分离输入与输出。把特征输入和标签期望输出放入独立的 NumPy 数组X pie_pumpkins[DayOfYear].to_numpy().reshape(-1,1) y pie_pumpkins[Price]注意这里必须对输入做reshapeLinearRegression期望输入是 2D 数组每一行对应一个输入特征向量。本案例只有一个输入特征所以需要形状为 N×1 的数组N 为数据集大小。第二步划分训练集与测试集。训练后需要用未见过的数据验证模型X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state0)test_size0.2表示留出 20% 数据作为测试集random_state0固定随机种子以保证结果可复现。第三步训练。定义LinearRegression对象并调用fitlin_reg LinearRegression() lin_reg.fit(X_train,y_train)fit之后回归的所有系数存放在.coef_属性中。本例只有一个系数约为-0.017——意味着价格随时间略微下降每天约降 2 美分。通过lin_reg.intercept_可取得回归线与 y 轴的截距约21代表年初的价格。解决方案 Notebook 的实测值为lin_reg.coef_, lin_reg.intercept_ # (array([-0.01751876]), 21.133734359909326)第四步评估精度。在测试集上预测再用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%并不理想。另一个模型质量指标是决定系数coefficient of determinationscore 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)多项式回归用 Pipeline 拟合曲线简单线性回归的前提是变量间存在线性关系例如南瓜体积越大价格越高但真实数据常常无法用平面或直线刻画。日期-价格散点图真的必须用直线分析吗价格难道不会波动此时可以尝试多项式回归。多项式是由一个或多个变量与系数组成的数学表达式。多项式回归会生成一条曲线来更好地拟合非线性数据。在本例中如果把DayOfYear的平方项加入输入数据就能用一条在某年内有极小值的抛物线拟合数据。Scikit-learn 提供了便捷的Pipeline管道API把多个数据处理步骤串联起来。管道就是一条估计器链先给模型添加多项式特征再训练回归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²若有两个输入变量 X 和 Y则会额外生成 X²、XY、Y²。需要时也可以使用更高阶多项式。管道与原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 略有下降、决定系数略有上升但提升并不显著——我们需要纳入更多特征。解决方案 Notebook 实测RMSE 2.7317.0%决定系数 0.076。曲线显示南瓜价格最低点出现在万圣节Halloween前后——你能解释这是为什么吗分类特征为什么要 one-hot 编码理想情况下我们希望用同一个模型预测不同品种南瓜的价格。但Variety列与Month等列不同它包含非数值内容这类列称为分类categorical特征。平均价格随品种变化的柱状图见前文price-by-variety图表明品种信息非常有价值。要把品种纳入模型必须先把它转成数值形式即编码。常见做法有两种简单数值编码建立品种-索引表把品种名替换为表中索引。这对线性回归不是好选择线性回归会把索引的数值直接乘上系数加进结果而索引数值与价格的关系显然是非线性的即使人为把索引排序也改变不了这一点One-hot 编码把Variety一列替换为 4 列每品种一列某行属于该品种则对应列取 1否则取 0。这样线性回归会为每种南瓜产生一个系数负责该品种的起始价格或附加价格。用pd.get_dummies即可完成 one-hot 编码pd.get_dummies(new_pumpkins[Variety])输出示意415 行 × 4 列IDFAIRYTALEMINIATUREMIXED HEIRLOOM VARIETIESPIE TYPE700001710001...............1738010017390100174001001741010017420100用 one-hot 编码后的品种作为输入训练线性回归只需正确初始化 X 和 yX pd.get_dummies(new_pumpkins[Variety]) y new_pumpkins[Price]其余训练代码与前面完全相同解决方案 Notebook 将其封装为run_linear_regression(X, y)辅助函数。实测结果均方根误差基本不变5.2419.7%但决定系数大幅提升到约 0.77。想要更高精度可以纳入更多分类特征以及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 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(..., squaredFalse)直接返回 RMSE 值等价于np.sqrt(mean_squared_error(...))的写法。该组合得到决定系数接近 97%、RMSE 2.23约 8% 预测误差。四种模型横向对比本课共训练了四个回归模型评估指标汇总如下数值来自课程文档与解决方案 Notebook 的实际运行结果模型RMSE决定系数DayOfYear线性回归2.77 (17.2%)0.07DayOfYear多项式回归2.73 (17.0%)0.08Variety线性回归5.24 (19.7%)0.77全部特征 线性回归2.84 (10.5%)0.94全部特征 多项式回归2.23 (8.25%)0.97对比结论很清晰特征工程加入分类特征并编码带来的提升远大于单纯从线性换到多项式而全部特征 多项式的组合把模型质量从 0.07 一路提升到 0.97。学习资料与配套资源围绕本课仓库提供了完整可运行的配套资源主 Notebook2-Regression/3-Linear/notebook.ipynb——含数据加载与初始清洗、散点图绘制解决方案 Notebook2-Regression/3-Linear/solution/notebook.ipynb——包含全部模型的实测输出数值是最佳对照参考数据文件2-Regression/data/US-pumpkins.csv——美国南瓜批发价原始数据集作业2-Regression/3-Linear/assignment.md——要求用线性/多项式回归在自有数据集或 Scikit-learn 内置数据集上构建新模型说明技术选型理由并展示模型精度评分标准包括解决方案完整性与正确性R 语言版本2-Regression/3-Linear/solution/R/lesson_3.html——同一课程内容的 R 实现含 Rmd/ipynb 源文件前序课程2-Regression/2-Data/README.md——数据清洗与可视化基础。此外课程还推荐进一步了解 Stepwise、Ridge、Lasso、Elasticnet 等其它重要回归技术详见本课复习与自学章节。挑战与总结挑战在配套 Notebook 中尝试不同的变量组合观察相关性如何影响模型精度——例如只用City、只用Package或改变PolynomialFeatures的阶数对比 RMSE 与决定系数的变化。小结通过本课你完成了用 Scikit-learn 训练四种回归模型的完整闭环用corr做相关性探索发现品种而非日期才是价格的主要驱动因素用LinearRegressiontrain_test_split建立基线模型用 RMSE 和决定系数量化其不足用make_pipeline(PolynomialFeatures(2), LinearRegression())引入非线性拟合用pd.get_dummies对分类特征做 one-hot 编码并通过join融合多类特征最终用全部特征 多项式回归把模型质量提升到 97%。下一节回归课程将引入逻辑回归解决分类判断类别问题——届时你会发现本课掌握的 Pipeline、特征编码与评估方法将再次派上用场。【免费下载链接】ML-For-Beginners12 weeks, 26 lessons, 52 quizzes, classic Machine Learning for all项目地址: https://gitcode.com/GitHub_Trending/ml/ML-For-Beginners创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考