"ディズニーヴィランズ" を判別するAIアプリを作ってみた

はじめに

この記事はプログラミング学習においてAIによる画像認識アプリを作成する過程を記録したものです。

ディズニーといえばプリンセス。ですが裏の主役であるヴィランズに光を当ててみたいと思い、ヴィランズの識別を行いました。

アプリ開発は初心者のためまずは4人の識別から行いました。

目次

はじめに
作業手順・・・①〜④
まとめ

作業手順

画像の識別は下記の流れで行います。
①画像(データの収集)
②画像(データ)の前処理
③学習器の作成(機械学習など)
④モデルのテストとチューニング
⑤アプリの作成
順に解説していきます。

①画像の収集

スクレイピングを用いて 画像収集を行いました。今回はiCrawlerを利用してGoogle検索エンジンを利用した画像収集を行いました。またそれだけでは数が足りなかったため、それぞれのディズニーアニメのスクリーンショットを撮影した画像を利用しました。
また、検索エンジンを利用した場合、使用できない不要データが多くあったため、全てのデータを一旦ドライブへ移して可視化し、必要データと不要データに分ける作業を行いました。

まずはインストール

!pip install icrawlerω

次に検索ワードと保存するディレクトリを指示

from icrawler.builtin import BingImageCrawler

keyword_lists = ["マレフィセント アニメ",”アースラ アニメ”,”ラプンツェル ゴーテル”,
"ハートの女王"]
class_lists = [ "Maleficent","Ursula","Gortel","Heart",]

for i in range(len(keyword_lists)):
 crawler = BingImageCrawler(storage={"root_dir": class_lists[i]})
 crawler.crawl(keyword=keyword_lists[i], max_num=100)

上記でドライブに保存されているため、各フォルダを確認、データを精査したのち、testフォルダを作り、テストデータと学習用データに分けました。

★余談★(データリーケージについて)
当初学習器を作成した際、テストデータと学習用データに分けることを行わず、そのまま全てのデータを水増ししたのち、テストデータと学習用データに分けて学習器の作成を行っていました。精度はとてもよく完成したので実際テストを行ってみると、全く思った通りの判定を行ってくれず、、、
どうしたものかと思っていたらデータリーケージが原因でした。
思わぬ壁にぶち当たりました。。。この部分はAidemyの教科書には載っていない部分なのでいい経験になったと思っています。

データリーケージとは?
リーケージとは、本来得られるはずのないデータをモデルの学習時に使用してしまうことを言います。リーケージが発生することを「リークする」とも言います。
例えるなら、学校で受ける期末テストのときに、カンニングをしていい点数を取るようなイメージです。カンニングでは、本来自分が持つ実力をかさ増しして見せることができます。
<参考:機械学習のリーケージについて考える>
https://note.com/kenichiro/n/n2ff08344160a

②画像(データ)の前処理

各クラスに対して50枚~60枚程度のデータしか集められなかったため、データの水増しを行いました。行った手法は下記の通りです。

from keras.callbacks import EarlyStopping, ModelCheckpoint
from tensorflow.keras.optimizers import RMSprop
from keras.utils.np_utils import to_categorical
import tensorflow.keras.utils as utils
from tensorflow.keras.layers import Activation, Dropout, Flatten, Dense
from tensorflow.keras.layers import Conv2D, MaxPooling2D
from tensorflow.keras.models import Sequential
import os
import glob
from os import listdir
import numpy as np
from sklearn import model_selection
from keras.preprocessing.image import ImageDataGenerator, load_img, img_to_array, array_to_img
import matplotlib.pylab as plt

def return_file(path):
   return [filename for filename in listdir(search_dir+path) if not filename.startswith('.')]

search_dir = "学習用データの保存先ディレクトリ"
search_topic = ["Gortel", "Heart", "Maleficent", "Ursula"] #ファイル名
image_size = 100

X = []
Y = []
for index, classlabel in enumerate(search_topic):

   # 拡張する画像群の読み込み
   images = return_file(search_topic[index])  # ファイル名を指定
   # 拡張する際の設定
   generator = ImageDataGenerator(
       rotation_range=90,  # 90°まで回転
       width_shift_range=0.1, # 水平方向にランダムでシフト
       height_shift_range=0.1, # 垂直方向にランダムでシフト
       channel_shift_range=50.0, # 色調をランダム変更
       shear_range=0.39, # 斜め方向(pi/8まで)に引っ張る
       horizontal_flip=True, # 垂直方向にランダムで反転
       vertical_flip=True # 水平方向にランダムで反転
   )
   # 読み込んだ画像を順に拡張
   for i in range(len(images)):
       img = load_img(
           search_dir+"/"+search_topic[index]+"/"+images[i], target_size=(image_size, image_size))
       # 画像を配列化して転置a
       x = img_to_array(img)
       x = np.expand_dims(x, axis=0)

       # 画像の拡張
       g = generator.flow(x, batch_size=1)

       # g.next()の回数分拡張される。1つの入力画像から何枚拡張するかを指定
       for i in range(10):
           bach = g.next()
           g_img = bach[0].astype(np.uint8)
           X.append(g_img)
           Y.append(index)

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

rand_index = np.random.permutation(np.arange(len(X)))
X_shuffle = X[rand_index]
Y_shuffle = Y[rand_index]

#X_train, X_test, y_train, y_test = model_selection.train_test_split(
#    X_shuffle, Y_shuffle, test_size=0.3)
#xy = (X_train, X_test, y_train, y_test)
xy = (X_shuffle,Y_shuffle)
#Numpy配列をファイルに保存
np.savez('保存先ディレクトリ',X_shuffle,Y_shuffle)

print(len(X))#データ数の確認


③学習器の作成

KerasのCNNであるVGG16を使用しました。画像がある程度似ていたのもありある程度の精度を出すことができました。

import os
import cv2
import numpy as np
import matplotlib.pyplot as plt
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.layers import Dense, Dropout, Flatten, Input
from tensorflow.keras.applications.vgg16 import VGG16
from tensorflow.keras.models import Model, Sequential
from tensorflow.keras import optimizers


npz = np.load('./data_2.npz', allow_pickle=True)
X_train,y_train = npz["arr_0"],npz["arr_1"]
def return_file(path):
   return [search_dir+path+"/"+filename for filename in listdir(search_dir+path) if not filename.startswith('.')]

search_dir = "test/"  #テストデータのディレクトリ
search_topic = ["Gortel", "Heart", "Maleficent", "Ursula"] 

X_test=[]
y_test=[]

for index, classlabel in enumerate(search_topic):

   images = return_file(search_topic[index])
   for img_path in images:
     img = cv2.imread(img_path)
     print(img_path)
     img = cv2.resize(img,(image_size,image_size))
     X_test.append(img)
     y_test.append(index)

X_test = np.array(X_test)
y_test = np.array(y_test)

y_train = to_categorical(y_train)
y_test = to_categorical(y_test)

input_tensor = Input(shape=(image_size, image_size, 3))
vgg16 = VGG16(include_top=False, weights='imagenet', input_tensor=input_tensor)
top_model = Sequential()
top_model.add(Flatten(input_shape=vgg16.output_shape[1:]))
top_model.add(Dense(256, activation='sigmoid'))
top_model.add(Dropout(0.1))
top_model.add(Dense(len(search_topic), activation='softmax'))
model = Model(inputs=vgg16.input, outputs=top_model(vgg16.output))

for layer in model.layers[:15]:
layer.trainable = False

model.compile(loss='categorical_crossentropy', optimizer=optimizers.SGD(
   lr=1e-4, momentum=0.9), metrics=['accuracy'])

es_cb = EarlyStopping(monitor='val_loss', patience=3, verbose=0, mode='auto')

history = model.fit(X_train, y_train, validation_data=(X_test, y_test), validation_split=0.1, shuffle=True,
                   batch_size=32, epochs=9, callbacks=[es_cb])

plt.plot(history.history["accuracy"], label="accuracy", ls="-", marker="o")
plt.plot(history.history["val_accuracy"],
        label="val_accuracy", ls="-", marker="x")
plt.ylabel("accuracy")
plt.xlabel("epoch")
plt.legend(loc="best")
plt.show()

model.save('model_2.h5')
scores = model.evaluate(X_test, y_test, verbose=1)
print('Test loss:', scores[0])
print('Test accuracy:', scores[1])

④モデルのテストとチューニング

先述したデータリーケージ以後は精度も問題なかったので学習器の作成はこれまでとしました。

⑤アプリの作成

アプリは講座と同じくherokuを利用して作成しました。デプロイに多少手こずりました。herokuへのデプロイの際は誤字や空白をいれてしまっているとエラーがでて回収も大変でした・・・。
HTMLとCSSを利用してデザインにも多少こだわってみました。
javascriptも調べてモーダルコンテンツの作成も行ってみました。
作成したアプリはこちらです↓

まとめ

今回の講座を通してpythonの基礎を始め、機械学習や画像処理等について学び、簡単ではありますがアプリ作成まで実装することができました。学習器がうまく動いた時、アプリの実装ができた時、完成したときはとても嬉しかったです。なかなか知識不足を感じる部分もあったので、復習もかね勉強を続けていきたいと思っています。ご協力をいただきました多くの方々、ありがとうございました。

この記事が気に入ったらサポートをしてみませんか?