自前のDeep Learning用のデータセットを拡張して水増しする


データセットを拡張する

需要が高まり、かつ、フレームワークも増えてきて簡単に手が出せるようになってきたDeep Learning
ですが、
データセットを用意するのが面倒!

数千、数万のデータが必要なので、手書き文字のように誰かが用意したものでないと集収がとても大変です。そのため、少ない画像から複数の画像に拡張して増やしてしまえ!と言う仕組みが用意されています。

筆者自身も自前でデータセットを用意する必要があって調べていたので、メモも兼ねて記します。
誤りやもっといい方法があれば是非ご教授ください!

 
筆者も自身の手を1,000枚以上撮影する苦行をしましたが、
この拡張による水増しで54,000枚まで増やした結果ディレクトリ内がちょっとしたホラーです。
知人に見られて病んでないか心配されました。

※画像は拡張前

実行環境

筆者の環境は以下の通りです。
確実に動作させたければanacondaなりpyenvなりで環境を作ってそちらから動かしてください。

Python 3.5.3
Keras==2.0.4
numpy==1.12.1
tensorflow==1.0.0

kerasTensor Flowバックエンドで動かしてます。

Kerasって?

Kerasについてざっくり知りたければ、過去記事を見て頂ければと思います。
本当にざっくりなので、より詳しく知りたい場合は公式ドキュメント等をどうぞ

今回かこのKerasを用いますが、
あくまでデータセットを用意する目的なのでkerasはもちろん、機械学習でもchainerでもTensor Flowでもcaffeでもお好きなフレームワーク用にお使いください。
おそらくどのフレームワークにも同じような機能が用意されているとは思いますが、調べるのが面倒ですぐ増やしたい方はそのまま下記のコードをお試しください。

ソースコード

データセットのディレクトリ内で動かすだけでデータセットを拡張して別ディレクトリに保存します。
1枚から何枚に拡張するか、出力ディレクトリ名等、用途に合わせてお使いください。

デフォルトだと、カレントディレクトリ内の全てのjpgファイルを各10枚分拡張し、./extenedに出力します(なければ作成されます)。

dataset_generator.py
import os
import glob
import numpy as np
from keras.preprocessing.image import ImageDataGenerator, load_img, img_to_array, array_to_img

def draw_images(generator, x, dir_name, index):
    # 出力ファイルの設定
    save_name = 'extened-' + str(index)
    g = generator.flow(x, batch_size=1, save_to_dir=output_dir, save_prefix=save_name, save_format='jpg')

    # 1つの入力画像から何枚拡張するかを指定
    # g.next()の回数分拡張される
    for i in range(10):
        bach = g.next()


if __name__ == '__main__':

    # 出力先ディレクトリの設定
    output_dir = "extended"
    if not(os.path.exists(output_dir)):
        os.mkdir(output_dir)

    # 拡張する画像群の読み込み
    images = glob.glob(os.path.join('./', "*.jpg"))

    # 拡張する際の設定
    generator = ImageDataGenerator(
                    rotation_range=90, # 90°まで回転
                    width_shift_range=0.1, # 水平方向にランダムでシフト
                    height_shift_range=0.1, # 垂直方向にランダムでシフト
                    channel_shift_range=50.0, # 色調をランダム変更
                    shear_range=0.39, # 斜め方向(pi/8まで)に引っ張る
                    horizontal_flip=True, # 垂直方向にランダムで反転
                    vertical_flip=True # 水平方向にランダムで反転
                    )

    # 読み込んだ画像を順に拡張
    for i in range(len(images)):
        img = load_img(images[i])
        # 画像を配列化して転置a
        x = img_to_array(img)
        x = np.expand_dims(x, axis=0)
        # 画像の拡張
        draw_images(generator, x, output_dir, i)

カスタマイズしよう

  generator = ImageDataGenerator(
                  rotation_range=90,
                  width_shift_range=0.1,
                  height_shift_range=0.1,
                  channel_shift_range=50.0,
                  shear_range=0.39,
                  zoom_range=0.2,
                  horizontal_flip=True,
                  vertical_flip=True
                  )

この部分を書き換えることで、様々な拡張ができます。
用途に合わせてカスタマイズしてください。

引数の種類と説明は以下の通りです。
ドキュメントとほぼほぼ同じ解説です、あしからず...

  • 使いやすい、わかりやすいもの

    • rotation_range: 整数。画像をランダムに回転する回転範囲(0-180)
    • width_shift_range: 浮動小数点数(横幅に対する割合)。指定した範囲内をランダムに水平シフトします。
    • height_shift_range: 浮動小数点数(縦幅に対する割合)。指定した範囲内をランダムに垂直シフトします。
    • shear_range: 浮動小数点数。シアー強度(反時計回りのシアー角度(ラジアン))。シアー変換を行います。簡単に説明すると指定した範囲内角度でランダムに引っ張ります。
    • zoom_range: 浮動小数点数または[lower,upper]。指定した値内の倍率でランダムにズームします。浮動小数点数が与えられた場合,[lower, upper] = [1-zoom_range, 1+zoom_range]です。
    • channel_shift_range: 浮動小数点数。範囲内でランダムにチャンネルをシフトします。色が変わります。
    • horizontal_flip: 真理値。水平方向に入力をランダムに反転させます。文字認識等には使ってはいけない禁忌の設定です。
    • vertical_flip: 真理値。垂直方向に入力をランダムに反転させます。文字認識等には使ってはいけない禁忌の設定です。
  • よくわからないもの(ご解説等頂けると助かります)

    • featurewise_center: 真理値。データセット全体で入力の平均を0にします。
    • samplewise_center: 真理値。 各サンプルの平均を0にします。
    • featurewise_std_normalization: 真理値。 入力をデータセットの標準偏差で正規化します。
    • samplewise_std_normalization: 真理値。各入力をその標準偏差で正規化します。
    • zca_whitening: 真理値。ZCA白色化を適用します。
    • zca_epsilon: ZCA白色化のイプシロン。デフォルトは1e-6。
    • fill_mode: {"constant", "nearest", "reflect", "wrap"}のいずれか。指定されたモードに応じて入力画像の境界周りを埋めます。
    • cval: 浮動小数点数または整数;fill_mode = "constant"のときに利用される値です。
    • rescale: rescale factor. デフォルトはNone、指定すれば他の変換を行う前に与えられた値をデータに積算します。
    • preprocessing_function: 各入力に適用される関数です。この関数は他の変更が行われる前に実行されます。この関数は三次元のnumpyテンソルを引数にとり、同じshapeのテンソルを出力するように定義する必要があります。
    • data_format: "channels_last"(デフォルト)か"channels_first"を指定します。 "channels_last"の場合、入力のshapeは(samples, height, width, channels)となり、"channels_first"の場合は(samples, channels, height, width)となります。デフォルトはKerasの設定ファイル~/.keras/keras.jsonのimage_data_formatの値です。一度も値を変更していなければ"channels_last"になります。

最後に

何度もしつこいようですが、まだまだ未熟者ですので是非色々とご教授ください!