More than 1 year has passed since last update.

Deep Learningを使えるようにするために先日開催されたDeep Learningハッカソンに参加してきました。
pylearn2で階層型パーセプトロンを使って学習させてたのですが、意外と情報が少ない気がするので方法をまとめておきます。

実験環境

Mac OS X 10.10
python2.7.9

ソースコードはGitHubに公開しておきました。
pylearn2_AutoEncoder_MLP

pylearn2のインストール方法

インストール方法はここには記載しないので、適当にググるかこの辺を参考にしてください。

男のための機械学習
Qiitaの記事

参考にしたチュートリアル

pylearn2にはコメント付きのチュートリアルコードが用意されてて、それ読むとだいたい使い方がわかります。
チュートリアルの保存パスは以下です。
[pylearn2]->[pylearn2]->[scripts]->[tutorial]
今回は stacked_autoencoderというチュートリアルを参考にしました。

stacked_autoencoderの説明

stacked_autoencoderフォルダ内を確認すると

  • [tests]-> test_dae.py
  • dae_l1.yaml
  • dae_l2.yaml
  • dae_mlp.yaml

があると思います。これが今回使うファイルです。
yamlファイルはpylearn2使うには必須です。yamlファイルとか知らねって人は適当にこのへんのサイト眺めてください。XMLファイルとかjsonファイルみたいに設定が描かれたファイルで可読性が高いファイル形式っぽいです。
プログラマーのためのyaml入門(初級編)

このチュートリアルはAutoEncoderで各階層をプレ学習させた多段型パーセプトロンを学習させます。
AutoEncoderって何すかって人はこの辺見て参考にしてください。
AutoEncoderで特徴抽出

dae_l1.yamlとdae_l2.yamlは各層をAutoEncoderでプレ学習するための設定ファイルです。
dae_mlp.yamlは多段型パーセプトロンを学習させるための設定ファイルです。

cd tests

とテストフォルダに移動して、

python test_dae.py

と実行すると、1層目のプレ学習、2層目のプレ学習、最後中間層2つのパーセプトロンの学習をして、そのモデルが記載されたdae_mlp.pklファイルを生成してくれます。
ちなみにmlpとはMulti Layer Perceptronの略とのこと。

使用するデータセットの準備

学習に使用するデータセットはチュートリアルではMNISTという手書き文字のデータセットが使用されています。ただ、これを実行してもあまりおもしろくないので、自分でデータセットを用意してみます。
データセットの用意の仕方は色々あるけど、今回はCSVファイル形式で用意します。
データ形式としては

クラス, 特徴量1 特徴量2 特徴量3 ... 特徴量N
クラス, 特徴量1 特徴量2 特徴量3 ... 特徴量N
クラス, 特徴量1 特徴量2 特徴量3 ... 特徴量N

という形式なので、

3クラス分類だったら、

1, 特徴量
0, 特徴量
2, 特徴量
...

みたいな書き方しておけばOKです。

例えば、今回は犬猫の識別器を作ったので、犬猫画像のデータセットを用意してみます。
GitHub上のcreateCSV.pyというフォルダを準備してください。

createCSV.pyが配置されているディレクトリに

  • 犬画像を配置した"dog_train"
  • 猫画像を配置した"cat_train"
  • その他画像を配置した"other_train"

というディレクトリを用意し、各画像を配置してください。
今回犬、猫、その他の画像はここから用意しています。
ImageNetDogs 犬画像データセット
catDatabase 猫画像データセット
Holiday Dataset 風景画像データセット

そして、

python createCSV.py dog_train cat_train other_train train.csv

と実行すれば、train.csvファイルが生成されます。

同じようにテスト用画像が配置されたフォルダ用意して、

python createCSV.py dog_test cat_test other_test test.csv

と実行すればテスト用CSVファイルが配置されます。
このスクリプトでは画像をグレースケールにして、64x64に縮小したものを[0,1]にピクセル値を正規化して特徴量としています。つまり、特徴量は4096次元です。

このCSVファイルはpylearn2をインストールした時にセットしたPYLEARN2_DATA_PATHフォルダに適当に配置してください。ディレクトリを挟んで、PYLEARN2_DATA_PATH/dogcat/みたいなとこに保存してもOKです。

その後、データセット読み込み用のスクリプトを用意します。
GitHub上のdogcat_dataset.pyを開いてみてください。
これは先ほど生成したCSVファイルを読み込むためのスクリプトです。
(1度読み込んだら.npyファイルを生成して、2回目はそっち読み込むようにして速度上げてるの、
 CSVファイルを変更したらこのnpyファイルも削除するようにしてください!)

PYLEARN2_DATA_PATH内にディレクトリを生成してCSVファイルを配置した場合は、スクリプトのbase_pathのところのディレクトリ名を修正してください。

dogcat_dataset.py
def __init__(self, which_set,
        base_path = '${PYLEARN2_DATA_PATH}/dogcat',
        start = None,
        stop = None,
        preprocessor = None,
        fit_preprocessor = False,
        axes = ('b', 0, 1, 'c'),
        fit_test_preprocessor = False):

そしてこのスクリプトを
[pylearn2]->[pylearn2]->[datasets]
配下に配置します。

データセットの読み込み方としては、こんな感じで読み込みます。

from pylearn2.datasets.dogcat_dataset import DogCatDataset

train_dataset = DogCatDataset('train')
test_dataset  = DogCatDataset('test')

# クラスはy, 特徴量はXの行列に格納されてます。
target_class = train_dataset.y
target_feature = train_dataset.X

読み込めなかった場合は、何かがおかしいので見なおしてみてください。

用意したデータセットで学習

データセットが用意出来たので、学習してみます。
yamlファイル内で使用するデータセットの定義をします。
例えば、dae_l1.yamlの2~6行目を見てください。

dae_l1.yaml
dataset: &train !obj:pylearn2.datasets.mnist.MNIST {
    which_set: 'train',
    start: 0,
    stop: %(train_stop)i
},

となっているのを、こんな感じで修正します。

dae_l1.yaml
dataset: &train !obj:pylearn2.datasets.dogcat_dataset {
    which_set: 'train'
},

これで先ほど修正したデータセットを読み込んで学習してくれます。
同じように、dae_l2.yaml, dae_mlp.yamlもデータセットの読み込みを修正します。
また、dae_l1.yamlとdae_mlp.yamlは入力次元が4096に変更されたので、nvisの値を4096に修正する必要があります。

dae_l1.yaml
model: !obj:pylearn2.models.autoencoder.DenoisingAutoencoder {
    nvis : 4096,
    nhid : %(nhid)i,
    irange : 0.05,
    corruptor: !obj:pylearn2.corruption.BinomialCorruptor {
        corruption_level: .2,
    },
    act_enc: "tanh",
    act_dec: null,    # Linear activation on the decoder side.
},
dae_mlp.yaml
model: !obj:pylearn2.models.mlp.MLP {
    batch_size: %(batch_size)i,
    layers: [
             !obj:pylearn2.models.mlp.PretrainedLayer {
                 layer_name: 'h1',
                 layer_content: !pkl: "%(save_path)s/dae_l1.pkl"
             },
             !obj:pylearn2.models.mlp.PretrainedLayer {
                 layer_name: 'h2',
                 layer_content: !pkl: "%(save_path)s/dae_l2.pkl"
             },
             !obj:pylearn2.models.mlp.Softmax {
                 max_col_norm: 1.9365,
                 layer_name: 'y',
                 n_classes: 10,
                 irange: .005
             }
            ],
    nvis: 4096
},

次にtest_dae.pyを修正します。学習回数(max_epochs)が1と正直学習していないので、500ぐらいに増やします。その他データセットが変わったことで不要になるパラメータもあるので、色々パラメータ修正します。

test_dae.py
def train_layer1(yaml_file_path, save_path):

    yaml = open("{0}/dae_l1.yaml".format(yaml_file_path), 'r').read()
    hyper_params = {'batch_size': 100,
                    'monitoring_batches': 1,
                    'nhid': 1000, # 1つめの中間層のノード数は1000
                    'max_epochs': 500, # テスト回数を500回にする
                    'save_path': save_path}
    yaml = yaml % (hyper_params)
    train_yaml(yaml)


def train_layer2(yaml_file_path, save_path):

    yaml = open("{0}/dae_l2.yaml".format(yaml_file_path), 'r').read()
    hyper_params = {'batch_size': 100,
                    'monitoring_batches': 1,
                    'nvis': 1000, # 1つめの中間層のノード数と合わせる
                    'nhid': 100,  # 2つめの中間層のノード数は100
                    'max_epochs': 500, # テスト回数を500回にする
                    'save_path': save_path}
    yaml = yaml % (hyper_params)
    train_yaml(yaml)


def train_mlp(yaml_file_path, save_path):

    yaml = open("{0}/dae_mlp.yaml".format(yaml_file_path), 'r').read()
    hyper_params = {'valid_stop': 12000,
                    'batch_size': 100,
                    'max_epochs': 500,
                    'save_path': save_path}
    yaml = yaml % (hyper_params)
    train_yaml(yaml)

これでtestsディレクトリ内で

python test_dae.py

と実行すると、学習が始まります。
(最初テストするときはmax_epochsを1にしてちゃんと動くか見たほうがいいかもしれません。)

学習されたモデルをテストデータに適用

こんなコードを用意すればテストデータに学習されたモデルを適用できます。

test_calcurated_model.py
import numpy as np
import pickle
import theano
from pylearn2.datasets.dogcat_dataset import DogCatDataset

if __name__ == "__main__":

    model = pickle.load(open('dae_mlp.pkl'))
    test_data = DogCatDataset("test")

    results = model.fprop(theano.shared(test_data.X)).eval()

    nCorrect = 0
    for result, label in zip(results, test_data.y):
        if np.argmax(result) == np.argmax(label):
            nCorrect += 1

    print '%d / %d' %(nCorrect, len(test_data.X))

精度があまり良くなかったら、モデルのパラメータだったり、入力画像だったり、階層の数を変えてみるといいと思います。
階層の増やし方は、また別記事で書いてみます!

参考

http://qiita.com/icoxfog417/items/65e800c3a2094457c3a0
http://qiita.com/dsanno/items/a1d805a89e192c44730d