☰
手把手写线性回归代码:从原理到实践全解析
2026/10/9 12:46:32 网站建设 项目流程

手把手写一个线性回归代码,看这一篇就够了

线性回归作为机器学习的“Hello World”,几乎是每位数据从业者的第一课。很多人看完公式觉得懂,一动手敲代码就卡壳:损失函数怎么算?梯度下降怎么更新?训练完怎么评估好坏?即便用sklearn一行搞定,也说不清背后到底发生了什么。这篇文章就把“第2章第2节 简单的线性回归代码”这个经典话题彻底讲透——从原理拆解、代码实现、可视化分析到疑难排查,全部覆盖。适合刚开始接触机器学习的大学生、转行做数据分析的从业者,以及任何想亲手写出第一个回归模型的人。

1. 内容整体设计与思路拆解

1.1 线性回归的本质:一条直线描述数据关系

线性回归想要解决的问题其实特别朴素:给定一堆数据点,我们试图找到一条直线,让这条直线尽可能贴合所有点的分布趋势。数学上,这条直线就是 y = wx + b,其中 w 表示斜率,描述了x每变化一个单位,y平均变化多少;b 是截距,描述了x=0时y的基准值。这里的w和b就是我们常说的模型参数。

看着很简单对吧?真正关键的在于“尽可能贴合”这四个字。怎么定义贴合?怎么衡量当前这条直线和真实数据点的差距?这就引出了损失函数——通常使用均方误差(MSE),也就是把每个真实值和预测值的差平方,再求平均。平方的目的是消除正负抵消,同时放大较大的偏差,让模型对“错得很离谱”的预测施以更重的惩罚。

从更专业的角度来说,线性回归的本质是最小化这个损失函数的过程。一开始我们的w和b是随机初始化的,直线位置和形状都很离谱,损失很大;然后模型根据某种策略不断调整w和b,让直线逐渐逼近数据,损失越来越小,最后收敛到一个比较理想的状态。

1.2 两种常见解法:最小二乘法与梯度下降法

在实现上,线性回归有两条典型路线:

最小二乘法是解析解法,直接利用数学推导,求出损失函数对w和b的偏导数,令它们等于0,就可以直接解出最优参数。好处是不用设置学习率、不用迭代,一次算出结果;坏处是当特征维度特别多、数据量特别大时,矩阵求逆的计算成本非常高昂,而且某些情况下矩阵不可逆导致无法求解。

梯度下降法则是迭代式的优化方法。它每次计算损失函数对参数的梯度(也就是当前最陡峭的上升方向),然后沿着反方向迈出一小步,不断重复这个过程。学习率就是这一步的大小,太大会震荡甚至发散,太小会龟速收敛。梯度下降适用于绝大多数实际问题,尤其是深度学习场景,也是理解后续更复杂模型的基础。

我的建议是,学习阶段两条路都亲手实现一遍。先用最小二乘法验证结果的正确性,再用梯度下降法体会迭代优化的过程,对后续学习逻辑回归、神经网络非常有帮助。很多人在这一步贪快,直接调用sklearn,结果后面看损失函数、调学习率的时候一脸懵。

1.3 代码结构规划:从数据到评估的完整闭环

这篇博文里的代码,我会按照一个标准机器学习的完整流程来组织:数据准备、模型定义、模型训练、结果评估、可视化分析。每一步都提供可以直接复制运行的代码块,并且标注清楚参数的含义和选择的原因。

强烈建议你动手敲一遍代码,而不是复制粘贴后直接运行。因为训练模型的过程中会出现各种有意思的现象——比如损失值在某个区间突然飙升,或者斜率迭代到一半方向反了——这些都需要亲手操作才能真的理解。我也是在各种实验和报错中,才逐渐把线性回归的几个关键问题想明白的。

2. 核心细节解析与代码实现

2.1 数据准备与可视化观察

写回归代码的第一步不是建模,而是生成或获取数据,并先画图看一看。我习惯用下面这组带噪声的线性数据来演示。这里我们用NumPy生成100个样本点,真实的函数关系是 y = 4x + 3,然后人为加上一些服从正态分布的随机噪声,用来模拟现实世界中数据不完美的情况。

import numpy as np import matplotlib.pyplot as plt np.random.seed(42) # 固定随机种子,保证结果可复现 X = np.linspace(0, 10, 100) true_w, true_b = 4.0, 3.0 noise = np.random.normal(0, 1, size=X.shape) y = true_w * X + true_b + noise plt.scatter(X, y, alpha=0.6, label='样本数据') plt.plot(X, true_w * X + true_b, color='red', linestyle='--', label='真实关系') plt.xlabel('X') plt.ylabel('y') plt.title('带噪声的线性数据') plt.legend() plt.show()

这里可能有人会问,为什么要设随机种子?因为如果不设,每次运行生成的噪声都不一样,数据点分布不同,会导致训练结果有细微差别。固定种子后,大家复现实验时能得到完全一致的结果,这在科研和工程协作里是基本素养。

画图这一步极其重要。先看数据分布是否呈现线性趋势,有没有明显的离群点,梯度下降对离群点非常敏感——一个极端值就能把整条线拉偏。这个习惯请务必养成,我见过太多人上来就写模型,最后模型效果差还不知道问题出在哪,其实第一步可视化就已经暴露了数据质量问题。

2.2 通过最小二乘法求解参数

上文提到的解析解,具体展开就是把 y = wx + b 代入均方误差损失函数,然后对 w 和 b 分别求偏导令其为0。对于一元线性回归,最终可以推导出两个公式:

w = Σ((xi - x̄)(yi - ȳ)) / Σ((xi - x̄)²)

b = ȳ - w * x̄

其中 x̄ 是x的均值,ȳ 是y的均值。推导过程教科书里写得很详细,这里就不抄公式了,直接看代码:

x_mean = np.mean(X) y_mean = np.mean(y) # 计算斜率 w numerator = np.sum((X - x_mean) * (y - y_mean)) denominator = np.sum((X - x_mean) ** 2) w_lsm = numerator / denominator # 计算截距 b b_lsm = y_mean - w_lsm * x_mean print(f"最小二乘法求解:w = {w_lsm:.4f}, b = {b_lsm:.4f}") print(f"预设真实值:w = {true_w}, b = {true_b}")

运行这段代码,你会发现求解出的 w 大约在 3.9 左右,b 大约在 3.2 左右,与真实值非常接近,但因为噪声的存在,它们并不会完全等于 4 和 3。这正是模型学习能力的体现——它提取出了数据中的主要趋势,但无法完全还原被噪声污染前的真相。

最小二乘法的优势是直接、快捷,不需要调参。在数据量不大的教学场景中,我建议先用它算出结果,作为后续梯度下降法结果正确性的参照基准。如果你算出来的梯度下降最终结果和最小二乘法差异特别大,那说明训练过程出了问题,可以及时排查。

2.3 通过梯度下降法训练模型

梯度下降需要我们自己定义损失函数、梯度计算和参数更新逻辑。这里我使用向量化写法,尽量减少for循环,因为NumPy的底层是C语言实现,向量化运算比Python循环效率高出一个量级。向量化也是从“能跑”到“跑得快”的一条重要分水岭。

def compute_loss(w, b, X, y): n = len(X) y_pred = w * X + b loss = np.mean((y - y_pred) ** 2) return loss def gradient_descent(X, y, w_init, b_init, lr=0.01, epochs=1000): w = w_init b = b_init n = len(X) losses = [] for epoch in range(epochs): y_pred = w * X + b # 计算梯度 dw = (-2 / n) * np.sum(X * (y - y_pred)) db = (-2 / n) * np.sum(y - y_pred) # 更新参数 w -= lr * dw b -= lr * db # 记录损失 loss = compute_loss(w, b, X, y) losses.append(loss) if epoch % 100 == 0: print(f"Epoch {epoch}, Loss = {loss:.6f}") return w, b, losses

有几个细节值得展开讲。第一是 dw 和 db 的表达式中都有一个 -2/n 的系数,这个负号就是让参数向损失减小的方向移动,2/n 来自平方误差求导的系数。第二是学习率 lr,设置得越小收敛越慢,设置得太大则可能直接发散,这个值的“感觉”需要在实验中找。第三是epochs,也就是训练轮数,太少了没收敛,太多了浪费时间甚至过拟合。

说了这么多,直接运行看看效果:

w_init, b_init = 0.0, 0.0 w_gd, b_gd, losses = gradient_descent(X, y, w_init, b_init, lr=0.01, epochs=1000) print(f"梯度下降求解:w = {w_gd:.4f}, b = {b_gd:.4f}") print(f"最小二乘法结果:w = {w_lsm:.4f}, b = {b_lsm:.4f}")

当epochs跑到1000,理论上梯度下降的结果会非常接近最小二乘法的结果。我实测时两者的w差距通常在0.01以内。如果差距大,可以尝试增大epochs或者调小学习率。

2.4 损失函数的收敛趋势可视化

训练过程记录下来的losses列表非常有价值。把它画成曲线,你能直观看到模型是如何一步步逼近最优解的。这个可视化对理解梯度下降的动态过程帮助非常大。

plt.figure(figsize=(10, 5)) plt.plot(range(len(losses)), losses) plt.xlabel('Epoch') plt.ylabel('MSE Loss') plt.title('训练过程中的损失下降曲线') plt.grid(True, alpha=0.3) plt.show()

一个正常收敛的训练过程,损失曲线应该是刚开始剧烈下降,然后逐渐趋于平缓,最后变成一条几乎水平的线。这就是“收敛”的直观含义。如果你看到损失曲线上升或者剧烈震荡,那几乎可以断定是学习率设置得不合理。学习率大了,参数每次更新跨越太大,可能直接跳过最优点,在附近来回振荡;学习率小了,损失下降得慢,曲线看起来像乌龟爬,虽然最后也能到,就是浪费时间。

这个曲线还可以辅助判断训练是否充分。曲线已经变平,说明继续训练收益不大了;曲线还在明显下降,说明模型尚未收敛,需要增加训练轮数或调整学习率。

2.5 引入sklearn做对照验证

有人可能会说,我直接用sklearn一行搞定不香吗?确实香,但在理解原理之后用,才是如虎添翼。我们来看标准实现:

from sklearn.linear_model import LinearRegression X_reshaped = X.reshape(-1, 1) # sklearn要求二维数组 model = LinearRegression() model.fit(X_reshaped, y) w_sk = model.coef_[0] b_sk = model.intercept_ print(f"sklearn求解:w = {w_sk:.4f}, b = {b_sk:.4f}")

这里有个新手高频踩坑点:sklearn的fit()方法要求特征矩阵是二维的,哪怕你只有一个特征,也得是(n, 1)的形状,不能直接塞一维数组进去。我之前就见过不少人在这里报ValueError,然后又跑去百度半天。

把三组结果放在一起,你会发现最小二乘法、梯度下降法和sklearn的计算结果高度一致。这样一套流程下来,既理解了原理,又验证了实现,还能顺便学会调库,一举三得。

3. 实操过程与完整项目演练

3.1 完整代码串联:一份可直接运行的脚本

上面各个部分都是零散的,我把它拼成一份完整的独立脚本,你可以保存为.py文件,或者直接贴在Jupyter Notebook里运行。为了方便阅读,我在关键行加了中文注释。

import numpy as np import matplotlib.pyplot as plt from sklearn.linear_model import LinearRegression # ---------- 1. 数据准备 ---------- np.random.seed(42) X = np.linspace(0, 10, 100) true_w, true_b = 4.0, 3.0 noise = np.random.normal(0, 1, size=X.shape) y = true_w * X + true_b + noise # ---------- 2. 最小二乘法 ---------- x_mean, y_mean = np.mean(X), np.mean(y) w_lsm = np.sum((X - x_mean) * (y - y_mean)) / np.sum((X - x_mean) ** 2) b_lsm = y_mean - w_lsm * x_mean print(f"最小二乘法: w={w_lsm:.4f}, b={b_lsm:.4f}") # ---------- 3. 梯度下降法 ---------- def compute_loss(w, b, X, y): y_pred = w * X + b return np.mean((y - y_pred) ** 2) def gradient_descent(X, y, w_init, b_init, lr=0.01, epochs=1000): w, b = w_init, b_init n = len(X) losses = [] for epoch in range(epochs): y_pred = w * X + b dw = (-2 / n) * np.sum(X * (y - y_pred)) db = (-2 / n) * np.sum(y - y_pred) w -= lr * dw b -= lr * db losses.append(compute_loss(w, b, X, y)) return w, b, losses w_gd, b_gd, losses = gradient_descent(X, y, 0.0, 0.0, lr=0.01, epochs=1000) print(f"梯度下降: w={w_gd:.4f}, b={b_gd:.4f}") # ---------- 4. sklearn对照 ---------- model = LinearRegression() model.fit(X.reshape(-1, 1), y) print(f"sklearn: w={model.coef_[0]:.4f}, b={model.intercept_:.4f}") # ---------- 5. 可视化 ---------- plt.figure(figsize=(12, 4)) plt.subplot(1, 2, 1) plt.scatter(X, y, alpha=0.6, label='样本数据') plt.plot(X, w_gd * X + b_gd, color='red', label='拟合直线') plt.xlabel('X'); plt.ylabel('y') plt.title('数据与拟合直线') plt.legend() plt.subplot(1, 2, 2) plt.plot(range(len(losses)), losses, color='green') plt.xlabel('Epoch'); plt.ylabel('MSE Loss') plt.title('损失下降曲线') plt.grid(True, alpha=0.3) plt.tight_layout() plt.show()

整个脚本的运行流程是:生成数据 → 最小二乘法快速求解 → 梯度下降迭代训练 → 调库验证 → 可视化观察。这是一个规范的最小机器学习项目结构,以后学更多模型时,也可以沿用这个骨架,只替换模型定义和损失函数部分。

3.2 调整学习率:一场关于“步长”的实操观察

学习率这个东西,光看公式是体会不深的,必须亲手试一试。我建议你做一组对照实验:把代码里的学习率分别设为 0.001、0.01、0.1、0.5,固定epochs为50,然后观察损失曲线的形态差异。

根据我的实操经验:

  • lr=0.001:损失下降非常缓慢,50轮后损失还没降到位,曲线像一个缓坡,需要更大的epochs才能收敛。
  • lr=0.01:下降速度适中,曲线呈漂亮的指数衰减形态,大约300轮后基本平稳。
  • lr=0.1:下降非常快,但曲线末端可能出现细微震荡,说明步长开始触碰最优解附近但是停不稳。
  • lr=0.5:几乎必然发散,损失值疯狂飙升,甚至变成NaN。

为什么会出现发散?我们可以从数学上粗略理解。梯度下降的更新公式是 w_new = w_old - lr * dw。如果lr* dw太大,更新后的参数一下子跳过最优点,跑到了对面的半山腰,而且位置比原来还高——损失反而变大了。下一次更新又跳过另一侧,如此反复,参数就在山谷上方越弹越高,最终发散。

这个观察告诉大家,学习率真的需要调试。平时我调模型,一般先用0.01试跑,观察损失曲线形态,再决定放大还是缩小。这个“从小到大试探”的策略,在很多深度学习框架里也适用。

3.3 参数初始化带来的影响

梯度下降需要给定初始参数。常规做法是全部初始化为0,但也可以随机初始化。这引出一个问题:初始参数会影响最终收敛结果吗?

对于线性回归这种凸优化问题,答案是不影响。因为损失函数是一个凸函数,只有一个全局最小值,无论从哪里出发,最终都会走向同一个谷底。这也是线性回归相对“简单”的原因之一——不用太担心收敛到局部最优。但如果你以后学神经网络,损失函数是非凸的,初始化的位置就会影响最终落到哪个局部最优点了。

不过,初始化会影响“到达”的速度。比如你把w初始化为100,b初始化为-50,损失函数值一开始巨大,梯度也很大,前期迭代会比较猛,但总体还是能回到正确位置的。倒是这种极端初始化在数值计算上偶尔会产生溢出问题,所以平时还是建议老老实实用0或小随机数初始化。

4. 模型评估与可视化分析

4.1 评估指标:从MSE到R²

训练完模型,下一步是回答那个灵魂问题:“你这个模型到底行不行?”光靠肉眼看拟合直线贴不贴近数据点,太主观了,我们需要量化指标。

第一个指标是均方误差MSE,我们在训练过程中已经见过。计算公式是预测值和真实值差的平方求平均。它的量纲是目标变量y的量纲平方,所以解释性稍差。比如y是房价(单位万元),那MSE的单位就是万元的平方,听着很别扭。

第二个指标是均方根误差RMSE,就是MSE开根号。它的量纲和y一致,更好解释。第三个是平均绝对误差MAE,取绝对值再平均,对离群点没那么敏感。第四个是决定系数R²,取值在0到1之间,表示模型解释了目标变量百分之多少的方差。R²越接近1,模型效果越好;R²接近0说明模型几乎没什么预测力;如果R²为负,通常意味着模型比直接用均值预测还差。

直接看代码怎么写:

from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score y_pred = w_gd * X + b_gd mse = mean_squared_error(y, y_pred) rmse = np.sqrt(mse) mae = mean_absolute_error(y, y_pred) r2 = r2_score(y, y_pred) print(f"MSE: {mse:.4f}") print(f"RMSE: {rmse:.4f}") print(f"MAE: {mae:.4f}") print(f"R²: {r2:.4f}")

我的实测结果大致是MSE在1附近,RMSE在1附近,R²在0.96以上。因为生成数据时噪声的标准差就是1,所以MSE接近1在直觉上是完全合理的。如果你构造的噪声标准差是5,那么MSE会显著变大,但R²依然能hold住,因为它是一个相对指标,衡量的是模型相对数据内在波动性的表现。

在写项目报告时,我一般会同时报告R²和RMSE:R²给出模型整体的解释力,RMSE给出预测误差的真实量纲。通常还会附上预测值vs真实值的散点图,让读者一目了然。

4.2 残差分析:发现模型的“隐藏问题”

比指标更有说服力的是残差分析。所谓残差就是真实值减预测值(y - y_pred)。很多人训练完模型只看指标就收工了,但残差图能告诉你更多信息。基本操作如下:

residuals = y - y_pred plt.figure(figsize=(12, 4)) plt.subplot(1, 2, 1) plt.scatter(X, residuals, alpha=0.6) plt.axhline(y=0, color='red', linestyle='--') plt.xlabel('X') plt.ylabel('残差') plt.title('残差分布图') plt.grid(True, alpha=0.3) plt.subplot(1, 2, 2) plt.hist(residuals, bins=20, edgecolor='white') plt.xlabel('残差') plt.ylabel('频数') plt.title('残差直方图') plt.grid(True, alpha=0.3) plt.tight_layout() plt.show()

一份“健康”的残差图应该满足三个条件:残差在0附近随机分布,没有明显的曲线或漏斗形状;残差幅度不随X变化而变化;残差大致呈正态分布(直方图中间高两边低)。如果残差图呈现出明显的规律,比如呈现出U形曲线,说明数据关系可能不是线性的,应该考虑多项式回归;如果残差幅度随X增大而增大,呈扇形展开,说明数据存在异方差性,需要做数据变换或使用加权回归。

数据清洗阶段你漏检的一个离群点,在这里也会现出原形——残差图上会出现一个明显远离0的点。我的习惯是每次训练完模型,都先把残差图打出来看一眼,比自己对着指标琢磨半天有效得多。

4.3 预测与真实值对比图

最后一个辅助判断方式是预测值对比图。以真实值y为横轴、预测值 y_pred 为纵轴画散点图。如果模型完美,所有点会落在 y=x 这条对角线上。实际情况下点会在对角线附近散布。散布越窄,模型越好。这个图特别适合向非技术背景的合作方展示模型效果,比一堆数字直观得多。

plt.figure(figsize=(6, 6)) plt.scatter(y, y_pred, alpha=0.6) plt.plot([y.min(), y.max()], [y.min(), y.max()], color='red', linestyle='--') plt.xlabel('真实值') plt.ylabel('预测值') plt.title('预测值与真实值对比') plt.grid(True, alpha=0.3) plt.show()

5. 常见问题与排查技巧实录

5.1 损失函数发散为NaN,怎么处理?

这是我见过最多的问题。loss变成了 NaN 通常是学习率过大的典型症状,参数更新步长太大,数值直接溢出。排查路径很简单:先把学习率大幅调小,比如从0.01改成0.001,如果还发散,就检查数据是不是存在极端值。某些异常巨大的离群点会导致梯度爆炸,同样会引发NaN。更隐蔽的一个原因是数据特征尺度差异特别大,比如一个特征取值在0~1,另一个在0~100000,量纲差异会导致梯度方向被大尺度特征主导,模型训练不稳定。解决办法是特征标准化,把数据缩放到均值为0、方差为1的分布。

5.2 梯度下降结果和最小二乘法差距很大,哪一步出了问题?

这种二次验证法是我特别推荐的做法。两个方法理论上应该给出几乎一致的结果,如果差距很大,排查顺序如下:

第一,检查梯度公式是否正确。手动算一个简单数据点的梯度,比对代码输出,或者打印中间过程的 dw 和 db 看看数值是否合理。第二,检查是否忘记除以样本数n,这会导致梯度被放大了n倍,看起来好像收敛了,但最终结果会偏。第三,检查epochs是否太少,训练未充分收敛。第四,检查学习率是否太大,导致在最优解附近震荡无法稳定。

现在我基本先跑最小二乘法做基准,再用梯度下降去对齐,一旦两者结果吻合,代码正确性就八九不离十了。

5.3 sklearn的fit报错ValueError: Expected 2D array,怎么解决?

这个问题在前文提过,这里详细说一下。fit()方法要求特征矩阵是二维的,也就是形状是(n_samples, n_features),即使只有一个特征也要保持这个结构。解决办法是X.reshape(-1, 1),-1表示自动推断样本数量,1表示1个特征维度。另一种办法是X[:, np.newaxis],效果相同。如果特征维度本来就该是一维的,比如时间序列数据,则必须先reshape再传入。这个坑虽然小,但新手遇到时会很头疼。

5.4 如何选择合适的学习率和迭代次数?

我给一个比较实用的“土办法”。先用一个较大的学习率(如0.1),跑100轮,看损失曲线。如果发散,就把学习率除以10再试;如果收敛得很平滑但还没降到最低,就增加epochs。反复几次之后,你就能找到一个合适的量级。深度学习框架如PyTorch里还有学习率调度器,可以在训练过程中动态调整学习率,先大后小,兼顾收敛速度与精度。

5.5 特征缩放的重要性

当只有一个特征X时,缩放与否影响不大。但如果以后处理多特征线性回归,特征缩放就非常重要了。比如一个特征是房屋面积(数值在几十到几百),另一个特征是房龄(数值在0到30),两个特征的单位和尺度不同。如果不做缩放,梯度下降在未缩放的特征上收敛极慢。最常用的缩放方法是标准化:z = (x - mean) / std。sklearn里就是StandardScaler。这一步做与不做,训练速度能差一个数量级。

6. 梯度下降的进阶理解与后续扩展

6.1 为什么梯度方向是上升方向,而我们却在做下降?

数学上,梯度是函数在某点处方向导数最大的方向,也就是说沿着梯度方向走,函数值增长最快。我们要最小化损失函数,自然要反着来——沿着负梯度方向走。这就是为什么更新公式里参数要减去学习率乘以梯度。这个直觉很重要。很多初学者第一次看梯度下降,容易绕进“为什么减去梯度”的弯里,理解这一点之后就豁然开朗了。

6.2 批量梯度下降、随机梯度下降与小批量梯度下降

前面实现的是批量梯度下降,每一轮计算梯度时使用全部样本。当数据量很大时,每一轮的计算成本太高。随机梯度下降(SGD)则每次随机取一个样本计算梯度并更新参数,速度快,但梯度方向噪声大,收敛路径曲折。小批量梯度下降(Mini-batch GD)是两者折中,每次取一小批样本(如32、64、128个)计算梯度,既平滑又高效,是深度学习训练中的事实标准。

6.3 从一元走向多元线性回归

一元线性回归理解了,多元线性回归的迁移成本极低。把 y = wx + b 改成 y = w1x1 + w2x2 + ... + wnxn + b 即可。代码上用矩阵运算一次性更新所有参数,核心逻辑完全一致。以后学多项式回归时也只需要增加特征的幂次组合,学正则化时在损失函数后面加惩罚项,这些都是线性回归这根树干上长出来的分支。

7. 实操过程中的心得与建议

7.1 我的几个亲测有效的调试习惯

第一,无论如何先把损失曲线画出来,这是模型训练状态的晴雨表。第二,和成熟库(sklearn)的结果做对照,不盲目相信自己的实现。第三,修改任何参数时一次只改一个变量,否则出了问题无法定位。第四,刚开始调试时使用小数据集,比如只有20个样本,训练速度快,发现问题也快。

7.2 尽量避免的几件事

不要一上来就调库。先手写一遍梯度下降,才能真正理解优化过程。不要忽略数据可视化,只看数字指标容易遗漏数据质量问题。不要追求一次到位,调参本来就是迭代的过程。最后,不要照抄代码,自己把每一行打出来并理解它。

7.3 我踩过的那些坑

回想自己初学线性回归时踩的坑,最典型的是梯度公式忘除以n,导致损失虽然也在下降但最终参数偏差很大。后来反复对照最小二乘法的结果才定位到这个问题。还有一次是把学习率设为强大的1.0,损失曲线直接飞升到溢出,那种“哇一声叫出来”的经历到现在都记得。这些坑,写出来就是希望大家少走弯路。

回归这块内容虽然基础,但真的是后续一切机器学习模型的地基。把线性回归的代码、原理、调参与评估这套流程吃透,后面学什么都会有底气得多。动手把代码跑起来,你会发现自己对机器学习的理解会上一个台阶。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询