第18回および第19回では、隠れ層が、それぞれ2層および3層からなる多層ニューラルネットワークを使用して、非線形データを分類することをご紹介しました。今回は、隠れ層が5層からなる多層ニューラルネットワークを使用して、更に複雑な非線形データを分類してみたいと思います。

 

図1

TensorFlowによる多層ニューラルネットワークを使用して図1の非線形データを分類する機械学習モデルを作成します。図1のデータを分類するためには、ここでは隠れ層の数を5で検討してみました。 隠れ層が5層からなるTensorFlowによる多層ニューラルネットワークを使用した機械学習の流れは、概ね以下になります。なお、Pythonのバージョンは3.5以上を想定しています。まだ、Pythonや関連ライブラリーをまだ導入されていない方は、第2回および第13回を参考に導入してください。

  1. 非線形データを入力する。
  2. 入力データを、トレーニングデータとテストデータに分ける。
  3. TensorFlowにより、1層目の隠れ層(hidden layer)の最適化すべき係数(Variable)と活性化関数を定義する。
  4. TensorFlowにより、2層目の隠れ層(hidden layer)の最適化すべき係数(Variable)と活性化関数を定義する。
  5. TensorFlowにより、3層目の隠れ層(hidden layer)の最適化すべき係数(Variable)と活性化関数を定義する。
  6. TensorFlowにより、4層目の隠れ層(hidden layer)の最適化すべき係数(Variable)と活性化関数を定義する。
  7. TensorFlowにより、5層目の隠れ層(hidden layer)の最適化すべき係数(Variable)と活性化関数を定義する。
  8. TensorFlowにより、出力層(output layer)の最適化すべき係数(Variable)と活性化関数を定義する。
  9. TensorFlowにより誤差関数とその最適化法を定義する。
  10. TensorFlowにより精度を定義する。
  11. TensorFlowによりセッションを定義する。
  12. TensorFlowによりセッションを実行する。
  13. 学習結果に基づき、新データを予測分類するpredict関数を定義する。
  14. 分類結果および予測値を表示するためのプロット関数を定義する。
  15. 分類結果および予測値を図に表示する。

では、各ステップを詳しく見ていきましょう。

 

①非線形データを入力する。

まず、対話形式ではなく、Pythonのスクリプトをファイルで用意します。Pythonスクリプトの先頭に以下の2行を添付しておくことをお勧めします。

#!/usr/bin/env python
# -*- coding:utf-8 -*-            
            

 

今回は、図1の非線形データをNumpyライブラリーのrandom.seed関数とrandom.randn関数を使用して2次元の乱数の変量を作成し、logical_xor関数を使用して、変量から排他的論理和を生成し、分類クラス(1または-1)を作成します。既にデータが標準化されているため、今回も、変量データの標準化は不要です。

 

import numpy as np
np.random.seed(0)
X = np.random.randn(1000, 2)
y = np.logical_xor(X[:, 0] > 0,
                       X[:, 1] > 0)
y = np.where(y, 1, -1)
            

 

入力データを、トレーニングデータとテストデータに分ける。

scikit-learning.model_selectionライブラリーのtrain_test_split関数を使用して、

変量配列Xとラベル配列yについて、トレーニングデータとテストデータに分けます。変量配列Xを、それぞれ、X_train配列, X_test配列に分割し、ラベル配列yは、y_tarin配列, y_test配列へそれぞれ分割します。test_sizeのパラメータにより、テストデータの割合を指定できます。ここでは、0.2を指定することで、テストデータの割合を全体の20%と指定しています。全1,000サンプルの20%(= 200サンプル)がテストデータで、残りの800サンプルがトレーニングデータとなります。random_state=0を指定することにより、ランダムにトレーニングデータとテストデータを分割することができます。

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X,y, test_size=0.2, random_state=0)     
            

 

③TensorFlowにより、1段目の隠れ層(hidden layer)の最適化すべき係数(Variable)と活性化関数を定義する。

ここからが、TensorFlowの実際の記述になります。1層目の隠れ層について以下のように記述します。

import tensorflow as tf
num_units1 = 4
x = tf.placeholder(tf.float32, [None, 2])
w5 = tf.Variable(tf.truncated_normal([2, num_units1]))
b5 = tf.Variable(tf.zeros([num_units1]))
hidden0 = tf.nn.tanh(tf.matmul(x, w5) + b5)
            

num_units1は1層目の隠れ層におけるノードの数です。ここでは4を指定していますが、2以上の自然数を指定することにより、ノードの数を増やすことができます。ノードの数を増やすことにより、より複雑なモデルに対応することができますが、それに伴いより多くのコンピュータ資源が必要となります。この例では4を指定します。

TensorFlowでは、トレーニングデータを「placeholder」に格納し、機械学習により最適化すべき1層目の隠れ層の係数b5およびw5を「Variable」に格納します。ここでは、placeholderのデータの型と変量の数(ここでは2)のみを指定し、サンプルの数(ここではトレーニングデータの800)は指定しなくても構いません(ここではNoneを指定)。実際のデータ内容はセッション実行時に設定するため、ここでは指定しません。最適化すべき係数のb5の値は、0(ゼロ)に初期化しますが、w5は乱数を用いて初期値を決定しています。Net Input関数と活性化関数による1層目の隠れ層の出力としてhidden0を定義します。ここで注意すべきことは、Net Input関数も活性化関数も、必ずtensorflowライブラリーのものを使用しなければならないということです。例えば、Pythonプログラミングとしては、tf.matmul(x,w5)もnp.dot(x,w5)も全く同じ機能で計算結果も同じですが、np.dot(x,w5)を使用するとTensorFlowのセッション実行時にエラーとなるので、注意が必要です。tf.tanh関数に関しても同様で、np.tanhと指定するとセッション実行時にエラーになります。

 

④TensorFlowにより、2層目の隠れ層(hidden layer)の最適化すべき係数(Variable)と活性化関数を定義する。

2層目の隠れ層について以下のように記述します。

num_units2 = 6
w4 = tf.Variable(tf.truncated_normal([num_units1, num_units2]))
b4 = tf.Variable(tf.zeros([num_units2]))
hidden1 = tf.nn.tanh(tf.matmul(hidden0, w4) + b4)
            

num_units2は2層目の隠れ層におけるノードの数です。ここでは6を指定しています。機械学習により最適化すべき2層目の隠れ層の係数b4およびw4を「Variable」に格納します。w4はnum_units1 x num_units2の行列になります。最適化すべき係数のb4の値は、0(ゼロ)に初期化しますが、w4は乱数を用いて初期値を決定しています。1層目の隠れ層の出力であるhidden0を入力にしてNet Input関数と活性化関数による2層目の隠れ層の出力hidden1を定義します。

 

⑤TensorFlowにより、3層目の隠れ層(hidden layer)の最適化すべき係数(Variable)と活性化関数を定義する。

3層目の隠れ層について以下のように記述します。

num_units3 = 8
w3 = tf.Variable(tf.truncated_normal([num_units2, num_units3]))
b3 = tf.Variable(tf.zeros([num_units3]))
hidden2 = tf.nn.tanh(tf.matmul(hidden1, w3) + b3)
            

num_units3は3層目の隠れ層におけるノードの数です。ここでは8を指定しています。機械学習により最適化すべき3層目の隠れ層の係数b3およびw3を「Variable」に格納します。w3はnum_units2 x num_units3の行列になります。最適化すべき係数のb3の値は、0(ゼロ)に初期化しますが、w3は乱数を用いて初期値を決定しています。2層目の隠れ層の出力であるhidden1を入力にしてNet Input関数と活性化関数による3層目の隠れ層の出力hidden2を定義します。

 

⑥TensorFlowにより、4層目の隠れ層(hidden layer)の最適化すべき係数(Variable)と活性化関数を定義する。

4層目の隠れ層について以下のように記述します。

num_units4 = 10
w2 = tf.Variable(tf.truncated_normal([num_units3, num_units4]))
b2 = tf.Variable(tf.zeros([num_units4]))
hidden3 = tf.nn.tanh(tf.matmul(hidden2, w2) + b2)
            

num_units4は4層目の隠れ層におけるノードの数です。ここでは10を指定しています。機械学習により最適化すべき4層目の隠れ層の係数b2およびw2を「Variable」に格納します。w2はnum_units3 x num_units4の行列になります。最適化すべき係数のb2の値は、0(ゼロ)に初期化しますが、w2は乱数を用いて初期値を決定しています。3層目の隠れ層の出力であるhidden2を入力にしてNet Input関数と活性化関数による4層目の隠れ層の出力hidden3を定義します。

 

⑦TensorFlowにより、5層目の隠れ層(hidden layer)の最適化すべき係数(Variable)と活性化関数を定義する。

5層目の隠れ層について以下のように記述します。

num_units5 = 12
w1 = tf.Variable(tf.truncated_normal([num_units4, num_units5]))
b1 = tf.Variable(tf.zeros([num_units5]))
hidden4 = tf.nn.tanh(tf.matmul(hidden3,w1) + b1)
            

num_units5は5層目の隠れ層におけるノードの数です。ここでは12を指定しています。機械学習により最適化すべき5層目の隠れ層の係数b1およびw1を「Variable」に格納します。w1はnum_units4 x num_units5の行列になります。最適化すべき係数のb1の値は、0(ゼロ)に初期化しますが、w1は乱数を用いて初期値を決定しています。4層目の隠れ層の出力であるhidden3を入力にしてNet Input関数と活性化関数による5層目の隠れ層の出力hidden4を定義します。

 

⑧TensorFlowにより、出力層(output layer)の最適化すべき係数(Variable)と活性化関数を定義する。

最適化すべき係数 w0 と b0を0(ゼロ)に初期化します。5層目の隠れ層からの出力であるhidden4の値を入力にNet Input関数を計算し、sigmoid関数で0〜1の確率に変換します。

 

w0 = tf.Variable(tf.zeros([num_units5, 1]))
b0 = tf.Variable(tf.zeros([1]))
p = tf.nn.sigmoid(tf.matmul(hidden4, w0) + b0)
            

 

⑨TensorFlowにより誤差関数とその最適化法を定義する。

 

トレーニングデータの分類ラベルtのデータの型と大きさ(ここでは1)をplaceholderを用いて定義します。サンプル数(ここではトレーニングデータの数)については指定は不要です(ここではNoneを指定)。また、実際のデータ内容はセッション実行時に指定するため、ここでは指定しません。

次に誤差関数を定義します。i番目のデータの分類がt=1である確率をpとすると、tでない(t=0)確率は( 1- p )となります。したがって、i番目のデータを正しく予測する確率Piは、Pi = p^t x ( 1-p)^(1-t) のように表すことができます。N個のデータ全てを正しく予測する確率は、P = P1 x P2 x ... xPN = ΠPi = Π{ p^t x ( 1-p)^(1-t) }となり、この確率Pを最大にするように係数(Variable)の w0、b0、w1、b1、w2、b2、w3、b3、w4、b4、w5、b5の値を最適化すれば良いことになります。これを最尤推定法(maximum likelihood method)と呼びます。したがって誤差関数としては、-Pを最小になるように定義すれば良いわけです。対数関数を使って、次のように記述できます。

 

loss = - log P = -log Π{ p^t x ( 1-p)^(1-t) } = -Σ{ t*log(p) + (1-t)*log(1-p) }

 

ここで、Σは、tesorflowライブラリーで、 reduce_sum関数が用意されています。

また、train.AdamOptimizer関数で、誤差関数 lossを最小化するよう設定しています。

 

t = tf.placeholder(tf.float32, [None, 1])
loss = -tf.reduce_sum(t*tf.log(p) + (1-t)*tf.log(1-p))
train_step = tf.train.AdamOptimizer().minimize(loss)           
            

 

⑩TensorFlowにより精度を定義する。

1行目は、(p-0.5)と(t -0.5)の符号を比較することで、計算値pが元データtと一致しているかどうかを判定しています。signは符号を取り出す関数で、equalは、2つの符号が等しいかどうかを判定して、Bool値を返す関数です。correct_predictionには、データ数(ここではトレーニングデータの800個またはテストデータの200個)分の要素のBool値が格納されます。2行目では、cast関数で、correct_predictionの各要素のBool値を1,0の値に変換します。reduce_mean関数は、1,0の値に変換されたcorrect_predictionの各要素の平均値を返します。これが結局、この機械学習アルゴリズムの精度となります。

 

correct_prediction = tf.equal(tf.sign(p-0.5), tf.sign(t-0.5))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))           
            

 

(11)TensorFlowによりセッションを定義する。

Variable(w0, b0, w1,b1, w2, b2,w3,b3,w4,b4,w5,b5)の値を最適化するための、セッションを定義して、各Variableの値を初期化します。

 

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

(12)TensorFlowによりセッションを実行する。

誤差関数が最小になるよう、Variableの最適化を1,000回繰り返します。ここでは、100回繰り返すごとに、その時点での誤差関数lossとトレーニングデータの精度Accuracy(train)とテストデータの精度Accuracy(test)の値をそれぞれ計算し、表示しています。sess.runの中で、feed_dictにより、placeholderのx と t にそれぞれ、実際のデータ(変量と分類ラベル)を割り当てています。

 

 

y_matrix_train = y_train.reshape(len(y_train),1)
y_matrix_test = y_test.reshape(len(y_test),1)

i = 0
for _ in range(1000):
    i += 1
    sess.run(train_step, feed_dict={x:X_train, t:y_matrix_train})
    if i % 100 == 0:
        loss_val, acc_val = sess.run(
            [loss, accuracy], feed_dict={x:X_train, t:y_matrix_train})
        acc_test_val =sess.run(accuracy, feed_dict={x:X_test, t:y_matrix_test})
        print ('Step: %d, Loss: %f, Accuracy(train): %f, Accuracy(test): %f'
               % (i, loss_val, acc_val, acc_test_val))           
            

 

(実行結果)

Step: 100, Loss: 25.741905, Accuracy(train): 0.620000, Accuracy(test): 0.615000

Step: 200, Loss: -836.727295, Accuracy(train): 0.680000, Accuracy(test): 0.685000

Step: 300, Loss: -1764.638672, Accuracy(train): 0.912500, Accuracy(test): 0.915000

Step: 400, Loss: -2568.245361, Accuracy(train): 0.961250, Accuracy(test): 0.945000

Step: 500, Loss: -3298.591064, Accuracy(train): 0.976250, Accuracy(test): 0.955000

Step: 600, Loss: -3948.014160, Accuracy(train): 0.981250, Accuracy(test): 0.970000

Step: 700, Loss: -4572.254883, Accuracy(train): 0.986250, Accuracy(test): 0.975000

Step: 800, Loss: -5199.461426, Accuracy(train): 0.991250, Accuracy(test): 0.980000

Step: 900, Loss: -5806.454590, Accuracy(train): 0.993750, Accuracy(test): 0.995000

Step: 1000, Loss: -6400.137695, Accuracy(train): 0.995000, Accuracy(test): 0.990000

 

 

実行Stepが進むにつれ、誤差関数(Loss)の値は減少するとともに、トレーニングデータおよびテストデータの精度(Accuracy)の値が、それぞれ、増加して、最終的に精度が99%となり、分類上手くいっていることが分かります。

 

次に、最終時点での係数(w0, w1, w2, w3, w4,w5, b0, b1, b2, b3, b4,b5)の値を取得します。

        
w0_val, w1_val,w2_val,w3_val,w4_val,w5_val,b0_val,b1_val,b2_val,b3_val,b4_val,b5_val  = sess.run([w0, w1,w2,w3,w4,w5,b0,b1,b2,b3,b4,b5])        
         

 

(13)学習結果に基づき、新データを予測分類するpredict関数を定義する。

学習結果の結果、最終的に得られた係数(w0, w1, w2, w3,w4,w5, b0, b1,b2, b3,b4,b5)の値を用いて、予測モデル predict関数を以下のように定義します。

        
import numpy as np
def net_input(X):
    return np.dot(X,w0_val)+b0_val

def predict(X):
    hidden_0 = np.tanh(np.dot(X,w5_val) + b5_val)
    hidden_1 = np.tanh(np.dot(hidden_0,w4_val) + b4_val)
    hidden_2 = np.tanh(np.dot(hidden_1,w3_val) + b3_val)
    hidden_3 = np.tanh(np.dot(hidden_2,w2_val) + b2_val)
    hidden_4 = np.tanh(np.dot(hidden_3,w1_val) + b1_val)
    return np.where(1/(1+np.exp(-net_input(hidden_4))) >=0.5, 1, 0)
         

 

(14)分類結果および予測値を表示するためのプロット関数を定義する。

 

(13)で定義したpredict関数を用いて、分類結果および予測データを表示するためのplot_decision_regions関数を以下のように定義します。        

import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
def plot_decision_regions(X, y, resolution=0.02):

    # setup marker generator and color map
    markers = ('s', 'x')
    colors = ('red', 'blue')
    cmap = ListedColormap(colors[:len(np.unique(y))])

    # plot the decision surface
    x1_min, x1_max = X[:, 0].min() - 0.2, X[:, 0].max() + 0.2
    x2_min, x2_max = X[:, 1].min() - 0.2, X[:, 1].max() + 0.2
    xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution),
                         np.arange(x2_min, x2_max, resolution))
    Z = predict(np.array([xx1.ravel(), xx2.ravel()]).T)
    Z = Z.reshape(xx1.shape)
    plt.contourf(xx1, xx2, Z, alpha=0.4, cmap=cmap)
    plt.xlim(xx1.min(), xx1.max())
    plt.ylim(xx2.min(), xx2.max())

    # plot class samples
    for idx, cl in enumerate(np.unique(y)):
        plt.scatter(x=X[y == cl, 0], y=X[y == cl, 1],
                    alpha=0.8, c=cmap(idx),
                    marker=markers[idx], label=cl)          
            

(15)分類結果および予測値を図に表示する。

(14)で定義したplot_decision_regions関数を実行し、テストデータの分類結果を表示させます。

        
plot_decision_regions(X_test, y_test)
plt.legend(loc='lower left')
plt.tight_layout()
plt.show()
        

図2. 

 

テストデータについて、2つの曲線で、2つのクラスがよく分類されていることがわかります。このように、多層ニューラルネットワークの隠れ層の数とノードの数を適切に増やすことにより、複雑な非線形データを分類することができます。

 

なお、今回の内容は、以下の書籍の3章を参考に執筆いたしました。ご興味のある方は、ご参考いただければと思います。

 

「TensorFlowで学ぶディープラーニング入門 ~畳み込みニューラルネットワーク徹底解説~」 

中井 悦司 (著)

出版社: マイナビ出版

 

全体を通してのコードは以下のようになります。なお、本コードの稼働環境は、Python3.5以上を想定しています。

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import numpy as np
np.random.seed(0)
X = np.random.randn(1000, 2)
y = np.logical_xor(X[:, 0] > 0,
                       X[:, 1] > 0)
y = np.where(y, 1, -1)

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X,y, test_size=0.2, random_state=0)

import tensorflow as tf
num_units1 = 4
x = tf.placeholder(tf.float32, [None, 2])
w5 = tf.Variable(tf.truncated_normal([2, num_units1]))
b5 = tf.Variable(tf.zeros([num_units1]))
hidden0 = tf.nn.tanh(tf.matmul(x, w5) + b5)

num_units2 = 6
w4 = tf.Variable(tf.truncated_normal([num_units1, num_units2]))
b4 = tf.Variable(tf.zeros([num_units2]))
hidden1 = tf.nn.tanh(tf.matmul(hidden0, w4) + b4)

num_units3 = 8
w3 = tf.Variable(tf.truncated_normal([num_units2, num_units3]))
b3 = tf.Variable(tf.zeros([num_units3]))
hidden2 = tf.nn.tanh(tf.matmul(hidden1, w3) + b3)

num_units4 = 10
w2 = tf.Variable(tf.truncated_normal([num_units3, num_units4]))
b2 = tf.Variable(tf.zeros([num_units4]))
hidden3 = tf.nn.tanh(tf.matmul(hidden2, w2) + b2)

num_units5 = 12
w1 = tf.Variable(tf.truncated_normal([num_units4, num_units5]))
b1 = tf.Variable(tf.zeros([num_units5]))
hidden4 = tf.nn.tanh(tf.matmul(hidden3,w1) + b1)

w0 = tf.Variable(tf.zeros([num_units5, 1]))
b0 = tf.Variable(tf.zeros([1]))
p = tf.nn.sigmoid(tf.matmul(hidden4, w0) + b0)

t = tf.placeholder(tf.float32, [None, 1])
loss = -tf.reduce_sum(t*tf.log(p) + (1-t)*tf.log(1-p))
train_step = tf.train.AdamOptimizer().minimize(loss)

correct_prediction = tf.equal(tf.sign(p-0.5), tf.sign(t-0.5))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

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

y_matrix_train = y_train.reshape(len(y_train),1)
y_matrix_test = y_test.reshape(len(y_test),1)

i = 0
for _ in range(1000):
    i += 1
    sess.run(train_step, feed_dict={x:X_train, t:y_matrix_train})
    if i % 100 == 0:
        loss_val, acc_val = sess.run(
            [loss, accuracy], feed_dict={x:X_train, t:y_matrix_train})
        acc_test_val =sess.run(accuracy, feed_dict={x:X_test, t:y_matrix_test})
        print ('Step: %d, Loss: %f, Accuracy(train): %f, Accuracy(test): %f'
               % (i, loss_val, acc_val, acc_test_val))

w0_val, w1_val,w2_val,w3_val,w4_val,w5_val,b0_val,b1_val,b2_val,b3_val,b4_val,b5_val  = sess.run([w0, w1,w2,w3,w4,w5,b0,b1,b2,b3,b4,b5])

import numpy as np
def net_input(X):
    return np.dot(X,w0_val)+b0_val

def predict(X):
    hidden_0 = np.tanh(np.dot(X,w5_val) + b5_val)
    hidden_1 = np.tanh(np.dot(hidden_0,w4_val) + b4_val)
    hidden_2 = np.tanh(np.dot(hidden_1,w3_val) + b3_val)
    hidden_3 = np.tanh(np.dot(hidden_2,w2_val) + b2_val)
    hidden_4 = np.tanh(np.dot(hidden_3,w1_val) + b1_val)
    return np.where(1/(1+np.exp(-net_input(hidden_4))) >=0.5, 1, 0)

import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
def plot_decision_regions(X, y, resolution=0.02):

    # setup marker generator and color map
    markers = ('s', 'x')
    colors = ('red', 'blue')
    cmap = ListedColormap(colors[:len(np.unique(y))])

    # plot the decision surface
    x1_min, x1_max = X[:, 0].min() - 0.2, X[:, 0].max() + 0.2
    x2_min, x2_max = X[:, 1].min() - 0.2, X[:, 1].max() + 0.2
    xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution),
                         np.arange(x2_min, x2_max, resolution))
    Z = predict(np.array([xx1.ravel(), xx2.ravel()]).T)
    Z = Z.reshape(xx1.shape)
    plt.contourf(xx1, xx2, Z, alpha=0.4, cmap=cmap)
    plt.xlim(xx1.min(), xx1.max())
    plt.ylim(xx2.min(), xx2.max())

    # plot class samples
    for idx, cl in enumerate(np.unique(y)):
        plt.scatter(x=X[y == cl, 0], y=X[y == cl, 1],
                    alpha=0.8, c=cmap(idx),
                    marker=markers[idx], label=cl)

plot_decision_regions(X_test, y_test)
plt.legend(loc='lower left')
plt.tight_layout()
plt.show()