Keras: CNN画像分類 (Keras-provided CNN)

Takami Torao Python 3.5 Keras 2.2 TensorFlow 1.8 #Keras #TensorFlow #Xception
  • このエントリーをはてなブックマークに追加

概要

Keras は過去のコンテストで優秀な成績を収めた CNN がすぐに利用可能な形で含まれている。ここではこれらの CNN を独自のデータセットで学習して画像分類を行う。

Keras に用意されている CNN は ImageNet で学習済みのモデルとしても利用できるため転移学習に流用することができる。一般的な写真の分類ではこのモデルから転移学習させたほうが学習効率も精度も良好な結果が期待できるだろう。しかし、例えば手書き文字や、そもそも画像ではないデータのような ImageNet のデータセットとは全く特性の異なるデータセットを使用する場合は ImageNet の重みを使用しないで比較検証することも必要だろう。

Keras の CNN モデルはデフォルトで ImageNet で学習した重みをプリセットされる。weights=None を指定するとパラメータを乱数で初期化するため、学習されていない状態から新しい学習を行う事ができる。加えて、目的のタスクに合わせて入力テンソル input_tensor と出力分類数 classes を指定する。

model = Xception(weights=None, input_tensor=input, classes=n_labels)

プログラミング

学習

ここでの実装は Xception を使用して CIFAR-10 の 32×32 の画像を 10 クラスに分類する。API が統一されているため他の CNN でもほぼ同じ実装で良いが、CIFAR-10 の 32×32 画像が入力として小さすぎるものは利用できないことがある。Xception 以外の CNN については Keras Document - Applications を参照。

# -*- encoding: utf-8 -*-
from keras.applications.xception import Xception
from keras.datasets.cifar10 import load_data
from keras.layers import Input
from keras.utils import to_categorical
import numpy as np
import csv

def train():

  # CIFAR-10 の画像データを取得 (学習用とテスト用に分かれている)
  (x_train, y_train), (x_test, y_test) = load_data()
  print(x_train.shape, x_test.shape, y_train.shape, y_test.shape)

  # サンプルの x は 256 階調 RGB のため 0.0-1.0 の範囲に変換
  x_train = x_train.astype(np.float32) / 255.
  x_test = x_test.astype(np.float32) / 255.

  # 多クラス分類のために y を one-hot vector に変換
  n_labels = len(np.unique(y_train))
  y_train = to_categorical(y_train, n_labels)
  y_test = to_categorical(y_test, n_labels)
  print(y_train.shape, y_test.shape)

  # モデルを作成して学習を実行
  input = Input(shape=(32, 32, 3))
  model = Xception(weights=None, input_tensor=input, classes=n_labels)
  model.compile(optimizer="rmsprop", loss="categorical_crossentropy", metrics=["accuracy"])
  h = model.fit(x_train, y_train, epochs=500, validation_data=(x_test, y_test))

  # 実行履歴 (正解率の推移) を CSV で保存
  loss = h.history["loss"]
  val_loss = h.history["val_loss"]
  acc = h.history["acc"]
  val_acc = h.history["val_acc"]
  with open("history.csv", "wt", encoding="utf-8") as out:
    writer = csv.writer(out)
    writer.writerow(["EPOCH", "ACC(TRAIN)", "ACC(TEST)", "LOSS(TRAIN)", "LOSS(TEST)"])
    for i in range(len(loss)):
      writer.writerow([i+1, acc[i], val_acc[i], loss[i], val_loss[i]])

  return model

if __name__ == "__main__":
  import sys, keras, tensorflow
  print("Python %s" % sys.version)
  print("Keras %s" % keras.__version__)
  print("TensorFlow %s" % tensorflow.__version__)

  model = train()
  model.save("cifer10+xception.hdf5")
Python 3.5.2 (default, Nov 23 2017, 16:37:01) [GCC 5.4.0 20160609]
Keras 2.2.0
TensorFlow 1.4.0
(50000, 32, 32, 3) (10000, 32, 32, 3) (50000, 1) (10000, 1)
(50000, 10) (10000, 10)

学習曲線の評価

上記のスクリプトは model.fit() によって得られた学習曲線を history.csv に保存している。以下は GTX 1080 Ti を使用して 500[epoch] まで行なった学習曲線の推移である (6時間以上かかる)

Xception + CIFAR-10 Learning Curve
Fig 1. Xception + CIFAR-10 学習曲線

epoch が少ないうちは学習データ側の正解率のみが上昇/安定して過学習の傾向が見られたが、400 epoch 付近から検証データの正解率も 80% 近くで安定している。したがってこのケースでは 400[epoch] のモデルを使用すればよいだろう。

パフォーマンス

Table 1 はモデル作成における 1[epoch] あたりに必要な処理時間を演算装置ごとに表したものである。廉価モデルであっても CUDA に対応した GPU 環境で実行したほうが良いことは明らかだろう。

Model Clock Time
[sec/epoch]
Core
CPU i7-7700 3.6GHz 3,360 4C/8T
Xeon E3-1230 3.2GHz 2,951 4C/8T
GPU GeForce GTX 1050 Ti 1.3GHz 149 768
GeForce GTX 1080 Ti 1.5GHz 46 3584
Table 1. 演算装置ごとの実行時間

一般的に、ランダムな重みが設定された無学習の状態から適切な精度の CNN モデルを構築するには大量の計算時間を必要とする。epoch あたりの処理時間を考えると、実計算でも preflight な観測でも、CPU のみでの計算は現実的ではないことが分かるだろう。例えば今回の 400[epoch] のモデル作成に対して CPU のみでは 2 週間程度かかると予想されるが GPU を使用すれば数時間~1日程度で済む。

計算量の問題は転移学習 (fine tuning) という手法を用いることで回避できる可能性がある。転移学習については別のページでまとめる。

画像分類

続いて学習工程で保存したモデル cifer10+xception.hdf5 を使用して画像の分類を行う。この 500[epoch] で学習済みのモデルは cifer10+xception.hdf5.bz2 からダウンロードすることができる。

# -*- encoding: utf-8 -*-
from keras.models import load_model
from keras.preprocessing.image import load_img, img_to_array
import numpy as np

def predict(model_path, image_paths):

  # 画像を 32×32 の RGB で読み込み
  images = map(lambda f: load_img(f, target_size=(32, 32)), image_paths)
  images = img_to_array(images)
  images = np.stack(images)
  # images = img_to_array(image).astype(np.float32) / 255.

  # モデルのロード
  model = load_model(model_path)

  # 予測の実行
  probs = model.predict(images, verbose=1)
  return probs.tolist()

if __name__ == "__main__":
  import sys, keras, tensorflow
  print("Python %s" % sys.version)
  print("Keras %s" % keras.__version__)
  print("TensorFlow %s" % tensorflow.__version__)

  probs = predict("cifer10+xception.hdf5", sys.argv[1:])
  print(probs)

  labels =  ["airplane", "automobile", "bird", "cat", "deer", "dog", "frog", "horse", "ship", "truck"]
  for file, ps in zip(image_paths, probs.tolist()):
    print("[%s]" % file)
    for prob, label in zip(ps, labels):
      print("%.3f: %s" % (prob, label))
>python predict.py frog.png
Python 3.5.4 (v3.5.4:3f56838, Aug  8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)]
Keras 2.2.0
TensorFlow 1.8.0
Red-eyed Tree Frog
0.000: airplane
0.000: automobile
0.000: bird
0.000: cat
0.000: deer
0.000: dog
1.000: frog
0.000: horse
0.000: ship
0.000: truck
Cat
0.000: airplane
0.000: automobile
0.000: bird
1.000: cat
0.000: deer
0.000: dog
0.000: frog
0.000: horse
0.000: ship
0.000: truck
F-22 Raptor
0.769: airplane
0.000: automobile
0.231: bird
0.000: cat
0.000: deer
0.000: dog
0.000: frog
0.000: horse
0.000: ship
0.000: truck
Bhoot Jolokia
0.000: airplane
0.000: automobile
0.000: bird
0.000: cat
0.000: deer
0.000: dog
0.923: frog
0.000: horse
0.000: ship
0.077: truck

参照

  1. Xception: Deep Learning with Depthwise Separable Convolutions, François Chollet, 7 Oct 2016
  2. Keras Documenttion - Applications
  3. CIFAR-10 (MOXBOX)
  4. Red-eyed Tree Frog - Litoria chloris edit1 CC BY-SA 3.0, CC BY-SA 3.0, F-22 Raptor edit1 PUBLIC DOMAIN, Bhoot Jolokia ( Ghost Chili pepper ) CC BY-SA 4.0