TFLearnで論理演算を学習させてみた

[Bash on Ubuntu on WindowsでTensorFlowを使うためのメモ](http://qiita.com/toyolab/items/bccd03d4cb7795112ab6)
で、TensorFlowが使えるようになったのだが、いまいち使い方がわからない。

[TFLearn](http://tflearn.org/)っていう、TensorFlowをもっと簡単に使えるライブラリがあるということなので、それも入れてみた。

````bash
$ pip install tflearn
````

サンプルコードを眺めていると、logical.pyっていう論理演算を学習させるらしいプログラムが入っていたので、今回それを試してみた。

# ORの学習

logical.pyは、複数の論理演算の学習がひとまとめになっているので、関連するところだけを抜き出してみた。

````py
import tensorflow as tf
import tflearn

# Logical OR operator
X = [[0., 0.], [0., 1.], [1., 0.], [1., 1.]]
Y = [[0.], [1.], [1.], [1.]]

# Graph definition
with tf.Graph().as_default():
    g = tflearn.input_data(shape=[None, 2])
    g = tflearn.fully_connected(g, 128, activation='linear')
    g = tflearn.fully_connected(g, 128, activation='linear')
    g = tflearn.fully_connected(g, 1, activation='sigmoid')
    g = tflearn.regression(g, optimizer='sgd', learning_rate=2.,
                           loss='mean_square')

    # Model training
    m = tflearn.DNN(g)
    m.fit(X, Y, n_epoch=100, snapshot_epoch=False)

    # Test model
    print("Testing OR operator")
    print("0 or 0:", m.predict([[0., 0.]]))
    print("0 or 1:", m.predict([[0., 1.]]))
    print("1 or 0:", m.predict([[1., 0.]]))
    print("1 or 1:", m.predict([[1., 1.]]))
````

初見だけどなんとなく意味はわかる。これを実行すると、以下のような結果となった。

````
--
Training Step: 100  | total loss: 0.00227
| SGD | epoch: 100 | loss: 0.00227 -- iter: 4/4

--
Testing OR operator
0 or 0: [[0.031054211780428886]]
0 or 1: [[0.9823662638664246]]
1 or 0: [[0.9786670207977295]]
1 or 1: [[0.9999874830245972]]
````
0か1というディジタルで見れば、ORの学習はできていることになる。

ところで、最初のコードだと128個の中間層が２層もつながっている。ORの学習ごときで中間層は要らないだろうということで、中間層は削除してみた。その代わり、学習回数を2000回に増やしてみた。

````py
import tensorflow as tf
import tflearn

# Logical OR operator
X = [[0., 0.], [0., 1.], [1., 0.], [1., 1.]]
Y = [[0.], [1.], [1.], [1.]]

# Graph definition
with tf.Graph().as_default():
    g = tflearn.input_data(shape=[None, 2])
    g = tflearn.fully_connected(g, 1, activation='sigmoid')
    g = tflearn.regression(g, optimizer='sgd', learning_rate=2., loss='mean_square')

    # Model training
    m = tflearn.DNN(g)
    m.fit(X, Y, n_epoch=2000, snapshot_epoch=False)

    # Test model
    print("Testing OR operator")
    print("0 or 0:", m.predict([[0., 0.]]))
    print("0 or 1:", m.predict([[0., 1.]]))
    print("1 or 0:", m.predict([[1., 0.]]))
    print("1 or 1:", m.predict([[1., 1.]]))
````
これを実行した結果。

````
--
Training Step: 2000  | total loss: 0.00098
| SGD | epoch: 2000 | loss: 0.00098 -- iter: 4/4
--
Testing OR operator
0 or 0: [[0.041201911866664886]]
0 or 1: [[0.9756871461868286]]
1 or 0: [[0.9764388799667358]]
1 or 1: [[0.9999741315841675]]
````
ちゃんとORの学習はできた模様。

# ANDの学習

続いてANDの学習をやってみた。コードは教師信号をANDに変えるだけであとは同じ。

````py
import tensorflow as tf
import tflearn

# Logical AND operator
X = [[0., 0.], [0., 1.], [1., 0.], [1., 1.]]
Y = [[0.], [0.], [0.], [1.]]

# Graph definition
with tf.Graph().as_default():
    g = tflearn.input_data(shape=[None, 2])
    g = tflearn.fully_connected(g, 1, activation='sigmoid')
    g = tflearn.regression(g, optimizer='sgd', learning_rate=2., loss='mean_square')

    # Model training
    m = tflearn.DNN(g)
    m.fit(X, Y, n_epoch=2000, snapshot_epoch=False)

    # Test model
    print("Testing AND operator")
    print("0 and 0:", m.predict([[0., 0.]]))
    print("0 and 1:", m.predict([[0., 1.]]))
    print("1 and 0:", m.predict([[1., 0.]]))
    print("1 and 1:", m.predict([[1., 1.]]))
````
これを実行した結果。

````
--
Training Step: 2000  | total loss: 0.00137
| SGD | epoch: 2000 | loss: 0.00137 -- iter: 4/4
--
Testing AND operator
0 and 0: [[8.591794176027179e-05]]
0 and 1: [[0.04014528915286064]]
1 and 0: [[0.03964542970061302]]
1 and 1: [[0.9525935053825378]]
````
確かにANDになった。

# XORの学習

OR, ANDは線形分離できるので中間層は不要だけど、XORは線形分離できないので中間層が必要。しかし、サンプルのコードでは、XORを直接学習させるのではなく、NANDとORを学習させてそれを合成して使っている。

どうして直接学習させないのかわからなかったので、XORを直接学習させるコードを書いてみた。

````py
import tensorflow as tf
import tflearn

# Logical XOR operator
X = [[0., 0.], [0., 1.], [1., 0.], [1., 1.]]
Y = [[0.], [1.], [1.], [0.]]

# Graph definition
with tf.Graph().as_default():
    g = tflearn.input_data(shape=[None, 2])
    g = tflearn.fully_connected(g, 2, activation='sigmoid')
    g = tflearn.fully_connected(g, 1, activation='sigmoid')
    g = tflearn.regression(g, optimizer='sgd', learning_rate=2., loss='mean_square')

    # Model training
    m = tflearn.DNN(g)
    m.fit(X, Y, n_epoch=2000, snapshot_epoch=False)

    # Test model
    print("Testing XOR operator")
    print("0 xor 0:", m.predict([[0., 0.]]))
    print("0 xor 1:", m.predict([[0., 1.]]))
    print("1 xor 0:", m.predict([[1., 0.]]))
    print("1 xor 1:", m.predict([[1., 1.]]))
````

これは、教師信号をXORにして、２個の中間層を追加しただけ。
で、これを実行するとこうなった。

````
--
Training Step: 2000  | total loss: 0.25000
| SGD | epoch: 2000 | loss: 0.25000 -- iter: 4/4
--
Testing XOR operator
0 xor 0: [[0.5000224709510803]]
0 xor 1: [[0.5000009536743164]]
1 xor 0: [[0.49999910593032837]]
1 xor 1: [[0.4999775290489197]]
````

あれ、学習できてない。
で、ググると、次のページが引っ掛かった。いつものStackOverflowのサイトだ。

[tflearn / tensorflow does not learn xor](http://stackoverflow.com/questions/37166268/tflearn-tensorflow-does-not-learn-xor)

これによると、標準の設定だと、重みの初期値が標準偏差0.02と幅がかなり狭いらしい。なので、重みの初期値の幅を-1から1までに広げてやるとよいらしい。

````py
import tensorflow as tf
import tflearn

# Logical XOR operator
X = [[0., 0.], [0., 1.], [1., 0.], [1., 1.]]
Y = [[0.], [1.], [1.], [0.]]

# Graph definition
with tf.Graph().as_default():
    tnorm = tflearn.initializations.uniform(minval=-1.0, maxval=1.0)
    g = tflearn.input_data(shape=[None, 2])
    g = tflearn.fully_connected(g, 2, activation='sigmoid', weights_init=tnorm)
    g = tflearn.fully_connected(g, 1, activation='sigmoid', weights_init=tnorm)
    g = tflearn.regression(g, optimizer='sgd', learning_rate=2., loss='mean_square')

    # Model training
    m = tflearn.DNN(g)
    m.fit(X, Y, n_epoch=2000, snapshot_epoch=False)

    # Test model
    print("Testing XOR operator")
    print("0 xor 0:", m.predict([[0., 0.]]))
    print("0 xor 1:", m.predict([[0., 1.]]))
    print("1 xor 0:", m.predict([[1., 0.]]))
    print("1 xor 1:", m.predict([[1., 1.]]))
````

このように重みの初期値の設定を変えて実行した結果。

````
--
Training Step: 2000  | total loss: 0.00131
| SGD | epoch: 2000 | loss: 0.00131 -- iter: 4/4
--
Testing XOR operator
0 xor 0: [[0.03527239337563515]]
0 xor 1: [[0.9663047790527344]]
1 xor 0: [[0.9607295393943787]]
1 xor 1: [[0.03082425333559513]]
````
無事にXORを学習することができた。

# TFLearnをいじってみた感想

ニューラルネットワークの考え方にコードが対応しているのでわかりやすい。TensorFlowの細かいチューニングはできないのかもしれないけど、そもそもTensorFlowで何ができるのかわからない人にとっては、TFLearnから始めるのもいいのでは？