机器学习系列(十七)——关于梯度下降法的更多思考
本篇主要内容是梯度下降法的总结。
梯度的调试
实际编程实现中,梯度下降法很可能因为编写的某个公式的运算错误,导致梯度运算的错误,但是在符合运算规则能计算的情况下,计算机是不会报错的。这就需要提前对梯度进行调试,及时了解梯度的数学表达式是否正确。
梯度调试以二维平面中的曲线为例,如上图所示,调试的一个简单办法是找到切点附近(两侧)的两个点,用它们连线的割线斜率近似代替该点的切线斜率,在数学中我们都知道,对于可导曲线,当取的两个点距离无限逼近切点时,这个割线斜率就是切线斜率。
对于高维度的情形(多特征)可以类似的推广过去。其实从上面的计算也可以发现,在真正的梯度计算中用这种方式一样是可以的,而且形式相比于向量表达式要简单很多,不过由于它增加了时间复杂度,因此一般多在调试中使用该方法。
调试的一般步骤是先用调试方法求出正确的解,再将真实的数学向量解编写进程序进行求解,如果数学向量解表达式没有问题,则会得到同样正确的结果,而且速度更快。否则可能是向量解有问题,检查向量解表达式,修改至正确。
下面在模拟数据集上使用调试:
'''模拟数据集'''
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(666)
X=np.random.random(size=(1000,10))
true_theta = np.arange(1,12,dtype=float)
X_b = np.hstack([np.ones((len(X),1)),X])
y = X_b.dot(true_theta) + np.random.normal(size=1000)
由于代码比较简单,不再赘述数据集情况,阅读代码显然。继续使用批量梯度下降的代码,添加dJ_debug函数作为调试方法:
'''修改dJ函数,增加调试dJ_debug函数'''
def J(theta, X_b, y):
try:
return np.sum((y - X_b.dot(theta))**2)/len(X_b)
except:
return float('inf')
def dJ_math(theta,X_b,y):
return X_b.T.dot(X_b.dot(theta)-y)*2./len(y)
def dJ_debug(theta,X_b,y,epsilon=0.01):
res = np.empty(len(theta))
for i in range(len(theta)):
theta_1 = theta.copy()
theta_1[i] += epsilon
theta_2 = theta.copy()
theta_2[i] -= epsilon
res[i] = (J(theta_1,X_b,y) - J(theta_2,X_b,y))/(2*epsilon)
return res
'''使用批量梯度下降法'''
def gradient_descent(dJ, X_b, y, initial_theta, eta, n_iters = 1e4, epsilon = 1e-5):
theta = initial_theta
i_iter = 0
while i_iter < n_iters:
gradient = dJ(theta, X_b, y)
last_theta = theta
theta = theta - eta * gradient#参数更新
if (abs(J(theta, X_b, y)-J(last_theta, X_b, y))<epsilon):
break
i_iter += 1
return theta
调试训练结果:
'''调试过程'''
initial_theta = np.zeros(X_b.shape[1])
eta = 0.01
%time theta = gradient_descent(dJ_debug,X_b,y,initial_theta,eta)
调试result
批量梯度下降训练结果:
实际训练结果可以发现,调试过程和实际训练过程都得到了正确的结果,调试过程的时间消耗确实要高于实际向量化的批量梯度下降过程。
不过值得一提的是,dJ_debug的实现是不依赖具体的损失函数J的表达式的,它适用于所有函数,因此可以被复用,尤其是遇到梯度不太好求的损失函数,或者梯度表达式极其复杂。这种情况下可以偷懒一下,以少量的时间性能换取便利。
梯度下降法的总结
小批量梯度下降Mini-Batch Gradient Descent
在之前已经介绍过Batch Gradient Descent和Stochastic Gradient Descent,其中BGD是能稳定收敛找到最优解的,不过由于梯度每个分量都要所有样本参与计算,速度和时间消耗比较大;SGD虽然速度很快,更容易跳出局部最优解,但是不能保证一定收敛,稳定性欠佳。于是Mini-Batch Gradient Descent应运而生,它是以上两种方法的综合。在MGD中,相比SGD会多出来一个超参数:每次的小批量k是多少,它的选取比较灵活。有了BGD和SGD的基础,MGD具体实现不是太难,可以自己动手实现。在以后的机器学习算法中还会有很多取两种算法结合折中的算法。
最后,再强调一下梯度下降法本身不是一个机器学习算法,它只是一种基于搜索的最优化方法,作用是最小化一个损失函数。同样还有梯度上升法,在后面PCA算法的实现中将会使用梯度上升法。