💻 源码
"""
演示
    梯度下降法的优化方法

介绍:
    梯度下降法,由损失函数的导数/梯度、学习率 来更新模型参数
    公式:w新 = w旧 - 学习率 * 梯度
    存在问题:
        1.平缓区域,梯度较小,参数更新慢
        2.“鞍点” ,梯度为 0,参数无法更新
        3.局部最小值,无法跳出,参数不是最优
梯度下降优化方法:
    1.调整梯度,比如使用指数移动加权平均来替代原始梯度
        动量法Momentum
            梯度的指数移动加权平均:st = β*st-1 + (1-β)*gt
            更新参数:wt = wt-1 - η*st
    2.调整学习率
        AdaGrad自适应学习率(Adaptive Gradient Estimation):
            累计平方梯度:
                St = St-1 + gt*gt
                其中,St为本次的累计平方梯度,St-1为上次的累计平方梯度,gt为本次的梯度
            学习率:
                η = η/(sqrt(St)+σ)
            更新参数公式:
                Wt = Wt-1 - 调整后的η * gt
            缺点:
                可能会使得学习率过早、过量的降低,导致模型训练后期学习率太小,较难找到最优解
        RMSProp(Root Mean Square Propagation)自适应学习率:
            对AdaGrad的优化,在计算累计平方梯度时,加入 调和权重系数
            指数加权累计平方梯度:
                St = β*St-1 + (1-β)*gt*gt
            其中St为本次的累计平方梯度,St-1为上次的累计平方梯度,gt为本次的梯度,β为 调和权重系数
            学习率:
                η = η/(sqrt(St)+σ)
            更新参数公式:
                Wt = Wt-1 - 调整后的η * gt
            优点:引入 调和权重系数,控制历史梯度 对 累计平方梯度的影响,防止累计平方梯度过大,从而防止学习率过早降低
        Adam(Adaptive Moment Estimation)自适应矩估计:
            同时优化 学习率 和 梯度
            公式:
                梯度(一阶矩):
                    Mt = β1 * Mt-1 +(1-β1)*gt
                    Mt_hat = Mt / (1-β1^t)
                学习率(二阶矩):
                    St = β2 * St-1 + (1-β2)*gt*gt
                    St_hat = St / (1-β2^t)
                更新参数公式:
                    Wt = Wt-1 - 学习率/(sqrt(St_hat)+σ) * 调整后的梯度Mt_hat
            就是 RMSProp + Momentum
        AdamW:
            Adam的优化,解偶了权重衰减,在更新参数时直接添加了权重衰减项。
            公式:
                梯度(使用一阶矩):
                    Mt = β1 * Mt-1 +(1-β1)*gt
                    Mt_hat = Mt / (1-β1^t)
                学习率(使用二阶矩):
                    St = β2 * St-1 + (1-β2)*gt*gt
                    St_hat = St / (1-β2^t)
                更新参数公式:
                    Wt = Wt-1 - 学习率/(sqrt(St_hat)+σ) * 调整后的梯度Mt_hat - η * λ * Wt-1
需要掌握:
    Adam,AdamW: optim.Adam/AdamW(实际使用)
    Momentum: optim.SGD(面试)

"""
# 导包
import torch
import torch.nn as nn
import torch.optim as optim # 优化器模块,实现梯度下降法以及梯度下降的优化方法

# 1.定义函数,演示 梯度下降的优化方法--动量法Momentum
def demo01():
    print("动量法Momentum")
    # 1.初始化模型参数w
    w = torch.tensor([1.0],requires_grad=True, dtype=torch.float32)
    print(f"初始权重w:{w}")
    # 2.定义损失函数,并计算损失
    loss = w**2
    # loss'=2w
    # 3.创建优化器,动量法Momentum
    # [w]:待训练的参数;lr:学习率,momentum:动量因子
    optimizer = optim.SGD([w],lr=0.01,momentum=0.95)
    # 4.梯度清零
    optimizer.zero_grad()
    # 5.反向传播,计算梯度
    loss.mean().backward()
    # 6.更新参数,第一次更新参数
    # dloss_mean/dw = 2*w = 2
    # w = w - 0.01 * 2 = 1 - 0.01*2 = 0.98
    optimizer.step()
    print(f"第一次更新参数:w:{w.data}, w.grad:{w.grad.data}")
    # 7.第二次更新参数
    loss = w ** 2
    optimizer.zero_grad()
    loss.mean().backward()
    optimizer.step()
    print(f"第二次更新参数:w:{w.data}, w.grad:{w.grad.data}")
    ...

# 2.定义函数,演示 梯度下降的优化方法--AdaGrad自适应学习率
def demo02():
    print("AdaGrad自适应学习率")
    # 1.初始化模型参数w
    w = torch.tensor([1.0],requires_grad=True, dtype=torch.float32)
    print(f"初始权重w:{w}")
    # 2.定义损失函数,并计算损失值
    loss = w**2
    # loss'=2w
    # 3.创建优化器,动量法Momentum
    # optimizer = optim.SGD([w],lr=0.01,momentum=0.95)
    # AdaGrad自适应学习率
    optimizer = optim.Adagrad([w],lr=0.01)
    # 4.梯度清零
    optimizer.zero_grad()
    # 5.反向传播,计算梯度
    loss.mean().backward()
    # 6.更新参数,第一次更新参数
    optimizer.step()
    print(f"第一次更新参数:w:{w.data}, w.grad:{w.grad.data}")
    # 7.第二次更新参数
    loss = w ** 2
    optimizer.zero_grad()
    loss.mean().backward()
    optimizer.step()
    print(f"第二次更新参数:w:{w.data}, w.grad:{w.grad.data}")
    ...

# 3.定义函数,演示 梯度下降的优化方法--RMSProp自适应学习率
def demo03():
    print("RMSProp自适应学习率")
    # 1.初始化模型参数w
    w = torch.tensor([1.0],requires_grad=True, dtype=torch.float32)
    print(f"初始权重w:{w}")
    # 2.定义损失函数,并计算损失值
    loss = w**2
    # loss'=2w
    # 3.创建优化器,动量法Momentum
    # optimizer = optim.SGD([w],lr=0.01,momentum=0.95)
    # AdaGrad自适应学习率
    # optimizer = optim.Adagrad([w],lr=0.01)
    # RMSProp自适应学习率
    optimizer = optim.RMSprop([w],lr=0.01)
    # 4.梯度清零
    optimizer.zero_grad()
    # 5.反向传播,计算梯度
    loss.mean().backward()
    # 6.更新参数,第一次更新参数
    optimizer.step()
    print(f"第一次更新参数:w:{w.data}, w.grad:{w.grad.data}")
    # 7.第二次更新参数
    loss = w ** 2
    optimizer.zero_grad()
    loss.mean().backward()
    optimizer.step()
    print(f"第二次更新参数:w:{w.data}, w.grad:{w.grad.data}")
    ...

# 4.定义函数,演示 梯度下降的优化方法--Adam自适应矩估计
def demo04():
    print("Adam自适应矩估计")
    # 1.初始化模型参数w
    w = torch.tensor([1.0],requires_grad=True, dtype=torch.float32)
    print(f"初始权重w:{w}")
    # 2.定义损失函数,并计算损失值
    loss = w**2
    # loss'=2w
    # 3.创建优化器,动量法Momentum
    # optimizer = optim.SGD([w],lr=0.01,momentum=0.9)
    # AdaGrad自适应学习率
    # optimizer = optim.Adagrad([w],lr=0.01)
    # RMSProp自适应学习率
    # optimizer = optim.RMSprop([w],lr=0.01)
    # Adam自适应矩估计
    optimizer = optim.Adam([w],lr=0.01)
    # 4.梯度清零
    optimizer.zero_grad()
    # 5.反向传播,计算梯度
    loss.mean().backward()
    # 6.更新参数,第一次更新参数
    optimizer.step()
    print(f"第一次更新参数:w:{w.data}, w.grad:{w.grad.data}")
    # 7.第二次更新参数
    loss = w ** 2
    optimizer.zero_grad()
    loss.mean().backward()
    optimizer.step()
    print(f"第二次更新参数:w:{w.data}, w.grad:{w.grad.data}")
    ...

# 5.定义函数,演示 梯度下降的优化方法--AdamW
def demo05():
    print("AdamW")
    # 1.初始化模型参数w
    w = torch.tensor([1.0],requires_grad=True, dtype=torch.float32)
    print(f"初始权重w:{w}")
    # 2.定义损失函数,并计算损失值
    loss = w**2
    # loss'=2w
    # 3.创建优化器,动量法Momentum
    # optimizer = optim.SGD([w],lr=0.01,momentum=0.9)
    # AdaGrad自适应学习率
    # optimizer = optim.Adagrad([w],lr=0.01)
    # RMSProp自适应学习率
    # optimizer = optim.RMSprop([w],lr=0.01)
    # Adam自适应矩估计
    # optimizer = optim.Adam([w],lr=0.01,decoupled_weight_decay=True,weight_decay=1e-2)
    # AdamW
    # [w]:待训练的模型参数;lr:学习率,weight_decay:权重衰减因子,λ*w
    optimizer = optim.AdamW([w],lr=0.01,weight_decay=1e-2)
    # 4.梯度清零
    optimizer.zero_grad()
    # 5.反向传播,计算梯度
    loss.mean().backward()
    # 6.更新参数,第一次更新参数
    optimizer.step()
    print(f"第一次更新参数:w:{w.data}, w.grad:{w.grad.data}")
    # 7.第二次更新参数
    loss = w ** 2
    optimizer.zero_grad()
    loss.mean().backward()
    optimizer.step()
    print(f"第二次更新参数:w:{w.data}, w.grad:{w.grad.data}")
    ...

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