Amazon SageMakerの物体検出で必要な教師データを作る

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

機械学習の物体検出のモデルを学習させるためには、画像と画像内のどこに何があるかといった情報が書かれたメタデータが必要になります。

Amazon SageMakerの物体検出の学習時の入力データは「画像+json形式のメタデータ」と「RecordIO形式のデータ」の2パターンに対応しています。しかし、ハイパーパラメータのチューニングを行う際の入力データはRecordIO形式でないといけません。

当エントリは、必要に駆られてRecordIO形式のデータを作る方法を調べたりして、足掻いた際に分かったこと等をまとめたものです。

内容

教師データを作成する流れとしてはこんな感じです。

  1. 教師画像のラベル情報を持ったメタデータを作成(=アノテーション)
  2. メタデータをlst形式に変換(json形式で学習させる場合はjson形式に変換)
  3. リスト形式のメタデータをRecordIO形式に変換(RecordIO形式で学習させる場合のみ)

※lst形式のメタデータは物体検出で使う場合と、画像分類で使う場合とでは異なったフォーマットなので注意が必要です。画像分類の場合はこちらのAWSのドキュメントに記載されているフォーマットで、物体検出の場合はMXNetのドキュメントに記載されているフォーマットになります。

では、以上の流れについて順に説明していきます。

教師画像のアノテーションを行う

アノテーションは画像を一つずつ確認して、画像内の学習させたい物に対してラベル付けを行い、メタデータを作成することです。 モデルを学習させるためには一つのラベルに対して、数百単位の教師データが必要になるため、機械学習のために自らが機械のようになり、ひたすら画像のラベル付けを行う必要があります。

そんな苦行を少しでも楽にするためにアノテーションツールがあります。ここではアノテーションツールを紹介しているところを紹介するに留めておきます。

上で紹介されているような何かしらのアノテーションツールを使ってラベル貼りを行い、メタデータ作成を行います。どの形式のメタデータにするかは注意が必要です。アノテーションを行なった後に別の形式にする必要が出た場合は変換スクリプトを作るか、アノテーションのやり直しが必要になってしまいます...

メタデータをlst/json形式に変換するスクリプトを作成し、変換する

json形式のメタデータと画像データでモデルを学習させる場合

json形式のメタデータで学習させる場合はjson形式に変換するスクリプトを作成し、変換します。
VoTTでアノテーションを行った場合は、以下のスクリプトで変換を行えます。

#coding:utf-8

"""
VoTTで作成したメタデータ(json形式)から
Amazon SageMakerの物体検出の学習時に使う教師画像データのメタデータ(json形式)へ変換します。
参考: https://dev.classmethod.jp/cloud/aws/sagemaker-umaibo-object-detection/
"""

import json


# VoTTで出力されたjsonファイル
input_file_name = './in.json'

# lstファイルの出力先
output_file_path= './out/'

# 検出したいクラスとそのid
class_list = {'hoge':0, 'hoge1':1, 'hoge2':2}

with open(input_file_name) as input_f:
    js = json.load(input_f)

    for k, v in js['frames'].items():

        k = int(k)
        line = {}
        line['file'] = '{0:04d}'.format(k+1) + '.jpg'
        line['image_size'] = [{
            'width':int(v[0]['width']),
            'height':int(v[0]['height']),
            'depth':3
        }]

        line['annotations'] = []

        for annotation in v:

            line['annotations'].append(
                {
                    'class_id':class_list[annotation['tags'][0]],
                    'top':int(annotation['y1']),
                    'left':int(annotation['x1']),
                    'width':int(annotation['x2'])-int(annotation['x1']),
                    'height':int(annotation['y2']-int(annotation['y1']))
                }
            )

        line['categories'] = []

        for name, class_id in class_list.items():
            line['categories'].append(
                {
                    'class_id':class_id,
                    'name':name
                }
            )

        with open(output_file_path+'{0:04d}'.format(k+1) + '.json', 'w') as out_f:
            json.dump(line, out_f)

json形式のメタデータと画像でモデルを学習させる場合は、これで教師データ作成完了です。

RecordIO形式のデータでモデルを学習させる場合

RecordIO形式のデータで学習させる場合は、先ほど作成したメタデータをlst形式に変換する必要があります。 そのためにはスクリプトを作成して、そのスクリプトを使ってメタデータの変換を行います。 VoTTでアノテーションを行った場合は、以下のスクリプトで変換を行えます。

# coding:utf-8
"""
VoTTで作成したメタデータ(json形式)から
Amazon SageMakerの物体検出の学習時に使う教師画像データのメタデータ(lst形式)へ変換します。
"""


import json
from decimal import Decimal

def divide_decimal(num1, num2):
    return (Decimal(num1) / Decimal(num2))

def create_lst(file_path, index, annotations, class_list):
    """
    lst形式のデータを作成
    """
    header_size = 2
    label_width = 5
    annotation_data = ['\t'.join([
        str(class_list[cl['tags'][0]]),
        str(divide_decimal(cl['x1'], cl['width'])),
        str(divide_decimal(cl['y1'], cl['height'])),
        str(divide_decimal(cl['x2'], cl['width'])),
        str(divide_decimal(cl['y2'], cl['height']))
    ]) for cl in annotations]
    return '\t'.join([
        str(index),
        str(header_size),
        str(label_width),
        '\t'.join(annotation_data),
        file_path])

# VoTTで出力されたjsonファイル
file_name = './input.json'

# lstファイルの出力先
out_file_name = './output.lst'

# 検出したいクラスとそのid
class_list = {'hoge':0, 'hoge1':1, 'hoge2':2}

# .lst形式のアノテーションファイルの中身をリストで作成する
out_content_list = []
with open(file_name) as in_f:
    js = json.load(in_f)

    for k, v in sorted(js['frames'].items(), key=lambda frame:int(frame[0])):
        k = int(k)
        img_file_name = '{0:04d}'.format(k+1)

        lst_data = create_lst(img_file_name+'.jpg', k+1, v, class_list)
        print(lst_data)
        out_content_list.append(lst_data)

# 作成したデータを要素ごとに改行して書き出す
with open(out_file_name, 'w') as out_f:
    out_f.write('\n'.join(out_content_list))

lst形式のメタデータをRecordIO形式に変換する

メタデータをlst形式からRecordIO形式にim2rec.pyというスクリプトを使って変換します。im2recはMXNetが公開しているスクリプトで、lst形式からRecordIO形式に変換したり、画像分類で使うためのlst形式のデータを作成したりできます。

以下のコードを実行することでlst形式のメタデータと画像データからRecordIO形式のバイナリデータを作成できます。

python im2rec.py lstファイルのパス 画像データが入っているディレクトリのパス

※データ量が多い場合は--num-thread 数字を付けてスレッド数を指定すると並列で処理してくれるので、速くなります。

さいごに

Amazon SageMakerでの物体検出を行う際に必要となる教師データの作成方法について紹介しました。

今後、触ってみようと思っている方の参考になれば幸いです。

最後までお読み頂きありがとうございました。

参考