Djangoroidの奮闘記

python,django,angularJS1~三十路過ぎたプログラマーの奮闘記

Python DeepLearningに再挑戦 5 手書き数字認識

概要

Python DeepLearningに再挑戦 5 手書き数字認識

参考書籍

MNIST データセット

学習済みのパラメータを使って、推論処理だけを実装する。 mnistのデータを取得する。

import sys, os
sys.path.append(os.pardir)
from dataset.mnist import load_mnist

# (訓練画像, 訓練ラベル), (テスト画像、テストラベル)という形式で、mnistデータを読み込む
(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True, normalize=False)

# normalize : 入力画像を0.0~1.0の値に正規化するかどうかの設定。Falseの場合は、ピクセルは元の0~255のまま。
# flatten : 入力画像を平らにする(1次元配列にする)かどうかを設定する。Falseに設定すると、1x28x28の3次元配列、Trueにすると784個の要素からなる1次元配列として格納される。
# one_hot_label : [0,0,1,0,0,0...] などのように正解となるラベルだけが1、それ以外が0の配列として表記される。

データの確認がてら、MNIST画像を表示させてみる。

from PIL import Image
import numpy as np

def img_show(img):
    pil_img = Image.fromarray(np.uint8(img)) # 配列からimageを生成する?復元する?
    pil_img.show()    

(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True, normalize=False)

img = x_train[0] # x_trainの0番目のデータを取り出す。(画像情報)
label = t_train[0] # t_trainの0番目のデータを取り出す(ラベル情報)
print(label) # 5が表示されるはず。
img = img.reshape(28,28) # イメージは1次元配列になっているので、元の画像サイズに変形する。
img_show(img) # 手書きの5の文字が表示されるはず。

無事データセットが読み込まれていることが確認できた。

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

入力層を784個、出力層を10個のニューロンで構成するらしい。 784というのは、画像サイズの28x28=784かららしい。なるほど、そいうことか!! 1つ1つのピクセル?から確率を割り振って、推論していくのか〜!

イメージ図は以下のような感じかなぁ。

f:id:riikku:20161220161211j:plain

入力層784, 出力層10, 第1層 50, 第2層 100 のニューロン

# はじめに3つの関数を定義する。

# データセットをゲットする。
def get_data():
    (x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, flatten=True, one_hot_label=False)
    return x_test, t_test

# 重みのパラメータをsample_weight.pkl(重みの学習済みデータ)から読み込む
def init_network():
    with open("sample_weight.pkl", 'rb') as f:
        network = pickle.load(f)
        
        return network

# シグモイド関数と出力層にsoftmaxを使った推論処理
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

学習済みデータがあるとかなりシンプルに定義できるな(^^) この関数を使って、認識精度の評価を行うコードを記載する。

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

import pickle
x, t = get_data() # x_testテスト画像, t_testテストラベルを読み込む
network=init_network() # sample_weight.pkl を読み込んで、networkに代入する。

accuracy_cnt = 0 # accuracy_cntを初期化する。数字があっていたら、+1にする。

for i in range(len(x)): # テスト画像のすべてのデータを1枚1枚取り出して処理する。
    y = predict(network, x[i]) # predict
    p = np.argmax(y) # argmaxで、一番大きい数値のラベルを取り出す。
    if p == t[i]: # 正解のラベル名と、pがあっているかを確認する。
        accuracy_cnt += 1

print("Accuracy:" + str(float(accuracy_cnt) /len(x))) #確率を計算する。
>>> Accuracy:0.9352

なるほど〜〜!!これは学習済みデータが圧倒的に重要なことがわかった。 画像の場合は、ピクセル値255(最大値)で徐算・割り算して、データの値を0〜1の間に収まるように変換する。 決まった範囲に変換する処理を、正規化する。 正規化も含めて、色々決まった変換をすることを、pre-processing前処理という。

今回は、画像の場合は、255で割るだけの正規化だったけど、色々テクニックがあるようで、その辺りもゆくゆくは調べてみたい。

いや〜〜、機械学習すごい!