Как передать dim ввода из метода fit в оболочку скорча? - PullRequest
2 голосов
/ 31 января 2020

Я пытаюсь включить функциональные возможности PyTorch в среду scikit-learn (в частности, Pipelines и GridSearchCV) и поэтому изучаю skorch. Стандартный пример документации для нейронных сетей выглядит как

import torch.nn.functional as F
from torch import nn
from skorch import NeuralNetClassifier

class MyModule(nn.Module):
    def __init__(self, num_units=10, nonlin=F.relu):
        super(MyModule, self).__init__()

        self.dense0 = nn.Linear(20, num_units)
        self.nonlin = nonlin
        self.dropout = nn.Dropout(0.5)
        ...
        ...
        self.output = nn.Linear(10, 2)
    ...
    ...

, где вы явно передаете входные и выходные измерения путем их жесткого кодирования в конструкторе. Однако это не совсем то, как работают интерфейсы scikit-learn, где входные и выходные измерения выводятся методом fit, а не передаются явно конструкторам. В качестве практического примера рассмотрим

# copied from the documentation
net = NeuralNetClassifier(
    MyModule,
    max_epochs=10,
    lr=0.1,
    # Shuffle training data on each epoch
    iterator_train__shuffle=True,
)

# any general Pipeline interface
pipeline = Pipeline([
        ('transformation', AnyTransformer()),
        ('net', net)
        ])

gs = GridSearchCV(net, params, refit=False, cv=3, scoring='accuracy')
gs.fit(X, y)

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

Неужели я неправильно понял, как это должно работать или нет, каково было бы предлагаемое решение? (Я думал об указании конструкторов в методе forward, где у вас уже есть X для уже готовой подгонки, но я не уверен, что это хорошая практика)?

1 Ответ

1 голос
/ 11 февраля 2020

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

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

class InputShapeSetter(skorch.callbacks.Callback):
    def on_train_begin(self, net, X, y):
        net.set_params(module__input_dim=X.shape[-1])

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

Полный пример:

import torch
import skorch
from sklearn.datasets import make_classification
from sklearn.pipeline import Pipeline
from sklearn.decomposition import PCA

X, y = make_classification()
X = X.astype('float32')

class ClassifierModule(torch.nn.Module):
    def __init__(self, input_dim=80):
        super().__init__()
        self.l0 = torch.nn.Linear(input_dim, 10)
        self.l1 = torch.nn.Linear(10, 2)

    def forward(self, X):
        y = self.l0(X)
        y = self.l1(y)
        return torch.softmax(y, dim=-1)


class InputShapeSetter(skorch.callbacks.Callback):
    def on_train_begin(self, net, X, y):
        net.set_params(module__input_dim=X.shape[-1])


net = skorch.NeuralNetClassifier(
    ClassifierModule,
    callbacks=[InputShapeSetter()],
)

pipe = Pipeline([
    ('pca', PCA(n_components=10)),
    ('net', net),
])

pipe.fit(X, y)
print(pipe.predict(X))
...