YAOTU INSIGHTS

线性回归身高预测实战:数据预处理与模型评估全流程

线性回归身高预测实战:数据预处理与模型评估全流程
简介这份资源面向机器学习入门者与需要掌握回归建模的开发者围绕身高预测这一具体场景讲解如何用线性回归建立身高与年龄、体重、性别等因素之间的依赖关系帮助读者理解连续值预测的完整流程。压缩包共2个文件包含1个xlsx数据表与1个py脚本整体约80KB前者用于存放身高、年龄、性别、体重等样本数据后者基于scikit-learn实现数据预处理、模型训练、评估与预测。内容覆盖数据清洗与标准化、分类变量编码、训练测试集划分以及MSE、RMSE、R²等评估指标的使用并延伸讨论多项式回归、岭回归、套索回归与集成方法等改进思路。已有115人学习下载适合希望用最小体量案例快速跑通线性回归全流程、并对照代码理解建模细节的读者参考。1. 线性回归身高预测三行代码能跑通但数据没洗对全白搭很多人第一次接触机器学习都是从线性回归开始的。原因很简单它数学直觉清晰、代码量少、结果可解释。但真正上手做身高预测这类任务时翻车往往不在模型本身而在数据。你拿到一份「身高预测参照表-1.xlsx」里面可能有年龄、性别、体重、身高几列看起来干净实际上缺失值、量纲差异、性别编码方式都会直接影响模型能不能收敛、系数能不能解释。这个资源包的核心价值在于它把「线性回归算法」从公式拉到了一个具体可复现的场景里。LinearRegression_1.py是主脚本身高预测参照表-1.xlsx是训练数据整个流程覆盖了数据预处理、模型训练、评估和预测四个环节。适合两类人一是正在做机器学习线性回归实验的学生需要一份能跑通的参考实现二是刚转行的工程师想用一个最小闭环理解 scikit-learn 的回归流程。下面我会按实际拆包的顺序把每一步的参数、坑和验证方法讲清楚。2. 拆开资源包数据表结构和脚本骨架先看懂2.1 身高预测参照表里到底有什么拿到身高预测参照表-1.xlsx之后第一件事不是急着read_excel而是先用 pandas 把列名、类型、缺失情况摸一遍。常见的身高预测数据集一般包含以下几类字段字段类型典型列名数据类型处理方式连续特征年龄、体重float/int标准化或直接使用分类特征性别object/int独热编码或标签编码目标变量身高float作为 y不参与特征缩放无关列编号、姓名object直接丢弃先跑这段代码做体检import pandas as pd df pd.read_excel(身高预测参照表-1.xlsx) print(df.shape) # 行数、列数 print(df.dtypes) # 每列数据类型 print(df.isnull().sum()) # 每列缺失值数量 print(df.describe()) # 连续列的均值、标准差、分位数 print(df.head(10)) # 前10行肉眼检查逻辑说明shape告诉你样本量够不够一般线性回归至少需要几十条以上才有统计意义dtypes决定后面要不要做类型转换比如性别如果是字符串必须编码isnull().sum()是血泪经验——很多人直接fit然后报错就是因为某列有 NaNdescribe()帮你看量纲如果体重是 60 而年龄是 20差距不大但如果有一列是「克」另一列是「米」不标准化会让系数失真。参数说明read_excel默认读第一个 sheet如果参照表有多个 sheet需要加sheet_nameSheet1指定。如果列名有空格或中文后续引用时要用df[列名]而不是df.列名。2.2 LinearRegression_1.py 的典型骨架这个脚本的常见结构是导入库 → 读数据 → 特征工程 → 划分训练测试集 → 训练模型 → 评估 → 预测。我一般会把它拆成函数方便单独调试每一步。核心依赖是scikit-learn和pandas安装命令pip install scikit-learn pandas openpyxlopenpyxl是读.xlsx的引擎不装会报ImportError。脚本里最关键的几行通常是from sklearn.linear_model import LinearRegression from sklearn.model_selection import train_test_split from sklearn.metrics import mean_squared_error, r2_score X df[[年龄, 体重, 性别编码]] # 特征矩阵 y df[身高] # 目标向量 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) model LinearRegression() model.fit(X_train, y_train) y_pred model.predict(X_test) print(MSE:, mean_squared_error(y_test, y_pred)) print(R2:, r2_score(y_test, y_pred)) print(系数:, model.coef_) print(截距:, model.intercept_)逻辑说明train_test_split的test_size0.2表示 20% 做测试random_state42保证每次划分一致方便复现。fit做的就是最小化残差平方和解析解直接算出系数。coef_告诉你每个特征对身高的影响方向和大小intercept_是基准值。参数说明LinearRegression默认fit_interceptTrue即自动计算截距如果数据已经中心化可以设为False。n_jobs参数在普通线性回归里没用别被误导。评估指标里 MSE 单位是身高的平方RMSE 开根号后才是「平均差多少厘米」更直观。3. 从原始表到可训练矩阵预处理四步不能省3.1 缺失值和异常值处理身高数据里最常见的脏数据是身高填了 0 或 999、体重缺失、性别写成「男/女/未知」。处理顺序应该是先处理异常值再处理缺失值最后编码。# 异常值身高合理范围设为 100-250 cm df df[(df[身高] 100) (df[身高] 250)] # 缺失值数值列用中位数填充分类列用众数填充 df[体重] df[体重].fillna(df[体重].median()) df[性别] df[性别].fillna(df[性别].mode()[0])逻辑说明用中位数而不是均值是因为身高体重类数据可能有偏态均值容易被极端值拉偏。众数填充性别适合缺失比例低的情况如果缺失超过 30%考虑直接删列或做模型插补。参数说明median()和mode()[0]都是 pandas 内置方法mode()返回的是 Series取第一个值即可。3.2 性别编码标签编码还是独热编码性别只有两个取值时标签编码男0女1就够用线性回归会把系数解释为「性别从 0 变到 1 时身高的平均变化」。但如果性别有三个以上取值必须用独热编码否则模型会误以为类别之间有大小顺序。# 二分类标签编码 df[性别编码] df[性别].map({男: 0, 女: 1}) # 多分类独热编码 # df pd.get_dummies(df, columns[性别], drop_firstTrue)逻辑说明map是显式映射比LabelEncoder更可控因为你知道每个值对应什么。get_dummies的drop_firstTrue会丢掉一个类别作为基准避免多重共线性。参数说明如果参照表里性别已经是 0/1跳过这步。注意map遇到未定义的键会返回 NaN所以映射前先unique()看一下实际取值。3.3 特征缩放标准化到底要不要做线性回归的系数解对特征量纲敏感。如果年龄范围 10-80体重范围 30-120两者差距不大不标准化也能跑。但如果加入「年收入」这种量级到万的列不标准化会导致系数数值极小解释困难。from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test)逻辑说明fit_transform只在训练集上做测试集用transform这是防止数据泄露的铁律。标准化后系数表示「特征每变化一个标准差身高变化多少」。参数说明StandardScaler做的是 (x - 均值) / 标准差。如果数据有极端离群值改用RobustScaler更稳。3.4 训练集测试集划分的随机种子random_state不设的话每次运行划分不同评估指标会波动你以为是模型改了其实是数据换了。固定一个种子比如 42 或 0是实验可复现的基本要求。X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) print(训练集:, X_train.shape, 测试集:, X_test.shape)逻辑说明输出 shape 确认划分比例对不对。如果样本量很小比如不到 100 条test_size0.2可能只剩十几条测试数据评估结果不稳定可以考虑交叉验证。4. 训练、评估与预测指标怎么看才不被忽悠4.1 拟合模型后先看系数和截距模型训练完不要只看 R²先把系数打出来。系数正负代表特征与身高的正负相关绝对值大小代表影响程度标准化后可比。model LinearRegression() model.fit(X_train, y_train) for name, coef in zip(X.columns, model.coef_): print(f{name}: {coef:.4f}) print(f截距: {model.intercept_:.4f})逻辑说明如果「年龄」系数是正的说明年龄越大身高越高在样本范围内如果「体重」系数是正的说明体重越大身高越高。如果出现与常识相反的符号先检查数据里有没有异常值或编码错误。参数说明zip把列名和系数配对避免看错顺序。:.4f保留四位小数方便对比。4.2 MSE、RMSE、R² 三个指标各看什么from sklearn.metrics import mean_squared_error, r2_score import numpy as np y_pred model.predict(X_test) mse mean_squared_error(y_test, y_pred) rmse np.sqrt(mse) r2 r2_score(y_test, y_pred) print(fMSE: {mse:.2f}) print(fRMSE: {rmse:.2f} cm) print(fR2: {r2:.4f})逻辑说明MSE 是均方误差单位是 cm²不直观RMSE 开根号后单位是 cm可以直接说「预测平均偏差多少厘米」R² 表示模型解释了目标变量多少比例的方差越接近 1 越好但为负说明模型还不如直接用均值预测。参数说明np.sqrt是 numpy 的开根号函数。R² 在测试集上比训练集低很多说明过拟合但线性回归本身过拟合风险低更可能是数据分布不一致。4.3 用新样本做预测的输入格式预测时输入的特征顺序必须和训练时一致列名也要一致。常见错误是手动构造 DataFrame 时列顺序变了导致预测结果离谱。new_data pd.DataFrame({ 年龄: [25], 体重: [70], 性别编码: [0] }) pred model.predict(new_data) print(f预测身高: {pred[0]:.1f} cm)逻辑说明new_data的列顺序和X.columns一致scikit-learn 内部按位置对应。如果训练时用了标准化预测前也要用同一个scaler.transform(new_data)。参数说明单条预测传入的是二维结构[25]而不是25。pred[0]取出标量结果。5. 避坑与排查这五类报错我几乎每次都遇到5.1 报错 Input contains NaN现象fit时抛出ValueError: Input contains NaN, infinity or a value too large。原因原始表里有空单元格或者map编码时遇到未定义类别产生了 NaN。解决在fit之前加print(df.isnull().sum())确认哪列有缺失用fillna或dropna处理。编码列用df[性别].unique()检查实际取值。5.2 R² 为负数现象测试集 R² 小于 0模型看起来「还不如瞎猜」。原因测试集样本太少、特征与身高无线性关系、或者训练集和测试集分布差异大。解决先看样本量如果测试集不到 20 条改用交叉验证cross_val_score。再看散点图确认特征和身高是否有大致线性趋势。如果确实非线性考虑多项式回归。5.3 系数符号与常识相反现象年龄的系数是负的意味着年龄越大身高越矮。原因数据里混入了儿童和成年人儿童年龄小但身高在增长期成年人年龄大但身高已固定整体不是单调关系。解决分年龄段建模或者加入年龄的平方项。也可以先画年龄 vs 身高的散点图肉眼确认关系形态。5.4 预测值出现负数或离谱大数现象新样本预测出身高 -30 cm 或 500 cm。原因新样本特征超出了训练数据的取值范围线性模型外推能力很差。解决预测前检查输入是否在训练集的min和max之间。如果必须外推考虑改用树模型树模型对外推更保守。5.5 中文列名导致的 KeyError现象df[身高]报KeyError。原因Excel 列名里有空格比如身高 或身高(cm)。解决读入后先df.columns df.columns.str.strip()去掉首尾空格再用print(df.columns.tolist())确认实际列名复制粘贴使用。6. 进阶技巧用交叉验证和残差图判断模型该不该换线性回归跑通不难难的是判断它在这个数据上到底够不够用。我一般会做两件事交叉验证看稳定性残差图看模式。交叉验证把数据分成 K 份轮流做测试集能给出 R² 的均值和标准差。如果均值不错但标准差很大说明模型对数据划分敏感样本量可能不够。from sklearn.model_selection import cross_val_score scores cross_val_score(model, X, y, cv5, scoringr2) print(fR2 均值: {scores.mean():.4f}) print(fR2 标准差: {scores.std():.4f})逻辑说明cv5表示五折交叉验证scoringr2指定评估指标。标准差超过 0.1 就要警惕说明模型不稳定。参数说明cross_val_score内部会自动划分不需要提前train_test_split。如果数据有顺序比如按时间排列要用TimeSeriesSplit。残差图是另一个黑匣子探测器。残差 真实值 - 预测值。如果残差随机分布在 0 附近说明线性假设合理如果残差呈现 U 形或喇叭形说明关系非线性或方差不齐。import matplotlib.pyplot as plt residuals y_test - y_pred plt.scatter(y_pred, residuals) plt.axhline(y0, colorr, linestyle--) plt.xlabel(预测身高) plt.ylabel(残差) plt.show()逻辑说明横轴是预测值纵轴是残差。理想情况是点均匀分布在红线上下没有明显形状。如果点呈现曲线趋势考虑加多项式项如果点越往右越散考虑对目标变量做对数变换。参数说明axhline画一条 y0 的参考线。plt.show()在脚本里会弹窗在 Jupyter 里直接显示。如果残差图显示明显非线性可以试一下多项式回归from sklearn.preprocessing import PolynomialFeatures from sklearn.pipeline import make_pipeline poly_model make_pipeline( PolynomialFeatures(degree2, include_biasFalse), LinearRegression() ) poly_model.fit(X_train, y_train) print(多项式 R2:, poly_model.score(X_test, y_test))逻辑说明PolynomialFeatures(degree2)会自动生成特征的平方项和交叉项make_pipeline把预处理和模型串起来。如果 R² 明显提升且残差图改善说明线性假设确实不成立。参数说明degree不要一上来就设太高2 或 3 足够再高容易过拟合。include_biasFalse是因为LinearRegression自己会算截距。从那以后我每次跑线性回归都强制走一遍「缺失值检查 → 散点图 → 交叉验证 → 残差图」这四步不再只看一个 R² 就下结论。这套流程帮我省了很多后悔药也希望帮到你。本文还有配套的精品资源点击获取