← 返回文章列表

学习率对梯度下降的影响分析

梯度下降的优化算法-动量算法MOmentum 梯度计算公式: s_t=βs_t−1 + (1−β)g_t 参数更新公式: w_t= …

梯度下降的优化算法-动量算法MOmentum

梯度计算公式:

s_t=βs_t−1 + (1−β)g_t

参数更新公式:

w_t=w_t−1 − ηs_t

s_t是当前时刻指数加权平均梯度值

s_t-1是历史指数加权平均梯度值

g_t是当前时刻的梯度值

β 是调节权重系数,通常取 0.9 或 0.99

η是学习率

w_t是当前时刻模型权重参数

import torch
import torch.nn as nn


#todo:St = β * St-1 +(1-β) * Gt

def dm01_momentum():
    #1:初始化权重参数
    w = torch.tensor([1.0],requires_grad=True,dtype=torch.float)
    #2:定义损失函数
    criterion = ((w**2)/2.0).sum()
    #3:定义优化器
    #参1:优化的参数 参2:学习率 参3:动量参数
    optimizer = torch.optim.SGD([w],lr=0.01,momentum=0.9) #细节:momentum=0时,只考虑本次梯度
    #4:计算梯度值:梯度清零+反向传播+参数更新
    optimizer.zero_grad()
    criterion.backward()
    optimizer.step()
    print(f'w:{w},w.grad:{w.grad}')
    #5:重复步骤,第二次,更新权重参数
    criterion = ((w**2)/2.0).sum()
    optimizer.zero_grad()
    criterion.backward()
    optimizer.step()
    print(f'w:{w},w.grad:{w.grad}')











if __name__ == '__main__':
    dm01_momentum()

梯度下降的优化方法——adaGrad

def dm02_adagrad():
    # 1:初始化权重参数
    w = torch.tensor([1.0], requires_grad=True, dtype=torch.float)
    # 2:定义损失函数
    criterion = ((w**2) / 2.0).sum()
    # 3:定义优化器
    # 参1:优化的参数 参2:学习率 参3:动量参数
    #思路1:基于SGD,加入参数momentum就是动量法
    # optimizer = torch.optim.SGD([w], lr=0.01, momentum=0.9)  # 细节:momentum=0时,只考虑本次梯度
    #思路2:基于Adagrad(自适应学习率)
    optimizer = torch.optim.Adagrad(params=[w], lr=0.01)
    # 4:计算梯度值:梯度清零+反向传播+参数更新
    optimizer.zero_grad()
    criterion.backward()
    optimizer.step()
    print(f"w:{w},w.grad:{w.grad}")
    # 5:重复步骤,第二次,更新权重参数
    criterion = ((w**2) / 2.0).sum()
    optimizer.zero_grad()
    criterion.backward()
    optimizer.step()
    print(f"w:{w},w.grad:{w.grad}")

梯度下降的优化方法RMSProp

def dm03_rmsprop():
    # 1:初始化权重参数
    w = torch.tensor([1.0], requires_grad=True, dtype=torch.float)
    # 2:定义损失函数
    criterion = ((w**2) / 2.0).sum()
    # 3:定义优化器
    # 参1:优化的参数 参2:学习率 参3:动量参数
    #思路1:基于SGD,加入参数momentum就是动量法
    # optimizer = torch.optim.SGD([w], lr=0.01, momentum=0.9)  # 细节:momentum=0时,只考虑本次梯度
    #思路2:基于Adagrad(自适应学习率)
    # optimizer = torch.optim.Adagrad(params=[w], lr=0.01)
    #思路3:基于RMSprop(自适应学习率)
    optimizer = torch.optim.RMSprop(params=[w], lr=0.01,alpha=0.99)
    # 4:计算梯度值:梯度清零+反向传播+参数更新
    optimizer.zero_grad()
    criterion.backward()
    optimizer.step()
    print(f"w:{w},w.grad:{w.grad}")
    # 5:重复步骤,第二次,更新权重参数
    criterion = ((w**2) / 2.0).sum()
    optimizer.zero_grad()
    criterion.backward()
    optimizer.step()
    print(f"w:{w},w.grad:{w.grad}")

三种算法的比较

总结:momentum更新梯度,adagrad和RMSprop更新学习率

梯度下降的优化方法Adam

momentum和adam用的最多

学习率衰减优化方法

学习率对梯度的影响

import torch
import matplotlib.pyplot as plt

x看成是权重,y看成是loss,下面通过代码来理解学习率的作用

def func(x_t):
    return torch.pow(2*x_t, 2)  # y = 4 x ^2

采用较小的学习率,梯度下降的速度慢

采用较大的学习率,梯度下降太快越过了最小值点,导致不收敛,甚至震荡

def dm01():
x = torch.tensor([2.], requires_grad=True)
# 记录loss迭代次数,画曲线
iter_rec, loss_rec, x_rec = list(), list(), list()

# 实验学习率: 0.01 0.02 0.03 0.1 0.2 0.3 0.4
# lr = 0.1    # 正常的梯度下降
# lr = 0.125      # 当学习率设置0.125 一下子求出一个最优解
                # x=0 y=0 在x=0处梯度等于0 x的值x=x-lr*x.grad就不用更新了
                # 后续再多少次迭代 都固定在最优点

lr = 0.2      # x从2.0一下子跨过0点,到了左侧负数区域
# lr = 0.3      # 梯度越来越大 梯度爆炸
max_iteration = 4
for i in range(max_iteration):
    y = func(x)   # 得出loss值
    y.backward()  # 计算x的梯度
    print("Iter:{}, X:{:8}, X.grad:{:8}, loss:{:10}".format(
        i, x.detach().numpy()[0], x.grad.detach().numpy()[0], y.item()))
    x_rec.append(x.item())      # 梯度下降点 列表
    # 更新参数
    x.data.sub_(lr * x.grad)    # x = x - x.grad
    x.grad.zero_()
    iter_rec.append(i)          # 迭代次数 列表
    loss_rec.append(y.item())  # 损失值 列表,这里将y改为y.item()以获取标量值
# 迭代次数-损失值 关系图
plt.subplot(121).plot(iter_rec, loss_rec, '-ro')
plt.grid()
plt.xlabel("Iteration X")
plt.ylabel("Loss value Y")
# 函数曲线-下降轨迹 显示图
x_t = torch.linspace(-3, 3, 100)
y = func(x_t)
plt.subplot(122).plot(x_t.detach().numpy(), y.detach().numpy(), label="y = 4*x^2")
y_rec = [func(torch.tensor(i)).item() for i in x_rec]
print('x_rec--->', x_rec)
print('y_rec--->', y_rec)
# 指定线的颜色和样式(-ro:红色圆圈,b-:蓝色实线等)
plt.subplot(122).plot(x_rec, y_rec, '-ro')
plt.grid()
plt.legend()
plt.show()
dm01()

运行效果图如下:

可以看出:采用较小的学习率,梯度下降的速度慢;采用较大的学习率,梯度下降太快越过了最小值点,导致震荡,甚至不收敛(梯度爆炸)。

等间隔学习率衰减

import torch
import matplotlib.pyplot as plt
from torch import  optim
def dm01():
    lr = 0.1
    epochs = 200
    iteration = 10

    y_ture = torch.tensor([0])
    x = torch.tensor([1.0],dtype=torch.float32)
    w = torch.tensor([1.0],dtype=torch.float32,requires_grad=True)

    optimizer = optim.SGD([w],lr=lr,momentum=0.9)

    scheduler = optim.lr_scheduler.StepLR(optimizer,step_size=50,gamma=0.5)

    lr_list,epoch_list = [],[]

    for epoch in range(epochs):
        epoch_list.append(epoch)
        lr_list.append(scheduler.get_last_lr())

        for batch in range(iteration):
            y_pred = w*x
            loss = (y_pred - y_ture)**2
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
        scheduler.step()
    print(f'lr_list:{lr_list}')


    plt.plot(epoch_list,lr_list)
    plt.xlabel('epoch')
    plt.ylabel('lr')    
    # plt.legend()
    plt.show()
if __name__ == '__main__':
    dm01()

指定间隔学习率衰减

只需修改一行代码

# 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.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)

正则化-dropout

隐藏层失活

def dm01():
    t1 = torch.randint(0,10,(1,4)).float()
    # print(t1)

    linear1 = nn.Linear(4,5)


    l1 = linear1(t1)
    print(f'l1:{l1}')
    output = torch.relu(l1)
    print(f'output:{output}')

    dropout = nn.Dropout(p=0.5) 
    d1 = dropout(output)
    print(f'随机失活后的数据:{d1}')

正则化-批量归一化

一般在计算机视觉领域应用较多

import torch
import torch.nn as nn


def dm01():
    input_2d = torch.randn(size=(1,2,3,4))

    bn2d = nn.BatchNorm2d(num_features=2,eps=1e-5,momentum=0.1,affine=True)

    output2d = bn2d(input_2d)
    print(f'output2d:{output2d}')

def dm02():
    input_1d = torch.randn(size=(2,2))
    print(f'input_1d:{input_1d}')

    linear1 = nn.linear(in_features=2,out_features=4)

    l1 = linear1(input_1d)
    print(f'l1:{l1}')

    bn1d=nn.batchnorm1d(num_features=2)

    output_1d = bn1d(l1)
    print(f'output_1d:{output_1d}')

if __name__ == '__main__':
    # dm01()
    dm02()