梯度下降法(Gradient Descent)是一个算法,但不同于解析解法一般局限,相反,他是一个非常通用的优化算法来帮助一些机器学习求出最优解。
解析解可以成功的原因是MSE损失函数是凸函数,即Hessian矩阵半正定,但假使损失函数非凸,解析解就比较局限。而且解析解的运算较为复杂,时间复杂度达到 。所以解析解法在涉及数据较大时并不好用。
梯度下降法是一点点逼近最优解的方法
编辑
梯度下降公式
这里梯度下降公式仅仅只是一个指导计算机迭代过程中如何去调整θ变化的式子,不需要推导和证明。其中 为学习率(learning rate)。学习率一般为正数。在最优解左侧,梯度小于0,学习率大于0,Wj递增,靠近最优解。同理,在最优解右侧,梯度大于0,学习率大于0,Wj递减,同样会靠近最优解。
学习率设置
编辑
如上图所示,小学习率比较稳定,但是迭代次数会比较大,如果遇到鞍面会停留很长时间。
适中的学习率迭代次数更少而且可以跳出局部最优解去寻找更好的解,但是可能会出现达不到最优解的情况。大学习率会发散,损失函数会越来越大。这种是不可取的学习率。最后是最优学习率(一般不考虑),即一步直接到最优解。
编辑
我们可以设置学习率随着迭代次数减小,这样既可以跳出局部最优解,同时也保证可以收敛到最优解。这个过程我们称为学习速率调整(又称学习速率调度):一般使用某种事先设定好的策略或者在每次迭代中衰减一个减小的阈值。
梯度下降的流程(附代码)
Step 1. 随机θ参数
我们先创建一组随机的θ参数作为起始点
import numpy as np theta = np.random.randn(2,1)#创建一组服从正态分布的参数
这里,我们创建一个两行一列的参数矩阵。
Step 2. 求解梯度
简而言之,假设X(mxn),W(nx1)某个维度j的梯度gradient(j) = ε(mx1)乘上X的第j列,这显然不符合矩阵相乘,所以真实情况是需要将Xj转置。
import numpy as np gradients = X_b.T.dot((X_b.dot(theta) - y))
Step 3. 调整θ参数
判断收敛时使用g= 0并不合理,因为当损失函数时非凸函数的话,g = 0只是一个驻点,还可能是最大值,这不能说明什么。其实判断loss下降收益更加合理,当收益不再变化就可以认为达到局部最优解。即
theta = theta - learning_rate * gradients
梯度下降的分类
区别:三种梯度下降的区别仅在于第二步求解梯度所用到的X数据集的样本数量不同,每次学习(更新模型参数)使用的样本数目,每次使用不同的样本会导致每次学习的准确性和学习时间不同。
全量梯度下降(Batch Gradient Descent)
在全量梯度下降中,对于θ的更新,所有样本都有贡献,也就是参与调整θ。其计算得到的是一个标准梯度。因而理论上来说一次更新的幅度是比较大的。如果样本不多的情况下,这样的收敛速度会更快,优点在于每次更新都会朝着正确的方向进行,最终收敛于极值点,缺点就是时间比较长。
import numpy as np #全量梯度下降 X = np.random.rand(100,1) y = 4+3*X + np.random.randn(100,1) X_b = np.c_[np.ones((100,1)),X] #创建超参数 n_iterations = 10000 t0,t1 = 5, 500 #定义一个函数来动态调整学习率 def learning_rate_schedule(t): return t0/(t+t1) #初始化theta theta = np.random.randn(2,1) truth =np.array([[4],[3]]) #判断是否收敛,一般不设置阈值,而是采用相对大的迭代次数保证收敛 for i in range(n_iterations): # 求梯度,计算gradient gradients = X_b.T.dot((X_b.dot(theta) - y)) # 一个含有g0和g1的列向量 # 应用梯度下降法的公式去调整theta值 learning_rate = learning_rate_schedule(i) theta = theta - learning_rate * gradients print(theta) print("Error(%):") print("W0=",((abs(theta-truth)[0,:]/abs(truth)[0,:])*100)[0],"%") print("W1=",((abs(theta-truth)[1,:]/abs(truth)[1,:])*100)[0],"%")
随机梯度下降(Stochastic Gradient Descent)
梯度下降算法每次从训练集中随机选择一个样本来进行学习
优点:学习非常迅速,可能会选择更好的局部最优解。
缺点:每次更新可能并不会按照正确方向进行,因此带来优化波动,迭代次数增加。
import numpy as np X = np.random.rand(100,1) y = 4 + 3*X + np.random.randn(100,1) X_b = np.c_[np.ones((100,1)),X] n_epochs = 10000 m = 100 truth =np.array([[4],[3]]) t0,t1 = 5, 500 def learning_rate_schedule(t): return t0/(t+t1) theta = np.random.randn(2,1) for epoch in range(n_epochs): #每个轮次开始前打乱顺序 arr = np.arange(len(X_b)) np.random.shuffle(arr) X_b = X_b[arr] y = y[arr] for i in range(m): xi = X_b[i:i+1]#左闭右开 yi = y[i:i+1] gradients = xi.T.dot((xi.dot(theta) - yi)) learning_rate = learning_rate_schedule(epoch*m + i) theta = theta - learning_rate*gradients print(theta) print("Error(%):") print("W0=",((abs(theta-truth)[0,:]/abs(truth)[0,:])*100)[0],"%") print("W1=",((abs(theta-truth)[1,:]/abs(truth)[1,:])*100)[0],"%")
编辑
小批量梯度下降(Mini-Batch Gradient Descent)
Mini-Batch梯度下降综合了batch梯度下降与stochastic梯度下降,在每次更新速度与更新次数中间取得一个平衡,其每次更新从训练集中随机选择batch-size个样本进行学习,使得学习更加稳定。
import numpy as np X = 2*np.random.rand(100,1) y = 4+3*X + np.random.randn(100,1) X_b = np.c_[np.ones((100,1)),X] learning_rate = 0.0001 n_epochs = 10000 m = 100 batch_size = 10 num_batches = int(m/batch_size) theta = np.random.randn(2,1) truth =np.array([[4],[3]]) for epoch in range(n_epochs): arr = np.arange(len(X_b)) np.random.shuffle(arr) X_b = X_b[arr] y = y[arr] for i in range(num_batches): selected_index = np.random.randint(m) x_batch = X_b[i:i+batch_size] y_batch = y[i:i+batch_size] gradients = x_batch.T.dot((x_batch.dot(theta) - y_batch)) theta = theta - learning_rate * gradients print(theta) print("Error(%):") print("W0=",((abs(theta-truth)[0,:]/abs(truth)[0,:])*100)[0],"%") print("W1=",((abs(theta-truth)[1,:]/abs(truth)[1,:])*100)[0],"%")
编辑