7層のネットワークを動かす 1

ゼロから作る Deep Learning
のサンプルソースコードを少し変更して、機能追加をして、7層のネットワークを動かします。
以降、ゼロから作る Deep Learningを本と記載します。

動作を確認するため、サンプルソースコードにprint文を挿入します。
入力をカラー画像に変更します。

環境は、Mac mini (CPU 2.8GHz / メモリ 16GB / SSD / (GPU Intel Iris 1536MB))です。
処理はCPUで行われます。

プログラムで実現することを決める

カラー画像の入力に対応します。
下記の2種類の分類をします。

  • タチコマ、猫
  • トマト、みかん
  • トマト、りんご
  • 犬、猫

プログラムを設計する

サンプルソースコードch07/train_convnet.pyを書き換えて、ネットワークとTrainerに設定する値を全てこのファイルで設定するようにします。
サンプルソースコードcommon/trainer.pyを書き換えて、Trainerにカラー画像読み込み機能を追加します。
サンプルソースコードch07/simple_convnet.pyを書き換えて、学習の結果として精度を得る回数を減らします。

pythonスクリプトを呼びだすシェルスクリプトを作成して、保存したネットワークを読み込むか否かをシェルスクリプトで設定できるようにします。

ニューラルネットワークの構成、設計は本の通りです。7層です。

simple_layers

準備する

画像を集めて、python3をインストールして、ctagsを使います。

画像を集める

自分で撮った写真と、画像検索の結果から学習用のデータとして適切なものを手作業で選別したもの、を使います。

ディレクトリ構成は下記とします。取得した画像をimageの下のtrainとtestに分けて入れます。
trainの下には、それぞれ150枚ずつ入れます。testの下にはそれぞれ25枚ずつ入れます。
ネットワークの入力には、それぞれ、trainから100枚ずつ、testから10枚ずつ、を使います。ここでは2分類なので合計して、200枚、20枚、を使うことになります。
カラー画像の扱いでOpenCVを使う実装にしています。OpenCVが扱えない画像もあるので、何枚か読み込みに失敗しても大丈夫なように多めに入れておきます。

ch07とcommonは、本のサンプルソースコードです。参考にしたり、そのまま使ったりします。
ch07を参考にsimpleを追加します。

  • image
    • train
      • apple
      • cat
      • dog
      • mandarin
      • tachikoma
      • tomato
    • test
      • apple
      • cat
      • dog
      • mandarin
      • tachikoma
      • tomato
  • develop
    • deep_learning
      • ch07(サンプルソースコード)
      • common(サンプルソースコード)
      • simple(追加)
      • deep(追加予定)
      • vgg16(追加予定)

学習に使う画像の大きさ

元が640×480の画像を使って、処理用に小さくすると次の画像のようになります。28×28、56×56、84×84、112×112、168×168、224×224です。(28×28と84×84は学習に使わないですが、参考として掲載します。)
それを補正なしで300×300に拡大しています。

28x2856x5684x84
112x112168x168224x224

python3などのインストール

Macにはpython3がインストールされていないのでインストールします。(2018.02.26)
Homebrewを使ってインストールします。

最初にCommand Line Tools for Xcodeをインストールします。


> xcode-select --install

Homebrewのホームページから下記をコピーして、Macのターミナルで実行します。Homebrewがインストールされます。
Homebrew


> /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

python3をインストールします。pip3もインストールされます。
brewが見つからない場合は、ターミナルを一度閉じて、新しいターミナルを起動します。


> brew install python3

最低限、必要なものをインストールします。(memory_profilerは、必須ではありません。)


> pip3 install numpy
> pip3 install matplotlib
> pip3 install opencv-python
> pip3 install Pillow

> pip3 install memory_profiler

ctagsを使う

simpleディレクトリでctagsを使います。Macに最初から入っているctagsを使います。
エディターとして、viを使う前提です。’..’は、1階層上を示します。


> ctags ..

関数の定義にジャンプする : Ctrl + ‘]’
ジャンプした先から戻る : Ctrl + ‘t’

もっと規模の大きなソースコードを解析する場合は、Homebrewを使ってctagsとcscopeをインストールした方が良いでしょう。

プログラムを実装する

ゼロから作る Deep LearningのGitHubからソースコードを取得します。

サンプルソースコードch07/train_convnet.pyを書き換える

simpleディレクトリを作成して、ch07/train_convnet.pyをコピーして書き換えます。名前をtrain.pyに変更します。

ネットワークとTrainerの引数のほとんどを変更可能にします。Trainerの引数を増やして、全て変更可能にします。
画像データの読み込みは、Trainerで行うようにします。

本ではMNISTデータベースを使い、0,1,2,3,4,5,6,7,8,9の手書き文字を10種類に分類しています。
MNISTデータベースはtraining用に60000枚、testing用に10000枚の手書き文字の画像データを持ちます。
MNISTデータベースは、グレースケール画像なのでカラー画像の入力に対応するようにサンプルソースコードを変更します。
load_mnist関数が手書き文字の画像を取り出している箇所なので、これを置き換えます。ここでは、MNISTデータベースからデータを取ってくる処理をコメントアウトするのみです。
xは画像データで、tはラベル(=分類の番号(例: 0=犬 1=猫))を表します。

全体の処理の流れは、

  1. SimpleConvNetクラスをインスタンスする(=ニューラルネットワークを作成する)
  2. Trainerクラスのtrain関数でtrainingをする

となります。

simple/train.py

# coding: utf-8
import sys, os
sys.path.append(os.pardir)  # 親ディレクトリのファイルをインポートするための設定
import numpy as np
import matplotlib.pyplot as plt
# from dataset.mnist import load_mnist
from simple_convnet import SimpleConvNet
from common.trainer import Trainer

import time
import glob
import sys

# ------------------------------------------------------------------------------

# (x_train, t_train), (x_test, t_test) = load_mnist(flatten=False)
# print('MNIST x_train[0].shape :', x_train[0].shape)
# print('MNIST x_test[0].shape :', x_test[0].shape)

# ------------------------------------------------------------------------------

## tachikoma, cat, 56x56

# ------------------------------------------------------------------------------

argv = sys.argv
if 1 < len(argv):
    print( 'argv[1] :', argv[1], flush=True )

# ------------------------------------------------------------------------------

## for Network

IMAGE_SIZE = 56
# IMAGE_SIZE = 112

FILTER_NUM = 30
# FILTER_NUM = 300

HIDDEN_NUM = 100
# HIDDEN_NUM = 1000

## for Trainer

CATEGORIES_LIST = [ 'tachikoma', 'cat' ]

EPOCHS = 200

TRAINING_IMAGE_PATH = '../../../image/train/'
TESTING_IMAGE_PATH = '../../../image/test/'

## dog(12500), cat(12500), tomato(12500), ...
## one epoch
TRAINING_EACH_DATA_NUM = 100

## for each categories
LOAD_IMAGE_NUM_AT_ONE_TIME = TRAINING_EACH_DATA_NUM

## use same images
TESTING_EACH_DATA_NUM = 10

# TRAINING_MINI_BATCH_SIZE = 1
# TRAINING_MINI_BATCH_SIZE = 10
# TRAINING_MINI_BATCH_SIZE = 20
TRAINING_MINI_BATCH_SIZE = 50
# TRAINING_MINI_BATCH_SIZE = 100
# TRAINING_MINI_BATCH_SIZE = 1000

# OPTIMIZER = 'SGD'
# OPTIMIZER = 'Momentum'
# OPTIMIZER = 'Nesterov'
OPTIMIZER = 'AdaGrad'
# OPTIMIZER = 'RMSprop'
# OPTIMIZER = 'Adam'

LEARNING_RATE = 0.001
# LEARNING_RATE = 10 ** np.random.uniform(-6, -2)

print( 'learning rate :', LEARNING_RATE, flush=True )

# ------------------------------------------------------------------------------

# network = SimpleConvNet(input_dim=(1,28,28),
#                         conv_param = {'filter_num': 30, 'filter_size': 5, 'pad': 0, 'stride': 1},
#                         hidden_size=100, output_size=10, weight_init_std=0.01)

network = SimpleConvNet( input_dim=( 3, IMAGE_SIZE, IMAGE_SIZE ),
                         conv_param = { 'filter_num': FILTER_NUM, 'filter_size': 5, 'pad': 0, 'stride': 1 },
                         hidden_size=HIDDEN_NUM, output_size=len(CATEGORIES_LIST), weight_init_std=0.01 )

# ------------------------------------------------------------------------------

PARAMETER_LOAD_MODE = 0

if 1 < len(argv):
    if argv[1] == 'LOAD_PARAMETER':
        PARAMETER_LOAD_MODE = 1
    else:
        print( 'ERROR : argv[1] :', argv[1], flush=True )
        print( 'LOAD_PARAMETER is valid.', flush=True )
        quit()

# ------------------------------------------------------------------------------

## if pickle file exists, load it.

pickle_list = glob.glob('network_parameters.pkl.*')
print( 'pickle_list :', pickle_list)

new_pickle_name = 'network_parameters.pkl.' + '1'
pickle_file_latest_number = 1

if PARAMETER_LOAD_MODE == 1:
    if pickle_list:
        pickle_list.sort()
        print( 'sorted pickle_list :', pickle_list, flush=True )
        pickle_file_latest_name = pickle_list[-1]
        print( 'pickle_file_latest_name :', pickle_file_latest_name, flush=True )
        tmp_array = pickle_file_latest_name.split('.')
        pickle_file_latest_number = int( tmp_array[-1] )
        pickle_file_latest_number = pickle_file_latest_number + 1
        new_pickle_name = 'network_parameters.pkl.' + str(pickle_file_latest_number)

        network.load_params(file_name=pickle_file_latest_name)

print( 'new_pickle_name :', new_pickle_name, flush=True )

# ------------------------------------------------------------------------------

trainer = Trainer(network,
        training_image_path = TRAINING_IMAGE_PATH,
        testing_image_path = TESTING_IMAGE_PATH,
        load_image_num_at_one_time = LOAD_IMAGE_NUM_AT_ONE_TIME,
        training_each_data_num = TRAINING_EACH_DATA_NUM,
        testing_each_data_num = TESTING_EACH_DATA_NUM,
        categories_list = CATEGORIES_LIST, image_size = IMAGE_SIZE,
        epochs = EPOCHS,
        training_mini_batch_size = TRAINING_MINI_BATCH_SIZE,
        optimizer = OPTIMIZER, optimizer_param = {'lr': LEARNING_RATE},
        )

start_time = time.time()
trainer.train()
print('train_time :', time.time() - start_time )

# ------------------------------------------------------------------------------

## save parameter

network.save_params( new_pickle_name )
print( 'Saved Network Parameters! :', new_pickle_name )

## draw graph after

accuracy_list_file_name = 'accuracy_list.' + str(pickle_file_latest_number)
accuracy_list_file = open(accuracy_list_file_name, 'w')

print('y (train accuracy) :', trainer.train_acc_list, file=accuracy_list_file)
print('y (test  accuracy) :', trainer.test_acc_list, file=accuracy_list_file)
accuracy_list_file.close()

# ------------------------------------------------------------------------------

## draw graph

x = np.arange(EPOCHS)
print('x (epochs) :', x)
print('y (train accuracy) :', trainer.train_acc_list)
print('y (test  accuracy) :', trainer.test_acc_list)
plt.plot(x, trainer.train_acc_list, marker='o', label='train', markevery=1)
plt.plot(x, trainer.test_acc_list, marker='s', label='test', markevery=1)
plt.xlabel("epochs")
plt.ylabel("accuracy")
plt.ylim(0, 100.0)
plt.legend(loc='lower right')
# plt.show()
plt.savefig('learning_graph.png')
# plt.pause(30)

追加、変更した部分について説明します。

CATEGORIES_LIST = [ 'tachikoma', 'cat' ]

CATEGORIES_LISTは、trainディレクトリ、testディレクトリの下のディレクトリの名前に対応させます。

## dog(12500), cat(12500), tomato(12500), ...
## one epoch
TRAINING_EACH_DATA_NUM = 100

## for each categories
LOAD_IMAGE_NUM_AT_ONE_TIME = TRAINING_EACH_DATA_NUM

TRAINING_EACH_DATA_NUMは、1つの分類で使用する画像データの数を決めます。ですので、1エポック = TRAINING_EACH_DATA_NUM * len(CATEGORIES_LIST)となります。
LOAD_IMAGE_NUM_AT_ONE_TIMEは、例えば100万枚の画像を扱うとした場合、最初に100万枚を読み込んで保持し続けるとメモリ使用量が大きくなりすぎる場合も考えられるため、任意の枚数ずつ読み込んで学習できるようにするために用意しました。しかし、物理メモリの多いPCを買った方が懸命と思われるので、機能させないようにしています。Trainerの実装も、この機能がないものを掲載しています。

TRAINING_MINI_BATCH_SIZE = 50

TRAINING_MINI_BATCH_SIZEは、ミニバッチサイズです。2分類の場合、それぞれの分類から25枚ずつ取り出して学習します。
(np.randomを使っているため、平均25枚になります。)

メモリ4GBや8GBや16GBの環境で、swapが頻発する場合、ミニバッチサイズを小さくすることでswapを抑えることができます。

network = SimpleConvNet( input_dim=( 3, IMAGE_SIZE, IMAGE_SIZE ),
                        conv_param = { 'filter_num': FILTER_NUM, 'filter_size': 5, 'pad': 0, 'stride': 1 },
                        hidden_size=HIDDEN_NUM, output_size=CATEGORIES_NUM, weight_init_std=0.01 )

入力をカラー画像にするため、チャネル数を1から3に変更します。

PARAMETER_LOAD_MODE = 0

if 1 < len(argv):
    if argv[1] == 'LOAD_PARAMETER':
        PARAMETER_LOAD_MODE = 1
    else:
        print( 'ERROR : argv[1] :', argv[1], flush=True )
        print( 'LOAD_PARAMETER is valid.', flush=True )
        quit()

# ------------------------------------------------------------------------------

## if pickle file exists, load it.

pickle_list = glob.glob('network_parameters.pkl.*')
print( 'pickle_list : ', pickle_list)

new_pickle_name = 'network_parameters.pkl.' + '1'
pickle_file_latest_number = 1

if PARAMETER_LOAD_MODE == 1:
    if pickle_list:
        pickle_list.sort()
        print( 'sorted pickle_list :', pickle_list, flush=True )
        pickle_file_latest_name = pickle_list[-1]
        print( 'pickle_file_latest_name : ', pickle_file_latest_name, flush=True )
        tmp_array = pickle_file_latest_name.split('.')
        pickle_file_latest_number = int( tmp_array[-1] )
        pickle_file_latest_number = pickle_file_latest_number + 1
        new_pickle_name = 'network_parameters.pkl.' + str(pickle_file_latest_number)

        network.load_params(file_name=pickle_file_latest_name)

train.pyを呼び出すとき、特定の文字列を引数としたときだけ、保存してある学習結果のネットワークのパラメーターを読み込んでから学習するようにしています。下記のx.shを参照ください。

x.sh

python3 train.py
python3 train.py LOAD_PARAMETER
python3 train.py LOAD_PARAMETER
python3 train.py LOAD_PARAMETER
python3 train.py LOAD_PARAMETER

上記のシェルスクリプトを作成して実行すると、

network_parameters.pkl.1
network_parameters.pkl.2
network_parameters.pkl.3
network_parameters.pkl.4
network_parameters.pkl.5

が作成されていき、それまでの最新のパラメーターを読み込んでから学習します。
学習したネットワークのパラメーターの保存と読み込みはサンプルソースコードで用意してくれているので、利用します。

サンプルソースコードcommon/trainer.pyを書き換える

common/trainer.pyを書き換えます。元のファイルはtrainer.py.originalのようにして取っておきましょう。

全体がわかるようにprint文を入れていきます。
画像を読み込むための関数を追加します。この変更のため、Trainerが別物になります。MNISTやCIFAR10を使う場合は元のサンプルソースコードの方が適しています。

common/trainer.py

# coding: utf-8
import sys, os
sys.path.append(os.pardir)  # 親ディレクトリのファイルをインポートするための設定
import numpy as np
from common.optimizer import *

import cv2 as open_cv
import time
import matplotlib.pyplot as plt
from memory_profiler import profile

class Trainer:
    """ニューラルネットの訓練を行うクラス
    """

    # @profile
    def __init__(self, network,
            training_image_path = '',
            testing_image_path = '',
            load_image_num_at_one_time = 0,
            training_each_data_num = 0,
            testing_each_data_num = 0,
            categories_list = [], image_size = 56,
            epochs = 20,
            training_mini_batch_size = 100,
            optimizer='SGD', optimizer_param={'lr':0.01},
            evaluate_sample_num_per_epoch=None, verbose=True):

        self.network = network
        self.training_image_path = training_image_path
        self.testing_image_path = testing_image_path
        self.load_image_num_at_one_time = load_image_num_at_one_time
        self.training_each_data_num = training_each_data_num
        self.testing_each_data_num = testing_each_data_num
        self.categories_list = categories_list
        self.image_size = image_size
        self.verbose = verbose
        self.epochs = epochs
        self.training_mini_batch_size = training_mini_batch_size
        self.evaluate_sample_num_per_epoch = evaluate_sample_num_per_epoch

        # optimzer
        optimizer_class_dict = {'sgd':SGD, 'momentum':Momentum, 'nesterov':Nesterov,
                                'adagrad':AdaGrad, 'rmsprpo':RMSprop, 'adam':Adam}
        self.optimizer = optimizer_class_dict[optimizer.lower()](**optimizer_param)

        ## the number of images for training
        # self.train_size = x_train.shape[0]

        ## ex. iter_per_epoch(9) = self.train_size(900) / mini_batch_size(100)
        # self.iter_per_epoch = max(self.train_size / mini_batch_size, 1)
        self.iter_per_epoch = int( max( training_each_data_num * len(categories_list) / training_mini_batch_size, 1) )
        print( 'one epoch : mini batch size x', self.iter_per_epoch )

        ## ex. self.max_iter(9000) = epochs(1000) / self.iter_per_epoch(9)
        # self.max_iter = int(epochs * self.iter_per_epoch)

        self.current_iter = 0
        self.current_epoch = 0

        self.train_loss_list = []
        self.train_acc_list = []
        self.test_acc_list = []

    def get_image_and_label( self, path, categories_list, x_array, t_array, load_image_numbers ):

        for dir in os.listdir( path ):

            if dir == ".DS_Store":
                continue

            file_numbers = 0
            dir_with_path = path + dir
            # print( 'dir : ' + dir_with_path )

            ## dir : 'tachikoma' or 'dog' or 'cat' or ...
            # label = 0
            if dir in categories_list:
                label = categories_list.index( dir )
            else:
                continue

            for file in os.listdir(dir_with_path):

                # ------------------------------------------------------------------------------

                if file == ".DS_Store":
                    continue

                # ------------------------------------------------------------------------------

                if file.startswith('.'):
                    print('file name : ', file_with_path)
                    print('read skip')
                    continue

                # ------------------------------------------------------------------------------

                file_with_path = dir_with_path + "/" + file
                image = open_cv.imread(file_with_path)

                if image is None:
                    print( 'image read failed.', flush=True )
                    print( 'file name : ', file_with_path, flush=True )
                else:
                    ## debug
                    # print('file name : ', file_with_path)

                    image = open_cv.cvtColor(image, open_cv.COLOR_BGR2RGB)

                    image = open_cv.resize(image, (self.image_size, self.image_size))

                    ## debug
                    # print( 'category : ', dir, flush=True )
                    # print( 'label : ', label, flush=True )

                    ## set
                    x_array.append(image)
                    t_array.append(label)
                    file_numbers += 1

                    if file_numbers == load_image_numbers:
                        break

                    ## debug : break >> load one image only
                    # break

            print( '%s : the number of files : ' % dir , file_numbers, flush=True )

        ## debug
        ## 1920 x 2160
        fig = plt.figure(figsize=(19.2, 21.6))
        fig.subplots_adjust(left=0, right=1, bottom=0, top=1, hspace=0.05, wspace=0.05)
        for image_i in range( len(x_array) ):
            ax = fig.add_subplot(int(len(x_array)/10), 20, image_i + 1)
            ax.imshow(x_array[image_i], interpolation='nearest')
        # plt.show(block=False)
        # plt.pause(.3)
        load_image_savefig_name = 'load_image_savefig' + '.png'
        plt.savefig(load_image_savefig_name)

        ## clear the current axis
        # plt.cla()

        ## clear the current figure
        # plt.clf()

        ## clear a figure window
        plt.close()

    # @profile
    def train_step( self, x_batch, t_batch, x_test, t_test ):

        # ------------------------------------------------------------------------------

        ## gradient update loss

        grads = self.network.gradient( x_batch, t_batch )
        self.optimizer.update( self.network.params, grads )

        loss = self.network.loss(x_batch, t_batch)
        self.train_loss_list.append(loss)
        if self.verbose: print("train loss:" + str(loss), flush=True)

        # ------------------------------------------------------------------------------

        # one epoch complete >> get accuracy

        ## ex. iter_per_epoch(9)
        if self.current_iter % self.iter_per_epoch == 0:
            self.current_epoch += 1

            # x_train_sample, t_train_sample = x_train, t_train
            # x_test_sample, t_test_sample = x_test, t_test
            # if not self.evaluate_sample_num_per_epoch is None:
            #    t = self.evaluate_sample_num_per_epoch
            #    x_train_sample, t_train_sample = self.x_train[:t], self.t_train[:t]
            #    x_test_sample, t_test_sample = self.x_test[:t], self.t_test[:t]

            # train_acc = self.network.accuracy(x_train_sample, t_train_sample)
            train_acc = self.network.accuracy(x_batch, t_batch, len(x_batch)) * int(100 / len(x_batch))

            # test_acc = self.network.accuracy(x_test_sample, t_test_sample)
            test_acc = self.network.accuracy(x_test, t_test, len(x_test)) * int(100 / len(x_test))

            print('train accuracy : ', train_acc, flush=True)
            print('test  accuracy : ', test_acc, flush=True)
            self.train_acc_list.append(train_acc)
            self.test_acc_list.append(test_acc)

        # ------------------------------------------------------------------------------

        self.current_iter += 1
        print('current iter   : ', self.current_iter, flush=True)

    # @profile
    def train( self ):

        # ------------------------------------------------------------------------------

        ## get images for testing
        x_test, t_test = [], []
        self.get_image_and_label( self.testing_image_path, self.categories_list, x_test, t_test, self.testing_each_data_num )

        x_test = np.array(x_test)
        t_test = np.array(t_test)

        ## numbers, height, width, channel -> numbers channel, height, width
        x_test = x_test.transpose(0, 3, 1, 2)

        # ------------------------------------------------------------------------------

        ## self.training_each_data_num : 12500
        ## self.load_image_num_at_one_time : 12500 or 5000 or 1000

        ## get images for training (self.load_image_num_at_one_time * self.categories_list)
        x_train, t_train = [], []
        self.get_image_and_label( self.training_image_path, self.categories_list, x_train, t_train, self.load_image_num_at_one_time )

        x_train = np.array(x_train)
        t_train = np.array(t_train)
        print( 'load image total :', x_train.shape, flush=True )

        ## numbers, height, width, channel -> numbers channel, height, width
        x_train = x_train.transpose(0, 3, 1, 2)

        # ------------------------------------------------------------------------------

        ## training

        for epoch_i in range( self.epochs ):

            print( 'epoch :', epoch_i + 1, flush=True )

            for mini_batch_i in range( self.iter_per_epoch ):
                start_time = time.time()

                print( 'mini batch :', mini_batch_i + 1, flush=True )

                batch_mask = np.random.choice(x_train.shape[0], self.training_mini_batch_size)
                print( 'batch mask :', batch_mask, flush=True )
                x_batch = x_train[batch_mask]
                t_batch = t_train[batch_mask]

                self.train_step( x_batch, t_batch, x_test, t_test )

                print( 'train_step_time :', time.time() - start_time, flush=True )

        # ------------------------------------------------------------------------------

        ## get accuracy

        # test_acc = self.network.accuracy(self.x_test, self.t_test)
        last_test_num = int( self.testing_each_data_num * len(self.categories_list) )
        test_acc = self.network.accuracy(x_test, t_test, last_test_num) * int(100 / last_test_num)

        if self.verbose:
            print("=============== Final Test Accuracy ===============")
            print("test acc:" + str(test_acc))

train関数について説明します。

        ## get images for testing
        x_test, t_test = [], []
        self.get_image_and_label( self.testing_image_path, self.categories_list, x_test, t_test, self.testing_each_data_num )

学習結果を評価するための画像データを読み込みます。

        ## get images for training (self.load_image_num_at_one_time * self.categories_list)
        x_train, t_train = [], []
        self.get_image_and_label( self.training_image_path, self.categories_list, x_train, t_train, self.load_image_num_at_one_time )

学習に使うための画像データを読み込みます。

        for epoch_i in range( self.epochs ):

設定したエポック数だけ繰り返します。

            for mini_batch_i in range( int( self.training_each_data_num * len(self.categories_list) / self.training_mini_batch_size ) ):

ミニバッチサイズずつ、1エポックの枚数に達するまで学習を繰り返します。

                self.train_step( x_batch, t_batch, x_test, t_test )

実際の学習を行います。
train関数の説明は以上です。

        # one epoch complete >> get accuracy

        ## ex. iter_per_epoch(9)
        if self.current_iter % self.iter_per_epoch == 0:
            self.current_epoch += 1

            # x_train_sample, t_train_sample = x_train, t_train
            # x_test_sample, t_test_sample = x_test, t_test
            # if not self.evaluate_sample_num_per_epoch is None:
            #    t = self.evaluate_sample_num_per_epoch
            #    x_train_sample, t_train_sample = self.x_train[:t], self.t_train[:t]
            #    x_test_sample, t_test_sample = self.x_test[:t], self.t_test[:t]

            # train_acc = self.network.accuracy(x_train_sample, t_train_sample)
            train_acc = self.network.accuracy(x_batch, t_batch, len(x_batch)) * int(100 / len(x_batch))

            # test_acc = self.network.accuracy(x_test_sample, t_test_sample)
            test_acc = self.network.accuracy(x_test, t_test, len(x_test)) * int(100 / len(x_test))

            print('train accuracy :', train_acc, flush=True)
            print('test  accuracy :', test_acc, flush=True)
            self.train_acc_list.append(train_acc)
            self.test_acc_list.append(test_acc)

train_step関数の上記の部分は、下記に移動させた方がより自然と思われるので移動させます。

        for epoch_i in range( self.epochs ):
            ...
            for mini_batch_i in range( int( self.training_each_data_num * len(self.categories_list) / self.training_mini_batch_size ) ):
                ...
            ここ !!

サンプルソースコードch07/simple_convnet.pyを書き換える

simpleディレクトリに、ch07/simple_convnet.pyをコピーして書き換えます。
simple_convnet.pyのaccuracy関数だけを変更します。

    def accuracy(self, x, t, batch_size=100):
        if t.ndim != 1 : t = np.argmax(t, axis=1)

        acc = 0.0

        # for i in range(int(x.shape[0] / batch_size)):
        for i in range(1):
            # tx = x[i*batch_size:(i+1)*batch_size]
            # tt = t[i*batch_size:(i+1)*batch_size]
            batch_mask = np.random.choice(x.shape[0], batch_size)
            print('accuracy batch mask : ', batch_mask)
            tx = x[batch_mask]
            tt = t[batch_mask]

            print('answer label :')
            print(tt)

            y = self.predict(tx)
            y = np.argmax(y, axis=1)

            print('y : ')
            print(y)

            acc += np.sum(y == tt)

            print('accuracy :', np.sum(y == tt), '/ ', batch_size)

        # return acc / x.shape[0]
        return acc

書き換えた箇所について説明します。

        for i in range(1):

1回だけ評価するように変更しています。

            # tx = x[i*batch_size:(i+1)*batch_size]
            # tt = t[i*batch_size:(i+1)*batch_size]
            batch_mask = np.random.choice(x.shape[0], batch_size)
            print('accuracy batch mask :', batch_mask)
            tx = x[batch_mask]
            tt = t[batch_mask]

精度を評価する回数を1回に減らしたので、ランダムで選択するように変更します。こうしないと、選択する画像の種類が偏ってしまいます。

            print('answer label :')
            print(tt)

正解のラベルを表示します。

            print('y :')
            print(y)

ネットワークの出力結果のラベルを表示します。何を何と間違えているかがわかります。

        # return acc / x.shape[0]
        return acc

正解率ではなく、正解した個数を返すようにしています。accuracy関数で100個のデータで評価するようにするとこれがそのままパーセンテージになります。

実行環境

実行環境としてシェルスクリプトを3種類作成します。

x.sh.common

#! /bin/sh

date "+%Y_%m_%d_%H_%M"

current_dir_name=`basename \`pwd\``

# (categories) (layers) (image size) (train times)
dir_name=output_${current_dir_name}.$1.`date "+%Y_%m_%d_%H_%M"`

pushd ..
if [ ! -d ${dir_name} ]; then
    mkdir ${dir_name}
else
    echo ${dir_name}
    echo 'directory exists'
    exit
fi
popd

/usr/bin/time -lp python3 train.py.$1 $2 2>&1 | tee log_train
# /usr/bin/time -lp python3 -m cProfile train.py.$1 $2 2>&1 | tee log_train

cp log_train ../${dir_name}/
cp learning_graph.png ../${dir_name}/

latest_num=`ls -1 network_parameters.pkl.* | tail -n 1 | cut -d . -f 3`

cp network_parameters.pkl.${latest_num} ../${dir_name}/
cp accuracy_list.${latest_num} ../${dir_name}/

simpleディレクトリ内で、すぐ後で説明するy.shを実行したとき、output_simple.001.2018_07_06_08_31の様な名前のディレクトリを1つ上の階層に作成して、学習したパラメーターなどをコピーします。

x.sh.001

#! /bin/sh

number=001

. ./x.sh.common ${number} $1

自分の番号を文字列としてx.sh.commonに渡す役割のシェルスクリプトです。

y.sh

#! /bin/sh -x


## tachikoma, cat, 56x56
./x.sh.001

## tomato, mandarin, 56x56
./x.sh.002

## tomato, apple, 56x56
./x.sh.003


## dog, cat, 56x56
./x.sh.010

## dog, cat, 112x112
./x.sh.011

## dog, cat, 168x168
./x.sh.012

## dog, cat, 224x224
./x.sh.013


## dog, cat, 56x56, kaggle
 ./x.sh.020
 ./x.sh.020 LOAD_PARAMETER
# ./x.sh.020 LOAD_PARAMETER

## dog, cat, 112x112, kaggle
./x.sh.021

## dog, cat, 168x168, kaggle
# ./x.sh.022

## dog, cat, 224x224, kaggle
# ./x.sh.023

引数の変更で管理するより、実体のある方が好きなので、x.sh.XXXを複数作成しています。実行する内容の種類が増える場合はx.sh.XXXは無くすべきでしょう。
ネットワークのパラメーターを読み込む場合も読み込まない場合も、連続して実行させることができます。

このとき、画像を読み込むという同じ処理をするオーバーヘッドが発生します。連続して実行させたまま、それまでに完了した結果を確認することができて、それまでの学習結果も残るためメリットの方が大きいでしょう。

まとめ

  • サンプルソースコードを書き換えて、カラー画像を入力とした学習をした
  • 画像を集めた
  • Mac miniに、python3やnumpyをインストールした
  • ネットワークとTrainerに対する各パラメーターをほとんど全てを変更できるようにtrain.pyを実装した
  • 実行環境としてシェルスクリプトを作成した

次は、実際に動作させます。

参考

ゼロから作るDeep Learning
deep-learning-from-scratch
Kerasによる、ものすごくシンプルな画像分類(りんごとオレンジ)





«       »