学习记录 --- Pytorch优化器


个人学习总结,持续更新中……

参考文献

官方教程
【学习笔记】Pytorch深度学习—优化器(一)
【学习笔记】Pytorch深度学习—优化器(二)

什么是优化器

Pytorch的优化器:
管理并更新模型中可学习参数的值,使得模型输出更接近真实标签。

分析
其中,可学习参数指 权值 和 偏置bias;
其次,优化器最主要的2大功能:
(1)管理:指优化器管理哪一部分参数;
(2)更新:优化器当中具有一些优化策略,优化器可采用这些优化策略更新模型中可学习参数的值;这一更新策略,在神经网络中通常都会采用梯度下降法。
什么是梯度下降法

<总结>
Pytorch中优化器optimizer 管理着模型中的可学习参数,并采用梯度下降法 更新着可学习参数的值。

optimizer的定义

以Adam为例:

class Adam(Optimizer):
    def __init__(self, params, lr=1e-3, betas=(0.9, 0.999), eps=1e-8,
                 weight_decay=0, amsgrad=False):
import torch

# 构建1个2×2大小的随机张量,并开放"梯度"
weight = torch.tensor([[1., 2.], [3., 4.]], requires_grad=True)
# 构建1个2×2大小的全1梯度张量
weight.grad = torch.ones((2, 2))
# 将可学习参数[weight]传入优化器
optimizer1 = torch.optim.Adam([weight], lr=0.01, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, amsgrad=False)  
# optimizer2 = torch.optim.Adam([weight])  
import torch
import torch.nn as nn

initial_lr = 0.1


class model(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(in_channels=3, out_channels=3, kernel_size=(3, 3))
        self.conv2 = nn.Conv2d(in_channels=3, out_channels=3, kernel_size=(3, 3))

    def forward(self, x):
        pass


net = model()
optimizer = torch.optim.Adam(net.parameters(), lr=initial_lr)

optimizer的属性

defaults

优化器超参数,用来存储学习率、momentum的值等等;

import torch
import torch.nn as nn

initial_lr = 0.1


class model(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(in_channels=3, out_channels=3, kernel_size=(3, 3))
        self.conv2 = nn.Conv2d(in_channels=3, out_channels=3, kernel_size=(3, 3))

    def forward(self, x):
        pass


net = model()
optimizer = torch.optim.Adam(net.parameters(), lr=0.01, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, amsgrad=False)

print(optimizer.defaults)
'''
{'lr': 0.01, 'betas': (0.9, 0.999), 'eps': 1e-08, 'weight_decay': 0, 'amsgrad': False}
'''

state

参数的缓存,如momentum的缓存;采用momentum时会使用前几次更新时使用的梯度,也就是前几次的梯度,把前几次的梯度值缓存下来,在本次更新中使用;

import torch
import torch.nn as nn

initial_lr = 0.1


class model(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(in_channels=3, out_channels=3, kernel_size=(3, 3))
        self.conv2 = nn.Conv2d(in_channels=3, out_channels=3, kernel_size=(3, 3))

    def forward(self, x):
        pass


net = model()
optimizer = torch.optim.Adam(net.parameters(), lr=0.01, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, amsgrad=False)

print(optimizer.state)
'''
defaultdict(<class 'dict'>, {})
'''

param_groups

管理的参数组;优化器最重要的属性,已经知道优化器是管理可学习参数,这一系列可学习参数就放在param_groups这一属性中,同时,这一参数组定义为list。
param_groups=[{‘params’:param_groups,‘lr’: 0.01,}]
因此,param_groups是1个list。而在list[ ] 中,每一个元素又是1个字典{ } ,这些字典中有很多key,其中最重要的key是-‘params’,只有’params’当中才会存储训练模型的参数。

import torch

# 构建1个2×2大小的随机张量,并开放"梯度"
weight = torch.tensor([[1., 2.], [3., 4.]], requires_grad=True)
# 构建1个2×2大小的全1梯度张量
weight.grad = torch.ones((2, 2))
# 将可学习参数[weight]传入优化器
optimizer = torch.optim.Adam([weight], lr=0.01, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, amsgrad=False)

print(optimizer.param_groups)
'''
[{'params': [tensor([[1., 2.],
        [3., 4.]], requires_grad=True)], 'lr': 0.01, 'betas': (0.9, 0.999), 'eps': 1e-08, 'weight_decay': 0, 'amsgrad': False}]
'''

optimizer的方法

zero_grad()

清空所管理参数的梯度
Pytorch中tensor特性:tensor张量梯度不自动清零

已知参数param是1个特殊的张量,张量当中都会有梯度grad。由于Pytorch中张量tensor的梯度grad是不会自动清零的,它会在每一次backward反向传播时采用autograd计算梯度,并把梯度值累加到张量的grad属性中的。
由于Pytorch中的grad属性不自动清零,因此每计算1次梯度就自动累加到grad属性中造成错误;因此,一定要在使用完梯度后或者进行梯度求导(反向传播)之间通过zero_grad进行清零。
在这里插入图片描述

import torch

# 构建1个2×2大小的随机张量,并开放"梯度"
weight = torch.tensor([[1., 2.], [3., 4.]], requires_grad=True)
# 构建1个2×2大小的全1梯度张量
weight.grad = torch.ones((2, 2))
# 将可学习参数[weight]传入优化器
optimizer = torch.optim.Adam([weight], lr=0.01, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, amsgrad=False)

print(weight.grad)
'''
tensor([[1., 1.],
        [1., 1.]])
'''

optimizer.zero_grad()

print(weight.grad)
'''
tensor([[0., 0.],
        [0., 0.]])
'''

step()

执行一步更新
当计算得到Loss,利用Loss进行backward反向传播计算各个参数的梯度之后,采用step()进行一步更新,更新权值参数。step()会采用梯度下降的策略,具体方法有很多种,比如随机梯度下降法、momentum+动量方法、autograd自适应学习率等等一系列优化方法。

参数_new = 参数_old + 负梯度值 * 学习率
import torch

# 构建1个2×2大小的随机张量,并开放"梯度"
weight = torch.tensor([[2., 3.], [4., 5.]], requires_grad=True)
# 构建1个2×2大小的全1梯度张量
weight.grad = torch.ones((2, 2))
# 将可学习参数[weight]传入优化器
optimizer = torch.optim.Adam([weight], lr=1.0, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, amsgrad=False)

print(weight.data)
'''
tensor([[2., 3.],
        [4., 5.]])
'''

optimizer.step()  # 修改lr=0.1观察结果

print(weight.data)
'''
tensor([[1.0000, 2.0000],
        [3.0000, 4.0000]])
'''

add_param_group()

添加参数组
add_param_group()添加一组参数到优化器中。已知优化器管理很多参数,这些参数是可以分组;对于不同组的参数,有不同的超参数设置,例如在某一模型中,希望特征提取部分的权值参数的学习率小一点,学习更新慢一点,这时可以把特征提取的参数设置为一组参数,而对于后面全连接层,希望其学习率大一点,学习快一点。这时,可以把整个模型参数设置为两组,一组为特征提取部分的参数,另一部分是全连接层的参数,对这两组设置不同的学习率或超参数,这时就需要用到参数组概念。

import torch

# 构建1个2×2大小的随机张量,并开放"梯度"
weight = torch.tensor([[2., 3.], [4., 5.]], requires_grad=True)
# 构建1个2×2大小的全1梯度张量
weight.grad = torch.ones((2, 2))
# 将可学习参数[weight]传入优化器
optimizer = torch.optim.Adam([weight], lr=1.0, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, amsgrad=False)

print(optimizer.param_groups)
'''
[{'params': [tensor([[2., 3.],
        [4., 5.]], requires_grad=True)], 'lr': 1.0, 'betas': (0.9, 0.999), 'eps': 1e-08, 'weight_decay': 0, 'amsgrad': False}]
'''

w2 = torch.randn((3, 3), requires_grad=True)

optimizer.add_param_group({"params": w2, 'lr': 0.0001})

print(optimizer.param_groups)

'''
[{'params': [tensor([[2., 3.],
        [4., 5.]], requires_grad=True)], 'lr': 1.0, 'betas': (0.9, 0.999), 'eps': 1e-08, 'weight_decay': 0, 'amsgrad': False}, {'params': [tensor([[ 0.2155,  1.3953, -0.2814],
        [ 1.3192,  2.0449,  1.6898],
        [ 2.0740, -1.5179, -0.1514]], requires_grad=True)], 'lr': 0.0001, 'betas': (0.9, 0.999), 'eps': 1e-08, 'weight_decay': 0, 'amsgrad': False}]
'''
import torch

# 构建1个2×2大小的随机张量,并开放'梯度'
weight = torch.tensor([[1., 2.], [3., 4.]], requires_grad=True)
# 构建1个2×2大小的全1梯度张量
weight.grad = torch.ones((2, 2))
# 将可学习参数[weight]传入优化器
optimizer = torch.optim.Adam([weight], lr=1)  # 0.1

w2 = torch.tensor([[11., 12.], [13., 14.]], requires_grad=True)
w3 = torch.tensor([[11., 12.], [13., 14.]], requires_grad=True)
# 构建字典,key中 params中放置参数 w2,利用方法 add_param_group把这一组参数加进来
optimizer.add_param_group({"params": [w2,w3], 'lr': 0.0001})
# optimizer.add_param_group({"params": w3, 'lr': 0.0001})

state_dict()、load_state_dict()

在这里插入图片描述

import torch

# 构建1个2×2大小的随机张量,并开放'梯度'
weight = torch.tensor([[1., 2.], [3., 4.]], requires_grad=True)
# 构建1个2×2大小的全1梯度张量
weight.grad = torch.ones((2, 2))
# 将可学习参数[weight]传入优化器
optimizer = torch.optim.Adam([weight], lr=1)  # 0.1

w2 = torch.tensor([[11., 12.], [13., 14.]], requires_grad=True)
w3 = torch.tensor([[11., 12.], [13., 14.]], requires_grad=True)
# 构建字典,key中 params中放置参数 w2,利用方法 add_param_group把这一组参数加进来
optimizer.add_param_group({"params": [w2,w3], 'lr': 0.0001})
# optimizer.add_param_group({"params": w3, 'lr': 0.0001})

opt_state_dict = optimizer.state_dict()

# 打印step()更新之前的状态信息字典
print("state_dict before step:\n", opt_state_dict)

optimizer.step()

# 打印step()更新之后的状态信息字典
print("state_dict after step:\n", optimizer.state_dict())
# 保存更新之后的状态信息字典在当前文件夹下名为 optimizer_state_dict.pkl的文件
torch.save(optimizer.state_dict(), 'optimizer_state_dict.pkl')

# -------------------------load state_dict --------------
# 重新构建优化器
optimizer = torch.optim.Adam([weight], lr=0.1)
optimizer.add_param_group({"params": [w2,w3], 'lr': 0.0001})
# optimizer.add_param_group({"params": w3, 'lr': 0.0001})
# 创建加载状态名
state_dict = torch.load("optimizer_state_dict.pkl")

print("state_dict before load state:\n", optimizer.state_dict())

# 利用load_state_dict方法加载状态信息,接着当前状态往下训练
optimizer.load_state_dict(state_dict)
print("state_dict after load state:\n", optimizer.state_dict())

优化一个网络

在这里插入图片描述

import torch
import torch.nn as nn

initial_lr = 0.1


class model(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=(3, 3))
        self.conv2 = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=(3, 3))

    def forward(self, x):
        pass


net_1 = model()

optimizer_1 = torch.optim.Adam(net_1.parameters(), lr=initial_lr)
print("******************optimizer_1*********************")
print("optimizer_1.defaults:\n", optimizer_1.defaults)
print("optimizer_1.param_groups:\n", optimizer_1.param_groups)

'''
******************optimizer_1*********************
optimizer_1.defaults:
 {'lr': 0.1, 'betas': (0.9, 0.999), 'eps': 1e-08, 'weight_decay': 0, 'amsgrad': False}
optimizer_1.param_groups:
 [{'params': [Parameter containing:
tensor([[[[-0.2143, -0.3299,  0.0063],
          [ 0.1602, -0.0350,  0.0579],
          [-0.1537,  0.0446,  0.0909]]]], requires_grad=True), Parameter containing:
tensor([-0.2204], requires_grad=True), Parameter containing:
tensor([[[[ 0.1074, -0.1432,  0.0954],
          [ 0.1079,  0.3233, -0.2487],
          [-0.1410,  0.1557,  0.0839]]]], requires_grad=True), Parameter containing:
tensor([-0.1368], requires_grad=True)], 'lr': 0.1, 'betas': (0.9, 0.999), 'eps': 1e-08, 'weight_decay': 0, 'amsgrad': False}]
'''

同时优化多个网络

在这里插入图片描述

当成一个网络优化

这种方法,每个网络的学习率是相同的。

optimizer = torch.optim.Adam([*net_1.parameters(), *net_2.parameters()], lr = initial_lr)

当成多个网络优化

这样可以很容易的让多个网络的学习率各不相同。

optimizer_3 = torch.optim.Adam([{"params": net_1.parameters()}, {"params": net_2.parameters()}], lr = initial_lr)

optimizer_3 = torch.optim.Adam( [{"params": net_1.parameters(), 'lr': initial_lr}, {"params": net_2.parameters(), 'lr': initial_lr}])
import torch
import torch.nn as nn

initial_lr = 0.1


class model(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=(3,3))
        self.conv2 = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=(3,3))

    def forward(self, x):
        pass


net_1 = model()
net_2 = model()

optimizer_2 = torch.optim.Adam([*net_1.parameters(), *net_2.parameters()],
                               lr=initial_lr)
# optimizer_2 = torch.opotim.Adam(itertools.chain(net_1.parameters(), net_2.parameters())) # 和上一行作用相同
print("******************optimizer_2*********************")
print("optimizer_2.defaults:", optimizer_2.defaults)
print("optimizer_2.param_groups长度:", len(optimizer_2.param_groups))
print("optimizer_2.param_groups一个元素包含的键:", optimizer_2.param_groups[0].keys())
print()

optimizer_3 = torch.optim.Adam([{
    "params": net_1.parameters()
}, {
    "params": net_2.parameters()
}],
                               lr=initial_lr)
print("******************optimizer_3*********************")
print("optimizer_3.defaults:", optimizer_3.defaults)
print("optimizer_3.param_groups长度:", len(optimizer_3.param_groups))
print("optimizer_3.param_groups一个元素包含的键:", optimizer_3.param_groups[1].keys())

optimizer_4 = torch.optim.Adam([{
    "params": net_1.parameters(),
    'lr': initial_lr
}, {
    "params": net_2.parameters(),
    'lr': initial_lr
}])
print("******************optimizer_4*********************")
print("optimizer_4.defaults:", optimizer_4.defaults)
print("optimizer_4.param_groups长度:", len(optimizer_4.param_groups))
print("optimizer_4.param_groups一个元素包含的键:", optimizer_4.param_groups[1].keys())
'''
******************optimizer_2*********************
optimizer_2.defaults: {'lr': 0.1, 'betas': (0.9, 0.999), 'eps': 1e-08, 'weight_decay': 0, 'amsgrad': False}
optimizer_2.param_groups长度: 1
optimizer_2.param_groups一个元素包含的键: dict_keys(['params', 'lr', 'betas', 'eps', 'weight_decay', 'amsgrad'])

******************optimizer_3*********************
optimizer_3.defaults: {'lr': 0.1, 'betas': (0.9, 0.999), 'eps': 1e-08, 'weight_decay': 0, 'amsgrad': False}
optimizer_3.param_groups长度: 2
optimizer_3.param_groups一个元素包含的键: dict_keys(['params', 'lr', 'betas', 'eps', 'weight_decay', 'amsgrad'])
******************optimizer_4*********************
optimizer_4.defaults: {'lr': 0.001, 'betas': (0.9, 0.999), 'eps': 1e-08, 'weight_decay': 0, 'amsgrad': False}
optimizer_4.param_groups长度: 2
optimizer_4.param_groups一个元素包含的键: dict_keys(['params', 'lr', 'betas', 'eps', 'weight_decay', 'amsgrad'])
'''

只优化网络的某些指定的层

optimizer = optim.SGD( [{'params': model.layer0.parameters(), "lr": 0.01}, {'params': model.layer2.parameters(), "lr": 0.01}])
from torch import optim
from torch import nn
import torch


class MLP(nn.Module):
    def __init__(self, in_dim, hid_dim1, hid_dim2, out_dim):
        super(MLP, self).__init__()
        self.layer0 = nn.Linear(in_dim, hid_dim1)
        self.layer1 = nn.ReLU()
        self.layer2 = nn.Linear(hid_dim1, hid_dim2)
        self.layer3 = nn.ReLU()
        self.layer4 = nn.Linear(hid_dim2, out_dim)
        self.layer5 = nn.ReLU()

    def forward(self, x):
        x = self.layer0(x)
        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.layer4(x)
        x = self.layer5(x)
        return x


model = MLP(10, 3, 3, 10)
optimizer = optim.SGD(
    [{'params': model.layer0.parameters(), "lr": 0.01}, {'params': model.layer2.parameters(), "lr": 0.01}])

data = torch.randn(10, 10)
label = torch.Tensor([1, 0, 4, 7, 9, 2, 4, 5, 3, 2]).long()
criterion = nn.CrossEntropyLoss()
for i in range(100):
    output = model(data)
    loss = criterion(output, label)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    if i == 0:
        print('model.layer0.parameters(): \n', [x for x in model.layer0.parameters()])
        print('model.layer4.parameters(): \n', [x for x in model.layer4.parameters()])
    optimizer.step()
    if i == 99:
        print('model.layer0.parameters(): \n', [x for x in model.layer0.parameters()])
        print('model.layer4.parameters(): \n', [x for x in model.layer4.parameters()])

调整学习率

torch.optim.lr_scheduler:调整学习率

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值