Skip to content

カスタムモデルを実装する

隠れ層1層の多層パーセプトロンを実装する

カスタムモデルの例として、多層パーセプトロン(MLP)をKerasで実装してみます。(以下にある実装は、tensorflow 1.10.0で動作確認しています) タスクとしては非線形モデルでないと分離できないmoonの分類をやります。 カスタムモデルを実装するには、最低でもakebono.model.WrappedModel をベースにしたモデルが実装されている必要があります。

まず、カスタマイズされたモジュールを置く名前空間をcustomizedとして以下のようなmodel.py を作成します。

from tensorflow import keras as kr
from akebono.model import WrappedModel
from akebono.utils import (
    pathjoin,
)


def _get_keras_mlp_object(n_output_dim=None):
    model = kr.Sequential()
    model.add(kr.layers.Dense(64, activation='relu'))
    model.add(kr.layers.Dropout(0.5))
    model.add(kr.layers.Dense(64, activation='relu'))
    model.add(kr.layers.Dropout(0.5))
    model.add(kr.layers.Dense(n_output_dim, activation='softmax'))
    sgd = kr.optimizers.SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True)
    model.compile(loss='categorical_crossentropy', optimizer=sgd)
    return model


class MLP(WrappedModel):
    def base_init_finished(self):
        self.reset()

    def reset(self):
        self._value = _get_keras_mlp_object(**self._init_kwargs)

    def fit(self, X, y):
        self._value.fit(X, y, **self._fit_kwargs)
        return self

    def predict(self, X):
        return self._value.predict(X)

    def predict_proba(self, X):
        return self._value.predict_proba(X)

    def dump(self, dirpath, name):
        self.value.save(pathjoin(dirpath, name + '.h5'))
        kr.backend.clear_session()
        return self

    def load(self, dirpath, name):
        self._value = kr.models.load_model(pathjoin(dirpath, name + '.h5'))
        return self

また、Kerasの場合、目的変数の入力フォーマットがone-hot型のためmoonのような1次元ベクトル型の目的変数にそのままでは対応できません。 ここはdatasetの前処理を追加します。 dataset_preprocessor.py として以下を作成します。

from tensorflow import keras as kr


def target2categorical(df):
    if 'target' in df:
        target = df['target'].values
        t_categorical = list(kr.utils.to_categorical(target.reshape(target.shape[0], 1)))
        df['target'] = t_categorical
    return df

また、上記のように変換した型の目的変数は、pandas.DataFrameの型としてはnp.arrayのリストとして表現されてしまうのでKerasのモデルに入力 する際にはnp.arrayに変換する必要があります。このような目的にはFormatterが使えるので、カスタマイズされたFormatterを追加します。 formatter.py として以下を保存します。

import numpy as np


def convert_object2nparray(ser):
    return np.array(list(ser.values)).astype(np.int32)

config.py に追加する

先程作ったカスタムモジュールを含めた以下のようなconfig.py を作成します。

train_config = {
    'dataset_config': {
        'loader_config': {
            'name': 'binary_classifier_sample_moon',
            'kwargs': {
                'n_samples': 1000,
                'noise': 0.1,
                'random_state': 0,
            },
        },
        'preprocess_func': 'target2categorical@customized.dataset_preprocessor',
    },
    'model_config': {
        'name': 'MLP@customized.model',
        'init_kwargs': {
            'n_output_dim': 2,
        },
        'fit_kwargs': {
            'epochs': 30,
            'batch_size': 10,
            'verbose': 0,
        },
        'evaluate_kwargs': {
            'cross_val_iterator': 'KFold@sklearn.model_selection',
            'cross_val_iterator_kwargs': {
                'n_splits': 5,
                'shuffle': True,
                'random_state': 0,
            },
        },
        'model_type': 'binary_classifier',
    },
    'formatter_config_for_target': {
        'name': 'convert_object2nparray@customized.formatter',
    },
    'evaluate_enabled': True,
    'fit_model_enabled': True,
    'dump_result_enabled': True,
}

今回は、MLPのdumpでHDF5で保存しているため、HDF5を扱うためのライブラリが環境にインストールされている必要があります。 また、evaluateの際、one-hot型の目的変数の場合model_typeの推論が上手くいかないのでmodel_typeを明示的に指定する必要があることに注意してください。

モデルの学習結果は、手元の環境では以下のようになりました。(実行するたび結果が変わると思います)

=== scenario summary .. tag: default ===

------------------------------------------------------------
train_id: 0

accuracy  f1_score  log_loss  precision recall    roc_auc  
0.99500   0.99475   0.17270   0.99193   0.99765   0.99936