どん底から這い上がるまでの記録

どん底から這い上がりたいけど這い上がれない人がいろいろ書くブログ(主にプログラミング)

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)