第18回および第19回では、隠れ層が、それぞれ2層および3層からなる多層ニューラルネットワークを使用して、非線形データを分類することをご紹介しました。今回は、隠れ層が5層からなる多層ニューラルネットワークを使用して、更に複雑な非線形データを分類してみたいと思います。
図1
TensorFlowによる多層ニューラルネットワークを使用して図1の非線形データを分類する機械学習モデルを作成します。図1のデータを分類するためには、ここでは隠れ層の数を5で検討してみました。 隠れ層が5層からなるTensorFlowによる多層ニューラルネットワークを使用した機械学習の流れは、概ね以下になります。なお、Pythonのバージョンは3.5以上を想定しています。まだ、Pythonや関連ライブラリーをまだ導入されていない方は、第2回および第13回を参考に導入してください。
- 非線形データを入力する。
- 入力データを、トレーニングデータとテストデータに分ける。
- TensorFlowにより、1層目の隠れ層(hidden layer)の最適化すべき係数(Variable)と活性化関数を定義する。
- TensorFlowにより、2層目の隠れ層(hidden layer)の最適化すべき係数(Variable)と活性化関数を定義する。
- TensorFlowにより、3層目の隠れ層(hidden layer)の最適化すべき係数(Variable)と活性化関数を定義する。
- TensorFlowにより、4層目の隠れ層(hidden layer)の最適化すべき係数(Variable)と活性化関数を定義する。
- TensorFlowにより、5層目の隠れ層(hidden layer)の最適化すべき係数(Variable)と活性化関数を定義する。
- TensorFlowにより、出力層(output layer)の最適化すべき係数(Variable)と活性化関数を定義する。
- TensorFlowにより誤差関数とその最適化法を定義する。
- TensorFlowにより精度を定義する。
- TensorFlowによりセッションを定義する。
- TensorFlowによりセッションを実行する。
- 学習結果に基づき、新データを予測分類するpredict関数を定義する。
- 分類結果および予測値を表示するためのプロット関数を定義する。
- 分類結果および予測値を図に表示する。
では、各ステップを詳しく見ていきましょう。
①非線形データを入力する。
まず、対話形式ではなく、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()
|