100均のキッチンばかりで異常検知（成功編）

[前回](https://qiita.com/moritalous/items/e51330fafd387a0edffd) は失敗してしまいましたが、試行錯誤の上、上手く識別できるようになりました。
試行錯誤の軌跡を残します。

今回もこれ！
![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f34313537342f37326136333766332d653430372d356363342d643166302d3633393963396131316138632e706e67.png](https://qiita-image-store.s3.amazonaws.com/0/41574/96424764-b3aa-b4b8-ba9f-d38bef28dca0.png)


# 画像の水増し

前回使用した画像は全部で400枚（正常200枚、異常200枚）でした。画像が少なすぎるのかと思い、画像を増やすことにしました。Kerasに便利な機能がありました。

自前のDeep Learning用のデータセットを拡張して水増しする
https://qiita.com/halspring/items/7692504afcba97ece249

画像の前処理 - Keras Documentation
https://keras.io/ja/preprocessing/image/

参考にしたサイトほぼそのままですが、今回のキッチンばかりの場合、画像の角度が重要だと考え、画像の回転はしないようにしました。

| パラメーター | 設定値 | 
|---|---|
|rotation_range|コメントアウト|
|horizontal_flip|False|
|vertical_flip|False|

```python
    # 拡張する際の設定
    generator = ImageDataGenerator(
                    # rotation_range=90, # 90°まで回転
                    width_shift_range=0.1, # 水平方向にランダムでシフト
                    height_shift_range=0.1, # 垂直方向にランダムでシフト
                    channel_shift_range=50.0, # 色調をランダム変更
                    shear_range=0.39, # 斜め方向(pi/8まで)に引っ張る
                    horizontal_flip=False, # 垂直方向にランダムで反転
                    vertical_flip=False # 水平方向にランダムで反転
                    )
```

この方法で画像を10倍の4000枚（正常2000枚、異常2000枚）にすることができました。

![スクリーンショット (298).png](https://qiita-image-store.s3.amazonaws.com/0/41574/096171da-b124-031d-e441-eef411eb6f95.png)


# モデルのパラメーターの変更
Kerasのドキュメントを眺めて、今回の問題は「イヌ」「ネコ」「ゾウ」のように複数に分けるのではなく、「正常値」「異常値」の２つに分類するだけの問題だと気づきました。

Sequentialモデルのガイド - Keras Documentation
https://keras.io/ja/getting-started/sequential-model-guide/

```python
# マルチクラス分類問題の場合
model.compile(optimizer='rmsprop',
              loss='categorical_crossentropy',
              metrics=['accuracy'])
```

```python
# 2値分類問題の場合
model.compile(optimizer='rmsprop',
              loss='binary_crossentropy',
              metrics=['accuracy'])
```

```python
# 前回のコード（参考元のコピペです）
model.compile(loss='categorical_crossentropy',
              optimizer='SGD',
              metrics=['accuracy'])
```

真ん中の「2値分類問題」のコードに変更しました。
optimizerの意味は勉強します。

Optimizer : 深層学習における勾配法について
https://qiita.com/tokkuman/items/1944c00415d129ca0ee9

# 画像のグレースケール化
水増しした画像を見たときに、今回の問題は色情報は関係ないと思いました。
これもKerasで簡単にできました。（すごいね、Keras!!）

```python
img = img_to_array(load_img(picture, target_size=(224,224), grayscale=True))
```

グレースケールにすることで、色情報がRGBの3から1になるので、`input_shape`を追加で指定する必要があります。（指定なしだと3扱いなのかな？）

```python
model = MobileNet(include_top=True, weights=None, classes=2,input_shape=(224, 224, 1))
```

# モデルの変更（今回は不採用）
モデルを変えると結果も変わるのかなと思って試したのですが、Raspberry Pi Zeroで予測させようとすると、メモリ不足で動作させることができませんでした。感覚がわかりませんが、Raspberry Pi Zeroの512MBのメモリで動作するって、すごいことなのでしょうね。

# 予測

| 画像 | 結果 |
|---|---|
|![1.jpg](https://qiita-image-store.s3.amazonaws.com/0/41574/8a60c875-9f1d-3568-aff4-d8dd43f3641b.jpeg) | [[9.9999988e-01 1.2415921e-07]]<br>200g以下！<br><br>**正解！** | 
|![2.jpg](https://qiita-image-store.s3.amazonaws.com/0/41574/d8dde70f-6dac-b2a2-ee7d-b58bd34fc0f4.jpeg) | [[1.0000000e+00 5.4107854e-08]]<br>200g以下！<br><br>**正解！**| 
|![3.jpg](https://qiita-image-store.s3.amazonaws.com/0/41574/2722bc85-b19b-a970-5a00-4b239f2a3ffb.jpeg) | [[8.696176e-06 9.999913e-01]]<br>200g以上<br><br>**正解！**| 
|![4.jpg](https://qiita-image-store.s3.amazonaws.com/0/41574/ec94845f-a3d4-29c9-0dbc-e6275ce8c6d0.jpeg) | [[7.8308704e-10 1.0000000e+00]]<br>200g以上！<br><br>**正解！**| 
|![5.jpg](https://qiita-image-store.s3.amazonaws.com/0/41574/0a00465c-ba71-66c0-d062-174eef6c38c4.jpeg) | [[4.8343203e-08 1.0000000e+00]]<br>200g以上！<br><br>**正解！** | 
|![6.jpg](https://qiita-image-store.s3.amazonaws.com/0/41574/09af8d06-ec51-8356-bed6-5d3e1d98ccba.jpeg) | [[1.0000000e+00 1.5057713e-08]]<br>200g以下！<br><br>**不正解！**<br>（キッチンばかりの裏側）<br>想定外の画像のときってどうすればいいんだろう| 

予測のソース

```python
# coding:utf-8

from time import sleep

from keras.applications import mobilenet

from keras.models import load_model
from keras.preprocessing import image
from keras.preprocessing.image import img_to_array, load_img
import numpy as np
from keras.utils import np_utils

model = load_model('0212_my_model_0216.h5', custom_objects={
                   'relu6': mobilenet.relu6,
                   'DepthwiseConv2D': mobilenet.DepthwiseConv2D})

while True:
    X = []

    picture = '/home/pi/motion/capture/lastsnap.jpg'
    img = img_to_array(load_img(picture, target_size=(224, 224), grayscale=True))
    X.append(img)

    X = np.asarray(X)
    X = X.astype('float32')
    X = X / 255.0

    features = model.predict(X)

    print(features)
    if features[0][0] > features[0][1]:
        print('200g以下！')
    else:
        print('200g以上！')

    sleep(5)

```

# 終わりに
なんとか上手く分類させることができました。
ただ、閾値を200gから300gに変えたくなったらもう一回学習させないといけないのかな？
「マルチクラス分類」にすればいいのかな？

# おまけ
予測に使った画像と予測結果をLINE Notifyで通知したら、なんか、やったった感。

![スクリーンショット (300).png](https://qiita-image-store.s3.amazonaws.com/0/41574/369663d2-dc51-bd8a-9f24-03419818bf77.png)

PythonからLINE NotifyでLINEにメッセージを送る
https://qiita.com/tadaken3/items/0998c18df11d4a1c7427