Pythonでの画像認識~2人のキャラ分類にトライ~




# <a id="index" href="#index">目次</a>
* [1.はじめに](#section1)
  - 自己紹介
  - 今回のテーマ
* [2.お試し機械学習](#section2)
  - 先日スクレイピングで画像を取得できるようになった
  - きっかけ
  - 機械学習で2分類問題を考えたい
  - コード作成してみる
  - 結果確認
  - 今後の展望

* [3.おわりに](#section3)


## 実行環境
　- Google Coloboratory

## <a id="section1" href="#section1">1.はじめに</a>  

### 自己紹介
こんにちは。sgswといいます。

今大学生で、好きな言語はPythonです。
![Python](https://www.python.org/static/img/python-logo@2x.png)
[公式サイトはこちら(外部サイトに飛びます)](https://www.python.org)

Pythonにおける、実際の活用例をアウトプットしてゆきたいと思います！


###　今回のテーマ

本日は、__Pythonの機械学習ライブラリ、kerasを活用した画像認識について__の記事を投稿しようと思います。

<p class="ec__link-index"><a href="#index">[↑ 目次へ]</a></p>
   

## <a id="section2" href="#section2">2.お試し機械学習</a>

### 先日スクレイピングで画像を取得できるように！

先日、webスクレイピングの本をかじり、ちょっとした規模の画像収集ができるようになりました。

でもこれだけでは微妙です。

[Pythonで機械学習用の画像を取得する(別のQiita記事に飛びます)](https://qiita.com/sgsw/items/26f7c43f0c67ca19cb27)

### きっかけ

集めた画像を活用してまた何か他のことにつなげられないかと思ったところ、本屋で面白そうな本を見つけました。

[PythonとKerasによるディープラーニング (日本語) 単行本（ソフトカバー） – 2018/5/28(外部サイトに飛びます)](https://www.amazon.co.jp/Python%E3%81%A8Keras%E3%81%AB%E3%82%88%E3%82%8B%E3%83%87%E3%82%A3%E3%83%BC%E3%83%97%E3%83%A9%E3%83%BC%E3%83%8B%E3%83%B3%E3%82%B0-Francois-Chollet/dp/4839964262)

![画像イメージ](https://images-na.ssl-images-amazon.com/images/I/51YQWxiLolL._SX388_BO1,204,203,200_.jpg)

ということで、今回はこちらに書かれていた内容を活用して、画像認識問題を考えてみようと思います。



##機械学習で2分類問題を考えたい

上の画像スクレイピングだけでは機械学習をするだけの画像を集めることができません。

かといって、スクレイピングの技術をあげて一度により多くの画像を集めるのは難しそうなので、とりあえず__画像の水増し__をすることにしました。

色々Qiita記事を眺めていると、水増しに関する素晴らしい記事がたくさんあり、そこに書いてあるコードを少し変えるだけで、おかげで1クラスの画像を__2000枚(およそ16倍!)__くらいにまで増やすことができました。


[こちらの方のGitHubのコードをお借りしました。ありがとうございますm(_ _ )m (外部サイトに飛びます)](https://github.com/bohemian916/deeplearning_tool/blob/master/increase_picture.py)

学習素材を得ることができたので、いよいよ機械学習ができそうです！

以下ではGoogle Colaboratoryという無料で使えるGPU実行環境を使っています。

学習素材である大量の画像はあらかじめドライブにアップロードしておいて、オンライン環境で実行する際にアクセスできるようにしておきます。



![output3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1195763/5a934dd1-4bce-96fb-2f13-6d80e5f3456f.png)

このような感じです。

### コード作成してみる

必要なライブラリーはあらかじめインストールしておきます。

```python

import numpy as np
from sklearn.model_selection import train_test_split
from keras import models 
from keras import layers 
import numpy as np 
import matplotlib.pyplot as plt 
import os 
import cv2
import glob
```
とりあえず画像をnumpy配列に収めるための準備をおこなっていきます。

```python

def image_to_tensor(PATH):
    if os.path.exists(PATH) == False:
        print("PATH INVALID")
        return

    img = cv2.imread(PATH)
    img = cv2.resize(img,(100,100))
    img = img/255
    return img


def input_generator(PATH,SIZE = 50):
    """
    PATHフォルダにあるjpg画像を指定枚数 = SIZE集めて(SIZE,100,100,3)の画像データを持つnp.arrayを返します。
    """
    if os.path.exists(PATH) == False:
        print("PATH INVALID")
        return np.array([])
    res = []
    sz = 0
    for img_name in glob.glob(PATH + "/*.jpg"):
        res.append(image_to_tensor(img_name))
        sz += 1
        if sz == SIZE:
            break

    return np.array(res) 
```

これで、PATHを指定したら、そこから決められた枚数の画像を拾ってきて、np.arrayのデータに収めることができます。

あらかじめドライブに保存したファイルにアクセスして、画像を収集します。

今回は、2値分類の対象として特定の2キャラクター(クラス名は__honoka = 0,kotori = 1__とします)を選択し、1クラスのデータ数は__1500__枚としました。


教師データを4:1の割合で分割して、学習データと検証データに分けます。

```python
#テストデータ、と検証データを分離。比率は4:1
X_train, X_test, y_train, y_test = train_test_split(IN,OUT, test_size=0.2, random_state=100)
```

2値問題に使えるモデルを制作します。

(このモデルを作成する際、[購入した本](https://www.amazon.co.jp/Python%E3%81%A8Keras%E3%81%AB%E3%82%88%E3%82%8B%E3%83%87%E3%82%A3%E3%83%BC%E3%83%97%E3%83%A9%E3%83%BC%E3%83%8B%E3%83%B3%E3%82%B0-Francois-Chollet/dp/4839964262)で得た内容/知識を参考にしています。)

```python

#畳み込みニュートラルネットワーク(CNN)をモデルとして適用。過学習防止にドロップアウト関数を
model = models.Sequential()
model.add(layers.Conv2D(32,(3,3),activation ='relu',input_shape = (100,100,3)))
model.add(layers.Dropout(0.5))
model.add(layers.MaxPool2D((2,2)))
model.add(layers.Conv2D(32, (3,3),activation = "relu"))
model.add(layers.Dropout(0.5))
model.add(layers.MaxPool2D((2,2)))
model.add(layers.Flatten())
model.add(layers.Dense(64,activation="relu"))
model.add(layers.Dense(1,activation="sigmoid"))#2値分類問題には最終活性化関数はシグモイド関数が良い


#モデルのコンパイル
model.compile(
    optimizer = "rmsprop",
    loss = "binary_crossentropy",
    metrics = ["accuracy"]
)


##学習フェーズ
#1エポックあたり大体1.5sくらい
log = model.fit(X_train,
                y_train,
                epochs = 20,
                batch_size = 64,
                validation_data = (X_test,y_test)
                )

"""
RESULT:
    Epoch 1/20
    38/38 [==============================] - 2s 37ms/step - loss: 2.2422 - accuracy: 0.5655 - val_loss: 0.6293 - val_accuracy: 0.8167
    Epoch 2/20
    38/38 [==============================] - 1s 28ms/step - loss: 0.5794 - accuracy: 0.7462 - val_loss: 0.5142 - val_accuracy: 0.8867
    Epoch 3/20
    38/38 [==============================] - 1s 28ms/step - loss: 0.4034 - accuracy: 0.8277 - val_loss: 0.3745 - val_accuracy: 0.9467
    Epoch 4/20
    38/38 [==============================] - 1s 28ms/step - loss: 0.2889 - accuracy: 0.9035 - val_loss: 0.2677 - val_accuracy: 0.9700
    Epoch 5/20
    38/38 [==============================] - 1s 28ms/step - loss: 0.1859 - accuracy: 0.9404 - val_loss: 0.1918 - val_accuracy: 0.9917
    Epoch 6/20
    38/38 [==============================] - 1s 28ms/step - loss: 0.1517 - accuracy: 0.9600 - val_loss: 0.2113 - val_accuracy: 0.9217
    Epoch 7/20
    38/38 [==============================] - 1s 28ms/step - loss: 0.1649 - accuracy: 0.9437 - val_loss: 0.1451 - val_accuracy: 0.9950
    Epoch 8/20
    38/38 [==============================] - 1s 28ms/step - loss: 0.0266 - accuracy: 0.9956 - val_loss: 0.2190 - val_accuracy: 0.9883
    Epoch 9/20
    38/38 [==============================] - 1s 28ms/step - loss: 0.0457 - accuracy: 0.9884 - val_loss: 0.0719 - val_accuracy: 0.9967
    Epoch 10/20
    38/38 [==============================] - 1s 28ms/step - loss: 0.0131 - accuracy: 0.9984 - val_loss: 0.0327 - val_accuracy: 1.0000
    Epoch 11/20
    38/38 [==============================] - 1s 28ms/step - loss: 0.1859 - accuracy: 0.9553 - val_loss: 0.0426 - val_accuracy: 1.0000
    Epoch 12/20
    38/38 [==============================] - 1s 28ms/step - loss: 0.0124 - accuracy: 0.9974 - val_loss: 0.0319 - val_accuracy: 0.9983
    Epoch 13/20
    38/38 [==============================] - 1s 28ms/step - loss: 1.2138 - accuracy: 0.9483 - val_loss: 0.0548 - val_accuracy: 0.9983
    Epoch 14/20
    38/38 [==============================] - 1s 28ms/step - loss: 0.0025 - accuracy: 0.9998 - val_loss: 0.0389 - val_accuracy: 0.9983
    Epoch 15/20
    38/38 [==============================] - 1s 29ms/step - loss: 0.0013 - accuracy: 1.0000 - val_loss: 0.0148 - val_accuracy: 0.9983
    Epoch 16/20
    38/38 [==============================] - 1s 28ms/step - loss: 0.0892 - accuracy: 0.9859 - val_loss: 0.0219 - val_accuracy: 0.9983
    Epoch 17/20
    38/38 [==============================] - 1s 28ms/step - loss: 0.0075 - accuracy: 0.9978 - val_loss: 0.0249 - val_accuracy: 0.9983
    Epoch 18/20
    38/38 [==============================] - 1s 28ms/step - loss: 0.3543 - accuracy: 0.9917 - val_loss: 0.0470 - val_accuracy: 0.9950
    Epoch 19/20
    38/38 [==============================] - 1s 28ms/step - loss: 0.0064 - accuracy: 1.0000 - val_loss: 0.0309 - val_accuracy: 0.9967
    Epoch 20/20
    38/38 [==============================] - 1s 28ms/step - loss: 0.0029 - accuracy: 0.9997 - val_loss: 0.0602 - val_accuracy: 0.9933
"""
```

この結果得られた、training_loss,validation_lossをグラフで可視化/確認してみたところ、以下のようになりました。


![output2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1195763/375a0c9a-98cc-65a6-e1a3-e2e60b469a09.png)

大体、validation_lossはtrain_lossに追従できている感じがしますが、何個かtraining_lossが不連続な点がありました。


### 結果確認

最後に、学習モデルを使って別に用意して確認用データ(各クラス80枚の画像で構成)で認識率をみていきたいと思います。


```python

#メインテストデータを収集
honoka_verify_path = "/content/drive/MyDrive/HONOKA_verify"
kotori_verify_path = "/content/drive/MyDrive/KOTORI_verify"
honoka_main_data = input_generator(honoka_verify_path,SIZE = 80)
kotori_main_data = input_generator(kotori_verify_path,SIZE = 80)


##結果分析1.割合評価
honoka_score = np.average(prediction_of_honoka)
print("honoka評価指標(0に近いほど良い)",honoka_score)
kotori_score = np.average(prediction_of_kotori)
print("kotori評価指標(1に近いほど良い)",kotori_score)


"""
RESULT:
    honoka評価指標(0に近いほど良い) 0.26125386
    kotori評価指標(1に近いほど良い) 0.7796327
"""

##結果分析2.正解人数評価
def correct(v,person):
    if person == 0:
      return v < 0.5
    else:
      return v > 0.5

honoka_correct_count = np.sum(correct(prediction_of_honoka,0))
kotori_correct_count = np.sum(correct(prediction_of_kotori,1))
print(f"honoka-judge正解回数(うち80試行回数) > {honoka_correct_count}")
print(f"kotori-judge正解回数(うち80試行回数) > {kotori_correct_count}")


"""
RESULT:
    honoka-judge正解回数(うち80試行回数) > 60
    kotori-judge正解回数(うち80試行回数) > 72
"""
```

###　今後の展望

まだ半分くらいしか本を読めていないので、あらかた読んで理解し終えたらKaggleなどにも参加して実際に活用できるようにしたいところです。













<p class="ec__link-index"><a href="#index">[↑ 目次へ]</a></p>

# <a id="section2" href="#section2">3.おわりに</a>

機械学習は色々試行錯誤をすることが大切で、直感的なイメージが大切になってくるイメージを強く受けました。

今回得られた判断結果はあまり良い数値ではなさそうですので、更なる改善を目指したいですね〜。


<p class="ec__link-index"><a href="#index">[↑ 目次へ]</a></p>