画像処理とか機械学習とか

画像処理や機械学習関連の事について気まぐれで書いていきます。歩行者検出関係が多いと思います。ハリネズミもたまに出現します。

chainerのimagenetサンプルで好きな画像サイズで入力する方法

chainerのサンプルにあるimagenetは,1000クラスの大規模画像分類用に設計されたネットワークである,Network In Network や,AlexNet, GoogLeNetなどを使うことが出来ます。

入力画像のサイズは256×256となっており,train_imagenet.pyの学習プログラムの内部でランダムにクリッピングをすることで,学習データを増やし,多少のズレに頑健なネットワークの学習が可能となっています。

このサンプルソースを使って,自分で用意した学習データを使って学習させたい場合,入力画像のサイズや,アスペクト比が違うこともあると思います。

そういう場合に、どこを変更すれば簡単に動くようになるかを紹介したいと思います。

まず、train_imagenet.pyのプログラムから
PreprocessedDatasetクラスの変更部分がこちらです。

class PreprocessedDataset(chainer.dataset.DatasetMixin):

    def __init__(self, path, root, mean, crop_size_x, crop_size_y random=True):
        self.base = chainer.datasets.LabeledImageDataset(path, root)
    self.mean = mean
        self.crop_size_x = crop_size_x
        self.crop_size_y = crop_size_y
        self.random = random

    def __len__(self):
        return len(self.base)

    def get_example(self, i):
        # It reads the i-th image/label pair and return a preprocessed image.
        # It applies following preprocesses:
        #     - Cropping (random or center rectangular)
        #     - Random flip
        #     - Scaling to [0, 1] value
        crop_size_x = self.crop_size_x
        crop_size_y = self.crop_size_y

        image, label = self.base[i]
        _, h, w = image.shape

        if self.random:
            # Randomly crop a region and flip the image
            top = random.randint(0, h - crop_size_y - 1)
            left = random.randint(0, w - crop_size_x - 1)
            if random.randint(0, 1):
                image = image[:, :, ::-1]
        else:
            # Crop the center
            top = (h - crop_size_y) // 2
            left = (w - crop_size_x) // 2
        bottom = top + crop_size_y
        right = left + crop_size_x

        image = image[:, top:bottom, left:right]
        image -= self.mean[:, top:bottom, left:right]
        image /= 255
        return image, label

このクラスは、初期化を行う部分と,データセットを読み込む際にデータをクリッピングする操作が含まれています。
元のプログラムは正方形の256×256ピクセルの画像が入力される前提となっているため,縦長や横長の画像に対応するために
crop_sizeにcrop_size_xとcrop_size_yを加え、修正しています。

次はTestModeEvaluatorクラスです。
まず、meanファイルとデータセット読み込みの部分で、先ほどcrop_size_xとcrop_size_yを加えた部分の修正をこちらでも行います。
model.insizeは、モデルファイルの中に書かれている入力画像のサイズを取得している部分です。
縦横違うサイズの場合は変更、もしくは追加する必要があります。

 # Load the datasets and mean file
    mean = np.load(args.mean)
    train = PreprocessedDataset(args.train, args.root, mean, model.insize_x, model.insize_y)
    val = PreprocessedDataset(args.val, args.root, mean, model.insize_x, model.insize_y False)

次にモデルファイルです。
今回は例としてNetwork In Networkを用います。

import math

import chainer
import chainer.functions as F
import chainer.links as L


class NIN(chainer.Chain):

    """Network-in-Network example model."""

    insize_x = 227 #横の入力画像サイズ(クリッピング後のサイズ)
    insize_y = 227 #縦の入力画像サイズ(クリッピング後のサイズ)

    #入力画像サイズに合わせて、畳み込みなどのフィルタサイズを変更してください
    def __init__(self):
        w = math.sqrt(2)  # MSRA scaling
        super(NIN, self).__init__(
            mlpconv1=L.MLPConvolution2D(
                None, (96, 96, 96), 11, stride=4, wscale=w),
            mlpconv2=L.MLPConvolution2D(
                None, (256, 256, 256), 5, pad=2, wscale=w),
            mlpconv3=L.MLPConvolution2D(
                None, (384, 384, 384), 3, pad=1, wscale=w),
            mlpconv4=L.MLPConvolution2D(
                None, (1024, 1024, 1000), 3, pad=1, wscale=w),
        )
        self.train = True

    def __call__(self, x, t):
        h = F.max_pooling_2d(F.relu(self.mlpconv1(x)), 3, stride=2)
        h = F.max_pooling_2d(F.relu(self.mlpconv2(h)), 3, stride=2)
        h = F.max_pooling_2d(F.relu(self.mlpconv3(h)), 3, stride=2)
        h = self.mlpconv4(F.dropout(h, train=self.train))
        h = F.reshape(F.average_pooling_2d(h, 6), (x.data.shape[0], 1000))

        loss = F.softmax_cross_entropy(h, t)
        chainer.report({'loss': loss, 'accuracy': F.accuracy(h, t)}, self)
return loss