在 Pytorch 中使用 Dropout:nn.Dropout 与 F.dropout
Posted
技术标签:
【中文标题】在 Pytorch 中使用 Dropout:nn.Dropout 与 F.dropout【英文标题】:Pytorch: nn.Dropout vs. F.dropout 【发布时间】:2019-04-24 11:02:05 【问题描述】:使用 pyTorch 有两种方法可以退出
torch.nn.Dropout
和 torch.nn.functional.Dropout
。
我很难看出它们在使用上的区别:
什么时候用什么? 有什么不同吗?我在切换它们时没有发现任何性能差异。
【问题讨论】:
【参考方案1】:检查torch.nn.functional
的实现:
if p < 0. or p > 1.:
raise ValueError("dropout probability has to be between 0 and 1, "
"but got ".format(p))
return (_VF.dropout_(input, p, training)
if inplace
else _VF.dropout(input, p, training))
检查:torch.nn.dropout
的实现:
def forward(self, input):
return F.dropout(input, self.p, self.training, self.inplace)
所以:它们的内部操作是相同的。接口不同。至于_VF
,我猜那是一些 C/C++ 代码。
【讨论】:
【参考方案2】:其他答案中已经显示了技术差异。但主要区别在于nn.Dropout
是一个torch Module 本身,它具有一些便利性:
说明一些差异的简短示例:
import torch
import torch.nn as nn
class Model1(nn.Module):
# Model 1 using functional dropout
def __init__(self, p=0.0):
super().__init__()
self.p = p
def forward(self, inputs):
return nn.functional.dropout(inputs, p=self.p, training=True)
class Model2(nn.Module):
# Model 2 using dropout module
def __init__(self, p=0.0):
super().__init__()
self.drop_layer = nn.Dropout(p=p)
def forward(self, inputs):
return self.drop_layer(inputs)
model1 = Model1(p=0.5) # functional dropout
model2 = Model2(p=0.5) # dropout module
# creating inputs
inputs = torch.rand(10)
# forwarding inputs in train mode
print('Normal (train) model:')
print('Model 1', model1(inputs))
print('Model 2', model2(inputs))
print()
# switching to eval mode
model1.eval()
model2.eval()
# forwarding inputs in evaluation mode
print('Evaluation mode:')
print('Model 1', model1(inputs))
print('Model 2', model2(inputs))
# show model summary
print('Print summary:')
print(model1)
print(model2)
输出:
Normal (train) model:
Model 1 tensor([ 1.5040, 0.0000, 0.0000, 0.8563, 0.0000, 0.0000, 1.5951,
0.0000, 0.0000, 0.0946])
Model 2 tensor([ 0.0000, 0.3713, 1.9303, 0.0000, 0.0000, 0.3574, 0.0000,
1.1273, 1.5818, 0.0946])
Evaluation mode:
Model 1 tensor([ 0.0000, 0.3713, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
0.0000, 0.0000, 0.0000])
Model 2 tensor([ 0.7520, 0.1857, 0.9651, 0.4281, 0.7883, 0.1787, 0.7975,
0.5636, 0.7909, 0.0473])
Print summary:
Model1()
Model2(
(drop_layer): Dropout(p=0.5)
)
那么我应该使用哪个?
两者在应用 dropout 方面是完全等价的,即使在使用上的差异不是很大,也有一些理由支持nn.Dropout
而不是nn.functional.dropout
:
Dropout 设计为仅在训练期间应用,因此在对模型进行预测或评估时,您希望关闭 dropout。
dropout 模块nn.Dropout
可以方便地处理这个问题,并在您的模型进入评估模式后立即关闭 dropout,而功能 dropout 不关心评估/预测模式。
即使您可以将功能退出设置为training=False
以将其关闭,但它仍然不像nn.Dropout
那样方便。
掉率也存储在模块中,因此您不必将其保存在额外的变量中。在较大的网络中,您可能希望创建具有不同丢弃率的不同丢弃层 - 这里nn.Dropout
可能会增加可读性,并且在多次使用这些层时也可以提供一些便利。
最后,分配给您的模型的所有模块都在您的模型中注册。因此,您的模型类会跟踪它们,这就是为什么您可以通过调用 eval()
来关闭 dropout 模块。使用功能性 dropout 时,您的模型不知道它,因此它不会出现在任何摘要中。
【讨论】:
感谢“我应该使用哪个?” - 那是我缺少的部分!我通常只做F.dropout(x,training = self.training)
来处理它的火车/评估差异。所以总结一下:这是个人喜好的问题?
@Jakob 是的,完全正确! - nn.Dropout
只是打算为可用于图层样式的功能性 dropout 提供稍微更高级别的 API。但是,如果您按照描述的方式使用它,行为并没有真正的区别。
当我有多个要应用 dropout 的层时,我应该为每一层实例化一个 nn.Dropout 对象还是可以安全地重用它?一般来说:我如何知道哪些层可以重复使用,哪些不能重复使用?
我实际上在每一层都有一个(当然除了输入和输出)作为标准。
另一方面,与改变 Dropout“层”的 dropout 率相比,我发现功能 API 在 dropout 不固定时更方便。【参考方案3】:
如果查看nn.Dropout和Functional.Dropout的源代码,可以看到Functional
是一个接口,nn
模块实现了与该接口相关的功能。
查看nn
类中的实现:
from .. import functional as F
class Dropout(_DropoutNd):
def forward(self, input):
return F.dropout(input, self.p, self.training, self.inplace)
class Dropout2d(_DropoutNd):
def forward(self, input):
return F.dropout2d(input, self.p, self.training, self.inplace)
等等。
Functional
类的实现:
def dropout(input, p=0.5, training=False, inplace=False):
return _functions.dropout.Dropout.apply(input, p, training, inplace)
def dropout2d(input, p=0.5, training=False, inplace=False):
return _functions.dropout.FeatureDropout.apply(input, p, training, inplace)
看下面的例子就明白了:
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
self.conv2_drop = nn.Dropout2d()
self.fc1 = nn.Linear(320, 50)
self.fc2 = nn.Linear(50, 10)
def forward(self, x):
x = F.relu(F.max_pool2d(self.conv1(x), 2))
x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))
x = x.view(-1, 320)
x = F.relu(self.fc1(x))
x = F.dropout(x, training=self.training)
x = self.fc2(x)
return F.log_softmax(x)
在forward()
函数中有一个F.dropout
,在__init__()
函数中有一个nn.Dropout
。现在是这样的解释:
在 PyTorch 中,您将模型定义为 torch.nn.Module 的子类。
在 init 函数中,您应该初始化要使用的层。与 keras 不同,Pytorch 的级别更低,您必须指定网络的大小以使所有内容都匹配。
在 forward 方法中,您指定层的连接。这意味着您将使用已初始化的层,以便为您所做的每个前向传递数据重复使用相同的层。
torch.nn.Functional 包含一些有用的函数,例如可以使用的激活函数和卷积操作。但是,这些不是完整的层,所以如果你想指定任何类型的层,你应该使用 torch.nn.Module。
您可以使用 torch.nn.Functional conv 操作来定义自定义层,例如使用卷积操作,但不能定义标准卷积层。
【讨论】:
但是什么时候应该使用呢?这有什么不同吗? 我强烈建议您在讨论.pytorch.org 中询问有关pytorch
的问题。通过阅读问题和答案,我已经加入并学到了很多东西。
但是dropout本身没有任何参数/权重。那么为什么要将它们添加为图层呢?我有点想知道 F.dropout(x) 何时优于 nn.Dropout(反之亦然)。对我来说,他们做的完全一样。例如:F.droput(x)
和F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))
有什么区别(一个是函数,另一个是模块)?你能不能把后者换成F.relu(F.max_pool2d(F.dropout(self.conv2(x)), 2))
在上面进行编辑:为什么要将它们添加到初始函数中/以这种方式使用它们?
你也可以看到这个帖子:discuss.pytorch.org/t/…以上是关于在 Pytorch 中使用 Dropout:nn.Dropout 与 F.dropout的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Pytorch 中实现 dropout,以及在哪里应用它