新着情報

Neural Network Classをインスタント化して手書き数字の訓練データの学習

Neural Newwork Classをインスタンス化して、手書き数字の画像データセットMINSTの訓練データの学習を実施する。

それを行うnn_ecognition.pyのプログラム例を示す。

import numpy as np
class neuralNetwork:    
    
    # ニューラルネットワークの初期化
    def __init__(self,
                 input_neurons,  # 入力層のニューロン数
                 hidden_neurons, # 隠れ層のニューロン数
                 output_neurons, # 出力層のニューロン数
                 learning_rate   # 学習率
                ):
        # 入力層、隠れ層、出力層のニューロン数をインスタンス変数に代入
        self.inneurons = input_neurons     # 入力層のニューロン数
        self.hneurons = hidden_neurons     # 隠れ層のニューロン数
        self.oneurons = output_neurons     # 出力層のニューロン数
        # 学習率をインスタンス変数に代入
        self.lr = learning_rate
        # weight_initialize()を呼ぶ
        self.weight_initialize()

    # 重みの初期化を行う
    def weight_initialize(self):
        # 入力層と隠れ層の間のリンクの重みの初期値を設定
        self.w_h = np.random.normal(0.0,                       # 平均は0
                                    pow(self.inneurons, -0.5), # 標準偏差は入力層のニューロン数を元に計算
                                    (self.hneurons,            # 隠れ層のニューロン数を行数
                                     self.inneurons + 1)       # バイアスを加え、列数とする
                                    )
       # 隠れ層と出力層の間のリンクの重みの初期値を設定
        self.w_o = np.random.normal(0.0,                       # 平均は0
                                    pow(self.hneurons, -0.5),  # 標準偏差は隠れ層のニューロン数を元に計算
                                    (self.oneurons,            # 出力層のニューロン数を行数
                                                               # 隠れ層のニューロン数に
                                     self.hneurons + 1)        # バイアスを加え、列数とする
                                   )
    # シグモイド関数を活性化関数として定義
    def activation_function(self, x):
        return 1 / (1 + np.exp(-x))

    # ニューラルネットワークの学習を行うメソッド
    def train(self,
              inputs_list,  # 入力値の配列
              targets_list  # 目標出力の配列
             ):
        # 入力値の配列にバイアス項を追加して1列の行列に変換
        inputs = np.array(
            np.append(inputs_list, [1]),     # 配列の末尾にバイアスのための値「1」を追加
            ndmin=2                          # 2次元化
        ).T                                  # 転置して1列の行列にする
        
        # 目標値の配列を1列の行列に変換する
        targets = np.array(targets_list,     # 目標値の配列
                                   inputs        # 入力値の行列
                              )

        # 活性化関数を適用して隠れ層から出力する
        hidden_outputs = self.activation_function(hidden_inputs)
        
        # 隠れ層の出力行列の末尾にバイアスの値として1を追加
        hidden_outputs = np.append(hidden_outputs,     # 隠れ層の出力行列
                                   [[1]],    # 2次元形式でバイアス値を追加
                                   axis=0    # 行を指定(列は1)
                                  )        
        # 出力層への入力信号を計算
        final_inputs = np.dot(self.w_o,      # 隠れ層と出力層の間の重み
                              hidden_outputs # 隠れ層の出力
                              )

        # 活性化関数を適用して出力層から出力する
        final_outputs = self.activation_function(final_inputs)
        
        # 目標値と出力層の出力信号の誤差を求める
        output_errors = targets - final_outputs
        # 誤差逆伝播により隠れ層の誤差を求める
        hidden_errors = np.dot(self.w_o.T,   # 隠れ層と出力層の間の重み行列を転置する
                               output_errors # 出力値と目標値との誤差
                              ) 
        # 隠れ層と出力層の間の重みの更新
        self.w_o += self.lr * np.dot(
            # 出力誤差*出力信号* (1-出力信号)
            (output_errors * final_outputs * (1.0 - final_outputs)),
            # 出力信号の行列を転置
            np.transpose(hidden_outputs)
        )
        
        # 隠れ層の出力エラーからバイアスのものを取り除く
        hidden_errors_nobias = np.delete(
            hidden_errors,  # 隠れ層の出力エラーの行列
            self.hneurons,  # バイアスを除くニューロン数をインデックスにする
            axis=0          # 行の削除を指定
            )
        # 隠れ層の出力からバイアスを取り除く
        hidden_outputs_nobias = np.delete(
            hidden_outputs, # 隠れ層の出力の行列
            self.hneurons,  # バイアスを除くニューロン数をインデックスにする
            axis=0          # 行の削除を指定
            )

        # 入力層と隠れ層の間の重みの更新
        self.w_h += self.lr * np.dot(
            # 隠れ層の出力誤差*隠れ層の出力* (1-隠れ層の出力)
            (hidden_errors_nobias * hidden_outputs_nobias * (
                1.0 - hidden_outputs_nobias)),
            # 入力層の出力信号の行列を転置
            np.transpose(inputs))
   
    # 学習結果を元にテストデータを評価するメソッド
    def evaluate(self,
                 inputs_list                 # テスト用データの配列
                ):
        # テスト用データの配列を1列の行列に変換する
        inputs = np.array(
            np.append(inputs_list, [1]),     # 配列の末尾にバイアスの値「1」を追加
            ndmin=2                          # 2次元化
        ).T                                  # 転置して1列の行列にする
        
        # 隠れ層への入力信号を計算
        hidden_inputs = np.dot(self.w_h,     # 入力層と隠れ層の間の重み
                               inputs        # テストデータの行列
                              )
        
        # 活性化関数を適用して隠れ層から出力する
        hidden_outputs = self.activation_function(hidden_inputs)
        
        # 出力層への入力信号を計算
        final_inputs = np.dot(
            self.w_o,                        # 隠れ層と出力層の間の重み
            np.append(hidden_outputs, [1]),  # 隠れ層の出力配列の末尾にバイアスの値「1」を追加
            )
        
        # 活性化関数を適用して出力層から出力する
        final_outputs = self.activation_function(final_inputs)
        
        # 出力層からの出力を戻り値として返す
        return final_outputs

学習の実行に際し、以下の様に、MINSTデータ・セットを読み込み、0.01から0.99の範囲に値を加工する。

from keras.datasets import mnist
(x_trains, y_trains), (x_tests, y_tests) = mnist.load_data()
# 60000x28x28の2次元配列を60000×784の行列に変換
x_trains = x_trains.reshape(60000, 784)
# データを255で割って0.99を掛けた後、0.01を加えてシフトする
x_trains = (x_trains / 255.0 * 0.99) + 0.01

ニューラル・ネットワークによるMINSTデータセットの学習の実行

import time

# 学習を行って結果を出力
start = time.time()                   # プログラムの開始時刻を取得

input_neurons = 784    # 入力層のニューロンの数
hidden_neuronss = 200  # 隠れ層のニューロンの数
output_neurons = 10    # 出力層のニューロンの数
learning_rate = 0.1    # 学習率

# neuralNetworkオブジェクトの生成
n = neuralNetwork(input_neurons,
                  hidden_neuronss,
                  output_neurons,
                  learning_rate)

# ニューラルネットワークの学習
# 学習を繰り返す回数
epochs = 5

# 指定した回数だけ学習を繰り返す
for e in range(epochs):
    # 画像データと正解ラベルを順番に取り出してネットワークに入力
    for (inputs, target) in zip(x_trains, y_trains):
        # 出力層のニューロン数に合わせて目標配列を作成
        targets = np.zeros(output_neurons) + 0.01
        # 正解値に対応する要素を0.99にする
        targets[int(target)] = 0.99
        # 学習を実行
        n.train(inputs,  # 訓練データの行列
                targets  # 目標値の行列
               )

print('done')
# 処理にかかった時間を出力
print("Computation time:{0:.3f} sec".format(time.time() - start))

次に、訓練データを使って学習したニューラル・ネットワークの精度をMINSTデータ・セットのtestsデータ10,000個を使用して調べた結果を以下に示す。

# 10000x28x28の2次元配列を10000×784の行列に変換
x_tests = x_tests.reshape(10000, 784)
# データを255で割って0.99を掛けた後、0.01を加えてシフトする
x_tests = (x_tests / 255.0 * 0.99) + 0.01

# 正解は1、不正解は0を格納する配列
score = []

# x_testsをinputs、y_testsをcorrect_labelに格納
for (inputs,
     correct_label) in zip(x_tests,
                           y_tests):
    # ニューラルネットワークで評価する
    outputs = n.evaluate(inputs)
    # 出力層のニューロン数に合わせて正解の配列を作成
    targets = np.zeros(output_neurons) + 0.01
    # 正解値に対応する要素を0.99にする
    targets[int(target)] = 0.99
    # 出力の行列の最大値のインデックスが予測する手書き数字に対応
    label = np.argmax(outputs)
    # ネットワークの出力と正解ラベルを比較
    if (label == correct_label):
        # 正解ならscoreに1を追加
        score.append(1)
    else:
        # 不正解なら0を追加
        score.append(0)

配列score[]の可視化を以下により実施した結果

result = ['□' if i  == 1 else '■' for i in score]
print(result)

10,000個も示すことができないので一部だけ表示する。
‘□’は正解、’■’は不正解を示す
[‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘■’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘■’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘■’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’, ‘□’,

正解率の計算は以下の様に実施できる。

# 配列scoreをNumPy配列に変換
score_arry = np.asarray(score)
# score_arryの合計をscore_arryの要素数で割って正解率を求める 
print ("performance = ",
       score_arry.sum() / score_arry.size)

正解率は約 97.4 % となった。
performance = 0.9737

 

参考文献に記載されたpythonプログラムを、python 3.5, Tensorflow 1.9.0の環境下で使用してMNISTデータセットの訓練用手書き数字データの学習を実施、テストデータを使用して精度検証をおこなった。

参考文献:

夢見るディープラーニング:ニューラルネットワーク[Python実装]入門

秀和システム、2018、金城俊哉著

コメント投稿フォーム

メールアドレスが公開されることはありません。 が付いている欄は必須項目です