ニューラルネットワーク(3)

前回使用したテストデータは10種ですが、次にもっと大きなデータを使ってテストするときのために正解率を表示できるようにしておきます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 結果判定リスト
score = []
for data in test_data:
val = data.split(',')
answer = int(val[0])
res = n_network.query((numpy.asfarray(val[1:]) / 255.0 * 0.99) + 0.01)
# 最大値のものを算出した答えとする
res_max = numpy.argmax(res)
print('正解', answer, '算出した答え', res_max, '=>', '〇' if answer == res_max else '×')

score.append(1 if answer == res_max else 0)

# 正解率
print('# 正解率 # {:5.2f}%'.format(sum(score) / len(score) * 100))

【結果】
結果

現状7割の正解率ですが学習データを増やしたり学習回数、学習率を調整してみます。

これまでは学習データ100個、テストデータ10個で簡単に動作確認してきましたが、今回は学習データ60,000個、テストデータ10,000個を使ってどのくらい正確に手書き文字を認識するかテストします。
おさらいとして、自作した完成版のニューラルネットワークを確認します。

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
import numpy
import scipy.special
# ニューラルネットワーク(完成版)
class neural_network:
# 【初期化】
# 入力層、隠れ層、出力層のノード数を設定する。
def __init__(self, in_node, hid_node, out_node, learn_rate):
self.in_node = in_node # 入力層
self.hid_node = hid_node # 隠れ層
self.out_node = out_node # 出力層
self.learn_rate = learn_rate # 学習率

# 重み行列(処理の核となる)
# 正規分布の平均、標準偏差、配列の大きさを設定
self.weight_in_hid = numpy.random.normal(0.0, pow(self.hid_node, -0.5), (self.hid_node, self.in_node))
self.weight_hid_out = numpy.random.normal(0.0, pow(self.out_node, -0.5), (self.out_node, self.hid_node))

# 活性化関数はシグモイド関数
self.activation_func = lambda x: scipy.special.expit(x)

# 【学習】
# 学習データから重みを調整する。
def train(self, in_list, target_list):
# 入力データ(1次元)を2次元化して転置をとる。
#(横長の配列が縦長になる)
in_matrix = numpy.array(in_list, ndmin=2).T
target_matrix = numpy.array(target_list, ndmin=2).T
# -------------- 重みをかけて発火させる --------------
# 入力層→隠れ層の計算
hid_in = numpy.dot(self.weight_in_hid, in_matrix)
hid_out = self.activation_func(hid_in)

# 隠れ層→出力層の計算
final_in = numpy.dot(self.weight_hid_out, hid_out)
final_out = self.activation_func(final_in)
# -------------- 誤差の計算 --------------
# 出力層の誤差(目標出力 - 最終出力)
out_err = target_matrix - final_out
# 隠れ層の誤差は出力層の誤差をリンクの重みの割合で分配
hid_err = numpy.dot(self.weight_hid_out.T, out_err)
# -------------- 重みの更新(処理の核) --------------
# 隠れ層と出力層の間のリンクの重みを更新
self.weight_hid_out += self.learn_rate * numpy.dot((out_err * final_out * (1.0 - final_out)), numpy.transpose(hid_out))

# 入力層と隠れ層の間のリンクの重みを更新
self.weight_in_hid += self.learn_rate * numpy.dot((hid_err * hid_out * (1.0 - hid_out)), numpy.transpose(in_matrix))

# 【照会】
# 入力に対して出力層からの答えを返す。
def query(self, input_list):
# 入力リストを行列に変換
# 1次元配列は2次元配列に変換し転置をとる。
#(横長の配列が縦長になる)
in_matrix = numpy.array(input_list, ndmin=2).T

# 隠れ層に入ってくる信号の計算(入力層に重みをかける)
hid_in = numpy.dot(self.weight_in_hid, in_matrix)
# 隠れ層で結合された信号を活性化関数(シグモイド関数)により出力
# (閾値を超えたものが発火する)
hid_out = self.activation_func(hid_in)

# 出力層に入ってくる信号の計算(隠れ層に重みをかける)
final_in = numpy.dot(self.weight_hid_out, hid_out)
# 出力層で結合された信号を活性化関数(シグモイド関数)により出力
# (閾値を超えたものが発火する)
final_out = self.activation_func(final_in)

return final_out

次に今回使用する学習データ60,000個とテストデータ10,000個をダウンロードしておきます。

1
2
!wget https://www.pjreddie.com/media/files/mnist_train.csv
!wget https://www.pjreddie.com/media/files/mnist_test.csv

60,000個の学習データを学習率0.2、学習回数(エポック)1回で学習させます。

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
# 60,000個のデータを学習
import numpy
import matplotlib.pyplot
%matplotlib inline

in_node = 784 # 入力層のノード数(28 * 28)
hid_node = 200 # 隠れ層のノード数
out_node = 10 # 出力層のノード数(0~9を表す)

learn_rate = 0.2 # 学習率

# ニューラルネットワークのインスタンス生成
n_network = neural_network(in_node, hid_node, out_node, learn_rate)

# 学習データファイルを読み込んでリスト化
with open('mnist_train.csv', 'r') as f:
train_data = f.readlines()

epochs = 1 # 学習回数
for e in range(epochs):
# 学習データすべてに対して実行
for record in train_data:
val = record.split(',')
# 入力値のスケールとシフト
in_data = (numpy.asfarray(val[1:]) / 255.0 * 0.99) + 0.01
# 目標配列の生成(ラベル位置0.99、残り0.01)
target = numpy.zeros(out_node) + 0.01
target[int(val[0])] = 0.99
n_network.train(in_data, target)

10,000個のテストデータで正解率を算出します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 10,000個のテストデータで正解率を算出
with open('mnist_test.csv', 'r') as f:
test_data = f.readlines()

# 結果判定リスト
score = []
for data in test_data:
val = data.split(',')
answer = int(val[0])
res = n_network.query((numpy.asfarray(val[1:]) / 255.0 * 0.99) + 0.01)
# 最大値のものを算出した答えとする
res_max = numpy.argmax(res)

score.append(1 if answer == res_max else 0)

# 正解率
print('# 正解率 # {:5.2f}%'.format(sum(score) / len(score) * 100))

【結果】

1
# 正解率 # 95.38%

正解率をあげるため学習率や学習回数(エポック)を調整しようと考えていたのですが、もうすでに正解率95%以上と十分な正解率(認識率)となっています。
次に正解率がどう変動するのかを確認したいと思います。

学習処理と検証処理を関数化します。引数に学習率を設定すると、その学習率での正解率が返ります。

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
import numpy

# 学習率を変えて、学習・検証ができるように関数化する。
def train_test(learn_rate):
in_node = 784 # 入力層のノード数(28 * 28)
hid_node = 200 # 隠れ層のノード数
out_node = 10 # 出力層のノード数(0~9を表す)

# ニューラルネットワークのインスタンス生成
n_network = neural_network(in_node, hid_node, out_node, learn_rate)

# 学習データファイルを読み込んでリスト化
with open('mnist_train.csv', 'r') as f:
train_data = f.readlines()

epochs = 1 # 学習回数
for e in range(epochs):
# 学習データすべてに対して実行
for record in train_data:
val = record.split(',')
# 入力値のスケールとシフト
in_data = (numpy.asfarray(val[1:]) / 255.0 * 0.99) + 0.01
# 目標配列の生成(ラベル位置0.99、残り0.01)
target = numpy.zeros(out_node) + 0.01
target[int(val[0])] = 0.99
n_network.train(in_data, target)

# 10,000個のテストデータで正解率を算出
with open('mnist_test.csv', 'r') as f:
test_data = f.readlines()

# 結果判定リスト
score = []
for data in test_data:
val = data.split(',')
answer = int(val[0])
res = n_network.query((numpy.asfarray(val[1:]) / 255.0 * 0.99) + 0.01)
# 最大値のものを算出した答えとする
res_max = numpy.argmax(res)
score.append(1 if answer == res_max else 0)

# 正解率
return sum(score) / len(score) * 100

上記で定義した関数を学習率を変化させながら実行し、その結果を折れ線グラフに表示します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
%matplotlib inline
import matplotlib.pyplot as plt

x_data = []
y_data = []
# 0.1から0.9までの学習率で、正解率を算出する。
for i in numpy.arange(0.1, 1, 0.1):
x_data.append(i)
y_data.append(train_test(i))

plt.xlabel("learn_rate") # 学習率
plt.ylabel("accuracy_rate(%)") # 正解率

plt.plot(x_data, y_data, marker='o')
plt.show()

X軸に学習率、Y軸に正解率が表示されます。
結果

上記の結果から学習率が0.1の場合が一番正解率が高いことがわかりました。
次回は学習率を0.1に固定し、学習回数(エポック)を変化させると正解率がどのように変化するかを調べてみます。

さきほど関数化したものの引数に学習回数を追加します。

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
import numpy

# 学習率と学習回数を変えて、学習、検証ができるように関数化する。
def train_test(learn_rate, epochs):
in_node = 784 # 入力層のノード数(28 * 28)
hid_node = 200 # 隠れ層のノード数
out_node = 10 # 出力層のノード数(0~9を表す)

# ニューラルネットワークのインスタンス生成
n_network = neural_network(in_node, hid_node, out_node, learn_rate)

# 学習データファイルを読み込んでリスト化
with open('mnist_train.csv', 'r') as f:
train_data = f.readlines()

for e in range(epochs):
# 学習データすべてに対して実行
for record in train_data:
val = record.split(',')
# 入力値のスケールとシフト
in_data = (numpy.asfarray(val[1:]) / 255.0 * 0.99) + 0.01
# 目標配列の生成(ラベル位置0.99、残り0.01)
target = numpy.zeros(out_node) + 0.01
target[int(val[0])] = 0.99
n_network.train(in_data, target)

# 10,000個のテストデータで正解率を算出
with open('mnist_test.csv', 'r') as f:
test_data = f.readlines()

# 結果判定リスト
score = []
for data in test_data:
val = data.split(',')
answer = int(val[0])
res = n_network.query((numpy.asfarray(val[1:]) / 255.0 * 0.99) + 0.01)
# 最大値のものを算出した答えとする
res_max = numpy.argmax(res)
#print('正解', answer, '算出した答え', res_max, '=>', '〇' if answer == res_max else '×')

score.append(1 if answer == res_max else 0)

# 正解率
#print('# 正解率 # {:5.2f}%'.format(sum(score) / len(score) * 100))
return sum(score) / len(score) * 100

上記で定義した関数を学習回数を変化させながら実行し、その結果を折れ線グラフに表示します。
(学習率は前回もっとも結果のよかった0.1を指定します。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
%matplotlib inline
import matplotlib.pyplot as plt

x_data = []
y_data = []
# 0.1から0.9までの学習率(で、正解率を算出する。
for i in numpy.arange(0.1, 1, 0.1):
x_data.append(i)
y_data.append(train_test(i))

# データをグラフに設定
plt.xlabel("learn_rate") # 学習率
plt.ylabel("accuracy_rate(%)") # 正解率

plt.plot(x_data, y_data, marker='o')
plt.show()

X軸に学習回数、Y軸に正解率が表示されます。
結果

もともと学習回数が1回でも正解率95.75%となかなかの精度がでているのですが、学習回数をふやすとやや結果がよくなっていっているのがわかります。
ただ2%以内の増減なので、処理時間がすごく増える割には効果があるとは思えませんでした。
次回は学習率を0.1、学習回数を5回に固定し、隠れ層の数を変化させると正解率がどのように変化するかを調べてみます。

関数化したものの引数に隠れ層の数を追加します。

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
import numpy

# 学習率と学習回数を変えて、学習、検証ができるように関数化する。
def train_test(learn_rate, epochs, hid_node):
in_node = 784 # 入力層のノード数(28 * 28)
#hid_node = 200 # 隠れ層のノード数
out_node = 10 # 出力層のノード数(0~9を表す)

# ニューラルネットワークのインスタンス生成
n_network = neural_network(in_node, hid_node, out_node, learn_rate)

# 学習データファイルを読み込んでリスト化
with open('mnist_train.csv', 'r') as f:
train_data = f.readlines()

for e in range(epochs):
# 学習データすべてに対して実行
for record in train_data:
val = record.split(',')
# 入力値のスケールとシフト
in_data = (numpy.asfarray(val[1:]) / 255.0 * 0.99) + 0.01
# 目標配列の生成(ラベル位置0.99、残り0.01)
target = numpy.zeros(out_node) + 0.01
target[int(val[0])] = 0.99
n_network.train(in_data, target)

# 10,000個のテストデータで正解率を算出
with open('mnist_test.csv', 'r') as f:
test_data = f.readlines()

# 結果判定リスト
score = []
for data in test_data:
val = data.split(',')
answer = int(val[0])
res = n_network.query((numpy.asfarray(val[1:]) / 255.0 * 0.99) + 0.01)
# 最大値のものを算出した答えとする
res_max = numpy.argmax(res)
#print('正解', answer, '算出した答え', res_max, '=>', '〇' if answer == res_max else '×')

score.append(1 if answer == res_max else 0)

# 正解率
#print('# 正解率 # {:5.2f}%'.format(sum(score) / len(score) * 100))
return sum(score) / len(score) * 100

上記で定義した関数を隠れ層を変化させながら実行し、その結果を折れ線グラフに表示します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 隠れ層と正解率の関係
# 引数に隠れ層数追加 0.1 5 隠れ層 100~700
# 隠れ層をX軸に、正解率をY軸にした折れ線グラフを作成
%matplotlib inline
import matplotlib.pyplot as plt

x_data = []
y_data = []
# 0.1から0.9までの学習率(で、正解率を算出する。
for i in range(100, 701, 100):
x_data.append(i)
y_data.append(train_test(0.1, 5, i))

# データをグラフに設定
plt.xlabel("hidden layer") # 学習回数
plt.ylabel("accuracy_rate(%)") # 正解率

plt.plot(x_data, y_data, marker='o')
plt.show()

X軸に隠れ層の数、Y軸に正解率が表示されます。
結果
100層から200層で1%弱の情報がありますがそれ以降はあまり変化がありません。
これまでに、学習率・学習回数・隠れ層の数と正解率の関係を見てきましたがもともとの正解率が高かったこともありほんとに微調整といった感じです。
このあたりのパラメータ調整は、正解率が低い場合には調整する意味合いが大きくなるかと思います。

(Google Colaboratoryで動作確認しています。)