CNTK入門05:手書き識別ピクチャを分類するための全接続フィードフォワードモデル


CNTK入門05:手書き識別ピクチャを分類するための全接続フィードフォワードモデル
------- A fully connected feed-forward model for classification of MNIST images.
1.  .ExamplesImageDataSetsMNISTの下に2つのファイルがあり、Python install_を実行します.mnist.py手書き認識画像をダウンロードします.
このうち60000は試験訓練データ、10000枚は試験データとした.
コード:install_mnist.py
from __future__ import print_function
import os
import mnist_utils as ut

if __name__ == "__main__":
	# 
    os.chdir(os.path.abspath(os.path.dirname(__file__)))
    train = ut.load('http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz',
        'http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz', 60000)
    print ('Writing train text file...')
    ut.savetxt(r'./Train-28x28_cntk_text.txt', train)
    print ('Done.')
	
    test = ut.load('http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz',
        'http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz', 10000)
    print ('Writing test text file...')
    ut.savetxt(r'./Test-28x28_cntk_text.txt', test)
    print ('Done.')

上のメイン関数は、mnist_というファイルの関数を呼び出します.utils.py
from __future__ import print_function
try: 
    from urllib.request import urlretrieve 
except ImportError: 
    from urllib import urlretrieve
import sys
import gzip
import shutil
import os
import struct
import numpy as np

def loadData(src, cimg):
    print ('Downloading ' + src)
	#retrieve, ;src  ,./delete.me ; tuple(filename,header)
	#filename  ,header  
    gzfname, h = urlretrieve(src, './delete.me')
    print ('Done.')
    try:
        with gzip.open(gzfname) as gz:
			# (fmt) string, tuple
            n = struct.unpack('I', gz.read(4))
            # Read magic number.
            if n[0] != 0x3080000:
                raise Exception('Invalid file: unexpected magic number.')
            # Read number of entries.
            n = struct.unpack('>I', gz.read(4))[0]
            if n != cimg:
                raise Exception('Invalid file: expected {0} entries.'.format(cimg))
            crow = struct.unpack('>I', gz.read(4))[0]
            ccol = struct.unpack('>I', gz.read(4))[0]
            if crow != 28 or ccol != 28:
                raise Exception('Invalid file: expected 28 rows/cols per image.')
            # Read data.
            res = np.fromstring(gz.read(cimg * crow * ccol), dtype = np.uint8)
    finally:
        os.remove(gzfname)
    return res.reshape((cimg, crow * ccol))

def loadLabels(src, cimg):
    print ('Downloading ' + src)
    gzfname, h = urlretrieve(src, './delete.me')
    print ('Done.')
    try:
        with gzip.open(gzfname) as gz:
            n = struct.unpack('I', gz.read(4))
            # Read magic number.
            if n[0] != 0x1080000:
                raise Exception('Invalid file: unexpected magic number.')
            # Read number of entries.
            n = struct.unpack('>I', gz.read(4))
            if n[0] != cimg:
                raise Exception('Invalid file: expected {0} rows.'.format(cimg))
            # Read labels.
            res = np.fromstring(gz.read(cimg), dtype = np.uint8)
    finally:
        os.remove(gzfname)
    return res.reshape((cimg, 1))# cimg ,1 

# : , , 
def load(dataSrc, labelsSrc, cimg):
    data = loadData(dataSrc, cimg)
    labels = loadLabels(labelsSrc, cimg)
	#
    return np.hstack((data, labels))

def savetxt(filename, ndarray):
    with open(filename, 'w') as f:
        labels = list(map(' '.join, np.eye(10, dtype=np.uint).astype(str)))
        for row in ndarray:
            row_str = row.astype(str)
            label_str = labels[row[-1]]
            feature_str = ' '.join(row_str[:-1])
            f.write('|labels {} |features {}
'.format(label_str, feature_str))

目次\Examples\Image\Classification\MLP\Python\SimpleMNIST.pyは手書きで識別するプログラムです.
 Python SimpleMNIST.pyを実行します.
import argparse
import numpy as np
import sys
import os
from cntk.train import Trainer, minibatch_size_schedule 
from cntk.io import MinibatchSource, CTFDeserializer, StreamDef, StreamDefs, INFINITELY_REPEAT
from cntk.device import cpu, try_set_default_device
from cntk.learners import adadelta, learning_rate_schedule, UnitType
from cntk.ops import input, relu, element_times, constant
from cntk.losses import cross_entropy_with_softmax
from cntk.metrics import classification_error
from cntk.train.training_session import *
from cntk.logging import ProgressPrinter, TensorBoardProgressWriter

abs_path = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.join(abs_path, "..", "..", "..", "..", "common"))
from nn import fully_connected_classifier_net

def check_path(path):
    if not os.path.exists(path):
        readme_file = os.path.normpath(os.path.join(
            os.path.dirname(path), "..", "README.md"))
        raise RuntimeError(
            "File '%s' does not exist. Please follow the instructions at %s to download and prepare it." % (path, readme_file))

def create_reader(path, is_training, input_dim, label_dim):
    return MinibatchSource(CTFDeserializer(path, StreamDefs(
        features  = StreamDef(field='features', shape=input_dim, is_sparse=False),
        labels    = StreamDef(field='labels',   shape=label_dim, is_sparse=False)
    )), randomize=is_training, max_sweeps = INFINITELY_REPEAT if is_training else 1)


# Creates and trains a feedforward classification model for MNIST images

def simple_mnist(tensorboard_logdir=None):
    input_dim = 784
    num_output_classes = 10
    num_hidden_layers = 1
    hidden_layers_dim = 200

    # Input variables denoting the features and label data
    feature = input(input_dim, np.float32)
    label = input(num_output_classes, np.float32)

    # Instantiate the feedforward classification model
    scaled_input = element_times(constant(0.00390625), feature)
    z = fully_connected_classifier_net(
        scaled_input, num_output_classes, hidden_layers_dim, num_hidden_layers, relu)

    ce = cross_entropy_with_softmax(z, label)
    pe = classification_error(z, label)

    data_dir = os.path.join(abs_path, "..", "..", "..", "DataSets", "MNIST")

    path = os.path.normpath(os.path.join(data_dir, "Train-28x28_cntk_text.txt"))
    check_path(path)

    reader_train = create_reader(path, True, input_dim, num_output_classes)

    input_map = {
        feature  : reader_train.streams.features,
        label  : reader_train.streams.labels
    }

    # Training config
    minibatch_size = 64
    num_samples_per_sweep = 60000
    num_sweeps_to_train_with = 10

    # Instantiate progress writers.
    #training_progress_output_freq = 100
    progress_writers = [ProgressPrinter(
        #freq=training_progress_output_freq,
        tag='Training',
        num_epochs=num_sweeps_to_train_with)]

    if tensorboard_logdir is not None:
        progress_writers.append(TensorBoardProgressWriter(freq=10, log_dir=tensorboard_logdir, model=z))

    # Instantiate the trainer object to drive the model training
    trainer = Trainer(z, (ce, pe), adadelta(z.parameters), progress_writers)

    training_session(
        trainer=trainer,
        mb_source = reader_train,
        mb_size = minibatch_size,
        model_inputs_to_streams = input_map,
        max_samples = num_samples_per_sweep * num_sweeps_to_train_with,
        progress_frequency=num_samples_per_sweep
    ).train()
    
    # Load test data
    path = os.path.normpath(os.path.join(data_dir, "Test-28x28_cntk_text.txt"))
    check_path(path)

    reader_test = create_reader(path, False, input_dim, num_output_classes)

    input_map = {
        feature  : reader_test.streams.features,
        label  : reader_test.streams.labels
    }

    # Test data for trained model
    test_minibatch_size = 1024
    num_samples = 10000
    num_minibatches_to_test = num_samples / test_minibatch_size
    test_result = 0.0
    for i in range(0, int(num_minibatches_to_test)):
        mb = reader_test.next_minibatch(test_minibatch_size, input_map=input_map)
        eval_error = trainer.test_minibatch(mb)
        test_result = test_result + eval_error

    # Average of evaluation errors of all test minibatches
    return test_result / num_minibatches_to_test


if __name__=='__main__':
    # Specify the target device to be used for computing, if you do not want to
    # use the best available one, e.g.
    # try_set_default_device(cpu())

    parser = argparse.ArgumentParser()
    parser.add_argument('-tensorboard_logdir', '--tensorboard_logdir',
                        help='Directory where TensorBoard logs should be created', required=False, default=None)
    args = vars(parser.parse_args())

    error = simple_mnist(args['tensorboard_logdir'])
    print("Error: %f" % error)

----------------------------------------------------------------------------------------------------------------------------
付録:
1. os.chdir()#現在の作業ディレクトリを指定したパスに変更
2.np.hstack()の解釈:stack()Join a sequence of arrays along a new axis.hstack() Stack arrays in sequence horizontally (column wise). dstack() Stack arrays in sequence depth wise (along third dimension). concatenate() Join a sequence of arrays along an existing axis. vsplit () Split array into a list of multiple sub-arrays vertically. 2.1 stack():新しい軸で2つの配列を接続します.
>>> arrays = [np.random.randn(3, 4) for _ in range(10)]  
>>> np.stack(arrays, axis=0).shape  
(10, 3, 4)   
>>> np.stack(arrays, axis=1).shape  
(3, 10, 4)  
>>> np.stack(arrays, axis=2).shape  
(3, 4, 10)  
>>> a = np.array([1, 2, 3])  
>>> b = np.array([2, 3, 4])  
>>> np.stack((a, b))  
array([[1, 2, 3],  
       [2, 3, 4]])  
>>> np.stack((a, b), axis=-1)  
array([[1, 2],  
       [2, 3],  
       [3, 4]])  

np.random.randn(3,4)は、標準正規分布に従う3行4列の行列を返します.
2.2 numpy.hstack()関数
関数プロトタイプ:numpy.hstack(tup)
ここでtupはarraysシーケンス、The arrays must have the same shape、except in the dimensioncorresponding to axis(the first、by default).
等価:np.concatenate(tup, axis=1)
>>> a = np.array((1,2,3))  
>>> b = np.array((2,3,4))  
>>> np.hstack((a,b))  
array([1, 2, 3, 2, 3, 4])  
>>> a = np.array([[1],[2],[3]])  
>>> b = np.array([[2],[3],[4]])  
>>> np.hstack((a,b))  
array([[1, 2],  
       [2, 3],  
       [3, 4]])  
2.3 
numpy.vstack()関数
関数プロトタイプ:numpy.vstack(tup)
等価:np.concatenate(tup, axis=0) if tup contains arrays thatare at least 2-dimensional.
プログラムの例:
>>> a = np.array([1, 2, 3])  
>>> b = np.array([2, 3, 4])  
>>> np.vstack((a,b))  
array([[1, 2, 3],  
       [2, 3, 4]])  
>>> a = np.array([[1], [2], [3]])  
>>> b = np.array([[2], [3], [4]])  
>>> np.vstack((a,b))  
array([[1],  
       [2],  
       [3],  
       [2],  
       [3],  
       [4]]) 

2.4   numpy.dstack()関数
関数プロトタイプ:numpy.dstack(tup)
等価:np.concatenate(tup, axis=2)
プログラムの例:
>>> a = np.array((1,2,3))  
>>> b = np.array((2,3,4))  
>>> np.dstack((a,b))  
array([[[1, 2],  
        [2, 3],  
        [3, 4]]])  
  
>>>  
  
>>> a = np.array([[1],[2],[3]])  
>>> b = np.array([[2],[3],[4]])  
>>> np.dstack((a,b))  
array([[[1, 2]],  
       [[2, 3]],  
       [[3, 4]]])  

2.5 numpy.concatenate()関数
関数プロトタイプ:numpy.concatenate((a1, a2, ...), axis=0)
プログラムの例:
>>> a = np.array([[1, 2], [3, 4]])  
>>> b = np.array([[5, 6]])  
>>> np.concatenate((a, b), axis=0)  
array([[1, 2],  
       [3, 4],  
       [5, 6]])  
>>> np.concatenate((a, b.T), axis=1)  
array([[1, 2, 5],  
       [3, 4, 6]])  
  
This function will not preserve masking of MaskedArray inputs.  
>>>  
  
>>> a = np.ma.arange(3)  
>>> a[1] = np.ma.masked  
>>> b = np.arange(2, 5)  
>>> a  
masked_array(data = [0 -- 2],  
             mask = [False  True False],  
       fill_value = 999999)  
>>> b  
array([2, 3, 4])  
>>> np.concatenate([a, b])  
masked_array(data = [0 1 2 2 3 4],  
             mask = False,  
       fill_value = 999999)  
>>> np.ma.concatenate([a, b])  
masked_array(data = [0 -- 2 2 3 4],  
             mask = [False  True False False False False],  
       fill_value = 999999)  

2.6 numpy.vsplit()関数
関数プロトタイプ:numpy.vsplit(ary, indices_or_sections)
プログラムの例:
>>> x = np.arange(16.0).reshape(4, 4)  
>>> x  
array([[  0.,   1.,   2.,   3.],  
       [  4.,   5.,   6.,   7.],  
       [  8.,   9.,  10.,  11.],  
       [ 12.,  13.,  14.,  15.]])  
>>> np.vsplit(x, 2)  
[array([[ 0.,  1.,  2.,  3.],  
       [ 4.,  5.,  6.,  7.]]),  
 array([[  8.,   9.,  10.,  11.],  
       [ 12.,  13.,  14.,  15.]])]  
>>> np.vsplit(x, np.array([3, 6]))  
[array([[  0.,   1.,   2.,   3.],  
       [  4.,   5.,   6.,   7.],  
       [  8.,   9.,  10.,  11.]]),  
 array([[ 12.,  13.,  14.,  15.]]),  
 array([], dtype=float64)]  
  
With a higher dimensional array the split is still along the first axis.  
>>>  
  
>>> x = np.arange(8.0).reshape(2, 2, 2)  
>>> x  
array([[[ 0.,  1.],  
        [ 2.,  3.]],  
       [[ 4.,  5.],  
        [ 6.,  7.]]])  
>>> np.vsplit(x, 2)  
[array([[[ 0.,  1.],  
        [ 2.,  3.]]]),  
 array([[[ 4.,  5.],  
        [ 6.,  7.]]])]  

------------------------------------------------------------------------------------
3.url.urlretrieve(filename[,reporthook[,data]]]))の使用
urllib.urlretrieve(url,filename)はネットワークファイルをダウンロードし、最初の要素はターゲットurlであり、2番目のパラメータは保存されたファイルの絶対パス(ファイル名を含む)であり、戻り値はtuple(filename,header)であり、filenameはローカルに保存されたパスを表し、headerはサーバの応答ヘッダを表す.urlretrieveに1つのパラメータしか指定されていない場合、返される値のfilenameは生成された一時ファイル名であり、関数の実行が完了するとその一時ファイルはパラメータを削除されます.パラメータreporthookはコールバック関数であり、サーバに接続し、対応するデータブロックの転送が完了するとコールバックがトリガーされます.コールバック関数名は任意ですが、パラメータは3つでなければなりません.一般的にreporthook(block_read,block_size,total_size)を直接使用してコールバック関数を定義し、block_sizeは、読み出すたびにブロックのサイズ、block_readは、読み出すたびのデータブロック数、taotal_sizeは合計で読み込まれたデータ量で、単位はbyteです.reporthook関数を使用して、読み取りの進捗状況を表示できます.