Pytorch总结十五之优化算法:AdaGrad、RMSProp、AdaDelta、Adam算法详解

Pytorch总结十五之优化算法:AdaGrad、RMSProp、AdaDelta、Adam算法详解

简介:

  • 继续解析优化算法!

1. AdaFrad算法

在这里插入图片描述

1.1 算法

在这里插入图片描述

1.2 特点

在这里插入图片描述

#Adagrad算法
import math
import torch
import  sys
sys.path.append("..")
import d2lzh_pytorch as d2l

def adagrad_2d(x1,x2,s1,s2):
    g1,g2,eps=0.2*x1,4*x2,1e-6  #前两项为自变量梯度
    s1+=g1**2
    s2+=g2**2
    x1-=eta/math.sqrt(s1+eps)*g1
    x2-=eta/math.sqrt(s2+eps)*g2
    return x1,x2,s1,s2

def f_2d(x1,x2):
    return 0.1*x1**2+2*x2**2

eta=0.4
d2l.show_trace_2d(f_2d,d2l.train_2d(adagrad_2d))

output:

epoch 20, x1 -2.382563, x2 -0.158591

在这里插入图片描述
下⾯将学习率增⼤到2。可以看到⾃变量更为迅速地逼近了最优解。

eta=2
d2l.show_trace_2d(f_2d,d2l.train_2d(adagrad_2d))

output:

epoch 20, x1 -0.002295, x2 -0.000000

在这里插入图片描述

1.3 从零开始实现

同动量法⼀样,AdaGrad算法需要对每个⾃变量维护同它⼀样形状的状态变量。我们根据AdaGrad算
法中的公式实现该算法。

##从零实现adagrad算法
def get_data_ch7():  
    data = np.genfromtxt('data/airfoil_self_noise.dat', delimiter='\t')
    data = (data - data.mean(axis=0)) / data.std(axis=0)
    return torch.tensor(data[:1500, :-1], dtype=torch.float32), \
        torch.tensor(data[:1500, -1], dtype=torch.float32) # 前1500个样本(每个样本5个特征)

def init_adagrad_states():
    s_w=torch.zeros((features.shape[1],1),dtype=torch.float32)
    s_b=torch.zeros(1,dtype=torch.float32)
    return (s_w,s_b)

def adagrad(params,states,hyperparams):
    eps=1e-6
    for p,s in zip(params,states):
        s.data+=(p.grad.data**2)
        p.data-=hyperparams['lr']*p.grad.data/torch.sqrt(s+eps)
        
def train_ch7(optimizer_fn, states, hyperparams, features, labels,
              batch_size=10, num_epochs=2):
    # 初始化模型
    net, loss = linreg, squared_loss
    
    w = torch.nn.Parameter(torch.tensor(np.random.normal(0, 0.01, size=(features.shape[1], 1)), dtype=torch.float32),
                           requires_grad=True)
    b = torch.nn.Parameter(torch.zeros(1, dtype=torch.float32), requires_grad=True)

    def eval_loss():
        return loss(net(features, w, b), labels).mean().item()

    ls = [eval_loss()]
    data_iter = torch.utils.data.DataLoader(
        torch.utils.data.TensorDataset(features, labels), batch_size, shuffle=True)
    
    for _ in range(num_epochs):
        start = time.time()
        for batch_i, (X, y) in enumerate(data_iter):
            l = loss(net(X, w, b), y).mean()  # 使用平均损失
            
            # 梯度清零
            if w.grad is not None:
                w.grad.data.zero_()
                b.grad.data.zero_()
                
            l.backward()
            optimizer_fn([w, b], states, hyperparams)  # 迭代模型参数
            if (batch_i + 1) * batch_size % 100 == 0:
                ls.append(eval_loss())  # 每100个样本记录下当前训练误差
    # 打印结果和作图
    print('loss: %f, %f sec per epoch' % (ls[-1], time.time() - start))
    set_figsize()
    plt.plot(np.linspace(0, num_epochs, len(ls)), ls)
    plt.xlabel('epoch')
    plt.ylabel('loss')
    plt.show()

features,labels=d2l.get_data_ch7()
#使用更大的学习率来训练模型
d2l.train_ch7(adagrad,init_adagrad_states(),{'lr':0.1},features,labels)

output:

loss: 0.243252, 0.052886 sec per epoch

在这里插入图片描述

1.4 简洁实现

通过名称为 Adagrad 的优化器⽅法,我们便可使⽤PyTorch提供的AdaGrad算法来训练模型。

# 本函数与原书不同的是这里第一个参数优化器函数而不是优化器的名字
# 例如: optimizer_fn=torch.optim.SGD, optimizer_hyperparams={"lr": 0.05}
def train_pytorch_ch7(optimizer_fn, optimizer_hyperparams, features, labels,
                    batch_size=10, num_epochs=2):
    # 初始化模型
    net = nn.Sequential(
        nn.Linear(features.shape[-1], 1)
    )
    loss = nn.MSELoss()
    optimizer = optimizer_fn(net.parameters(), **optimizer_hyperparams)

    def eval_loss():
        return loss(net(features).view(-1), labels).item() / 2

    ls = [eval_loss()]
    data_iter = torch.utils.data.DataLoader(
        torch.utils.data.TensorDataset(features, labels), batch_size, shuffle=True)

    for _ in range(num_epochs):
        start = time.time()
        for batch_i, (X, y) in enumerate(data_iter):
            # 除以2是为了和train_ch7保持一致, 因为squared_loss中除了2
            l = loss(net(X).view(-1), y) / 2 
            
            optimizer.zero_grad()
            l.backward()
            optimizer.step()
            if (batch_i + 1) * batch_size % 100 == 0:
                ls.append(eval_loss())
    # 打印结果和作图
    print('loss: %f, %f sec per epoch' % (ls[-1], time.time() - start))
    set_figsize()
    plt.plot(np.linspace(0, num_epochs, len(ls)), ls)
    plt.xlabel('epoch')
    plt.ylabel('loss')
    plt.show()

d2l.train_pytorch_ch7(torch.optim.Adagrad, {'lr': 0.1}, features,labels)

output:

loss: 0.243147, 0.040675 sec per epoch 1

在这里插入图片描述

1.4 小结

  • AdaGrad算法在迭代过程中不断调整学习率,并让⽬标函数⾃变量中每个元素都分别拥有⾃⼰
    的学习率。
  • 使⽤AdaGrad算法时,⾃变量中每个元素的学习率在迭代过程中⼀直在降低(或不变)

2.Rmsprop算法

AdaGrad算法中提到,因为调整学习率时分⺟上的变量 ⼀直在累加按元素平⽅的⼩批量随机梯度,所以⽬标函数⾃变量每个元素的学习率在迭代过程中⼀直在降低(或不变)。因此,当学习率在迭代早期降得较快且当前解依然不佳时,AdaGrad算法在迭代后期由于学习率过⼩,可能较难找到⼀个有⽤的解。为了解决这⼀问题,RMSProp算法对AdaGrad算法做了⼀点⼩⼩的修改。

2.1 算法

在这里插入图片描述

#Rmsprop算法
import math
import torch
import sys
sys.path.append("..")
import d2lzh_pytorch as d2l
def rmsprop_2d(x1, x2, s1, s2):
    g1, g2, eps = 0.2 * x1, 4 * x2, 1e-6
    s1 = gamma * s1 + (1 - gamma) * g1 ** 2
    s2 = gamma * s2 + (1 - gamma) * g2 ** 2
    x1 -= eta / math.sqrt(s1 + eps) * g1
    x2 -= eta / math.sqrt(s2 + eps) * g2
    return x1, x2, s1, s2
def f_2d(x1, x2):
    return 0.1 * x1 ** 2 + 2 * x2 ** 2

eta, gamma = 0.4, 0.9
d2l.show_trace_2d(f_2d, d2l.train_2d(rmsprop_2d))

输出:

epoch 20, x1 -0.010599, x2 0.000000

在这里插入图片描述

2.2 从零开始实现

接下来按照RMSProp算法中的公式实现该算法。

features, labels = d2l.get_data_ch7()
def init_rmsprop_states():
    s_w = torch.zeros((features.shape[1], 1), dtype=torch.float32)
    s_b = torch.zeros(1, dtype=torch.float32)
    return (s_w, s_b)
def rmsprop(params, states, hyperparams):
    gamma, eps = hyperparams['gamma'], 1e-6
    for p, s in zip(params, states):
        s.data = gamma * s.data + (1 - gamma) * (p.grad.data)**2
        p.data -= hyperparams['lr'] * p.grad.data / torch.sqrt(s + eps)

在这里插入图片描述

d2l.train_ch7(rmsprop, init_rmsprop_states(), {'lr': 0.01, 'gamma':0.9},features, labels)

output:

loss: 0.243452, 0.049984 sec per epoch 1

在这里插入图片描述

2.3 简洁实现

通过名称为 RMSprop 的优化器⽅法,我们便可使⽤PyTorch提供的RMSProp算法来训练模型。注意,超参数 通过 alpha 指定。

#简洁实现:
d2l.train_pytorch_ch7(torch.optim.RMSprop, {'lr': 0.01, 'alpha':0.9},features, labels)

output:

loss: 0.244027, 0.057885 sec per epoch

在这里插入图片描述

2.4 小结

  • RMSProp算法和AdaGrad算法的不同在于,RMSProp算法使⽤了⼩批量随机梯度按元素平⽅的指数加权移动平均来调整学习率。

3.AdaDelta算法

除了RMSProp算法以外,另⼀个常⽤优化算法AdaDelta算法也针对AdaGrad算法在迭代后期可能较难找到有⽤解的问题做了改进。有意思的是,AdaDelta算法没有学习率这⼀超参数。

3.1 算法

在这里插入图片描述

3.2 从零开始实现

在这里插入图片描述

##AdaDelta算法
import torch
import sys
sys.path.append("..")
import d2lzh_pytorch as d2l

features,labels=d2l.get_data_ch7()

def init_adadelta_states():
    s_w,s_b=torch.zeros((features.shape[1],1),dtype=torch.float32),torch.zeros(1,dtype=torch.float32)
    delta_w,delta_b=torch.zeros((features.shape[1],1),dtype=torch.float32),torch.zeros(1,dtype=torch.float32)
    return ((s_w,delta_w),(s_b,delta_b))

def adadelta(params,states,hyperparams):
    rho,eps=hyperparams['rho'],1e-5
    for p,(s,delta) in zip(params,states):
        s[:]=rho*s+(1-rho)*(p.grad.data**2)
        g=p.grad.data*torch.sqrt((delta+eps)/(s+eps))
        p.data-=g
        delta[:]=rho*delta+(1-rho)*g*g

#使用超参数p=0.9来训练模型
d2l.train_ch7(adadelta,init_adadelta_states(),{'rho':0.9},features,labels)

output:

loss: 0.242700, 0.066821 sec per epoch

在这里插入图片描述

3.3 简洁实现

通过名称为 Adadelta 的优化器⽅法,我们便可使⽤PyTorch提供的AdaDelta算法。它的超参数可以通过 rho 来指定。

#简洁实现
d2l.train_pytorch_ch7(torch.optim.Adadelta,{'rho':0.9},features,labels)

输出:

loss: 0.273120, 0.062369 sec per epoch

在这里插入图片描述

3.4 小结

AdaDelta算法没有学习率超参数,它通过使⽤有关⾃变量更新量平⽅的指数加权移动平均的项来替代RMSProp算法中的学习率。

4.Adam算法

  • Adam算法在RMSProp算法基础上对⼩批量随机梯度也做了指数加权移动平均。下⾯我们来介绍这个算法。
  • 所以Adam算法可以看作是RMSProp算法与动量法的组合

4.1 算法

在这里插入图片描述
在这里插入图片描述

4.2 从零开始实现

按照Adam算法中的公式实现该算法,其中时间步t通过hyperparams参数传入adam函数。

#Adam算法从零开始实现
import torch
import sys
sys.path.append("..")
import d2lzh_pytorch  as d2l

features,labels=d2l.get_data_ch7()

def init_adam_states():
    v_w,v_b=torch.zeros((features.shape[1],1),dtype=torch.float32),torch.zeros(1,dtype=torch.float32)
    s_w,s_b=torch.zeros((features.shape[1],1),dtype=torch.float32),torch.zeros(1,dtype=torch.float32)
    return ((v_w,s_w),(v_b,s_b))
def adam(params,states,hyperparams):
    beta1,beta2,eps=0.9,0.999,1e-6
    for p,(v,s) in zip(params,states):
        v[:]=beta1*v+(1-beta1)*p.grad.data
        s[:]=beta2*s+(1-beta2)*p.grad.data**2
        v_bias_corr=v/(1-beta1**hyperparams['t'])
        s_bias_corr=s/(1-beta2**hyperparams['t'])
        p.data-=hyperparams['lr']*v_bias_corr/(torch.sqrt(s_bias_corr)+eps)
        hyperparams['t']+=1

#使用学习率为0.01的Adam算法来训练模型
d2l.train_ch7(adam,init_adam_states(),{'lr':0.01,'t':1},features,labels)

output:

loss: 0.244865, 0.069813 sec per epoch

在这里插入图片描述

4.3 简洁实现

通过名称为“adam”的 Trainer 实例,我们便可使⽤Gluon提供的Adam算法。

#简洁实现:
d2l.train_pytorch_ch7(torch.optim.Adam,{'lr':0.01},features,labels)

output:

loss: 0.242826, 0.065362 sec per epoch

在这里插入图片描述

4.4 小结

  • Adam算法在RMSProp算法的基础上对⼩批量随机梯度也做了指数加权移动平均。
  • Adam算法使⽤了偏差修正。
  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

明月醉窗台

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

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

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

打赏作者

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

抵扣说明:

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

余额充值