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

  1. # coding: utf-8
  2. import sys, os
  3. sys.path.append(os.pardir) # 親ディレクトリのファイルをインポートするための設定
  4. import numpy as np
  5. import matplotlib.pyplot as plt
  6. # from dataset.mnist import load_mnist
  7. from simple_convnet import SimpleConvNet
  8. from common.trainer import Trainer
  9. import time
  10. import glob
  11. import sys
  12. # ------------------------------------------------------------------------------
  13. # (x_train, t_train), (x_test, t_test) = load_mnist(flatten=False)
  14. # print('MNIST x_train[0].shape :', x_train[0].shape)
  15. # print('MNIST x_test[0].shape :', x_test[0].shape)
  16. # ------------------------------------------------------------------------------
  17. ## tachikoma, cat, 56x56
  18. # ------------------------------------------------------------------------------
  19. argv = sys.argv
  20. if 1 < len(argv):
  21. print( 'argv[1] :', argv[1], flush=True )
  22. # ------------------------------------------------------------------------------
  23. ## for Network
  24. IMAGE_SIZE = 56
  25. # IMAGE_SIZE = 112
  26. FILTER_NUM = 30
  27. # FILTER_NUM = 300
  28. HIDDEN_NUM = 100
  29. # HIDDEN_NUM = 1000
  30. ## for Trainer
  31. CATEGORIES_LIST = [ 'tachikoma', 'cat' ]
  32. EPOCHS = 200
  33. TRAINING_IMAGE_PATH = '../../../image/train/'
  34. TESTING_IMAGE_PATH = '../../../image/test/'
  35. ## dog(12500), cat(12500), tomato(12500), ...
  36. ## one epoch
  37. TRAINING_EACH_DATA_NUM = 100
  38. ## for each categories
  39. LOAD_IMAGE_NUM_AT_ONE_TIME = TRAINING_EACH_DATA_NUM
  40. ## use same images
  41. TESTING_EACH_DATA_NUM = 10
  42. # TRAINING_MINI_BATCH_SIZE = 1
  43. # TRAINING_MINI_BATCH_SIZE = 10
  44. # TRAINING_MINI_BATCH_SIZE = 20
  45. TRAINING_MINI_BATCH_SIZE = 50
  46. # TRAINING_MINI_BATCH_SIZE = 100
  47. # TRAINING_MINI_BATCH_SIZE = 1000
  48. # OPTIMIZER = 'SGD'
  49. # OPTIMIZER = 'Momentum'
  50. # OPTIMIZER = 'Nesterov'
  51. OPTIMIZER = 'AdaGrad'
  52. # OPTIMIZER = 'RMSprop'
  53. # OPTIMIZER = 'Adam'
  54. LEARNING_RATE = 0.001
  55. # LEARNING_RATE = 10 ** np.random.uniform(-6, -2)
  56. print( 'learning rate :', LEARNING_RATE, flush=True )
  57. # ------------------------------------------------------------------------------
  58. # network = SimpleConvNet(input_dim=(1,28,28),
  59. # conv_param = {'filter_num': 30, 'filter_size': 5, 'pad': 0, 'stride': 1},
  60. # hidden_size=100, output_size=10, weight_init_std=0.01)
  61. network = SimpleConvNet( input_dim=( 3, IMAGE_SIZE, IMAGE_SIZE ),
  62. conv_param = { 'filter_num': FILTER_NUM, 'filter_size': 5, 'pad': 0, 'stride': 1 },
  63. hidden_size=HIDDEN_NUM, output_size=len(CATEGORIES_LIST), weight_init_std=0.01 )
  64. # ------------------------------------------------------------------------------
  65. PARAMETER_LOAD_MODE = 0
  66. if 1 < len(argv):
  67. if argv[1] == 'LOAD_PARAMETER':
  68. PARAMETER_LOAD_MODE = 1
  69. else:
  70. print( 'ERROR : argv[1] :', argv[1], flush=True )
  71. print( 'LOAD_PARAMETER is valid.', flush=True )
  72. quit()
  73. # ------------------------------------------------------------------------------
  74. ## if pickle file exists, load it.
  75. pickle_list = glob.glob('network_parameters.pkl.*')
  76. print( 'pickle_list :', pickle_list)
  77. new_pickle_name = 'network_parameters.pkl.' + '1'
  78. pickle_file_latest_number = 1
  79. if PARAMETER_LOAD_MODE == 1:
  80. if pickle_list:
  81. pickle_list.sort()
  82. print( 'sorted pickle_list :', pickle_list, flush=True )
  83. pickle_file_latest_name = pickle_list[-1]
  84. print( 'pickle_file_latest_name :', pickle_file_latest_name, flush=True )
  85. tmp_array = pickle_file_latest_name.split('.')
  86. pickle_file_latest_number = int( tmp_array[-1] )
  87. pickle_file_latest_number = pickle_file_latest_number + 1
  88. new_pickle_name = 'network_parameters.pkl.' + str(pickle_file_latest_number)
  89. network.load_params(file_name=pickle_file_latest_name)
  90. print( 'new_pickle_name :', new_pickle_name, flush=True )
  91. # ------------------------------------------------------------------------------
  92. trainer = Trainer(network,
  93. training_image_path = TRAINING_IMAGE_PATH,
  94. testing_image_path = TESTING_IMAGE_PATH,
  95. load_image_num_at_one_time = LOAD_IMAGE_NUM_AT_ONE_TIME,
  96. training_each_data_num = TRAINING_EACH_DATA_NUM,
  97. testing_each_data_num = TESTING_EACH_DATA_NUM,
  98. categories_list = CATEGORIES_LIST, image_size = IMAGE_SIZE,
  99. epochs = EPOCHS,
  100. training_mini_batch_size = TRAINING_MINI_BATCH_SIZE,
  101. optimizer = OPTIMIZER, optimizer_param = {'lr': LEARNING_RATE},
  102. )
  103. start_time = time.time()
  104. trainer.train()
  105. print('train_time :', time.time() - start_time )
  106. # ------------------------------------------------------------------------------
  107. ## save parameter
  108. network.save_params( new_pickle_name )
  109. print( 'Saved Network Parameters! :', new_pickle_name )
  110. ## draw graph after
  111. accuracy_list_file_name = 'accuracy_list.' + str(pickle_file_latest_number)
  112. accuracy_list_file = open(accuracy_list_file_name, 'w')
  113. print('y (train accuracy) :', trainer.train_acc_list, file=accuracy_list_file)
  114. print('y (test accuracy) :', trainer.test_acc_list, file=accuracy_list_file)
  115. accuracy_list_file.close()
  116. # ------------------------------------------------------------------------------
  117. ## draw graph
  118. x = np.arange(EPOCHS)
  119. print('x (epochs) :', x)
  120. print('y (train accuracy) :', trainer.train_acc_list)
  121. print('y (test accuracy) :', trainer.test_acc_list)
  122. plt.plot(x, trainer.train_acc_list, marker='o', label='train', markevery=1)
  123. plt.plot(x, trainer.test_acc_list, marker='s', label='test', markevery=1)
  124. plt.xlabel("epochs")
  125. plt.ylabel("accuracy")
  126. plt.ylim(0, 100.0)
  127. plt.legend(loc='lower right')
  128. # plt.show()
  129. plt.savefig('learning_graph.png')
  130. # plt.pause(30)

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

  1. CATEGORIES_LIST = [ 'tachikoma', 'cat' ]

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

  1. ## dog(12500), cat(12500), tomato(12500), ...
  2. ## one epoch
  3. TRAINING_EACH_DATA_NUM = 100
  4. ## for each categories
  5. 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の実装も、この機能がないものを掲載しています。

  1. TRAINING_MINI_BATCH_SIZE = 50

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

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

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

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

  1. PARAMETER_LOAD_MODE = 0
  2. if 1 < len(argv):
  3. if argv[1] == 'LOAD_PARAMETER':
  4. PARAMETER_LOAD_MODE = 1
  5. else:
  6. print( 'ERROR : argv[1] :', argv[1], flush=True )
  7. print( 'LOAD_PARAMETER is valid.', flush=True )
  8. quit()
  9. # ------------------------------------------------------------------------------
  10. ## if pickle file exists, load it.
  11. pickle_list = glob.glob('network_parameters.pkl.*')
  12. print( 'pickle_list : ', pickle_list)
  13. new_pickle_name = 'network_parameters.pkl.' + '1'
  14. pickle_file_latest_number = 1
  15. if PARAMETER_LOAD_MODE == 1:
  16. if pickle_list:
  17. pickle_list.sort()
  18. print( 'sorted pickle_list :', pickle_list, flush=True )
  19. pickle_file_latest_name = pickle_list[-1]
  20. print( 'pickle_file_latest_name : ', pickle_file_latest_name, flush=True )
  21. tmp_array = pickle_file_latest_name.split('.')
  22. pickle_file_latest_number = int( tmp_array[-1] )
  23. pickle_file_latest_number = pickle_file_latest_number + 1
  24. new_pickle_name = 'network_parameters.pkl.' + str(pickle_file_latest_number)
  25. network.load_params(file_name=pickle_file_latest_name)

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

x.sh

  1. python3 train.py
  2. python3 train.py LOAD_PARAMETER
  3. python3 train.py LOAD_PARAMETER
  4. python3 train.py LOAD_PARAMETER
  5. 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

  1. # coding: utf-8
  2. import sys, os
  3. sys.path.append(os.pardir) # 親ディレクトリのファイルをインポートするための設定
  4. import numpy as np
  5. from common.optimizer import *
  6. import cv2 as open_cv
  7. import time
  8. import matplotlib.pyplot as plt
  9. from memory_profiler import profile
  10. class Trainer:
  11. """ニューラルネットの訓練を行うクラス
  12. """
  13. # @profile
  14. def __init__(self, network,
  15. training_image_path = '',
  16. testing_image_path = '',
  17. load_image_num_at_one_time = 0,
  18. training_each_data_num = 0,
  19. testing_each_data_num = 0,
  20. categories_list = [], image_size = 56,
  21. epochs = 20,
  22. training_mini_batch_size = 100,
  23. optimizer='SGD', optimizer_param={'lr':0.01},
  24. evaluate_sample_num_per_epoch=None, verbose=True):
  25. self.network = network
  26. self.training_image_path = training_image_path
  27. self.testing_image_path = testing_image_path
  28. self.load_image_num_at_one_time = load_image_num_at_one_time
  29. self.training_each_data_num = training_each_data_num
  30. self.testing_each_data_num = testing_each_data_num
  31. self.categories_list = categories_list
  32. self.image_size = image_size
  33. self.verbose = verbose
  34. self.epochs = epochs
  35. self.training_mini_batch_size = training_mini_batch_size
  36. self.evaluate_sample_num_per_epoch = evaluate_sample_num_per_epoch
  37. # optimzer
  38. optimizer_class_dict = {'sgd':SGD, 'momentum':Momentum, 'nesterov':Nesterov,
  39. 'adagrad':AdaGrad, 'rmsprpo':RMSprop, 'adam':Adam}
  40. self.optimizer = optimizer_class_dict[optimizer.lower()](**optimizer_param)
  41. ## the number of images for training
  42. # self.train_size = x_train.shape[0]
  43. ## ex. iter_per_epoch(9) = self.train_size(900) / mini_batch_size(100)
  44. # self.iter_per_epoch = max(self.train_size / mini_batch_size, 1)
  45. self.iter_per_epoch = int( max( training_each_data_num * len(categories_list) / training_mini_batch_size, 1) )
  46. print( 'one epoch : mini batch size x', self.iter_per_epoch )
  47. ## ex. self.max_iter(9000) = epochs(1000) / self.iter_per_epoch(9)
  48. # self.max_iter = int(epochs * self.iter_per_epoch)
  49. self.current_iter = 0
  50. self.current_epoch = 0
  51. self.train_loss_list = []
  52. self.train_acc_list = []
  53. self.test_acc_list = []
  54. def get_image_and_label( self, path, categories_list, x_array, t_array, load_image_numbers ):
  55. for dir in os.listdir( path ):
  56. if dir == ".DS_Store":
  57. continue
  58. file_numbers = 0
  59. dir_with_path = path + dir
  60. # print( 'dir : ' + dir_with_path )
  61. ## dir : 'tachikoma' or 'dog' or 'cat' or ...
  62. # label = 0
  63. if dir in categories_list:
  64. label = categories_list.index( dir )
  65. else:
  66. continue
  67. for file in os.listdir(dir_with_path):
  68. # ------------------------------------------------------------------------------
  69. if file == ".DS_Store":
  70. continue
  71. # ------------------------------------------------------------------------------
  72. if file.startswith('.'):
  73. print('file name : ', file_with_path)
  74. print('read skip')
  75. continue
  76. # ------------------------------------------------------------------------------
  77. file_with_path = dir_with_path + "/" + file
  78. image = open_cv.imread(file_with_path)
  79. if image is None:
  80. print( 'image read failed.', flush=True )
  81. print( 'file name : ', file_with_path, flush=True )
  82. else:
  83. ## debug
  84. # print('file name : ', file_with_path)
  85. image = open_cv.cvtColor(image, open_cv.COLOR_BGR2RGB)
  86. image = open_cv.resize(image, (self.image_size, self.image_size))
  87. ## debug
  88. # print( 'category : ', dir, flush=True )
  89. # print( 'label : ', label, flush=True )
  90. ## set
  91. x_array.append(image)
  92. t_array.append(label)
  93. file_numbers += 1
  94. if file_numbers == load_image_numbers:
  95. break
  96. ## debug : break >> load one image only
  97. # break
  98. print( '%s : the number of files : ' % dir , file_numbers, flush=True )
  99. ## debug
  100. ## 1920 x 2160
  101. fig = plt.figure(figsize=(19.2, 21.6))
  102. fig.subplots_adjust(left=0, right=1, bottom=0, top=1, hspace=0.05, wspace=0.05)
  103. for image_i in range( len(x_array) ):
  104. ax = fig.add_subplot(int(len(x_array)/10), 20, image_i + 1)
  105. ax.imshow(x_array[image_i], interpolation='nearest')
  106. # plt.show(block=False)
  107. # plt.pause(.3)
  108. load_image_savefig_name = 'load_image_savefig' + '.png'
  109. plt.savefig(load_image_savefig_name)
  110. ## clear the current axis
  111. # plt.cla()
  112. ## clear the current figure
  113. # plt.clf()
  114. ## clear a figure window
  115. plt.close()
  116. # @profile
  117. def train_step( self, x_batch, t_batch, x_test, t_test ):
  118. # ------------------------------------------------------------------------------
  119. ## gradient update loss
  120. grads = self.network.gradient( x_batch, t_batch )
  121. self.optimizer.update( self.network.params, grads )
  122. loss = self.network.loss(x_batch, t_batch)
  123. self.train_loss_list.append(loss)
  124. if self.verbose: print("train loss:" + str(loss), flush=True)
  125. # ------------------------------------------------------------------------------
  126. # one epoch complete >> get accuracy
  127. ## ex. iter_per_epoch(9)
  128. if self.current_iter % self.iter_per_epoch == 0:
  129. self.current_epoch += 1
  130. # x_train_sample, t_train_sample = x_train, t_train
  131. # x_test_sample, t_test_sample = x_test, t_test
  132. # if not self.evaluate_sample_num_per_epoch is None:
  133. # t = self.evaluate_sample_num_per_epoch
  134. # x_train_sample, t_train_sample = self.x_train[:t], self.t_train[:t]
  135. # x_test_sample, t_test_sample = self.x_test[:t], self.t_test[:t]
  136. # train_acc = self.network.accuracy(x_train_sample, t_train_sample)
  137. train_acc = self.network.accuracy(x_batch, t_batch, len(x_batch)) * int(100 / len(x_batch))
  138. # test_acc = self.network.accuracy(x_test_sample, t_test_sample)
  139. test_acc = self.network.accuracy(x_test, t_test, len(x_test)) * int(100 / len(x_test))
  140. print('train accuracy : ', train_acc, flush=True)
  141. print('test accuracy : ', test_acc, flush=True)
  142. self.train_acc_list.append(train_acc)
  143. self.test_acc_list.append(test_acc)
  144. # ------------------------------------------------------------------------------
  145. self.current_iter += 1
  146. print('current iter : ', self.current_iter, flush=True)
  147. # @profile
  148. def train( self ):
  149. # ------------------------------------------------------------------------------
  150. ## get images for testing
  151. x_test, t_test = [], []
  152. self.get_image_and_label( self.testing_image_path, self.categories_list, x_test, t_test, self.testing_each_data_num )
  153. x_test = np.array(x_test)
  154. t_test = np.array(t_test)
  155. ## numbers, height, width, channel -> numbers channel, height, width
  156. x_test = x_test.transpose(0, 3, 1, 2)
  157. # ------------------------------------------------------------------------------
  158. ## self.training_each_data_num : 12500
  159. ## self.load_image_num_at_one_time : 12500 or 5000 or 1000
  160. ## get images for training (self.load_image_num_at_one_time * self.categories_list)
  161. x_train, t_train = [], []
  162. self.get_image_and_label( self.training_image_path, self.categories_list, x_train, t_train, self.load_image_num_at_one_time )
  163. x_train = np.array(x_train)
  164. t_train = np.array(t_train)
  165. print( 'load image total :', x_train.shape, flush=True )
  166. ## numbers, height, width, channel -> numbers channel, height, width
  167. x_train = x_train.transpose(0, 3, 1, 2)
  168. # ------------------------------------------------------------------------------
  169. ## training
  170. for epoch_i in range( self.epochs ):
  171. print( 'epoch :', epoch_i + 1, flush=True )
  172. for mini_batch_i in range( self.iter_per_epoch ):
  173. start_time = time.time()
  174. print( 'mini batch :', mini_batch_i + 1, flush=True )
  175. batch_mask = np.random.choice(x_train.shape[0], self.training_mini_batch_size)
  176. print( 'batch mask :', batch_mask, flush=True )
  177. x_batch = x_train[batch_mask]
  178. t_batch = t_train[batch_mask]
  179. self.train_step( x_batch, t_batch, x_test, t_test )
  180. print( 'train_step_time :', time.time() - start_time, flush=True )
  181. # ------------------------------------------------------------------------------
  182. ## get accuracy
  183. # test_acc = self.network.accuracy(self.x_test, self.t_test)
  184. last_test_num = int( self.testing_each_data_num * len(self.categories_list) )
  185. test_acc = self.network.accuracy(x_test, t_test, last_test_num) * int(100 / last_test_num)
  186. if self.verbose:
  187. print("=============== Final Test Accuracy ===============")
  188. print("test acc:" + str(test_acc))

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

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

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

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

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

  1. for epoch_i in range( self.epochs ):

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

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

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

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

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

  1. # one epoch complete >> get accuracy
  2. ## ex. iter_per_epoch(9)
  3. if self.current_iter % self.iter_per_epoch == 0:
  4. self.current_epoch += 1
  5. # x_train_sample, t_train_sample = x_train, t_train
  6. # x_test_sample, t_test_sample = x_test, t_test
  7. # if not self.evaluate_sample_num_per_epoch is None:
  8. # t = self.evaluate_sample_num_per_epoch
  9. # x_train_sample, t_train_sample = self.x_train[:t], self.t_train[:t]
  10. # x_test_sample, t_test_sample = self.x_test[:t], self.t_test[:t]
  11. # train_acc = self.network.accuracy(x_train_sample, t_train_sample)
  12. train_acc = self.network.accuracy(x_batch, t_batch, len(x_batch)) * int(100 / len(x_batch))
  13. # test_acc = self.network.accuracy(x_test_sample, t_test_sample)
  14. test_acc = self.network.accuracy(x_test, t_test, len(x_test)) * int(100 / len(x_test))
  15. print('train accuracy :', train_acc, flush=True)
  16. print('test accuracy :', test_acc, flush=True)
  17. self.train_acc_list.append(train_acc)
  18. self.test_acc_list.append(test_acc)

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

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

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

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

  1. def accuracy(self, x, t, batch_size=100):
  2. if t.ndim != 1 : t = np.argmax(t, axis=1)
  3. acc = 0.0
  4. # for i in range(int(x.shape[0] / batch_size)):
  5. for i in range(1):
  6. # tx = x[i*batch_size:(i+1)*batch_size]
  7. # tt = t[i*batch_size:(i+1)*batch_size]
  8. batch_mask = np.random.choice(x.shape[0], batch_size)
  9. print('accuracy batch mask : ', batch_mask)
  10. tx = x[batch_mask]
  11. tt = t[batch_mask]
  12. print('answer label :')
  13. print(tt)
  14. y = self.predict(tx)
  15. y = np.argmax(y, axis=1)
  16. print('y : ')
  17. print(y)
  18. acc += np.sum(y == tt)
  19. print('accuracy :', np.sum(y == tt), '/ ', batch_size)
  20. # return acc / x.shape[0]
  21. return acc

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

  1. for i in range(1):

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

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

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

  1. print('answer label :')
  2. print(tt)

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

  1. print('y :')
  2. print(y)

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

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

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

実行環境

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

x.sh.common

  1. #! /bin/sh
  2. date "+%Y_%m_%d_%H_%M"
  3. current_dir_name=`basename \`pwd\``
  4. # (categories) (layers) (image size) (train times)
  5. dir_name=output_${current_dir_name}.$1.`date "+%Y_%m_%d_%H_%M"`
  6. pushd ..
  7. if [ ! -d ${dir_name} ]; then
  8. mkdir ${dir_name}
  9. else
  10. echo ${dir_name}
  11. echo 'directory exists'
  12. exit
  13. fi
  14. popd
  15. /usr/bin/time -lp python3 train.py.$1 $2 2>&1 | tee log_train
  16. # /usr/bin/time -lp python3 -m cProfile train.py.$1 $2 2>&1 | tee log_train
  17. cp log_train ../${dir_name}/
  18. cp learning_graph.png ../${dir_name}/
  19. latest_num=`ls -1 network_parameters.pkl.* | tail -n 1 | cut -d . -f 3`
  20. cp network_parameters.pkl.${latest_num} ../${dir_name}/
  21. cp accuracy_list.${latest_num} ../${dir_name}/

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

x.sh.001

  1. #! /bin/sh
  2. number=001
  3. . ./x.sh.common ${number} $1

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

y.sh

  1. #! /bin/sh -x
  2. ## tachikoma, cat, 56x56
  3. ./x.sh.001
  4. ## tomato, mandarin, 56x56
  5. ./x.sh.002
  6. ## tomato, apple, 56x56
  7. ./x.sh.003
  8. ## dog, cat, 56x56
  9. ./x.sh.010
  10. ## dog, cat, 112x112
  11. ./x.sh.011
  12. ## dog, cat, 168x168
  13. ./x.sh.012
  14. ## dog, cat, 224x224
  15. ./x.sh.013
  16. ## dog, cat, 56x56, kaggle
  17. ./x.sh.020
  18. ./x.sh.020 LOAD_PARAMETER
  19. # ./x.sh.020 LOAD_PARAMETER
  20. ## dog, cat, 112x112, kaggle
  21. ./x.sh.021
  22. ## dog, cat, 168x168, kaggle
  23. # ./x.sh.022
  24. ## dog, cat, 224x224, kaggle
  25. # ./x.sh.023

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

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

まとめ

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

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

参考

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





«       »