プロクラシスト

今日の寄り道 明日の近道

【Day-20】PyTorchを超絶使いやすくするsklearnラッパー『skorch』で快適ディープラーニング


スポンサーリンク

f:id:imslotter:20171220124248p:plain

データ分析ガチ勉強アドベントカレンダー 20日目。

Skorchとは

f:id:imslotter:20171220123959p:plain
  • PyTorchのsklearnラッパー
  • sklearnのインターフェースでPyTorchを使える

自分が感じたメリット

  • sklearnが行うところとPyTorchが作るところがはっきりしていて、コードがすっきりまとまる
  • sklearnの関数(fit, predictなど)がそのまま使える
  • numpy配列をそのまま学習器に投げられるので、イメージしやすい
  • 前処理、GridSearch, 評価もsklearnのものを使えて、便利
  • インストール

    git clone https://github.com/dnouri/skorch.git
    cd skorch
    # create and activate a virtual environment
    pip install -r requirements.txt
    # install pytorch version for your system (see below)
    python setup.py install
    

    ※私の環境(ubuntu14.04, python3.6.1)では、sklearn0.18.1では実行時エラーが出たので、sklearn0.19.1にアップグレードしました。

    pip install -U sklearn

    使い方

    データ読み込みはsklearn

    データの作り方などはday-7の記事を参照

    コードをみる
    %matplotlib inline
    import matplotlib.pyplot as plt
    
    import torch
    from torch import nn
    import torch.nn.functional as F
    torch.manual_seed(0);
    
    import numpy as np
    from sklearn.datasets import make_classification
    import sklearn
    X, y = make_classification(1000, 20, n_informative=10, random_state=0)
    X = X.astype(np.float32)
    X.shape, y.shape,y.mean()
    

    学習ネットワークの構築はPyTorch

    day19の記事のように、ネットワークの設計をdefine by runでしていきましょう。

    コードをみる
    class ClassifierModule(nn.Module):
        def __init__(
                self,
                num_units=10,
                nonlin=F.relu,
                dropout=0.5,
        ):
            super(ClassifierModule, self).__init__()
            self.num_units = num_units
            self.nonlin = nonlin
            self.dropout = dropout
    
            self.dense0 = nn.Linear(20, num_units)
            self.nonlin = nonlin
            self.dropout = nn.Dropout(dropout)
            self.dense1 = nn.Linear(num_units, 10)
            self.output = nn.Linear(10, 2)
    
        def forward(self, X, **kwargs):
            X = self.nonlin(self.dense0(X))
            X = self.dropout(X)
            X = F.relu(self.dense1(X))
            X = F.softmax(self.output(X), dim=-1)
            return X
    

    skorchでwrap

    skorch.net 用意されているのは2種類

    関数 用途
    NeuralNetClassifier 分類器をsklearn風に
    NeuralNetRegressor 回帰をsklearn風に

    初期化の際に、学習の仕方を決める。パラメータは、PyTorchの関数を使用できる。

    • criterion : 損失関数の設定
    • optimizer : 最適化関数の設定
    • lr : 学習率の決定

    その他色々とパラメータはあるので、skorch.netを参照。

    PyTorchのパラメータに関しては『PyTorch入門』 使い方&Tensorflow, Keras等との違いとは?を参照

    • .fitで、自動的にtorch.tensorに変換される
    • validationまでやってくれて嬉しい
    コードをみる
    from skorch.net import NeuralNetClassifier
    net = NeuralNetClassifier(
        module=ClassifierModule,
        max_epochs=20,
        lr=0.1,
        # use_cuda=True,  # uncomment this to train with CUDA
    )
    net.fit(X,y)
    y_pred = net.predict(X[:5])
    y_proba = net.predict_proba(X[:5])
    for pred, proba in zip(y_pred,y_proba):
        print("score {}: class {}".format(proba, pred))
    

    sklearnとのその他連携

    pipeline

    • スケーリングなどsklearnの処理をデータの流れに組み込める
    • 前処理の話は day-8
    コードをみる
    from sklearn.pipeline import Pipeline
    from sklearn.preprocessing import StandardScaler
    
    # データの流れるPipelineを設計
    pipe = Pipeline([
        ("scale",StandardScaler()),
        ("neuralnet", net)
    ])
    print(pipe.named_steps)
    
    pipe.fit(X,y)
    
    y_pred = pipe.predict(X[:5])
    y_proba = pipe.predict_proba(X[:5])
    for pred, proba in zip(y_pred,y_proba):
        print("score {}: class {}".format(proba, pred))
    
    • NeuralNetClassifier/NeuralNetRegressorで設定できるパラメータを調べることができる(GridSearchCV or RandomSearchCV)
    • optimizerなどもtorch.optimのリストを使って網羅的に調べられる
    • クラス内変数も module__hoge(hogeはメンバ変数)と入力することで置き換えられる
    コードをみる
    from sklearn.model_selection import GridSearchCV
    params = {
        "lr":[i*0.01 for i in range(1,5)],
        "optimizer":[torch.optim.Adam, torch.optim.Adagrad, torch.optim.SGD],
        "module__num_units":[10,20],
    }
    gs = GridSearchCV(net, params, refit=False, cv=3, scoring="accuracy")
    gs.fit(X,y)
    import pandas as pd
    df = pd.DataFrame(gs.cv_results_)
    df_scored = df.sort_values(by=["rank_test_score"])[["params","mean_test_score","std_test_score","mean_fit_time"]]
    df_scored
    

    MNIST

    MNISTを実装してみる。sklearnで書けるところはsklearnで書いて、PyTorchで書くべきところはそちらで書く。使い分けがはっきりしていてかなりいい感じ。

    # データの読み込み(sklearn)
    from skorch import NeuralNetClassifier
    from torch import nn
    from sklearn.datasets import fetch_mldata
    from sklearn.model_selection import train_test_split
    from sklearn.metrics import classification_report
    mnist = fetch_mldata('MNIST original')
    X = mnist.data.astype('float32')
    y = mnist.target.astype('int64')
    X /= 255
    XCnn = X.reshape(-1, 1, 28, 28)
    XCnn_train, XCnn_test, y_train, y_test = train_test_split(XCnn, y, test_size=0.25, random_state=42)
    # Networkの設計(PyTorch)
    class Net(nn.Module):
        def __init__(self):
            super(Net, self).__init__()
            self.conv1 = nn.Conv2d(1, 32, kernel_size=3)
            self.conv2 = nn.Conv2d(32, 64, kernel_size=3)
            self.conv2_drop = nn.Dropout2d()
            self.fc1 = nn.Linear(1600, 128) # 1600 = number channels * width * height
            self.fc2 = nn.Linear(128, 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, x.size(1) * x.size(2) * x.size(3)) # flatten over channel, height and width = 1600
            x = F.relu(self.fc1(x))
            x = F.dropout(x, training=self.training)
            x = self.fc2(x)
            x = F.softmax(x, dim=-1)
            return x
    # ラッパーを使う(skorch)
    net = NeuralNetClassifier(
        Net,
        max_epochs=10,
        lr=1,
        optimizer=torch.optim.Adadelta,
        # use_cuda=True,  # uncomment this to train with CUDA
    )
    # training
    net.fit(XCnn_train, y_train)
    
    # test
    y_pred = net.predict(XCnn_test)
    print(classification_report(y_test, y_pred))
    

    結果

    学習の様子

    結果の表示

                 precision    recall  f1-score   support
    
              0       1.00      0.99      0.99      1677
              1       0.99      0.99      0.99      1935
              2       0.99      0.99      0.99      1767
              3       0.99      0.99      0.99      1766
              4       0.99      0.99      0.99      1691
              5       0.99      0.99      0.99      1653
              6       0.99      0.99      0.99      1754
              7       0.99      0.99      0.99      1846
              8       0.98      0.99      0.99      1702
              9       0.99      0.98      0.99      1709
    
    avg / total       0.99      0.99      0.99     17500
    
    

    まとめ

    sklearnとPyTorchをつなげられる便利なライブラリを紹介しました。 PyTorchの持つ柔軟性と、sklearnの利用しやすさが相まって最高ですね。

    かなり使いやすいので、sklearnに慣れている人は、一回使ってみることをおすすめします!

    明日はどうするかな、ベイズ的な変化点検知をする予定だが、果たして、、、ではでは!

    PROCRASIST