実行環境
・Python3.6.1
・JetBrains PyCharm Community Edition
・Windows10
学習データおよびテストデータについて
学習データおよびテストデータとして手書き文字を何万データ分作るのは大変なので今回はMNISTという手書き数字のデータベースを用いてやります。
以下のサイトよりデータを入手できます
学習データ
テストデータ
※このデータは学習データで60000件、テストデータで10000件のデータセットになっている。いかに今回のソースを示すがもしソースコードを修正し自分なりに
アレンジを加えたいと思う方がいればより小さいデータセットが以下になる
学習データ(100件)
テストデータ(10件)
ニューラルネットワークの初期化
詳しい概念はページの下に参考書籍を載せておいたのでそちらを参考に!まず最初にニューラルネットワークで使う値の設定を行う。
ニューラルネットワークで重要になってくるのは入力層、隠れ層、出力層のノード数、リンクの重み付、学習率の設定を行う必要がある。
また活性化関数としてシグモイド関数(sig(a)=1/(1+exp(−a)))を用いる。シグモイド関数の公式を書いているがこれを多言語でやるとなかなか大変そうに感じるが、
Pythonは数学関係のライブラリーが多数存在し、シグモイド関数も一行で示すことができる。
以下初期化のメソッドである
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# ニューラルネットワークの初期化 def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate): # 入力層、隠れ層、出力層のノード数の設定 self.inodes = inputnodes self.hnodes = hiddennodes self.onodes = outputnodes # リンクの重み行列 wih と who # 行列内の重み w_i_j, ノード i から次の層のノードjへのリンクの重み # w11 w21 # w12 w22 など self.wih = numpy.random.normal(0.0, pow(self.hnodes, -0.5), (self.hnodes, self.inodes)) self.who = numpy.random.normal(0.0, pow(self.onodes, -0.5), (self.onodes, self.hnodes)) # 学習率の設定 self.lr = learningrate # 活性化関数はシグモイド関数 self.activation_function = lambda x: scipy.special.expit(x) pass |
ニューラルネットワークへの照会
ブログの概念を書くのには限界があるためよりくわしい概念を知りたい方はページの下に参考書籍を載せているので参考に!
このメソッドではニューラルネットワークへの入力を受け取り、そのネットワークへの出力を返す役割を果たすメソッドです。出力を行うためには
初期化で設定した内容を用いて計算する必要があります。入力層から隠れ層へ、隠れ層から出力層へ信号を送っていきます。
隠れ層へはまずニューラルネットワークへの入力に対し、隠れ層の重みを掛け合わせます。
式ではX(hidden) = W(input_hidden) ・ I
となります。以下で求めた値を出力層へ送るために活性化関数を用います。
式では O(hidden) = sigmoid(X(hidden)) となる。
同じ流れで隠れ層から出力層も行い出力された値がニューラルネットワークからの出力となる
以下メソッドの全貌である
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# ニューラルネットワークへの照会 def query(self, inputs_list): # 入力リストを行列に変換 inputs = numpy.array(inputs_list, ndmin=2).T # 隠れ層に入ってくる信号の計算 hidden_inputs = numpy.dot(self.wih, inputs) # 隠れ層で結合された信号を活性化関数により出力 hidden_outputs = self.activation_function(hidden_inputs) # 出力層に入ってくる信号の計算 final_inputs = numpy.dot(self.who, hidden_outputs) # 出力層で結合された信号を活性化関数により出力 final_outputs = self.activation_function(final_inputs) return final_outputs |
ニューラルネットワークの学習
出力結果が実際の結果と違ったとき、重みを調整する必要性がある。それを繰り返しすることによりニューラルネットワークの精度を上げていく必要がある。
誤差を計算し最適な値を求めていく必要があるがそれが以下となる
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# ニューラルネットワークの学習 def train(self, inputs_list, targets_list): # 入力リストを行列に変換 inputs = numpy.array(inputs_list, ndmin=2).T targets = numpy.array(targets_list, ndmin=2).T # 隠れ層に入ってくる信号の計算 hidden_inputs = numpy.dot(self.wih, inputs) # 隠れ層で結合された信号を活性化関数により出力 hidden_outputs = self.activation_function(hidden_inputs) # 出力層に入ってくる信号の計算 final_inputs = numpy.dot(self.who, hidden_outputs) # 出力層で結合された信号を活性化関数により出力 final_outputs = self.activation_function(final_inputs) # 出力層の誤差 = (目標出力 - 最終出力) output_errors = targets - final_outputs # 隠れ層の誤差は出力層の誤差リンクの重みの割合で分配 hidden_errors = numpy.dot(self.who.T, output_errors) # 隠れ層と出力層の間のリンクの重みを更新 self.who += self.lr * numpy.dot( (output_errors * final_outputs * (1 - final_outputs)), numpy.transpose(hidden_outputs)) # 入力層と隠れ層の間のリンクの重みを更新 self.wih += self.lr * numpy.dot( (hidden_errors * hidden_outputs * (1 - hidden_outputs)), numpy.transpose(inputs)) pass |
早速やってみよう
入力層のノード数を784、隠れ層を200、出力層を10とする。隠れ層のノードは調整する必要があるが残りについてはまず、
入力層:784についてはMNISTのテストデータのピクセル配列が28×28=784であるところからきている。出力層:10は今回の手書き数字は0~9の
一桁を対象に行うもので0~9の計10数を対象にしていることからきている。
いかに学習からテストまでの内容を書いている
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# 入力層、隠れ層、出力層のノード数 input_nodes = 784 hidden_nodes = 200 output_nodes = 10 # 学習率 = 0.01 learning_rate = 0.01 # epochs: 訓練データが学習で使われた回数 epochs = 5 # ニューラルネットワークのインスタンスの生成 n = neuralNetrwork(input_nodes, hidden_nodes, output_nodes, learning_rate) # MNIST 訓練データのCSVファイルを読み込んでリストにする training_data_file = open("mnist_dataset/mnist_train.csv", 'r') training_data_list = training_data_file.readlines() training_data_file.close() # ニューラルネットワークの学習 for e in range(epochs): # 訓練データの全データに対して実行 for record in training_data_list: # データをコンマ ',' でsplit all_values = record.split(',') # 入力値のスケーリングとシフト inputs = (numpy.asfarray(all_values[1:]) / 255.0 * 0.99) + 0.01 # 目標配列の生成(ラベルの位置が0.99 残り0.01) targets = numpy.zeros(output_nodes) + 0.01 # all_values[0] はこのデータのラベル targets[int(all_values[0])] = 0.99 n.train(inputs, targets) ## create rotated variations # rotated anticlockwise by x degrees inputs_plusx_img = scipy.ndimage.interpolation.rotate(inputs.reshape(28, 28), 10, cval=0.01, order=1, reshape=False) n.train(inputs_plusx_img.reshape(784), targets) # rotated clockwise by x degrees inputs_minusx_img = scipy.ndimage.interpolation.rotate(inputs.reshape(28, 28), -10, cval=0.01, order=1, reshape=False) n.train(inputs_minusx_img.reshape(784), targets) pass pass # MNISTテストデータのCSVファイルを読み込み test_data_file = open("mnist_dataset/mnist_test.csv", 'r') test_data_list = test_data_file.readlines() test_data_file.close() # ニューラルネットワークのテスト scorecard = [] for record in test_data_list: all_values = record.split(',') # 正解は配列の1番目 correct_label = int(all_values[0]) # 入力値のスケーリングとシフト inputs = (numpy.asfarray(all_values[1:]) / 255.0 * 0.99) + 0.01 # ネットワークへの照会 outputs = n.query(inputs) # 最大値のインデックスがラベルに対応 label = numpy.argmax(outputs) # 正解(1)、間違い(0)をリストに追加 if(label == correct_label): scorecard.append(1) else: scorecard.append(0) pass pass # 評価値の計算 scorecard_array = numpy.asarray(scorecard) print("perfomance = ", scorecard_array.sum() / scorecard_array.size) |
今回のまとめ
以上でテストしてみると正答率が約96%という結果が出た。今回はscikit-learnやtensorflowといったライブラリーを使わずにやったが高い正答率がでるものが
完成できた。
以下もっと詳しい概念を知りたい人がいれば以下の本をお勧めします
〇マイナビ出版 ニューラルネットワーク自作入門 Tariq Rashid[著]親納浩幸[監訳]