TensorFlow向けに書いたコードをTheanoに移植してみた


元々，Deep Learning用フレームワーク"Theano"の学習を進めていたが，先月(2015/11)公開された"TensorFlow"に興味を惹かれ最近はそちらをメインで使っていた．TensorFlowを触りながら，「ここは"Theano"に近い，ここはかなり違う」と感じていたが，両者の違いを確認するために簡単なコードを"TensorFlow"から”Theano"に移植してみた．（普通，方向が逆かと思いますが．）

## 概要： "Theano" vs. "TensorFlow"

まず２つのフレームワークの概要を比較する．

|    項目   |    Theano  |    TensorFlow   |
|:--------:|:-----------:|:----------------:|
| 開発主体 | アカデミック (University of Montreal)　| 企業 (Google)
| 公開年度 | 2010年ぐらい(?)   |  2015年
| Tensor operation |   support  |  support |
| numpy 'basic' level 機能 | support  | support |
|  自動微分 (Graph Transformation)  | support | support |
| GPU演算 |  support   |   support |
| Graph Visualization |  support (一応あります）|  support (ご存知 Tensorboard) |
| Optimizer | not support (built-inではないという意味） | いろいろsupport |
| Neural Network系の関数 | support | (いろいろ) support |

機能面では，上の表の最後の部分に違いが見られる．Theanoでは細かい部分を自前で用意しなければならないのに対し，ThensorFlowにはライブラリ関数として初めからいろいろ準備されている，といった印象である．  
（Theanoで詳細部からプログラムしたくない方には，Theanoベースの高機能ライブラリ，例えば Pylearn2 があります．）

## コードで比較（Neural Networkのモデル化)

ここでは，"Theano"と”ThensorFlow"ではほとんど同じようにコードを書くことができる．[前回の記事](http://qiita.com/TomokIshii/items/2cab778a3192d561a1ef)で紹介した多クラス分類を行うMLP(Multi-layer Perceptron)のコードから抜粋する．

**TensorFlow版**

```py
# Hidden Layer
class HiddenLayer(object):
    def __init__(self, input, n_in, n_out):
        self.input = input
    
        w_h = tf.Variable(tf.random_normal([n_in,n_out],mean=0.0,stddev=0.05))
        b_h = tf.Variable(tf.zeros([n_out]))
     
        self.w = w_h
        self.b = b_h
        self.params = [self.w, self.b]
    
    def output(self):
        linarg = tf.matmul(self.input, self.w) + self.b
        self.output = tf.nn.relu(linarg)  # switch sigmoid() to relu()
        
        return self.output

# Read-out Layer
class ReadOutLayer(object):
    def __init__(self, input, n_in, n_out):
        self.input = input
        
        w_o = tf.Variable(tf.random_normal([n_in,n_out],mean=0.0,stddev=0.05))
        b_o = tf.Variable(tf.zeros([n_out]))
       
        self.w = w_o
        self.b = b_o
        self.params = [self.w, self.b]
    
    def output(self):
        linarg = tf.matmul(self.input, self.w) + self.b
        self.output = tf.nn.softmax(linarg)  

        return self.output

```

**Theano版**

```py
# Hidden Layer
class HiddenLayer(object):
    def __init__(self, input, n_in, n_out):
        self.input = input
    
        w_h = theano.shared(floatX(np.random.standard_normal([n_in, n_out])) 
                             * 0.05) 
        b_h = theano.shared(floatX(np.zeros(n_out)))
   
        self.w = w_h
        self.b = b_h
        self.params = [self.w, self.b]
    
    def output(self):
        linarg = T.dot(self.input, self.w) + self.b
        # self.output = T.nnet.relu(linarg)
        self.output = T.nnet.sigmoid(linarg)
        
        return self.output

# Read-out Layer
class ReadOutLayer(object):
    def __init__(self, input, n_in, n_out):
        self.input = input
        
        w_o = theano.shared(floatX(np.random.standard_normal([n_in,n_out]))
                             * 0.05)
        b_o = theano.shared(floatX(np.zeros(n_out)))
       
        self.w = w_o
        self.b = b_o
        self.params = [self.w, self.b]
    
    def output(self):
        linarg = T.dot(self.input, self.w) + self.b
        self.output = T.nnet.softmax(linarg)  

        return self.output

```

細かく見なければ「"tf." を"T."に変えただけ」の違いである．活性化関数については後発のTenfsorFlowの方が "tf.nn.relu()"等もあって便利と思っていたが，Theanoでも ver.0.7.1からrelu() (Rectified Linear Unit) をサポートするようである．（本記事で使用したプログラムは，Python　ver.2.7.10, TensorFlow ver.0.5.0, Theano ver.0.7.0 になります．）

また，Softmax関数も当然のことながら両者でサポートされている．

## コードの比較（最適化プロセス）

ここでは両者(TensorFlow vs. Theano)で違いがみられる．TensorFlowでは，Optimizerのクラス・ライブラリが充実している一方，TheanoではOptimizerのクラス・ライブラリは自分で用意する必要がある．

**TensorFlow版(AdagradOptimzer使用例)**

```py
    # Train
    optimizer = tf.train.AdagradOptimizer(0.01)
    train_op = optimizer.minimize(loss)
    
    init = tf.initialize_all_variables()

    with tf.Session() as sess:
        sess.run(init)
        print('Training...')
        for i in range(10001):
            train_op.run({x: train_x, y_: train_y})
            if i % 1000 == 0:                # echo status on screen
                train_accuracy = accuracy.eval({x: train_x, y_: train_y})
                print(' step, accurary = %6d: %8.3f' % (i, train_accuracy))

```

TensorFlowでは，optimizerを指定して，Sessionを動かす．Seesionの中でTrain DataをFeed_dictの形で供給するという流れ．

**Theano版(Adagradを実装した例)**
できるだけ関数のcallの仕方をTensorFlowのコードに近づけるように書いたつもりだが，以下のようになった．

```py
# Optimizers - (GradientDescent), AdaGrad
class Optimizer(object):
    def __init__(self, params, learning_rate=0.01):
        self.lr = learning_rate
        self.params = params
       
    def minimize(self, loss):
        self.gradparams = [T.grad(loss, param) for param in params]
        
class AdagradOptimizer(Optimizer):
    def __init__(self, params, learning_rate=0.01, eps=1.e-6):
        super(AdagradOptimizer, self).__init__(params, learning_rate)
        self.eps = eps
        self.accugrads = [theano.shared(floatX(np.zeros(t.shape.eval())),
                          'accugrad') for t in self.params
                         ]
    def minimize(self, loss):
        super(AdagradOptimizer, self).minimize(loss)
        self.updates = OrderedDict()    # for Theano's rule

        for accugrad, param, gparam in zip(
                              self.accugrads, self.params, self.gradparams):
            agrad = accugrad + gparam * gparam
            dx = - (self.lr / T.sqrt(agrad + self.eps)) * gparam
            self.updates[param] = param + dx
            self.updates[accugrad] = agrad

        return self.updates

```

これらを使って最適化プロセス（学習）を走らせる．

```py
    # Train
    myoptimizer = AdagradOptimizer(params, learning_rate=0.01, eps=1.e-8)
    one_update = myoptimizer.minimize(loss)
    
    # Compile ... define theano.function
    train_model = theano.function(
        inputs=[],
        outputs=[loss, accuracy],
        updates=one_update,
        givens=[(x, strain_x), (y_, strain_y)],
        allow_input_downcast=True
    )

    n_epochs = 10001
    epoch = 0
    
    while (epoch < n_epochs):
        epoch += 1
        loss, accu = train_model()
        if epoch % 1000 == 0:
            print('epoch[%5d] : cost =%8.4f, accyracy =%8.4f' % (epoch, loss, accu))

```

このように最適化プロセスの部分において，
**TensorFlowでは...**
変数を初期化した後，Sessionを走らせる．訓練データは，セッション中に op.run( {Feed_dict})のような形で与える．  
**Theanoでは...**
theano.function()のところで（訓練データの供給を含め）学習プロセスの流れを定義する．定義した関数(theano.function)を用いて，反復学習計算を実行する，
という違いが見られる．

”Theano"の学習を始めた当初は，この theano.function() の使い方に悩んだ記憶があるが，上記のように"TensorFlow"と比較することで，theano.function() への理解が深まった．（違う見方をすれば，theano.function() についてきちんと理解していれば，Theanoをうまく使いこなせると思います．）

## まとめと感想
同じ目的で使用されるフレームワークですので，似ているところがたくさんあります．機能面では，TensorFlowの方が充実しているので，ThenoコードをThensorFlow向けに移植するのは容易ではないかと思われます．但し，Optimizerや新しい関数も一度，実装していればそれを使いまわすことができるので，Theanoが不利というわけではありません．（先人のコードも参考になりますし，アドオンのライブラリもたくさんあります．）

まだMLPのコードを見ただけで，より複雑なネットワークモデルを試していませんが，どちらも非常に可能性のあるツールと思われます．（Chainerもインストールしたのですがなかなか手がつがず... ）

## （追記）Keras についての情報
本記事を書くにあたりNeural Network Library "Keras"についてサイトを確認したところ，”Theano"と組み合わせて使用するライブラリに加えて， ”TensorFlow"との組み合わせで使うものもリリースされているようです．（後で調べてみたいと思います．）

[Keras: Deep Learning library for Theano and TensorFlow](http://keras.io/)


## 参考文献 (web site)
- TensorFlow Documentation : [https://www.tensorflow.org/](https://www.tensorflow.org/)
- Theano Documentation : http://deeplearning.net/software/theano/index.html
- gradient-optimizers 0.0.4 : https://pypi.python.org/pypi/gradient-optimizers/0.0.4
- ニューラルネットで学習係数最適化を色々試してみる- Qiita
[http://qiita.com/hogefugabar/items/1d4f6c905d0edbc71af2](http://qiita.com/hogefugabar/items/1d4f6c905d0edbc71af2)  
- TensorFlowのMLPコードで"Wine"を分類 - Qiita  
[http://qiita.com/TomokIshii/items/2cab778a3192d561a1ef](http://qiita.com/TomokIshii/items/2cab778a3192d561a1ef)
- Keras Documentation : http://keras.io/