Kerasで構築したモデル(CNN)をチューニングして精度を上げてみる

割と適当に数値を上げ下げしてるような印象があるが気のせいだろうか。
Jupyter * Keras(深層学習のライブラリ)を試してみる
Kerasで作成した訓練データを読み込んで自前の画像をテストする
の続き。
例のごとくGitHubに上げている。




モデルをチューニングして精度を上げてみる



そこそこ普通の画像でも認識してくれなかったのでどうにかする。
モデルはおそらく十分最適化されているので手をつける必要はあまりないかも。
調べた感じ適当にパラメータを増減してるケースが多いような?

BatchNormalizationを入れる

Dropoutと一緒に使わないほうがいいとか色々な意見があるがCNNならとりあえず入れておいてもいいらしい。

from keras.layers import BatchNormalization
model = Sequential()
model.add(BatchNormalization())

若干精度が上がった。

EarlyStoppingを入れる


学習が進まなくなったら止める。

model.fit(x_train, y_train,
          batch_size=batch_size,
          epochs=epochs,
          verbose=1,
          validation_data=(x_test, y_test),
          callbacks=[EarlyStopping()])

val_accが下がったタイミングで打ち切るようになる。


データを水増しする

# データ水増し 上下左右に10%ずらす
datagen = image.ImageDataGenerator(
    width_shift_range=0.1,
    height_shift_range=0.1)
datagen.fit(x_train)
# here's a more "manual" example
for e in range(epochs):
    print('Epoch', e)
    batches = 0
    for x_batch, y_batch in datagen.flow(x_train, y_train, batch_size=32):
        model.fit(x_batch, y_batch,
                  batch_size=batch_size,
                  epochs=epochs,
                  verbose=1,
                  validation_data=(x_test, y_test))
        batches += 1
        if batches >= len(x_train) / 32:
            # we need to break the loop by hand because
            # the generator loops indefinitely
            break

Kerasには専用の関数ImageDataGeneratorが用意されている。便利。
https://keras.io/ja/preprocessing/image/

使い方を間違えているのかあまり精度が出なかった。



テスト対象画像を加工する



テスト画像を学習モデルに沿うように加工する。


画像に閾値を設ける


画像ファイルなどは人間に認識できないくらい微妙に色がついているケースがある。
これが誤認識の原因ではないかとあたりをつけて一定の値以下は0に変換する。

img_path = '../img/6.png'
img = load_img(img_path, color_mode = "grayscale", target_size=(28, 28)) #入力画像のサイズ
x = img_to_array(img) # 画像データをnumpy.arrayへ変換
# 元が黒背景白文字なので反転している
x = 255 - x
x = np.expand_dims(x, axis=0)
x = x.astype('uint8')

# 閾値を下回るものは0にする
x = np.where(x>100, x, 0)

効果はあると思うが結果は変わらなかった。
おそらく元のデータが汚いせいではなかろうか(人間にも判別困難なものがある) 実際には誤解を生みそうなデータ(6が反転しているやつとか)は学習時に除外すべきなのだろう。

画像サイズを合わせる

学習データと比べて小さい場合などうまくいかない。余白を削ってテストしてみる。
大きめのサイズで画像データを作成したあと削っている。

# 周りをトリミングしている
img_path = '../img/2.jpg'
img = load_img(img_path, color_mode = "grayscale", target_size=(36, 36)) #入力画像のサイズ
x = img_to_array(img) # 画像データをnumpy.arrayへ変換
# 上下左右4pxトリム
x = x[4:32, 4:32]
# 元が黒背景白文字なので反転している
x = 255 - x
x = np.expand_dims(x, axis=0)
x = x.astype('uint8')

これはうまくいった。実際は画像から自動でトリムを判断するような処理が必要だろう。

サンプルコードは以下
https://github.com/ninomae-makoto/keras/tree/master/03_tuning


参考



https://qiita.com/Kazuki000/items/417d2f647ea27b35f48a

2019年3月31日日曜日