人工智能/模式识别/机器学习精华专题大数据,机器学习,人工智能机器学习与数据挖掘

机器学习系列(十七)——关于梯度下降法的更多思考

2019-07-11  本文已影响1人  Ice_spring

本篇主要内容是梯度下降法的总结。

梯度的调试

实际编程实现中,梯度下降法很可能因为编写的某个公式的运算错误,导致梯度运算的错误,但是在符合运算规则能计算的情况下,计算机是不会报错的。这就需要提前对梯度进行调试,及时了解梯度的数学表达式是否正确。

梯度调试

以二维平面中的曲线为例,如上图所示,调试的一个简单办法是找到切点附近(两侧)的两个点,用它们连线的割线斜率近似代替该点的切线斜率,在数学中我们都知道,对于可导曲线,当取的两个点距离无限逼近切点时,这个割线斜率就是切线斜率。
\frac{dJ}{d\theta}=\frac{J(\theta+\varepsilon)-J(\theta-\varepsilon)}{2\varepsilon}

对于高维度的情形(多特征)可以类似的推广过去。其实从上面的计算也可以发现,在真正的梯度计算中用这种方式一样是可以的,而且形式相比于向量表达式要简单很多,不过由于它增加了时间复杂度,因此一般多在调试中使用该方法。
调试的一般步骤是先用调试方法求出正确的解,再将真实的数学向量解编写进程序进行求解,如果数学向量解表达式没有问题,则会得到同样正确的结果,而且速度更快。否则可能是向量解有问题,检查向量解表达式,修改至正确。
下面在模拟数据集上使用调试:

'''模拟数据集'''
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算法的实现中将会使用梯度上升法。

上一篇 下一篇

猜你喜欢

热点阅读