来源:原始 Jupyter Notebook。内容已转换为网页阅读格式;下载原文件

我们前面在求解线性回归时,都是通过下面的这个被叫做正规方程的式子来计算线性回归的:

a=(XTX)1XTy \bf{a} = (\rm{X}^T\rm{X})^{-1}\rm{X}^T\bf{y}

使用这种方法虽然简单,容易理解,但在现实生活中却有着很大的局限性。首先,并非所有的函数都可以通过求导来计算它的极值点,有些函数的导数根本不存在解析解;其次,在正规方程的计算过程中使用的矩阵 XTX\rm{X}^T\rm{X} 必须是可逆的,且不说有些矩阵是不可逆的,还有些矩阵的求逆非常困难,比如 希尔伯特矩阵;另外,当数据量或维度不断增加时,矩阵计算的开销也会越来越大,要知道,矩阵求逆的时间复杂度是矩阵维度的三次方,因此当数据量过大时,使用正规方程会非常耗时,数据量如果只有成百上千,可能影响并不大,一旦数据量达到万级以上,使用正规方程的性能问题就会凸显出来。

我们以一元线性回归为例,在前面的最小二乘法求解过程中我们知道,求解一元线性回归可以转化为求解损失函数的最小值,损失函数是一个关于参数 a, b 的二元二次函数:

loss=ny2ˉ+a2nx2ˉ+nb2+2abnxˉ2anxyˉ2bnyˉ loss = n\bar{y^2} + a^2n\bar{x^2} + nb^2 + 2abn\bar{x} - 2an\bar{xy} - 2bn\bar{y}

它是三维空间里的一个曲面,图像类似于下面左图这样:

右图代表的是损失函数的等高图,每一条线表示损失函数的值相等,红色的叉号表示损失函数的最小值。要求这个最小值,除了前面介绍的正规方程法,这一节我们将学习一种新的方法,梯度下降(Gradient Descent),它比正规方程更具有广泛性,可以用于很多复杂算法的求解。

梯度下降算法的基本思路是,在坡上任意取一点,然后沿着下坡方向走最后到达最低点,为了走的最快,我们每次都沿着最陡的方向下坡,这样不断的迭代,直到损失函数收敛到最小值。

什么是梯度?

为了更好的理解梯度下降,我们来看看什么是梯度(gradient),而要知道什么是梯度,还得从函数的导数(derivative)说起。

导数的公式如下所示:

f(x)=limΔx0ΔyΔx=limΔx0f(x0+Δx)f(x0)Δx f'(x) = \lim\limits_{\Delta x \to 0}\frac{\Delta y}{\Delta x} = \lim\limits_{\Delta x \to 0}\frac{f(x_0 + \Delta x) - f(x_0)}{\Delta x}

它可以表示函数在某点的切线斜率,也就是函数在该点附近的变化率,如果导数大于零,那么函数在区间内单调递增,如果导数小于零,函数在区间内则是单调递减。

上面是一个一元函数的导数示例,当扩展到二元或更多元的情况时,就出现了 偏导数(partial derivative)和 方向导数(directional derivative),偏导数是多元函数沿坐标轴的变化率,而方向导数则是多元函数沿任意方向的变化率。

从上面的导数定义可以看出,导数是一个数,包括偏导数和方向导数也是一个数,代表的是函数沿某个方向的变化率。梯度并不是一个数,而是一个向量,它代表的是在各个导数中,变化趋势最大的那个方向。梯度的数学定义为:

设函数 z=f(x,y)z = f(x, y) 在平面区域 DD 内具有一阶连续偏导数,则对于每一点 (x0,y0)(x_0, y_0) 属于 DD,都可定义一个向量:

f(x0,y0)xi+f(x0,y0)yj \frac{\partial{f(x_0, y_0)}}{\partial x}i + \frac{\partial{f(x_0, y_0)}}{\partial y}j

这个向量被称为函数 z=f(x,y)z = f(x, y) 在点 (x0,y0)(x_0, y_0) 处的梯度,记作 gradf(x0,y0)grad f(x_0, y_0),即:

gradf(x0,y0)=f(x0,y0)xi+f(x0,y0)yj=fx(x0,y0)i+fy(x0,y0)j \begin{align} grad f(x_0, y_0) &= \frac{\partial{f(x_0, y_0)}}{\partial x}i + \frac{\partial{f(x_0, y_0)}}{\partial y}j \\ &= f_x(x_0, y_0)i + f_y(x_0, y_0)j \end{align}

有时候,梯度也可以用 \nabla 来表示:

gradf(x0,y0)=f(x0,y0) grad f(x_0, y_0) = \nabla f(x_0, y_0)

其中,xi+yj\frac{\partial}{\partial x}i + \frac{\partial}{\partial y}j 被称为 向量微分算子Nabla 算子

可以看到梯度就是对各个坐标轴分别求偏导,得到的一个向量,下面是一个例子,假设 J(θ)J(\theta) 是一个三元一次函数:

J(θ)=5θ1+2θ212θ3 J(\theta) = 5\theta_1 + 2\theta_2 - 12\theta_3

该函数的梯度为:

\[ \nabla J(\theta) = \langle \frac{\partial J}{\partial \theta_1}, \frac{\partial J}{\partial \theta_2}, \frac{\partial J}{\partial \theta_3} \rangle \\ = \langle 5, 2, -12 \rangle \]

那么为什么说梯度是变化趋势最大的方向呢?可以参考这里。总之,沿着梯度方向,函数会以最快的速度到达极大值,而沿着负梯度方向,函数会以最快的速度到达极小值。如果这个函数是一个凸函数,那么极值点就是最值点。假设上面的 f(x,y)f(x,y) 就是我们的损失函数,且它是一个凸函数,那么我们就可以通过沿着负梯度方向,不断迭代,最终收敛在最小值。

不过,如果损失函数是一个非凸函数,则可能会存在多个极值点,使用梯度下降法收敛的结果可能只是一个局部最小值,而不是全局最小值,譬如下图中的两种情况,可以看出,选择的初始点不同,计算出的极值点也不尽相同。

这是梯度下降法的一个最主要的缺点,对初始点的选择极为敏感,不可避免的会存在陷入局部极小值的情形,梯度下降另一个缺点是,当到达最小点附近的时候收敛速度会变的很慢,所以针对这两个缺点,后来又提出了很多改进算法,比如 拟牛顿法共轭梯度法 等。