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()
切换到测试模式
- 在val模式下,
dropout
层会让所有的激活单元都通过,而batchnorm
层会停止计算和更新mean
和var,直接使用在训练阶段已经学出的mean
和var值(这里的学习是指在训练阶段数据前向传播的过程中累积更新的mean
和var
值)
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
)不会被记录,也就不能反向传播更新参数。对非叶子节点来说:- 非叶子节点的
requires_grad
属性变为了False
- 非叶子节点的
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
判断。叶子节点分为两种,一种是参数不需要更新的,一种是梯度信息需要更新,且需要保存下来的。
- requires_grad = False 的张量是叶子节点,类似与常数,不需要更新
- 神经网络层中的权值
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起相同的作用。