訓練データの変換から,任意の画像の予測までを行う機械学習プログラムセット一式を書きました。

Kerasを使用したCNNモデルの構築など,基本的な部分はクジラ飛行机さんの著書を参考にしています。

また,キルミーラーニングという単語の語呂の良さに感動したことや,公式サイトが学習データとして良い感じの画像を配布していることなどから,今回はキルミーベイベーのキャラクターを分類しました。

参考にしたサイト,文献


ディレクトリとか


このプログラムセット一式では,以下のようなディレクトリ構造を想定しています。

<C:>
| - <samplepy> 
|    | - kill_me_data_converter.py
|    | - kill_me_learning.py
|    | - kill_me_classification.py
|
| - <Machine_Learning_data> 
|    | - <kill_me_baby_data>
|    |    | - <yasuna>
|    |    | - <sonya>
|    |    | - <agiri>
|    |    | - <botsu> 
|    |    | - <others>
|    |    | - <yasuna&sonya>

汎用性を重視して,カテゴリー名とか,ディレクトリ名とかは変数に代入する形でプログラム上部にまとめてあります。必要があれば適宜変更してください。

それから,Windows上ではディレクトリ表記に「¥」を使うことが一般的ですが,別に「/」でも大丈夫です。プログラム中で「¥」を使うとエスケープシーケンスになってしまうので「/」で記述しています。

プログラム一式


処理内容によってプログラムが分かれています。処理の内容は以下の通りです。

  1. :画像とディレクトリ名をセットにして,Numpy配列に変換&保存
  2. :Numpy配列データを読み込んで学習処理&学習済みモデル保存
  3. :学習済みモデルを用いて画像予測

初めて実行する際は1から順番に実行してください。

1.kill_me_converter.py


# pngファイルを24ビット,RGBに変換してNumpyの配列形式で保存する

from sklearn import cross_validation
from PIL import Image
import os, sys, glob
import numpy as np

data_dir = "C:/Machine_Learning_data/kill_me_baby_data"
categories = ["yasuna","sonya","agiri","botsu","others","yasuna&sonya"]
# 変換したデータの保存先
npy_file = "C:/Machine_Learning_data/kill_me_baby_data/kill_me_baby_data.npy"

# ディレクトリ確認
for s, t in enumerate(categories):
    if not os.path.exists(data_dir + "/" + t):
        print("ディレクトリが存在しません:", data_dir + "/" + t)
        quit()

nb_classes = len(categories)
image_size = 64  # 画像サイズを指定(一辺の長さが64)
pixels = image_size * image_size * 3    # RGBだから3

# 画像データを読み込んでNumpy配列に変換
X = []  # 画像データ
Y = []  # ラベルデータ
for idx, cat in enumerate(categories):
    label = [0 for i in range(nb_classes)]
    label[idx] = 1
    image_dir = data_dir + "/" + cat
    # glob関数を利用して拡張子が「.png」のものだけを列挙する
    files = glob.glob(image_dir+"/*.png")
    for i, f in enumerate(files):
        img = Image.open(f)
        img = img.convert("RGB")
        img = img.resize((image_size, image_size))
        data = np.asarray(img)
        X.append(data)
        Y.append(label)
        # 進捗表示
        print("\r",cat, ":", i // len(files) * 100,"%",end="")
    print("\r",cat, ": 100%")

X = np.array(X)
Y = np.array(Y)

# 学習データとテストデータを分ける
X_train, X_test, y_train, y_test = cross_validation.train_test_split(X, Y)
xy = (X_train, X_test, y_train, y_test)
np.save(npy_file, xy)

print("ok,", len(Y), ",ファイル名:", npy_file)

キルミーベイベーの配布画像がPNG形式だったので,ここではPNGで統一していますが,32行目を変更すればJPG形式も読み込めます。

2.kill_me_learning.py


# Kerasで構築したCNNモデルを用いて学習処理
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
import numpy as np
import os

categories = ["yasuna","sonya","agiri","botsu","others","yasuna&sonya"]
hdf5_file = "C:/Machine_Learning_data/kill_me_baby_data/kill_me-model.hdf5"
npy_file = "C:/Machine_Learning_data/kill_me_baby_data/kill_me_baby_data.npy"

nb_classes = len(categories)
image_size = 64

# 既に学習済みモデルがあればプログラムを終了する
if os.path.exists(hdf5_file):
    print("既に学習済みモデルが存在します:", hdf5_file)
    print("新たに学習を行う場合は,上記ファイル名を変更ないしは削除してください。")
    quit()

# データをロード
X_train, X_test, y_train, y_test = np.load(npy_file)
# データを正規化する
X_train = X_train.astype("float") / 256
X_test  = X_test.astype("float")  / 256
print('X_train shape:', X_train.shape)

# CNNのモデルを構築
model = Sequential()
model.add(Conv2D(32, (3, 3),
          padding = 'same',
          input_shape = X_train.shape[1:]))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Conv2D(64, (3, 3), padding='same'))
model.add(Activation('relu'))
model.add(Conv2D(64, (3, 3)))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(512))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(nb_classes)) # 出力データのカテゴリー数を指定
model.add(Activation('softmax'))
model.compile(loss='binary_crossentropy',
              optimizer='rmsprop',
              metrics=['accuracy'])

# モデルを訓練する
model.fit(X_train, y_train, epochs=40, batch_size=32)

# モデルを保存
model.save_weights(hdf5_file)
print("新しい学習モデルを保存しました:", hdf5_file)

# モデルを評価する
score = model.evaluate(X_test, y_test)
print('loss=', score[0])
print('accuracy=', score[1])

この処理が一番マシンリソースを必要とします。特に何も指定しませんでしたが,GPUを使用できる状況にあれば自動的にGPUで処理を行うみたいです。詳細はKeras公式ドキュメントを参照してください。

3.kill_me_classification.py


import tkinter.filedialog as fd
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
import matplotlib.pyplot as plt  # 画像表示用
from PIL import Image
import numpy as np
import sys, os
import h5py

categories = ["yasuna","sonya","agiri","botsu","others","yasuna&sonya"]
hdf5_file = "C:/Machine_Learning_data/kill_me_baby_data/kill_me-model.hdf5"

# GUIでファイルのパスを取得
path = fd.askopenfilename(title = "判定するキルミーメンバーの画像を選んでね",  # ダイアログ上部のタイトルを設定
                          filetypes = [('JPG','.jpg'),('PNG','.png'),('JPEG','.jpeg')])
print("画像のパス:",path)

nb_classes = len(categories)
image_size = 64

# 入力画像をNumpy配列に変換 & リサイズ
X = []
files = []
img = Image.open(path)  # GIUで取得したpathの画像を開く
img = img.convert("RGB")
img = img.resize((image_size, image_size))
data = np.asarray(img)
X.append(data)
files.append(path)
X = np.array(X)
img.close()

# CNNのモデルを構築
model = Sequential()
model.add(Conv2D(32, (3, 3),
          padding = 'same',
          input_shape = X.shape[1:]))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Conv2D(64, (3, 3), padding='same'))
model.add(Activation('relu'))
model.add(Conv2D(64, (3, 3)))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(512))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(nb_classes))  # 出力データのカテゴリー数を指定
model.add(Activation('softmax'))
model.compile(loss='binary_crossentropy',
              optimizer='rmsprop',
              metrics=['accuracy'])

# 用意しておいたモデルの学習データをロード
model.load_weights(hdf5_file)

# データを予測(判定)
pro = model.predict_proba(X)  # 予測値を格納したNmupy配列.
for i, p in enumerate(pro):
    y = p.argmax()

print("-" * 30,"予測結果","-" * 30)
print("+ 入力画像:", files[i])
print("| キャラ名:", categories[y], "(",p[y] * 100,"%)")
# 各キャラの予測確率を列挙
for i in range(len(categories)):
    for s, pr in enumerate(pro):
        print("|", categories[i],":",pr[i] , end=" ")

# matplotlibで読み込ませた画像を表示
im = Image.open(path)
plt.imshow(im)
plt.show()
im.close()

Kerasでは学習モデルをHDF5形式で保存するため,h5pyというモジュールが必要です。予めpipコマンドで入れておいてください。

このプログラムではPNGとJPGのどちらでも読み込むことができます。

こんな感じにGUIで任意の画像を選択すると,画像のパスをCNNモデルに渡して予測を行います。

ありがとうキルミー


今回は,キルミーベイベー公式が画像データ(本来の使用目的はアイコンですが)を配布してくれていたおかげで最初のデータ集めがすんなり進みました。

686枚の画像を手作業で分類するのは結構大変ですが,それでも既にトリミングされているというのはとてもありがたいことです。

最後に,ありがとうキルミーベイベー。