Реализация отсева с нуля - PullRequest
0 голосов
/ 09 января 2019

Этот код пытается использовать пользовательскую реализацию dropout:

%reset -f

import torch
import torch.nn as nn
# import torchvision
# import torchvision.transforms as transforms
import torch
import torch.nn as nn
import torch.utils.data as data_utils
import numpy as np
import matplotlib.pyplot as plt
import torch.nn.functional as F

num_epochs = 1000

number_samples = 10

from sklearn.datasets import make_moons
from matplotlib import pyplot
from pandas import DataFrame
# generate 2d classification dataset
X, y = make_moons(n_samples=number_samples, noise=0.1)
# scatter plot, dots colored by class value

x_data = [a for a in enumerate(X)]
x_data_train = x_data[:int(len(x_data) * .5)]
x_data_train = [i[1] for i in x_data_train]
x_data_train

y_data = [y[i[0]] for i in x_data]
y_data_train = y_data[:int(len(y_data) * .5)]
y_data_train

x_test = [a[1] for a in x_data[::-1][:int(len(x_data) * .5)]]
y_test = [a for a in y_data[::-1][:int(len(y_data) * .5)]]

x = torch.tensor(x_data_train).float() # <2>
print(x)

y = torch.tensor(y_data_train).long()
print(y)

x_test = torch.tensor(x_test).float()
print(x_test)

y_test = torch.tensor(y_test).long()
print(y_test)

class Dropout(nn.Module):
    def __init__(self, p=0.5, inplace=False):
#         print(p)
        super(Dropout, self).__init__()
        if p < 0 or p > 1:
            raise ValueError("dropout probability has to be between 0 and 1, "
                             "but got {}".format(p))
        self.p = p
        self.inplace = inplace

    def forward(self, input):
        print(list(input.shape))
        return np.random.binomial([np.ones((len(input),np.array(list(input.shape))))],1-dropout_percent)[0] * (1.0/(1-self.p))

    def __repr__(self):
        inplace_str = ', inplace' if self.inplace else ''
        return self.__class__.__name__ + '(' \
            + 'p=' + str(self.p) \
            + inplace_str + ')'

class MyLinear(nn.Linear):
    def __init__(self, in_feats, out_feats, drop_p, bias=True):
        super(MyLinear, self).__init__(in_feats, out_feats, bias=bias)
        self.custom_dropout = Dropout(p=drop_p)

    def forward(self, input):
        dropout_value = self.custom_dropout(self.weight)
        return F.linear(input, dropout_value, self.bias)



my_train = data_utils.TensorDataset(x, y)
train_loader = data_utils.DataLoader(my_train, batch_size=2, shuffle=True)

my_test = data_utils.TensorDataset(x_test, y_test)
test_loader = data_utils.DataLoader(my_train, batch_size=2, shuffle=True)

# Device configuration
device = 'cpu'
print(device)

# Hyper-parameters 
input_size = 2
hidden_size = 100
num_classes = 2

learning_rate = 0.0001

pred = []

# Fully connected neural network with one hidden layer
class NeuralNet(nn.Module):
    def __init__(self, input_size, hidden_size, num_classes, p):
        super(NeuralNet, self).__init__()
#         self.drop_layer = nn.Dropout(p=p)
#         self.drop_layer = MyLinear()
#         self.fc1 = MyLinear(input_size, hidden_size, p)
        self.fc1 = MyLinear(input_size, hidden_size , p) 
        self.relu = nn.ReLU()
        self.fc2 = nn.Linear(hidden_size, num_classes)  

    def forward(self, x):
#         out = self.drop_layer(x)
        out = self.fc1(x)
        out = self.relu(out)
        out = self.fc2(out)
        return out

model = NeuralNet(input_size, hidden_size, num_classes, p=0.9).to(device)

# Loss and optimizer
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)  

# Train the model
total_step = len(train_loader)
for epoch in range(num_epochs):
    for i, (images, labels) in enumerate(train_loader):  
        # Move tensors to the configured device
        images = images.reshape(-1, 2).to(device)
        labels = labels.to(device)

        # Forward pass
        outputs = model(images)
        loss = criterion(outputs, labels)

        # Backward and optimize
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

    if (epoch) % 100 == 0:
        print ('Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}'.format(epoch+1, num_epochs, i+1, total_step, loss.item()))

Пользовательский выпад реализован как:

class Dropout(nn.Module):
    def __init__(self, p=0.5, inplace=False):
#         print(p)
        super(Dropout, self).__init__()
        if p < 0 or p > 1:
            raise ValueError("dropout probability has to be between 0 and 1, "
                             "but got {}".format(p))
        self.p = p
        self.inplace = inplace

    def forward(self, input):
        print(list(input.shape))
        return np.random.binomial([np.ones((len(input),np.array(list(input.shape))))],1-dropout_percent)[0] * (1.0/(1-self.p))

    def __repr__(self):
        inplace_str = ', inplace' if self.inplace else ''
        return self.__class__.__name__ + '(' \
            + 'p=' + str(self.p) \
            + inplace_str + ')'

class MyLinear(nn.Linear):
    def __init__(self, in_feats, out_feats, drop_p, bias=True):
        super(MyLinear, self).__init__(in_feats, out_feats, bias=bias)
        self.custom_dropout = Dropout(p=drop_p)

    def forward(self, input):
        dropout_value = self.custom_dropout(self.weight)
        return F.linear(input, dropout_value, self.bias)

Кажется, я выполнил функцию отсева неправильно? :

np.random.binomial([np.ones((len(input),np.array(list(input.shape))))],1-dropout_percent)[0] * (1.0/(1-self.p))

Как изменить, чтобы правильно использовать dropout?

Эти сообщения были полезны для достижения этой точки:

Выпадение Хинтона в 3-х строках Python: https://iamtrask.github.io/2015/07/28/dropout/

Создание пользовательской функции отсева: https://discuss.pytorch.org/t/making-a-custom-dropout-function/14053/2

Ответы [ 2 ]

0 голосов
/ 13 января 2019

Кажется, я выполнил функцию отсева неправильно

np.random.binomial([np.ones((len(input),np.array(list(input.shape))))],1 dropout_percent)[0] * (1.0/(1-self.p))

Фактически, вышеприведенная реализация известна как Inverted Dropout . Inverted Dropout - это то, как Dropout реализуется на практике в различных системах глубокого обучения.

Что такое перевернутый отсев?

Прежде чем перейти к перевернутому отсеву, полезно посмотреть, как выпадение работает для одного нейрона:

Так как во время фазы поезда нейрон сохраняется с вероятностью q (= 1-p), во время фазы тестирования мы должны эмулировать поведение ансамбля сетей, используемых в фазе обучения. С этой целью авторы предлагают масштабировать функцию активации с коэффициентом q на этапе тестирования, чтобы использовать ожидаемый результат, полученный на этапе обучения, в качестве единственного результата, необходимого на этапе тестирования ( Раздел 10, Мультипликативный гауссовский шум ). Таким образом:

Перевернутый отсев немного отличается. Этот подход заключается в масштабировании активаций во время фазы обучения, оставляя фазу тестирования нетронутой. Масштабный коэффициент является обратной величиной вероятности удержания 1/1-p = 1/q, таким образом:

enter image description here

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

Ссылки:


Как реализовать инвертированный выпадение Pytorch?

class MyDropout(nn.Module):
    def __init__(self, p: float = 0.5):
        super(MyDropout, self).__init__()
        if p < 0 or p > 1:
            raise ValueError("dropout probability has to be between 0 and 1, " "but got {}".format(p))
        self.p = p

    def forward(self, X):
        if self.training:
            binomial = torch.distributions.binomial.Binomial(probs=1-self.p)
            return X * binomial.sample(X.size()) * (1.0/(1-self.p))
        return weights

Как реализовать в Numpy?

import numpy as np

pKeep = 0.8
weights = np.ones([1, 5])
binary_value = np.random.rand(weights.shape[0], weights.shape[1]) < pKeep
res = np.multiply(weights, binary_value)
res /= pKeep  # this line is called inverted dropout technique
print(res)

Как реализовать в Tensorflow?

import tensorflow as tf
tf.enable_eager_execution()

weights = tf.ones(shape=[1, 5])
keep_prob = 0.8
random_tensor = keep_prob
random_tensor += tf.random_uniform(weights.shape)
# 0. if [keep_prob, 1.0) and 1. if [1.0, 1.0 + keep_prob)
binary_tensor = tf.floor(random_tensor)
ret = tf.div(weights, keep_prob) * binary_tensor
print(ret)
0 голосов
/ 13 января 2019

Возможно, вы не используете маску PyTorch и градиент не может распространяться. Вот один способ замаскировать ~ p процентов нейронов:

class Dropout(nn.Module):
    def __init__(self, p: float = 0.5, inplace: bool = False):
        super(Dropout, self).__init__()
        if p < 0 or p > 1:
            raise ValueError(
                "dropout probability has to be between 0 and 1, " "but got {}".format(p)
            )
        self.p: float = p
        self.inplace: bool = inplace

    def forward(self, weights):
        binomial = torch.distributions.binomial.Binomial(probs=self.p)
        return weights * binomial.sample(weights.size())

Эта версия работает с autograd PyTorch, поскольку она не прибегает к numpy (вы всегда должны стремиться выполнять операции с использованием PyTorch, а не numpy, если это возможно ofc).

Кроме того, этот выпадающий должен использовать флаг self.evaluation = False во время тренировки или что-то похожее, что включено для оценки. Когда для этого значения установлено значение True, веса умножаются на вероятность, а не маскируются.

Пример кода:

class Dropout(nn.Module):
    def __init__(self, p: float = 0.5, inplace: bool = False, evaluation: bool = False):
        super(Dropout, self).__init__()
        if p < 0 or p > 1:
            raise ValueError(
                "dropout probability has to be between 0 and 1, " "but got {}".format(p)
            )
        self.p: float = p
        self.inplace: bool = inplace
        self.evaluation: bool = evaluation

    def evaluation_mode(self):
        self.evaluation = True

    def forward(self, weights):
        if not self.evaluation:
            binomial = torch.distributions.binomial.Binomial(probs=self.p)
            return weights * binomial.sample(weights.size())
        return weights * self.p

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

Кроме этого, вы можете представить нам немного меньший пример и определить вашу проблему немного подробнее. Если что-то не так, оставьте комментарий с подробным объяснением проблемы (например, почему вы предполагаете, что ваша реализация неверна).

...