来源:原始 Jupyter Notebook。内容已转换为网页阅读格式;下载原文件。
我们前面在求解线性回归时,都是通过下面的这个被叫做正规方程的式子来计算线性回归的:
使用这种方法虽然简单,容易理解,但在现实生活中却有着很大的局限性。首先,并非所有的函数都可以通过求导来计算它的极值点,有些函数的导数根本不存在解析解;其次,在正规方程的计算过程中使用的矩阵 必须是可逆的,且不说有些矩阵是不可逆的,还有些矩阵的求逆非常困难,比如 希尔伯特矩阵;另外,当数据量或维度不断增加时,矩阵计算的开销也会越来越大,要知道,矩阵求逆的时间复杂度是矩阵维度的三次方,因此当数据量过大时,使用正规方程会非常耗时,数据量如果只有成百上千,可能影响并不大,一旦数据量达到万级以上,使用正规方程的性能问题就会凸显出来。
我们以一元线性回归为例,在前面的最小二乘法求解过程中我们知道,求解一元线性回归可以转化为求解损失函数的最小值,损失函数是一个关于参数 a, b 的二元二次函数:
它是三维空间里的一个曲面,图像类似于下面左图这样:

右图代表的是损失函数的等高图,每一条线表示损失函数的值相等,红色的叉号表示损失函数的最小值。要求这个最小值,除了前面介绍的正规方程法,这一节我们将学习一种新的方法,梯度下降(Gradient Descent),它比正规方程更具有广泛性,可以用于很多复杂算法的求解。
梯度下降算法的基本思路是,在坡上任意取一点,然后沿着下坡方向走最后到达最低点,为了走的最快,我们每次都沿着最陡的方向下坡,这样不断的迭代,直到损失函数收敛到最小值。
什么是梯度?
为了更好的理解梯度下降,我们来看看什么是梯度(gradient),而要知道什么是梯度,还得从函数的导数(derivative)说起。
导数的公式如下所示:
它可以表示函数在某点的切线斜率,也就是函数在该点附近的变化率,如果导数大于零,那么函数在区间内单调递增,如果导数小于零,函数在区间内则是单调递减。

上面是一个一元函数的导数示例,当扩展到二元或更多元的情况时,就出现了 偏导数(partial derivative)和 方向导数(directional derivative),偏导数是多元函数沿坐标轴的变化率,而方向导数则是多元函数沿任意方向的变化率。
从上面的导数定义可以看出,导数是一个数,包括偏导数和方向导数也是一个数,代表的是函数沿某个方向的变化率。梯度并不是一个数,而是一个向量,它代表的是在各个导数中,变化趋势最大的那个方向。梯度的数学定义为:
设函数 在平面区域 内具有一阶连续偏导数,则对于每一点 属于 ,都可定义一个向量:
这个向量被称为函数 在点 处的梯度,记作 ,即:
有时候,梯度也可以用 来表示:
其中, 被称为 向量微分算子或 Nabla 算子。
可以看到梯度就是对各个坐标轴分别求偏导,得到的一个向量,下面是一个例子,假设 是一个三元一次函数:
该函数的梯度为:
那么为什么说梯度是变化趋势最大的方向呢?可以参考这里。总之,沿着梯度方向,函数会以最快的速度到达极大值,而沿着负梯度方向,函数会以最快的速度到达极小值。如果这个函数是一个凸函数,那么极值点就是最值点。假设上面的 就是我们的损失函数,且它是一个凸函数,那么我们就可以通过沿着负梯度方向,不断迭代,最终收敛在最小值。

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

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