黄金时代 —— Pytorch学习记录(三)

model.eval()和with torch.no_grad()的区别

  • 在PyTorch中进行validation时,会使用model.eval()切换到测试模式,在该模式下,主要用于通知dropout层和batchnorm层在train和val模式间切换
    • 在train模式下,dropout网络层会按照设定的参数p设置保留激活单元的概率(保留概率=p); batchnorm层会继续计算数据的mean和var等参数并更新。
    • 在val模式下,dropout层会让所有的激活单元都通过,而batchnorm层会停止计算和更新mean和var,直接使用在训练阶段已经学出的mean和var值。
    • 该模式不会影响各层的gradient计算行为,即gradient计算和存储与training模式一样,只是不进行反传(back propagation) (在计算梯度但是没有反传)
  • with torch.no_grad()主要是用于停止autograd模块的工作,以起到加速和节省显存的作用,具体行为就是停止gradient计算,从而节省了GPU算力和显存,但是并不会影响dropout和batchnorm层的行为

使用场景

  • 如果不在意显存大小和计算时间的话,仅仅使用model.eval()已足够得到正确的validation的结果;
  • with torch.zero_grad()则是更进一步加速和节省gpu空间(因为不用计算和存储gradient),从而可以更快计算,也可以跑更大的batch来测试。

requires_grad和detach

  • requires_grad_()会修改Tensor的requires_grad属性。不构建计算图 不算梯度
  • detach()会返回一个与计算图分离的新Tensor,新Tensor不会在反向传播中计算梯度,会在特定场合使用。浅复制
  • torch.no_grad()更节省计算资源和存储资源,其作用域范围内的操作不会构建计算图,常用在网络推断中。作用域 requires_grad

nn.ModuleList()和nn.Sequential()

链接

  • nn.ModuleList是一个储存不同 module,并自动将每个 module 的 parameters 添加到网络之中的容器。但是,我们需要注意到,nn.ModuleList 并没有定义一个网络,它只是将不同的模块储存在一起
  • 具体的模型定义还是按forward中进行定义!
class net4(nn.Module):
    def __init__(self):
        super(net4, self).__init__()
        self.linears = nn.ModuleList([nn.Linear(5, 10), nn.Linear(10, 10)])
    def forward(self, x):
        x = self.linears[0](x)
        x = self.linears[1](x)
        x = self.linears[1](x)
        return x
  • nn.Sequential:已经实现了内部的 forward 函数,而且里面的模块必须是按照顺序进行排列的,必须确保前一个模块的输出大小和下一个模块的输入大小是一致的!

OrderedDict()和nn.ModuleList

  • 前者常常和Sequential结合使用,就是一个有序字典,网络定义的层变量,其实变量名和右值其实就是一个字典Dict
  • 后者只是个存储Module的容器,没有顺序!
  • 普通list中的子module并不能被主module所识别,而ModuleList中的子module能够被主module所识别。这意味着如果用list保存子module,将无法调整其参数,因其未加入到主module的参数中。
  • 除ModuleList之外还有ParameterList,其是一个可以包含多个parameter的类list对象。在实际应用中,使用方式与ModuleList类似。

nn.Module类属性

def __init__(self):
    self._parameters = OrderedDict()
    self._modules = OrderedDict()
    self._buffers = OrderedDict()
    self._backward_hooks = OrderedDict()
    self._forward_hooks = OrderedDict()
    self.training = True
  • _parameters:字典,保存用户直接设置的parameter,self.param1 = nn.Parameter(t.randn(3, 3))会被检测到,在字典中加入一个key为’param’,value为对应parameter的item。而self.submodule = nn.Linear(3, 4)中的parameter则不会存于此。
  • _modules:子module,通过self.submodel = nn.Linear(3, 4)指定的子module会保存于此。
  • _buffers:缓存。如batchnorm使用momentum机制,每次前向传播需用到上一次前向传播的结果。
  • _backward_hooks与_forward_hooks:钩子技术,用来提取中间变量,类似variable的hook。
  • training:BatchNorm与Dropout层在训练阶段和测试阶段中采取的策略不同,通过判断training值来决定前向传播策略。

Module.train()、Module.eval() 方法和 Module.training属性的关系

import torch as t
from torch import nn
from torch.autograd import Variable as V
 
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        # 等价与self.register_parameter('param1' ,nn.Parameter(t.randn(3, 3)))
        self.param1 = nn.Parameter(t.rand(3, 3))
        self.submodel1 = nn.Linear(3, 4)
    def forward(self, input):
        x = self.param1.mm(input)
        x = self.submodel11(x)
        return x
net = Net()
print(net.training, net.submodel1.training) # True True
net.train() # 将本层及子层的training设定为True
net.eval() # 将本层及子层的training设定为False
net.training = True # 注意,对module的设置仅仅影响本层,子module不受影响
net.training, net.submodel1.training # (True, False)

Pytorch tricks

指定GPU编号

import os
os.environ["CUDA_VISIBLE_DEVICES"] = "0"

查看模型每层输出详情

  • pip install torchsummary
  • input_size=(channels, H, W) 是根据你自己的网络模型的输入尺寸进行设置
from torchsummary import summary
summary(your_model, input_size=(channels, H, W))

梯度裁剪(Gradient Clipping)

import torch.nn as nn

outputs = model(data)
loss= loss_fn(outputs, target)
optimizer.zero_grad()
loss.backward()
nn.utils.clip_grad_norm_(model.parameters(), max_norm=20, norm_type=2)
optimizer.step()
  • parameters – 一个基于变量的迭代器,会进行梯度归一化
  • max_norm – 梯度的最大范数
  • norm_type – 规定范数的类型,默认为L2

图片扩维

  • 因为在训练时的数据维度一般都是 (batch_size, c, h, w),而在测试时只输入一张图片,所以需要扩展维度,扩展维度有多个方法(tensot):
  • img = image.view(1, *image.size())
  • img = image.unsqueeze(dim=0)

one-hot 编码

  • 在PyTorch中使用交叉熵损失函数的时候会自动把label转化成onehot,所以不用手动转化,而使用MSE需要手动转化成onehot编码
onehot = m_zeros.scatter_(1, label, 1)  # (dim,index,value)

防止验证模型时爆显存

  • 验证模型时不需要求导,即不需要梯度计算,关闭autograd,可以提高速度,节约内存。如果不关闭可能会爆显存。
with torch.no_grad():
    # 使用model进行预测的代码
    pass
  • Pytorch 训练时无用的临时变量可能会越来越多,导致 out of memory ,可以使用下面语句来清理这些不需要的变量:
torch.cuda.empty_cache()
  • torch.cuda.empty_cache() 的作用就是释放缓存分配器当前持有的且未占用的缓存显存,以便这些显存可以被其他GPU应用程序中使用,并且通过 nvidia-smi命令可见。注意使用此命令不会释放tensors占用的显存。

学习率衰减

import torch.optim as optim
from torch.optim import lr_scheduler

# 训练前的初始化
optimizer = optim.Adam(net.parameters(), lr=0.001)
scheduler = lr_scheduler.StepLR(optimizer, 10, 0.1)  # # 每过10个epoch,学习率乘以0.1

# 训练过程中
for n in n_epoch:
    scheduler.step()
    ...
  • 可以随时查看学习率的值: optimizer.param_groups[0]['lr']
  • 还有其他学习率更新的方式:
  • 自定义更新公式:
    scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=lambda epoch:1/(epoch+1))
  • 不依赖epoch更新学习率:
    **lr_scheduler.ReduceLROnPlateau()**提供了基于训练中某些测量值使学习率动态下降的方法;提醒一点就是参数 mode=‘min’ 还是’max’,取决于优化的的损失还是准确率,即使用 scheduler.step(loss)还是scheduler.step(acc)

冻结某些层的参数

no_grad = [
    'cnn.VGG_16.convolution1_1.weight',
    'cnn.VGG_16.convolution1_1.bias',
    'cnn.VGG_16.convolution1_2.weight',
    'cnn.VGG_16.convolution1_2.bias'
]

net = Net.CTPN()  # 获取网络结构
for name, value in net.named_parameters():
    if name in no_grad:
        value.requires_grad = False
    else:
        value.requires_grad = True

optimizer = optim.Adam(filter(lambda p: p.requires_grad, net.parameters()), lr=0.01)

对不同层使用不同学习率

conv1_params = []
conv2_params = []

for name, parms in net.named_parameters():
    if "convolution1" in name:
        conv1_params += [parms]
    else:
        conv2_params += [parms]

# 然后在优化器中进行如下操作:
optimizer = optim.Adam(
    [
        {"params": conv1_params, 'lr': 0.01},
        {"params": conv2_params, 'lr': 0.001},
    ],
    weight_decay=1e-3,
)

CUDA函数

import torch
# 判断cuda是否可用;
print(torch.cuda.is_available())
# 获取gpu数量;
print(torch.cuda.device_count())
# 获取gpu名字;
print(torch.cuda.get_device_name(0))
# 返回当前gpu设备索引,默认从0开始;
print(torch.cuda.current_device())
# 查看tensor或者model在哪块GPU上
print(torch.tensor([0]).get_device())
use_cuda = torch.cuda.is_available()

# 方法一:
if use_cuda:
    data = data.cuda()
    model.cuda()

# 方法二:
device = torch.device("cuda" if use_cuda else "cpu")
data = data.to(device)
model.to(device)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值