自作のデータで画像認識モデルをつくって遊ぼう！

#はじめに
僕自身以前MNISTとかじゃなくて自作のデータで画像認識をやりたかったけどなかなかズバリこれ！！ていう記事は少なかったり、できても出力されるのは正解率とかだけで実験ができなくて詰まったことがあったのでまとめたいと思います。
余裕があったらwebアプリに搭載して画像認識アプリを作るところまで書きたい。

至らぬところ等あると思いますが間違っているところ等ありましたらご指摘いただけると幸いです。

#大まかな流れ、できること
最近の画像認識手法の基本であるCNNを用います。
自分で用意した画像を学習し認識できるようなモデルを作成し、さらに学習済みデータを出力して、別コードにてそれを読みだしてあそべるようにします。
今回は僕の自宅の三毛猫と白猫を判別するプログラムを作成します。

#環境
- win10
- python 3.7.3
- numpy
- Pillow 6.1.0
- tensorflow==1.13.1
- keras==2.1.2
- sklearn

#コード
すべてのコードは[github](https://github.com/Ryunosuke-Ikeda/CNN_app)にもありますのでそこからダウンロードしてください。



#その１：データの準備＆水増し
まずデータの準備を行います。
ここでは非常にシンプルで集めた画像をフォルダーに分けて保存するだけになります。
（ここで判別したいものごとにファイルを分けてください。以下画像のsiroとmikeがそうです。）

また予測したい画像を保存しておくフォルダーのtest_imgも作っておいてください。
![スクリーンショット (94).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556547/cd3c2110-f76c-e6ab-d2e6-6f4d555af745.png)




次にデータの水増し＆データセット化を行います。
データの水増しとは、学習十分なデータを人の手で集めることは非常に大変（めんどくさい）為、ある程度画像を集めたらその画像を回転させたり反転させたりしてデータセットを水増しし、学習を行う工夫になります。
以下のコードは先ほどあげた画像のaug.pyとなっています。

```python:aug.py
from PIL import Image
import os, glob
import numpy as np
from keras.utils import np_utils
from sklearn import model_selection
from sklearn.model_selection import train_test_split

classes = ["mike","siro"]####ここで判別したいラベルを入力
num_classes = len(classes)
image_size = 128


#datesetのディレクトリ
#さっき作ったmike,siroフォルダーの直前までのパスを記入
datadir='./'

#画像の読み込み
X = []
Y = []


for index, classlabel in enumerate(classes):
    photos_dir = datadir+ classlabel
    files = glob.glob(photos_dir + "/*.jpg")
    for i, file in enumerate(files):
        
        image = Image.open(file)
        image = image.convert("RGB")
        image = image.resize((image_size, image_size))
        #image.save("./test/{}{}.jpg".format(classlabel,i))
        data = np.asarray(image)
        
        for angle in range(-20, 20, 5):##5
            # 回転
            img_r = image.rotate(angle)
            data = np.asarray(img_r)
            X.append(data)
            Y.append(index)

            # 反転
            img_trans = image.transpose(Image.FLIP_LEFT_RIGHT)
            data = np.asarray(img_trans)
            X.append(data)
            Y.append(index)



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

#２割テストデータへ
(X_train, X_test, y_train, y_test) = train_test_split(X, Y,test_size=0.2)

#正規化
X_train = X_train.astype("float") / 255
X_test = X_test.astype("float") / 255

#教師データの型を変換
y_train = np_utils.to_categorical(y_train,num_classes)
y_test = np_utils.to_categorical(y_test, num_classes)

#X_train, X_test, y_train, y_test = model_selection.train_test_split(X, Y)
xy = (X_train, X_test, y_train, y_test)
np.save("./dataset.npy", xy)


```

実行が終わると同じディレクトリにdataset.npyというファイルが保存されているはずです。
これが今回用いるデータセットになります。

#その2：学習

先ほど作ったデータセットを読み込み、いよいよ学習を行います。
以下のコードは最初にあげた画像のCNN.pyに対応しています。

```python:CNN.py

import keras
from keras.utils import np_utils
from keras.models import Sequential
from keras.layers.convolutional import Conv2D, MaxPooling2D
from keras.layers.core import Dense, Dropout, Activation, Flatten
from keras.layers import BatchNormalization
from keras.optimizers import SGD
import numpy as np
from sklearn.model_selection import train_test_split
from PIL import Image
import glob

from PIL import Image
import matplotlib.pyplot as plt

import os
from keras.callbacks import TensorBoard,ModelCheckpoint



#ハイパーパラメーター
hp1 = {}
hp1['class_num'] = 2 # クラス数（今回はmike,siroの2クラスなため２）
hp1['batch_size'] = 64 # バッチサイズ #####32
hp1['epoch'] = 20 #エポック数



#データセットのロード
##前章で作ったデータセットをここで読み込む
X_train, X_test, y_train, y_test = np.load("./dataset.npy", allow_pickle=True)


#入力サイズ
input_shape=X_train.shape[1:]

# CNNを構築
def CNN(input_shape):
        model = Sequential()
 
        model.add(Conv2D(32, (3, 3), padding='same',input_shape=input_shape))
        model.add(Activation('relu'))
        model.add(Conv2D(32, (3, 3)))
        model.add(BatchNormalization())
        model.add(Activation('relu'))
        model.add(MaxPooling2D(pool_size=(2, 2)))
        model.add(Dropout(0.25))

        model.add(Conv2D(64, (3, 3), padding='same'))
        model.add(Activation('relu'))
        model.add(Conv2D(64, (3, 3)))
        model.add(BatchNormalization())
        model.add(Activation('relu'))
        model.add(MaxPooling2D(pool_size=(2, 2)))
        model.add(Dropout(0.25))

        model.add(Conv2D(128, (3, 3), padding='same'))
        model.add(Activation('relu'))
        model.add(Conv2D(128, (3, 3)))
        model.add(BatchNormalization())
        model.add(Activation('relu'))

        model.add(MaxPooling2D(pool_size=(2, 2)))
        model.add(Dropout(0.25))


        model.add(Flatten())
        model.add(Dense(512))
        model.add(BatchNormalization())
        model.add(Activation('relu'))
        model.add(Dropout(0.5))
        model.add(Dense(hp1['class_num']))
        model.add(Activation('softmax'))
    
        return model
 

#モデルを選択
model=CNN(input_shape)



#コンパイル

model.compile(loss='categorical_crossentropy',optimizer='SGD',metrics=['accuracy'])


#データの記録
log_dir = os.path.join(os.path.dirname(__file__), "logdir")
model_file_name="model_file.hdf5"

#訓練
history = model.fit(
        X_train, y_train,
         epochs=hp1['epoch'],
         validation_split = 0.2,
         callbacks=[
                TensorBoard(log_dir=log_dir),
                ModelCheckpoint(os.path.join(log_dir,model_file_name),save_best_only=True)
                ]
        )

#評価 & 評価結果出力


loss,accuracy = model.evaluate(X_test, y_test, batch_size=hp1['batch_size'])
```
ここでは学習を行うため、多少時間はかかります。
あとめちゃくちゃ警告でると思いますが気にしないでください、、
実行が終わるとlogdirというフォルダーができており、その中に**model_file.hdf5**というファイルがあるはずです。
これが一番重要で、学習済みモデルとなります。

#その3：認識結果を予測して遊ぶ

いよいよ学習済みモデルで遊びます。
いまフォルダーはこんな感じになっていると思います。

![スクリーンショット (96).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556547/38d269ff-309d-3346-c761-fbbaf512b9ba.png)
このtest_imgの中に予測したい画像を入れておいてください、

先ほど出力した**model_file.hdf5**を読み込み予測を行います。
以下のコードはpredict.pyになります。

```python:predict.py
from pathlib import Path
import numpy as np
from PIL import Image
from keras.models import load_model
import glob

#####  mike=0,siro=1  ######


###学習済みモデルの読み込み
model_path = "./logdir/model_file.hdf5"

##予測したいデータの場所
images_folder = "./test_img"

classes = ["mike","siro"]

# load model
model = load_model(model_path)

#image_size=100
image_size=128
X = []


dir = images_folder
#パスの確認
#print(dir)

files = glob.glob(dir + "/*.jpg")
for i, file in enumerate(files):
    image = Image.open(file)
    image = image.convert("RGB")
    image = image.resize((image_size, image_size))
    data = np.asarray(image)
    X.append(data)
    
 
X = np.array(X)


#正規化(0-1)
X = X.astype('float32')
X = X / 255.0

#print(len(files))

##softmax
for w in range(len(files)):

    result = model.predict([X])[w]
    predicted = result.argmax()
    percentage = int(result[predicted] * 100)
    print(files[w].split('\\')[-1])
    print("{0}({1} %)".format(classes[predicted],percentage))

```

今回はこんな画像を入力してみました。(かわいい)
![c5d7411c6bd88106ebab0730b8af4f907_4620693218538323992_200325_0003.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556547/9f69565a-42b6-359a-5add-f0a048d0ef6a.jpeg)

predict.pyの実行結果はこんな感じになると思います。
![スクリーンショット (98).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556547/ae87a76c-afb1-00a4-c251-63ccb3e86bf8.png)

無事に白猫だと予測できていますね！！

#まとめ
いかがだったでしょうか？？
無事に遊べたなら幸いです。
ここからの発展で、仮に画像認識アプリを作りたい場合は学習済みモデルであるmodel_file.hdf5とpredict.pyさえサーバーにあれば予測を返すアプリケーションができちゃいます。

参考までに、過去にハッカソンで自分とほかのメンバーで作り、技育展で発表を行った画像認識webアプリは[こちら](https://github.com/Ryunosuke-Ikeda/Pmake-up)


#参考文献
- ゼロから作るDeepLearning
- 直感DeepLearning