目录
一、算法思想
二、算法思路
三、算法实现
四、代码实现
一、算法思想
梯度下降算法是一种优化算法,用于寻找函数的局部最小值。其基本思想是通过迭代的方式,逐步调整参数,使得函数的输出值减小。以下是梯度下降算法的主要思想:
- 初始化参数:选择一个初始点作为参数的起始值。
- 计算梯度:在当前参数值处,计算目标函数的梯度。梯度表示了函数在各个方向上的斜率,指向函数增长最快的方向。
- 更新参数:根据梯度和学习率对参数进行更新。学习率决定了在梯度方向上前进的步长。参数更新的目标是沿着梯度的反方向移动,因为这样可以减小函数的值。
- 迭代优化:重复计算梯度和更新参数的过程,直到满足停止条件,如达到预定的迭代次数、梯度的变化小于一个阈值或者函数值的变化小于一个阈值。
- 输出结果:当算法停止时,当前的参数值即为函数的一个局部最小值。
在上述过程中,学习率的选取非常关键。如果学习率太小,算法可能会需要很多次迭代才能收敛;如果学习率太大,算法可能会在最小值附近震荡,甚至偏离最小值。因此,有时会采用学习率衰减策略,随着迭代的进行逐渐减小学习率。
此外,梯度下降算法的效果很大程度上取决于目标函数的性质和初始参数的选择。对于非凸函数,梯度下降可能会陷入局部最小值,而不是全局最小值。因此,在实际应用中,可能需要多次尝试不同的初始值,或者使用更复杂的优化算法来寻找更好的解。
二、算法思路
- 定义一个带有平方项和正弦项的函数。
- 使用自动求导计算该函数在任意点处的梯度。
- 初始化一个起始点和学习率。
- 在多次迭代中,根据当前梯度和学习率更新输入值。
- 如果新的函数值小于当前的最小值,则更新最小值和对应的输入值;否则,增加学习率。
- 根据梯度的大小来调整迭代次数。
- 可视化每次迭代的输入值和函数值。
- 输出找到的局部最小值。
三、算法实现
1. 函数定义:
questions(x): 定义了一个带有平方项和正弦项的函数。
- grad_fun(x): 计算给定函数在点x处的梯度。
2. 梯度下降函数:
- dealer(x, gradient, lr): 根据当前梯度和学习率更新输入值,并计算新的函数值。
- gradient_descent(x, lr, iterations): 执行梯度下降优化。它使用`dealer`函数来更新输入值,并根据梯度的大小来调整学习率。如果新的函数值小于当前的最小值,则更新最小值和对应的输入值;否则,增加学习率。
3. 梯度优化:
- grad_optimization(gradient, iterations): 如果梯度小于一个阈值,则增加额外的迭代次数。
4. 可视化:
- draw(): 绘制每次迭代的输入值和函数值。
5. 主函数:
- 用户输入起始点、学习率、迭代次数和额外步数。
- 初始化局部最优解和记录列表。
- 执行梯度下降优化。
- 输出找到的局部最小值。
- 可视化结果。
四、代码实现
import torch
import math
import matplotlib.pyplot as pltdef questions(x):'''定义函数'''return x**2 + torch.sin(5*x)def grad_fun(x):'''计算函数在 x 处的梯度'''# 确保 x 是一个带有 requires_grad=True 的张量x = x.clone().detach().requires_grad_(True)y = questions(x) # 计算函数值y.backward() # 使用自动求导计算梯度grad = x.grad # 获取计算得到的梯度return graddef dealer(x, gradient, lr):'''使用梯度下降更新 x。:param x: 当前的输入值:param gradient: 当前输入值处的梯度:param lr: 学习率:return: 更新后的输入值和对应的新函数值'''print(f'当前输入: {x}')print(f'当前最小值:: {questions(x)}')print(f'当前梯度: {gradient}')print(f'当前学习率: {lr}')print('-'*50)# 根据梯度和学习率更新输入值new_x = x - lr * gradient# 计算更新后的输入值对应的新函数值new_y = questions(new_x)return new_x, new_ydef gradient_descent(x, lr, iterations):'''执行梯度下降优化。:param x: 初始输入值:param lr: 初始学习率:param iterations: 迭代次数:return: 最优化后的输入值和对应的最小函数值'''# 修改全局变量global local_xglobal local_yi = 0while i < iterations:# 记录当前的值x_record.append(local_x)y_record.append(local_y)# 计算当前输入点处的梯度gradient = grad_fun(local_x)iterations = grad_optimization(gradient,iterations)gradient_record.append(gradient)# 移动后有新的值new_x, new_y = dealer(local_x, gradient, lr) # 使用梯度下降更新输入值if new_y < local_y:local_y = new_y # 更新最小函数值local_x = new_x # 更新对应的输入值lr *= 0.97 # 减小学习率以加快收敛else:lr *= 1.03 # 增大学习率以避免陷入局部最小值i += 1return local_x, local_y # 返回优化后的输入值和对应的最小函数值def grad_optimization(gradient,iterations):'''梯度提前收敛了,就增加迭代次数:param gradient::return:'''global local_xglobal local_yglobal extra_moveif math.fabs(gradient) < 0.001:iterations += extra_movereturn iterationsdef draw():x_list = [i.detach().numpy() for i in x_record]y_list = [i.detach().numpy() for i in y_record]print(x_list)print(y_list)plt.plot(x_list, y_list, label='')plt.show()passif __name__ == '__main__':x = float(input('起始点: '))start_lr = float(input('学习率: '))iterations_num = int(input('迭代次数: '))extra_move = int(input('添加的步数:'))# 当前的局部最优解local_x = torch.tensor(x, requires_grad=True) # 初始化输入点为张量local_y = questions(local_x) # 计算初始输入点处的函数值x_record = [] # 记录每次迭代的输入值y_record = [] # 记录每次迭代的函数值gradient_record = [] # 记录每次迭代的梯度值# 执行梯度下降优化final_x, final_y = gradient_descent(local_x, start_lr, iterations_num)# 输出找到的局部最小值print(f'极值点:{final_x}')print(f'找到的最小值为: {final_y}')# 可视化draw()