Edited at

Chainer に IoU評価関数 を追加する(Evaluatorを自作)


更新履歴


  • 2018/11/17 集計時に誤差が発生するバグを修正しました


はじめに

こんにちは、のんびりエンジニアのたっつーです。

ブログを運営しているのでよろしければ見てください。

Chainer に IoU の評価関数を追加したかったので Evaluator を自作してみました。以下にように、学習中ログに IoU が追加して表示されるようになります。

詳細は、Chainer に IoU評価関数 を追加する(Evaluatorを自作) をご参照ください。

epoch       iou           main/loss   main/accuracy  ...

1 0 0.042866 0.993138 ...
2 0.000329707 0.0347965 0.993241 ...
3 0.00700626 0.0307309 0.993857 ...


使用例


iouのログを追加

trainer.extend(IouEvaluator(test_iter, model, device=gpu_id))


画面にiouを表示

trainer.extend(extensions.PrintReport(['epoch', 'iou', 'main/loss', 'main/accuracy', 'validation/main/loss', 'validation/main/accuracy', 'elapsed_time']))


ファイルにiouグラフを保存する

trainer.extend(extensions.PlotReport(['iou'], x_key='epoch', file_name='iou.png'))


ソースコード


IouEvaluator.py 本体


IouEvaluator.py

import chainer

from chainer import reporter as reporter_module
from chainer.training import extensions
from chainer import function
import numpy as np

class IouEvaluator(extensions.Evaluator):

def evaluate(self):
iterator = self._iterators['main']
model = self._targets['main']
eval_func = self.eval_func or model

if self.eval_hook:
self.eval_hook(self)

if hasattr(iterator, 'reset'):
iterator.reset()
it = iterator
else:
it = copy.copy(iterator)

summary = reporter_module.DictSummary()

and_count = 0.
or_count = 0.

for batch in it:
observation = {}

with reporter_module.report_scope(observation):
in_arrays = self.converter(batch, self.device)
with function.no_backprop_mode():
if isinstance(in_arrays, tuple):
eval_func(*in_arrays)
ac, oc = self.iou(in_arrays)
elif isinstance(in_arrays, dict):
eval_func(**in_arrays)
ac, oc = self.iou(in_arrays)
else:
eval_func(in_arrays)
ac, oc = self.iou(in_arrays)
and_count = and_count + ac
or_count = or_count + oc

# print(observation)
summary.add(observation)

iou_observation = {}
if(or_count == 0):
iou_observation['iou'] = 0.
else:
iou_observation['iou'] = float(and_count) / or_count
summary.add(iou_observation)

return summary.compute_mean()

def iou(self, in_arrays):
model = self._targets['main']

_, labels = in_arrays
if self.device >= 0:
labels = chainer.cuda.to_cpu(labels)

y = model.y.data
if self.device >= 0:
y = chainer.cuda.to_cpu(y)
# print(y)
y = y.argmax(axis=1)

# print('labels', labels)
# print('predct', y)
and_count = (labels & y).sum()
or_count = (labels | y).sum()
return and_count, or_count



呼び出し側

def train(model_object, batchsize=64, gpu_id=0, max_epoch=20, dataset_func=None):

# 1. Dataset
dataset_name=dataset_func.__name__
train, test = dataset_func()
print('train=' + str(len(train)) + ' test=' + str(len(test)))
# print(train)
img, label = train[0]
print(img.shape)

# 2. Iterator
train_iter = iterators.SerialIterator(train, batchsize)
test_iter = iterators.SerialIterator(test, batchsize, False, False)

# 3. Model
model = L.Classifier(model_object)
if gpu_id >= 0:
model.to_gpu(gpu_id)

# 4. Optimizer
optimizer = optimizers.Adam()
optimizer.setup(model)

# 5. Updater
updater = training.StandardUpdater(train_iter, optimizer, device=gpu_id)

# 6. Trainer
model_name = model_object.__class__.__name__
outdir = '{}_{}'.format(model_name, dataset_name)
if not os.path.exists(outdir):
os.makedirs(outdir)

epoch_npz = '{}_{}_{}.npz'.format(model_name, dataset_name, '{.updater.epoch}')
final_npz = '{}_{}_{}.npz'.format(model_name, dataset_name, 'fin')
# print(epoch_npz)
# print(final_npz)

trainer = training.Trainer(updater, (max_epoch, 'epoch'), out=outdir)

# 7. Evaluator
# TestModeEvaluator

trainer.extend(extensions.LogReport())
trainer.extend(IouEvaluator(test_iter, model, device=gpu_id))
# trainer.extend(SemanticSegmentationEvaluator(test_iter, model, label_names=['negative','positive']))
trainer.extend(extensions.PrintReport(['epoch', 'iou', 'main/loss', 'main/accuracy', 'validation/main/loss', 'validation/main/accuracy', 'elapsed_time']))
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.extend(extensions.PlotReport(['iou'], x_key='epoch', file_name='iou.png'))
trainer.extend(extensions.snapshot_object(model, epoch_npz))
trainer.run()
del trainer

# save model
serializers.save_npz(outdir + '/' + final_npz, model)

return model


終わりに

よければ ブログ「初心者向けUnity情報サイト」の方にも色々記載しているのでぜひご参照いただければと思います。