Keras+TensorFlowで実践CNN(その4)

学習させる画像に変化を加える

これまでのトライアルでは、元の画像のピクセル数を小さくして、すべて同じ画像サイズに変形させたものを学習に使用してきました。テストデータによる検証の結果、グレースケール画像、RGB画像ともに90%の予測精度であることを確認しました。

今回は、画像データに回転や水平反転を加えたものを学習器に入力した場合に、予測精度がどのように変化するかを比較していきます。

まず、学習とテストに入力する画像を加工します。作成元の画像はCaltech 101を使用します。この中に含まれている、パンダ、ヤマネコ、サイ、象、フラミンゴ、カモノハシ、オカピ、ラマ、カンガルー、ハリネズミ、エミュー及びビーバーの画像について、ピクセル数の変更、回転及び水平反転させてバイナリファイルに出力していきます。

from sklearn.model_selection import train_test_split
from PIL import Image
import os, glob, sys
import numpy as np

def convert_image(conv_type, name):
  if conv_type is not 'L' and conv_type is not 'RGB':
    print(conv_type + "==>入力された画像変換タイプはありません")
    return
    #sys.exit()
  #読み込み対象ディレクトリの設定
  sample_dir = "./images/101_ObjectCategories"
  categories = ["panda","wild_cat","rhino","elephant","flamingo","platypus","okapi","llama","kangaroo","hedgehog","emu","beaver"]
  #カテゴリ数(12種類)と縦横のリサイズするピクセル数の設定
  num_class = len(categories)
 
  img_width = 64
  img_height = 64
  #画像データとラベルを格納するリストの初期化
  X = []
  Y = []
  #画像変換処理
  for idx, cat in enumerate(categories):
    label = [0 for i in range(num_class)]
    label[idx] = 1
    image_dir = sample_dir + "/" + cat
    files = glob.glob(image_dir + "/*")
    for i, fl in enumerate(files):
      #print(fl)
      img = Image.open(fl)
      img = img.convert(conv_type)
      img = img.resize((img_width,img_height))
      for angle in range(-20, 25, 5):
        #画像を回転させる
        rot_img = img.rotate(angle)
        data = np.asarray(rot_img)
        X.append(data)
        Y.append(label)
        if i == 0:
          rot_img.save(name + "_" + cat + "_" + str(angle)
                       + "deg_" + os.path.basename(fl))
        #画像を水平反転させる
        trn_img = rot_img.transpose(Image.FLIP_LEFT_RIGHT)
        data = np.asarray(trn_img)
        X.append(data)
        Y.append(label)
        if i == 0:
          trn_img.save(name + "_" + cat + "_" + str(angle)
                       + "deg_transe_" + os.path.basename(fl))
    #画像データとラベルを格納したリストをnumpy.ndarrayに変換
    X = np.array(X)
    Y = np.array(Y)
    #学習用データとテスト用データに分割
    train_data, test_data, train_label, test_label = \
    train_test_split(X, Y, test_size=0.3, shuffle=True)
    #分割したデータをタプルで並べてファイルに出力
    xy = (train_data, test_data, train_label, test_label)
    np.save("./images/wildlife_"+ name + ".npy", xy)
    #処理した画像数を出力して終わり
    print("Success to convert " + name + " images=,", len(Y))

if __name__ == "__main__":
  #RGB画像の作成
  convert_image("RGB", "rgb")

画像の回転や水平反転についても、Pillow(PIL)のImageモジュールを使用することで簡単に処理できます。ポイントは以下のコード部分です。

for i, fl in enumerate(files):
  #print(fl)
  img = Image.open(fl)
  img = img.convert(conv_type)
  img = img.resize((img_width,img_height))
  for angle in range(-20, 25, 5):
    #画像を回転させる
    rot_img = img.rotate(angle)
    data = np.asarray(rot_img)
    X.append(data)
    Y.append(label)
    if i == 0:
      rot_img.save(name + "_" + cat + "_" + str(angle)
                   + "deg_" + os.path.basename(fl))
    #画像を水平反転させる
    trn_img = rot_img.transpose(Image.FLIP_LEFT_RIGHT)
    data = np.asarray(trn_img)
    X.append(data)
    Y.append(label)
    if i == 0:
      trn_img.save(name + "_" + cat + "_" + str(angle)
                   + "deg_transe_" + os.path.basename(fl))

rotate()モジュールで画像に回転を加えます。引数に回転角度を与えます。左回りは正の角度、右回りは負の確度を与えます。画像の水平反転は、transpose(Image.FLIP_LEFT_RIGHT)です。また、今回は使用していませんが、上下反転もtranspose(Image.FLIP_TOP_BOTTOM)で処理できます。

コードを実行すると上記のような画像が作成され、ファイルに出力されます。真ん中の大きな画像が元画像です。周辺の画像が変換後の画像で、全て64×64ピクセルに変換されています。左側が回転のみで、右側が水平反転+回転された画像です。

では、これら作成した画像を使用してCNNで学習させ、予測精度がどのように変化するか実験します。今回使用するKeras+TensorFlowのPythonコードは、以前作成したものを使用します。詳しくは以下を参照してください。

回転や水平反転させた画像をCNNに学習させる

まず始めに、画像の回転や水平反転を含んでいないRGB画像を使用して学習とテストデータによる予測精度を行います。エポック数は10回、バッチ数は64を設定しています。

Epoch 1/10
456/456 [==============================] - 4s - loss: 1.4192 - acc: 0.8575
Epoch 2/10
456/456 [==============================] - 4s - loss: 0.3288 - acc: 0.9145
Epoch 3/10
456/456 [==============================] - 4s - loss: 0.2800 - acc: 0.9163
Epoch 4/10
456/456 [==============================] - 4s - loss: 0.2567 - acc: 0.9176
Epoch 5/10
456/456 [==============================] - 4s - loss: 0.2278 - acc: 0.9225
Epoch 6/10
456/456 [==============================] - 4s - loss: 0.1857 - acc: 0.9329
Epoch 7/10
456/456 [==============================] - 4s - loss: 0.1526 - acc: 0.9415
Epoch 8/10
456/456 [==============================] - 4s - loss: 0.1504 - acc: 0.9437
Epoch 9/10
456/456 [==============================] - 4s - loss: 0.0983 - acc: 0.9627
Epoch 10/10
456/456 [==============================] - 4s - loss: 0.0768 - acc: 0.9726
loss= 0.305940233323
accuracy= 0.90

10回目の学習時の損失は0.0768ですが、テスト用データの検証時は0.3059とオーダが一桁大きくなっています。正解率も10回目の学習時では97.2%でしたが、テスト用データの検証時は90%に低下しています。学習時と検証時の差が大きいことから、CNNの学習は不十分だと言えるでしょう。

次に、画像の回転や水平反転を含めた画像データで学習とテストデータによる予測精度を行います。エポックとバッチ数は同じす。

Epoch 1/10
8215/8215 [==============================] - 76s - loss: 0.3420 - acc: 0.9139
Epoch 2/10
8215/8215 [==============================] - 75s - loss: 0.1924 - acc: 0.9310
Epoch 3/10
8215/8215 [==============================] - 76s - loss: 0.1388 - acc: 0.9487
Epoch 4/10
8215/8215 [==============================] - 75s - loss: 0.0978 - acc: 0.9634
Epoch 5/10
8215/8215 [==============================] - 76s - loss: 0.0709 - acc: 0.9740
Epoch 6/10
8215/8215 [==============================] - 76s - loss: 0.0518 - acc: 0.9815
Epoch 7/10
8215/8215 [==============================] - 76s - loss: 0.0385 - acc: 0.9862
Epoch 8/10
8215/8215 [==============================] - 76s - loss: 0.0291 - acc: 0.9900
Epoch 9/10
8215/8215 [==============================] - 75s - loss: 0.0225 - acc: 0.9925
Epoch 10/10
8215/8215 [==============================] - 75s - loss: 0.0195 - acc: 0.9935
loss= 0.0506781227778
accuracy= 0.98

10回目の学習時の損失は0.0195で、テスト用データの検証時は0.0506とオーダは一致しています。正解率も10回目の学習時では99.3%で、テスト用データの検証時は98%でした。学習時と検証時の差が小さいことから、CNNの学習は上手くできた考えることができるでしょう。

CNNに入力して学習させる画像に回転や水平反転を加えてサンプル数を増加させたことにより、テスト用データによる予測精度を90%から98%に向上させることができました。この予測精度ならば、ほぼ画像識別ができているレベルと言えそうです。

使用したCNNモデルの構築設定やエポック数などはまったく同条件であるため、単に学習した画像が456枚から8,215枚に増加したことが予測精度の向上に寄与したと考えることができます。

最初からたくさんの画像のサンプル数があればよいのですが、画像枚数が限られていて少ない場合は、回転や反転を加えて画像のサンプル数を増やすことがCNN学習では重要なことが、今回の実験で確認できたとおもいます。

今回のまとめ

今回は、CNNによる12種類の動物の画像認識を行いました。画像に回転や反転を加えてサンプル数を増やすことにより、正解率を向上させることができることを実験を通じて理解することができました。

これまで、4回に渡ってKeras+TensorFlowによる画像識別の実験を実践してきました。ここまでのトライアルを振り返ってみると、CNN(畳み込みニューラルネットワーク)の構築や学習用データの作成方法などが大まかに理解できたのではないでしょうか。

次回は、CNNの学習結果を使用して動物の画像識別をやってみようとおもいます。正解率98%の数字の通りに、写真の中の動物を正しく識別できるのかを体験していきます。