画像識別(CNN)アプリを作成してみました

# はじめに
　私は４０代のメカ設計者です。以前から気になっていたAIの知見を深めたいと思い、AI学習スクールで学び、プログラミング未経験から悪戦苦闘しながらAIを活用したWebアプリを開発しました。その成果をまとめておきたいと思います。

　もし、これからプログラミング未経験からAIを学ぼうとされる方は、予めpython（AI作成で使用する言語）に触れておくと、円滑にAIの学習ができると思います。

## 目次

　実行環境
・1. 学習データの準備
・2. 学習データの作成
・3. モデルの作成
・4. 学習
・5. 学習の評価
・6. 結果
・7. 今後の課題

## 実行環境
Google Colab
Flask
## 1. 学習データの準備

　学習データは、kaggleのdatasetからダウンロードしてきて、Googleのマイドライブにデータを保存します。 URL:https://www.kaggle.com/datasets/tongpython/cat-and-dog
## 2. 学習データの作成

　以下のコードを実行することで、Google Colab上にマイドライブのデータを読み込むことができます。
```py
#データセットの読み込み
from google.colab import drive
drive.mount('/content/drive')
```
```py
#必要なライブラリをimport
import os
import cv2
import numpy as np
import matplotlib.pyplot as plt
from keras.utils.np_utils import to_categorical
from keras.layers import Dense, Dropout, Flatten, Input
from keras.applications.vgg16 import VGG16
from keras.models import Model, Sequential
from keras import optimizers
from google.colab import files


#データの作成
path_dog = os.listdir('/content/drive/MyDrive/datasets/dogs')
path_cat = os.listdir('/content/drive/MyDrive/datasets/cats')

img_dog = []
img_cat = []

for i in range(len(path_dog)):
    img = cv2.imread('/content/drive/MyDrive/datasets/dogs/' + path_dog[i])
    img = cv2.resize(img, (50,50))
    img_dog.append(img)

for i in range(len(path_cat)):
    img = cv2.imread('/content/drive/MyDrive/datasets/cats/' + path_cat[i])
    img = cv2.resize(img, (50,50))
    img_cat.append(img)

X = np.array(img_dog + img_cat)
y = np.array([0]*len(img_dog) + [1]*len(img_cat))

rand_index = np.random.permutation(np.arange(len(X)))
X = X[rand_index]
y = y[rand_index]

# データの分割
X_train = X[:int(len(X)*0.8)]
y_train = y[:int(len(y)*0.8)]
X_test = X[int(len(X)*0.8):]
y_test = y[int(len(y)*0.8):]

# 正解ラベルをone-hotへ変形
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)
```
　Google Colab上の犬、猫の画像データをOpenCVというライブラリで読み込み、画像の大きさを調整し、犬、猫の画像リストを作成します。作成したリストにアノテーションしたものを訓練データと検証データに分けた後、正解ラベルをone-hotベクトルに変形します。

## 3. モデルの作成
　今回はvgg16という学習済みモデルを利用して転移学習を行いました。転移学習とは、学習済みモデルの一部を利用して、新たなモデルを作成する手法です。この手法によって短期間で高精度のモデルを作ることができます。
　CNNの前半部分（畳み込み層とプーリング層）にvgg16を利用し、後半の全結合層部の構造を変えたモデルをいくつか作成してみました。

```py
# モデルにvggを使用
input_tensor = Input(shape=(50, 50, 3))
vgg16 = VGG16(include_top=False, weights='imagenet', input_tensor=input_tensor)

# vggのoutputを受け取り、2クラス分類する層を定義
top_model = Sequential()
top_model.add(Flatten(input_shape=vgg16.output_shape[1:]))
top_model.add(Dense(256, activation='relu'))
top_model.add(Dropout(0.5))
top_model.add(Dense(128, activation='relu'))
top_model.add(Dropout(0.5))
top_model.add(Dense(2, activation='softmax'))

# vggと、top_modelを連結
model = Model(inputs=vgg16.input, outputs=top_model(vgg16.output))

# vggの層の重みを固定
for layer in model.layers[:19]:
    layer.trainable = False

# コンパイル
model.compile(loss='categorical_crossentropy',
              optimizer=optimizers.SGD(lr=1e-4, momentum=0.9),
              metrics=['accuracy'])
```
以下の４つのモデルのパターンを試してみました。
【パターン１】
```py
top_model = Sequential()
top_model.add(Flatten(input_shape=vgg16.output_shape[1:]))
top_model.add(Dense(256, activation='relu'))
top_model.add(Dropout(0.5))
top_model.add(Dense(2, activation='softmax'))
```
※以降の【パターン２】～【パターン４】では、以下のコードは共通しているため省略します。
```
top_model = Sequential()
top_model.add(Flatten(input_shape=vgg16.output_shape[1:]))
　　　　　　　　　　　・・・
top_model.add(Dense(2, activation='softmax'))
```
【パターン２】パターン１に２行を追加（最も精度が高かったこの構造を採用）
```py
top_model.add(Dense(256, activation='relu'))
top_model.add(Dropout(0.5))
top_model.add(Dense(128, activation='relu'))
top_model.add(Dropout(0.5))
```
【パターン３】パターン２のドロップアウトの値を０.５から０.２に変更
```py
top_model.add(Dense(256, activation='relu'))
top_model.add(Dropout(0.5))
top_model.add(Dense(128, activation='relu'))
top_model.add(Dropout(0.2))
```
【パターン４】パターン２のドロップアウトをバッチ正規化に置換
```py
top_model.add(Dense(256, activation='relu'))
top_model.add(Dropout(0.5))
top_model.add(Dense(128, activation='relu'))
top_model.add(BatchNormalization())
```

※バッチ正規化を使用する場合は、下記のように「BatchNormalization」も追加してimportしておく必要があります。
```py
from keras.layers import BatchNormalization
```
※バッチ正規化は、活性化関数ReLUなど、出力値の範囲が限定されてない関数の出力に対して使用すると学習の精度が高まると期待されます。
## 4. 学習
　学習のさせ方は下の３パターンを試しました。
　　バッチサイズ：１００　　エポック数：１０　
　　バッチサイズ：　５０　　エポック数：１０
　　バッチサイズ：１００　　エポック数：　５

　バッチサイズは５０よりも１００の方が精度が高く、エポック数は１０でも５でもあまり変わりませんでした。したがって、バッチサイズは１００、エポック数は５にして、精度を高めつつ時間コストの削減を図りました。

```py
model.fit(X_train, y_train, batch_size=100, epochs=5, verbose=1
         , validation_data=(X_test, y_test))
```

## 5.  学習の評価
　最も結果が良かったデータだけを示します。
　　Test loss: 1.222611665725708
　　Test accuracy: 0.7149999737739563



 　訓練と検証で使用したデータ以外の犬、猫データをランダムに取り出したテストの結果は、正解率が６０％（２４/４０）でした。サンプル数が小さいので正解率は下がる可能性もあります。

## 6.  結果

　今回は高い精度の結果を得ることはできませんでした。複数のモデルを試してもあまり精度の向上がみられなかったことから、学習データの改善が必要ではないかと推測しています。訓練データ、検証データで利用した犬、猫の写真データには、犬、猫以外のもの（たとえば、人、ケージ、ペット用のおもちゃ等）が映り込んでいるものもあるため、それらが学習を妨げたと考えています。


## 7.  今後の課題
　使用したデータセットから学習の妨げになると考えられるものが映り込んでいる画像データを取り除き、正規化を行い学習させます。





















　今回のアプリは犬、猫の識別をする、2値分類（2種類の識別）のモデルでした。時間があれば、多値分類（2種類よりも多い種類の識別）やデータセットを使わず（スクレイピングで画像を集めて）、モデルを学習させることにも取り組ます。さらに画像を正確に認識するだけでなく、それを実用的なことに応用できるように挑戦していきます。