ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

线性回归模型训练全流程解析:从数据清洗到梯度下降

线性回归模型训练全流程解析:从数据清洗到梯度下降 很多人学机器学习第一眼就盯着神经网络、Transformer、大模型结果一上手训练就懵。我见过太多人折腾目标检测、OCR这类深度学习模型调了一周环境最后连最基本的回归问题都没捋清楚。线性回归模型训练恰恰是帮你建立“模型训练”直觉的最好起点——它简单到可以用几行代码写完却包含了数据处理、损失函数、梯度下降、评估指标这一整套流程。这篇文章我不讲虚的直接带你走一遍完整训练过程把每一步背后的“为什么”也讲透。无论你是准备面试、做课程实验还是想真正入门机器学习这篇都值得看完。1. 先想清楚线性回归到底在做什么1.1 从直觉到公式直线的故事线性回归的核心直觉一句话就能说完在二维平面上找一条直线让所有数据点到这条直线的距离总和最小。换成三维就是找一个平面再往上就是找一个超平面。我用个生活案例解释一下。假设你记录了一组房屋面积和价格的对应关系面积平方米价格万元5065708890110110135横轴是面积纵轴是价格把数据点画在坐标系里你会发现它们大致沿着一条斜向上的直线分布。线性回归要做的就是把这条直线找出来。直线的表达式是y w * x bx是输入特征面积y是预测值价格w是斜率也叫权重b是截距也叫偏置。模型训练的终极目标就是找到最合适的w和b让预测价格和真实价格之间的误差尽量小。之前有学生在西电机器学习期末复习时跑来问我“为什么线性回归的公式是y wx b而不是y ax c”其实只是符号习惯不同a/c就是w/b换个字母而已。真正要理解的是w决定了特征对结果的影响方向和强度b则是在没有任何输入时的基线预测值。比如面积每增加1平方米房价平均上涨0.9万元那w就是0.9如果一套0平米的房子“理论价格”是20万那b就是20——虽然现实中不存在0平米的房子但b的存在让直线可以上下平移拟合更灵活。1.2 为什么用“最小二乘法”而不是“绝对值法”衡量预测值和真实值之间的误差有几种方式。最直观的是绝对误差|y_true - y_pred|。把所有点的绝对误差加起来就是平均绝对误差MAE。那为什么线性回归标准解法用的是最小二乘法也就是均方误差MSE呢两个原因。第一平方误差对大的误差更敏感。一个点的误差从1变成3绝对值法的惩罚只增加了2但平方误差从1跳到9增加了8。方向相反的误差正误差和负误差在平方之后都变成了正数不会相互抵消。这在实际训练中意味着模型会优先修正那些偏差特别大的样本整体拟合效果通常更符合直觉。第二平方误差是光滑的凸函数。凸函数的意思是只有一个全局最低点不存在“掉进局部极小值出不来”的问题。这对梯度下降法太重要了。绝对误差在0点处不可导梯度计算时不方便平方误差处处可导导数是2 * (y_pred - y_true) * x形式极其简洁。我每次都跟朋友强调损失函数的选择不是随意的它直接决定了优化过程的难易程度和最终模型的行为偏好。你要是选择绝对值损失也能训练SGD照样能跑但你会遇到梯度在0点附近跳动、收敛不稳的问题。初学者不需要在损失函数上玩花样老老实实用MSE先跑通全流程再说。2. 训练前的准备数据清洗与特征工程2.1 数据从哪里来别跳过这个“脏活”很多教程上来就加载sklearn自带的波士顿房价数据集跑个fit就完事。但真实场景下你拿到的数据远没有这么干净。我在带学生做实验时至少三分之一的时间花在数据预处理上而不是模型训练本身。常见的脏数据有这几类缺失值某条记录的面积是NaN或者价格空缺。处理方式有删除该样本、用均值/中位数填充、用前后值填充。对于线性回归如果缺失比例不高比如低于5%直接删除是省事且安全的选择如果比例较高用均值填充可能引入偏差可以考虑用其他特征做简单预测模型来填充。异常值面积500平米却标价5万这种明显是录入错误也可能是特殊交易亲属过户。线性回归对异常值非常敏感因为误差是平方的一个极端异常值会在损失函数中占主导地位。我习惯先画散点图或箱线图人工确认异常值后再决定是否剔除。重复样本同一套房源被录入了多次。重复样本会让模型在训练时“偏爱”这部分数据导致评估结果虚高。删除重复项是一行代码的事但很多人会忽略。量纲不一致面积用平方米、楼龄用年、价格用万元取值范围从个位数到上千。如果不做归一化梯度下降的收敛速度会慢得让人抓狂这个我在下一节详细说。有人可能会问“我用sklearn的LinearRegression它用的是正规方程最小二乘法的闭式解不归一化也能解出来啊”确实闭式解不需要特征缩放但代价是计算复杂度是O(n^3)特征维度一高比如超过1万维内存和耗时都会爆炸。而且你一旦切换到梯度下降或深度学习框架特征的量纲问题会立刻变成拦路虎。所以我建议初学者不管用什么模型都养成做特征缩放的习惯。2.2 特征缩放与数据集划分两个关键步骤特征缩放最简单的做法是标准化Standardization公式是z (x - mean) / std处理后每个特征的均值是0标准差是1。这样所有特征都处在相近的数值范围内梯度下降时每个维度的更新步伐不会相差太大。还有一种方式叫归一化MinMaxScaler把数据压缩到[0,1]区间x_norm (x - min) / (max - min)当你知道特征的上下界并且没有极端离群值的时候MinMaxScaler效果不错。但如果数据里有极端值MinMaxScaler会被一个异常值拉垮标准化因为用的是均值和标准差受异常值影响相对小一些。数据划分也是训练前必须做的事。最常用的比例是训练集:测试集 8:2或7:3。划分时注意两点第一划分之前要做随机打乱避免数据本身存在顺序规律比如前100条都是小面积廉价房后100条是大面积豪宅否则训练集和测试集分布会严重不一致。第二要在训练集上计算均值和标准差然后用训练集的mean和std去转换验证集和测试集。这是个非常容易踩坑的地方。有人图省事先对整个数据集做标准化再划分这就造成了信息泄露——测试集的信息在训练开始前已经“泄漏”给了模型。正规做法是先划分再fit训练集的scaler再用transform去处理测试集。from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42) scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test) # 注意只用transform不用fit这段代码是标准示范random_state固定随机种子保证实验结果可复现这对写实验报告或者和别人对比结果都非常重要。我见过太多人random_state不设置每次运行结果都不一样还以为自己代码写错了。3. 模型训练的核心过程从梯度下降到参数收敛3.1 损失函数与梯度下降的关系有了数据和损失函数接下来就是优化问题怎么找到让MSE最小的w和b。直观想象一下MSE关于w和b的函数是一个碗状的曲面。碗底就是最优解。梯度下降的思路是在当前点计算梯度也就是损失函数对参数的偏导数梯度的方向是损失上升最快的方向那我们沿着相反方向走一小步就能让损失下降。反复走直到走到碗底。对单变量线性回归损失函数的定义是L(w, b) (1/N) * Σ(y_i - (w * x_i b))²对w求偏导∂L/∂w -(2/N) * Σ(y_i - (w * x_i b)) * x_i对b求偏导∂L/∂b -(2/N) * Σ(y_i - (w * x_i b))然后更新参数w w - learning_rate * ∂L/∂w b b - learning_rate * ∂L/∂b这里的learning_rate学习率就是每次走多远。走太大了容易跨过碗底甚至发散走太小了半天都到不了底。这个参数的控制是模型训练里最有手感的部分。3.2 学习率怎么选经验值和方法论学习率的取值没有绝对标准但有几个常用量级可以参考。特征归一化之后数据范围在0附近波动学习率0.01或者0.1是比较安全的起步值。深度学习框架默认的学习率策略很多TensorFlow和PyTorch里Adam优化器默认学习率是0.001但在手写线性回归时SGD用0.01到0.1之间通常都能正常收敛。怎么判断学习率是否合适看损失曲线损失曲线震荡剧烈、不下降学习率太大参数在最优解附近反复横跳。损失曲线缓慢下降但迟迟不收敛学习率太小可以试试增大10倍。损失曲线下降到某个值后不再变化可能是学习率适中但模型容量不够也可能是接近收敛了。我通常会先跑几次小实验把学习率按1e-3、1e-2、1e-1、5e-1这样对数量级试探一遍选出曲线最平滑的那个。这个过程叫“学习率扫描”虽然朴素但极其有效。很多深度学习框架里也有自动调整学习率的工具比如PyTorch的ReduceLROnPlateau在loss连续几个epoch不下降时自动调小学习率。但调试线性回归阶段我建议手动调一次你会对参数更新形成真正的直觉。还有一点要注意SGD每次迭代使用一个样本计算梯度噪声大但计算快批量梯度下降使用所有样本梯度准确但计算昂贵实践中常用小批量Mini-batch每次用32或64个样本计算平均梯度。线性回归的数据量通常不大所以手写代码时用全批量梯度下降也能接受。3.3 用numpy手写一个训练循环这是我个人比较推荐的学习方式——不要一开始就依赖sklearn先用numpy手写整个训练过程让每一行代码都对应一个数学概念。你才能真正理解模型训练到底在做什么。下面是一个完整的单变量线性回归训练代码我加了详细的注释import numpy as np import matplotlib.pyplot as plt # 生成模拟数据y 3 * x 5 噪声 np.random.seed(42) X np.random.rand(100, 1) * 10 # 面积范围0-10 y 3 * X 5 np.random.randn(100, 1) * 2 # 价格加噪声使任务更真实 # 参数初始化w和b都从0开始 w 0.0 b 0.0 learning_rate 0.01 epochs 300 N len(X) loss_history [] for epoch in range(epochs): # 前向传播计算预测值 y_pred w * X b # 计算损失MSE loss np.mean((y - y_pred) ** 2) # 计算梯度 dw -(2 / N) * np.sum((y - y_pred) * X) db -(2 / N) * np.sum(y - y_pred) # 更新参数 w w - learning_rate * dw b b - learning_rate * db loss_history.append(loss) if epoch % 50 0: print(fEpoch {epoch}, Loss: {loss:.4f}, w: {w:.4f}, b: {b:.4f}) # 打印最终结果 print(f最终参数w {w:.4f}, b {b:.4f}) # 可视化损失曲线 plt.plot(loss_history) plt.xlabel(Epoch) plt.ylabel(MSE Loss) plt.title(Loss Curve) plt.show()运行结果中你会看到loss从最初的几十一路下降最后稳定在某个值附近。训练100条随机数据300轮迭代w收敛到2.9左右、b收敛到5.3左右和真实生成数据的参数w3b5很接近。这就是模型“学到东西了”的过程从一无所知的0慢慢逼近数据背后的真实规律。这里的X是二维的100, 1形状如果你传入一维数组numpy的广播机制会捣乱矩阵乘法时容易报shape不匹配的错误。初学者碰到这种报错不要慌用X.reshape(-1, 1)或者X[:, np.newaxis]扩展维度就能解决。4. 评估与效果分析不只是看loss4.1 R²和调整R²模型到底好不好训练完以后不能只看训练集loss低就宣布成功。一个真正有用的评估要在测试集上进行而且要用多个指标综合判断。最常用的指标是R²决定系数它的公式是R² 1 - SS_res / SS_totSS_res是残差平方和预测和真实值差距的平方和SS_tot是总平方和真实值与其均值差距的平方和。R²的范围是负无穷到1越接近1说明模型解释的方差比例越高。R² 0.85的意思是模型解释了因变量85%的方差剩下15%是噪声或未被建模的因素。但R²有它的问题当特征数量增加时R²会只增不减哪怕新加的特征和预测目标毫无关系。所以多变量回归时要看调整R²Adjusted R² 1 - (1 - R²) * (n - 1) / (n - p - 1)n是样本数p是特征数。这个公式会惩罚多余特征帮你判断新增特征是否真的有价值。我举个实际例子。一个学生做房价预测实验单特征面积的R²是0.72加了一个“所在楼层”特征后R²变成0.73看起来提升了一点点。但调整R²几乎没变说明楼层这个特征对房价的解释贡献很有限可能主要是噪声。如果只盯着R²就会得出“楼层很重要”的错误结论。4.2 多元线性回归与特征相关性陷阱现实问题很少只有一个特征。房价不只取决于面积还和地段、朝向、楼龄、交通便利度都有关。当特征增多后线性回归的形式变成y w1x1 w2x2 ... wn*xn b此时可以写成矩阵形式y X * w b训练思路和单变量时完全一致只是梯度公式里的X变成矩阵。用numpy实现时参数w从一个标量变成一个向量更新逻辑不变。sklearn里的LinearRegression可以直接处理多特征场景from sklearn.linear_model import LinearRegression model LinearRegression() model.fit(X_train_scaled, y_train) y_pred model.predict(X_test_scaled) print(系数, model.coef_) print(截距, model.intercept_)多特征时最需要警惕的是多重共线性两个特征之间高度相关。比如“房屋面积”和“房间数量”面积大的房子通常房间也多两者携带的信息高度重叠。这种情况下模型的w1和w2互相“拉扯”系数估计变得不稳定可能在样本数据上表现不错但泛化能力差。怎么发现共线性看特征之间的相关系数矩阵。相关系数绝对值超过0.8就要警惕了。处理方式有三种直接删掉其中一个冗余特征用PCA做降维改用岭回归L2正则化或Lasso回归L1正则化让模型自动压制不重要的系数。这些方法在scikit-learn里都有现成实现但对刚入门的朋友我的建议是先学会识别问题再考虑解法。还有一个容易被忽略的评估视角残差图。把残差y_true - y_pred画在纵轴上预测值画在横轴上。如果残差随机分布在0附近说明模型拟合良好如果残差呈现明显的规律性结构比如喇叭形说明可能存在异方差性或者模型缺少了某个关键特征。线性回归的基本假设之一是残差独立同分布诊断残差图能帮你发现这个假设是否被破坏。5. 常见问题与排查技巧实录5.1 训练报NaN怎么办从数据到梯度逐一排查模型训练报NaN是我在带学生做实验时遇到次数最多的问题之一。很多人的第一反应是去搜“训练报nan”找到一堆深度学习框架的解决方案结果一排查发现根因完全不同。线性回归报NaN的原因主要有三类第一类学习率太大。梯度下降时参数更新幅度过大导致参数值飞向无穷大再算loss时就变成NaN。排查方法把学习率调到原来的十分之一看是否恢复。我试过最夸张的一次learning_rate从0.01改成0.001loss曲线立刻从一团乱麻变成平滑下降。第二类数据里有无穷大或缺失值。有些数据源会直接把缺失值填成99999或者-99999这些极端值参与平方计算时中间结果可能溢出。排查方法打印数据的最小值、最大值看是否有明显异常。第三类优化选型的问题。对于线性回归这种凸优化问题理论上梯度下降只要学习率合适就不会发散。但如果特征没做归一化不同维度的梯度数值差异巨大更新时一个维度恢复“正常”的速度远快于另一个累计误差在特定条件下容易失控。所以再强调一次特征标准化是你所有问题排查的第一步。5.2 loss不下降时怎么debug几个立竿见影的操作loss不下降比报NaN更让人头疼因为程序没报错但结果就是不好。我总结了一套高效的排查顺序先检查数据本身。画一下y和x的散点图如果y是固定值或规律极其混乱模型当然学不出来。我有一次调试了很久最后发现数据导入时列对应错了标签列取成了id列模型再怎么训练都不可能拟合。再检查梯度计算。手写代码时最容易出错的点就是梯度公式里的符号和括号。一个简单有效的验证方法把参数手动设成一组已知值用数值微分法有限差分法和解析梯度对比差值应该在1e-6级别的误差以内。写代码做一个梯度检查比盯着公式看一整天高效得多。然后检查训练流程的中间输出。打印每个epoch的loss、w、b的值观察它们的变化规律。如果最开始loss就很大且一路持平通常是前向传播或损失函数有bug如果loss下降却卡在某个值上不降可能是模型容量不足比如数据根本不是线性关系或者特征中没有足够的信息。最后在数据中加入一个强线性关系的特征做“冒烟测试”。比如生成一个y 2*x 1的完美线性数据如果模型在100轮内还不能逼近说明训练代码还有问题如果能快速收敛说明问题出在你的真实数据上而不是训练流程。5.3 欠拟合与过拟合线性回归同样要面对很多人觉得线性回归简单就不需要提过拟合——这是误解。当特征数量多于样本数量时线性回归同样会过拟合学到数据中的噪声而不是规律。欠拟合的表现是训练集loss和测试集loss都很高。原因通常是特征不够、模型太简单比如真实关系是曲线但你用直线去拟合、或者特征工程做得不到位。解决方案是增加有效特征、引入多项式特征用x²、x³等扩展特征维度、或者换用非线性模型。过拟合的表现是训练集loss很低但测试集loss显著偏高。原因通常是特征过多、样本过少。这时候可以考虑增加样本量或者做数据增强在允许的范围内对数据做合理扰动正则化在线性回归的损失函数中加入惩罚项L2正则化就是岭回归把系数往小里压L1正则化是Lasso可以让一些系数直接变成0相当于自动特征选择简化模型删掉一些相关性强或重要性低的特征正则化系数的选择也是一个可以细说的点。岭回归的alpha太小几乎等于普通最小二乘解决不了过拟合alpha太大把所有系数都压制到接近0模型变成欠拟合。一个常用策略是尝试一组对数分布的alpha值比如从1e-4到10画验证集loss随alpha变化的曲线选择最优点。sklearn的RidgeCV可以自动帮你在给定候选集里选出最优alpha非常方便。最后说点实在的我个人的体会是线性回归模型训练这个看似“入门级”的话题实际上蕴含了整个机器学习流程的骨架——数据清洗、特征工程、模型选型、损失函数设计、参数优化、评估与诊断。你用sklearn一行代码fit出结果和你手写梯度下降一步步逼近最优参数这两者的认知深度是完全不同的。所以我建议所有初学者都做一遍“手写线性回归”实验哪怕代码花了半天才跑出和sklearn相同的结果这半天的调试过程比看十遍教程都有价值。最后再分享一个小技巧训练完模型后把你学到的参数代回原始公式和物理常识对一下。比如房价预测模型里面积的系数如果是负数说明面积越大房价越低这大概率是数据有问题而不是什么新发现。模型输出的数字会骗人但领域常识不会。带着这种怀疑精神去审视每个训练结果你才能真正用好机器学习而不是被它带着走。
返回列表