目录
李沐老师的讲解思路是先从数学概念引入,讲完以后再到代码实现:
1. 数学概念
1.1 标量导数
1.2 向量求导(梯度)
分为四种情况:
1.2.1 标量y,关于向量x求导
李沐老师这里先讲了y为标量,x为向量的情况,x是长度为1的列向量,关于列向量的导数(即梯度)是行向量,具体解释如下:
在这个例子里, 𝑦= + 描述的是一个二维空间中的二次曲面,其在三维空间中的形状是一个椭圆锥面(eccentric cone)。
李沐老师:可以不用记忆这个导数怎么求,但一定要理解 (导数运算的核心概念 -- 梯度)
梯度
- 理解1:这个曲面在二维平面上投影为一个椭圆,类似于地理上的“等高线”;它是一个标量函数,因为输出 𝑦 是一个单一的数值,而不是一个向量
- 理解2:梯度是一个向量,它在多元函数的每一点上指向函数增长最快的方向(极为关键,这也是今后所有机器学习求解的核心思想)
- 理解3:梯度方向就是那个使得方向导数(即变化率)达到最大的方向
- 理解4:梯度方向与等高线的切线是正交的
- 理解5:y关于x的梯度”,就是y关于x的求导结果
(补充:“dy/dx” 通常读作 “dee-y over dee-x”,其中 “over” 指分数中的 “之上/之下” 的关系)
李沐老师最后还给出了一些样例:
(最后这里,有个很有意思的点:两个向量的导数是一个矩阵。现在还没讲,后面会讲)
(GPT是我们的好朋友,我有问题都靠他)
1.2.2 向量y,关于标量x求导
李沐老师:关于分子还是分母布局,总得选一个,这样形状才能对上,所以这里一般是用“分子布局符号”
分子布局符号
以下是智谱AI给出的解释:
分子布局(Numerator Layout):
- 在分子布局中,矩阵的行被视为“分子”,而列被视为“分母”。
- 当进行矩阵与向量的乘法时,矩阵位于乘法的左侧,向量位于右侧。例如,如果 A 是一个 m×n 的矩阵,b 是一个 n 维列向量,那么它们的乘积 Ab 是一个 m 维列向量。
- 在分子布局中,矩阵的行与向量的列相乘,得到的是列向量
分母布局(Denominator Layout):
- 在分母布局中,矩阵的列与向量的行相乘,得到的是行向量
在许多数学和编程环境中,默认的或更常用的约定是分子布局。
1.2.3 向量y,关于向量x求导
两个向量之间的求导,可以拆解为m个 “1.2.1 标量y,关于向量x求导” 的问题
这里的加粗大写字母 “I” 通常用来表示单位矩阵(Identity Matrix),大概长这样:
1.2.4 对矩阵求导(拓展)
图形右边括号里的是 求导结果 的 “形状”
李沐老师:矩阵和矩阵之间的求导大概了解即可,不用 (像前三种那样) 完全弄懂
1.3 QA(拓展)
- 导数的作用主要是梯度下降,容易陷入局部最优解,只有在凸函数上才有可能避免这个问题(使用李雅普诺夫函数、模拟退火函数等),不幸的是,机器学习中几乎不会处理凸函数
- 李沐老师答疑说不用纠结最优解这个问题
2. 自动求导
2.1 向量的链式法则
李沐老师:标量的链式法则,拓展到向量,最重要的是把形状搞对
关于“形状”:
- (1,) 指标量,表示一个包含单个元素的数组,例如 [5] 或者 [x]。
- (1,n) 指1行n列的行向量,例如 [1, 2, 3] 便是一个 (1,3) 的行向量。
- (2,5,4) 数组可以理解为有2个5行4列的矩阵组成的
李沐老师还具体举了个例子,来讲清楚两个向量求导具体是怎么计算的:
李沐老师:这其实就是我们以后要讲的,线性回归的一个例子,对线性回归做一次求导
解读:
- 假设:x 和 w 都是长为 n 的向量,y是一个标量,z 等于 x 和 w 作点积,减去y做平方
- 计算:z 关于 w 的导数,得先分解
- 分解:把长式子分解为三个小步骤(一层一层换元),用上链式法则排个序,z在最外层,a 在最里层,随后在分子部分按照 z, b, a 的由外至内的顺序逐个展开
- d<x,w>关于dw的求导结果为什么是x的转置,可参考 “1.2.3 向量y,关于向量x求导”
- 最后是 b 带回去,因为点积的结果是标量,最后输出的结果是 行向量(经过缩放后的 x 的转置)
李沐老师:当然我们可以再往前走一点点,涉及到 矩阵 的例子2:
李沐老师:其实这个跟之前(例子1)一样,都是先分解出中间变量,就不详细讲啦,这样我们就知道矩阵求导,是怎么求出来的了~
2.2 自动求导 -- 定义
2.3 计算图 -- PyTorch 的内部工作原理
李沐老师:
- 当然我们使用pytorch不需要大家去理解计算图,但我觉得大家有必要去知道一下它的内部的工作原理
- 计算图等价于我们刚刚用链式法则求导的过程
- 小圆圈表示输入,或者计算
李沐老师这里做了个 TensorFlow 和 PyTorch 的对比,用的是两种不同的构造方式。
2.4 自动求导的两种模式 -- 正向积累 vs 反向传递
李沐老师:
- 正向积累:从x出发,从第一个中间变量开始,逐渐向最终的y函数求导
- 反向传递:y,即最终的函数,关于最后的中间变量的导数,来乘以导数第二个,不断往前往前......来算到最前面
- 反向传递 (反向积累) 在人工智能领域鼎鼎大名
2.5 反向传递
(2)和(3)都要读取,才能计算(4)
2.6 复杂度 -- 反向传递vs正向
李沐老师:
- 正向和反向,计算复杂度差不多,但是内存复杂度差很多
- 反向传递,需要把所有正向计算的中间结果给存起来,这也是深度神经网络特别耗GPU资源的最大祸源
- 正向积累的内存复杂度虽然低(不用像反向传递那样存储每次结果),但它的问题是,我们计算每一个变量的梯度,需要多扫一遍
- 我们通常在神经网络里不会用正向积累,因为我们需要对每一层计算梯度,正向积累的计算复杂度太高
智谱AI:为什么深度学习不用正向传播
- 如果我们尝试使用一种类似于正向传播的方式来直接计算梯度,那么对于每个参数的梯度计算,我们可能都需要重新执行一次完整的前向传播。这是因为在前向传播中,如果不保存中间层的输出,我们就无法直接从当前层回溯到前面层来计算梯度。
- 如果我们考虑每个参数都需要单独的前向传播,那么正向积累的计算复杂度是标准反向传播的n倍
3. 自动求导(PyTorch 实现)
可参考官方课程jupyter文档(我这里直接上放上文档截图):
3.1 案例引入
关键概念:y关于x的梯度”,就是y关于x的求导结果
3.2 非标量变量的反向传播
李沐老师:这就意味着,我们之前大篇幅讲的向量和向量、矩阵和矩阵的求导,在我们深度学习里面用得是很少的
(言外之意:深度学习大都是标量和向量之间求导)
3.3 分离计算
3.4 Python控制流的梯度计算
李沐老师:
- 这个循环计算,是由输入的a, b(主要), c 来控制的
- 这里pytorch的工作原理:每次计算,torch都会在背后把计算图存下来
- 把计算图倒回去,算一遍,就能得到它的矩阵
- 这里的a是生成的随机数,也就是说,不管a是什么数,都能算出控制流的梯度
- 这就是pytorch(隐式)的好处,控制流也能求导,但会比TensorFlow(显式)慢一些