AIによる画像認識で犬猫の判別をする

# 概要
犬と猫はどちらが好きですか？犬が好きは人は猫も好きですよね:dog::cat:？
かく言う私も，ワンちゃん猫ちゃんどちらも好きなので，犬も猫も飼っています．![IMG_20190903_200907.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3531284/dec616a0-336f-1e0e-760f-b72013b88406.jpeg)

今回，はじめてAIを使ってアプリケーションを作ることとなり．大好きな犬と猫の判別機を作ってみることにしました．初心者向けの課題であること，これまで学習したことを活かせること，自分の好きな犬猫を題材にできること...これがアプリケーション作成のモチベーションになりました．
この記事では，VGG16を転移学習させたCNNのモデルを作成し犬と猫を見分けることを目標にしています。

# 目次
・準備
・モデル作成・学習・評価
・アプリでの動作確認
・反省点


# 準備
＜必要なもの＞
・google colaboratory：モデルの学習および作成したモデルのファイル出力．
・python 
・flaskなどのモジュール類
・アプリ用クラウドにアップロードする際、必要なものをrequirements.txtにしてアップロード
  以下
flask>=2.0.1
gunicorn>=20.0.4
tensorflow-cpu>=2.12.0
Werkzeug>=2.0.0
pillow>=7.2.0

＜画像の準備＞
Kaggleのデータセットより
犬の画像：100枚
猫の画像：100枚

# モデル作成・学習・評価
今回，VGG16を利用した転移学習によってモデルを作成しました．
以下，コードの解説です．

~~~python
#マウント
from google.colab import drive
drive.mount('/content/drive')

#必要なライブラリのインポート
import os
import cv2
import numpy as np
import matplotlib.pyplot as plt
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.layers import Dense, Dropout, Flatten, Input
from tensorflow.keras.applications.vgg16 import VGG16
from tensorflow.keras.models import Model, Sequential
from tensorflow.keras import optimizers

#モデルの保存
import os
from google.colab import files

#ディレクトリ構造
path_cat = os.listdir('/content/drive/MyDrive/Cat100')
path_dog = os.listdir('/content/drive/MyDrive/Dog100')

img_cat = []
img_dog = []

for i in range(len(path_cat)):
    img = cv2.imread('/content/drive/MyDrive/Cat100/' + path_cat[i])
    b,g,r = cv2.split(img)
    img = cv2.merge([r,g,b])
    img = cv2.resize(img, (50,50))
    img_cat.append(img)

for i in range(len(path_dog)):
    img = cv2.imread('/content/drive/MyDrive/Dog100/' + path_dog[i])
    b,g,r = cv2.split(img)
    img = cv2.merge([r,g,b])
    img = cv2.resize(img, (50,50))
    img_dog.append(img)

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

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)

#モデルに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(2, activation='softmax'))

＃vggと、top_modelを連結
model = Model(vgg16.inputs, 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'])

＃学習
model.fit(X_train, y_train, batch_size=100, epochs=10, validation_data=(X_test, y_test))

＃画像を一枚受け取り、猫か犬かを判定する関数
def catdog(img):
    img = cv2.resize(img, (50, 50))
    pred = np.argmax(model.predict(np.array([img])))
    if pred == 0:
        return 'cat'
    else:
        return 'dog'

＃精度の評価
scores = model.evaluate(X_test, y_test, verbose=1)
print('Test loss:', scores[0])
print('Test accuracy:', scores[1])

＃pred_catdog関数に写真を渡して犬猫を予測
img = cv2.imread('/content/drive/MyDrive/Dog100/' + path_cat[1])
b,g,r = cv2.split(img)
img1 = cv2.merge([r,g,b])
plt.imshow(img1)
plt.show()
result = catdog(img)
print(result)

#resultsディレクトリを作成
result_dir = 'results'
if not os.path.exists(result_dir):
    os.mkdir(result_dir)
# 重みを保存
model.save(os.path.join(result_dir, 'model.h5'))

files.download( '/content/results/model.h5' )
~~~
-----結果------
![スクリーンショット 2023-08-20 18.30.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3531284/d1d378a6-8ac5-04fe-1c0e-72eedfa58d01.png)

# アプリでの動作確認
:ear_of_rice:モデルを組み込んだ実際のアプリ:herb:
URL：https://sugi-app-5wg8.onrender.com

ファイル選択で画像を選んで「画像を判定する」ボタンを押す．

結果が返ってきます．


#　反省点:sweat: 
loss: 2.9046730995178223
accuracy: 0.7250000238418579
2つの分類として，損失，精度ともに良い結果とはいえないため，学習データの数の増加やエポック数の増加を今後考慮する必要があります:writing_hand:

最後までお付き合いいただきありがとうございました:relaxed: