查看全课大纲(第 18 / 82 节)
  1. 1.函数与数据泛化
  2. 2.向量:从几何直觉到高维空间
  3. 3.矩阵与张量
  4. 4.特征值与特征向量:矩阵变换的不动方向
  5. 5.凸函数:从向量求导的矩阵记号到好优化的函数形状
  6. 6.概率与统计:机器学习的数学基础
  7. 7.独立性与随机变量:贝叶斯公式与二项分布
  8. 8.期望、方差与协方差:随机变量的数字画像
  9. 9.联合概率:当一条轴不够用的时候
  10. 10.概率分布(一):从伯努利到伽马函数
  11. 11.概率分布(二):贝塔、泊松、正态与两个贝叶斯故事
  12. 12.机器学习的模型:找输入与输出的关系
  13. 13.机器学习的应用
  14. 14.过拟合
  15. 15.机器学习的分类
  16. 16.有监督学习:从房价预测说起
  17. 17.损失函数
  18. 18.梯度下降(上):更新规则与学习速率
  19. 19.梯度下降(2)
  20. 20.牛顿方法
  21. 21.KNN简介
  22. 22.距离测算
  23. 23.朴素贝叶斯
  24. 24.朴素贝叶斯的实际应用
  25. 25.信息熵:决策树构建的度量基础
  26. 26.信息增益与决策树的构建
  27. 27.ID3算法
  28. 28.决策树节点的划分
  29. 29.CART树
  30. 30.Bagging
  31. 31.Boosting:自适应提升算法
  32. 32.Gradient Boosting
  33. 33.线性模型:回归与分类的同一副骨架
  34. 34.最小二乘:把最优参数直接解出来
  35. 35.最小二乘法的几何与概率视角
  36. 36.极大似然:给最小二乘一个概率出身
  37. 37.逻辑回归(1):把线性模型接到分类上
  38. 38.逻辑回归(2):损失函数与多分类
  39. 39.---- 手造数据: 5 个样本, 2 个特征; x2 几乎 = 2*x1 (偏差手工指定, 无随机) ----
  40. 40.讲师例: 向量 "23014" -- 三把范数尺子量同一个向量
  41. 41.优化方法(1):坐标下降与前向选择
  42. 42.优化方法(2):前向策略与最小角回归
  43. 43.拉格朗日乘子法(1):从无约束到等式约束
  44. 44.拉格朗日乘子法(2):等式约束下的平行条件与拉格朗日函数
  45. 45.拉格朗日乘子法(3):不等式约束与 KKT 条件
  46. 46.最大间隔:把分类问题变成优化问题
  47. 47.拉格朗日对偶问题:把 w 和 b 全部换成 α
  48. 48.支持向量:边界上的少数派
  49. 49.SVM 计算过程:一笔到底算完,再走进核方法
  50. 50.贝叶斯公式:概率即信念
  51. 51.隐含变量和概率模型:给看不见的原因建模
  52. 52.EM 算法:期望与最大化的迭代
  53. 53.琴生不等式:EM 的数学钥匙
  54. 54.三个硬币问题:EM 的复盘与衔接
  55. 55.高斯混合模型:用隐变量解释双峰数据
  56. 56.隐马尔可夫模型
  57. 57.隐马尔可夫模型(2):前向算法与动态混合模型
  58. 58.隐马尔可夫模型在 NLP 中的运用
  59. 59.EM 算法:把「为什么收敛」推到底
  60. 60.HMM 在 NLP 中的运用:从分词到机器翻译
  61. 61.HMM 参数估计:Baum-Welch 的三块拉格朗日
  62. 62.高维下的高斯模型
  63. 63.主题模型:从词袋到隐含主题
  64. 64.Dirichlet 分布(1)
  65. 65.Dirichlet 分布(2)
  66. 66.统计学习内容回顾
  67. 67.K-means 算法
  68. 68.PCA 降维
  69. 69.SVD 降维
  70. 70.从聚类到流形
  71. 71.局部线性嵌入
  72. 72.随机邻域嵌入:SNE 与 t-SNE
  73. 73.无参模型:让数据自己说话
  74. 74.高斯过程:函数空间里的正态分布
  75. 75.高斯过程(2):噪声、正则化与核的线性组合
  76. 76.神经网络简介:从感知器到自编码器
  77. 77.反向传播:把误差一层层传回去
  78. 78.卷积神经网络(CNN):让特征自己学出来
  79. 79.循环神经网络(RNN):给网络装上记忆
  80. 80.深度学习回顾:静态一类,动态一类
  81. 81.增强学习(1):蒙特卡洛、树搜索与 Q-learning
  82. 82.强化学习(2):从乌鸦吃核桃到井字棋的 value 回传

梯度下降(上):更新规则与学习速率

约 27 分钟

📺 正在播放小象官方高清录播(支持倍速与清晰度调节)

小象实战讲义 · 机器学习算法精讲

前面几节我们为监督学习定义了假设函数与代价函数:一条直线拟合得好不好,交给代价函数 J(θ)J(\theta) 打分。本节解决紧随而来的问题——怎么找到让 J(θ)J(\theta) 取最小值的参数?答案是梯度下降(Gradient Descent)。学完本节,你可以用几十行纯 Python 亲手把它跑起来,并真正理解”学习速率”这件事为什么重要。

💡 核心导读

  • 梯度下降的全部核心,是一行更新规则:θ←θ−α⋅∂J∂θ\theta \leftarrow \theta - \alpha \cdot \frac{\partial J}{\partial \theta}
  • 它为什么有效:导数的正负决定移动方向,沿负梯度方向,代价一步一步变小
  • 学习速率 α\alpha 控制每步走多远:太大会来回震荡,太小会慢得离谱,取值靠经验、看问题特性
  • 参数扩到二维(θ0\theta_0、θ1\theta_1)后,各维度要用同一点上的偏导同时更新
  • 为什么一般不需要”自适应 α\alpha“:越接近最优点,梯度本身越小,步长会自动衰减

一、要解决的问题:给代价函数找最优参数

先把主角请出来——前面定义过的代价函数:

J(θ)=12N∑i=1N(hθ(x(i))−y(i))2J(\theta) = \frac{1}{2N}\sum_{i=1}^{N}\left(h_\theta(x^{(i)}) - y^{(i)}\right)^2

其中 hθ(x)h_\theta(x) 是假设函数(hypothesis),NN 是训练样本数。我们的目标很明确:调整参数 θ\theta(可能是一个,也可能是 θ0,θ1,…\theta_0, \theta_1, \ldots 一整组),让 J(θ)J(\theta) 达到最小。

求最优值不止一条路。以后学习线性回归的解析解法时会看到,有些问题可以直接解方程;但梯度下降走的是另一条路——迭代学习(iterative learning):先随便给参数一个初始值(相当于随手画一条线),再根据这条线与已知数据的误差,沿着让误差变小的方向不断调整,循环往复直到满意。这就好比在山区里找山谷:你不知道谷底在哪,但每一步都往”更低”的方向走。

二、更新规则:一行公式与两个记号问题

梯度下降的更新规则只有一行:

θ←θ−α⋅∂J(θ)∂θ\theta \leftarrow \theta - \alpha \cdot \frac{\partial J(\theta)}{\partial \theta}

先说清两件容易疑惑的事。

箭头是什么意思? ←\leftarrow 表示赋值:把右边算出来的值,赋给左边的 θ\theta,作为下一轮的起点。它不是”两边相等”的数学等式,而是”下一时刻的 θ\theta 由上一时刻算出来”的更新语句。

为什么明明只有一个 θ\theta,还写偏导 ∂\partial? 因为 θ\theta 通常是一组参数 θ0,θ1,…,θn\theta_0, \theta_1, \ldots, \theta_n,我们每次求的是 JJ 关于其中某一个分量(比如 θ3\theta_3)的偏导数;当 θ\theta 只是单个标量时,写成普通导数 dJdθ\frac{dJ}{d\theta} 就足够了。两种记号在这里指的是同一件事:求当前点的切线斜率。导数与偏导的几何背景,可以对照站内《人工智能数学基础》的相关章节补齐。

式中的 α\alpha 叫学习速率(learning rate),代表每次更新的步长,是一个大于 0 的数(取 0 就永远不动,没有意义)。至于 α\alpha 到底该取多大——这正是本节后面要重点讨论的问题。

三、一维直觉:为什么”减去梯度”就能走到最低点

拿讲师用的最简例子:hθ(x)=θxh_\theta(x) = \theta x,训练数据 (1,1)(1,1)、(2,2)(2,2)、(3,3)(3,3)。此时 J(θ)J(\theta) 是关于 θ\theta 的一条抛物线。

现在随便指定一个初始值 θ(0)\theta^{(0)},看看会发生什么:

  • 如果 θ(0)\theta^{(0)} 落在抛物线右半边:这一点的导数(切线斜率)是正数。代入更新式,就是 θ\theta 减去一个正数——θ\theta 向左移动。每移动一步再算一次导数,导数依然是正的,但越来越接近 0,于是 θ\theta 一步步逼近谷底,最终收敛到最优解 θ∗\theta^{*}。
  • 如果 θ(0)\theta^{(0)} 落在左半边:导数是负数,减去一个负数等于加上一个正数——θ\theta 向右移动,同样收敛。

也就是说,梯度下降不需要一次算出最优值,它靠”看导数符号、迈一小步”的循环逼近最优。对上面这个例子,J(θ)J(\theta) 的导数可以直接算出来:

dJ(θ)dθ=1N∑i=1N(θx(i)−y(i))x(i)\frac{dJ(\theta)}{d\theta} = \frac{1}{N}\sum_{i=1}^{N}\left(\theta x^{(i)} - y^{(i)}\right)x^{(i)}

把整件事写成纯 Python(只用标准库),从 θ=0\theta = 0 出发跑五轮:

# 梯度下降一维示例:h(x) = θ·x,拟合 (1,1) (2,2) (3,3)(讲师笔记同款数据)
X = [1.0, 2.0, 3.0]
Y = [1.0, 2.0, 3.0]

def cost(theta):
    """代价函数 J(θ) = (1/2N)·Σ(θ·x - y)^2"""
    return sum((theta * x - y) ** 2 for x, y in zip(X, Y)) / (2 * len(X))

def grad(theta):
    """梯度(这里就是导数)dJ/dθ = (1/N)·Σ(θ·x - y)·x"""
    return sum((theta * x - y) * x for x, y in zip(X, Y)) / len(X)

# 先对账两个手工算过的点(笔记:J(1/2)=7/12,J(0)=7/3)
print(f"J(0.5) = {cost(0.5):.4f}   # 手工值 7/12 ≈ 0.5833")
print(f"J(0.0) = {cost(0.0):.4f}   # 手工值 7/3  ≈ 2.3333")
print()

# 梯度下降:θ ← θ - α · dJ/dθ,从 θ=0 出发,α = 0.1
theta = 0.0
alpha = 0.1
for step in range(1, 6):
    theta = theta - alpha * grad(theta)
    print(f"第 {step} 轮:θ = {theta:.4f},J(θ) = {cost(theta):.4f}")

运行输出(手工算过的两个点先对上了账,再五轮收敛):

J(0.5) = 0.5833   # 手工值 7/12 ≈ 0.5833
J(0.0) = 2.3333   # 手工值 7/3  ≈ 2.3333

第 1 轮:θ = 0.4667,J(θ) = 0.6637
第 2 轮:θ = 0.7156,J(θ) = 0.1888
第 3 轮:θ = 0.8483,J(θ) = 0.0537
第 4 轮:θ = 0.9191,J(θ) = 0.0153
第 5 轮:θ = 0.9568,J(θ) = 0.0043

五轮之后 θ\theta 已经非常接近真值 1,J(θ)J(\theta) 从 2.33 一路降到 0.004——这就是”循环逼近”的力量。

四、学习速率 α:步子太大与太小

α\alpha 该取 0.1 还是 0.01,甚至 10−510^{-5}?先看两种典型失败:

α\alpha 太大:把”迭代轮数”作为横轴、J(θ)J(\theta) 作为纵轴画一条下降曲线,正常情况是快速下降后趋于平缓。但如果 α\alpha 偏大,曲线会开始上下波动:每一步都从谷底一侧直接跨到另一侧,JJ 一会儿降一会儿升,怎么也压不到最低点。对策就是把 α\alpha 缩小。

α\alpha 太小:方向完全正确,但每一步只挪一点点。只要时间够长它也能到最优值,但收敛速度慢得让人没有耐心。对策是把 α\alpha 适当放大。

讲师给了一个好记的比喻:在草原上找一个小坑的最低点。你是大象,一步就跨过整个坑,永远找不到坑底;你是蚂蚁,方向找对了,但每步挪得太小,得走三天四天才能到坑底。

所以 α\alpha 怎么取?这是一个经验问题,没有万能常数,要结合当前损失函数的特性来定:有的问题要稍大一点,有的要稍小一点,关键是找到对这个问题最合适的大小。

五、二维参数:曲面、等高线与”同时更新”

真实问题里参数几乎从不止一个。最简单的线性模型也要写成:

hθ(x)=θ0x0+θ1x1,x0=1h_\theta(x) = \theta_0 x_0 + \theta_1 x_1, \quad x_0 = 1

此时代价函数变成 J(θ0,θ1)J(\theta_0, \theta_1),几何上不再是平面曲线,而是三维空间里一张碗状的曲面;参数再多一个维度,就是更高维空间里的”碗”。

更新规则相应地变成对每个分量各做一次:

θj←θj−α⋅∂J(θ0,θ1)∂θj,j=0,1\theta_j \leftarrow \theta_j - \alpha \cdot \frac{\partial J(\theta_0, \theta_1)}{\partial \theta_j}, \quad j = 0, 1

注意这里有两个要点:

  1. 同时更新:先用当前这组 (θ0,θ1)(\theta_0, \theta_1) 把两个偏导都算出来,再一起更新;不能更新完 θ0\theta_0 就拿新的 θ0\theta_0 去算 θ1\theta_1 的偏导。
  2. 一个 α\alpha 就够了:有同学问,两个方向为什么共用同一个 α\alpha,能不能各配各的?可以,但意义不大。看等高线图就明白——θ0\theta_0 方向的偏导和 θ1\theta_1 方向的偏导合起来是一个”方向”(梯度向量),α\alpha 是沿这个合成方向迈出的总步长,在两个维度上各有一个分量,分量大小由偏导自身的比例决定。梯度定方向,α\alpha 定步子大小,用两个不同的 α\alpha 并不会让优化明显变好。

最后回答一个自然的疑问:随着迭代进行,需不需要把 α\alpha 做成自适应的(离最优越近就把 α\alpha 调得越小)?一般不需要。因为梯度本身就在自动衰减:离谷底越近,导数越小,即使 α\alpha 固定不变,实际迈出的步子也会越来越小——开始一大步,后来一小步、更小步,天然不会错过最优点。前提只有一个:初始的 α\alpha 别大到离谱(比如一步从山这头直接跨到山那头)。

把二维情形跑起来(呼应课件里”房屋面积 → 价格”的例子,这里用一份迷你数据),并顺带用三档 α\alpha 重现第四节的两种失败:

# 二维梯度下降:h(x) = θ0 + θ1·x(呼应课件"房屋面积→价格"的迷你数据)
X = [1.0, 2.0, 3.0, 4.0]
Y = [2.1, 2.9, 4.2, 5.0]

def cost2(t0, t1):
    """J(θ0, θ1) = (1/2N)·Σ(θ0 + θ1·x - y)^2"""
    return sum((t0 + t1 * x - y) ** 2 for x, y in zip(X, Y)) / (2 * len(X))

def grad2(t0, t1):
    """返回两个偏导:∂J/∂θ0 与 ∂J/∂θ1"""
    n = len(X)
    g0 = sum((t0 + t1 * x - y) for x, y in zip(X, Y)) / n
    g1 = sum((t0 + t1 * x - y) * x for x, y in zip(X, Y)) / n
    return g0, g1

def run_gd(alpha, rounds=10):
    t0, t1 = 0.0, 0.0
    js = []
    for _ in range(rounds):
        g0, g1 = grad2(t0, t1)                     # 先在"当前点"同时算出两个偏导
        t0, t1 = t0 - alpha * g0, t1 - alpha * g1  # 再同时更新两个参数
        js.append(cost2(t0, t1))
    return t0, t1, js

# 同一份数据,三档学习速率:太小(慢)、合适(稳)、太大(震荡甚至发散)
for a in (0.01, 0.1, 0.25):
    t0, t1, js = run_gd(a)
    head = ", ".join(f"{v:.3f}" for v in js[:3])
    print(f"α={a:<5} J 前3轮: {head} … 第10轮 J={js[-1]:.3f},θ0={t0:.3f},θ1={t1:.3f}")

运行输出:

α=0.01  J 前3轮: 5.829, 4.903, 4.124 … 第10轮 J=1.244,θ0=0.250,θ1=0.705
α=0.1   J 前3轮: 0.227, 0.043, 0.037 … 第10轮 J=0.031,θ0=0.503,θ1=1.186
α=0.25  J 前3轮: 8.190, 9.678, 11.439 … 第10轮 J=36.968,θ0=-0.342,θ1=-1.669

α=0.01\alpha = 0.01 十轮才把 JJ 从 6.93 磨到 1.244(蚂蚁);0.10.1 十轮到 0.031(合适);0.250.25 则每轮都在变大,最后冲到 36.97、参数都跑成负数(大象踩空)。同一个算法、同一份数据,α\alpha 的选择决定成败。

📝 动手练一练

练习 1(概念辨析):在二维梯度下降里,“梯度”和”学习速率”各自承担什么角色?为什么 θ0\theta_0、θ1\theta_1 共用一个 α\alpha 就够了?

👉 点击查看参考答案

梯度是”方向”:θ0\theta_0 与 θ1\theta_1 方向的偏导合起来给出一个合成向量,指明让 JJ 变小的方向;α\alpha 是沿这个方向迈出的”步长”。两个维度上的移动分量由两个偏导的比例自动确定,α\alpha 只决定总步子迈多大——所以给两个维度分别配 α0\alpha_0、α1\alpha_1 可以做,但对优化几乎没帮助,通常一个固定的 α\alpha 就够用。

练习 2(动手改代码):把示例二的 α\alpha 从 0.25 改成 0.15 与 0.3 各跑一遍,观察 JJ 的十轮轨迹有什么不同。

👉 点击查看参考答案

实测:α=0.15\alpha = 0.15 时 JJ 的轨迹是 0.478、0.065、0.038……一路下降到 0.028,仍能收敛,而且前期比 α=0.1\alpha=0.1 更快;α=0.3\alpha = 0.3 时轨迹是 15.651、35.404、80.154……到第十轮已经涨到 24526,比 0.25 发散得更猛。可以看到,“合适”与”过大”之间存在一条与数据和曲面形状相关的分界线,这正是第四节说”取 α\alpha 要看损失函数特性、靠经验”的含义——调参时不妨从大到小试探,一旦出现震荡就立刻回调。

本章小结

  • 梯度下降的更新规则:θ←θ−α⋅∂J∂θ\theta \leftarrow \theta - \alpha \cdot \frac{\partial J}{\partial \theta},箭头是赋值,沿负梯度方向一步步逼近最优。
  • 一维直觉:导数为正就左移、为负就右移,越靠近谷底导数越接近 0。
  • 学习速率 α\alpha 太大会震荡甚至发散,太小会慢得离谱;取值是经验问题,要看损失函数特性。
  • 多维参数要用同一点上的偏导同时更新;梯度定方向、α\alpha 定步长,一个 α\alpha 全维度共用即可。
  • 接近最优点时梯度自动变小,固定 α\alpha 的实际步长也随之衰减,所以一般不需要自适应 α\alpha。

📋 行动清单

  • 把示例一的初始点从 θ=0\theta = 0 换成 θ=3\theta = 3(抛物线另一侧),重跑观察与最优点的距离是否每轮按同样比例收缩
  • 在示例二里打印每一轮的 ∂J/∂θ0\partial J/\partial\theta_0 与 ∂J/∂θ1\partial J/\partial\theta_1,验证”越接近最优、梯度越小”
  • 对照站内《人工智能数学基础》中导数与偏导数的章节,把切线斜率的几何含义补扎实

—— 小象教研组

配套资料 · 直接下载

课件、代码、数据集等配套资料全部免费,点击即可直接下载,无需分享或解锁。

  • 第4章课件:机器学习的数学基础 III
    下载
  • 第4章笔记:梯度下降
    下载