使用sympy计算梯度与海森矩阵

在机器学习中,我们通常需要对问题进行建模,然后可以得到一个成本函数(cost function),通过对这个成本函数进行最小化,我们可以得到我们所需要的参数,从而得到具体的模型。这些优化问题中,只有少部分可以得到解析解(如最小二乘法),而大部分这类优化问题只能迭代求解,而迭代求解中两种最常用的方法即梯度下降法与牛顿法。为了使用这两种方法,我们需要计算成本函数的梯度与海森矩阵,这篇文章简要说明了如何使用python的符号计算库sympy来计算特定函数的梯度与海森矩阵,求解机器学习中的最优化问题。

对梯度的直观理解

梯度概念是建立在偏导数与方向导数概念基础上的。所谓偏导数,简单来说是对于一个多元函数,选定一个自变量并让其他自变量保持不变,只考察因变量与选定自变量的变化关系。数学上说,是指对于多元函数,假设其偏导数都存在,则该函数共有个偏导数,可以表示为:

偏导数只能表示多元函数沿某个坐标轴方向的导数,如对于二元函数表示函数沿轴方向的导数,而表示函数沿轴方向的导数。

除开沿坐标轴方向上的导数,多元函数在非坐标轴方向上也可以求导数,这种导数称为方向导数。很容易发现,多元函数在特定点的方向导数有无穷多个,表示函数值在各个方向上的增长速度。一个很自然的问题是:在这些方向导数中,是否存在一个最大的方向导数,如果有,其值是否唯一?为了回答这个问题,便需要引入梯度的概念。

一般来说,梯度可以定义为一个函数的全部偏导数构成的向量(这一点与偏导数与方向导数不同,两者都为标量)。一般将函数的梯度记为,即:

图片

事实上,梯度向量的方向即为函数值增长最快的方向,为什么会如此,可以从几个角度去理解。

图片

在上图中,我们可以看到,为了找到方向导数中的最大值,我们可以将其投影到平面来理解,这种投影方式对应的便是等高线图。如对于一个二元函数,我们可以画出它的等高线图如下:

图片

该函数的等高线图为圆心在原点的一系列圆,等高线的值由里向外逐渐增加。点为点平面上的投影,可以看到向量即为函数在点处的梯度向量。根据方向导数的定义,方向导数,其中为此向量与正方向的夹角。由于梯度向量为,单位向量,则方向导数的大小可以表述为梯度向量与此单位向量的数量积,即:

其中为梯度向量与单位向量之间的夹角,即。可以看出,方向导数的大小可以看作梯度向量在指示方向导数方向的单位向量上的投影,即线段的长度。显而易见,线段的长度小于线段的长度,也即梯度向量的模总是大于等于方向导数向量的模。这就解释了为什么沿着梯度向量方向是函数值增长最快的方向,而它正是函数所有偏导数构成的向量。

在上图中也可以看出,梯度向量垂直于等高线,这为我们提供了另一个观察梯度向量的角度。如对于函数,其等高线图与梯度向量如下:

图片

我们可以两个角度考虑:第一,在特定函数点,固定每次移动的步长,向那个方向移动函数值增长最快?第二,固定需要增加的函数值,向哪个方向需要移动的步长最短?

图片

在左图中,固定移动的步长,我们可以看到垂直于等高线图的方向即为函数值增长最快的方向,也就是梯度向量指示的方向。在右图中,假设函数值有一个固定的微小的增长,则明显梯度向量指示的方向所需要的步长最短,而这个向量也是垂直于等高线的。

梯度下降或上升法正是基于梯度指示函数值增长最快的方向而产生的,利用这个方法,我们可以使用迭代的方法计算函数的最大或最小值,从而解决机器学习中遇到的最优化问题。

使用sympy计算函数的梯度与海森矩阵

sympy是python的开源符号计算库,它的主要功能与Mathematica类似,但相比于Mathematica它也有一些优势:它是免费的,而Mathematica相当昂贵;轻量,对于日常符号计算,它提供的功能已经够用,而安装起来也很容易;它是一个python库,这意味着你可以与你其它的python程序一起使用,任意扩展它的功能。对于sympy的使用,完整的介绍可以阅读它的文档,这里只简要介绍它的基本用法。

与mathematica不同,sympy在使用符号前需要先创建,如下:

 

然后你可以利用这些符号创建其它的表达式,如函数

 

你可以利用这个表达式生成新的表达式:

 

,而。我们可以求函数的微分:

 

分别表示,可以看出二阶导数可以有两种写法。

当我们求出表达式后,我们可能想要代入数值计算,这可以使用lambdify函数来实现:

 

这表示

为了求函数的梯度与海森矩阵,我们需要引入向量微分的概念,即我们可以将梯度看作一个数量函数对自变量向量的上阶导数,而海森矩阵为数量函数对自变量量向量的二阶导数,设函数为,则自变量向量为,则梯度与海森矩阵可以分别表述为

在sympy中,可以通过函数derive_by_array实现上述计算:

 

其中Matrix类可以将结果表达为sympy中的矩阵形式。上面的计算结果分别为:, ,分别为函数的梯度向量与海森矩阵。

为了简化计算梯度与海森矩阵的代码,可以编写一个自定义函数对相应功能进行封装,如下:

 

如对于函数,则有:

 

则梯度为:

则海森矩阵为:

可以看出,使用sympy计算函数的梯度与海森矩阵相当便捷,省去了繁琐易错的手工微分过程。对于以上计算结果,我们只需要使用lambdify函数代入数值就可以得到梯度向量与海森矩阵的数值结果,从而为实现梯度下降法与牛顿法提供基础。