-Deep Learnin- 手書き数字認識

ニューラルネットワークの推論処理の実装例を,手書き数字画像の認識を例として以下に示します.

使用するデータセットはMINSTです.
ここでは,MINIST データセットのダウンロードから画像データのNumpy配列への変換までをサポートしてくれる,書籍ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装で提供されている mnist.py を用います(mnist.py の関数 load_mnist() を用いればMNISTデータを簡単に読み込むことができます).
以下で実行するexe-1.py,exe-2.py ファイルと,mnist.py*の位置関係は以下のようであるとします.

.
├── exe file.           <- ディレクトリ名は何でもOK
│   ├── exe-1.py
│   └── exe-2.py

└── dataset             <- ディレクトリ名は"dataset"でないと動かない
       └── mnist.py

以下のようなexe-1.pyというファイルを準備します.

import sys, os
sys.path.append(os.pardir)  # Setting of import from Parent Directory
from dataset.mnist import load_mnist
 
(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True, normalize=False)
 
print(x_train.shape)    # (60000, 784)
print(t_train.shape)    # (60000,)
print(x_test.shape)     # (10000, 784)

 

print(t_test.shape)     # (10000,)
 

exe-1.pyでは,最初に親ディレクトリのファイル(mnist.py)をインポートする設定と,dataset.mnist.py の関数 load_mnist() を用いてMINISTデータを読み込む設定を行なっています.
load_mnist() 関数は,
    (訓練画像,訓練ラベル),(テスト画像,テストラベル)
という形式で,読み込んだMNISTデータを返します.また,上記の例では引数を2つにしていますが,引数として
    load_mnist(flatten=True, normalize=False, one_hot_label=False)
のように,3つの引数を設定することが可能です.
引数normalizeは,入力画像を0.0〜1.0の値に正規化するかどうかを設定します(Falseに設定すると,入力画像のピクセルは元の 0 - 255 のままです).
引数flattenは,入力画像を平らにする(1次元配列にする)かどうかを設定します(Falseに設定すると,入力画像は 1 x 28 x 28の3次元配列として,Trueにすると784個の要素からなる1次元配列として格納されます).
引数one_hot_labelは,ラベルをone-hot表現として格納するかどうかの設定を行います(Falseに設定した時は,単純に正解となるラベルが格納されますが,Trueの時は,ラベルはone-hot表現として格納されます).

これを実行すると以下のようになります.

Downloading train-images-idx3-ubyte.gz ... 
Done
Downloading train-labels-idx1-ubyte.gz ... 
Done
Downloading t10k-images-idx3-ubyte.gz ... 
Done
Downloading t10k-labels-idx1-ubyte.gz ... 
Done
Converting train-images-idx3-ubyte.gz to NumPy Array ...
Done
Converting train-labels-idx1-ubyte.gz to NumPy Array ...
Done
Converting t10k-images-idx3-ubyte.gz to NumPy Array ...
Done
Converting t10k-labels-idx1-ubyte.gz to NumPy Array ...
Done
Creating pickle file ...
Done!
(60000, 784)
(60000,)
(10000, 784)
(10000,)
 
続いて,MNIST画像を表示させてみます.画像の表示にはPIL (Python Image Library) モジュールを使用します.以下のコード(exe-1.py)を実行すると以下のようになり,画像が表示されます.

 

5
(784,)
(28, 28)

f:id:HidehikoMURAO:20190107105552p:plain


上記のコードでは,flatten=Trueとして読み込んだ画像は NumPy配列として1次元(1列)で格納されていることに注意が必要です.なので,画像の表示を行う際には,元の形状である 28 x 28 サイズに reshape(再変形)する必要があります.
NumPy配列の形状の変形は reshape() メソッドによって行い,必要な形状を引数で指定します.


* mnist.py

# coding: utf-8
try:
    import urllib.request
except ImportError:
    raise ImportError('You should use Python 3.x')
import os.path
import gzip
import pickle
import os
import numpy as np
 
 
key_file = {
    'train_img':'train-images-idx3-ubyte.gz',
    'train_label':'train-labels-idx1-ubyte.gz',
    'test_img':'t10k-images-idx3-ubyte.gz',
    'test_label':'t10k-labels-idx1-ubyte.gz'
}
 
dataset_dir = os.path.dirname(os.path.abspath(__file__))
save_file = dataset_dir + "/mnist.pkl"
 
train_num = 60000
test_num = 10000
img_dim = (1, 28, 28)
img_size = 784
 
 
def _download(file_name):
    file_path = dataset_dir + "/" + file_name
 
    if os.path.exists(file_path):
        return
 
    print("Downloading " + file_name + " ... ")
    urllib.request.urlretrieve(url_base + file_name, file_path)
    print("Done")
 
def download_mnist():
    for v in key_file.values():
       _download(v)
 
def _load_label(file_name):
    file_path = dataset_dir + "/" + file_name
 
    print("Converting " + file_name + " to NumPy Array ...")
    with gzip.open(file_path, 'rb') as f:
            labels = np.frombuffer(f.read(), np.uint8, offset=8)
    print("Done")
 
    return labels
 
def _load_img(file_name):
    file_path = dataset_dir + "/" + file_name
 
    print("Converting " + file_name + " to NumPy Array ...")
    with gzip.open(file_path, 'rb') as f:
            data = np.frombuffer(f.read(), np.uint8, offset=16)
    data = data.reshape(-1, img_size)
    print("Done")
 
    return data
 
def _convert_numpy():
    dataset = {}
    dataset['train_img'] =  _load_img(key_file['train_img'])
    dataset['train_label'] = _load_label(key_file['train_label'])
    dataset['test_img'] = _load_img(key_file['test_img'])
    dataset['test_label'] = _load_label(key_file['test_label'])
 
    return dataset
 
def init_mnist():
    download_mnist()
    dataset = _convert_numpy()
    print("Creating pickle file ...")
    with open(save_file, 'wb') as f:
        pickle.dump(dataset, f, -1)
    print("Done!")
 
def _change_one_hot_label(X):
    T = np.zeros*1
    for idx, row in enumerate(T):
        row[X[idx]] = 1
 
    return T
 
 
def load_mnist(normalize=True, flatten=True, one_hot_label=False):
    """MNISTデータセットの読み込み
 
    Parameters
    ----------
    normalize : 画像のピクセル値を0.0~1.0に正規化する
    one_hot_label :
        one_hot_labelがTrueの場合、ラベルはone-hot配列として返す
        one-hot配列とは、たとえば[0,0,1,0,0,0,0,0,0,0]のような配列
    flatten : 画像を一次元配列に平にするかどうか
 
    Returns
    -------
    (訓練画像, 訓練ラベル), (テスト画像, テストラベル)
    """
    if not os.path.exists(save_file):
        init_mnist()
 
    with open(save_file, 'rb') as f:
        dataset = pickle.load(f)
 
    if normalize:
        for key in ('train_img', 'test_img'):
            dataset[key] = dataset[key].astype(np.float32)
            dataset[key] /= 255.0
 
    if one_hot_label:
        dataset['train_label'] = _change_one_hot_label(dataset['train_label'])
        dataset['test_label'] = _change_one_hot_label(dataset['test_label'])
 
    if not flatten:
         for key in ('train_img', 'test_img'):
            dataset[key] = dataset[key].reshape(-1, 1, 28, 28)
 
    return (dataset['train_img'], dataset['train_label']), (dataset['test_img'], dataset['test_label'])
 
 
if __name__ == '__main__':

 

    init_mnist()
 

*1:X.size, 10