skorch入門

PyTorchをscikit-learn風に使えるライブラリskorchを使ってみました。

この記事ではirisの分類問題を通してskorchのTutorialをやってみます。

 

環境

私の環境です。

関連リンク

Github

Documentation

インストール

pip install skorch

そのほかのインストール方法はこちら

Tutorial

前準備

skorchの使い方を理解するために、まずは簡単なirisの分類をやってみます。

はじめに、PyTorchをインポートします。

import torch
from torch import nn
import torch.nn.functional as F
torch.manual_seed(0)

scikit-learnで用意されているirisのデータセットを用意し、学習用とテスト用に分けています。

from sklearn import datasets
from sklearn.model_selection import train_test_split

iris = datasets.load_iris()
X = iris.data
Y = iris.target
train_x, test_x, train_t, test_t = train_test_split(X, Y, test_size=0.1)

次に、2層の隠れ層からなるシンプルなニューラルネットワークのモデルを作ります。irisの分類は入力が4、3つのクラスがあるので出力は3となります。今回は以下のようにしてみました。

class Classifier(nn.Module):
    def __init__(self):
        super(Classifier, self).__init__()
        self.dense0 = nn.Linear(4, 6)
        self.dense1 = nn.Linear(6, 6)
        self.output = nn.Linear(6, 3)

    def forward(self, x):
        h = F.relu(self.dense0(x))
        h = F.relu(self.dense1(h))
        y = self.output(h)
        return y

ここで、やっとskorchの出番が来ます。skorchのNeuralNetClassifierを使えばPyTorchにより定義されたモデルをscikit-learn風の分類器に渡し学習することができます。

from skorch import NeuralNetClassifier
clf = NeuralNetClassifier(
    Classifier,
    optimizer=torch.optim.Adam, # default=torch.optim.SGD
    criterion=torch.nn.CrossEntropyLoss, # default=torch.nn.NLLLoss
    max_epochs=40,
    lr=0.1, # default=0.01
    iterator_train__batch_size=20, # default=128
    iterator_train__shuffle=True
)

学習

学習を開始するにはscikit-learnと同じようにfitを使います。fitを使えばデータを勝手にtorch.Tensorに変換してくれます。

clf.fit(train_x, train_t)

エラーが出た

RuntimeError: Expected object of type torch.FloatTensor but found type torch.DoubleTensor for argument #4 'mat1'

これは要するに、torch.FloatTensorを求められているのにtorch.DoubleTensorが渡されていてエラーが起きたようです。

これを解決するにはこのページが参考になります。

irisのデータの型を見てみると、

print(iris.data.dtype)
#float64

torch.FloatTensorにするにはfloat32である必要があるので型を変換します。同様に出力の方もtorch.LongTensorでなければならないのでこちらも型を変換します。

データセットを用意する部分を以下のようにします。

import numpy as np
from sklearn import datasets
from sklearn.model_selection import train_test_split

iris = datasets.load_iris()
X = iris.data.astype(np.float32)
Y = iris.target.astype(np.int64)
train_x, test_x, train_t, test_t = train_test_split(X, Y, test_size=0.1)

これで学習することができました。

テスト

テストデータを予測するにはsciki-learnと同じようにpredictを使います。

predicted = clf.predict(test_x)
print("Accuracy {:.2f}".format(sum(x for x in test_t == predicted) / test_t.shape[0]))

ここまでのソースコード

import torch
from torch import nn
import torch.nn.functional as F
torch.manual_seed(0)


import numpy as np
from sklearn import datasets
from sklearn.model_selection import train_test_split

# データの用意
iris = datasets.load_iris()
X = iris.data.astype(np.float32)
Y = iris.target.astype(np.int64)
train_x, test_x, train_t, test_t = train_test_split(X, Y, test_size=0.1)


# ネットワークの定義
class Classifier(nn.Module):
    def __init__(self):
        super(Classifier, self).__init__()
        self.dense0 = nn.Linear(4, 6)
        self.dense1 = nn.Linear(6, 6)
        self.output = nn.Linear(6, 3)

    def forward(self, x):
        h = F.relu(self.dense0(x))
        h = F.relu(self.dense1(h))
        y = self.output(h)
        return y

from skorch import NeuralNetClassifier
# 分類器を作る
clf = NeuralNetClassifier(
    Classifier,
    optimizer=torch.optim.Adam, # default=torch.optim.SGD
    criterion=torch.nn.CrossEntropyLoss, # default=torch.nn.NLLLoss
    max_epochs=40,
    lr=0.1, # default=0.01
    iterator_train__batch_size=20, # default=128
    iterator_train__shuffle=True
)

# 学習
clf.fit(train_x, train_t)

# 予測
predicted = clf.predict(test_x)
print("Accuracy {:.2f}".format(sum(x for x in test_t == predicted) / test_t.shape[0]))

おまけ

Pipeline

scikit-learnのPipelineが使えます。上のプログラムで作ったモデルを渡しています。

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
pipe = Pipeline([
    ('scale', StandardScaler()),
    ('clf', clf)
])

pipe.fit(train_x, train_t)

GridSearch

sckit-learnのGridSearchが使えます。

from sklearn.model_selection import GridSearchCV

params = {
    'lr': [0.01, 0.1],
    'max_epochs' : [20, 40]
}

gs = GridSearchCV(clf, params, refit=False, cv=3, scoring="accuracy")
gs.fit(train_x, train_t)
print(gs.best_score_, gs.best_params_)

Q&A

GPUを使うには?

分類器を作る際に、オプションのdeviceにcudaを渡す。

clf = NeuralNetClassifier(Classifier, max_epochs=40, lr=0.1, device="cuda")

モデルを保存するには?

pickleを使います。

import pickle

方法①:モデル全体を保存

filename = "model.pkl"
with open(filename, "wb") as f:
    pickle.dump(clf, f)

方法②:モデルのパラメータだけを保存

このやり方ではlrmax_epochsといったハイパーパラメータは保存されない。

filename = "model_params.pkl"
clf.save_params(filename)

モデルを読み込むには?

モデルを方法①で保存した場合

with open(filename, "rb") as f:
    new_clf = pickle.load(f)

モデルを方法②で保存した場合

モデルを初期化した後に読み込む

new_clf = NeuralNetClassifier(
    Classifier,
    optimizer=torch.optim.Adam, # default=torch.optim.SGD
    criterion=torch.nn.CrossEntropyLoss, # default=torch.nn.NLLLoss
    max_epochs=40,
    lr=0.1, # default=0.01
    iterator_train__batch_size=20, # default=128
    iterator_train__shuffle=True
).initialize()

new_clf.load_params(filename)