基于Python从头开始使用 RMSProp 进行梯度下降
2021/5/31 20:23:29
本文主要是介绍基于Python从头开始使用 RMSProp 进行梯度下降,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
【翻译自 : Gradient Descent With RMSProp from Scratch】
【说明:Jason Brownlee PhD大神的文章个人很喜欢,所以闲暇时间里会做一点翻译和学习实践的工作,这里是相应工作的实践记录,希望能帮到有需要的人!】
梯度下降是一种优化算法,它遵循目标函数的负梯度以定位函数的最小值。
梯度下降的一个限制是它对每个输入变量使用相同的步长(学习率)。简而言之,AdaGrad 是梯度下降优化算法的扩展,它允许优化算法使用的每个维度中的步长根据搜索过程中针对变量(偏导数)看到的梯度进行自动调整。
AdaGrad 的一个限制是,它可能导致在搜索结束时每个参数的步长非常小,这会大大减慢搜索的进度,并可能意味着无法找到最优值。
均方根传播(或 RMSProp)是梯度下降法和梯度下降法的 AdaGrad 版本的扩展,它使用部分梯度的衰减平均值来适应每个参数的步长。使用衰减移动平均允许算法忘记早期梯度并专注于在搜索过程中看到的最近观察到的部分梯度,克服了 AdaGrad 的限制。
在本教程中,您将了解如何使用 RMSProp 优化算法从头开始开发梯度下降。完成本教程后,您将了解:
梯度下降是一种优化算法,它使用目标函数的梯度来导航搜索空间。 可以使用偏导数的衰减移动平均值(称为 RMSProp)更新梯度下降以对每个输入变量使用自动自适应步长。 如何从头开始实现 RMSProp 优化算法并将其应用于目标函数并评估结果。
教程概述
本教程分为三个部分; 他们是:
梯度下降 均方根传播 (RMSProp) 使用 RMSProp 进行梯度下降 二维测试题 使用 RMSProp 进行梯度下降优化 RMSProp 的可视化
梯度下降
梯度下降是一种优化算法。它在技术上被称为一阶优化算法,因为它明确地利用了目标目标函数的一阶导数。
一阶导数,或简称“导数”,是目标函数在特定点的变化率或斜率,例如对于特定的输入。
如果目标函数接受多个输入变量,它被称为多元函数,输入变量可以被认为是一个向量。反过来,多元目标函数的导数也可以作为向量,一般称为梯度。
梯度:多元目标函数的一阶导数。
对于特定输入,导数或梯度指向目标函数最陡峭上升的方向。
梯度下降是指一种最小化优化算法,它遵循目标函数的梯度下坡的负数来定位函数的最小值。梯度下降算法需要一个正在优化的目标函数和目标函数的导数函数。目标函数 f() 返回给定输入集的分数,而导数函数 f'() 给出目标函数对给定输入集的导数。梯度下降算法需要问题中的一个起点 (x),例如输入空间中随机选择的点。然后计算导数并在输入空间中采取一步,假设我们正在最小化目标函数,预计会导致目标函数中的下坡运动。
下坡运动是通过首先计算输入空间中的运动距离来进行的,计算方法是将步长(称为alpha或学习率)乘以坡度。然后从当前点中减去它,确保我们逆着梯度移动,或者沿着目标函数向下移动。
x = x – step_size * f'(x)
给定点的目标函数越陡峭,梯度的幅度就越大,反过来,在搜索空间中采取的步长就越大。所采取步骤的大小使用步长超参数进行缩放。
步长 (alpha):控制算法每次迭代在搜索空间中针对梯度移动多远的超参数。
如果步长太小,搜索空间中的移动会很小,搜索时间会很长。如果步长太大,搜索可能会在搜索空间周围反弹并跳过最优值。
现在我们熟悉了梯度下降优化算法,让我们来看看 RMSProp。
均方根传播 (RMSProp)
均方根传播,简称RMSProp,是梯度下降优化算法的扩展。
这是一个未发表的扩展,首先在 Geoffrey Hinton 为他的 Coursera 神经网络课程讲义中描述,特别是第 6e 课题为“rmsprop:将梯度除以最近幅度的运行平均值”。
RMSProp 旨在加速优化过程,例如减少达到最优值所需的函数评估次数,或提高优化算法的能力,例如得到更好的最终结果。它与梯度下降的另一个扩展有关,称为自适应梯度或 AdaGrad。
AdaGrad 旨在专门探索为搜索空间中的每个参数自动定制步长(学习率)的想法。这是通过首先计算给定维度的步长,然后使用计算的步长使用偏导数在该维度上进行移动来实现的。然后对搜索空间中的每个维度重复此过程。
Adagrad 计算每个参数的步长,首先将搜索期间看到的参数的偏导数求和,然后将初始步长超参数除以偏导数平方和的平方根。
一个参数的自定义步长计算如下:
cust_step_size = step_size / (1e-8 + sqrt(s))
其中 cust_step_size 是搜索期间给定点的输入变量的计算步长,step_size 是初始步长,sqrt() 是平方根运算,s 是在搜索过程中看到的输入变量的平方偏导数之和到目前为止的搜索。这具有平滑搜索优化问题中的振荡的效果,这些优化问题在搜索空间中具有大量曲率。
AdaGrad 的一个问题是它会使搜索速度减慢太多,导致在运行结束时每个参数或搜索维度的学习率非常小。这会导致在找到最小值之前过早停止搜索。
RMSProp 可以被认为是 AdaGrad 的扩展,因为它在计算每个参数的学习率时使用偏导数的衰减平均值或移动平均值而不是总和。
这是通过添加一个我们称为 rho 的新超参数来实现的,它的作用类似于偏导数的动量。使用偏导数的衰减移动平均允许搜索忘记早期的偏导数值并专注于搜索空间的最近看到的形状。
一个参数的均方偏导数的计算如下:
s(t+1) = (s(t) * rho) + (f'(x(t))^2 * (1.0-rho))
其中 s(t+1) 是算法当前迭代的一个参数的平方偏导数的衰减移动平均值,s(t) 是前一次迭代的衰减移动平均平方偏导数,f'(x( t))^ 2是当前参数的平方偏导数,而rho是超参数,通常像动量一样为0.9。
鉴于我们正在使用偏导数的衰减平均值并计算该平均值的平方根,从而为该技术命名,例如均方偏导数的平方根或均方根 (RMS)。例如,参数的自定义步长可以写为:
cust_step_size(t+1) = step_size / (1e-8 + RMS(s(t+1)))
一旦我们有了参数的自定义步长,我们就可以使用自定义步长和偏导数 f'(x(t)) 更新参数。
x(t+1) = x(t) – cust_step_size(t+1) * f'(x(t))
然后对每个输入变量重复此过程,直到在搜索空间中创建一个新点并可以对其进行评估。RMSProp 是梯度下降的一种非常有效的扩展,是通常用于拟合深度学习神经网络的首选方法之一。现在我们熟悉了 RMSprop 算法,让我们探索如何实现它并评估它的性能。
使用 RMSProp 进行梯度下降
在本节中,我们将探讨如何使用 RMSProp 算法实现具有自适应梯度的梯度下降优化算法。
二维测试题
首先,让我们定义一个优化函数。我们将使用一个简单的二维函数,对每个维度的输入进行平方,并定义从 -1.0 到 1.0 的有效输入范围。
下面的objective()函数实现了这个函数
# objective function def objective(x, y): return x**2.0 + y**2.0
我们可以创建数据集的三维图来感受响应面的曲率。
下面列出了绘制目标函数的完整示例。
# 3d plot of the test function from numpy import arange from numpy import meshgrid from matplotlib import pyplot # objective function def objective(x, y): return x**2.0 + y**2.0 # define range for input r_min, r_max = -1.0, 1.0 # sample input range uniformly at 0.1 increments xaxis = arange(r_min, r_max, 0.1) yaxis = arange(r_min, r_max, 0.1) # create a mesh from the axis x, y = meshgrid(xaxis, yaxis) # compute targets results = objective(x, y) # create a surface plot with the jet color scheme figure = pyplot.figure() axis = figure.gca(projection='3d') axis.plot_surface(x, y, results, cmap='jet') # show the plot pyplot.show()
运行该示例创建目标函数的三维曲面图。
我们可以看到熟悉的碗形状,在 f(0, 0) = 0 处具有全局最小值。
我们还可以创建函数的二维图。 这将在稍后我们想要绘制搜索进度时有所帮助。
下面的示例创建了目标函数的等高线图。
# contour plot of the test function from numpy import asarray from numpy import arange from numpy import meshgrid from matplotlib import pyplot # objective function def objective(x, y): return x**2.0 + y**2.0 # define range for input bounds = asarray([[-1.0, 1.0], [-1.0, 1.0]]) # sample input range uniformly at 0.1 increments xaxis = arange(bounds[0,0], bounds[0,1], 0.1) yaxis = arange(bounds[1,0], bounds[1,1], 0.1) # create a mesh from the axis x, y = meshgrid(xaxis, yaxis) # compute targets results = objective(x, y) # create a filled contour plot with 50 levels and jet color scheme pyplot.contourf(x, y, results, levels=50, cmap='jet') # show the plot pyplot.show()
运行该示例创建目标函数的二维等高线图。
我们可以看到碗的形状被压缩为带有颜色渐变的轮廓。 我们将使用此图绘制在搜索过程中探索的特定点。
现在我们有了一个测试目标函数,让我们看看如何实现 RMSProp 优化算法。
使用 RMSProp 进行梯度下降优化
我们可以将RMSProp的梯度下降应用于测试问题。首先,我们需要一个函数来计算这个函数的导数。
f(x) = x^2 f'(x) = x * 2
x ^ 2的导数在每个维度上均为x * 2。 衍生()函数在下面实现了这一点。
# derivative of objective function def derivative(x, y): return asarray([x * 2.0, y * 2.0])
接下来,我们可以实现梯度下降优化。首先,我们可以在问题范围内选择一个随机点作为搜索的起点。这假设我们有一个数组,该数组定义搜索的边界,每个维度有一行,第一列定义最小值,第二列定义维度的最大值。
# generate an initial point solution = bounds[:, 0] + rand(len(bounds)) * (bounds[:, 1] - bounds[:, 0])
接下来,我们需要将每个维度的平方偏导数的衰减平均值初始化为 0.0 值。
# list of the average square gradients for each variable sq_grad_avg = [0.0 for _ in range(bounds.shape[0])]
然后,我们可以枚举由“n_iter”超参数定义的搜索优化算法的固定迭代次数。
... # run the gradient descent for it in range(n_iter): ...
第一步是使用derivative()函数计算当前解的梯度。
# calculate gradient gradient = derivative(solution[0], solution[1])
然后,我们需要计算偏导数的平方,并使用“rho”超参数更新平方偏导数的衰减平均值。
# update the average of the squared partial derivatives for i in range(gradient.shape[0]): # calculate the squared gradient sg = gradient[i]**2.0 # update the moving average of the squared gradient sq_grad_avg[i] = (sq_grad_avg[i] * rho) + (sg * (1.0-rho))
然后我们可以使用平方偏导数和梯度的移动平均值来计算下一个点的步长。我们将一次执行一个变量,首先计算变量的步长,然后计算变量的新值。 这些值建立在一个数组中,直到我们有一个全新的解决方案,该解决方案使用自定义步长从当前点开始的最陡下降方向。
# build a solution one variable at a time new_solution = list() for i in range(solution.shape[0]): # calculate the step size for this variable alpha = step_size / (1e-8 + sqrt(sq_grad_avg[i])) # calculate the new position in this variable value = solution[i] - alpha * gradient[i] # store this variable new_solution.append(value)
然后可以使用objective() 函数评估这个新的解决方案,并且可以报告搜索的性能。
# evaluate candidate point solution = asarray(new_solution) solution_eval = objective(solution[0], solution[1]) # report progress print('>%d f(%s) = %.5f' % (it, solution, solution_eval))
就是这样。我们可以将所有这些结合到一个名为 rmsprop() 的函数中,该函数采用目标函数和导数函数的名称,一个具有域边界和算法迭代总数和初始学习率的超参数值的数组 ,并返回最终解决方案及其评估。下面列出了这个完整的功能。
# gradient descent algorithm with rmsprop def rmsprop(objective, derivative, bounds, n_iter, step_size, rho): # generate an initial point solution = bounds[:, 0] + rand(len(bounds)) * (bounds[:, 1] - bounds[:, 0]) # list of the average square gradients for each variable sq_grad_avg = [0.0 for _ in range(bounds.shape[0])] # run the gradient descent for it in range(n_iter): # calculate gradient gradient = derivative(solution[0], solution[1]) # update the average of the squared partial derivatives for i in range(gradient.shape[0]): # calculate the squared gradient sg = gradient[i]**2.0 # update the moving average of the squared gradient sq_grad_avg[i] = (sq_grad_avg[i] * rho) + (sg * (1.0-rho)) # build a solution one variable at a time new_solution = list() for i in range(solution.shape[0]): # calculate the step size for this variable alpha = step_size / (1e-8 + sqrt(sq_grad_avg[i])) # calculate the new position in this variable value = solution[i] - alpha * gradient[i] # store this variable new_solution.append(value) # evaluate candidate point solution = asarray(new_solution) solution_eval = objective(solution[0], solution[1]) # report progress print('>%d f(%s) = %.5f' % (it, solution, solution_eval)) return [solution, solution_eval]
注意:为了可读性,我们特意使用了列表和命令式编码风格,而不是矢量化操作。 随意将实现调整为使用 NumPy 数组的矢量化实现以获得更好的性能。
然后我们可以定义我们的超参数并调用 rmsprop() 函数来优化我们的测试目标函数。
在这种情况下,我们将使用算法的 50 次迭代,初始学习率为 0.01,rho 超参数的值为 0.99,所有这些都是经过一些反复试验后选择的。
# seed the pseudo random number generator seed(1) # define range for input bounds = asarray([[-1.0, 1.0], [-1.0, 1.0]]) # define the total iterations n_iter = 50 # define the step size step_size = 0.01 # momentum for rmsprop rho = 0.99 # perform the gradient descent search with rmsprop best, score = rmsprop(objective, derivative, bounds, n_iter, step_size, rho) print('Done!') print('f(%s) = %f' % (best, score))
将所有这些结合在一起,下面列出了使用 RMSProp 进行梯度下降优化的完整示例。
# gradient descent optimization with rmsprop for a two-dimensional test function from math import sqrt from numpy import asarray from numpy.random import rand from numpy.random import seed # objective function def objective(x, y): return x**2.0 + y**2.0 # derivative of objective function def derivative(x, y): return asarray([x * 2.0, y * 2.0]) # gradient descent algorithm with rmsprop def rmsprop(objective, derivative, bounds, n_iter, step_size, rho): # generate an initial point solution = bounds[:, 0] + rand(len(bounds)) * (bounds[:, 1] - bounds[:, 0]) # list of the average square gradients for each variable sq_grad_avg = [0.0 for _ in range(bounds.shape[0])] # run the gradient descent for it in range(n_iter): # calculate gradient gradient = derivative(solution[0], solution[1]) # update the average of the squared partial derivatives for i in range(gradient.shape[0]): # calculate the squared gradient sg = gradient[i]**2.0 # update the moving average of the squared gradient sq_grad_avg[i] = (sq_grad_avg[i] * rho) + (sg * (1.0-rho)) # build a solution one variable at a time new_solution = list() for i in range(solution.shape[0]): # calculate the step size for this variable alpha = step_size / (1e-8 + sqrt(sq_grad_avg[i])) # calculate the new position in this variable value = solution[i] - alpha * gradient[i] # store this variable new_solution.append(value) # evaluate candidate point solution = asarray(new_solution) solution_eval = objective(solution[0], solution[1]) # report progress print('>%d f(%s) = %.5f' % (it, solution, solution_eval)) return [solution, solution_eval] # seed the pseudo random number generator seed(1) # define range for input bounds = asarray([[-1.0, 1.0], [-1.0, 1.0]]) # define the total iterations n_iter = 50 # define the step size step_size = 0.01 # momentum for rmsprop rho = 0.99 # perform the gradient descent search with rmsprop best, score = rmsprop(objective, derivative, bounds, n_iter, step_size, rho) print('Done!') print('f(%s) = %f' % (best, score))
运行示例将 RMSProp 优化算法应用于我们的测试问题,并报告算法每次迭代的搜索性能。
注意:由于算法或评估程序的随机性或数值精度的差异,您的结果可能会有所不同。 考虑多次运行该示例并比较平均结果。
在这种情况下,我们可以看到在大约 33 次搜索迭代后找到了接近最优的解决方案,输入值接近 0.0 和 0.0,评估为 0.0。
>30 f([-9.61030898e-14 3.19352553e-03]) = 0.00001 >31 f([-3.42767893e-14 2.71513758e-03]) = 0.00001 >32 f([-1.21143047e-14 2.30636623e-03]) = 0.00001 >33 f([-4.24204875e-15 1.95738936e-03]) = 0.00000 >34 f([-1.47154482e-15 1.65972553e-03]) = 0.00000 >35 f([-5.05629595e-16 1.40605727e-03]) = 0.00000 >36 f([-1.72064649e-16 1.19007691e-03]) = 0.00000 >37 f([-5.79813754e-17 1.00635204e-03]) = 0.00000 >38 f([-1.93445677e-17 8.50208253e-04]) = 0.00000 >39 f([-6.38906842e-18 7.17626999e-04]) = 0.00000 >40 f([-2.08860690e-18 6.05156738e-04]) = 0.00000 >41 f([-6.75689941e-19 5.09835645e-04]) = 0.00000 >42 f([-2.16291217e-19 4.29124484e-04]) = 0.00000 >43 f([-6.84948980e-20 3.60848338e-04]) = 0.00000 >44 f([-2.14551097e-20 3.03146089e-04]) = 0.00000 >45 f([-6.64629576e-21 2.54426642e-04]) = 0.00000 >46 f([-2.03575780e-21 2.13331041e-04]) = 0.00000 >47 f([-6.16437387e-22 1.78699710e-04]) = 0.00000 >48 f([-1.84495110e-22 1.49544152e-04]) = 0.00000 >49 f([-5.45667355e-23 1.25022522e-04]) = 0.00000 Done! f([-5.45667355e-23 1.25022522e-04]) = 0.000000
RMSProp 的可视化
我们可以在域的等高线图上绘制搜索进度。这可以为算法的迭代中的搜索进度提供直觉。我们必须更新 rmsprop() 函数以维护搜索期间找到的所有解决方案的列表,然后在搜索结束时返回此列表。下面列出了具有这些更改的函数的更新版本。
# gradient descent algorithm with rmsprop def rmsprop(objective, derivative, bounds, n_iter, step_size, rho): # track all solutions solutions = list() # generate an initial point solution = bounds[:, 0] + rand(len(bounds)) * (bounds[:, 1] - bounds[:, 0]) # list of the average square gradients for each variable sq_grad_avg = [0.0 for _ in range(bounds.shape[0])] # run the gradient descent for it in range(n_iter): # calculate gradient gradient = derivative(solution[0], solution[1]) # update the average of the squared partial derivatives for i in range(gradient.shape[0]): # calculate the squared gradient sg = gradient[i]**2.0 # update the moving average of the squared gradient sq_grad_avg[i] = (sq_grad_avg[i] * rho) + (sg * (1.0-rho)) # build solution new_solution = list() for i in range(solution.shape[0]): # calculate the learning rate for this variable alpha = step_size / (1e-8 + sqrt(sq_grad_avg[i])) # calculate the new position in this variable value = solution[i] - alpha * gradient[i] new_solution.append(value) # store the new solution solution = asarray(new_solution) solutions.append(solution) # evaluate candidate point solution_eval = objective(solution[0], solution[1]) # report progress print('>%d f(%s) = %.5f' % (it, solution, solution_eval)) return solutions
然后,我们可以像以前一样执行搜索,这次将检索解决方案列表,而不是最佳的最终解决方案。
# seed the pseudo random number generator seed(1) # define range for input bounds = asarray([[-1.0, 1.0], [-1.0, 1.0]]) # define the total iterations n_iter = 50 # define the step size step_size = 0.01 # momentum for rmsprop rho = 0.99 # perform the gradient descent search with rmsprop solutions = rmsprop(objective, derivative, bounds, n_iter, step_size, rho)
然后我们可以像以前一样创建目标函数的等高线图。
# sample input range uniformly at 0.1 increments xaxis = arange(bounds[0,0], bounds[0,1], 0.1) yaxis = arange(bounds[1,0], bounds[1,1], 0.1) # create a mesh from the axis x, y = meshgrid(xaxis, yaxis) # compute targets results = objective(x, y) # create a filled contour plot with 50 levels and jet color scheme pyplot.contourf(x, y, results, levels=50, cmap='jet')
最后,我们可以将搜索过程中找到的每个解决方案绘制为由一条线连接的白点。
# plot the sample as black circles solutions = asarray(solutions) pyplot.plot(solutions[:, 0], solutions[:, 1], '.-', color='w')
将所有这些结合在一起,下面列出了对测试问题执行 RMSProp 优化并将结果绘制在等高线图上的完整示例。
# example of plotting the rmsprop search on a contour plot of the test function from math import sqrt from numpy import asarray from numpy import arange from numpy.random import rand from numpy.random import seed from numpy import meshgrid from matplotlib import pyplot from mpl_toolkits.mplot3d import Axes3D # objective function def objective(x, y): return x**2.0 + y**2.0 # derivative of objective function def derivative(x, y): return asarray([x * 2.0, y * 2.0]) # gradient descent algorithm with rmsprop def rmsprop(objective, derivative, bounds, n_iter, step_size, rho): # track all solutions solutions = list() # generate an initial point solution = bounds[:, 0] + rand(len(bounds)) * (bounds[:, 1] - bounds[:, 0]) # list of the average square gradients for each variable sq_grad_avg = [0.0 for _ in range(bounds.shape[0])] # run the gradient descent for it in range(n_iter): # calculate gradient gradient = derivative(solution[0], solution[1]) # update the average of the squared partial derivatives for i in range(gradient.shape[0]): # calculate the squared gradient sg = gradient[i]**2.0 # update the moving average of the squared gradient sq_grad_avg[i] = (sq_grad_avg[i] * rho) + (sg * (1.0-rho)) # build solution new_solution = list() for i in range(solution.shape[0]): # calculate the learning rate for this variable alpha = step_size / (1e-8 + sqrt(sq_grad_avg[i])) # calculate the new position in this variable value = solution[i] - alpha * gradient[i] new_solution.append(value) # store the new solution solution = asarray(new_solution) solutions.append(solution) # evaluate candidate point solution_eval = objective(solution[0], solution[1]) # report progress print('>%d f(%s) = %.5f' % (it, solution, solution_eval)) return solutions # seed the pseudo random number generator seed(1) # define range for input bounds = asarray([[-1.0, 1.0], [-1.0, 1.0]]) # define the total iterations n_iter = 50 # define the step size step_size = 0.01 # momentum for rmsprop rho = 0.99 # perform the gradient descent search with rmsprop solutions = rmsprop(objective, derivative, bounds, n_iter, step_size, rho) # sample input range uniformly at 0.1 increments xaxis = arange(bounds[0,0], bounds[0,1], 0.1) yaxis = arange(bounds[1,0], bounds[1,1], 0.1) # create a mesh from the axis x, y = meshgrid(xaxis, yaxis) # compute targets results = objective(x, y) # create a filled contour plot with 50 levels and jet color scheme pyplot.contourf(x, y, results, levels=50, cmap='jet') # plot the sample as black circles solutions = asarray(solutions) pyplot.plot(solutions[:, 0], solutions[:, 1], '.-', color='w') # show the plot pyplot.show()
运行该示例会像以前一样执行搜索,但在这种情况下,会创建目标函数的等高线图。
在这种情况下,我们可以看到在搜索过程中找到的每个解决方案都显示了一个白点,从最优值上方开始,并逐渐靠近图中心的最优值。
这篇关于基于Python从头开始使用 RMSProp 进行梯度下降的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-26Python基础编程
- 2024-11-25Python编程基础:变量与类型
- 2024-11-25Python编程基础与实践
- 2024-11-24Python编程基础详解
- 2024-11-21Python编程基础教程
- 2024-11-20Python编程基础与实践
- 2024-11-20Python编程基础与高级应用
- 2024-11-19Python 基础编程教程
- 2024-11-19Python基础入门教程
- 2024-11-17在FastAPI项目中添加一个生产级别的数据库——本地环境搭建指南