このシリーズではE資格対策として、書籍「ゼロから作るDeep Learning」を参考に学習に役立つ情報をまとめています。

<参考書籍>

MNISTデータセットについて

MNISTは機械学習やディープラーニングの分野で広く使われている初歩的なデータセットです。MNISTデータセットには、0から9までの手書き数字が含まれており、それぞれの画像は28×28ピクセルのグレースケール画像です。MNISTは、画像認識やクラス分類のモデルを評価するためのベンチマークデータセットとして使われます。

MNISTデータセットから画像をロードし、正解ラベルとともに表示してみましょう。

import numpy as np
from PIL import Image
from keras.datasets import mnist

# 画像を表示する関数の定義
def img_show(img):
    pil_img = Image.fromarray(np.uint8(img))
    pil_img.show()

# MNISTデータの読み込み
(x_train, t_train), (x_test, t_test) = mnist.load_data()

# 画像データと正解ラベルの取得
img = x_train[0]  # 1番目の画像データ
label = t_train[0]  # 1番目の正解ラベル
print(label)  # 正解ラベルの表示 (例: 5)

print(img.shape)  # 画像データの形状の表示 (例: (784,))
img = img.reshape(28, 28)  # 画像データの形状を元の画像サイズに変形 (例: (28, 28))
print(img.shape)  # 変形後の画像データの形状の表示 (例: (28, 28))

img_show(img)  # 画像の表示

実行結果:

5
(28, 28)
(28, 28)

1番目の画像の数字は5であることがわかりました。
なお、以下の数字の0の部分を他の数字に変えることで、別な画像の結果も確かめることができます。

img = x_train[0]  # 1番目の画像データ 
label = t_train[0]  # 1番目の正解ラベル

img = x_train[1]  # 2番目の画像データ 
label = t_train[1]  # 2番目の正解ラベル

ニューラルネットワークの推論処理

MNISTデータセット(手書き数字の画像データ)を使用して、学習済みのニューラルネットワークを使って手書き数字の認識(分類)を行います。まず、必要なライブラリとデータセットをインポートし、シグモイド関数とソフトマックス関数を定義しています。次に、テストデータを取得するための関数と学習済みネットワークを読み込むための関数を定義しています。その後、入力データに対する予測値を計算する関数を定義し、データの取得とネットワークの初期化を行います。学習済みのネットワークを使ってテストデータに対する予測を行い、予測の正解数をカウントします。最後に、テストデータ全体の正解率を計算して表示します。これらによって、手書き数字認識の性能を評価することができます。

学習済み重みファイルの取得

今回はこちらのものを使用します。

!wget https://raw.githubusercontent.com/oreilly-japan/deep-learning-from-scratch/master/ch03/sample_weight.pkl

実装

以下の通り実装します。
入力層は、MNISTデータセットの各画像を表現する784個のニューロン(28×28ピクセルの画像データをフラット化したもの)があり、出力層には10個のニューロンがあり、0から9までの10種類の手書き数字を表現しています。出力層の活性化関数としてソフトマックス関数が使用されており、各ニューロンの出力はその数字が与えられた画像に対応する確率になります。

import numpy as np
import pickle
from keras.datasets import mnist

# シグモイド関数の定義
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

# ソフトマックス関数の定義
def softmax(a):
    c = np.max(a)
    exp_a = np.exp(a - c)
    sum_exp_a = np.sum(exp_a)
    y = exp_a / sum_exp_a
    return y

# MNISTデータセットのテストデータを取得する関数
def get_data():
    (x_train, t_train), (x_test, t_test) = mnist.load_data()
    x_test = x_test.reshape(-1, 784)  # データを平らにする
    x_test = x_test.astype('float32') / 255  # 正規化
    return x_test, t_test

# 学習済みネットワークの読み込み
def load_trained_network(file_path):
    with open(file_path, 'rb') as f:
        network = pickle.load(f)
    return network

# 入力データxに対する予測値を計算する関数
def predict(network, x):
    W1, W2, W3 = network['W1'], network['W2'], network['W3']
    b1, b2, b3 = network['b1'], network['b2'], network['b3']

    a1 = np.dot(x, W1) + b1
    z1 = sigmoid(a1)
    a2 = np.dot(z1, W2) + b2
    z2 = sigmoid(a2)
    a3 = np.dot(z2, W3) + b3
    y = softmax(a3)

    return y

# データの取得とネットワークの初期化
x, t = get_data()
network = load_trained_network('sample_weight.pkl')

# 予測の正解数をカウント
accuracy_cnt = 0
for i in range(len(x)):
    y = predict(network, x[i])
    p = np.argmax(y)  # 最も確率の高い要素のインデックスを取得
    if p == t[i]:
        accuracy_cnt += 1

# 予測の正解数をカウントし、テストデータ全体の正解率を計算して表示
print("Accuracy:" + str(float(accuracy_cnt) / len(x)))

実行結果:

Accuracy:0.9352

バッチ処理

MNISTデータセットに対するバッチ処理の推論を実行する3層のニューラルネットワークを実装します。ニューラルネットワークは、重み(w1、w2、w3)とバイアス(b1、b2、b3)を持ち、各層で線形変換と非線形活性化関数(シグモイド関数)が適用されます。最後の層では、ソフトマックス関数が適用されて確率分布を得ます。このコードにおいて、多次元配列に対応する次元の要素数が一致していることが重要です。

MNISTデータセットの画像は、28×28ピクセルのグレースケール画像で、入力層は784次元(28×28)のベクトルに変換されます。バッチ処理の推論では、一度に複数の画像を処理するため、入力データxは、形状が(batch_size, 784)の2次元配列となります。ここで、batch_sizeは一度に処理する画像の数です。

多次元配列に対応する次元の要素数が一致していることの重要性は以下の通りです。

  1. 重みと入力データの内積(np.dot(x, w1))の計算: xの形状は(batch_size, 784)であり、w1の形状は(784, hidden_units_1)です。ここで、hidden_units_1は第1層の隠れユニットの数です。xの列数(784)とw1の行数(784)が一致しているため、内積計算が可能です。
  2. 隠れ層での内積計算: z1の形状は(batch_size, hidden_units_1)で、w2の形状は(hidden_units_1, hidden_units_2)です。ここで、hidden_units_2は第2層の隠れユニットの数です。z1の列数とw2の行数が一致しているため、内積計算が可能です。同様に、z2とw3の内積計算も可能です。
  3. バイアスの加算: a1 = np.dot(x, w1) + b1のように、バイアスが各層で加算されます。バイアス項b1の形状は(1, hidden_units_1)であり、a1の形状は(batch_size, hidden_units_1)です。バイアスの形状がブロードキャスト可能であるため、加算が正常に行われます。同様に、a2とb2、およびa3とb3の加算が正常に行われます。バイアスの加算では、各層のバイアス項(b2、b3)の形状が(1, hidden_units_2)および(1, output_units)であり、a2およびa3の形状が(batch_size, hidden_units_2)および(batch_size, output_units)です。ここで、output_unitsは出力層のユニット数です。バイアスの形状がブロードキャスト可能であるため、加算が正常に行われます。

MNISTデータセットのバッチ処理の推論において、多次元配列に対応する次元の要素数が一致していることが重要です。次元の要素数が一致している場合、各層で適切な行列積や要素ごとの加算が可能になり、ニューラルネットワークが正しく機能します。一致していない場合、エラーが発生し、ニューラルネットワークが期待通りに機能しなくなります。

import numpy as np
from keras.datasets import mnist


# シグモイド関数の定義
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

# ソフトマックス関数の定義
def softmax(a):
    c = np.max(a)
    exp_a = np.exp(a - c)
    sum_exp_a = np.sum(exp_a)
    y = exp_a / sum_exp_a
    return y

# MNISTデータセットのテストデータを取得する関数
def get_data():
    (x_train, t_train), (x_test, t_test) = mnist.load_data()
    x_test = x_test.reshape(-1, 784)  # データを平らにする
    x_test = x_test.astype('float32') / 255  # 正規化
    return x_test, t_test

# 学習済みネットワークの読み込み
def load_trained_network(file_path):
    with open(file_path, 'rb') as f:
        network = pickle.load(f)
    return network

def predict(network, x):
    w1, w2, w3 = network['W1'], network['W2'], network['W3']
    b1, b2, b3 = network['b1'], network['b2'], network['b3']

    a1 = np.dot(x, w1) + b1
    z1 = sigmoid(a1)
    a2 = np.dot(z1, w2) + b2
    z2 = sigmoid(a2)
    a3 = np.dot(z2, w3) + b3
    y = softmax(a3)

    return y


x, t = get_data()
network = load_trained_network('sample_weight.pkl')

batch_size = 100 # バッチの数
accuracy_cnt = 0

for i in range(0, len(x), batch_size):
    x_batch = x[i:i+batch_size]
    y_batch = predict(network, x_batch)
    p = np.argmax(y_batch, axis=1)
    accuracy_cnt += np.sum(p == t[i:i+batch_size])

print("Accuracy:" + str(float(accuracy_cnt) / len(x)))

実行結果:

Accuracy:0.9352

まとめ

まとめ

最後までご覧いただきありがとうございました。