深層学習の自己符号化器を4行で記述できるライブラリを作りました

深層学習(DeepLeanring)の事前学習や特徴抽出に使われる積層自己符号化器(stacked auto-encoder)を簡単に記述するためのライブラリ(Chainerインターフェイス)を作りました。名前はzChainerです。

zChainer - scikit-learn like interface and stacked autoencoder for chainer

なぜ作ったか

ニューラルネットワークを簡単に実装することができるライブラリとしてChainerが多くの方に利用されていますが、ほぼ同じ記述を繰り返し書く必要があったり、学習器の定義・評価の記述が煩雑になりがちであるという課題があります。そこでインターフェイスとなるライブラリがあれば便利だと思って開発しました。scikt-learn likeに使うためにscikit-chainerとxchainerを参考にさせていただきました。

作ってみると積層自己符号化器の実装も共通なコードが多いことに気付いたので、本ライブラリは積層自己符号化器を管理するところまでインターフェイスに含めてみました。後述のサンプルコードのとおり、少ない行数(整形のための改行を除けばわずか4行)で記述することができます。

機能

  • Chainerをscikit-learnの学習器として使用できる
  • 少ない行数でニューラルネットワークを記述できる
  • 少ない行数で積層自己符号化器を記述できる
  • 出力先を指定するだけでログやシリアライズされたモデルを出力できる

インストール方法

$ pip install zChainer

サンプルコード

import numpy as np
import chainer.functions as F
import chainer.links as L
from chainer import ChainList, optimizers
from zChainer import NNAutoEncoder, utility

data = (..).astype(np.float32)

# encoderの定義
# 例)ノード数784-200-100のネットワーク
# 学習後に出力層(MNISTの場合はノード数10)をadd_linkする
encoder = ChainList(
    L.Linear(784, 200),
    L.Linear(200, 100))

# decoderの定義
# encoderとノード数を反対にしたLinkをChainする
decoder =ChainList(
    L.Linear(200, 784),
    L.Linear(100, 200))

# NNAutoEncoderインスタンスの作成
ae = NNAutoEncoder(encoder, decoder, optimizers.Adam(), epoch=100, batch_size=100,
    log_path="./ae_"+utility.now()+"_log.csv", export_path="./ae_"+utility.now()+".model")

# 学習
ae.fit(data)

forward関数にはdropoutありのreluを登録していますが、好みのforward関数をセットすることもできます。詳しくはgithubのexampleをご確認ください。

実装

class NNAutoEncoder ():
    def __init__(self, encoder, decoder, optimizer,
        epoch=20, batch_size=100, log_path="", export_path=""):
        self.encoder = encoder
        self.decoder = decoder
        self.optimizer = optimizer
        self.epoch = epoch
        self.batch_size = batch_size
        self.log_path = log_path
        self.export_path = export_path
        self.autoencoded = ChainList()

    def fit(self, x_train):
        for layer in range(0, len(self.encoder)):
            # Creating model
            self.model = ChainList(self.encoder[layer].copy(), self.decoder[layer].copy())
            NNManager.forward = self.forward
            nn = NNManager(self.model, self.optimizer, F.mean_squared_error,
                self.epoch, self.batch_size, self.log_path)

            # Training
            x_data = self.encode(x_train, layer).data
            nn.fit(x_data, x_data)
            self.autoencoded.add_link(nn.model[0].copy())

        if self.export_path != "":
            pickle.dump(self.autoencoded, open(self.export_path, 'wb'), -1)
        return self

    def predict(self, x_test):
        raise Exception("Prediction for AutoEncoder is not implemented.")

    def encode(self, x, n):
        if n == 0:
            return Variable(x)
        else:
            h = self.encode(x, n-1)
            return F.relu(self.autoencoded[n-1](h))

    def forward(self, x):
        h = F.dropout(F.relu(self.model[0](x)))
        return F.dropout(F.relu(self.model[1](h)))

Chainer1.5のLink and Chainを使用しています。ネットワークの構造をChainではなくChainListとして管理することで各層に添字でアクセスできるようにして、学習が済んだものをself.autoencodedにコピーしています。学習済みモデル最先端の層からの出力はencoded関数を再帰的に呼ぶことで得ています。(層が深くなるにつれて遅くなるかも?時間ができれば改良したい)

まとめ

積層自己符号化器を含めたChainerのインターフェイスとなるライブラリを作成しました。時間の都合で十分検証ができていないので、不具合などあるかもしれません。おかしいところがあれば連絡いただけると嬉しいです。バージョン1.0になるまで使用は自己責任でお願いします。

https://github.com/shoya140/zChainer