少ない枚数で画像分類のデータ拡張を試す(ImageDataGenerator)
AIで画像分類を実現したいけれども、学習用の画像データを用意するのが大変・・・。
そこで「水増し」と言われているデータ拡張を少し試してみたメモです。
データ拡張(Data augmentation)とは
TensorFlowのチュートリアルに掲載されている画像のように、1枚の画像から反転させた画像や回転させた画像などを生成することです。
水増し画像の作成
後の違いを見たいので水増しは雑に用意
300枚ほどの画像をなんとか用意
tf.kerasのImageDataGeneratorを利用
rotation_range: 画像をランダムに回転
width_shift_range: ランダムに水平シフト
height_shift_range: ランダムに垂直シフト
zoom_range: ランダムにズーム
image_gen_train = ImageDataGenerator(
rescale=1./255,
rotation_range=45,
width_shift_range=.15,
height_shift_range=.15,
zoom_range=0.5)
クラス毎のファイル毎に水増し
水増ししたファイルを保存(先にディレクトリは作成済み)
ex_num=1000
for _, label in enumerate(os.listdir(org_dir)):
if label == '.DS_Store':
continue
print(os.path.join(org_dir, label))
target_dir = os.path.join(org_dir, label)
target_files = glob.glob(target_dir + '/*.jpg')
for _, file in enumerate(target_files):
img = load_img(file)
x = img_to_array(img)
x = np.expand_dims(x, axis=0)
train_data_gen = image_gen_train.flow(x,
batch_size=1,
save_prefix='N',
save_to_dir=os.path.join(save_dir, label),
save_format='jpg')
for i in range(ex_num):
train_data_gen.next()
比較検証
300枚ほどの画像で学習した場合(データ拡張なし)
ここが出発点のため、確認しておきます。
学習準備
# 学習用、検証用データ件数
# train data: Found 167 images belonging to 15 classes.
# validation data: Found 161 images belonging to 15 classes.
# パラメータ
batch_size = 128
epochs = 15
IMG_HEIGHT = 100
IMG_WIDTH = 100
# 学習モデル
model = Sequential([
Conv2D(16, 3, padding='same', activation='relu', input_shape=(IMG_HEIGHT, IMG_WIDTH ,3)),
MaxPooling2D(),
Conv2D(32, 3, padding='same', activation='relu'),
MaxPooling2D(),
Conv2D(64, 3, padding='same', activation='relu'),
MaxPooling2D(),
Flatten(),
Dense(512, activation='relu'),
Dense(15, activation='softmax')
])
# モデルのビルド
model.compile(optimizer='sgd',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
学習結果(M1 Pro, 処理時間: 3.6秒)
# 結果
test loss: 2.59, test accuracy: 18.63%
結果はご覧の通りダメダメですね。
各画像を300枚ずつに水増しした場合(データ拡張あり)
一般に、データ拡張はサンプルのみに適用する、とのことなので検証用データは水増ししません。
学習準備
# 学習用、検証用データ件数
training data: Found 41575 images belonging to 15 classes.
validation data: Found 161 images belonging to 15 classes.
# パラメータ、モデルは初期と同じ
学習結果(M1 Pro, 処理時間: 約10分)
# 結果
test loss: 0.70, test accuracy: 77.02%
一気にそれっぽくなりました。この方向を伸ばせば良さそうに見えます。
各画像を1,000枚ずつに水増しした場合(データ拡張あり)
学習準備
# 学習用、検証用データ件数
train data: Found 95938 images belonging to 15 classes.
validation data: Found 161 images belonging to 15 classes.
# パラメータ、モデルは初期と同じ
学習結果(Google Colab Pro, 処理時間: 約11分)
# 結果
test loss: 0.74, test accuracy: 83.23%
おお、伸びてきてますね。このまま水増し量を試していってもいいんですが、少し違う方向も試しておきます。
各画像1,000枚水増しでepochを3倍にした場合
epochは「一つの訓練データを何回繰り返して学習させるか」ということのようで、95,000枚ほどの学習用データが一旦出来ているので繰り返し学習させても過学習は少しは起きにくくなっているかもしれません。
学習準備
epochsを15から45に増やします。
# パラメータ(epochsを15 -> 45に変更)
batch_size = 128
epochs = 45
IMG_HEIGHT = 100
IMG_WIDTH = 100
# 学習用、検証用データ、モデルは各画像1,000枚水増しの時と同じ
学習結果(Google Colab Pro, 処理時間: 約45分)
# 結果
test loss: 0.83, test accuracy: 81.37%
少し乱れてますね。無駄な学習になってしまっているようです。
もう少し効率良く学習できるか試してみます。
各画像1,000枚水増し+epochを3倍にドロップアウトを追加した場合
ドロップアウトとは指定した割合のアウトプットをゼロにすることのようです。これにより、過学習を防ぎ学習効率を図ります。
学習準備
モデルのレイヤーに「 Dropout(0.2)」を追加します。
model = Sequential([
Conv2D(16, 3, padding='same', activation='relu', input_shape=(IMG_HEIGHT, IMG_WIDTH ,3)),
MaxPooling2D(),
Dropout(0.2),
Conv2D(32, 3, padding='same', activation='relu'),
MaxPooling2D(),
Conv2D(64, 3, padding='same', activation='relu'),
MaxPooling2D(),
Dropout(0.2),
Flatten(),
Dense(512, activation='relu'),
Dense(15, activation='softmax')
])
# 学習用、検証用データ、パラメータは各画像1,000枚水増し+epochを3倍の時と同じ
学習結果(Google Colab Pro, 処理時間: 約41分)
# 結果
test loss: 0.66, test accuracy: 89.44%
良い感じになってきたと思います。
まとめ
画像データの水増しが楽で効果的だということが分かって良かったです。
あと何気にM1 Pro(Macbook Pro)で画像の学習もストレス感じないの良いですね(Google Colabお金払っているので途中からそっち使ってしまっていますが)、このCPUが80%超えているの初めて見ました。
あとは元データをどれだけ少なくできるか・・・はやりすぎなのかな。
今回の検証で一旦やりたいことに使えそうなので、他の検証はまたの機会にでもしてみたいと思います。