弊社は様々な業界の集客をお手伝いしています。
その一つがブライダルです。
ブライダル業界の集客において、画像(ビジュアル)は最重要事項の一つです。
素敵な結婚式をイメージできるビジュアルに、花嫁たちは魅かれます。
そのビジュアルは、ターゲット(花嫁)の趣味嗜好に応じていくつかのパターンに分類されます。
今回は300枚のデータを用意し、訓練したモデルをもとに、新しいビジュアルを読み込んでクラス分類するソフトを作成します。
作るもの(ゴール)
サンプル画像を読み込み、どのクラスに所属するか判定するソフトを作る。
大まかなステップ
①画像データ収集・分類 ②データ生成 ③cnnモデル定義・訓練・評価 ④推定モデル作成
①画像データ収集・分類
今回はブライダルの画像300枚を、下記3つのパターンに分類しました。
▼「A」フォルダ(100枚)……チャペルをイメージとした、「ザ・結婚式」な写真
(例)
▼「F」フォルダ(100枚)……友人たちと歓談する、フレンドリーな雰囲気の写真
(例)
※ディレクトリ構成
visual_ml ├─ A │ └(画像100枚) ├─ F │ └(画像100枚) ├─ O │ └(画像100枚) │ ├── gen_data.py // データを生成する ├── predict.py // 画像を読み込んで推定する ├── visual_cnn.h5 ├── visual_cnn.py // cnnモデル定義 ├── visual.npy └── sample.jpg
②データ生成
gen_data.pyで、300枚の画像を良い感じにデータ変換し、保存します。
# gen_data.py from PIL import Image import os, glob import numpy as np from sklearn import model_selection classes = ['A', 'F', 'O'] num_classes = len(classes) image_size = 50 # 画像の読み込み X = [] Y = [] for index, classlabel in enumerate(classes): photos_dir = './' + classlabel # classesからフォルダ名をfor文で取得 files = glob.glob(photos_dir + '/*.jpg') # 画像のパスとファイル名を取得 for i, file in enumerate(files): if i > 100: break # 画像が100枚を越えるとbreak image = Image.open(file) # Image.openで画像ファイルを開く image = image.convert('RGB') # 画像をRGB(Red,Green,Blue)に変換 image = image.resize((image_size, image_size)) # 画像をimage_size(50px)に変換 data = np.asarray(image) # 画像をnumpy配列に変換 X.append(data) # リストXに追加する Y.append(index) # リストYに追加する # Tensorflowが扱いやすいデータ型に揃える X = np.array(X) Y = np.array(Y) # scikitlearnのmodel_selectionを使って、300枚の画像XとラベルYを「訓練用」と「テスト用」に分類 X_train, X_test, y_train, y_test = model_selection.train_test_split(X, Y) xy = (X_train, X_test, y_train, y_test) #分割したデータ一式をnpy形式で同階層に保存 np.save('./visual.npy', xy)
③cnnモデル定義・訓練・評価
②で保存したデータをもとに、cnnモデルを定義して保存します。 model_eval()の実行により、学習結果の精度を評価します。
# visual_cnn.py from keras.models import Sequential from keras.layers import Conv2D, MaxPooling2D from keras.layers import Activation, Dropout, Flatten, Dense from keras.utils import np_utils import keras import numpy as np classes = ['A', 'F', 'O'] num_classes = len(classes) image_size = 50 # メインの関数を定義する def main(): # visual_cnn.pyで作成したvisual.npyをloadする X_train, X_test, y_train, y_test = np.load("./visual.npy") # Xのデータをfloat型にして、各値を最大値が1になるように256で割る X_train = X_train.astype("float") / 256 X_test = X_test.astype("float") / 256 # yの分類を数値(0="A",1="F",2="O")に変換 y_train = np_utils.to_categorical(y_train, num_classes) y_test = np_utils.to_categorical(y_test, num_classes) # 訓練関数 model = model_train(X_train, y_train) # 評価関数 model_eval(model, X_test, y_test) def model_train(X, y): # CNN model = Sequential() model.add(Conv2D(32, (3, 3), padding = 'same', input_shape = X.shape[1:])) model.add(Activation('relu')) model.add(Conv2D(32, (3, 3))) 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(Activation('relu')) 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(3)) model.add(Activation('softmax')) opt = keras.optimizers.rmsprop(lr = 0.0001, decay = 1e-6) model.compile(loss = 'categorical_crossentropy', optimizer = opt, metrics = ['accuracy']) model.fit(X, y, batch_size = 32, epochs = 100) # モデルの保存 model.save('./visual_cnn.h5') return model def model_eval(model, X, y): scores = model.evaluate(X, y, verbose = 1) print('Test Loss: ', scores[0]) print('Test Accuracy: ', scores[1]) if __name__ == "__main__": main()
④推定モデル作成
コマンドライン引数で読み込む画像を指定し、判定を行います。 ※モデル定義自体は②と同じです。
# predict.py from keras.models import Sequential, load_model from keras.layers import Conv2D, MaxPooling2D from keras.layers import Activation, Dropout, Flatten, Dense from keras.utils import np_utils import keras, sys import numpy as np from PIL import Image classes = ['A', 'F', 'O'] num_classes = len(classes) image_size = 50 def build_model(): # CNN model = Sequential() model.add(Conv2D(32, (3, 3), padding = 'same', input_shape = (50, 50, 3))) model.add(Activation('relu')) model.add(Conv2D(32, (3, 3))) 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(Activation('relu')) 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(3)) model.add(Activation('softmax')) opt = keras.optimizers.rmsprop(lr = 0.0001, decay = 1e-6) model.compile(loss = 'categorical_crossentropy', optimizer = opt, metrics = ['accuracy']) # モデルの保存 model = load_model('./visual_cnn.h5') return model def main(): image = Image.open(sys.argv[1]) image = image.convert('RGB') image = image.resize((image_size, image_size)) data = np.asarray(image) X = [] X.append(data) X = np.array(X) model = build_model() result = model.predict([X])[0] predicted = result.argmax() percentage = int(result[predicted] * 100) print('{0}({1}%)'.format(classes[predicted], percentage)) if __name__ == '__main__': main()
実際に推定してみます。
友人たちとワイワイして、カップルがこちらを見ている下記の画像を読み込ませてみます。
なんとなくFっぽいですね。
コマンドラインの第二引数(左から3番目)にsample.jpgをおきます。
$ python predict.py sample.jpg
「F」の画像である確率が100%であると推定されました。
想定通り分類されています。
所感
実際のブライダルの画像は、3パターン(「ザ・結婚式」「みんなでワイワイ」「その他」)で分類できるほど単純ではないのが実情です。
ここからは、
・分類パターンを増やす
・教師なし学習で、クラスタリングする
などの展開ができます。
今回はシンプルな推定モデルの作成までやってみました。