Edited at

Kerasは相撲取りとアイドルを見分けられるかやってみた

More than 1 year has passed since last update.

Keras(バックエンドはtensorflow)で、この度横綱に昇進した稀勢の里と、千年に一人の逸材の橋本環奈を見分けられるのかやってみた。で、その途中でアイドル同士を見分けることってできるんだろうかと思って広瀬すずも追加して三つ巴でやってみた。

Kerasによる、ものすごくシンプルな画像分類(りんごとオレンジ)では読み込んだ画像をフラットな1次元配列に変換してモデルの学習に使ったけど、今回はより本格的に畳み込み層(Convolution2D)を追加してやってみます。

Kerasのドキュメントを読むと、Convolution2Dに喰わせる前に、画像を読み込んだ配列の次元をいじる必要があるようです。例えば 縦:100 x 横:100 x RGB:3 の画像を読み込んだ場合、100x100x3の配列として読み込まれますが、これを 3x100x100の形に直してからConvolution2Dに与えろってことらしいです。

これらからまず、

./data/train/kisenosato/(稀勢の里の画像)

/hashimoto/(橋本環奈の画像)
/hirose/(広瀬すずの画像)

./data/test/kisenosato/(稀勢の里の画像)
/hashimoto/(橋本環奈の画像)
/hirose/(広瀬すずの画像)

として、 data/train 以下の画像は学習用、 data/test 以下の画像は学習後のテスト用として使用する。学習用の画像はそれぞれ30枚づつぐらい、Google画像検索で手動で集めたです....

コードはこんな感じ


kisenosato.py

from keras.models import Sequential

from keras.layers import Activation, Dense, Dropout, Convolution2D, Flatten, MaxPooling2D
from keras.utils.np_utils import to_categorical
from keras.optimizers import Adagrad
from keras.optimizers import Adam
import numpy as np
from PIL import Image
import os

# 学習用のデータを作る.
image_list = []
label_list = []

# ./data/train 以下のorange,appleディレクトリ以下の画像を読み込む。
for dir in os.listdir("data/train"):
if dir == ".DS_Store":
continue

dir1 = "data/train/" + dir
label = 0

if dir == "kisenosato": # 稀勢の里はラベル0
label = 0
elif dir == "hashimoto": # 橋本環奈はラベル1
label = 1
elif dir == "hirose": # 広瀬すずはラベル2
label = 2

for file in os.listdir(dir1):
if file != ".DS_Store":
# 配列label_listに正解ラベルを追加(稀勢の里:0 橋本環奈:1 広瀬すず:2)
label_list.append(label)
filepath = dir1 + "/" + file
# 画像を100x100pixelに変換し、1要素が[R,G,B]3要素を含む配列の100x100の2次元配列として読み込む。
# [R,G,B]はそれぞれが0-255の配列。
image = np.array(Image.open(filepath).resize((100, 100)))
print(filepath)
# 配列を変換し、[[Redの配列],[Greenの配列],[Blueの配列]] のような形にする。
image = image.transpose(2, 0, 1)
print(image.shape)
# 出来上がった配列をimage_listに追加。
image_list.append(image / 255.)

# kerasに渡すためにnumpy配列に変換。
image_list = np.array(image_list)

# ラベルの配列を1と0からなるラベル配列に変更
# 0 -> [1,0,0], 1 -> [0,1,0] という感じ。
Y = to_categorical(label_list)

# モデルを生成してニューラルネットを構築
model = Sequential()
model.add(Convolution2D(32, 3, 3, border_mode='same', input_shape=(3, 100, 100)))
model.add(Activation("relu"))
model.add(Convolution2D(32, 3, 3))
model.add(Activation("relu"))
model.add(MaxPooling2D(pool_size=(2, 2), border_mode=("same")))
model.add(Dropout(0.25))

model.add(Flatten())

model.add(Dense(200))
model.add(Activation("relu"))
model.add(Dropout(0.2))

model.add(Dense(200))
model.add(Activation("relu"))
model.add(Dropout(0.2))

model.add(Dense(3))
model.add(Activation("softmax"))

# オプティマイザにAdamを使用
opt = Adam(lr=0.0001)
# モデルをコンパイル
model.compile(loss="categorical_crossentropy", optimizer=opt, metrics=["accuracy"])
# 学習を実行。10%はテストに使用。
model.fit(image_list, Y, nb_epoch=1000, batch_size=25, validation_split=0.1)

# テスト用ディレクトリ(./data/train/)の画像でチェック。正解率を表示する。
total = 0.
ok_count = 0.

for dir in os.listdir("data/test"):
if dir == ".DS_Store":
continue

dir1 = "data/test/" + dir
label = 0

if dir == "kisenosato":
label = 0
elif dir == "hashimoto":
label = 1
elif dir == "hirose":
label = 2

for file in os.listdir(dir1):
if file != ".DS_Store":
label_list.append(label)
filepath = dir1 + "/" + file
image = np.array(Image.open(filepath).resize((100, 100)))
print(filepath)
image = image.transpose(2, 0, 1)
result = model.predict_classes(np.array([image / 255.]))
print("label:", label, "result:", result[0])

total += 1.

if label == result[0]:
ok_count += 1.

print("seikai: ", ok_count / total * 100, "%")



2017/02/15追記

ニューラルネットワークにConvolution2Dを1層追加して合わせて2層とし、加えてMaxPooling2D層を追加したところ識別率が上がりました。


さて、結果の出力ですが、上記のスクリプトを実行すると学習プロセスが表示されて、最終的に

Epoch 1000/1000

108/108 [==============================] - 0s - loss: 2.8859e-05 - acc: 1.0000 - val_loss: 0.6708 - val_acc: 0.7500

data/test/hashimoto/1032kanna-top_1200x.jpg
1/1 [==============================] - 0s
label: 1 result: 1
data/test/hashimoto/4616_original-e1444887883222.jpg
1/1 [==============================] - 0s
label: 1 result: 1
data/test/hashimoto/d724e07900b770b9f78eaa1b111c8a2f.jpg
1/1 [==============================] - 0s
label: 1 result: 1
data/test/hashimoto/JLU_4kBV.jpeg
1/1 [==============================] - 0s
label: 1 result: 1
data/test/hashimoto/wam1022-kanna-003_x480.jpg
1/1 [==============================] - 0s
label: 1 result: 1
data/test/hashimoto/WS000043.jpg
1/1 [==============================] - 0s
label: 1 result: 1
data/test/hashimoto/橋本環奈.jpg
1/1 [==============================] - 0s
label: 1 result: 2
data/test/hirose/20170110hirose.jpg
1/1 [==============================] - 0s
label: 2 result: 2
data/test/hirose/2e2d42fcee97871743e9a52f54c0fabb_319.jpeg
1/1 [==============================] - 0s
label: 2 result: 2
data/test/hirose/m_E5BA83E780ACE38199E3819A.jpg
1/1 [==============================] - 0s
label: 2 result: 2
data/test/hirose/mig.jpeg
1/1 [==============================] - 0s
label: 2 result: 2
data/test/hirose/uV3Uh8toqAmGDHQ_Inuz4_109.png
1/1 [==============================] - 0s
label: 2 result: 2
data/test/kisenosato/2012062514095159c.jpg
1/1 [==============================] - 0s
label: 0 result: 0
data/test/kisenosato/20161226-00000028-tospoweb-000-1-view.jpg
1/1 [==============================] - 0s
label: 0 result: 0
data/test/kisenosato/images.jpeg
1/1 [==============================] - 0s
label: 0 result: 0
data/test/kisenosato/kise.jpg
1/1 [==============================] - 0s
label: 0 result: 0
data/test/kisenosato/o03780450m0023152112.jpg
1/1 [==============================] - 0s
label: 0 result: 0
seikai: 94.11764705882352 %

実行結果の出力は上記の通り。

結果を見やすく表にすると...



判定:橋本環奈



判定:橋本環奈



判定:橋本環奈



判定:橋本環奈



判定:橋本環奈



判定:橋本環奈



判定:広瀬すず(不正解!)



判定:広瀬すず



判定:広瀬すず



判定:広瀬すず



判定:広瀬すず



判定:広瀬すず



判定:稀勢の里



判定:稀勢の里



判定:稀勢の里



判定:稀勢の里



判定:稀勢の里

というわけで識別率は94.11%となりました。一枚が不正解、他の全ては正解です。素材の画像が少ない状態であることを考えると結構いい確率なのでは。

あと、ラベルは 0が稀勢の里 1が橋本環奈 2が広瀬すず なんですが、稀勢の里とアイドルを間違えてないところがさすがです笑 橋本環奈と広瀬すずは一箇所だけ間違えてますね。正直なところ、相撲取りとアイドルは見分けられても、アイドル同士はこの学習用画像数では難しいのでは、って思ってたら結構見分けられるんですねえ。

とりあえず画像の分類については一旦ここで置いて、今度はRNNをやってみようと思います。