Keras as wrapper of Theano & TensorFlow

Kerasは，元々Deep LearningフレームワークTheanoと組み合わせて使うライブラリであったが，最近のアップデートでGoogle製のTensorFlowもバックエンドとして使えるようになっている．

> Keras: Deep Learning library for Theano and TensorFlow
>
> You have just found Keras.
>
>  Keras is a minimalist, highly modular neural networks library, written in Python and capable of running either on top of either TensorFlow or Theano. It was developed with a focus on enabling fast experimentation. Being able to go from idea to result with the least possible delay is key to doing good research.

Theanoは，テンソル演算の基本部分を中心にサポートしていることから，Theanoをベースに，より抽象化を図ったライブラリがいくつかあるのはリーズナブルであるのに対し，より広範囲な機能をサポートするTensorFlowをKerasがサポートしたことについては，個人的に少し意外だった．以下，簡単なコードを作成しながら状況を調べてみた．

（プログラム環境は，Linux Ubuntu 14.04, Python 2.7.11, Theano ver.0.7.0, TensorFlow ver.0.6.0, Keras ver.0.3.0 になります．）

## インストールから'mnist_mlp.py'を動かすまで
Theano, ThensorFlowは，前にGPUを使える環境にインストールしてある．今回は，"Keras"を追加した．

```bash
> git clone https://github.com/fchollet/keras.git
> cd keras
> python setup.py install
```

examplesのディレクトリをのぞくと代表的な問題についてサンプルコードが用意されている．

```bash
> ls examples
addition_rnn.py  imdb_bidirectional_lstm.py  kaggle_otto_nn.py        mnist_mlp.py
babi_memnn.py    imdb_cnn.py                 lstm_text_generation.py  mnist_transfer_cnn.py
babi_rnn.py      imdb_cnn_lstm.py            mnist_cnn.py             reuters_mlp.py
cifar10_cnn.py   imdb_lstm.py                mnist_irnn.py

```

この中で'mnist_mlp.py'がMNIST（手書き数字の分類問題）の例題であったので，これを実行したところ，最初，エラーが発生した．

```bash
Traceback (most recent call last):
  File "mnist_mlp.py", line 50, in <module>
    model.compile(loss='categorical_crossentropy', optimizer=rms)
...
（中略）
...
  File "build/bdist.linux-x86_64/egg/keras/activations.py", line 25, in relu
  File "build/bdist.linux-x86_64/egg/keras/backend/theano_backend.py", line 463, in relu
AttributeError: 'module' object has no attribute 'relu'

```

これは，'minist_mlp.py'の中で活性化関数として 'relu' を指定していたが，もともとAnacondaでインストールしていたTheano version ver.0.7.0ではサポートされていないものであったためである．（Thenoのドキュメント上は，ver.0.7.1から'relu'をサポートと書かれている．）但し，GitHubに置かれているTheanoでは(ver.0.7.0ながら）'relu'があるとのことなので，これにupgadeしたところ上記エラーは解消した．

```text
（前略）
...
Epoch 20/20
1s - loss: 0.0092 - acc: 0.9974 - val_loss: 0.0555 - val_acc: 0.9842
Test score: 0.0554862711126
Test accuracy: 0.9842
```

Testの分類精度は，約98.4 %となっている．

## バックエンドを'Theano'から’TensorFlow'に変更
バンクエンドを変更するにあたり，方法は２つある．
1. ホームディレクトリ下にある環境ファイル("$HOME/.keras/keras.json")の変更
"keras.json"は，`{"epsilon": 1e-07, "floatx": "float32", "backend": "theano"}` の一行のファイルなので，一番最後の項，"theano"を"thensorfolw"に変更してあげればよい．
2. 環境変数 "KERAS_BACKEND" を変更．
環境変数が定義されていなければ，上記keras.jsonの値が設定される．環境変数が設定されればそちらの設定が優先される．

```bash
export KERAS_BACKEND=tensorflow
```

これだけで，同じKeras用コードがTensorFlowライブラリを使って実行されるようになった．（とっても簡単！）２つの方法の比較では，環境変数設定の方が手軽な印象である．（もちろん，theonoに戻すには上記環境変数を`unset`するか，'theano'に設定すればよい．）

因みにこちらにパフォーマンスに関するベンチマークテストの結果が掲載されているので引用する．
https://github.com/fchollet/keras/wiki/Keras,-now-running-on-TensorFlow

|       Task                       |     TensorFlow        |      Theano  |
|----------------------------------|-----------------------|--------------|        
| mnist_mlp.py: compilation (s)    |  **0.6**              |      5.9     | 
| mnist_mlp.py: runtime/epoch (s)  |    7.5                |     **6.3**  |
| imdb_lstm.py: compilation (s)    |  39.3                 |   **38.3**   |
| imdb_lstm.py: runtime/epoch (s)  |  283                  |    **123**   | 
| mnist_cnn.py: compilation (s)    |  **0.8**              |       11.4   |
| mnist_cnn.py: runtime/epoch (s)  | **190**               |       3230   |

同じ問題に対し同じコードを走らせても，バンクエンドにより結構，所要時間が変わることがわかる．（CPU演算によるものだそうです．）

## 自分のコードで試してみる
最近投稿の記事で扱った，TensorFlow版/Theano版のWine分類MLPコードをKeras版にしてみた．Tutorial用として書いた短いコードだが，Keras版ではさらにコンパクトなコードとなった．
（元のコードは，[TensorFlow版](http://qiita.com/TomokIshii/items/2cab778a3192d561a1ef), [Theano版](http://qiita.com/TomokIshii/items/0bae5d3468ab67f5d7e5) を参照ください．）

```py
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import numpy as np
np.random.seed(1337)  # for reproducibility

from keras.models import Sequential
from keras.layers.core import Dense, Activation, Dropout
from keras.regularizers import l2
from keras.optimizers import SGD, Adagrad

from input_winedata import load_data

if __name__ == '__main__':
    WineData = '../../Data/Wine/wine.data'
    train_x, train_y, test_x, test_y = load_data(WineData)
    print(train_x.shape[0], 'train samples')
    print(test_x.shape[0], 'test samples')
     
    model = Sequential()
    model.add(Dense(20, input_shape=(13,), W_regularizer=l2(0.001)))
    model.add(Activation('sigmoid'))
    model.add(Dropout(0.05))
    model.add(Dense(20, W_regularizer=l2(0.001)))
    model.add(Activation('sigmoid'))
    model.add(Dropout(0.05))
    model.add(Dense(3, W_regularizer=l2(0.001)))
    model.add(Activation('softmax'))
    
    adagrad = Adagrad(lr=0.01, epsilon=1e-08)
    model.compile(loss='categorical_crossentropy', optimizer=adagrad)
    
    batch_size = 16
    nb_epoch = 1000

    print('Train...')
    model.fit(train_x, train_y,
          batch_size=batch_size, nb_epoch=nb_epoch,
          show_accuracy=False, verbose=2,
          validation_data=(test_x, test_y))
    score = model.evaluate(test_x, test_y,
                       show_accuracy=True, verbose=0)
    print('Test score:', score[0])
    print('Test accuracy:', score[1])

```

以下，詳細を確認していく．

初めにデータを入力した後，行っているのが，ネットワークモデル(MLP)の定義である．Theano，TensorFlowでも隠れ層のレイヤーClass，出力層レイヤーClassを定義しておけば，メインのMLP定義部分はかなり簡潔にまとめることができるが，Kerasでは自分でClassを作成することなく，以下のコードでネットワークが定義できてしまう．

```py
    model = Sequential()
    model.add(Dense(20, input_shape=(13,), W_regularizer=l2(0.001)))
    model.add(Activation('sigmoid'))
    model.add(Dropout(0.05))
    model.add(Dense(20, W_regularizer=l2(0.001)))
    model.add(Activation('sigmoid'))
    model.add(Dropout(0.05))
    model.add(Dense(3, W_regularizer=l2(0.001)))
    model.add(Activation('softmax'))
```

最初の `model=Sequential()` で順伝搬型のネットワークを定義することを選択する．その後，入力側から（隠れ層１の定義），（隠れ層２の定義），（出力層の定義）と順番に行っている．活性化(Activation)関数の選択，正則化(Rezularizer)の指定，Dropoutの指定等，とても明確に書くことができる．

因みに順伝搬ネットワークモデル `model=Sequential()` は，CNN(Convolutional Neural Network)まで対応可能で，より複雑な構造をもつRNN(Recurrent Neural Net)等に対してKerasでは，`model=Graph()` という宣言から始めるGraphモデルで構造を定義することができる．（こちらは今後調査したいと思います．）

MLPコードの後半では，オプティマイザーを指定して，`model.fit()` のメソッドを呼ぶ．

```py
    adagrad = Adagrad(lr=0.01, epsilon=1e-08)
    model.compile(loss='categorical_crossentropy', optimizer=adagrad)
    
    batch_size = 16
    nb_epoch = 1000

    print('Train...')
    model.fit(train_x, train_y,
          batch_size=batch_size, nb_epoch=nb_epoch,
          show_accuracy=False, verbose=2,
          validation_data=(test_x, test_y))

```

以上で，学習までプログラムは実行される．


## 使ってみての感想
何よりも短いコード，しかも"YAML"や"Lua"といった別スクリプトなしでネットワークを定義して計算を実行できるのはすばらしい．バックエンドが"Theano"と”TensorFlow"の２種類になったことに対しては，今時点で面白い使い方は頭に浮かばないが，ユーザに対する間口が広がることに間違いはない．

またよく言われることだが，抽象度が上がることで柔軟性が失われるトレードオフは，当然 "Keras" にも当てはまる．しかし，プロトタイピングで「自分のモデルをすぐに試してみたい」とか，「ハイパーパラメータの数値的な実験を繰り返し行いたい」という目的に対しては非常に有用なライブラリであると思われる．  
（一気にKerasサポーターになってしまいました．）


## 参考文献 (web site)
- Keras Documentation : http://keras.io/
- Theano Documenatation : http://deeplearning.net/software/theano/
- TensorFlow Documentation : https://www.tensorflow.org/
- TensorFlowのMLPコードで"Wine"を分類 - Qiita
[http://qiita.com/TomokIshii/items/2cab778a3192d561a1ef](http://qiita.com/TomokIshii/items/2cab778a3192d561a1ef)
- TensorFlow向けに書いたコードをTheanoに移植してみた- Qiita
[http://qiita.com/TomokIshii/items/0bae5d3468ab67f5d7e5](http://qiita.com/TomokIshii/items/0bae5d3468ab67f5d7e5)