【Python】ジェスチャー画像を分類してみた

こんにちは、のっくんです。

今日はKaggleにあったジェスチャー画像の分類を、ニューラルネットを使ってやっていきたいと思います。

Hand Gesture Recognition Databaseという、リープモーションで撮影した赤外線画像のデータベースを使います。

https://www.kaggle.com/gti-upm/leapgestrecog/version/1

データセットをダウンロードするには、Kaggleにログインする必要があります。

10種類のジェスチャー画像

画像データの読み込み

まず最初に画像とラベルの読み込みをやっていきます。

画像データをX、ラベルをyとします。

画像枚数は2万枚ありますが、1枚ずつopencvで読み込んでリストに追加(append)していきます。

ラベルはファイル名についている、01~10の10種類のラベルを使用することにします。

こちらも1枚ずつ、ファイル名からリストに追加(append)していきます。

import os
from os import walk
import cv2

mypath = "./leapGestRecog"
f = []
for (dirpath, dirnames, filenames) in walk(mypath):
    f.extend(os.path.join(dirpath, filename) for filename in filenames)
    
X = []
y = []
for i in f:
    img = cv2.imread(i)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    img = cv2.resize(img,(120,320))
    X.append(img)
    file_name = i.split("/")[4]
    y.append(file_name.split("_")[2])

これで、Xとyが作成できました。

リストからnumpy配列に変換

Xとyが作成できましたが、今のままだとリストです。

ディープラーニングをする際には扱うデータはnumpyの配列が定石なので、numpy配列に変換しましょう。

# リストをNumPy配列ndarrayに変換
import numpy as np

X = np.array(X)
y = np.array(y)

さてここでyのラベルの数を数えてみます。

# yのラベルと数を数え上げる
u, c = np.unique(y, return_counts=True)
print(dict(zip(u, c)))
{'01': 2000, '02': 2000, '03': 2000, '04': 2000, '05': 2000, '06': 2000, '07': 2000, '08': 2000, '09': 2000, '10': 2000}

01~10のラベルがありますが、ワンホットベクトル(1次元ベクター)に変換する際に、10があるとエラーが発生しました。

`np.where`を使って、10を00に変換します。

y = np.where(y=="10", "00", y)

データの分割

学習用とテスト用にデータを分割します。

from sklearn.model_selection import train_test_split
X_train, X_test,y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
print(X_train.shape)
print(X_test.shape)
print(y_train.shape)
print(y_test.shape)
(16000, 320, 120)
(4000, 320, 120)
(16000,)
(4000,)

16000枚を学習用、4000枚をテスト用に分割しました。

学習の前処理

画像データの場合、ディープラーニングにかける前に1次元にしたり浮動小数点数にする必要があります。

ラベルデータはワンホットベクトルにするのが定石です。

このあたりの処理は、numpyとkerasを使うとすぐに変換できます。

import keras

height= 120
witdh = 320
im_size = height*witdh
num_classes = 10

# データを一次元配列に変換 
X_train = X_train.reshape(-1, im_size).astype('float32') / 255
X_test = X_test.reshape(-1, im_size).astype('float32') / 255

# ラベルデータをOne-Hot形式に変換
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)

学習

入力層(120*320)〜中間層(512)〜出力層(10)の3層から構成される多層パーセプトロン(ニューラルネット)を作ります。

from keras.models import Sequential
from keras.layers import Dense, Dropout
from keras.callbacks import EarlyStopping

# モデルを定義
model = Sequential()
model.add(Dense(512, activation='relu', input_shape=(im_size,)))
model.add(Dense(num_classes, activation='softmax'))

es = EarlyStopping(monitor="val_loss", patience=2)
# モデルをコンパイル
model.compile(
    loss='categorical_crossentropy',
    optimizer='adam',
    metrics=['accuracy'])
 
# 学習を実行
hist = model.fit(X_train, y_train,
    batch_size=32, epochs=50,
    verbose=1,
    validation_data=(X_test, y_test),
                callbacks=[es])
 
# モデルを評価
score = model.evaluate(X_test, y_test, verbose=1)
print('正解率=', score[1], 'loss=', score[0])

実行結果は以下の通り。

Train on 16000 samples, validate on 4000 samples
Epoch 1/50
16000/16000 [==============================] - 15s 961us/step - loss: 5.1985 - acc: 0.6251 - val_loss: 4.9323 - val_acc: 0.6760
Epoch 2/50
16000/16000 [==============================] - 14s 877us/step - loss: 3.3693 - acc: 0.7701 - val_loss: 3.2928 - val_acc: 0.7900
Epoch 3/50
16000/16000 [==============================] - 14s 886us/step - loss: 3.2374 - acc: 0.7946 - val_loss: 3.2884 - val_acc: 0.7887
Epoch 4/50
16000/16000 [==============================] - 14s 879us/step - loss: 0.9810 - acc: 0.9134 - val_loss: 0.0268 - val_acc: 0.9952
Epoch 5/50
16000/16000 [==============================] - 14s 882us/step - loss: 0.0089 - acc: 0.9985 - val_loss: 0.0065 - val_acc: 0.9982
Epoch 6/50
16000/16000 [==============================] - 14s 879us/step - loss: 0.0059 - acc: 0.9989 - val_loss: 0.0039 - val_acc: 0.9992
Epoch 7/50
16000/16000 [==============================] - 14s 881us/step - loss: 0.0271 - acc: 0.9921 - val_loss: 0.4057 - val_acc: 0.9018
Epoch 8/50
16000/16000 [==============================] - 14s 885us/step - loss: 0.0613 - acc: 0.9854 - val_loss: 0.0079 - val_acc: 0.9978
4000/4000 [==============================] - 1s 165us/step
正解率= 0.99775 loss= 0.007857956944731995

EarlyStoppingを使っているので8エポックで終了し、精度は約99.7%でした。

実行速度はGeforce GTX1060を使って2分程度でした。

シンプルなネットワークでしたが、かなりの精度が出て驚きました。

というのも、前にcifar10データセットに多層パーセプトロン(MLP)を適用してあまり精度が出なかったからです。

精度が低かった場合には畳み込みニューラルネット(CNN)を試そうと思っていましたが、試さなくても良いかな。

おわり。