GluonCVのpretrained modelについて

学習済みモデル

学習済みモデルを使うことは非常に簡単

from gluoncv.model_zoo import get_model
net = get_model('ResNet50_v2', pretrained=True)

「ImageNet」データセットを使って学習済みとのこと。

>>> len(net.classes)
1000

1000クラス?
「ImageNet」とは言っても「ILSVRC2012」データセットを使っていると思われる。
「ImageNet」と「ILSVRC2012」についてはこちらを参照させて頂いた。
starpentagon.net
「ILSVRC2012」データセットのクラスは動物が多く、人間が含まれない。

モデルの構成

「features」と「output」に分けられる。

>>> a = mx.nd.ones(shape=(1,3,244,244))
>>> out = net(a)
>>> out1 = net.features(a)
>>> out2 = net.output(out1)
>>> import numpy as np
>>> np.array_equal(out.asnumpy(), out2.asnumpy())  
True

「output」部分を変更してfine-tuneを行う。
「features」部分のパラメータを固定する場合には初期化後に以下の一文を加える。

finetune_net.features.collect_params().setattr('grad_req', 'null')

画像の前処理

pretrained modelを試す場合には画像の前処理は以下のように行う。

from mxnet import image
from gluoncv.data.transforms.presets.imagenet import transform_eval
img = image.imread('COCO_train2014_000000329134.jpg')
img = transform_eval(img)

なかみは以下と同様である

from mxnet import image
from mxnet.gluon.data.vision import transforms

transform_all = transforms.Compose([
        transforms.Resize(256, keep_ratio=True),
        transforms.CenterCrop(224),
        transforms.ToTensor(),
        transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))
    ])

img = image.imread('COCO_train2014_000000329134.jpg')
img = transform_all(img)
img = img.expand_dims(0)

GluonCVのチュートリアルでは訓練時のData Augmentation(データの水増し)として以下を使っている。

jitter_param = 0.4
lighting_param = 0.1

transform_train = transforms.Compose([
    transforms.RandomResizedCrop(224),
    transforms.RandomFlipLeftRight(),
    transforms.RandomColorJitter(brightness=jitter_param, contrast=jitter_param,
                                 saturation=jitter_param),
    transforms.RandomLighting(lighting_param),
    transforms.ToTensor(),
    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])

ToTensor以降に「uint8」から「float32」に変わるためサイズが大きくなる。
学習する時にメモリに余裕がなければ作業を2回に分けて前半終了時点のデータをメモリにのせるのが良いのではないか?
後半はバッチ毎に毎回処理するので無駄が増えるが・・・。
すべて未処理のままメモリにのせるのも良いが画像を縮小していないのでそれはそれでサイズが大きい。
つまりは最初に画像を縮小しておけば良いのではと思う。

transform_1st = transforms.Compose([
        transforms.Resize(256, keep_ratio=True),
        transforms.CenterCrop(224),
        #transforms.ToTensor(),
        #transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))
    ])

transform_2nd = transforms.Compose([
        #transforms.Resize(256, keep_ratio=True),
        #transforms.CenterCrop(224),
        transforms.ToTensor(),
        transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))
    ])

ちなみにMXNetではRecordIOという形式が推奨されている。(これに関しては不勉強)

画像の縮小

いろいろ方法はあるがgluonを使用して事前学習を忠実に再現する。
JPEGで保存すると圧縮の問題かどうかわからないが後で読み込んだ時に再現出来なかったのでPNGで保存。

import os
from PIL import Image
from mxnet import image
from mxnet.gluon.data.vision import transforms

transform_1st = transforms.Compose([
        transforms.Resize(256, keep_ratio=True),
        transforms.CenterCrop(224),
        #transforms.ToTensor(),
        #transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))
    ])

image_dir = 'train2014'
output_dir = 'resize_img'

if not os.path.exists(output_dir):
    os.makedirs(output_dir)

images = os.listdir(image_dir)

num_images = len(images)

for i, filename in enumerate(images):
    name, ext = os.path.splitext(filename)
    new_name = name + '.png'
    img = image.imread(os.path.join(image_dir, filename))
    img = transform_1st(img)
    Image.fromarray(img.asnumpy()).save(os.path.join(output_dir, new_name))

    if (i+1) % 100 == 0:
        print ("[{}/{}] Resized the images and saved into '{}'.".format(i+1, num_images, output_dir))

以下個人的メモ

以下のような使い方を想定している。
まずは画像をバイナリデータとして読み込んでおく。
学習する直前にNDArrayに変換して正規化する。

transform_2nd = transforms.Compose([
        #transforms.Resize(256, keep_ratio=True),
        #transforms.CenterCrop(224),
        transforms.ToTensor(),
        transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))
    ])

def image_transform(img):
    post = image.imdecode(img)
    post = transform_2nd(post)
    post = post.expand_dims(0)
    return post

with open('D:/downloads/data/resize_img/COCO_train2014_000000579156.png', 'rb') as f:
    img1 = f.read()
with open('D:/downloads/data/resize_img/COCO_train2014_000000000151.png', 'rb') as f:
    img2 = f.read()

image_list = []

image_list.append(image_transform(img1))
image_list.append(image_transform(img2))

result = mx.nd.concat(*image_list, dim=0)