Kerasを使ってμ's(2年生)の分類(2)

#概要
　スマートフォンアプリ「スクールアイドルフェスティバル(以下「スクフェス」)」のカードから学習データの作成を行いニューラルネットワークでの学習を行う．それに伴い[前回](https://qiita.com/mokki_0413/items/8cb3a3eabfd6e56483ad)は学習データの作成を行った．そして今回はCNNを用いて学習を行う．
#環境
Mac:Mojave10.14.3
Python3.7.2
TensorFlow1.13.0
Keras2.2.4
#とりあえずソース
```python:cnn.py
import numpy as np 
import glob
import time
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
from keras.layers.core import Dense,Dropout,Activation,Flatten
from keras.models import Sequential
from keras.utils import np_utils
from keras.preprocessing.image import array_to_img, img_to_array, load_img
from keras.layers.convolutional import Conv2D, MaxPooling2D
from keras.optimizers import RMSprop
from keras.callbacks import CSVLogger

#開始時間
t1 = time.time()

#ログの保存
csv_logger = CSVLogger("log.csv")

X = []
Y = []

#海未の画像を読み込み
files = glob.glob("Umi'/*.jpg")
for i,file in enumerate(files):
    img = img_to_array(load_img(file,target_size=(32,32)))
    X.append(img)
    Y.append(0)
#穂乃果の画像を読み込み
files = glob.glob("Honoka'/*.jpg")
for i,file in enumerate(files):
    img = img_to_array(load_img(file,target_size=(32,32)))
    X.append(img)
    Y.append(1)
#ことりの画像を読み込み
files = glob.glob("Kotori'/*.jpg")
for i,file in enumerate(files):
    img = img_to_array(load_img(file,target_size=(32,32)))
    X.append(img)
    Y.append(2)

X = np.asarray(X)
Y = np.asarray(Y)

#画素値を0~1の範囲に変更する
X = X.astype('float32')
X = X / 255.0
#one-hot表現に変換
Y = np_utils.to_categorical(Y, 3)
#トレーニングデータとテストデータに分割
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.3, random_state=0)

#モデルの作成
model = Sequential()
model.add(Conv2D(32,kernel_size=3,padding="same",input_shape=(32,32,3),activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Dropout(0.25))
model.add(Conv2D(64,kernel_size=3,padding="same"))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Flatten())
model.add(Dense(512,activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(3,activation='softmax'))

#ニューラルネットワークの形状を出力
model.summary()

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

epochs = 50
batch_size = 32

result = model.fit(X_train, y_train, epochs=epochs, batch_size=batch_size, validation_data=(X_test, y_test), callbacks=[csv_logger])

evalResult = model.evaluate(X_test, y_test)
print("**** RESULT ****")
print("Accuracy = ", evalResult[1])

accRate = evalResult[1]

#学習結果を保存
open('model.json', 'w').write(model.to_json())
model.save_weights('weights.h5')

#学習時間を出力
t2 = time.time()
elapsed_time = t2 - t1
print(elapsed_time)
```

#解説
###学習データの読み込み
```python
files = glob.glob("Kotori'/*.jpg")
for i,file in enumerate(files):
    img = img_to_array(load_img(file,target_size=(32,32)))
    X.append(img)
    Y.append(2)

X = np.asarray(X)
Y = np.asarray(Y)
```
globモジュールを使いファイル一覧を取得して32×32のカラー画像をXにインプットしてYにはクラス情報与えた．
![Figure_1.png](https://qiita-image-store.s3.amazonaws.com/0/323592/f6761b2a-fb2e-cd0e-92da-7e8d689d6341.png)
32×32のサイズにするとこんな感じ

```python
#画素値を0~1の範囲に変更する
X = X.astype('float32')
X = X / 255.0
#one-hot表現に変換
Y = np_utils.to_categorical(Y, 3)
#トレーニングデータとテストデータに分割
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.3, random_state=0)
```
Xの画素値を255で割ることで0~1の範囲に変換している．またYに対してto_categoricalを行うことで先ほどYに与えたクラスベクトルを2値クラスのone-hot表現に変えている．

###学習モデル
```python
model = Sequential()
model.add(Conv2D(32,kernel_size=3,padding="same",input_shape=(32,32,3),activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Dropout(0.25))
model.add(Conv2D(64,kernel_size=3,padding="same"))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Flatten())
model.add(Dense(512,activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(3,activation='softmax'))
```
今回学習には，CNNの一つであるLeNetを用いて行った．そして，出力層の活性化関数をsoftmax関数で3出力にすることで3つの分類を行なっている．

#結果
学習結果はkerasのコールバックのCSVLoggerを使うことでcsv出力を行った．そこから得られた学習曲線と正答率は以下のようになった．
![loss.jpg](https://qiita-image-store.s3.amazonaws.com/0/323592/afdc4ea1-6171-1c08-eef5-a1cb8cf28e8c.jpeg)
学習曲線
![acc.jpg](https://qiita-image-store.s3.amazonaws.com/0/323592/c7000819-5510-be8a-5c67-5766c422a953.jpeg)
正答率

最終的な正答率はトレーニングデータでは約99.9%，テストデータでは約72.2%となった．

#モデルの評価
###モデルの保存
```python
open('model.json', 'w').write(model.to_json())
model.save_weights('weights.h5')
```
今回学習時に学習したモデルを保存しておいた．この学習済みモデルに対して学習に使っていない画像を入力することで分類が行えるか試してみた．

###モデルを読み込んで画像の分類
```python
from keras.models import model_from_json
import numpy as np 
from keras.preprocessing.image import load_img, img_to_array

label=['umi','honoka','kotori']

#モデルの読み込み
model = model_from_json(open('model.json', 'r').read())
model.load_weights('weights.h5')

#画像の読み込み
img = img_to_array(load_img('分類したい画像',target_size=(32,32)))

img = np.asarray(img)
img = img.astype('float32')
img = img/255
img = img[None,...]

pred = model.predict(img,batch_size=1)
score = np.max(pred)
pred_label = label[np.argmax(pred[0])]

print(pred_label)
print(score)
```
###結果
![スクリーンショット 2019-03-15 13.12.04.png](https://qiita-image-store.s3.amazonaws.com/0/323592/89199fbd-0d52-880d-d41c-1848ad39192e.png)
学習に用いていことりと穂乃果の画像の分類を行った．結果としてしっかり分類が行えていた．
###海未ちゃん誕生日おめでとう！
記事投稿日の今日3月15日は筆者推しの園田海未ちゃんの誕生日！ということで誕生日に伴って新たに実装された2枚のカードに対して分類を行ってみた．
![スクリーンショット 2019-03-15 13.16.54.png](https://qiita-image-store.s3.amazonaws.com/0/323592/92abfc05-7d55-2445-9227-367bfc320411.png)
結果は一枚目はしっかり分類できたものの二枚目はことりと分類されてしまった．．．．
(ごめん海未ちゃん．．．)
#まとめ
今回は前回作成した学習データを使いCNNを用いての学習を行いμ'sの二年生3人の分類を行った．
結果はまあいい感じに分類できたんじゃないかなぁと思っています．
間違ってる点などあればご指摘いただけると幸いです．
そして本当に海未ちゃん誕生日おめでとう！

#参考にした本
[直感Deep Learning――Python×Kerasでアイデアを形にするレシピ](https://www.oreilly.co.jp/books/9784873118260/)