こしあん
2020-03-04

ImageNetのVal精度を再現するための前処理を考える


4.4k{icon} {views}

ImageNetのValidation精度は論文でよく見ますが、その精度をどのようにして再現するのかがよくわからなかったので書きました。10-Cropをするのが最も簡単な方法です。

ImageNetのTop1精度

論文で「☓☓NetはImageNetでTop1精度○○%達成しました」みたいなのよく見ますが、訓練済みモデルが提供されていたとして、その精度を再現するにはどうすればよいのでしょう?

論文が間違っているということよりも、「実装がおかしくて本来達成されるはずの精度が達成できなかった」というのがよくあります。特にフレームワークを超えてサードパーティーの移植があった場合。提供されているモデルが、本当にその精度を達成できるのかを確認するためにはどうすればよいのかを見ていきます。

Top1精度とは

Top1精度とはサンプルあたり「予測したクラスが正しければ1、間違っていれば0」とし、それをサンプル間で平均を取ったものすごい単純なものです。

他にImageNetではTop5精度というのがよく書かれますが、Top5精度は「予測上位の5クラスの中にただしいクラスが入っていれば1、そうでなければ0」ということを示します。上位のソート基準はソフトマックスの出力(確率)で見ます。例えば、Ground Truthが猫だったとして、

  • 50% イルカ、20% シャチ、10% アヒル、8% チーター、5% 犬、3% 猫、残りその他
  • 50% イルカ、20% シャチ、10% アヒル、8% チーター、5% 猫、3% 犬、残りその他

だった場合、上のケースではTop5では間違いですが、下のケースではTop5では正解扱いです。それぞれ6番目、5番目で正解だからです。順位1位で正解しようが、5位で正解しようが全て同じ正解として扱います。

今回はTop1のみについて扱いますが、ImageNetのValデータ(5万枚)についてTop1精度を取ると論文と同じ値になるはずです。ImageNetを訓練することは個人では大変ですが、テストすることは5万枚を推論すればいいだけなのでそこまで難しくありません。

前処理について

そもそもImageNetのVal精度に対してどう前処理を施せばいいのでしょうか?例えば、ResNetの論文で指摘があります。

In testing, for comparison studies we adopt the standard 10-crop testing [21].

もともとはAlexNetの論文からだそう(21がAlexNetの論文引用)。「standard 10-crop」というのがキーであるのがわかります。

ResNetの論文ではこのあとに次のようなことも書かれています。

For best results, we adopt the fullyconvolutional form as in [41, 13], and average the scores at multiple scales (images are resized such that the shorter
side is in {224, 256, 384, 480, 640}).

41がVGGの論文引用です。より良い結果を出すにはアスペクト比を加味した前処理(TTA)も使うということが書かれていますが、今回は10-Cropのみ考えます。もしアスペクト比を加味していた場合は、前処理の多さの違いになるため、10-Cropで測って若干プラスするとBest Resultになるでしょう。今回はだいたいわかればいいんで、アスペクト比まではいいかなと思いました。

10 Cropとは

10 Cropとは何でしょうか? これはPyTorchのTransformのドキュメントで
“`orchvision.transforms.TenCrop“`という関数があります。

Crop the given PIL Image into four corners and the central crop plus the flipped version of these (horizontal flipping is used by default)

https://pytorch.org/docs/stable/torchvision/transforms.html#torchvision.transforms.TenCrop

とあります。「四辺をクロップして、中央をクロップした5個の切り出しについて、Horizontal Flipを入れて5×2の10パターンにする」というのが読めます。これはData AugmentationにおけるRandom Cropの確定的なバージョンなので、10 CropもData Augmentationの一種と読むことができます。実際、中央だけのCenter Crop1個よりも、10Cropで測ったほうが精度が若干上がります。

Data Augmentationをテスト時(Valも同等)をかけることは、Testing Time Augmentation(TTA)というので、論文で出てきているImageNetの精度は実はTTAをかけていたということがわかります。この事実はちょっと意外ではないでしょうか?(自分も読んだときえっ?ってなりました)

10 CropのTensorFlowでの実装

さて、PyTorch(TorchVision)にはTenCropの関数がありますが、TensorFlow、もといNumpyで実装するにはどうすればいいでしょうか?

import numpy as np
import tensorflow as tf
import glob
from tqdm import tqdm
from tensorflow.keras.applications import resnet

def create_dataset_tencrop():
    dirs = sorted(glob.glob("ImageNet/imagenet/val/*")) # ImageNetのディレクトリ
    labels = []
    images = []
    for i, d in tqdm(enumerate(dirs), total=1000):
        image_paths = sorted(glob.glob(d + "/*.jpeg"))
        for img_path in image_paths:
            with Image.open(img_path) as img:
                img = img.convert("RGB")
                img = img.resize((256, 256), Image.BICUBIC)
                x = np.asarray(img, np.uint8)
                images.append(x)
                labels.append(i)

    labels = np.asarray(labels, np.int32) # (50000, 256, 256, 3)
    images = np.asarray(images, np.uint8) # (50000,)
    return images, labels

def eval_resnet_tencrop():
    base_images, labels = create_dataset_tencrop()
    all_probs = []

    inputs = tf.keras.layers.Input((224,224,3))
    model = resnet.ResNet50(input_tensor=inputs) # 適当なネットワーク

    for i in range(10):
        if i//2 == 0:
            img = base_images[:, :-32, :-32, :]
        elif i//2 == 1:
            img = base_images[:, 32:, :-32, :]
        elif i//2 == 2:
            img = base_images[:, :-32, 32:, :]
        elif i//2 == 3:
            img = base_images[:, 32:, 32:, :]
        else:
            img = base_images[:, 16:-16, 16:-16, :]

        if i % 2 == 1:
            img = img[:,:,::-1,:]

        pred_probs = []
        for j in tqdm(range(2000)):
            x = img[j*25:(j+1)*25]
            x = x.astype(np.float32)
            x = resnet.preprocess_input(x)
            p = model(x, training=False)
            pred_probs.append(p)

        all_probs.append(np.concatenate(pred_probs, axis=0))
        del img

    all_probs = np.mean(np.stack(all_probs, axis=-1), axis=-1)
    pred_labels = np.argmax(all_probs, axis=-1).astype(np.int32)

    print("resnet top 1 ten crop: ", np.mean(pred_labels==labels))

if __name__ == "__main__":
    eval_resnet_tencrop()

ResNetなので入力サイズは224×224で考えます。256×256でリサイズしておいて、224×224となるように10-Cropしているというわけです。10-CropでResNet50について計測したところ、Top1は74.2%となりました。Kerasの公式ドキュメントによると公称スペックがTop1が75.9%だそうです。アスペクト比も加味するとここまで上がるのかもしれません。

256×256でリサイズして、左上(16, 16)、右下(240, 240)でCenter Cropすると73.1%となりました。TTAで1%程度良くなるというわけですね。

ここらへんが10-Cropのメインです。

    for i in range(10):
        if i//2 == 0:
            img = base_images[:, :-32, :-32, :] # 左上
        elif i//2 == 1:
            img = base_images[:, 32:, :-32, :] # 右上
        elif i//2 == 2:
            img = base_images[:, :-32, 32:, :] # 左下
        elif i//2 == 3:
            img = base_images[:, 32:, 32:, :] # 右下
        else:
            img = base_images[:, 16:-16, 16:-16, :] # 中央

        if i % 2 == 1:
            img = img[:,:,::-1,:] # Horizontal flip(左右反転)

base_imagesのサイズが256×256です。imgのサイズは全て224×224です。

2ループ単位、順に左上、右上、左下、右下、中央のクロップになります。これは入力テンソルが(Batch, 縦, 横, カラーチャンネル)のフォーマットだからです。奇数の場合だけ左右反転しています。横の反転の場合3番目の軸です。

ImageNetの精度の再現を自分が意識するようになったのは、Efficient Netの係数移植のためだったのですが、Efficient Netでは10-Cropだけでほぼ公称性能取れました。なので、もしかするとネットワークによって微妙な違いがあるのかもしれないです。

まとめ

ImageNetのValidation精度を再現するにはとりあえず10-Cropしておけばよさそう。

宣伝

技術書典8の新刊『モザイク除去から学ぶ 最先端のディープラーニング』好評通販中です! 試し読みもできるのでよろしくね!



Shikoan's ML Blogの中の人が運営しているサークル「じゅ~しぃ~すくりぷと」の本のご案内

技術書コーナー

北海道の駅巡りコーナー


Add a Comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です