查看全课大纲(第 18 / 82 节)
- 1.函数与数据泛化
- 2.向量:从几何直觉到高维空间
- 3.矩阵与张量
- 4.特征值与特征向量:矩阵变换的不动方向
- 5.凸函数:从向量求导的矩阵记号到好优化的函数形状
- 6.概率与统计:机器学习的数学基础
- 7.独立性与随机变量:贝叶斯公式与二项分布
- 8.期望、方差与协方差:随机变量的数字画像
- 9.联合概率:当一条轴不够用的时候
- 10.概率分布(一):从伯努利到伽马函数
- 11.概率分布(二):贝塔、泊松、正态与两个贝叶斯故事
- 12.机器学习的模型:找输入与输出的关系
- 13.机器学习的应用
- 14.过拟合
- 15.机器学习的分类
- 16.有监督学习:从房价预测说起
- 17.损失函数
- 18.梯度下降(上):更新规则与学习速率
- 19.梯度下降(2)
- 20.牛顿方法
- 21.KNN简介
- 22.距离测算
- 23.朴素贝叶斯
- 24.朴素贝叶斯的实际应用
- 25.信息熵:决策树构建的度量基础
- 26.信息增益与决策树的构建
- 27.ID3算法
- 28.决策树节点的划分
- 29.CART树
- 30.Bagging
- 31.Boosting:自适应提升算法
- 32.Gradient Boosting
- 33.线性模型:回归与分类的同一副骨架
- 34.最小二乘:把最优参数直接解出来
- 35.最小二乘法的几何与概率视角
- 36.极大似然:给最小二乘一个概率出身
- 37.逻辑回归(1):把线性模型接到分类上
- 38.逻辑回归(2):损失函数与多分类
- 39.---- 手造数据: 5 个样本, 2 个特征; x2 几乎 = 2*x1 (偏差手工指定, 无随机) ----
- 40.讲师例: 向量 "23014" -- 三把范数尺子量同一个向量
- 41.优化方法(1):坐标下降与前向选择
- 42.优化方法(2):前向策略与最小角回归
- 43.拉格朗日乘子法(1):从无约束到等式约束
- 44.拉格朗日乘子法(2):等式约束下的平行条件与拉格朗日函数
- 45.拉格朗日乘子法(3):不等式约束与 KKT 条件
- 46.最大间隔:把分类问题变成优化问题
- 47.拉格朗日对偶问题:把 w 和 b 全部换成 α
- 48.支持向量:边界上的少数派
- 49.SVM 计算过程:一笔到底算完,再走进核方法
- 50.贝叶斯公式:概率即信念
- 51.隐含变量和概率模型:给看不见的原因建模
- 52.EM 算法:期望与最大化的迭代
- 53.琴生不等式:EM 的数学钥匙
- 54.三个硬币问题:EM 的复盘与衔接
- 55.高斯混合模型:用隐变量解释双峰数据
- 56.隐马尔可夫模型
- 57.隐马尔可夫模型(2):前向算法与动态混合模型
- 58.隐马尔可夫模型在 NLP 中的运用
- 59.EM 算法:把「为什么收敛」推到底
- 60.HMM 在 NLP 中的运用:从分词到机器翻译
- 61.HMM 参数估计:Baum-Welch 的三块拉格朗日
- 62.高维下的高斯模型
- 63.主题模型:从词袋到隐含主题
- 64.Dirichlet 分布(1)
- 65.Dirichlet 分布(2)
- 66.统计学习内容回顾
- 67.K-means 算法
- 68.PCA 降维
- 69.SVD 降维
- 70.从聚类到流形
- 71.局部线性嵌入
- 72.随机邻域嵌入:SNE 与 t-SNE
- 73.无参模型:让数据自己说话
- 74.高斯过程:函数空间里的正态分布
- 75.高斯过程(2):噪声、正则化与核的线性组合
- 76.神经网络简介:从感知器到自编码器
- 77.反向传播:把误差一层层传回去
- 78.卷积神经网络(CNN):让特征自己学出来
- 79.循环神经网络(RNN):给网络装上记忆
- 80.深度学习回顾:静态一类,动态一类
- 81.增强学习(1):蒙特卡洛、树搜索与 Q-learning
- 82.强化学习(2):从乌鸦吃核桃到井字棋的 value 回传
梯度下降(上):更新规则与学习速率
约 27 分钟
小象实战讲义 · 机器学习算法精讲
前面几节我们为监督学习定义了假设函数与代价函数:一条直线拟合得好不好,交给代价函数 打分。本节解决紧随而来的问题——怎么找到让 取最小值的参数?答案是梯度下降(Gradient Descent)。学完本节,你可以用几十行纯 Python 亲手把它跑起来,并真正理解”学习速率”这件事为什么重要。
💡 核心导读
- 梯度下降的全部核心,是一行更新规则:
- 它为什么有效:导数的正负决定移动方向,沿负梯度方向,代价一步一步变小
- 学习速率 控制每步走多远:太大会来回震荡,太小会慢得离谱,取值靠经验、看问题特性
- 参数扩到二维(、)后,各维度要用同一点上的偏导同时更新
- 为什么一般不需要”自适应 “:越接近最优点,梯度本身越小,步长会自动衰减
一、要解决的问题:给代价函数找最优参数
先把主角请出来——前面定义过的代价函数:
其中 是假设函数(hypothesis), 是训练样本数。我们的目标很明确:调整参数 (可能是一个,也可能是 一整组),让 达到最小。
求最优值不止一条路。以后学习线性回归的解析解法时会看到,有些问题可以直接解方程;但梯度下降走的是另一条路——迭代学习(iterative learning):先随便给参数一个初始值(相当于随手画一条线),再根据这条线与已知数据的误差,沿着让误差变小的方向不断调整,循环往复直到满意。这就好比在山区里找山谷:你不知道谷底在哪,但每一步都往”更低”的方向走。
二、更新规则:一行公式与两个记号问题
梯度下降的更新规则只有一行:
先说清两件容易疑惑的事。
箭头是什么意思? 表示赋值:把右边算出来的值,赋给左边的 ,作为下一轮的起点。它不是”两边相等”的数学等式,而是”下一时刻的 由上一时刻算出来”的更新语句。
为什么明明只有一个 ,还写偏导 ? 因为 通常是一组参数 ,我们每次求的是 关于其中某一个分量(比如 )的偏导数;当 只是单个标量时,写成普通导数 就足够了。两种记号在这里指的是同一件事:求当前点的切线斜率。导数与偏导的几何背景,可以对照站内《人工智能数学基础》的相关章节补齐。
式中的 叫学习速率(learning rate),代表每次更新的步长,是一个大于 0 的数(取 0 就永远不动,没有意义)。至于 到底该取多大——这正是本节后面要重点讨论的问题。
三、一维直觉:为什么”减去梯度”就能走到最低点
拿讲师用的最简例子:,训练数据 、、。此时 是关于 的一条抛物线。
现在随便指定一个初始值 ,看看会发生什么:
- 如果 落在抛物线右半边:这一点的导数(切线斜率)是正数。代入更新式,就是 减去一个正数—— 向左移动。每移动一步再算一次导数,导数依然是正的,但越来越接近 0,于是 一步步逼近谷底,最终收敛到最优解 。
- 如果 落在左半边:导数是负数,减去一个负数等于加上一个正数—— 向右移动,同样收敛。
也就是说,梯度下降不需要一次算出最优值,它靠”看导数符号、迈一小步”的循环逼近最优。对上面这个例子, 的导数可以直接算出来:
把整件事写成纯 Python(只用标准库),从 出发跑五轮:
# 梯度下降一维示例: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五轮之后 已经非常接近真值 1, 从 2.33 一路降到 0.004——这就是”循环逼近”的力量。
四、学习速率 α:步子太大与太小
该取 0.1 还是 0.01,甚至 ?先看两种典型失败:
太大:把”迭代轮数”作为横轴、 作为纵轴画一条下降曲线,正常情况是快速下降后趋于平缓。但如果 偏大,曲线会开始上下波动:每一步都从谷底一侧直接跨到另一侧, 一会儿降一会儿升,怎么也压不到最低点。对策就是把 缩小。
太小:方向完全正确,但每一步只挪一点点。只要时间够长它也能到最优值,但收敛速度慢得让人没有耐心。对策是把 适当放大。
讲师给了一个好记的比喻:在草原上找一个小坑的最低点。你是大象,一步就跨过整个坑,永远找不到坑底;你是蚂蚁,方向找对了,但每步挪得太小,得走三天四天才能到坑底。
所以 怎么取?这是一个经验问题,没有万能常数,要结合当前损失函数的特性来定:有的问题要稍大一点,有的要稍小一点,关键是找到对这个问题最合适的大小。
五、二维参数:曲面、等高线与”同时更新”
真实问题里参数几乎从不止一个。最简单的线性模型也要写成:
此时代价函数变成 ,几何上不再是平面曲线,而是三维空间里一张碗状的曲面;参数再多一个维度,就是更高维空间里的”碗”。
更新规则相应地变成对每个分量各做一次:
注意这里有两个要点:
- 同时更新:先用当前这组 把两个偏导都算出来,再一起更新;不能更新完 就拿新的 去算 的偏导。
- 一个 就够了:有同学问,两个方向为什么共用同一个 ,能不能各配各的?可以,但意义不大。看等高线图就明白—— 方向的偏导和 方向的偏导合起来是一个”方向”(梯度向量), 是沿这个合成方向迈出的总步长,在两个维度上各有一个分量,分量大小由偏导自身的比例决定。梯度定方向, 定步子大小,用两个不同的 并不会让优化明显变好。
最后回答一个自然的疑问:随着迭代进行,需不需要把 做成自适应的(离最优越近就把 调得越小)?一般不需要。因为梯度本身就在自动衰减:离谷底越近,导数越小,即使 固定不变,实际迈出的步子也会越来越小——开始一大步,后来一小步、更小步,天然不会错过最优点。前提只有一个:初始的 别大到离谱(比如一步从山这头直接跨到山那头)。
把二维情形跑起来(呼应课件里”房屋面积 → 价格”的例子,这里用一份迷你数据),并顺带用三档 重现第四节的两种失败:
# 二维梯度下降: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十轮才把 从 6.93 磨到 1.244(蚂蚁); 十轮到 0.031(合适); 则每轮都在变大,最后冲到 36.97、参数都跑成负数(大象踩空)。同一个算法、同一份数据, 的选择决定成败。
📝 动手练一练
练习 1(概念辨析):在二维梯度下降里,“梯度”和”学习速率”各自承担什么角色?为什么 、 共用一个 就够了?
👉 点击查看参考答案
梯度是”方向”: 与 方向的偏导合起来给出一个合成向量,指明让 变小的方向; 是沿这个方向迈出的”步长”。两个维度上的移动分量由两个偏导的比例自动确定, 只决定总步子迈多大——所以给两个维度分别配 、 可以做,但对优化几乎没帮助,通常一个固定的 就够用。
练习 2(动手改代码):把示例二的 从 0.25 改成 0.15 与 0.3 各跑一遍,观察 的十轮轨迹有什么不同。
👉 点击查看参考答案
实测: 时 的轨迹是 0.478、0.065、0.038……一路下降到 0.028,仍能收敛,而且前期比 更快; 时轨迹是 15.651、35.404、80.154……到第十轮已经涨到 24526,比 0.25 发散得更猛。可以看到,“合适”与”过大”之间存在一条与数据和曲面形状相关的分界线,这正是第四节说”取 要看损失函数特性、靠经验”的含义——调参时不妨从大到小试探,一旦出现震荡就立刻回调。
本章小结
- 梯度下降的更新规则:,箭头是赋值,沿负梯度方向一步步逼近最优。
- 一维直觉:导数为正就左移、为负就右移,越靠近谷底导数越接近 0。
- 学习速率 太大会震荡甚至发散,太小会慢得离谱;取值是经验问题,要看损失函数特性。
- 多维参数要用同一点上的偏导同时更新;梯度定方向、 定步长,一个 全维度共用即可。
- 接近最优点时梯度自动变小,固定 的实际步长也随之衰减,所以一般不需要自适应 。
📋 行动清单
- 把示例一的初始点从 换成 (抛物线另一侧),重跑观察与最优点的距离是否每轮按同样比例收缩
- 在示例二里打印每一轮的 与 ,验证”越接近最优、梯度越小”
- 对照站内《人工智能数学基础》中导数与偏导数的章节,把切线斜率的几何含义补扎实
—— 小象教研组
配套资料 · 直接下载
课件、代码、数据集等配套资料全部免费,点击即可直接下载,无需分享或解锁。