Chainerを使った学習(訓練)時の進捗状況を見てみよう
Keyword:DICOM,Chainer,Trainer, Extension,Extend
これまでPyゼミ3.04,3.05ではニューラルネットワークについて説明してきました。
そして学習時に精度と損失の変化を観察して,学習の進捗状況を知る方法を知っています。
本章では学習の進捗状況をグラフ化したり,学習を検証したりする方法を説明します。
Pytゼミ3.05など既に学習の進捗を見るTrainerのextend関数を使ってきました。
既に使ってきた3つの関数を見てみましょう。
LogReportは各エポックごとに損失や精度などをログに記録します。
ログファイルはresultディレクトリの中に作成されます。
trainer.extend(extensions.LogReport())
PrintReportは引数で指定した内容(ここでは,エポック数,損失と精度)をターミナルに表示ます。
trainer.extend(extensions.PrintReport(['epoch' , 'main/loss' , 'main/accuracy']))
ProgressBarは,学習の進捗状況を進捗バーを用いてパーセンテージで表示します。
大量のデータを使って長時間学習するような場合にどこまで,進んだのか進捗を見るのに便利です。
また,1エポックにかかる時間や終了までにかかる時間を示すので,時間のかかる学習には便利です。
trainer.extend(extensions.ProgressBar())
学習中に過学習を起こしていないか確認するために学習データの一部を検証データにします。
学習に用いるもとのデータはtrainに格納されています。
このデータを学習用と検証用の2つに分けます。
データを8割を学習用trnに,残り2割を検証用にvldに,split_dataset_randomを使ってランダムに振り分けます。
seedは乱数のシードで,毎回,同じシード値を用いることで同じ乱数系が生成されます。
つまり毎回同じ組み合わせのtrnとvldのデータが生成されます。
trn, vld = split_dataset_random(train, int( len(train)*0.8 ), seed = 0)
学習用trnと検証用vldそれぞれのiteratorを作成します。
t_iterator = iterators.SerialIterator(trn, batch_size, shuffle = True)
v_iterator = iterators.SerialIterator(vld, batch_size, repeat = False, shuffle = False)
検証用のデータを評価に用いるために次の一行を追加します。
trainer.extend(extensions.Evaluator(v_iterator, self.model))
そして学習時に精度と損失の変化を観察して,学習の進捗状況を知る方法を知っています。
本章では学習の進捗状況をグラフ化したり,学習を検証したりする方法を説明します。
学習の進捗を見る
Pytゼミ3.05など既に学習の進捗を見るTrainerのextend関数を使ってきました。
既に使ってきた3つの関数を見てみましょう。
LogReportは各エポックごとに損失や精度などをログに記録します。
ログファイルはresultディレクトリの中に作成されます。
trainer.extend(extensions.LogReport())
PrintReportは引数で指定した内容(ここでは,エポック数,損失と精度)をターミナルに表示ます。
trainer.extend(extensions.PrintReport(['epoch' , 'main/loss' , 'main/accuracy']))
ProgressBarは,学習の進捗状況を進捗バーを用いてパーセンテージで表示します。
大量のデータを使って長時間学習するような場合にどこまで,進んだのか進捗を見るのに便利です。
また,1エポックにかかる時間や終了までにかかる時間を示すので,時間のかかる学習には便利です。
trainer.extend(extensions.ProgressBar())
学習データを訓練用と検証用に分けて学習する
学習中に過学習を起こしていないか確認するために学習データの一部を検証データにします。
学習に用いるもとのデータはtrainに格納されています。
このデータを学習用と検証用の2つに分けます。
データを8割を学習用trnに,残り2割を検証用にvldに,split_dataset_randomを使ってランダムに振り分けます。
seedは乱数のシードで,毎回,同じシード値を用いることで同じ乱数系が生成されます。
つまり毎回同じ組み合わせのtrnとvldのデータが生成されます。
trn, vld = split_dataset_random(train, int( len(train)*0.8 ), seed = 0)
学習用trnと検証用vldそれぞれのiteratorを作成します。
t_iterator = iterators.SerialIterator(trn, batch_size, shuffle = True)
v_iterator = iterators.SerialIterator(vld, batch_size, repeat = False, shuffle = False)
検証用のデータを評価に用いるために次の一行を追加します。
trainer.extend(extensions.Evaluator(v_iterator, self.model))
学習の進捗と検証データの評価を確認する。
PrintReportに'validation/main/accuracy'を追加することで,学習の進捗と同時に検証の結果もターミナルに表示することができます。
trainer.extend(extensions.PrintReport(['epoch', 'main/loss', 'main/accuracy', 'validation/main/accuracy']))
進捗状況をグラフにプロットする
損失の変化がloss.pngという画像ファイルとしてresultディレクトリ内に記録されます。
画像ファイルは1エポックごとに更新されます。
loss.pngを表示しておくと,エッポックごとに損失の変化を観察することができます。
trainer.extend(extensions.PlotReport(['main/loss', 'validation/main/loss'], x_key='epoch', file_name='loss.png'))
精度の変化はaccuracy.pngに記録されます。
trainer.extend(extensions.PlotReport(['main/accuracy', 'validation/main/accuracy'], x_key='epoch', file_name='accuracy.png'))
プログラムを入力実行してみよう。
下に示すプログラムはPyゼミ3.05のAlexnetの一部(先に説明した部分,doTrain関数内)を変更しました。
違いを確かめながら入力し実行してみましょう。
import os, sys import numpy as np import cv2 import chainer from chainer import Function, report, training, utils, Variable from chainer import datasets, iterators, optimizers, serializers from chainer import Link, Chain, ChainList import chainer.functions as F import chainer.links as L from chainer.datasets import tuple_dataset from chainer.training import extensions from chainer.datasets import split_dataset_random #new import pydicom def constractTrainData( fname , resz ): image = [] label = [] for i, line in enumerate(open(fname, 'r')): data = line.split(",") # 画像ファイル名と正解データに分割 print("Train",i, data[0], "Label#",data[1]) img = readDicom2png(data[0], "tmp.png") if resz > 0: #画像のリサイズ img = cv2.resize(img,(resz, resz)) img = img.astype(np.float32) img = (img - 128)/128 #画素値0-255を-1.0から+1.0に変換 image.append([img]) t = np.array(int(data[1]), dtype=np.int32) label.append(t) train = tuple_dataset.TupleDataset(image, label) return train def readDicom2png(dcmfnm, tmpfnm): ds = pydicom.read_file(dcmfnm) wc = ds.WindowCenter ww = ds.WindowWidth img = ds.pixel_array max = wc + ww / 2 min = wc - ww / 2 img = 255 * (img - min)/(max - min) img[img > 255] = 255 img[img < 0 ] = 0 img = img.astype(np.uint8) cv2.imwrite(tmpfnm, img) return img def getLabelName(fname): lblnm = [] for line in open(fname, 'r'): data = line.split(",") lblnm.append(data[1].strip()) #lblnm[int(data[0])]= data[1] print("LabelName:",lblnm) return len(lblnm), lblnm class Alexnet(Chain): ### AlexNet ### def __init__(self, n_label, n_channel=1): super(Alexnet, self).__init__( conv1 = L.Convolution2D(n_channel, 96, 11, stride=4), conv2 = L.Convolution2D(96, 256, 5, pad=2), conv3 = L.Convolution2D(256, 384, 3, pad=1), conv4 = L.Convolution2D(384, 384, 3, pad=1), conv5 = L.Convolution2D(384, 256, 3, pad=1), fc6 = L.Linear(None, 4096), fc7 = L.Linear(4096, 4096), fc8 = L.Linear(4096, n_label), ) def __call__(self, x): h = F.max_pooling_2d(F.local_response_normalization( F.relu(self.conv1(x))), 3, stride=2) h = F.max_pooling_2d(F.local_response_normalization( F.relu(self.conv2(h))), 3, stride=2) h = F.relu(self.conv3(h)) h = F.relu(self.conv4(h)) h = F.max_pooling_2d(F.relu(self.conv5(h)), 2, stride=2) h = F.dropout(F.relu(self.fc6(h))) h = F.dropout(F.relu(self.fc7(h))) return self.fc8(h) class Classifier(): def __init__(self, mdlnm, nlbl): model = Alexnet(nlbl) self.model = L.Classifier(model,lossfun=F.softmax_cross_entropy) self.opt = optimizers.Adam() self.opt.setup(self.model) self.modelName = mdlnm def doTrain(self, nLbl, train, batch_size, n_epoch, modelName): trn, vld = split_dataset_random(train, int(len(train)*0.8), seed=0) t_iterator = iterators.SerialIterator(trn, batch_size, shuffle=True) v_iterator = iterators.SerialIterator(vld, batch_size, repeat=False, shuffle=False) updater = training.StandardUpdater(t_iterator, self.opt) trainer = training.Trainer(updater, (n_epoch, 'epoch'), out='result') trainer.extend(extensions.Evaluator(v_iterator, self.model)) trainer.extend(extensions.LogReport()) trainer.extend(extensions.PrintReport(['epoch', 'main/loss', 'main/accuracy', 'validation/main/accuracy'])) trainer.extend(extensions.ProgressBar()) trainer.extend(extensions.PlotReport(['main/loss', 'validation/main/loss'], x_key='epoch', file_name='loss.png')) trainer.extend(extensions.PlotReport(['main/accuracy', 'validation/main/accuracy'], x_key='epoch', file_name='accuracy.png')) trainer.run() print("学習モデル",self.modelName,"を保存します... . .") chainer.serializers.save_hdf5( self.modelName, self.model ) print("\t... . .学習モデルを保存しました") if __name__=='__main__': trainfnm = "./TrainTestList/TrainData.0.3.csv" labelfnm = "./TrainTestList/labelName.csv" resize = 128 #画像リサイズ 0のときそのままの画素サイズ modelnm = "Alexnet.0.3.hdf5" #モデル保存名 batch_size = 10 # バッチサイズ n_epoch = 30 # エポック数 nLbl, lblName = getLabelName(labelfnm) train = constractTrainData(trainfnm, resize) print("\ntrain\n") print("画像データ数:",len(train),"画像/ラベル:",len(train[0])) print("画像Chanel数:",len(train[0][0]),"高さ:",len(train[0][0][0]),"幅:",len(train[0][0][0][0])) dl = Classifier(modelnm, nLbl) dl.doTrain(nLbl, train, batch_size, n_epoch, modelnm)
実行結果
プログラムを実行すると,最初にラベル名のファイルを読み込みLungなど4つのラベル名を表示します。
その後DICOM画像を読み込み,画像ファイル名とラベル番号が表示されます。
そして,学習が始まります。
そして,学習が始まります。
今回,検証用のvalidation/main/accuracyの項目の値も一緒に表示されています。
$ python␣dicomAlexnetTrainExtd.py⏎ LabelName: ['Lung', 'Abdomen', 'Chest', 'Head'] Train 0 ../dcmdir2/Lung/W5305890 Label# 0 Train 1 ../dcmdir2/Lung/K5303593 Label# 0 Train 2 ../dcmdir2/Lung/R5305640 Label# 0 Train 3 ../dcmdir2/Lung/T5304000 Label# 0 Train 4 ../dcmdir2/Lung/A5304343 Label# 0 Train 5 ../dcmdir2/Lung/D5304468 Label# 0 ・・・・・ Train 137 ../dcmdir2/Head/D5221046 Label# 3 Train 138 ../dcmdir2/Head/F5221140 Label# 3 Train 139 ../dcmdir2/Head/G5221187 Label# 3 Train 140 ../dcmdir2/Head/Q5221671 Label# 3 Train 141 ../dcmdir2/Head/E5221093 Label# 3 train 画像データ数: 73 画像/ラベル: 2 画像Chanel数: 1 高さ: 128 幅: 128 epoch main/loss main/accuracy validation/main/accuracy 1 3.06279 0.341667 0.481481 2 1.19522 0.536364 0.725926 3 0.943893 0.6 0.725926 4 0.729782 0.675 0.7 5 0.612126 0.7 0.488889 ・・・・・ 26 0.0378268 0.981818 1 27 0.00758042 1 1 28 0.0108063 0.990909 1 29 0.0802395 0.981818 1 30 0.0146818 0.990909 1 学習モデル Alexnet.0.3.hdf5 を保存します... . . ... . .学習モデルを保存しました
resultディレクトリの中に精度と損失の変化を記録した画像ファイルが保存されます。
学習の進捗をグラフに自動的にプロットできるのはとても助かります。
精度の変化を観察できる。
損失の変化を観察できる。
まとめ
TrainerのExtensionを使った学習の進捗を観察する方法を説明しました。
Extensionにはまだたくさんの機能が提供されています。
たとえば,学習途中のスナップショットを保存したり,学習率をコントロールしたりすることができます。
いろいろ調べて使いこなせるようになると,より最適な学習が可能になると思います。
0 件のコメント:
コメントを投稿