人工知能に関する断創録

このブログでは人工知能のさまざまな分野について調査したことをまとめています(更新停止: 2019年12月31日)

積層自己符号化器の性能評価

Theanoによる積層自己符号化器の実装(2016/1/22)の続き。

今回は、積層自己符号化器の事前学習(pretraining)の効果が本当にあったのか検証してみたい。ついでにもう一つTheanoによる雑音除去自己符号化器の実装(2015/12/9)で書いた雑音除去(denoising)の効果も本当にあったのか合わせて検証してみたい。

自己符号化器自体は教師なし学習で特徴抽出しか行わないためその特徴が本当に分類に役立つのか直接検証できなかった。前回実装した積層自己符号化器は最後の層にロジスティック回帰をくっつけることで抽出した特徴を用いてMNISTの数字認識ができるようにしたので認識精度を使って雑音除去や事前学習の効果が比較できる。

ソースコード全体はここ

スクリプトの修正

前回の積層自己符号化器のスクリプトを少しだけ修正した。

  • メインの関数に事前学習を行うか行わないかのフラグ(pretrain_flag)を追加
  • pretrain_flag=Trueのときのみ事前学習フェーズを実行、pretrain_flag=Falseのときはランダムなパラメータから学習
  • 訓練ループをシンプルにし、training_epochs回数だけ回すようにした
  • テスト誤差を出力するファイルを引数で指定できるようにした
def test_stacked_autoencoder(finetune_lr=0.1, pretraining_epochs=15,
                             pretrain_lr=0.001, training_epochs=100,
                             dataset='mnist.pkl.gz', batch_size=1,
                             hidden_layers_sizes=[1000, 1000, 1000],
                             corruption_levels=[0.1, 0.2, 0.3],
                             pretrain_flag=True,
                             testerr_file='test_error.txt'):
    datasets = load_data(dataset)
    train_set_x = datasets[0][0]
    n_train_batches = train_set_x.get_value(borrow=True).shape[0] / batch_size
    numpy_rng = np.random.RandomState(89677)

    print "building the model ..."

    sda = StackedDenoisingAutoencoder(
        numpy_rng,
        28 * 28,
        hidden_layers_sizes,
        10,
        corruption_levels)

    # Pre-training
    if pretrain_flag:
        print "getting the pre-training functions ..."
        pretraining_functions = sda.pretraining_functions(train_set_x=train_set_x,
                                                      batch_size=batch_size)

        print "pre-training the model ..."
        for i in xrange(sda.n_layers):
            for epoch in xrange(pretraining_epochs):
                c = []
                for batch_index in xrange(n_train_batches):
                    c.append(pretraining_functions[i](index=batch_index,
                                                      corruption=corruption_levels[i],
                                                      lr=pretrain_lr))
                print "Pre-training layer %i, epoch %d, cost %f" % (i, epoch, np.mean(c))

    # Fine-tuning
    print "getting the fine-tuning functions ..."
    train_model, _, test_model = sda.build_finetune_functions(
        datasets=datasets,
        batch_size=batch_size,
        learning_rate=finetune_lr
    )

    print "fine-tuning the model ..."

    epoch = 0
    fp = open(testerr_file, "w")
    while (epoch < training_epochs):
        epoch = epoch + 1
        for minibatch_index in xrange(n_train_batches):
            train_model(minibatch_index)
        test_losses = test_model()
        test_score = np.mean(test_losses)
        print "Fine-tuning, epoch %d, test error %f" % (epoch, test_score * 100)
        fp.write("%d\t%f\n" % (epoch, test_score * 100))
    fp.close()

雑音除去の効果

まずは雑音除去を入れた場合と入れない場合でテスト誤差の推移を比較してみた。それ以外の条件は同じで隠れ層が3層から成るニューラルネットを構成した。

def test_denoising():
    test_stacked_autoencoder(hidden_layers_sizes=[1000, 1000, 1000],
                             corruption_levels=[0, 0, 0],
                             testerr_file="sa_error_without_denoising.txt")

    test_stacked_autoencoder(hidden_layers_sizes=[1000, 1000, 1000],
                             corruption_levels=[0.1, 0.2, 0.3],
                             testerr_file="sa_error_with_denoising.txt")

テスト誤差をプロットすると下のような結果が得られた。確かに雑音除去を入れると最終的な分類精度が高くなることがわかる。雑音除去により抽出された特徴がより良いことを示唆している。

f:id:aidiary:20160206111419p:plain

事前学習の効果

次に事前学習を入れた場合と入れない場合でテスト誤差の推移を比較してみた。それ以外の条件は同じ。

def test_pretraining():
    test_stacked_autoencoder(hidden_layers_sizes=[1000, 1000, 1000],
                             corruption_levels=[0.1, 0.2, 0.3],
                             testerr_file="sa_error_with_pretraining.txt",
                             pretrain_flag=True)

    test_stacked_autoencoder(hidden_layers_sizes=[1000, 1000, 1000],
                             corruption_levels=[0.1, 0.2, 0.3],
                             testerr_file="sa_error_without_pretraining.txt",
                             pretrain_flag=False)

テスト誤差をプロットすると下のような結果が得られた。確かに事前学習を入れると誤差が低くなることがわかる。

f:id:aidiary:20160206111615p:plain

今回は隠れ層が3層程度で比較的浅いニューラルネットだがより深くしていくとさらに事前学習の効果が出るのかもしれない(1回の実行に10時間くらいかかったので実験してないけど・・・)。ただMNISTは比較的単純なタスクなのでこれ以上モデルを複雑にしても過学習してしまう可能性が高い。もっと複雑なタスクで比較してみたほうがよいかもしれない。

『深層学習(人工知能学会編)』の3.2節でも事前学習の有用性を評価した研究がいくつか紹介されている(ただし積層自己符号化器でなく次回取り上げる深層ボルツマンマシンによる事前学習の結果)。一方で、事前学習を用いなくても十分よい性能が出せることを示す研究もあるとのこと。

まあ今回の実験でも改善は0.3%だし、前にも書いたけど事前学習フェーズはけっこう時間がかかるので費用対効果が見合うかは微妙なラインかも。

(追記)事前学習は教師なし学習なので容易に入手できる大量のラベルなしデータが使えることが利点として挙げられていた。事前学習をやっておけば少量のラベル付きデータでFine-tuningすればよい。事前学習なしでFine-tuningだけだと大量のラベル付きデータが必要になってしまう。