[译] 深度学习所需的矩阵微积分
如有评论、建议或纠错,请联系 Terence。
摘要
本文尝试讲清理解深度神经网络训练所需的全部矩阵微积分知识。我们只假设你掌握了微积分入门课程的内容,并在需要时提供链接,帮助你复习必要的数学知识。请注意:在开始实践、学习训练和使用深度学习模型之前,你不必先读懂本文。本文面向已经熟悉神经网络基础、希望进一步理解其数学原理的读者。如果读到某处卡住了,不必担心:回头重读上一节,试着写下几个例子,亲手推算。如果仍有疑问,我们很乐意在 forums.fast.ai 的 Theory 板块回答。注意:文末的速查部分汇总了本文讨论的关键矩阵微积分法则和术语。
目录
引言
我们大多数人上一次接触微积分还是在学校里,但导数是机器学习的关键组成部分。深度神经网络尤其如此:它们通过优化损失函数来训练。随手翻开一篇机器学习论文,或 PyTorch 这样的库的文档,微积分就会像逢年过节的远房亲戚一样,突然闯回你的生活。而且,出现的还不只是普通的标量微积分:你需要的是把线性代数与多元微积分拉到一起的矩阵微分学。
不过……“需要”也许不太准确。Jeremy 的课程表明,借助现代深度学习库内置的自动微分,只掌握少量标量微积分知识,也能成为一流的深度学习实践者。但是,如果你真的想理解这些库底层发生了什么,并读懂讨论最新模型训练技术的学术论文,就需要了解矩阵微积分中的一部分知识。
例如,神经网络中单个计算单元的激活值,通常先由连接权重向量 w 与输入向量 x 的点积(线性代数中的运算),再加上一个标量偏置(阈值)得到:
神经网络由许多这样的单元组成,神经元被组织成多个称为层的集合。一层中各单元的激活值,成为下一层各单元的输入。最后一层中一个或多个单元的激活值,就是网络输出。
训练这个神经元,意味着选择权重 w 和偏置 b,使其对全部 N 个输入 x 都产生期望输出。为此,我们最小化一个损失函数:它对所有输入向量 x,比较网络最终的
只要仔细一些,就可以对常见损失函数(均方误差)的标量形式求导,从而得到梯度:
但这还只是一个神经元,而神经网络必须同时训练所有层中全部神经元的权重和偏置。由于存在多个输入,以及可能存在的多个网络输出,我们确实需要函数对向量求导的一般法则,甚至需要向量值函数对向量求导的法则。
本文将逐步推导一些重要的向量偏导数计算法则,尤其是那些对训练神经网络有用的法则。这个领域称为矩阵微积分。好消息是,我们只需要其中很小的一部分,也就是本文介绍的内容。网上关于多元微积分和线性代数的资料很多,但它们通常作为两门独立的本科课程讲授,因此大多数资料也将二者分开讨论。真正讨论矩阵微积分的网页,往往只罗列法则而缺少解释,或者只讲了部分内容。由于符号密集,对基础概念的讨论又很少,除了少数数学专业读者之外,其他人通常很难读懂。(文末有一份附带评述的资源清单。)
这里,我们会通过重新推导、重新发现一些关键法则来解释矩阵微积分。你会发现,它其实并没有那么难!需要学习的不是几十条新规则,而只是几个关键概念。希望这篇短文能帮助你快速入门与神经网络训练相关的矩阵微积分。我们假设你已经熟悉神经网络结构和训练的基础知识;如果还不熟悉,请先完成 Jeremy 课程的第一部分,再回来阅读。(请注意,与许多更偏学术的学习路径不同,我们强烈建议先在实践中学习训练和使用神经网络,再研究背后的数学。有了具体背景,数学会容易理解得多;而且,成为有效的实践者,并不需要先彻底掌握这些微积分知识。)
关于符号的说明:Jeremy 的课程完全使用代码而非数学符号解释概念,因为代码中不熟悉的函数很容易检索和实验。本文则相反,会使用大量数学符号,因为目标之一就是帮助你理解深度学习论文和书籍中的记号。文末有一张简明符号表,同时列出了可用于进一步搜索的词语或短语。
回顾:标量求导法则
希望你还记得下面这些主要的标量求导法则。如果记忆有些模糊,可以看看可汗学院关于标量求导法则的视频。
| 法则 | 对 x 的标量导数记法 | 示例 | |
|---|---|---|---|
| 常数 | c | |
|
| 常数倍 | cf | ||
| 幂法则 | |||
| 和法则 | |||
| 差法则 | |||
| 乘积法则 | fg | ||
| 链式法则 |
此外还有三角函数、指数函数等的求导法则,可以在可汗学院微分学课程中找到。
对于只有一个参数的函数
可以把
这样一来,对
向量微积分与偏导数入门
神经网络层并不是单个单参数函数
对 x 求偏导,其实就是普通的标量求导,只需将式中的其他变量都视为常数。考虑函数
为了说明我们讨论的是向量微积分,而不只是多元微积分,来看看如何处理刚才为
所以,
矩阵微积分
从一个函数的导数转向多个函数的导数,我们就从向量微积分进入了矩阵微积分。来计算两个函数的偏导数,每个函数都有两个参数。保留上一节的
以及
由此得到梯度
梯度向量将一个特定标量函数的所有偏导数组织在一起。如果有两个函数,也可以把它们的梯度逐行堆叠成矩阵。这样得到的就是雅可比矩阵(Jacobian matrix,简称 Jacobian),其中每一行都是一个梯度:
欢迎来到矩阵微积分的世界!
注意,雅可比矩阵有多种表示方式。我们使用所谓的分子布局,但许多论文和软件采用分母布局。后者就是分子布局雅可比矩阵的转置(沿主对角线翻转):
雅可比矩阵的推广
到目前为止,我们看的是雅可比矩阵的具体例子。为了给出更一般的定义,先把多个参数合并成一个向量参数:
对于多个标量值函数,也可以像处理参数那样,把它们组合成一个向量。令
例如,上一节的
常见的情形是
在这个例子中,函数和参数的数量均为
每个
恒等函数
继续之前,请确保自己能推导上面的每一步。如果卡住了,就单独考虑矩阵中的每个元素,应用普通的标量求导法则。这是一个普遍有用的技巧:先把向量表达式拆成一组标量表达式,再计算所有偏导数,最后将结果适当地组合成向量和矩阵。
还要仔细区分矩阵是纵向的 x,还是横向的
向量逐元素二元运算的导数
向量的逐元素二元运算,例如向量加法
可以用
这里将 n 个(而不是 m 个)方程竖着排列,是为了强调逐元素运算的结果是大小为
根据上一节的思路可知,对 w 的雅可比矩阵在一般情况下是如下方阵:
而对 x 的雅可比矩阵为:
看上去相当复杂,但幸运的是,雅可比矩阵常常是对角矩阵,也就是除了主对角线之外,其他位置全为零的矩阵。这会极大地简化计算,因此我们仔细看看:对于逐元素运算,雅可比矩阵何时会化为对角矩阵?
在对角雅可比矩阵中,所有非对角元素都为零:
当 fi 和 gi 不依赖于 wj 时,这些偏导数就为零。我们知道,逐元素运算意味着 fi 仅是 wi 的函数,gi 仅是 xi 的函数。例如,
后面我们会利用这种简化,并将“
在这一条件下,雅可比矩阵的对角元素为
(大号的“0”是一种简写,表示所有非对角元素都为 0。)
更简洁地,可以写成:
以及
其中,
由于经常进行简单的向量算术,逐元素二元运算中的一般函数
由此得到
由于
算子
涉及标量扩展的导数
将标量与向量相乘或相加时,我们隐式地把标量扩展为向量,再执行逐元素二元运算。例如,标量 z 加到向量 x 上得到
对于向量与标量的加法、乘法,对向量 x 的偏导数可以用逐元素法则计算:
这是因为函数
利用通常的标量偏导数法则,可以得到向量与标量相加时雅可比矩阵的对角元素:
所以,
不过,对标量参数 z 求偏导,得到的是一个列向量,而不是对角矩阵。该向量的元素为:
因此,
向量与标量相乘时,雅可比矩阵的对角元素需要用到标量求导的乘积法则:
所以,
对标量参数 z 的偏导数是一个列向量,其元素为:
由此得到
向量求和归约
将向量元素求和是深度学习中的重要操作,例如网络损失函数中就会用到。它也能帮助我们简化向量点积及其他将向量归约为标量的运算的求导。
令
(梯度各分量内部的求和容易混淆,因此一定要保持符号一致。)
来看看简单函数
由于在
注意,结果是全 1 的行向量,而非列向量,所以梯度为
再看一个例子:向量乘以常数标量之后,对结果求和。若
对标量变量 z 的导数为
链式法则
仅靠目前介绍的基本矩阵微积分法则,还无法计算非常复杂函数的偏导数。例如,对于
从概念上说,链式法则是一种分治策略(类似快速排序):将复杂表达式拆成导数更容易计算的子表达式。它的威力在于,我们可以分别处理每个简单子表达式,再把中间结果组合起来,依然得到正确的整体结果。
当我们需要对包含嵌套子表达式的表达式求导时,链式法则就派上用场了。例如,处理
单变量链式法则
先给出这个嵌套表达式的导数:
链式法则通常用嵌套函数来定义,例如单变量链式法则中的
使用单变量链式法则时,遵循以下步骤:
- 为嵌套子表达式,以及二元和一元运算的子表达式引入中间变量。例如,
是二元运算; 和其他三角函数通常是一元运算,因为只有一个操作数。这一步将所有方程规范化为单个运算或单次函数调用。 - 计算各中间变量对其参数的导数。
- 将各中间变量的导数相乘,得到整体结果。
- 如果最终导数表达式中仍引用了中间变量,就代回它们的原始表达式。
第三步把中间结果串成一条“链”,这正是“链式法则”名称的由来。将中间导数相乘,是各种链式法则共有的主题。
用这个流程处理
- 引入中间变量。令
表示子表达式 (是以下表达式的简写: )。由此得到: 这些子表达式的排列顺序不影响答案,但我们建议按嵌套结构由内向外展开。这样,每个表达式及其导数都只依赖之前已经计算出的量。
- 计算导数。
- 组合。
- 代回。
注意,分别计算各个中间变量的导数有多么简单!链式法则保证这种做法成立,并告诉我们如何组合中间结果,得到
可以用单位约消来理解链式法则的组合步骤。设 y 的单位是英里,x 表示油箱中的加仑数,u 的单位也是加仑,那么
理解单变量链式法则的另一种方式,是把整个表达式画成数据流图或运算链(对编译器开发者而言,就是抽象语法树):
函数参数 x 的变化先经过平方运算,再经过 sin 运算,最终改变结果 y。可以将
单变量链式法则的适用条件。注意,从 x 到根节点 y 只有一条数据流路径。x 的变化只能通过一种方式影响输出 y,这就是应用单变量链式法则的条件。另一个更容易记住、但不那么严密的说法是:所有中间子表达式函数(如
给对自动微分感兴趣的读者补充一点:论文和库文档会使用前向微分与反向微分(用于反向传播算法)这些术语。从数据流的角度看,我们正在做前向微分,因为它沿着正常的数据流方向进行。反向微分则沿相反方向,追问输出的变化如何关联到函数参数 x。由于反向微分能同时确定所有函数参数对应的变化,对于参数很多的函数,其导数计算会高效得多。前向微分则需要依次考虑每个参数的变化如何影响函数输出 y。下表着重展示这两种方法计算偏导数的顺序。
| 从 x 到 y 的前向微分 | 从 y 到 x 的反向微分 |
|---|---|
自动微分超出了本文的范围,这里只是为今后的文章作铺垫。
许多读者可以在脑中算出
面对深层嵌套表达式,可以把使用链式法则想象成编译器将
- 引入中间变量。
- 计算导数。
- 组合四个中间值。
- 代回。
下面展示从 x 到 y 的运算链中的数据流:
至此,我们可以用链式法则处理单变量 x 的嵌套表达式导数,但前提是 x 只能通过一条数据流路径影响 y。为了处理更复杂的表达式,接下来需要扩展这一方法。
单变量全导数链式法则
单变量链式法则的适用范围有限,因为所有中间变量都必须是单变量函数。不过,它展示了链式法则的核心机制:将中间子表达式的导数相乘。要处理
当然,我们一眼就能看出
不妨硬试一下,看看会发生什么。如果假装
由于
糟糕!偏导数
x 既作为加法的操作数,也作为平方运算的操作数影响 y。下面的方程描述对 x 作微小调整如何影响输出:
于是有
如果令
这时就需要全导数“法则”了。其基本含义是:计算
使用这个公式,就得到正确答案:
这就是我们所称的单变量全导数链式法则的一次应用:
全导数假设所有变量都可能存在相互依赖;偏导数则将除 x 之外的所有变量视为常数。
这里的符号有一个微妙之处。所有导数都写成偏导数,因为 f 和 ui 是多变量函数。这与 MathWorld 的记法一致,但不同于 Wikipedia,后者使用
实际使用时,只要记住:对 x 求全导数时,其他变量也可能是 x 的函数,所以它们的贡献也要加进来。等式左侧看起来像普通偏导数,右侧实际上计算的是全导数。不过,许多临时变量通常只依赖一个参数,这时单变量全导数链式法则就退化为单变量链式法则。
考虑一个嵌套子表达式,例如
以及相应偏导数:
其中,
还要注意,全导数公式始终对
而全导数的形式仍然相同:
中间变量所用的运算符改变时,变化的是偏导数(权重),而不是公式本身。
微积分基础较好的读者可能会疑惑:为什么连
进一步使用中间变量,就可以把单变量全导数链式法则化为最终形式。目标是去掉前面那个显得突兀的
只需引入一个新的临时变量,作为 x 的别名:
当所有中间变量都是单变量函数时,这条全导数链式法则就退化为单变量链式法则。因此,只记住这个更一般的公式,就能覆盖两种情况。先埋个伏笔:注意,这个求和很像向量点积
继续之前,提醒一下网上的术语用法。遗憾的是,在微积分讨论中,本节这条基于全导数的链式法则通常被称为“多变量链式法则”,这个名称很容易造成误解!只有中间变量才是多元函数;整个函数(如
向量链式法则
理解全导数链式法则之后,就可以处理函数向量和向量变量的链式法则了。令人意外的是,这条更一般的法则,看起来与标量的单变量链式法则同样简洁。与其直接给出公式,不如亲自重新推导,以便牢固掌握。先计算一个向量函数对标量的导数,例如
引入两个中间变量
向量 y 对标量 x 的导数是一个列向量,其各元素可用单变量全导数链式法则计算:
现在,我们仅靠标量法则就得到了答案,只不过把导数组织成了向量。接下来尝试从这一结果抽象出向量形式:目标是将下面由标量运算组成的向量,改写为一次向量运算。
将
这意味着,一个雅可比矩阵等于另外两个雅可比矩阵的乘积,很有意思。来核对一下结果:
太好了!结果与标量方法相同。对于函数向量和单个参数,这条向量链式法则看来是正确的,而且确实与单变量链式法则相呼应。比较向量法则:
与单变量链式法则:
要使公式适用于多个参数或向量 x,只需将式中的 x 换成向量 x。这样,
相比单变量链式法则,向量公式的妙处在于:保持同样简洁的符号形式,同时自动计入全导数。雅可比矩阵包含了 fi 对 gj、以及 gi 对 xj 的所有可能组合。为完整起见,下面展开这两个雅可比矩阵:
其中
即使在
前面已经看到,对向量 w 和 x 的逐元素运算会产生对角矩阵,其元素为
在这种情况下,向量链式法则简化为:
因此,雅可比矩阵化为对角矩阵,每个对角元素都是相应单变量链式法则的计算结果。
经过这一大段数学推导,终于得到回报:你只需要掌握向量链式法则,因为单变量公式都是它的特例。下表汇总了为得到雅可比矩阵而需要相乘的各个组成部分。
神经元激活值的梯度
现在,我们已经具备所需的全部工具,可以计算神经网络中单个计算单元的典型激活值对模型参数 w 和 b 的导数:
(这表示一个采用全连接权重和整流线性单元激活函数的神经元。当然,也存在卷积等其他仿射函数,以及指数线性单元等其他激活函数,它们遵循类似的逻辑。)
先暂时放下 max,专注于计算
点积
将 y 改写之后,就能认出两个偏导数已经知道的子表达式:
向量链式法则告诉我们,将这些偏导数相乘:
为了核对结果,可以将点积完全展开为标量函数:
于是:
很好!标量计算的结果与向量链式法则的结果一致。
现在令
接下来处理神经元激活值
补充说明如何将标量函数广播到各元素上。当 max 的一个或两个参数是向量时,例如
对于广播后的版本,其导数对应一个由 0 和 1 组成的向量,其中:
要计算
向量链式法则给出:
可以将其改写为:
再将
这个等式符合直觉。当激活函数将仿射函数输出 z 截断为 0 时,对任意权重 wi 的导数都为零。当
接着计算神经元激活值对 b 的导数,得到:
现在利用这些偏导数,处理整个损失函数。
神经网络损失函数的梯度
训练神经元,需要对损失函数(也称“代价函数”)关于模型参数 w 和 b 求导。本例采用均方误差作为损失函数。由于训练使用多个向量输入(例如多张图像)及相应的标量目标(例如每张图像对应一个分类),我们需要引入更多符号。令:
其中
其中 yi 是标量。于是代价函数变为:
按照链式法则流程,引入以下中间变量:
先计算对 w 的梯度。
对权重的梯度
根据前面的结果,我们知道:
以及
于是,整体梯度为:
为了理解这个等式,可以代入误差项
注意,这个计算是在 X 中的所有 xi 上取加权平均。权重就是误差项,即对每个输入 xi,目标输出与神经元实际输出之间的差。所得梯度总体上指向代价或损失增大的方向,因为较大的 ei 会突出对应的 xi 的贡献。假设只有一个输入向量
当然,我们希望减小而不是增大损失,因此梯度下降的递推公式沿负梯度方向更新当前位置(标量学习率为
由于梯度指向代价增大的方向,我们要沿相反方向更新 w。
对偏置的导数
为了优化偏置 b,还需要对 b 的偏导数。再次列出中间变量:
前面已经计算了方程
对于 v,偏导数为:
而代价函数本身的偏导数为:
和之前一样,可以代入误差项:
因此,偏导数就是根据激活情况取误差值或零,再对它们求平均。更新神经元偏置时,沿代价增大的相反方向作小幅调整:
实际中,将 w 与 b 合并为一个向量参数更方便,这样不必分别处理两个偏导数:
至此,神经网络损失函数的优化推导就完成了:我们已经得到执行梯度下降所需的两个偏导数。
总结
希望你一路读到了这里。你已经在理解矩阵微积分的道路上迈出了重要一步!下一节提供速查资料,汇总本文的全部法则。也请看看后面附带评述的资源链接。
接下来,可以学习对矩阵而不只是对向量求偏导。例如,阅读 Matrix calculus 中关于矩阵微分的部分。
致谢。感谢 Yannet Interian(旧金山大学数据科学硕士项目教师)和 David Uminsky(数据科学硕士项目教师兼主任)帮助完善本文的符号记法。
矩阵微积分速查
梯度与雅可比矩阵
二元函数的梯度是一个长度为 2 的行向量:
以向量为输入的向量值函数,其雅可比矩阵是一个包含全部可能标量偏导数的
恒等函数
向量的逐元素运算
使用
对 w 的雅可比矩阵为(对 x 的情形类似):
如果满足逐元素对角条件,即
以下是一些逐元素运算的例子:
标量扩展
将标量 z 加到向量 x 上得到
标量乘法得到:
向量归约
向量求和对其中一个向量的偏导数为:
对于
对于
向量点积为
类似地,
链式法则
向量链式法则是一般形式,其他法则都是它的退化情形。当 f 是单变量 x 的函数,且所有中间变量 u 都是单变量函数时,适用单变量链式法则。当部分或全部中间变量是多变量函数时,适用单变量全导数链式法则。其他情况下,则使用向量链式法则。
| 单变量法则 | 单变量全导数法则 | 向量法则 |
|---|---|---|
符号说明
像 x 这样的粗体小写字母表示向量,像 x 这样的斜体字母表示标量。xi 是向量 x 的第
记号
I 表示维度适当的方形“单位矩阵”:主对角线全为 1,其他位置全为零。
点积
求导
函数对 x 的偏导数
f 对向量 x 的梯度
雅可比矩阵通过逐行堆叠,将多个函数的梯度组织成一个矩阵:
下面的记号表示:当
参考资源
Wolfram Alpha 可以进行符号矩阵代数运算;另外,还有一个很不错的专用矩阵微积分求导工具。
在网上查找资料时,请搜索“matrix calculus”,而不是“vector calculus”。下面是对 Google 搜索中靠前结果的一些评述:
- https://en.wikipedia.org/wiki/Matrix_calculus
Wikipedia 的条目其实相当不错,对不同布局约定也有清楚的说明。回忆一下,我们采用分子布局:雅可比矩阵中的变量横向排列,函数纵向排列。Wikipedia 对全导数的解释也很好,但要注意,其记号与本文略有不同。我们始终使用
,而不是 dx。 - http://www.ee.ic.ac.uk/hp/staff/dmb/matrix/calculus.html
这个网页有一节讨论矩阵微分,给出了一些有用的恒等式,作者采用分子布局。读完本文后,如果想进一步了解矩阵求导与向量求导的区别,可以从这里开始。
- https://www.colorado.edu/engineering/CAS/courses.d/IFEM.d/IFEM.AppC.d/IFEM.AppC.pdf
这是《有限元方法导论》课程讲义的一部分,作者应该是 Carlos A. Felippa。他采用分母布局,因此其雅可比矩阵是本文记法的转置。
- http://www.ee.ic.ac.uk/hp/staff/dmb/matrix/calculus.html
这个网页列出了大量不同向量和矩阵的实用导数,是一份很好的速查表。基本没有解释,只是一组法则。
- https://www.math.uwaterloo.ca/~hwolkowi/matrixcookbook.pdf
另一份速查资料,主要讨论一般矩阵运算,比上一项有更多说明。
- https://www.comp.nus.edu.sg/~cs5240/lecture/matrix-differentiation.pdf
一组有用的幻灯片。
如果想进一步了解神经网络,以及优化和反向传播背后的数学,我们强烈推荐 Michael Nielsen 的书。
如果你特别关注卷积神经网络,可以阅读 《深度学习卷积运算指南》(A guide to convolution arithmetic for deep learning)。
本文引用了全导数法则。这是一个重要概念,其含义只是:对 x 求导时,必须考虑所有依赖于 x 的变量对 x 的导数。