算法可视化与交互学习平台
线性模型中的梯度下降Gradient Descent for a Linear Model
用最小线性回归例子理解 loss、gradient、参数更新,并通过实时调参观察训练曲线与拟合效果。
目标理解
这个模块聚焦三个关键词:loss、gradient、参数更新。我们从最小线性模型 y = wx + b 出发,用梯度下降训练参数,并观察损失如何一步步下降。
交互直线:y = wx + b
单独调节斜率 w 和截距 b,观察直线如何围绕坐标轴旋转和上下平移。左侧改参数,右侧图会实时变化。
模型与损失
线性模型用参数 w、b 生成预测值,再用均方误差衡量预测与真实值之间的差异。
梯度下降更新
梯度告诉我们 loss 增长最快的方向,因此更新时要沿负梯度方向移动。学习率 η 控制每一步走多远。
公式 + 代码 解释
1. 我们到底在做什么?
目标很简单:用一条直线去拟合一组数据。模型写成 y = wx + b,其中 w 是斜率,b 是截距。
在代码里,这一步就是:
preds = w * xs + b这里的 w 和 b 是要学习的参数。一开始它们通常只是随便给一个初值,所以预测往往并不准确。
2. 怎样衡量预测得好不好?
我们用损失函数 loss 衡量“模型错了多少”。这个例子使用的是均方误差:
对应代码:
errors = preds - ys
loss = np.mean(errors * errors)含义是:先计算每个样本的误差,再把误差平方后取平均。
loss大,说明模型还差得远。loss小,说明预测已经更接近真实数据。
3. 怎样知道参数该往哪边改?
核心问题不是“要不要改参数”,而是“w 和 b 应该往哪个方向改,才能让 loss 变小”。答案就是看梯度。
对 w 求导,可得到:
代码对应:
dw = np.mean(2.0 * errors * xs)对 b 求导,可得到:
代码对应:
db = np.mean(2.0 * errors)可以把它们理解成:
dw:如果改变w,loss会怎么变。db:如果改变b,loss会怎么变。
误差越大,梯度通常也会越大;而 x 的取值越大,对 w 的影响也会更明显。
4. 梯度下降到底在“下降”什么?
梯度指向的是 loss 增长最快的方向,所以如果我们想让 loss 下降,就要沿着梯度的反方向更新参数:
代码就是:
w = w - lr * dw
b = b - lr * dblr就是学习率,也就是公式里的 \eta。- 减号表示我们要往“下坡方向”走,而不是往上坡走。
5. 整个训练过程其实就是重复同一件事
训练并不神秘,本质上就是在循环里不断重复四步:
- 先预测:
preds = w * xs + b - 再计算误差和损失:
errors、loss - 然后计算梯度:
dw、db - 最后更新参数:
w、b
对应代码中的训练循环:
for epoch in range(num_epochs):
preds = w * xs + b
errors = preds - ys
loss = np.mean(errors * errors)
dw = np.mean(2.0 * errors * xs)
db = np.mean(2.0 * errors)
w = w - lr * dw
b = b - lr * db同时程序会把每一轮的损失记录下来:
loss_history.append(...)6. 图表里应该重点看什么?
实验台里最值得观察的是 loss_history 对应的曲线。它展示的是:随着训练一轮轮进行,损失是如何变化的。
理想情况下,你会看到这样的趋势:
- 开始时
loss较大。 - 前几轮下降较快。
- 后面逐渐趋于稳定。
这说明参数正在逐步逼近更合适的值,拟合直线也在不断贴近数据点。
7. 一句话串起来
我们先用 loss 衡量“错多少”,再用梯度判断“往哪改”,然后按学习率控制步长,一步步更新参数,让误差持续减小,最后得到更合适的直线。
为什么“碗形”的是损失,不是模型
1. 先纠正一个常见误区
在线性模型里,\hat{y} = wx + b 本身是一条直线,不是碗形。
真正呈现“碗形”的,是损失函数。例如单个样本下:
这里的 L 是关于参数的函数。只要把某个样本的 x、y 和当前的 b 固定下来,它就会变成一个关于 w 的二次函数。
2. 为什么它一定像碗?
因为它本质上是“线性函数再平方”:
线性 + 平方 = 二次函数
比如固定 x = 2、b = 1、y = 5,那么:
展开后可以直接看到最高次项是 4w^2,系数为正,所以曲线一定开口向上,也就是碗形。
3. 梯度和“往哪边走”有什么关系?
对上面的损失函数求导:
- 当 w < 2 时,梯度为负,说明继续增大
w会让损失下降。 - 当 w > 2 时,梯度为正,说明应该减小
w。 - 当 w = 2 时,梯度为 0,正好落在最低点。
这就是梯度下降能工作的原因:不是模型本身是碗,而是平方误差把优化目标变成了碗,梯度就能稳定地把参数往最低点推过去。
4. 和更一般的线性回归怎么对应?
上面的例子只是把问题简化成“只看一个样本、只看一个参数”。回到完整线性回归时,均方误差仍然是关于参数的凸函数,所以整体思路不变:
- 先看当前参数下损失有多大。
- 再看梯度的方向和大小。
- 沿负梯度方向更新参数,让损失继续下降。
所以要记住的不是“y = wx + b 是碗”,而是“误差平方把损失函数变成了碗”。
固定样本下的损失曲线
import numpy as np
x = 2.0
b = 1.0
y = 5.0
ws = np.linspace(-1, 5, 121)
losses = (x * ws + b - y) ** 2
gradients = 2 * (x * ws + b - y) * x
w_star = 2.0
loss_star = 0.0
plot_data = {
'data': [
{
'x': ws.tolist(),
'y': losses.tolist(),
'type': 'scatter',
'mode': 'lines',
'name': 'L(w) = (2w - 4)^2',
'line': {'color': '#0f766e', 'width': 3}
},
{
'x': [w_star],
'y': [loss_star],
'type': 'scatter',
'mode': 'markers+text',
'name': 'minimum',
'text': ['grad = 0'],
'textposition': 'top center',
'marker': {'size': 12, 'color': '#f59e0b'}
},
{
'x': ws[::20].tolist(),
'y': losses[::20].tolist(),
'type': 'scatter',
'mode': 'markers',
'name': 'sample points',
'marker': {
'size': [max(8, min(18, abs(g) * 0.6)) for g in gradients[::20]],
'color': gradients[::20].tolist(),
'colorscale': 'RdBu',
'showscale': True,
'colorbar': {'title': {'text': 'gradient'}}
}
}
],
'layout': {
'title': {'text': 'Loss over w for one sample: x=2, b=1, y=5'},
'xaxis': {'title': {'text': 'w'}},
'yaxis': {'title': {'text': 'L(w)'}},
'annotations': [
{
'x': 0.5,
'y': 9,
'text': 'gradient < 0, increase w',
'showarrow': True,
'ax': -60,
'ay': -30
},
{
'x': 3.5,
'y': 9,
'text': 'gradient > 0, decrease w',
'showarrow': True,
'ax': 60,
'ay': -30
}
]
}
}
print('For x=2, b=1, y=5:')
print('L(w) = (2w - 4)^2')
print('dL/dw = 8w - 16')
print('minimum at w = 2')为什么叫“梯度”
1. 从“斜率”开始理解
在一元函数里,比如 y = f(x),我们用导数 \frac{dy}{dx} 描述“x 变化一点,y 会变化多少”。它本质上就是斜率。
所以在一维里,可以把导数理解成“当前这条曲线有多陡,以及是向上还是向下”。
2. 多个变量时,一个斜率就不够了
在线性回归里,损失不是只依赖一个变量,而是依赖参数 w 和 b:
这时我们不能只问“整体斜率是多少”,而要分别问:
- 沿着
w方向走一点,L怎么变? - 沿着
b方向走一点,L怎么变?
于是就会得到两个偏导数:
它们分别表示每个方向上的“局部斜率”。
3. 梯度就是“所有方向的斜率集合”
把这些方向上的斜率放在一起,就得到梯度:
这时它不再是一个单独的数,而是一个向量。你可以把它理解成:
- 每个分量告诉你某个方向有多陡。
- 整个向量一起告诉你:在当前点,函数往哪里上升最快。
4. 为什么叫“梯度”
“梯度”这个词本来就表示“变化的快慢和方向”。比如温度梯度表示哪里升温最快,海拔梯度表示哪里上升最快。
放到优化问题里,梯度描述的是:
函数在当前点增长最快的方向,以及这个增长有多快。
5. 为什么梯度下降要减去梯度
如果把损失函数想成一座山:
- 山的高度就是 loss。
- 你当前所在的位置就是参数点 (w, b)。
那么梯度告诉你的其实是:“往哪个方向走,会上坡最快”。但训练的目标不是上山,而是下山,所以更新时要沿着反方向走:
这里的减号就是关键:梯度指向最陡上坡,而我们要走最陡下坡。
6. 一句话把三个概念打通
| 概念 | 本质 |
|---|---|
| 导数 | 一维情况下的斜率 |
| 偏导 | 某一个方向上的斜率 |
| 梯度 | 所有方向斜率组成的向量 |
所以最值得记住的一句话是:
梯度之所以叫“梯度”,是因为它描述的是多维空间里函数变化最快的方向和速度。
实验台
调整学习率、训练轮数和初始参数,观察 loss 下降、参数收敛以及拟合直线如何变化。