畳み込みニューラルネットワークで画像分類

畳み込みニューラルネットワークで写真の画像分類を行います。
畳み込みニューラルネットワークを使って効率よくデータの特徴を抽出することができます。

データセットはCIFAR-10を使用します。10個に分類された画像と正解ラベルを集めたデータセットで、訓練データ50000件、テストデータ10000件が含まれています。

まずGoogle Colaboratoryのランタイムのハードウェアアクセラレータを「TPU」に設定します。
ハードウェアアクセラレータの設定

次にtensorflowのバージョンを1.13.1にします。

1
2
!pip uninstall tensorflow -y
!pip install tensorflow==1.13.1

インストール後にランタイムをリスタートする必要があります。


畳み込みニューラルネットワークで必要なパッケージをインポートします。

1
2
3
4
5
6
7
8
9
# パッケージのインポート
from tensorflow.keras.datasets import cifar10
from tensorflow.keras.layers import Activation, Dense, Dropout, Conv2D, Flatten, MaxPool2D
from tensorflow.keras.models import Sequential, load_model
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.utils import to_categorical
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

データセットの準備を行います。

データセットの配列は下記のようになります。

配列 説明
train_images 訓練画像の配列
train_labels 訓練ラベルの配列
test_images テスト画像の配列
test_labels テストラベルの配列
1
2
# データセットの準備
(train_images, train_labels), (test_images, test_labels) = cifar10.load_data()

データセットのシェイプを確認します、

1
2
3
4
5
# データセットのシェイプの確認
print(train_images.shape)
print(train_labels.shape)
print(test_images.shape)
print(test_labels.shape)

実行結果1
訓練データと訓練ラベルは50000件、テスト画像とテストラベルは10000件です。
画像サイズは32×32×3です。RGB画像のため画像サイズ(32×32)×3となります。


先頭10件の訓練画像を確認します。

1
2
3
4
5
# データセットの画像の確認
for i in range(10):
plt.subplot(2, 5, i+1)
plt.imshow(train_images[i])
plt.show()

実行結果2
先頭10件の訓練ラベルの確認を行います。

1
2
# データセットのラベルの確認
print(train_labels[0:10])

実行結果3

ラベルの意味は下記の通りです。

ID 説明
0 airplane(飛行機)
1 automobile(自動車)
2 bird(鳥)
3 cat(猫)
4 deer(鹿)
5 dog(犬)
6 frog(カエル)
7 horse(馬)
8 ship(船)
9 truck(トラック)

データセットの前処理と確認を行います。 訓練画像とテスト画像の正規化を行います。画像のRGBは「0~255」なので255で割って「0.0~1.0」に変換します。
1
2
3
4
5
6
7
# データセットの画像の前処理
train_images = train_images.astype('float32')/255.0
test_images = test_images.astype('float32')/255.0

# データセットの画像の前処理後のシェイプの確認
print(train_images.shape)
print(test_images.shape)

実行結果4
訓練ラベルとテストラベルをone-hot表現に変換します。

1
2
3
4
5
6
7
# データセットのラベルの前処理
train_labels = to_categorical(train_labels, 10)
test_labels = to_categorical(test_labels, 10)

# データセットのラベルの前処理後のシェイプの確認
print(train_labels.shape)
print(test_labels.shape)

実行結果5
畳み込みニューラルネットワークのモデルを作成します。
モデルのネットワーク構造は下記の通りです。

  1. 特徴の抽出を行う。(畳み込みブロック)
    1. (1回目)畳み込み層→畳み込み層→プーリング層→Dropout
    2. (2回目)畳み込み層→畳み込み層→プーリング層→Dropout
  2. 1次元に変換する。(Flatten)
  3. 分類を行う。
    1. 全結合層
    2. Dropout
    3. 全結合層

利用するクラスは次の通りです。

クラス 説明
Conv2D 畳み込み層。引数はカーネル数、カーネルサイズ、活性化関数、パティング。
MaxPool2D プーリング層(Maxプーリング)。引数はプーリング適用領域。
Dense 全結合層。引数はユニット数と活性化関数。
Dropout ドロップアウト。引数は無効にする割合。
Flatten 層の入出力を1次元に変換。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#モデルの作成
model = Sequential()

# Conv→Conv→Pool→Dropout
model.add(Conv2D(32, (3, 3), activation='relu', padding='same', input_shape=(32, 32, 3)))
model.add(Conv2D(32, (3, 3), activation='relu', padding='same'))
model.add(MaxPool2D(pool_size=(2, 2)))
model.add(Dropout(0.25))

# Conv→Conv→Pool→Dropout
model.add(Conv2D(64, (3, 3), activation='relu', padding='same'))
model.add(Conv2D(64, (3, 3), activation='relu', padding='same'))
model.add(MaxPool2D(pool_size=(2, 2)))
model.add(Dropout(0.25))

# Flatten→Dense→Dropout→Dense
model.add(Flatten())
model.add(Dense(512, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(10, activation='softmax'))

TPUモデルへの変換を行います。

1
2
3
4
5
6
7
8
9
# TPUモデルへの変換
import tensorflow as tf
import os
tpu_model = tf.contrib.tpu.keras_to_tpu_model(
model,
strategy=tf.contrib.tpu.TPUDistributionStrategy(
tf.contrib.cluster_resolver.TPUClusterResolver(tpu='grpc://' + os.environ['COLAB_TPU_ADDR'])
)
)

畳み込みニューラルネットワークモデルのコンパイルを行います。

1
2
# コンパイル
tpu_model.compile(loss='categorical_crossentropy', optimizer=Adam(lr=0.001), metrics=['acc'])

訓練画像と訓練ラベルの配列をモデルに渡して学習を開始します。

1
2
3
# 学習
history = tpu_model.fit(train_images, train_labels, batch_size=128,
epochs=20, validation_split=0.1)

実行結果6(途中略)
畳み込みニューラルネットワークの学習結果をグラフ表示します。

1
2
3
4
5
6
7
# グラフの表示
plt.plot(history.history['acc'], label='acc')
plt.plot(history.history['val_acc'], label='val_acc')
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(loc='best')
plt.show()

実行結果7
学習が進むと正解率が上がっていくことが分かります。


テスト画像とテストラベルの配列をモデルに渡して評価を行い正解率を取得します。

1
2
3
# 評価
test_loss, test_acc = tpu_model.evaluate(test_images, test_labels)
print('loss: {:.3f}\nacc: {:.3f}'.format(test_loss, test_acc ))

実行結果8
正解率は73.5%となりました。


最後に、先頭10件のテストデータの推論を行い予測結果を取得します。

1
2
3
4
5
6
7
8
9
10
11
12
# 推論する画像の表示
for i in range(10):
plt.subplot(2, 5, i+1)
plt.imshow(test_images[i])
plt.show()

# 推論したラベルの表示
test_predictions = tpu_model.predict(test_images[0:16]) # 学習データ数を16個に増やす
test_predictions = np.argmax(test_predictions, axis=1)[0:10] # 結果数を10個に減らす
labels = ['airplane', 'automobile', 'bird', 'cat', 'deer',
'dog', 'frog', 'horse', 'ship', 'truck']
print([labels[n] for n in test_predictions])

実行結果9

画像と推測結果を比較すると正解率が90%であることが分かります。

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

参考

AlphaZero 深層学習・強化学習・探索 人工知能プログラミング実践入門 サポートページ

深層学習 ニューラルネットワークで回帰

ニューラルネットワークで数値データの予測を行う推論モデルを作成します。
住宅の情報から価格を予測します。

まず回帰に必要なパッケージをインポートします。

1
2
3
4
5
6
7
8
9
10
# パッケージのインポート
from tensorflow.keras.datasets import boston_housing
from tensorflow.keras.layers import Activation, Dense, Dropout
from tensorflow.keras.models import Sequential
from tensorflow.keras.callbacks import EarlyStopping
from tensorflow.keras.optimizers import Adam
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

データセットの準備を行います。

データセットの配列は下記のようになります。

配列 説明
train_data 訓練データの配列
train_labels 訓練ラベルの配列
test_data テストデータの配列
test_labels テストラベルの配列

データ型はNumpyのndarrayになります。この配列型を使うと高速な配列演算が可能になります。

1
2
# データセットの準備
(train_data, train_labels), (test_data, test_labels) = boston_housing.load_data()

実行結果1


データセットのシェイプを確認します。

1
2
3
4
5
# データセットのシェイプの確認
print(train_data.shape)
print(train_labels.shape)
print(test_data.shape)
print(test_labels.shape)

実行結果2


先頭10件の訓練データの確認を行います。
1
2
3
4
# データセットのデータの確認
column_names = ['CRIM', 'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE', 'DIS', 'RAD', 'TAX', 'PTRATIO', 'B', 'LSTAT']
df = pd.DataFrame(train_data, columns=column_names)
df.head()

実行結果3


先頭10件の訓練ラベルの確認を行います。
1
2
# データセットのラベルの確認
print(train_labels[0:10])

実行結果4


訓練データと訓練ラベルのシャッフルを行います。 これは学習時に似たデータを連続して学習すると隔たりが生じてしまうのを防ぐためです。

また正規化を行い、同じ単位で比較しやすくします。

1
2
3
4
5
6
7
8
9
10
# データセットのシャッフルの前処理
order = np.argsort(np.random.random(train_labels.shape))
train_data = train_data[order]
train_labels = train_labels[order]

# データセットの正規化の前処理
mean = train_data.mean(axis=0)
std = train_data.std(axis=0)
train_data = (train_data - mean) / std
test_data = (test_data - mean) / std

データセットのデータが平均0、分散1になっていることを確認します。
1
2
3
4
# データセットの前処理後のデータの確認
column_names = ['CRIM', 'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE', 'DIS', 'RAD', 'TAX', 'PTRATIO', 'B', 'LSTAT']
df = pd.DataFrame(train_data, columns=column_names)
df.head()

実行結果5


ニューラルネットワークのモデルを作成します。
1
2
3
4
5
# モデルの作成
model = Sequential()
model.add(Dense(64, activation='relu', input_shape=(13,)))
model.add(Dense(64, activation='relu'))
model.add(Dense(1))

ニューラルネットワークのモデルをコンパイルします。
1
2
# コンパイル
model.compile(loss='mse', optimizer=Adam(lr=0.001), metrics=['mae'])

EarlyStoppingは任意のエポック数を実行したときに改善がないと学習を停止します。 今回は20試行の間に誤差の改善が見られない場合は学習を停止します。
1
2
# EarlyStoppingの準備
early_stop = EarlyStopping(monitor='val_loss', patience=30)

学習を実行します。callbacksにEarlyStoppingを追加します。
1
2
3
# 学習
history = model.fit(train_data, train_labels, epochs=500,
validation_split=0.2, callbacks=[early_stop])

実行結果6

学習時の結果(fit関数の戻り値)には次の情報が含まれています。

情報 説明
loss 訓練データの誤差。0に近いほどよい。
mean_absolute_error 訓練データの平均絶対誤差。0に近いほどよい。
val_loss 検証データの誤差。0に近いほどよい。
val_mean_absolute_error 検証データの平均絶対誤差。0に近いほどよい。

mean_absolute_errorとval_mean_absolute_errorをグラフ表示します。
1
2
3
4
5
6
7
8
# グラフの表示
plt.plot(history.history['mean_absolute_error'], label='train mae')
plt.plot(history.history['val_mean_absolute_error'], label='val mae')
plt.xlabel('epoch')
plt.ylabel('mae [1000$]')
plt.legend(loc='best')
plt.ylim([0,5])
plt.show()

実行結果7


テスト画像とテストラベルの配列をモデルに渡して評価を行います。
1
2
3
# 評価
test_loss, test_mae = model.evaluate(test_data, test_labels)
print('loss:{:.3f}\nmae: {:.3f}'.format(test_loss, test_mae))

実行結果8
平均絶対誤差が2.6となっています。


先頭10件のテストデータの推論を行い、予測結果を表示します。

1
2
3
4
5
6
# 推論する値段の表示
print(np.round(test_labels[0:10]))

# 推論した値段の表示
test_predictions = model.predict(test_data[0:10]).flatten()
print(np.round(test_predictions))

実行結果9
実際の価格に近い価格が推論されています。

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

参考

AlphaZero 深層学習・強化学習・探索 人工知能プログラミング実践入門 サポートページ

深層学習 ニューラルネットワークで分類

画像分類のためのニューラルネットワークを作成し、手書き数字の画像から数字を推論するモデルを作ります。

まず必要なパッケージをインポートします。

1
2
3
4
5
6
7
8
9
# パッケージのインポート
from tensorflow.keras.datasets import mnist
from tensorflow.keras.layers import Activation, Dense, Dropout
from tensorflow.keras.models import Sequential
from tensorflow.keras.optimizers import SGD
from tensorflow.keras.utils import to_categorical
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

データセットの準備と確認を行います。

データセットの配列は下記のようになります。

配列 説明
train_images 訓練画像の配列
train_labels 訓練ラベルの配列
test_images テスト画像の配列
test_labels テストラベルの配列

データ型はNumpyのndarrayになります。この配列型を使うと高速な配列演算が可能になります。

1
2
# データセットの準備
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

実行結果1


データセットのシェイプを確認します。

1
2
3
4
5
# データセットのシェイプの確認
print(train_images.shape)
print(train_labels.shape)
print(test_images.shape)
print(test_labels.shape)

実行結果2


先頭10件の訓練画像を確認します。
1
2
3
4
5
# データセットの画像の確認
for i in range(10):
plt.subplot(1, 10, i+1)
plt.imshow(train_images[i], 'gray')
plt.show()

実行結果3


データセットのラベルを確認します。 実行結果3とラベルが合致していることが分かります。
1
2
# データセットのラベルの確認
print(train_labels[0:10])

実行結果4


データセットをニューラルネットワークに適した形式に変換します。 2次元配列(28×28)を1次元配列(786)に変換します。
1
2
3
4
5
6
7
# データセットの画像の前処理
train_images = train_images.reshape((train_images.shape[0], 784))
test_images = test_images.reshape((test_images.shape[0], 784))

# データセットの画像の前処理後のシェイプの確認
print(train_images.shape)
print(test_images.shape)

実行結果5


訓練ラベルとテストラベルをone-hot表現に変換します。 one-hot表現とはある要素のみが1でそのほかの要素が0であるような表現です。
1
2
3
4
5
6
7
# データセットのラベルの前処理
train_labels = to_categorical(train_labels)
test_labels = to_categorical(test_labels)

# データセットのラベルの前処理後のシェイプの確認
print(train_labels.shape)
print(test_labels.shape)

実行結果6


ニューラルネットワークのモデルを作成します。
  • モデルのネットワーク構造は全結合層を3つ重ねたものです。
  • 入力層が784(28×28)で出力層が10になります。
  • 隠れ層は自由に決められますが今回は128としました。
  • 過学習防止のためDropoutを設定します。
  • 活性化関数はシグモイド関数とソフトマックス関数を使用します。
1
2
3
4
5
6
# モデルの作成
model = Sequential()
model.add(Dense(256, activation='sigmoid', input_shape=(784,))) # 入力層
model.add(Dense(128, activation='sigmoid')) # 隠れ層
model.add(Dropout(rate=0.5)) # ドロップアウト
model.add(Dense(10, activation='softmax')) # 出力層

ニューラルネットワークのモデルをコンパイルします。 コンパイル時には下記の3つを設定します。
  • 損失関数
    モデルの予測値と正解データの誤差を算出する関数です。
  • 最適化関数
    損失関数の結果が0に近づくように重みパラメータとバイアスを最適化する関数です。
  • 評価指標
    モデルの性能を測定するための指標です。
1
2
# コンパイル
model.compile(loss='categorical_crossentropy', optimizer=SGD(lr=0.1), metrics=['acc'])

訓練画像と訓練ラベルの配列をモデルに渡して学習を実行します。

1
2
3
# 学習
history = model.fit(train_images, train_labels, batch_size=500,
epochs=5, validation_split=0.2)

実行結果7


学習時の結果(fit関数の戻り値)には次の情報が含まれています。
情報 説明
loss 訓練データの誤差。0に近いほどよい。
acc 訓練データの正解率。1に近いほどよい。
val_loss 検証データの誤差。0に近いほどよい。
val_acc 検証データの正解率。1に近いほどよい。

accとval_accをグラフ化してみます。

1
2
3
4
5
6
7
# グラフの表示
plt.plot(history.history['acc'], label='acc')
plt.plot(history.history['val_acc'], label='val_acc')
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(loc='best')
plt.show()

実行結果8


テスト画像とテストラベルの配列をモデルに渡して評価を行います。
1
2
3
# 評価
test_loss, test_acc = model.evaluate(test_images, test_labels)
print('loss: {:.3f}\nacc: {:.3f}'.format(test_loss, test_acc ))

実行結果9

正解率が91%を超えていることが分かります。
最後に先頭10件のテスト画像の推論を行い、予測結果を取得します、
1
2
3
4
5
6
7
8
9
10
# 推論する画像の表示
for i in range(10):
plt.subplot(1, 10, i+1)
plt.imshow(test_images[i].reshape((28, 28)), 'gray')
plt.show()

# 推論したラベルの表示
test_predictions = model.predict(test_images[0:10])
test_predictions = np.argmax(test_predictions, axis=1)
print(test_predictions)

実行結果10

十分に正確な結果となっています。

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

参考

AlphaZero 深層学習・強化学習・探索 人工知能プログラミング実践入門 サポートページ

ニューラルネットワーク(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で動作確認しています。)

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

学習メソッドtrainを実装します。前回実装した照会メソッドqueryと似ています。
重みをかけて発火させたあとに目標出力との誤差を算出しそれを学習率に応じて重みに反映する・・・これがニューラルネットワークの最重要ポイントかと思われます。

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
78
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))
''' ↓こんな感じの配列ができる
[[ 0.37395332 0.07296579 0.36696637]
[-0.1570748 0.28908756 0.99958053]
[-0.09054778 -0.20084478 0.31981826]]
'''
self.weight_hid_out = numpy.random.normal(0.0, pow(self.out_node, -0.5), (self.out_node, self.hid_node))
''' ↓こんな感じの配列ができる
[[ 0.93304259 0.02641947 0.29506316]
[-0.74275445 0.9010841 -0.47840667]
[ 0.04494529 0.49177323 1.13985481]]
'''

# 活性化関数はシグモイド関数
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

これで自作ニューラルネットワークが完成しました。
このクラスを使ってMNISTデータの判定を行ってみます。

まず、学習データ(100種類)とテストデータ(10種類)をダウンロードします。

1
2
!wget https://raw.githubusercontent.com/makeyourownneuralnetwork/makeyourownneuralnetwork/master/mnist_dataset/mnist_test_10.csv
!wget https://raw.githubusercontent.com/makeyourownneuralnetwork/makeyourownneuralnetwork/master/mnist_dataset/mnist_train_100.csv</pre>

データを読み込んで、どんなデータか表示してみます。

1
2
3
4
5
6
7
8
9
10
import numpy
import matplotlib.pyplot
%matplotlib inline

with open('mnist_train_100.csv', 'r') as f:
data_list = f.readlines()

val = data_list[7].split(',') # データを選ぶ(0-99の間)
img = numpy.asfarray(val[1:]).reshape((28, 28))
matplotlib.pyplot.imshow(img, cmap='Greys', interpolation='None')

結果

データを学習し、テストデータの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
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_100.csv', 'r') as f:
train_data = f.readlines()

# 全学習データを10回学習
epochs = 10 # 学習回数
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)

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

val = test_data[0].split(',') # テストデータを選択(0-9の間)
img = numpy.asfarray(val[1:]).reshape((28, 28))
matplotlib.pyplot.imshow(img, cmap='Greys', interpolation='None')

# 選択したデータをニューラルネットワークで照会
res = n_network.query((numpy.asfarray(val[1:]) / 255.0 * 0.99) + 0.01)
for a,b in enumerate(res):
print('{}の可能性 {:5.2f}%'.format(a,b[0] * 100))

結果

今回は[7]の合致率が97%以上ときちんと認識しているようです。
10種類あるテストデータをすべて試してみましたが、なかなかよい結果がでてました。
ただ人間でもよくわからないデータ(手書き数字)だとはっきり認識するのは難しいようです。(あたり前か・・・)

隠れ層のノード数、学習率、学習回数の範囲をいろいろ試してみて認識率がどうかわるかを試すのもよいかと思います。

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

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

ニューラルネットワークを試してみます。
まずは、基本メソッド、各層の数と学習率をもつニューラルネットワーククラスを定義します。
中身はなにもありませんが、これがニューラルネットワーク・クラスの骨格となります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# ニューラルネットワークを表すクラス
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 # 学習率

# 【学習】
# 学習データから重みを調整する。
def train():
pass

# 【照会】
# 入力に対して出力層からの答えを返す。
def query():
pass

このクラスに少しずつ実装していきます。

初期化のところで重みを生成し、活性化関数にシグモイド関数を設定してます。
また照会メソッドで、入力に対し入力・隠れ層の重みをかけシグモイド関数で発火させます。
さらにその結果に隠れ・出力層の重みをかけシグモイド関数で発火させ、その結果を返しています。

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
import numpy
import scipy.special
# Pythonでニューラルネットワーク(3)
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))
''' ↓こんな感じの配列ができる
[[ 0.37395332 0.07296579 0.36696637]
[-0.1570748 0.28908756 0.99958053]
[-0.09054778 -0.20084478 0.31981826]]
'''
self.weight_hid_out = numpy.random.normal(0.0, pow(self.out_node, -0.5), (self.out_node, self.hid_node))
''' ↓こんな感じの配列ができる
[[ 0.93304259 0.02641947 0.29506316]
[-0.74275445 0.9010841 -0.47840667]
[ 0.04494529 0.49177323 1.13985481]]
'''

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

# 【学習】
# 学習データから重みを調整する。
def train():
pass

# 【照会】
# 入力に対して出力層からの答えを返す。
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

上記で定義したクラスが問題なく動くか、テストするコードを簡単に書いて実行してみます。

1
2
3
4
5
6
7
8
9
10
11
in_node = 3       # 入力層のノード数
hid_node = 3 # 隠れ層のノード数
out_node = 3 # 出力層のノード数

learn_rate = 0.3 # 学習率

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

# まだ学習してないけど照会してみる
n_network.query([1.0, 0.5, -1.5])

【結果】

1
2
3
array([[0.55799198],
[0.51430992],
[0.30202192]])

まだ学習するメソッドを実装していないので意味のない出力ではありますが、エラーなしで動作することを確認できました。
次回は学習メソッドを実装していきます。

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


Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×