前回の第18回では、隠れ層が2層からなる多層ニューラルネットワークを使用して、非線形データを分類することをご紹介しました。今回は、隠れ層が3層からなる多層ニューラルネットワークを使用して、更に複雑な非線形データを分類してみたいと思います。
図1
TensorFlowによる多層ニューラルネットワークを使用して図1の非線形データを分類する機械学習モデルを作成します。図1は一見、シンプルなデータに見えるのですが、このデータを分類するためには、少なくともS字曲線で境界を描かないと分類できないことが容易にわかるかと思います。ここでは、隠れ層が3層からなるTensorFlowによる多層ニューラルネットワークを使用した機械学習の流れは、概ね以下になります。なお、Pythonのバージョンは3.5以上を想定しています。まだ、Pythonや関連ライブラリーをまだ導入されていない方は、第2回および第13回を参考に導入してください。
- 非線形データを入力する。
- 入力データを、トレーニングデータとテストデータに分ける。
- TensorFlowにより、1層目の隠れ層(hidden layer)の最適化すべき係数(Variable)と活性化関数を定義する。
- TensorFlowにより、2層目の隠れ層(hidden layer)の最適化すべき係数(Variable)と活性化関数を定義する。
- TensorFlowにより、3層目の隠れ層(hidden layer)の最適化すべき係数(Variable)と活性化関数を定義する。
- TensorFlowにより、出力層(output layer)の最適化すべき係数(Variable)と活性化関数を定義する。
- TensorFlowにより誤差関数とその最適化法を定義する。
- TensorFlowにより精度を定義する。
- TensorFlowによりセッションを定義する。
- TensorFlowによりセッションを実行する。
- 学習結果に基づき、新データを予測分類するpredict関数を定義する。
- 分類結果および予測値を表示するためのプロット関数を定義する。
- 分類結果および予測値を図に表示する。
では、各ステップを詳しく見ていきましょう。
①非線形データを入力する。
まず、対話形式ではなく、Pythonのスクリプトをファイルで用意します。Pythonスクリプトの先頭に以下の2行を添付しておくことをお勧めします。
#!/usr/bin/env python # -*- coding:utf-8 -*- |
今回は、図1の非線形データをsklean.datasetライブラリーのmake_moons関数を使用して生成して、入力データを作成します。make_moons関数によって、既にデータが標準化されているため、今回も、変量データの標準化は不要です。
from sklearn.datasets import make_moons X, y = make_moons(n_samples=1000, random_state=123) |
②入力データを、トレーニングデータとテストデータに分ける。
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]) w3 = tf.Variable(tf.truncated_normal([2, num_units1])) b3 = tf.Variable(tf.zeros([num_units1])) hidden0 = tf.nn.tanh(tf.matmul(x, w3) + b3) |
num_units1は1層目の隠れ層におけるノードの数です。ここでは4を指定していますが、2以上の自然数を指定することにより、ノードの数を増やすことができます。ノードの数を増やすことにより、より複雑なモデルに対応することができますが、それに伴いより多くのコンピュータ資源が必要となります。この例では4を指定します。
TensorFlowでは、トレーニングデータを「placeholder」に格納し、機械学習により最適化すべき1層目の隠れ層の係数b3およびw3を「Variable」に格納します。ここでは、placeholderのデータの型と変量の数(ここでは2)のみを指定し、サンプルの数(ここではトレーニングデータの800)は指定しなくても構いません(ここではNoneを指定)。実際のデータ内容はセッション実行時に設定するため、ここでは指定しません。最適化すべき係数のb3の値は、0(ゼロ)に初期化しますが、w3は乱数を用いて初期値を決定しています。Net Input関数と活性化関数による1層目の隠れ層の出力としてhidden0を定義します。ここで注意すべきことは、Net Input関数も活性化関数も、必ずtensorflowライブラリーのものを使用しなければならないということです。例えば、Pythonプログラミングとしては、tf.matmul(x,w3)もnp.dot(x,w3)も全く同じ機能で計算結果も同じですが、np.dot(x,w3)を使用するとTensorFlowのセッション実行時にエラーとなるので、注意が必要です。tf.tanh関数に関しても同様で、np.tanhと指定するとセッション実行時にエラーになります。
④TensorFlowにより、2層目の隠れ層(hidden layer)の最適化すべき係数(Variable)と活性化関数を定義する。
2層目の隠れ層について以下のように記述します。
num_units2 = 6 w2 = tf.Variable(tf.truncated_normal([num_units1, num_units2])) b2 = tf.Variable(tf.zeros([num_units2])) hidden1 = tf.nn.tanh(tf.matmul(hidden0, w2) + b2) |
num_units2は2層目の隠れ層におけるノードの数です。ここでは6を指定しています。機械学習により最適化すべき2層目の隠れ層の係数b2およびw2を「Variable」に格納します。w2はnum_units1 x num_units2の行列になります。最適化すべき係数のb2の値は、0(ゼロ)に初期化しますが、w2は乱数を用いて初期値を決定しています。1層目の隠れ層の出力であるhidden0を入力にしてNet Input関数と活性化関数による2層目の隠れ層の出力hidden1を定義します。
⑤TensorFlowにより、3層目の隠れ層(hidden layer)の最適化すべき係数(Variable)と活性化関数を定義する。
3層目の隠れ層について以下のように記述します。
num_units3 = 8 w1 = tf.Variable(tf.truncated_normal([num_units2, num_units3])) b1 = tf.Variable(tf.zeros([num_units3])) hidden2 = tf.nn.tanh(tf.matmul(hidden1,w1) + b1) |
num_units3は3層目の隠れ層におけるノードの数です。ここでは8を指定しています。機械学習により最適化すべき3層目の隠れ層の係数b1およびw1を「Variable」に格納します。w1はnum_units2 x num_units3の行列になります。最適化すべき係数のb1の値は、0(ゼロ)に初期化しますが、w1は乱数を用いて初期値を決定しています。2層目の隠れ層の出力であるhidden1を入力にしてNet Input関数と活性化関数による3層目の隠れ層の出力hidden2を定義します。
⑥TensorFlowにより、出力層(output layer)の最適化すべき係数(Variable)と活性化関数を定義する。
最適化すべき係数 w0 と b0を0(ゼロ)に初期化します。3層目の隠れ層からの出力であるhidden2の値を入力にNet Input関数を計算し、sigmoid関数で0〜1の確率に変換します。
w0 = tf.Variable(tf.zeros([num_units3, 1])) b0 = tf.Variable(tf.zeros([1])) p = tf.nn.sigmoid(tf.matmul(hidden2, 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の値を最適化すれば良いことになります。これを最尤推定法(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)) |
⑨TensorFlowによりセッションを定義する。
Variable(w0, b0, w1,b1, w2, b2,w3,b3)の値を最適化するための、セッションを定義して、各Variableの値を初期化します。
sess = tf.InteractiveSession() sess.run(tf.initialize_all_variables()) |
⑩TensorFlowによりセッションを実行する。
誤差関数が最小になるよう、Variableの最適化を2,000回繰り返します。ここでは、200回繰り返すごとに、その時点での誤差関数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(2000): i += 1 sess.run(train_step, feed_dict={x:X_train, t:y_matrix_train}) if i % 200 == 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: 200, Loss: 286.829437, Accuracy(train): 0.902500, Accuracy(test): 0.895000
Step: 400, Loss: 156.088913, Accuracy(train): 0.957500, Accuracy(test): 0.960000
Step: 600, Loss: 54.059677, Accuracy(train): 1.000000, Accuracy(test): 1.000000
Step: 800, Loss: 26.490345, Accuracy(train): 1.000000, Accuracy(test): 1.000000
Step: 1000, Loss: 16.414062, Accuracy(train): 1.000000, Accuracy(test): 1.000000
Step: 1200, Loss: 11.394475, Accuracy(train): 1.000000, Accuracy(test): 1.000000
Step: 1400, Loss: 8.442007, Accuracy(train): 1.000000, Accuracy(test): 1.000000
Step: 1600, Loss: 6.523116, Accuracy(train): 1.000000, Accuracy(test): 1.000000
Step: 1800, Loss: 5.190636, Accuracy(train): 1.000000, Accuracy(test): 1.000000
Step: 2000, Loss: 4.220972, Accuracy(train): 1.000000, Accuracy(test): 1.000000
実行Stepが進むにつれ、誤差関数(Loss)の値は減少するとともに、トレーニングデータおよびテストデータの精度(Accuracy)の値が、それぞれ、増加して、最終的に精度が100%となり、完全に分類できていることが分かります。
次に、最終時点での係数(w0, w1, w2, w3, b0, b1, b2, b3)の値を取得します。
w0_val, w1_val,w2_val,w3_val,b0_val,b1_val,b2_val,b3_val = sess.run([w0, w1,w2,w3,b0,b1,b2,b3]) |
(11)学習結果に基づき、新データを予測分類するpredict関数を定義する。
学習結果の結果、最終的に得られた係数(w0, w1, w2, w3, b0, b1,b2, b3)の値を用いて、予測モデル 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,w3_val) + b3_val) hidden_1 = np.tanh(np.dot(hidden_0,w2_val) + b2_val) hidden_2 = np.tanh(np.dot(hidden_1,w1_val) + b1_val) return np.where(1/(1+np.exp(-net_input(hidden_2))) >=0.5, 1, 0) |
(12)分類結果および予測値を表示するためのプロット関数を定義する。
(11)で定義した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) |
(13)分類結果および予測値を図に表示する。
(12)で定義したplot_decision_regions関数を実行し、テストデータの分類結果を表示させます。
plot_decision_regions(X_test, y_test) plt.legend(loc='lower left') plt.tight_layout() plt.show() |
図2.
テストデータについて、完全に曲線で2つのクラスが分類されていることがわかります。このように、多層ニューラルネットワークの隠れ層の数とノードの数を増やすことにより、更に複雑な非線形データを分類することができました。
なお、今回の内容は、以下の書籍の3章を参考に執筆いたしました。ご興味のある方は、ご参考いただければと思います。
「TensorFlowで学ぶディープラーニング入門 ~畳み込みニューラルネットワーク徹底解説~」
中井 悦司 (著)
出版社: マイナビ出版
全体を通してのコードは以下のようになります。なお、本コードの稼働環境は、Python3.5以上を想定しています。
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from sklearn.datasets import make_moons
X, y = make_moons(n_samples=1000, random_state=123)
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])
w3 = tf.Variable(tf.truncated_normal([2, num_units1]))
b3 = tf.Variable(tf.zeros([num_units1]))
hidden0 = tf.nn.tanh(tf.matmul(x, w3) + b3)
num_units2 = 6
w2 = tf.Variable(tf.truncated_normal([num_units1, num_units2]))
b2 = tf.Variable(tf.zeros([num_units2]))
hidden1 = tf.nn.tanh(tf.matmul(hidden0, w2) + b2)
num_units3 = 8
w1 = tf.Variable(tf.truncated_normal([num_units2, num_units3]))
b1 = tf.Variable(tf.zeros([num_units3]))
hidden2 = tf.nn.tanh(tf.matmul(hidden1,w1) + b1)
w0 = tf.Variable(tf.zeros([num_units3, 1]))
b0 = tf.Variable(tf.zeros([1]))
p = tf.nn.sigmoid(tf.matmul(hidden2, 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(2000):
i += 1
sess.run(train_step, feed_dict={x:X_train, t:y_matrix_train})
if i % 200 == 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,b0_val,b1_val,b2_val,b3_val = sess.run([w0, w1,w2,w3,b0,b1,b2,b3])
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,w3_val) + b3_val)
hidden_1 = np.tanh(np.dot(hidden_0,w2_val) + b2_val)
hidden_2 = np.tanh(np.dot(hidden_1,w1_val) + b1_val)
return np.where(1/(1+np.exp(-net_input(hidden_2))) >=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()
|