ラムダ技術部 / Yoidea

不真面目な技術について

Tensorflowで最もシンプルにAND, ORを学習する

はじめに

機械学習のライブラリになれるためにTensorflowを使ってAND, ORを学習した。

環境はMacOS 10.12.5、Python 3.6.1、tensorflow 1.1.0で行った。

yoidea.hatenablog.com

ニューラルネットワーク

ニューラルネットワークについて自身で説明しようと思っていたが、どうも良い文章がかけなかった。もう少し勉強してから書こうと思う。

ANDを実装

Tensorflow読み込み

import モジュール名 as オブジェクト名

とすると、モジュールを自分で名前をつけたオブジェクトとして読み込むことができる。

今回はTensorflowをtfという名前で読み込んでみる。

import tensorflow as tf

ネットワークを定義

ANDは2入力なので入力変数は、要素数2のベクトルと考える。

 \displaystyle
  \mathbf{x} = \begin{pmatrix} x_{1} \\ x_{2} \end{pmatrix}

Tensorflowを利用すると上式のような変数を簡単に作成できる。

tf.placeholder(データ型, [列数, 行数])

とすると、指定したデータ型と大きさの行列をメモリ空間に確保できる。

データ型はtf.float32で問題ないだろう。要素数2のベクトルを作成する。

x = tf.placeholder(tf.float32, [None, 2])

重みは1行2列の行列と考える。

 \displaystyle
  W = \begin{pmatrix} W_{1} & W_{2} \end{pmatrix}
tf.random_normal([列数, 行数])

とすると、指定した大きさの乱数の入った行列を作成できる。

tf.Variable()の引数に、1行2列の乱数配列を格納して重み変数を作成する。

w = tf.Variable(tf.random_normal([2, 1]))

また、バイアス項として1行1列の乱数配列を作成する。

b = tf.Variable(tf.random_normal([1, 1]))

出力変数は、入力変数に重みを掛けてバイアス項を加えたものに活性化関数をかけたものと考える。

 \displaystyle
  \mathbf{z} = sigmoid(\mathbf{y})
 \displaystyle
  \mathbf{y} = W \mathbf{x} + B
tf.matmul(行列1, 行列2)

とすると、行列の掛け算ができる。また、行列の足し算は「+」演算子をそのまま使用できる。

tf.sigmoid(行列)

とすると、活性化関数としてsigmoid関数をかけることができる。

これらを用いて上式を表す。

z = tf.sigmoid(tf.matmul(x, w) + b)

教師データを格納するために要素数1のベクトルを作成する。

t = tf.placeholder(tf.float32, [None, 1])

損失関数を定義する。オーソドックスに2乗誤差の合計でいいだろう。

 \displaystyle
  l = \sum_{i = 1}^{4} (t_{i} - y_{i})^{2}
tf.reduce_sum(行列)

で合計を求められる。

tf.pow(行列, 指数)

でべき乗を求められる。

tf.subtract(行列1, 行列2)

で差を求められる。

これらを用いて二乗誤差を計算する。

l = tf.reduce_sum(tf.pow(tf.subtract(t, y), 2))

入力値・出力値の宣言

入力値と出力値の組み合わせを配列に格納する。すべての入力パターンに対する出力値なので2次元配列になる。

ANDの場合は、

入力1入力2出力
000
010
100
111

なので以下のような配列で表す。

inputs = [[0, 0], [0, 1], [1, 0], [1, 1]]
outputs = [[0], [0], [0], [1]]

最適化方法の設定

Tensorflowで最適化のメソッドが用意されているため、1行の記述で簡単に最適化方法が設定できる。

tf.train.GradientDescentOptimizer(学習レート).minimize(損失)

とすると、損失を小さくするように最急降下法で最適化される。

適当な変数を宣言して損失関数についてこれを適用する。

train = tf.train.GradientDescentOptimizer(0.1).minimize(l)

セッションの作成・初期化

sess = tf.Session()

として、セッションを作成する。

sess.run()の引数によって様々な操作ができる。

sess.run(tf.initialize_all_variables())

とすると、変数が初期化される。どうやら学習を開始するには初期化が必要なようだ。

学習ループ

最適化を繰り返し行い学習をしていく。

sess.run(最適化関数, 入力変数)

とすると、最適化処理が実行される。入力変数はfeed_dict = {x: inputs, t: outputs}という形で入力値・出力値を対応させる。

念のため10000回ループさせてみる。

for step in range(10000):
    sess.run(train, feed_dict = {x: inputs, t: outputs})

途中経過が知りたいので1000回に1回学習過程を表示するようにしたい。

sess.run(損失, 入力変数)

とすると、現在の損失の値が得られる。

sess.run(出力, 入力変数)

とすると、入力に対する現在の出力が得られる。

for step in range(10000):
    sess.run(train, feed_dict = {x: inputs, t: outputs})
    if step % 1000 == 0:
        print("step : " + str(step))
        print("loss = " + str(sess.run(l, feed_dict = {x: inputs, t: outputs})))
        for i in inputs:
            print("x = " + str(i) + " : y = " + str(sess.run(z, feed_dict = {x: [i]})))

これで途中経過が観察できるはずだ。

完成したソースコードは以下のようになった。

import tensorflow as tf

x = tf.placeholder(tf.float32, [None, 2])
w = tf.Variable(tf.random_normal([2, 1]))
b = tf.Variable(tf.random_normal([1, 1]))
z = tf.sigmoid(tf.matmul(x, w) + b)
t = tf.placeholder(tf.float32, [None, 1])
l = tf.reduce_sum(tf.pow(tf.subtract(z, t), 2))

inputs = [[0, 0], [0, 1], [1, 0], [1, 1]]
outputs = [[0], [0], [0], [1]]

train = tf.train.GradientDescentOptimizer(0.1).minimize(l)

sess = tf.Session()
sess.run(tf.initialize_all_variables())

for step in range(10000):
    sess.run(train, feed_dict = {x: inputs, t: outputs})
    if step % 1000 == 0:
        print("step : " + str(step))
        print("loss = " + str(sess.run(l, feed_dict = {x: inputs, t: outputs})))
        for i in inputs:
            print("x = " + str(i) + " : y = " + str(sess.run(z, feed_dict = {x: [i]})))

ORを実装

ORの場合は、

入力1入力2出力
000
011
101
111

なので以下の入出力の配列を以下のように変えるだけでよい。

inputs = [[0, 0], [0, 1], [1, 0], [1, 1]]
outputs = [[0], [1], [1], [1]]

実行結果

AND学習結果

$ python tensorflow_and.py
step : 0
loss = 1.10273
x = [0, 0] : y = [[ 0.30137503]]
x = [0, 1] : y = [[ 0.06085767]]
x = [1, 0] : y = [[ 0.86426908]]
x = [1, 1] : y = [[ 0.48888674]]
step : 1000
loss = 0.0606695
x = [0, 0] : y = [[ 0.00442047]]
x = [0, 1] : y = [[ 0.13255274]]
x = [1, 0] : y = [[ 0.13271405]]
x = [1, 1] : y = [[ 0.84041691]]
step : 2000
loss = 0.0287164
x = [0, 0] : y = [[ 0.00124461]]
x = [0, 1] : y = [[ 0.09150107]]
x = [1, 0] : y = [[ 0.09150464]]
x = [1, 1] : y = [[ 0.89059579]]
step : 3000
loss = 0.0183822
x = [0, 0] : y = [[ 0.00059863]]
x = [0, 1] : y = [[ 0.07328387]]
x = [1, 0] : y = [[ 0.07328429]]
x = [1, 1] : y = [[ 0.91258895]]
step : 4000
loss = 0.0133999
x = [0, 0] : y = [[ 0.00035929]]
x = [0, 1] : y = [[ 0.06260382]]
x = [1, 0] : y = [[ 0.06260396]]
x = [1, 1] : y = [[ 0.9254263]]
step : 5000
loss = 0.0104966
x = [0, 0] : y = [[ 0.00024315]]
x = [0, 1] : y = [[ 0.05542824]]
x = [1, 0] : y = [[ 0.0554283]]
x = [1, 1] : y = [[ 0.93403047]]
step : 6000
loss = 0.00860571
x = [0, 0] : y = [[ 0.00017737]]
x = [0, 1] : y = [[ 0.05020063]]
x = [1, 0] : y = [[ 0.05020065]]
x = [1, 1] : y = [[ 0.94028848]]
step : 7000
loss = 0.0072804
x = [0, 0] : y = [[ 0.00013618]]
x = [0, 1] : y = [[ 0.04618241]]
x = [1, 0] : y = [[ 0.04618241]]
x = [1, 1] : y = [[ 0.94509327]]
step : 8000
loss = 0.00630194
x = [0, 0] : y = [[ 0.00010851]]
x = [0, 1] : y = [[ 0.04297351]]
x = [1, 0] : y = [[ 0.04297351]]
x = [1, 1] : y = [[ 0.94892669]]
step : 9000
loss = 0.00555099
x = [0, 0] : y = [[  8.89249277e-05]]
x = [0, 1] : y = [[ 0.04033687]]
x = [1, 0] : y = [[ 0.04033687]]
x = [1, 1] : y = [[ 0.95207453]]

OR学習結果

$ python tensorflow_or.py
step : 0
loss = 0.885255
x = [0, 0] : y = [[ 0.7940169]]
x = [0, 1] : y = [[ 0.8566581]]
x = [1, 0] : y = [[ 0.61242062]]
x = [1, 1] : y = [[ 0.7101258]]
step : 1000
loss = 0.0355569
x = [0, 0] : y = [[ 0.14283016]]
x = [0, 1] : y = [[ 0.91314256]]
x = [1, 0] : y = [[ 0.91276491]]
x = [1, 1] : y = [[ 0.99848747]]
step : 2000
loss = 0.0154843
x = [0, 0] : y = [[ 0.09367228]]
x = [0, 1] : y = [[ 0.94210422]]
x = [1, 0] : y = [[ 0.94205385]]
x = [1, 1] : y = [[ 0.99960953]]
step : 3000
loss = 0.0096981
x = [0, 0] : y = [[ 0.07396416]]
x = [0, 1] : y = [[ 0.9540332]]
x = [1, 0] : y = [[ 0.95401716]]
x = [1, 1] : y = [[ 0.99981457]]
step : 4000
loss = 0.00700942
x = [0, 0] : y = [[ 0.06280303]]
x = [0, 1] : y = [[ 0.96085531]]
x = [1, 0] : y = [[ 0.96084803]]
x = [1, 1] : y = [[ 0.99988878]]
step : 5000
loss = 0.00546895
x = [0, 0] : y = [[ 0.05542986]]
x = [0, 1] : y = [[ 0.96538645]]
x = [1, 0] : y = [[ 0.96538246]]
x = [1, 1] : y = [[ 0.99992454]]
step : 6000
loss = 0.00447474
x = [0, 0] : y = [[ 0.05011057]]
x = [0, 1] : y = [[ 0.96866691]]
x = [1, 0] : y = [[ 0.96866459]]
x = [1, 1] : y = [[ 0.99994481]]
step : 7000
loss = 0.00378169
x = [0, 0] : y = [[ 0.04604707]]
x = [0, 1] : y = [[ 0.97117931]]
x = [1, 0] : y = [[ 0.97117776]]
x = [1, 1] : y = [[ 0.99995744]]
step : 8000
loss = 0.00327177
x = [0, 0] : y = [[ 0.04281572]]
x = [0, 1] : y = [[ 0.97318089]]
x = [1, 0] : y = [[ 0.97317988]]
x = [1, 1] : y = [[ 0.99996603]]
step : 9000
loss = 0.00288129
x = [0, 0] : y = [[ 0.0401685]]
x = [0, 1] : y = [[ 0.97482318]]
x = [1, 0] : y = [[ 0.9748224]]
x = [1, 1] : y = [[ 0.99997211]]

大体いい感じに学習できているのが確認できた。

今後の課題

次回は2層のニューラルネットワークでは解決できないXORの学習を行いたい。