Edited at

Kerasによる、ものすごくシンプルな画像分類(りんごとオレンジ)

More than 1 year has passed since last update.

とりあえず、いろいろ読んでも良く解らないので keras + tensorflow でシンプルにやってみた。これがうまくいったらより複雑な問題にチャレンジしよう。

画像はpythonのImage.openで読み込んだら配列になっているので、これを1次元配列に変換したら、あとはKerasによる、ものすごくシンプルな深層学習の例と同じように学習、結果を検証する。

下記の fruit.py と同じディレクトリに以下のように学習用データとテスト用データを配置。

./data/train/apple/(学習用りんご画像)

/orange/(学習用オレンジ画像)

test/apple/(テスト用りんご画像)
/orange/(テスト用オレンジ画像)

画像は頑張ってグーグル画像検索から落とした。学習用画像はそれぞれ20枚くらい、テスト用画像は5枚づつ適当に選んで上記ディレクトリに保存しておく。気をつけるのは、学習用の画像とテスト用の画像は別でなければテストになりませんので学習用画像とは別のものをダウンロードします。

コードは下記のように。考え方は単純で、学習用りんご画像とオレンジ画像が入ったディレクトリ内の画像を一つ一つ読み込む。よみこんだ画像は[R,G,B]を一要素とする25x25の配列となっているので、これを乱暴だけど1次元画像に変換、あとは入力値を 255. で割って0~1の値にして学習用モデルに喰わせる。

ちなみに正解ラベルはラベルの値そのものではなく、0と1からなる配列に変換してから喰わせる。

たとえば 0[1,0]1[0,1] ようするに1が立っている位置のインデックスがラベルの値になるような配列。


fruit.py

from keras.models import Sequential

from keras.layers import Activation, Dense, Dropout
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 == "apple": # appleはラベル0
label = 0
elif dir == "orange": # orangeはラベル1
label = 1

for file in os.listdir(dir1):
if file != ".DS_Store":
# 配列label_listに正解ラベルを追加(りんご:0 オレンジ:1)
label_list.append(label)
filepath = dir1 + "/" + file
# 画像を25x25pixelに変換し、1要素が[R,G,B]3要素を含む配列の25x25の2次元配列として読み込む。
# [R,G,B]はそれぞれが0-255の配列。
image = np.array(Image.open(filepath).resize((25, 25)))
print(filepath)
# 配列を変換し、[[Redの配列],[Greenの配列],[Blueの配列]] のような形にする。
image = image.transpose(2, 0, 1)
# さらにフラットな1次元配列に変換。最初の1/3はRed、次がGreenの、最後がBlueの要素がフラットに並ぶ。
image = image.reshape(1, image.shape[0] * image.shape[1] * image.shape[2]).astype("float32")[0]
# 出来上がった配列をimage_listに追加。
image_list.append(image / 255.)

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

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

# モデルを生成してニューラルネットを構築
model = Sequential()
model.add(Dense(200, input_dim=1875))
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(2))
model.add(Activation("softmax"))

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

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

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

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

if dir == "apple":
label = 0
elif dir == "orange":
label = 1

for file in os.listdir(dir1):
if file != ".DS_Store":
label_list.append(label)
filepath = dir1 + "/" + file
image = np.array(Image.open(filepath).resize((25, 25)))
print(filepath)
image = image.transpose(2, 0, 1)
image = image.reshape(1, image.shape[0] * image.shape[1] * image.shape[2]).astype("float32")[0]
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, "%")


上を実行した結果は以下

(学習過程の出力)....

36/36 [==============================] - 0s - loss: 1.9686e-06 - acc: 1.0000 - val_loss: 0.0016 - val_acc: 1.0000

(以下、学習したモデルで分類を実行)
data/test/apple/090527innhuruennza1.jpg
1/1 [==============================] - 0s
label: 0 result: 0
data/test/apple/20140924pre01.jpg
1/1 [==============================] - 0s
label: 0 result: 0
data/test/apple/e964c5a13ae8f444a06563838efd745a_s.jpg
1/1 [==============================] - 0s
label: 0 result: 0
data/test/apple/img_1795.jpg
1/1 [==============================] - 0s
label: 0 result: 0
data/test/apple/top-rinngo.jpg
1/1 [==============================] - 0s
label: 0 result: 0
data/test/orange/32e44d81-acbc-48fc-98e6-77d09f07197a.jpg
1/1 [==============================] - 0s
label: 1 result: 1
data/test/orange/menu_155_3.jpg
1/1 [==============================] - 0s
label: 1 result: 1
data/test/orange/nable1.jpg
1/1 [==============================] - 0s
label: 1 result: 1
data/test/orange/Nhwj8QQm.jpg
1/1 [==============================] - 0s
label: 1 result: 1
data/test/orange/orange.jpg
1/1 [==============================] - 0s
label: 1 result: 1
seikai: 100.0 %

毎度100%ではなくて、たまに90%とかになる。


感想

とりあえずわからんので、乱暴に1次元配列に変換してやってみたら、まあそれなりに判別できた。ただ、これだと顔の判別とかは難しそうだ。より微妙な違いを判別する方法にチャレンジしていきたい。