銀魂のキャラで画像認識を行う

#はじめに
AIについて何か学びたいの思って、いろいろ探していて何から始めようと迷っていた時、**Aidemy**というプログラミングスクールを見つけた。「自然言語処理」について学んでいたのだが、大学の授業で画像処理をやっていて、それで何かできないかと思い、画像認識プログラムを作った。

画像認識を使い何をしようかと迷っていたらその時読んでいた漫画「銀魂」でなにかできないかと思いメインキャラクターを認識するプログラムを作った。銀魂はギャグ線が高くキャラクターが変装したりすることがおおく、それでも判別できるのかと気になり、それも分類できるのか試してみた。

分類は**「坂田銀時」「志村新八」「神楽」**の3キャラクターにした。画象の収集にはgoogle、bingなどの検索エンジンからひたすら集めた。googlecolabolatoryを使えば集めなくても済んだかもしてないが、集めているのも楽しかったのでよしとした。

それではプログラムを作るためにしたことと、実際のコードを見せる。著作権の問題で画象は見せれないということだけご了承願いたい。銀魂を読んだことがある人なら著者の空知英秋先生なら、許してくれるだろうと思うと思うがが、念のためcodeだけにしておく。

***目次***

1.画像認識を行う上で学ぶ必要があること
2.画像を集めること
3.画像の水増し
4.画像の学習とモデル構築
5.実際に別の画像でテスト


#画像認識を行う上で学ぶ必要があること
画像認識を行う上で学ぶ必要があることは多いので、ここでは簡潔に説明する。今回のプログラムを作るのに必要な知識は**numpy**,**keras**,**matplotlib**がメインで必要になってくる。これらはpythonで機械学習を行う上で必要不可欠な知識である。ほかにも必要なものはあるが、重要なものだけ紹介した。

#画像を集めること
画象を集めることはとても大変だ。いちいち保存してというめんどくさい作業だからだ。初めはflickrのAPIを利用して行うつもりだったが、あまり画像がなく、自分で集める方法に切り替えた。自分で集める方法でも集まったのは
・銀時　約200枚
・神楽　約150枚
・新八　約150枚
これらをそれぞれのファイルにいれて今回はプログラムを実行する。これは機械学習を行う上で非常に少ない数値である。精度の高いモデルを作るには1000枚程度の画像が必要になってくる。今回は銀魂で行いたかったので、このままの数でやっていくことにした。

#画像の水増し
画像の数が単純に少ないのでとりあえず水増しする

```python
#データの水増し
#銀時、新八、神楽のデータを選択するパート
for index, classlabel in enumerate(classes):
    photos_dir = '/Users/shoma.k/Desktop/gintama/' + classlabel
    files = glob.glob(photos_dir + "/*.jpeg")
    
    #画象データを50×50のnumpy形式に変換
    for i, file in enumerate(files):
        if i  >=253: break
        image = Image.open(file)
        image = image.convert("RGB")
        image = image.resize((image_size, image_size))
        data = np.asarray(image)
        
        #50枚をテストデータにする
        if i < num_testdata:
            X_test.append(data)
            y_test.append(index)
            
        else:
            X_train.append(data)
            y_train.append(index)
            
            
            #角度を5度づつ、±30度までずらしてｎ増し（trainデータのみ）
            for angle in range(-30, 30, 5):
                num = 1
                img_r = image.rotate(angle)
                data = np.asarray(img_r)
                
                X_train.append(data)
                y_train.append(index)
                
                #反転
                img_trans = image.transpose(Image.FLIP_LEFT_RIGHT)
                data = np.asarray(img_trans)
                X_train.append(data)
                y_train.append(index)
                
                num +=num
```


#画像の学習とモデル構築
kerasをつかったCNNのモデルを作る

```python
#テストデータと教師データをnumpy形式に変換
X_train = np.array(X_train)
X_test = np.array(X_test)
y_train = np.array(y_train)
y_test = np.array(y_test)

#分割したデータを保存
xy = (X_train, X_test, y_train, y_test)
np.save("./ginnpy.npy", xy)


#データの正規化、カテゴリカル化
X_train = X_train.astype("float")/256
X_test = X_test.astype("float")/256
y_train = np_utils.to_categorical(y_train, num_classes)
y_test = np_utils.to_categorical(y_test, num_classes)


#CNNの実装
model = Sequential()
 
model.add(Conv2D(32, kernel_size=(3, 3),activation='relu',input_shape = X_train.shape[1:]))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D((2, 2)))
model.add(Conv2D(128, (3, 3), activation='relu'))
model.add(MaxPooling2D((2, 2)))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.25))
model.add(Dense(num_classes, activation='softmax'))

#モデルをコンパイル
model.compile(loss=keras.losses.binary_crossentropy,
              optimizer=keras.optimizers.Adadelta(),
              metrics=['accuracy'])


#学習の開始
hist = model.fit(X_train, y_train,
                 batch_size=30,
                 epochs=100,
                 validation_split=0.1,
                 verbose=1)
 
#スコア
scores1 = model.evaluate(X_test, y_test)
print('loss = {:.4} '.format(scores1[0]))
print('accuracy = {:.4%} '.format(scores1[1]))
                 
model.save('./gintama_cnn.h5')
```


画象が少ないためモデルの精度が悪く、ロスも多いが、結果はこうなった。

>・・・
303/303 [==============================] - 11s 36ms/step - loss: 0.3481 - accuracy: 0.7790 - val_loss: 0.7810 - val_accuracy: 0.3119
Epoch 99/100
303/303 [==============================] - 11s 36ms/step - loss: 0.3514 - accuracy: 0.7779 - val_loss: 0.7851 - val_accuracy: 0.3109
Epoch 100/100
303/303 [==============================] - 11s 37ms/step - loss: 0.3547 - accuracy: 0.7768 - val_loss: 0.7690 - val_accuracy: 0.3218
5/5 [==============================] - 0s 8ms/step - loss: 0.4548 - accuracy: 0.7067
loss = 0.4548 
accuracy = 70.6667% 


#実際に別の画像でテスト
実際にモデルの構築ができたのでやってみたかった画像でテストを行なった。

```python
import keras
import sys,os
import numpy as np
from keras.models import load_model
from PIL import Image

imsize = (50, 50)

testpic = '/Users/shoma.k/Desktop/gintoki1.jpeg'
keras_param = '/Users/shoma.k/Desktop/gintama/gintama_cnn.h5'

def load_image(path):
    img = Image.open(path)
    img = img.convert('RGB')
    img = img.resize(imsize)
    img = np.asarray(img)
    img = img / 255.0
    return img
model = load_model(keras_param)
img = load_image(testpic)
prd = model.predict(np.array([img]))
#精度の表示
print(prd)
prelabel = np.argmax(prd, axis = 1)
if prelabel == 0:
    print("gintoki")
elif prelabel == 1:
    print("kagura")
elif porelabel == 2:
    print("shinpati")
```

これでCNNを使ったモデルテストを行えた。銀時の変顔を使ったのだが、実行結果に驚きの数値がでた
>[[0.849497   0.04320009 0.10730284]]
gintoki

もしかしたら似たような画像があったのかもしれないがある程度の判別できるモデルが作れたのではないかと思う。

#最後に
Aidemyを受講していなければ、このようなプログラムを自分で書こうとも思っていなかったし、作れるようになるとも思っていなかった。途中でやる気がなくなり、一度プログラミングから離れた時期があったが、自分で払ったお金のことを考えた時に、このままではいけないと思い、本当の本当にギリギリだが、自分の作品を作りこうしてブログの完成を目指してやれている。モチベーションが続かない時に何をしていたかと言うと、銀魂を読んでいた。結果こうして銀魂に関する画像認識プログラムも作れて、今回は本当に銀魂に救われたと思っている。まだ学生の期間が2年弱残っているので、なにかウェブアプリやスマホアプリをつくろうかなと考えている。まだまだ自分とプログラミングの戦いは長いので頑張っていきたいと思う。