💻 源码
"""
演示
    学习率衰减策略/学习率调度策略
介绍:
    之前学的优化器,可以调节学习率:Adam/AdamW, AdaGrad, RMSProp,
    Momentum(调节梯度)
    也可以手动调节学习率:
        等间隔学习率衰减
        指定间隔学习率衰减
        指数间隔学习率衰减
        周期重启的余弦退火策略
等间隔学习率衰减
    固定间隔来衰减学习率,当前学习率lr = 上一次的学习率lr*γ, 比如γ=0.5

指定间隔学习率衰减
    人为指定间隔来衰减学习率,当前学习率lr = 上一次的学习率lr*γ, 比如γ=0.5

指数间隔学习率衰减
    当前轮次的lr = 上个轮次lr*γ, 比如γ=0.95

周期重启的余弦退火策略
    带有周期重启的余弦曲线,可以让学习率重新启动,有可能冲过局部最小值

神经网络的训练流程:
    1.准备数据集
    2.构建神经网络模型
    3.设置损失函数和优化器,以及学习率调度器
    4.模型训练
        1.前向传播
        2.计算损失
        3.梯度清零
        4.反向传播
        5.更新参数:w新 = w旧-学习率*梯度
        6.更新学习率
    5.模型测试

需要掌握:
    无
    建议周期重启的余弦退火策略 optim.lr_scheduler.CosineAnnealingWarmRestarts

"""


# 导包
import torch
import torch.nn as nn
import torch.optim as optim # 优化器模块,提供各种优化器对象,比如SGD,Adam
import matplotlib.pyplot as plt # 绘图
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']  # 微软雅黑
plt.rcParams['axes.unicode_minus'] = False            # 解决负号显示问题

# 1.定义函数,演示 等间隔学习率衰减策略
def demo01():
    # 0.初始化参数
    lr = 0.1
    epochs = 200
    iteration = 10
    # 1.准备数据集
    x = torch.tensor([1.0],dtype=torch.float32)
    y_true = torch.tensor([0.0],dtype=torch.float32)
    # 2.构建神经网络模型
    # 创建张量,模拟网络参数
    w = torch.tensor([1.0],dtype=torch.float32,requires_grad=True)
    # 3.设置损失函数和优化器,以及学习率调度器
    # 损失函数
    loss_fn = nn.MSELoss()
    # 优化器
    optimizer = optim.SGD([w],lr=lr)
    # 学习率调度器
    scheduler = optim.lr_scheduler.StepLR(optimizer,step_size=50,gamma=0.5)
    # 4.模型训练
    # 定义列表,记录训练轮数和学习率
    lr_list = []
    epoch_list = []
    for epoch in range(epochs):
        # 0.获取当前轮数 和 学习率,保存到记录列表中
        epoch_list.append(epoch)
        lr_list.append(scheduler.get_last_lr()[0])
        # 遍历批次
        for i in range(iteration):
            # 1.前向传播
            y_pred = w * x
            # 2.计算损失
            # 3.梯度清零
            # 4.反向传播
            # 5.更新参数:w新 = w旧-学习率*梯度
        # 6.更新学习率
        scheduler.step()
    # 5.可视化学习率变化
    plt.plot(epoch_list,lr_list)
    plt.title("等间隔学习率衰减")
    plt.xlabel("epoch")
    plt.ylabel("lr")
    plt.show()

# 2.定义函数,演示 指定间隔学习率衰减策略
def demo02():
    # 0.初始化参数
    lr = 0.1
    epochs = 200
    iteration = 10
    # 1.准备数据集
    x = torch.tensor([1.0],dtype=torch.float32)
    y_true = torch.tensor([0.0],dtype=torch.float32)
    # 2.构建神经网络模型
    # 创建张量,模拟网络参数
    w = torch.tensor([1.0],dtype=torch.float32,requires_grad=True)
    # 3.设置损失函数和优化器,以及学习率调度器
    # 损失函数
    loss_fn = nn.MSELoss()
    # 优化器
    optimizer = optim.SGD([w],lr=lr)
    # 学习率调度器
    # scheduler = optim.lr_scheduler.StepLR(optimizer,step_size=50,gamma=0.5)
    # 指定间隔学习率衰减策略
    scheduler = optim.lr_scheduler.MultiStepLR(optimizer,milestones=[50,125,160],gamma=0.5)
    # 4.模型训练
    # 定义列表,记录训练轮数和学习率
    lr_list = []
    epoch_list = []
    for epoch in range(epochs):
        # 0.获取当前轮数 和 学习率,保存到记录列表中
        epoch_list.append(epoch)
        lr_list.append(scheduler.get_last_lr()[0])
        # 遍历批次
        for i in range(iteration):
            # 1.前向传播
            y_pred = w * x
            # 2.计算损失
            # 3.梯度清零
            # 4.反向传播
            # 5.更新参数:w新 = w旧-学习率*梯度
        # 6.更新学习率
        scheduler.step()
    # 5.可视化学习率变化
    plt.plot(epoch_list,lr_list)
    plt.title("指定间隔学习率衰减")
    plt.xlabel("epoch")
    plt.ylabel("lr")
    plt.show()

# 3.定义函数,演示 指数间隔学习率衰减策略
def demo03():
    # 0.初始化参数
    lr = 0.1
    epochs = 200
    iteration = 10
    # 1.准备数据集
    x = torch.tensor([1.0],dtype=torch.float32)
    y_true = torch.tensor([0.0],dtype=torch.float32)
    # 2.构建神经网络模型
    # 创建张量,模拟网络参数
    w = torch.tensor([1.0],dtype=torch.float32,requires_grad=True)
    # 3.设置损失函数和优化器,以及学习率调度器
    # 损失函数
    loss_fn = nn.MSELoss()
    # 优化器
    optimizer = optim.SGD([w],lr=lr)
    # 学习率调度器
    # scheduler = optim.lr_scheduler.StepLR(optimizer,step_size=50,gamma=0.5)
    # 指定间隔学习率衰减策略
    # scheduler = optim.lr_scheduler.MultiStepLR(optimizer,milestones=[50,125,160],gamma=0.5)
    # 指数间隔学习率衰减策略
    scheduler = optim.lr_scheduler.ExponentialLR(optimizer,gamma=0.95)
    # 4.模型训练
    # 定义列表,记录训练轮数和学习率
    lr_list = []
    epoch_list = []
    for epoch in range(epochs):
        # 0.获取当前轮数 和 学习率,保存到记录列表中
        epoch_list.append(epoch)
        lr_list.append(scheduler.get_last_lr()[0])
        # 遍历批次
        for i in range(iteration):
            # 1.前向传播
            y_pred = w * x
            # 2.计算损失
            # 3.梯度清零
            # 4.反向传播
            # 5.更新参数:w新 = w旧-学习率*梯度
        # 6.更新学习率
        scheduler.step()
    # 5.可视化学习率变化
    plt.plot(epoch_list,lr_list)
    plt.title("指数间隔学习率衰减")
    plt.xlabel("epoch")
    plt.ylabel("lr")
    plt.show()

# 4.定义函数,演示 周期重启的余弦退火策略
def demo04():
    # 0.初始化参数
    lr = 0.1
    epochs = 200
    iteration = 10
    # 1.准备数据集
    x = torch.tensor([1.0],dtype=torch.float32)
    y_true = torch.tensor([0.0],dtype=torch.float32)
    # 2.构建神经网络模型
    # 创建张量,模拟网络参数
    w = torch.tensor([1.0],dtype=torch.float32,requires_grad=True)
    # 3.设置损失函数和优化器,以及学习率调度器
    # 损失函数
    loss_fn = nn.MSELoss()
    # 优化器
    optimizer = optim.SGD([w],lr=lr)
    # 学习率调度器
    # scheduler = optim.lr_scheduler.StepLR(optimizer,step_size=50,gamma=0.5)
    # 指定间隔学习率衰减策略
    # scheduler = optim.lr_scheduler.MultiStepLR(optimizer,milestones=[50,125,160],gamma=0.5)
    # 指数间隔学习率衰减策略
    # scheduler = optim.lr_scheduler.ExponentialLR(optimizer,gamma=0.95)
    # 周期重启的余弦退火策略
    scheduler = optim.lr_scheduler.CosineAnnealingWarmRestarts(
        optimizer,  # 优化器对象
        T_0=25, # 第1个周期的轮数,通常总训练轮数至少有5~10个周期
        eta_min=0,  # 最小学习率
        T_mult=1, # 周期倍增因子,默认为1,表示每个周期的轮数相同
    )
    # 4.模型训练
    # 定义列表,记录训练轮数和学习率
    lr_list = []
    epoch_list = []
    for epoch in range(epochs):
        # 0.获取当前轮数 和 学习率,保存到记录列表中
        epoch_list.append(epoch)
        lr_list.append(scheduler.get_last_lr()[0])
        # 遍历批次
        for i in range(iteration):
            # 1.前向传播
            y_pred = w * x
            # 2.计算损失
            # 3.梯度清零
            # 4.反向传播
            # 5.更新参数:w新 = w旧-学习率*梯度
        # 6.更新学习率
        scheduler.step()
    # 5.可视化学习率变化
    plt.plot(epoch_list,lr_list)
    plt.title("周期重启的余弦退火策略")
    plt.xlabel("epoch")
    plt.ylabel("lr")
    plt.show()

# 测试
if __name__ == '__main__':
    demo01()
    demo02()
    demo03()
    demo04()