畳み込みニューラルネットワークを使った画像検出モデルの作成方法

深層学習(ディープラーニング)で画像認識に挑戦

こんにちは。

AI coordinatorの清水秀樹です。

以前の記事でMNISTを使った数字画像認識の学習モデルを作成しました。

でも、やっぱり色々な画像を認識できる学習済モデルを作成したいと思い、画像認識モデルで有名なCIFAR-10を使った学習済モデルの作成に挑戦してみました。

で、その学習済みモデルを使って実際に画像認識ができるところまで作成してみたので、その内容を紹介します。

 

参考にした書籍

これからDeep Learningの勉強をするなら、こちらで紹介する書籍が参考になります。

 

開発環境

macOS Sierra

Anaconda3-4.2.0-MacOSX-x86_64

keras 1.2.2

tensorflow 0.12.1

 

CIFAR-10とは

今回使用する画像データセットCIFAR-10とは、10種類の画像それぞれ6000枚集めた計60000枚の画像データセットのことです。

そのうち訓練用画像が50000枚、テスト用画像が10000枚の構成になった画像データセットです。

 

どんな画像が入っているのかというと、

[“airplane”,”automobile”,”bird”,”cat”,”deer”,

“dog”,”frog”,”horse”,”ship”,”truck”]

です。

 

自分で画像を準備するとなると、ものすごい労力がかかりますが、手頃に画像データの学習を始めるならCIFAR-10がオススメです。

最近ではCIFAR-100とかいう画像データセットもあるようです。

詳細はこちらのサイトで確認してみてください。

 

学習モデルの作成

では早速作成に取り掛かりたいと思います。

深層学習(ディープラーニング)で有名な畳み込みニューラルネットワークの勉強も兼ねて実装しています。

今回は9層で試してみました。

正直、精度の高い学習モデルを作成するためには、どのくらいの層が必要で、過学習を防ぐためには「Dropout」にどんな値を設定すれば良いのか良く分かりません。

この辺については、そのうち色々な実験を繰り返して真面目に勉強していくとして、今回は適当に学習モデルを定義して実装することにします。

学習回数も適当です。

この辺りはお好みで修正してください。

取り敢えず以下のソースそのままでも動きます。

# coding:utf-8
import numpy as np
import scipy.misc
import tensorflow as tf
from keras.utils import np_utils
from keras.models import Sequential, Model, model_from_json
from keras.layers.core import Dense, Activation, Flatten, Dropout
from keras.layers.convolutional import Convolution2D
from keras.layers.convolutional import MaxPooling2D
from keras.optimizers import SGD
from keras.layers.normalization import BatchNormalization
from keras.callbacks import EarlyStopping, TensorBoard, ModelCheckpoint
from keras.datasets import cifar10

import keras.backend.tensorflow_backend as KTF
import matplotlib.pyplot as plt

#1024個の一層目を定義
in_shape = (32, 32, 3) #縦、横、RGB

#クラスを指定
categories = ["airplane","automobile","bird","cat","deer","dog","frog","horse","ship","truck"]
nub_classes = len(categories)

#モデルの作成
def build_model():
    model = Sequential()
    
    #畳み込み層の作成
    #1層目の追加  1024個の層を最初に作り、フィルター3*3のフィルターを16個作成
    model.add(Convolution2D(16, 3, 3, border_mode="same", input_shape=in_shape)) 
    model.add(Activation("relu"))
    
    #2層目の畳み込み層
    model.add(Convolution2D(16, 3, 3, border_mode="same"))
    model.add(Activation("relu"))
    
     #プーリング層
    model.add(MaxPooling2D())
    
    #Dropoutとは過学習を防ぐためのもの 0.5は次のニューロンへのパスをランダムに半分にするという意味
    model.add(Dropout(0.5))
    
    #3層目の作成
    model.add(Convolution2D(32, 3, 3, border_mode="same"))
    model.add(Activation("relu"))
    
    #4層目の作成
    model.add(Convolution2D(32, 3, 3, border_mode="same"))
    model.add(Activation("relu"))
    
     #プーリング層
    model.add(MaxPooling2D())
    model.add(Dropout(0.5))
    
    #5層目
    model.add(Convolution2D(64, 3, 3, border_mode="same"))
    model.add(Activation("relu"))
    
    #6層目
    model.add(Convolution2D(64, 3, 3, border_mode="same"))
    model.add(Activation("relu"))
    
    #プーリング層
    model.add(MaxPooling2D())
    
    #Dropout
    model.add(Dropout(0.5))
    
    #7層目
    model.add(Convolution2D(128, 3, 3, border_mode="same"))
    model.add(Activation("relu"))
    
    #Dropout
    model.add(Dropout(0.5))
    
    #平坦化
    model.add(Flatten())
    
    #8層目 全結合層 FC
    model.add(Dense(100))
    model.add(Activation("relu"))
    
    #Dropout
    model.add(Dropout(0.5))
    
    #8層目 引数nub_classesとは分類の数を定義する。
    model.add(Dense(nub_classes))
    model.add(Activation('softmax'))
    
    #ここまででモデルの層完成
    
    #lossは損失関数を定義するところ
    model.compile(loss="categorical_crossentropy", 
        metrics   = ["accuracy"], 
        optimizer = "adam"
    )
    return model

if __name__ == "__main__":
    #ここに読み込み処理と関数呼び出しを行う
    
    #cifar10からデータを取得 10種類の画像データセットがある 計5万枚
    (X_train, y_train), (X_test, y_test) = cifar10.load_data()
    print(X_train.shape, y_train.shape)
    print(X_test.shape, y_test.shape)

    y_train = np_utils.to_categorical(y_train)
    y_test  = np_utils.to_categorical(y_test)

    X_train = X_train.astype('float32') / 255
    X_test  = X_test.astype('float32') / 255
    
    print("X_train", X_train.shape)
    print("y_train", y_train.shape)
    print("X_test", X_test.shape)
    print("y_test", y_test.shape)
    
    #ここからが上記で作成したモデルを呼び出す処理
    model = build_model()
    model.fit(X_train, y_train, 
        nb_epoch=30, #学習させる回数 今回は10 回数はお好みで pythonのnb_epochとはrangeの繰り返しのこと
        batch_size=500, #無作為に何枚学習に使用するか決定する。数字はなんでも良い
        validation_data=(X_test, y_test)
             )
    
    #学習モデルの保存
    json_string = model.to_json()
    
    #モデルのファイル名 拡張子.json
    open('cifar10.json', 'w').write(json_string)
    
    #重みファイルの保存 拡張子がhdf5
    model.save_weights('cifar10.hdf5')

    # モデルの評価evaluate
    score = model.evaluate(X_train, y_train)
    
    print("test loss", score[0])
    print("test acc",  score[1])

以下学習結果です。

(50000, 32, 32, 3) (50000, 1)
(10000, 32, 32, 3) (10000, 1)
X_train (50000, 32, 32, 3)
y_train (50000, 10)
X_test (10000, 32, 32, 3)
y_test (10000, 10)
Train on 50000 samples, validate on 10000 samples
Epoch 1/30
50000/50000 [==============================] - 131s - loss: 2.1206 - acc: 0.1993 - val_loss: 1.9238 - val_acc: 0.2699
Epoch 2/30
50000/50000 [==============================] - 133s - loss: 1.8135 - acc: 0.3127 - val_loss: 1.6944 - val_acc: 0.3694
Epoch 3/30
50000/50000 [==============================] - 137s - loss: 1.6492 - acc: 0.3877 - val_loss: 1.5570 - val_acc: 0.4337
Epoch 4/30
50000/50000 [==============================] - 137s - loss: 1.5521 - acc: 0.4301 - val_loss: 1.5233 - val_acc: 0.4420
Epoch 5/30
50000/50000 [==============================] - 132s - loss: 1.4758 - acc: 0.4589 - val_loss: 1.3826 - val_acc: 0.4949
Epoch 6/30
50000/50000 [==============================] - 136s - loss: 1.4423 - acc: 0.4738 - val_loss: 1.4624 - val_acc: 0.4803
Epoch 7/30
50000/50000 [==============================] - 132s - loss: 1.3971 - acc: 0.4916 - val_loss: 1.3176 - val_acc: 0.5252
Epoch 8/30
50000/50000 [==============================] - 136s - loss: 1.3530 - acc: 0.5128 - val_loss: 1.3531 - val_acc: 0.5201
Epoch 9/30
50000/50000 [==============================] - 136s - loss: 1.3192 - acc: 0.5225 - val_loss: 1.3031 - val_acc: 0.5377
Epoch 10/30
50000/50000 [==============================] - 131s - loss: 1.2908 - acc: 0.5376 - val_loss: 1.1918 - val_acc: 0.5726
Epoch 11/30
50000/50000 [==============================] - 142s - loss: 1.2623 - acc: 0.5476 - val_loss: 1.2366 - val_acc: 0.5513
Epoch 12/30
50000/50000 [==============================] - 142s - loss: 1.2462 - acc: 0.5560 - val_loss: 1.1357 - val_acc: 0.5912
Epoch 13/30
50000/50000 [==============================] - 142s - loss: 1.2225 - acc: 0.5619 - val_loss: 1.1851 - val_acc: 0.5775
Epoch 14/30
50000/50000 [==============================] - 136s - loss: 1.1996 - acc: 0.5700 - val_loss: 1.2754 - val_acc: 0.5499
Epoch 15/30
50000/50000 [==============================] - 146s - loss: 1.1814 - acc: 0.5777 - val_loss: 1.1019 - val_acc: 0.6053
Epoch 16/30
50000/50000 [==============================] - 144s - loss: 1.1655 - acc: 0.5868 - val_loss: 0.9921 - val_acc: 0.6471
Epoch 17/30
50000/50000 [==============================] - 146s - loss: 1.1480 - acc: 0.5919 - val_loss: 1.0030 - val_acc: 0.6428
Epoch 18/30
50000/50000 [==============================] - 153s - loss: 1.1323 - acc: 0.5991 - val_loss: 0.9781 - val_acc: 0.6510
Epoch 19/30
50000/50000 [==============================] - 154s - loss: 1.1083 - acc: 0.6077 - val_loss: 1.0131 - val_acc: 0.6386
Epoch 20/30
50000/50000 [==============================] - 141s - loss: 1.1111 - acc: 0.6083 - val_loss: 1.0421 - val_acc: 0.6278
Epoch 21/30
50000/50000 [==============================] - 136s - loss: 1.0894 - acc: 0.6158 - val_loss: 1.0570 - val_acc: 0.6222
Epoch 22/30
50000/50000 [==============================] - 139s - loss: 1.0692 - acc: 0.6239 - val_loss: 0.9527 - val_acc: 0.6588
Epoch 23/30
50000/50000 [==============================] - 137s - loss: 1.0726 - acc: 0.6238 - val_loss: 0.9829 - val_acc: 0.6491
Epoch 24/30
50000/50000 [==============================] - 138s - loss: 1.0509 - acc: 0.6313 - val_loss: 0.9221 - val_acc: 0.6705
Epoch 25/30
50000/50000 [==============================] - 136s - loss: 1.0448 - acc: 0.6341 - val_loss: 0.9370 - val_acc: 0.6644
Epoch 26/30
50000/50000 [==============================] - 147s - loss: 1.0364 - acc: 0.6339 - val_loss: 0.9620 - val_acc: 0.6543
Epoch 27/30
50000/50000 [==============================] - 140s - loss: 1.0223 - acc: 0.6417 - val_loss: 0.9511 - val_acc: 0.6591
Epoch 28/30
50000/50000 [==============================] - 145s - loss: 1.0115 - acc: 0.6443 - val_loss: 0.8920 - val_acc: 0.6857
Epoch 29/30
50000/50000 [==============================] - 144s - loss: 1.0084 - acc: 0.6472 - val_loss: 0.9256 - val_acc: 0.6732
Epoch 30/30
50000/50000 [==============================] - 145s - loss: 0.9967 - acc: 0.6517 - val_loss: 0.8526 - val_acc: 0.6931
49952/50000 [============================>.] - ETA: 0stest loss 0.788950727882
test acc 0.71516
In [ ]:

78%の精度となりました。

かなり精度が悪いですね。

畳み込みニューラルネットワークの学習モデルをもう少し工夫するか、学習回数を大幅に増やすなどの対応が必要そうです。

ただ今回は学習済モデルの作成と、その学習済モデルで実際に画像を認識させてみるところが目的なので、学習結果は多少悪くても良しとします。

 

学習済モデルで画像認識に挑戦

学習済みモデルができたら、実際に画像を準備して認識させてみましょう。

今回準備した画像は以下の画像たちです。

適当にネットから拝借してきました。

 

blog_testpicというディレクトリを作成し、そこに認識させたい画像を保存して以下のソースを実行してみましょう。

# coding:utf-8
import keras
import sys, os
import scipy
import scipy.misc
import numpy as np
from keras.models import model_from_json

import json
imsize = (32, 32)

#認識したい画像のパスを指定する
# ./blog_testpic/xxx.jpg といった指定を意味する
testpic = "./blog_testpic/"
#使用するモデルを指定する
keras_model = "./cifar10.json"
keras_param = "./cifar10.hdf5"

#画像の読み込み
#読み込んだ画像を32X32にする
def load_image(path):
    img = scipy.misc.imread(path, mode="RGB")
    img = scipy.misc.imresize(img, imsize)
    img = img / 255.0 #RGBの最大値を指定
    return img

#リストで結果を返す関数
def get_file(dir_path):
    filenames = os.listdir(dir_path)
    return filenames

#メイン開始
if __name__ == "__main__":

    #画像を読み込んで、ファイル名をリスト化する。
    pic = get_file(testpic)
    print(pic)
    
    #モデルの読み込み
    model = model_from_json(open(keras_model).read())
    model.load_weights(keras_param)
    model.summary()
    ##ここまでで実行するとモデルの形が結果に表示される
    
    #リスト化したファイルから読み込んで処理する
    for i in pic:
        print(i) # ファイル名の出力
        
        #画像ディレクトリにあるファイルのi番目を読み込む
        img = load_image(testpic + i)
        
        prd = model.predict(np.array([img]))
        print(prd)
        
        #確信度最大値を取得する
        prelabel = np.argmax(prd, axis=1)
        print(prelabel)
        
        # 各画像ファイルにairplaneなら0が、dogなら5、shipなら8のラベルが付いている
        if prelabel == 0:
            print(">>> airplane")
        elif prelabel == 1:
            print(">>> automobile")
        elif prelabel == 2:
            print(">>> bird")
        elif prelabel == 3:
            print(">>> cat")
        elif prelabel == 4:
            print(">>> deer")
        elif prelabel == 5:
            print(">>> dog")
        elif prelabel == 6:
            print(">>> frog")
        elif prelabel == 7:
            print(">>> horse")
        elif prelabel == 8:
            print(">>> ship")
        elif prelabel == 9:
            print(">>> truck")

        print()
        print()

結果にinputにした画像ファイル名と認識した画像の種類を出力しているようにしています。

以下、実行結果です。

Total params: 351,846
Trainable params: 351,846
Non-trainable params: 0
____________________________________________________________________________________________________
airplane.jpg
[[  9.93673444e-01   3.23235804e-06   2.19863467e-03   8.30059871e-05
    2.08738929e-04   5.68661517e-06   3.38327550e-06   3.45552667e-06
    3.79910809e-03   2.12868217e-05]]
[0]
>>> airplane


automobile.jpg
[[  6.19480988e-06   9.98218834e-01   5.71798502e-08   4.14908605e-08
    5.71382852e-09   2.49827292e-09   2.76060405e-07   1.31873146e-09
    3.17344893e-06   1.77149393e-03]]
[1]
>>> automobile


dog.jpg
[[  3.08061601e-04   1.27699059e-05   8.04589465e-02   2.73394156e-02
    6.33710861e-01   9.80996788e-02   1.21392561e-02   1.47868127e-01
    5.19889863e-06   5.76526072e-05]]
[4]
>>> deer


ship.jpg
[[  3.52313067e-03   9.79593642e-06   2.25756394e-05   7.40491805e-06
    3.58149464e-06   8.80972451e-08   5.26029339e-07   1.23592031e-07
    9.96417522e-01   1.51478107e-05]]
[8]
>>> ship


truck.jpg
[[  1.21543708e-05   1.17212556e-04   8.85880809e-08   6.86258034e-07
    1.01148956e-07   2.54100208e-08   6.89700652e-09   2.59936337e-06
    1.81963208e-06   9.99865294e-01]]
[9]
>>> truck

概ね正解していますが、dogをdeerと間違えています。

学習精度が低いのでしょうがない感じですね。

 

ただ、実際に目的としていた画像を認識させてみるところまでは実装できたので、今後はどうやったら学習精度が上がるか模索していきたいと思います。

併せて、自分で集めた画像から学習モデルの作成にも挑戦していくので、成功したら紹介していきたいと思います。

 


その他の物体検出記事はこちらから

 

それではまた。

あなたにオススメの記事

コメント

  1. この記事へのコメントはありません。

  1. この記事へのトラックバックはありません。

PAGE TOP