Pytorch:nn.Dropout 对比 F.dropout

Pytorch: nn.Dropout vs. F.dropout

dropout有两种方式:

我问:

当我切换它们时,我没有看到任何性能差异。

如果你查看nn.Dropout and Functional.Dropout的源代码,你会发现Functional是一个接口,nn模块实现了关于这个接口的功能。
查看 nn class:

中的实现
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的实施 class:

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 的子class。

init 函数中,您应该初始化要使用的图层。与 keras 不同,Pytorch 的级别更低,您必须指定网络的大小,以便所有内容都匹配。

在 forward 方法中,您指定层的连接。这意味着您将使用已经初始化的层,以便为您制作的每个前向传递数据重复使用相同的层。

torch.nn.Functional 包含一些有用的函数,例如您可以使用的激活函数和卷积运算。然而,这些不是完整的图层,所以如果你想指定任何类型的图层,你应该使用 torch.nn.Module.

您可以使用 torch.nn.Functional conv 操作来定义自定义层,例如使用卷积操作,而不是定义标准卷积层。

其他答案中已经显示了技术差异。然而,主要区别在于 nn.Dropout 是一个 torch 模块本身,它具有一些便利性:

说明一些差异的简短示例:

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 可能会增加可读性,并且在多次使用这些层时也可以带来一些便利。

最后,分配给您的模型的所有模块都已在您的模型中注册。所以你的模型 class 会跟踪它们,这就是为什么你可以通过调用 eval() 来关闭 dropout 模块。使用功能性丢失时,您的模型不会意识到它,因此它不会出现在任何摘要中。

检查 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++ 代码。