nn.Dropout против F.dropout pyTorch

Используя pyTorch, есть два способа torch.nn.Dropout и torch.nn.F.Dropout.

Я изо всех сил, чтобы увидеть разницу между их использованием
- когда использовать что?
- Есть ли разница?
Я не вижу разницы в производительности, когда я их переключил.

Ответ 1

Технические различия уже были показаны в другом ответе. Однако основное отличие состоит в том, что nn.Dropout - это сам модуль факела, который имеет некоторое удобство:

Краткий пример для иллюстрации некоторых отличий:

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)
)

Так что я должен использовать?

И то, и другое полностью эквивалентно с точки зрения применения отсева, и хотя различия в использовании не так велики, есть некоторые причины nn.Dropout предпочтение nn.Dropout сравнению с nn.functional.dropout:

Выпадение предназначено для применения только во время обучения, поэтому при прогнозировании или оценке модели вы хотите, чтобы отсев был отключен.

Модуль отсева nn.Dropout удобно обрабатывает это и отключает выпадение, как только ваша модель переходит в режим оценки, в то время как функциональный выпад не заботится о режиме оценки/прогнозирования.

Даже если вы можете установить для функции dropout значение training=False чтобы отключить его, это все же не такое удобное решение, как с nn.Dropout.

Также в модуле хранится частота выпадения, поэтому вам не нужно сохранять ее в дополнительной переменной. В более крупных сетях вы можете захотеть создать разные выпадающие слои с разной скоростью выпадения - здесь nn.Dropout может повысить удобочитаемость и также может принести некоторое удобство при многократном использовании слоев.

Наконец, все модули, которые назначены для вашей модели, зарегистрированы в вашей модели. Итак, ваш модельный класс отслеживает их, поэтому вы можете просто отключить выпадающий модуль, вызвав eval(). При использовании функциональных выпадений ваша модель не знает об этом, поэтому она не будет отображаться ни в какой сводке.

Ответ 2

Если вы посмотрите на исходный код 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)

Есть F.dropout в forward() функция nn.Dropout в __init__(). Теперь это объяснение:

В PyTorch вы определяете свои Модели как подклассы torch.nn.Module.

В функции init вы должны инициализировать слои, которые хотите использовать. В отличие от keras, Pytorch выходит на более низкий уровень, и вам нужно указать размеры вашей сети, чтобы все совпадало.

В прямом методе вы указываете соединения ваших слоев. Это означает, что вы будете использовать уже инициализированные слои, чтобы повторно использовать один и тот же слой для каждого прямого прохода данных, который вы делаете.

torch.nn.Functional содержит некоторые полезные функции, такие как функции активации и операции свертки, которые вы можете использовать. Однако это не полные слои, поэтому, если вы хотите указать слой любого вида, вы должны использовать torch.nn.Module.

Вы должны использовать torch.nn.Functional conv операции, чтобы определить пользовательский уровень, например, с операцией свертки, но не определить стандартный слой свертки.

Ответ 3

Проверьте 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++.