梯度下降及其可视化

目录

一、算法思想

二、算法思路

三、算法实现

四、代码实现


一、算法思想

梯度下降算法是一种优化算法,用于寻找函数的局部最小值。其基本思想是通过迭代的方式,逐步调整参数,使得函数的输出值减小。以下是梯度下降算法的主要思想:

  1.  初始化参数:选择一个初始点作为参数的起始值。
  2.  计算梯度:在当前参数值处,计算目标函数的梯度。梯度表示了函数在各个方向上的斜率,指向函数增长最快的方向。
  3.  更新参数:根据梯度和学习率对参数进行更新。学习率决定了在梯度方向上前进的步长。参数更新的目标是沿着梯度的反方向移动,因为这样可以减小函数的值。
  4. 迭代优化:重复计算梯度和更新参数的过程,直到满足停止条件,如达到预定的迭代次数、梯度的变化小于一个阈值或者函数值的变化小于一个阈值。
  5. 输出结果:当算法停止时,当前的参数值即为函数的一个局部最小值。

        在上述过程中,学习率的选取非常关键。如果学习率太小,算法可能会需要很多次迭代才能收敛;如果学习率太大,算法可能会在最小值附近震荡,甚至偏离最小值。因此,有时会采用学习率衰减策略,随着迭代的进行逐渐减小学习率。
        此外,梯度下降算法的效果很大程度上取决于目标函数的性质和初始参数的选择。对于非凸函数,梯度下降可能会陷入局部最小值,而不是全局最小值。因此,在实际应用中,可能需要多次尝试不同的初始值,或者使用更复杂的优化算法来寻找更好的解。

二、算法思路

  1. 定义一个带有平方项和正弦项的函数。
  2.  使用自动求导计算该函数在任意点处的梯度。
  3.  初始化一个起始点和学习率。
  4.  在多次迭代中,根据当前梯度和学习率更新输入值。
  5.  如果新的函数值小于当前的最小值,则更新最小值和对应的输入值;否则,增加学习率。
  6.  根据梯度的大小来调整迭代次数。
  7.  可视化每次迭代的输入值和函数值。
  8.  输出找到的局部最小值。

三、算法实现


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 plt

def 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 grad

def 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_y


def gradient_descent(x, lr, iterations):
    '''
    执行梯度下降优化。

    :param x: 初始输入值
    :param lr: 初始学习率
    :param iterations: 迭代次数
    :return: 最优化后的输入值和对应的最小函数值
    '''

    # 修改全局变量
    global local_x
    global local_y

    i = 0
    while 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 += 1

    return local_x, local_y  # 返回优化后的输入值和对应的最小函数值


def grad_optimization(gradient,iterations):
    '''
    梯度提前收敛了,就增加迭代次数
    :param gradient:
    :return:
    '''

    global local_x
    global local_y
    global extra_move

    if math.fabs(gradient) < 0.001:
        iterations += extra_move

    return iterations

def 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()


    pass


if __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()


 

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

依恋、阳光

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值