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. 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)
net. train( )
net. eval ( )
net. training = True
net. training, net. submodel1. training
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 )
防止验证模型时爆显存
验证模型时不需要求导,即不需要梯度计算,关闭autograd,可以提高速度,节约内存。如果不关闭可能会爆显存。
with torch. no_grad( ) :
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 )
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
print ( torch. cuda. is_available( ) )
print ( torch. cuda. device_count( ) )
print ( torch. cuda. get_device_name( 0 ) )
print ( torch. cuda. current_device( ) )
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)