LoginSignup
5
3

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-11-14

更新履歴

  • 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情報サイト」の方にも色々記載しているのでぜひご参照いただければと思います。

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3