Py学习  »  机器学习算法

NMT Tutorial 3扩展a. 深度学习的矩阵微积分基础

ApacheCN_飞龙 • 5 年前 • 461 次点击  

写在前面:矩阵微积分是深度学习的数学基础之一,但是这部分内容在大学计算机系(及相关非数学类专业)本科几乎没有介绍,想要了解全凭自学。我之前看过的比较好的资料有三个:维基百科的Matrix Calculus词条The Matrix CookbookTowser的《机器学习中的矩阵/向量求导》。然而前两个都是英文资料,而且主要重视结论,可以当做字典用,但是看完总有点“知其然不知其所以然”的感觉(维基词条似乎有简单的计算过程介绍,但是还是有点简略);Towser大神的文章写得很不错,但是我数学较差,看到张量相关的部分还是觉得有点脑子转不过来

最近这几天我整理自己的微博收藏时,无意间发现北邮陈光老师(爱可可爱生活@微博)曾经推荐过一篇文章:Terence Parr和Jeremy Howard合作的The Matrix Calculus You Need For Deep Learning,感觉比较符合我的需求(再次证明收藏过的东西如果不回顾就是白收藏),相对Towser的文章更基础一点。这篇博客就是我阅读该论文的一些摘要

预备知识

对于一元函数的导数,存在如下几条规则(以下均认为x是自变量):

  • 常数函数的导数为0:f(x) = c \rightarrow df/dx = 0
  • 常量相乘法则:(cf(x))' = c\frac{df}{dx}
  • 幂函数求导法则:f(x) = x^n \rightarrow \frac{df}{dx} = nx^{n-1}
  • 加法法则:\frac{d}{dx}(f(x) + g(x)) = \frac{df}{dx} + \frac{dg}{dx}
  • 减法法则:\frac{d}{dx}(f(x) - g(x)) = \frac{df}{dx} - \frac{dg}{dx}
  • 乘法法则:\frac{d}{dx}(f(x)\cdot g(x)) = f(x)\cdot \frac{dg}{dx} + \frac{df}{dx}\cdot g(x)
  • 链式法则:\frac{d}{dx}(f(g(x))) = \frac{df(u)}{du}\cdot \frac{du}{dx},若令u=g(x)

对于二元函数,需要引入偏导数的概念。假设函数为f(x,y),求函数对xy的偏导数时,将另一个变量看作是常量(对于多元函数,求对某个变量的偏导数时,将其它所有变量都视为常量)。求得的偏导数可以组合成为梯度,记为 \nabla f(x,y)。即 ∇f(x,y)=[∂f(x,y)∂x∂f(x,y)∂y]

(注意:原文里梯度表示成了一个行向量,并说明他们使用了numerator layout。但是这样会使得标量对向量的微分结果与该向量形状相异,也与主流记法相左。因此本文记录时做了转换,使用主流的denominator layout记法)

矩阵微积分

同一个函数对不同变量求偏导的结果可以组成称为梯度,多个函数对各变量求偏导的结果则可以组合成一个矩阵,称为雅可比矩阵(Jacobian matrix)。例如如果有函数f, g,两者各自的梯度可以组合为 J=[∇Tf(x,y)∇Tg(x,y)]

雅可比矩阵的泛化

可以将多元变量组合成一个向量:f(x_1, x_2, \ldots, x_n) = f(\boldsymbol{x})(本文认为所有向量都是n \times 1维的,即 x=[x1x2⋮xn]

)假设有m个函数分别向量\boldsymbol{x}计算得出一个标量,即 y1=f1(x)y2=f2(x)⋮ym=fm(x) 可以简记为 y=f(x) \boldsymbol{y}\boldsymbol{x}求导的结果就是将每个函数对\boldsymbol{x}的导数堆叠起来得到的雅可比矩阵: ∂y∂x=[∂∂x1f1(x)∂∂x2f1(x)⋯∂∂xnf1(x)∂∂x1f2(x)∂∂x2f2(x)⋯∂∂xnf2(x)⋮⋮⋱⋮∂∂x1fm(x)∂∂x2fm(x)⋯∂∂xnfm(x)]

两向量间逐元素运算的导数

假设\bigcirc是一个对两向量逐元素进行计算的操作符(例如\bigoplus是向量加法,就是两个向量逐元素相加),则对于\boldsymbol{y} = \boldsymbol{f}(\boldsymbol{w}) \bigcirc \boldsymbol{g}(\boldsymbol{x}),假设n = m = |y| = |w| = |x|,可以做如下展开 [y1y2⋮yn]=[f1(w)◯g1(x)f2(w)◯g2(x)⋮fn(w)◯gn(x)]

\boldsymbol{y}分别对\boldsymbol{w}\boldsymbol{x}求导可以得到两个方阵 Jw=∂y∂w=[∂∂w1(f1(w)◯g1(x))∂∂w2(f1(w)◯g1(x))⋯∂∂wn(f1(w)◯g1(x))∂∂w1(f2(w)◯g2(x))∂∂w2(f2(w)◯g2(x))⋯∂∂wn(f2(w)◯g2(x))⋮⋮⋱⋮∂∂w1(fn(w)◯gn(x))∂∂w2(fn(w)◯gn(x))⋯∂∂wn(fn(w)◯gn(x))]

Jx=∂y∂x=[∂∂x1(f1(w)◯g1(x))∂∂x2(f1(w)◯g1(x))⋯∂∂xn(f1(w)◯g1(x))∂∂x1(f2(w)◯g2(x))∂∂x2(f2(w)◯g2(x))⋯∂∂xn(f2(w)◯g2(x))⋮⋮⋱⋮∂∂x1(fn(w)◯gn(x))∂∂x2(fn(w)◯gn(x))⋯∂∂xn(fn(w)◯gn(x))]

由于\bigcirc是逐元素操作,因此f_i是一个关于w_i的函数, g_i也是一个关于x_i的函数。因此当j \not= i时, \frac{\partial }{\partial w_j}f_i(w_i) = \frac{\partial }{\partial w_j}g_i(x_i) = 0,而且 0 0 = 0,因此上述两个雅可比矩阵都是对角矩阵,可以简记为 ∂y∂w=diag(∂∂w1(f1(w1)◯g1(x1)),∂∂w2(f2(w2)◯g2(x2)),…,∂∂wn(fn(wn)◯gn(xn)))

∂y∂x=diag(∂∂x1(f1(x1)◯g1(x1)),∂∂x2(f2(x2)◯g2(x2)),…,∂∂xn(fn(xn)◯gn(xn)))

如果只考虑最简单的向量计算,不妨令\boldsymbol{f}(\boldsymbol{w}) = \boldsymbol{w}, \boldsymbol{g}(\boldsymbol{x}) = \boldsymbol{x},因此有 f_i(w_i) = w_i。假设\bigcirc为逐元素相加,那么会有 ∂∂wi(fi(wi)+gi(xi))=1=∂∂x(fi(wi)+gi(xi))

其它减、乘、除同理,可以得到如下式子(由于向量加法+的法则就是 \oplus,因此\oplus简写为+。减法同理) ∂(w+x)∂w=I=∂(w+x)∂x∂(w−x)∂w=I∂(w−x)∂x=−I∂(w⊗x)∂w=diag(x)∂(w⊗x)∂x=diag(w)∂(w⊘x)∂w=diag(⋯1xi⋯)∂(w⊘x)∂x=diag(⋯−wix2i⋯)

向量与标量运算的导数

向量与标量之间的运算,可以把标量扩展成一个同维度的向量,然后对两个向量做逐元素的运算。标量的扩展一般是将其乘以一个全为1的向量,例如计算\boldsymbol{y} = \boldsymbol{x} + z实际上是\boldsymbol{y} = \boldsymbol{f}(\boldsymbol{x}) + \boldsymbol{g}(z) ,其中\boldsymbol{f}(\boldsymbol{x}) = \boldsymbol{x}, \boldsymbol{g}(z) = \boldsymbol{1}z

因此可以得到 ∂∂x(x+z)=I∂∂x(xz)=Iz∂∂z(x+z)=1∂∂z(xz)=x

向量的求和规约操作

求和规约,原文是sum reduce,实际上就是把一个向量的所有元素求和

y = {\rm sum}(\boldsymbol{f}(\boldsymbol{x})) = \sum_{i=1}^n f_i(\boldsymbol{x}),展开可得 ∂y∂x=[∂y∂x1∂y∂x2⋯∂y∂xn]T=[∂∂x1∑ifi(x)∂∂x2∑ifi(x)⋯∂∂xn∑ifi(x)]T=[∑i∂fi(x)∂x1∑i∂fi(x)∂x2⋯∑i∂fi(x)∂xn]T

讨论一个最简单的情况,就是y={\rm sum}(\boldsymbol{x})。由之前的讨论,有 f_i(\boldsymbol{x}) = x_i。将定义代入上式,并考虑对i \not= j\frac{\partial }{\partial x_j}x_i = 0,易得 y=sum(x)→∇y=1

链式法则

对复杂表达式(各种表达式组合嵌套得到的表达式),需要使用链式法则求导。本文将链式法则分为了三种

单变量链式法则

其实就是高数里学到的。假设y = f(g(x)),令g(x) = u,则单变量链式法则为 dydx=dydududx

单变量全微分链式法则

在最简单的单变量链式法则里,所有中间变量都是单变量函数。对于y = f(x) = x+x^2这种函数,情况会复杂一些。如果引入中间变量u_1u_2,有 u1(x)=x2u2(x,u1)=x+u1(y=f(x)=u2(x,u1))

如果只使用前面的单变量链式法则,那么du_2/du_1 = 1, du_1/dx = 2x,所以 dy/dx = du_2/dx = du_2/du_1 \cdot du_1/dx = 2x,跟结果(2x+1)对不上。如果因为u_2有两个变量而引入偏导数,那么 ∂u1(x)∂x=2x∂u2(x,u1)∂u1=1 注意此时不能直接说\partial u_2 / \partial x = 1!因为对x求偏导数的时候限定了其它变量不会随着 x的变化而变化,但是u_1实际上还是关于x的函数。因此应该使用单变量全微分链式法则:假设u_1, \ldots, u_n(可能)都与x有关,则 ∂f(x,u1,…,un)∂x=∂f∂x+n∑i=1∂f∂ui∂ui∂x 将上面的例子代入,可知 ∂f(x,u1)∂x=∂f∂x+∂f∂u1∂u1∂x=1+2x 注意全微分公式在任何时候都是算偏导数的加和,并不是因为例子里存在求和操作,而是因为它表示的是各个 xy的变化量的加权求和。考虑式子y = f(x) = x \cdot x^2,那么 u1(x)=x2u2(x,u1)=xu1∂u1∂x=2x∂u2∂x=u1∂u2∂u1=x 使用全微分公式,可知 dydx=∂u2∂x+∂u2∂u1∂u1∂x=u1+x⋅2x=x2+2x2=3x2 对前面介绍的全微分链式法则,如果引入一个新的变量u_{n+1} = x,则可以得到一个更简洁的公式 ∂f(u1,…,un+1)∂x=n+1∑i=1∂f∂ui∂ui∂x 这看上去很像两个向量的内积: ∂f∂u∂u∂x

向量的链式法则

为了引出向量的链式法则,可以先看一个例子。假设\boldsymbol{y} = \boldsymbol{f}(x),其中 [y1(x)y2(x)]=[f1(x)f2(x)]=[ln(x2)sin(3x)]

引入两个中间变量 g_1g_2,使得\boldsymbol{y} = \boldsymbol{f}(\boldsymbol{g}(x)),其中 [g1(x)g2(x)]=[x23x][f1(g)f2(g)]=[ln(g1)sin(g2)] 那么\partial \boldsymbol{y}/\partial x就可以使用全微分链式法则: ∂y∂x=[∂f1(g)∂x∂f2(g)∂x]=[∂f1∂g1∂g1∂x+∂f1∂g2∂g2∂x∂f2∂g1∂g1∂x+∂f2∂g2∂g2∂x] 这个向量可以写成矩阵与向量相乘的形式 [∂f1∂g1∂g1∂x+∂f1∂g2∂g2∂x∂f2∂g1∂g1∂x+∂f2∂g2∂g2∂x]=[∂f1∂g1∂f1∂g2∂f2∂g1∂f2∂g2][∂g1∂x∂g2∂x] 可以看到这个矩阵是雅可比矩阵,向量同理,即 [∂f1∂g1∂f1∂g2∂f2∂g1∂f2∂g2][∂g1∂x∂g2∂x]=∂f∂g∂g∂x 当x不是标量而是向量\boldsymbol{x}时,同样的法则也成立,这意味着我们可以得到一个向量的链式法则 y=f(g(x))→∂y∂x=∂f∂g∂g∂x 事实上,这个公式可以更加简洁。对很多应用,雅可比矩阵都是对角方阵,此时 f_i是一个只与g_i有关的函数,而g_i是一个只与x_i有关的函数。即 ∂f∂g=diag(∂fi∂gi)∂g∂x=diag(∂gi∂xi) 因此在这种情况下,向量的链式法则可以简化为 ∂∂xf(g(x))=diag(∂fi∂gi)diag(∂gi∂xi)=diag(∂fi∂gi∂gi∂xi)

激活函数的梯度

假设网络是全连接前馈神经网络(即这里不考虑卷积、RNN等),激活函数为ReLU,那么有 activation(x)=max(0,w⋅x+b)

要计算的是\frac{\partial }{\partial \boldsymbol{w}}(\boldsymbol{w} \cdot \boldsymbol{x} + b)\frac{\partial }{\partial b}(\boldsymbol{w} \cdot \boldsymbol{x} + b)。尽管之前没讨论过向量内积对向量的偏导数,但是考虑 w⋅x=n∑i(wixi)=sum(w⊗x) 而之前讨论过{\rm sum}(\boldsymbol{x})\boldsymbol{w} \otimes \boldsymbol{x}的偏导数和向量的链式法则,那么引入中间变量 u=w⊗xy=sum(u) 根据上面的推导,有 ∂u∂w=diag(x)∂y∂u=1 因此 ∂y∂w=∂y∂u∂u∂w=1⋅diag(x)=x 再令y = \boldsymbol{w} \cdot \boldsymbol{x} + b,不难得出 ∂y∂w=x,   ∂y∂b=1 激活函数是一个分段函数,且在0点不连续。分段求导可得 ∂∂zmax(0,z)={0z≤0dzdz=1z>0 因此 ∂∂xmax(0,x)=[∂∂x1max(0,x1)∂∂x2max(0,x2)⋮∂∂xnmax(0,xn)] 综合起来,对激活函数,引入中间变量z来表示仿射变换,有 z(w,b,x)=w⋅x+bactivation(z)=max(0,z) 通过链式法则 ∂activation∂w=∂activation∂z∂z∂w 代入前面的推导,有 ∂activation∂w={0∂z∂w=0z≤01∂z∂w=xz>0∂activation∂b={0z≤01z>0

神经网络损失函数的梯度

最后考虑一个完整的例子。假设模型的输入是\boldsymbol{X},且 X=[x1,x2,…,xN]T

样本个数为N = |\boldsymbol{X}|,令结果向量为 y=[target(x1),target(x2),…,target(xN)]T 其中每个y_i都是一个标量。假设损失函数使用平方误差函数,那么损失函数C为 C(w,b,X,y)=1NN∑i=1(yi−activation(xi))2=1NN∑i=1(yi−max(0,w⋅xi+b))2 引入中间变量,有 u(w,b,x)=max(0,w⋅x+b)v(y,u)=y−uC(v)=1NN∑i=1v2 这里主要说明如何计算权重的梯度。偏置b的计算方法同理。由前面的推导可知 ∂∂wu(w,b,x)={0w⋅x+b≤0xw⋅x+b>0 而 ∂v(y,u)∂w=∂∂w(y−u)=0−∂u∂w=−∂u∂w={0w⋅x+b≤0−xw⋅x+b>0 因此,整个梯度的计算过程为 ∂C(v)∂w=∂∂w1NN∑i=1v2=1NN∑i=1∂v2∂v∂v∂w=1NN∑i=12v∂v∂w 中间展开过程略,最后可得 ∂C(v)∂w={0w⋅xi+b≤02N∑Ni=1(w⋅xi+b−yi)xiw⋅xi+b>0 后面关于梯度下降的讨论略


今天看啥 - 高品质阅读平台
本文地址:http://www.jintiankansha.me/t/jaIPeGnvmt
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/25675
 
461 次点击