pytorch中model.train和model.eval

model.eval

  • 因为自定义的网络以及自定义的网络中的各个层都继承于nn.Module这个父类nn.Module存在一个training的属性,默认为True,所以,model.eval()使得自定义的网络以及自定义的网络中的各个层的training属性变为了False
import torch.nn as nn
class model(nn.Module):
    def __init__(self):
        super(model,self).__init__()
        self.linear = nn.Linear(10,20)
    def forward(self,x):
        return self.linear(x)
    
model = model()
print(model.training)
model.eval()
print(model.training)
print(model.linear.training)

>>> True
>>> False
>>> False
  • 该模式不会影响各层的gradient计算行为,即gradient计算和存储与training模式一样,并不是说设置为eval模式就会将模型中的requires_grad设置为false因此在该模式下不会影响各层的梯度计算,loss.backwad()也可以正常运行(通常不使用)
import torch.nn as nn
class model(nn.Module):
    def __init__(self):
        super(model,self).__init__()
        self.linear = nn.Linear(10,20)
    def forward(self,x):
        return self.linear(x)
    
model = model()
model.eval()
model.linear.weight.requires_grad
model.linear.bias.requires_grad
>>>True
>>>True
  • 在PyTorch中进行validation时,会使用model.eval()切换到测试模式
  1. 在val模式下,dropout层会让所有的激活单元都通过,而batchnorm层会停止计算和更新mean和var,直接使用在训练阶段已经学出的mean和var值(这里的学习是指在训练阶段数据前向传播的过程中累积更新的meanvar值)
import torch
class model(nn.Module):
    def __init__(self):
        super(model,self).__init__()
        self.bn = nn.BatchNorm2d(3)
    def forward(self,x):
        return self.bn(x)
    
model = model()
model.train()
print("train模式下bn层:","mean:",model.bn.running_mean,"var:",model.bn.running_var)
a = torch.rand(1,3,100,3)
b = torch.rand(1,3,100,3)
output1 = model(a)
print("train模式下bn层:","mean:",model.bn.running_mean,"var:",model.bn.running_var)

print("train模式下bn层:","mean:",model.bn.running_mean,"var:",model.bn.running_var)



model.eval()
print("eval模式下bn层:","mean:",model.bn.running_mean,"var:",model.bn.running_var)

output3 = model(a)
print("eval模式下bn层:","mean:",model.bn.running_mean,"var:",model.bn.running_var)

output4 = model(b)
print("eval模式下bn层:","mean:",model.bn.running_mean,"var:",model.bn.running_var)

输出

train模式下bn层: mean: tensor([0., 0., 0.]) var: tensor([1., 1., 1.])
train模式下bn层: mean: tensor([0.0496, 0.0484, 0.0480]) var: tensor([0.9090, 0.9089, 0.9075])
train模式下bn层: mean: tensor([0.0496, 0.0484, 0.0480]) var: tensor([0.9090, 0.9089, 0.9075])eval模式下bn层: mean: tensor([0.0496, 0.0484, 0.0480]) var: tensor([0.9090, 0.9089, 0.9075])
eval模式下bn层: mean: tensor([0.0496, 0.0484, 0.0480]) var: tensor([0.9090, 0.9089, 0.9075])
eval模式下bn层: mean: tensor([0.0496, 0.0484, 0.0480]) var: tensor([0.9090, 0.9089, 0.9075])


可以看出直接eval模式下bn层的running_mean和running_var是不变的,直接使用的训练好的参数

model.train

初始化模型的时候默认属性model.train=True,顾名思义,训练过程中会执行本该执行的过程。

with torch.no_grad

  • torch.no_grad()是一个上下文管理器,用来禁止梯度的计算,通常用在网络推断(eval)中,可以减少计算内存的使用量
  • torch.no_grad()包裹起来的部分不会被追踪梯度,虽然仍可以前向传播进行计算得到输出,但计算过程(grad_fn)不会被记录,也就不能反向传播更新参数。对非叶子节点来说:
    1. 非叶子节点的requires_grad属性变为了False
    2. 非叶子节点的grad_fn属性变为了None
import torch
import torch.nn as nn
class model(nn.Module):
    def __init__(self):
        super(model, self).__init__()
        self.linear = nn.Linear(10, 5)

    def forward(self,x):
        return self.linear(x)
        
model = model()
a = torch.rand(10,10)
output = model(a)
print(output.requires_grad)
with torch.no_grad():
    output = model(a)
print(output.requires_grad)

>>> True
>>> False

输出:with torch.no_grad 有点类似于将包含的所有tensor 设置为requires_grad=False

叶子节点和非叶子节点

  • 在Pytorch中,默认情况下,只有叶节点的梯度值能够被保留下来。非叶节点的梯度值在反向传播过程中使用完后就会被清除,不会被保留。(即调用loss.backward() 会将计算图的隐藏变量梯度清除)。

  • 被保留下来的叶子节点的梯度值会存入tensor的grad属性中,在 optimizer.step()过程中会更新叶子节点的data属性值,从而实现参数的更新。

  • 叶子节点可以通过is_leaf判断。叶子节点分为两种,一种是参数不需要更新的,一种是梯度信息需要更新,且需要保存下来的。

  1. requires_grad = False 的张量是叶子节点,类似与常数,不需要更新
  2. 神经网络层中的权值weight和偏差bias的tensor均为叶子节点,将其梯度信息保存下来,反向传播就是为了更新他们的参数。
  • 叶子节点可以看成是不依赖其他张量的张量
    在这里插入图片描述
lass model(nn.Module):
    def __init__(self):
        super(model, self).__init__()
        self.linear = nn.Linear(10, 5)

    def forward(self,x):
        return self.linear(x)

model = model()
print(model.linear.weight.is_leaf)
print(model.linear.weight.is_leaf)

>>> True
>>> True

叶子节点和非叶子节点的区别

import torch
 
x = torch.tensor(1.1, requires_grad=True)
y = x ** 2
z = y + 5.2
z.backward()
print(x.grad)
print(y.grad)
print(x.is_leaf)
print(y.is_leaf)
print(x.grad_fn)
print(y.grad_fn)

>>> tensor(4.)   # 这里是有一个梯度的累加
>>> None
>>> True
>>> False
>>> None
>>> <PowBackward0 object at 0x000001D512848430>

从这里可以看出,只有叶子节点有梯度值grad,非叶节点为None。只有非叶节点有grad_fn,叶节点为None。

在这里插入图片描述
拿神经网络举个例子,变量a就相当于神经网络中的参数(需要求梯度并且更新),那些常量就相当于你的输入,不要计算梯度,自然也不需要更新

  • detach 将其转为叶子节点,也就是让非叶子节点的grad_fn没有了,也让其requires_grad=false。其实就是一个常数。有的时候和with torch.no_grad起相同的作用。

参考链接01

02

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值