自作モデルで犬猫の分類


# 目次
[1.はじめに](#-はじめに)
[2.本記事の概要](#-本記事の概要)
[3.事前準備](#-事前準備)
[4.作成したプログラム](#-作成したプログラム)
[5.感想](#-感想)

# はじめに
初めましてNirと申します。
私は普段はVB.netでの開発に従事しております。
学びたいプログラミング言語ランキング１位である「Python」を
オンライン講座にて受講しました。
今回はその成果として犬猫の判別機を作成しました。

# 本記事の概要
内容としては講座で学んだ基本的な内容になりますが、アウトプットの練習も兼ねておりますのでご容赦ください。
データセットはオックスフォード大学の公開しているデータセットを使用しております。

# 事前準備
・Datasetフォルダを作成し、その中に分類に使用するクラス名でフォルダを作成します。
　今回は犬と猫の分類のためDOGとCATフォルダを作成しました。

・データセットをダウンロードします。
　The Oxford-IIIT Pet Dataset（https://www.robots.ox.ac.uk/~vgg/data/pets/）

・ダウンロードした画像を犬はDOG、猫はCATフォルダに分けておきます。

・pictureフォルダを作成し、この中に結果確認用の画像を入れておきます。

![aidemy_sub.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3193507/6ffbad9b-fed8-95ad-d056-abc6b050bf38.png)

最終的にDatasetフォルダ内にmodel.hdf5、dog_catr.npyの２つのファイルを作成していきます。


# 作成したプログラム

### 1. データセットの作成

```python  
# 必要なライブラリのインポート
from PIL import Image
import numpy as np
import glob
from keras.utils import np_utils
from sklearn.model_selection import train_test_split

# データセット画像のフォルダ名（クラス名）
classes = ["DOG","CAT"]

# データセットのディレクトリ
datadir='C:/作業用Python/Dataset/'

# リサイズ設定
image_size = 128
num_testdata = 25

num_classes = len(classes)

#画像の読み込み
X = []
Y = []

for index, classlabel in enumerate(classes):
    # 写真のディレクトリ
    photos_dir = datadir + classlabel

    # 画像データを取得
    files = glob.glob(photos_dir + "/*.jpg")

    # 写真を順番に取得 学習データ傘増し  -20度から20度まで4度刻みで回転したデータを追加
    for i, file in enumerate(files):

        # 読込
        image = Image.open(file)
        # 画像をRGBの3色に変換
        image = image.convert("RGB")       
        # 画像のサイズを揃える
        image = image.resize((image_size, image_size))
        # 画像を数字の配列変換
        data = np.asarray(image)

        # 画像が少ない場合は水増し
        if i < num_testdata:
            X.append(data)
            Y.append(index)
        else:
            for angle in range(-20, 20, 5):           
           
                # 回転
                img_r = image.rotate(angle)
                data = np.asarray(img_r)
                X.append(data)
                Y.append(index)

                # 反転
                img_trans = image.transpose(Image.FLIP_LEFT_RIGHT)
                data = np.asarray(img_trans)
                X.append(data)
                Y.append(index)

X = np.array(X)
Y = np.array(Y)

#全体の20%をテストデータへ
(X_train, X_test, y_train, y_test) = train_test_split(X, Y,test_size=0.2)

#正規化
X_train = X_train.astype("float") / X_train.max()
X_test = X_test.astype("float") /  X_test.max()

#教師データの型を変換
y_train = np_utils.to_categorical(y_train,num_classes)
y_test = np_utils.to_categorical(y_test, num_classes)

xy = (X_train, X_test, y_train, y_test)
np.save(photos_dir + "/dog_cat.npy", xy)   
```
### 2. モデルの作成

```python  
# 必要なライブラリのインポート
from keras.models import Sequential
from keras.layers.convolutional import Conv2D, MaxPooling2D
from keras.layers.core import Dense, Dropout, Activation, Flatten
import numpy as np
import matplotlib.pyplot as plt

#データセットのロード
X_train, X_test, y_train, y_test = np.load("/content/drive/MyDrive/Dataset/dog_cat.npy", allow_pickle=True)

#入力サイズ
input_shape=X_train.shape[1:]

#インスタンス
model = Sequential()

# 1層目 (畳み込み）
model.add(Conv2D(32, (3, 3), padding='same',input_shape=input_shape))
model.add(Activation('relu'))
# 2層目 (畳み込み）       
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
# 3層目 (Max Pooling）   
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Dropout(0.1))   

# 4層目 (畳み込み)
model.add(Conv2D(64,(3,3), padding='same'))
model.add(Activation('relu'))
# 5層目 (畳み込み)
model.add(Conv2D(64,(3,3)))
model.add(Activation('relu'))
# 6層目 (Max Pooling)
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Dropout(0.25))

# データを1次元化
model.add(Flatten())

# 7層目 (全結合層)
model.add(Dense(512))
model.add(Activation('relu'))
model.add(Dropout(0.45))
model.add(Dense(2))
model.add(Activation('softmax'))

model.summary()
#コンパイル
model.compile(loss='categorical_crossentropy',optimizer='SGD',metrics=['accuracy'])

#訓練
history = model.fit(X_train,y_train,
                    batch_size=2, 
                    epochs=5, 
                    verbose=1, 
                    validation_data=(X_test, y_test))

#評価 & 評価結果出力

plt.plot(history.history['accuracy'])
plt.plot(history.history['val_accuracy'])
plt.title('Model accuracy')
plt.ylabel('Accuracy')
plt.xlabel('Epoch')
plt.grid()
plt.legend(['Train', 'Validation'], loc='upper left')
plt.show()

plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('Model loss')
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.grid()
plt.legend(['Train', 'Validation'], loc='upper left')
plt.show()

open('cnn_model.json','w').write(model.to_json())
model.save('model.hdf5')

score = model.evaluate(X_test, y_test, verbose=0)
print('Loss:', score[0], '（損失関数値 - 0に近いほど正解に近い）') 
print('Accuracy:', score[1] * 100, '%', '（精度 - 100% に近いほど正解に近い）') 
```
上記のｺｰﾄﾞを実行すると、学習が始まります。
環境にもよりますが、約10分程度かかります。
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3193507/4055702a-3c14-5471-c925-19461a9520b7.png)

Epochsは学習回数です。回数を重ねるごとにlossが少なくなります。
![1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3193507/2c458c35-748a-962f-4347-2a39b3cfde83.jpeg)
![2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3193507/35c121ec-d6be-5908-a4fa-02c75bf7adb7.jpeg)

回数を重ねるごとに、テストデータに対する予測精度は上がっていきロスも少なくなっています。
検証データに対する精度は微妙です。

### 3. 動作確認
```python  
# 必要なライブラリのインポート
import numpy as np
from PIL import Image
from keras.models import load_model
import glob

#学習済みモデルの読み込み
model_path = "C:/作業用Python/Dataset/model.hdf5"

#予測したいデータの場所
images_folder = "C:/作業用Python/Dataset/Picture"

classes = ["DOG","CAT"]

# load model
model = load_model(model_path)

image_size=128
X = []

files = glob.glob(images_folder + "/*.jpg")
for i, file in enumerate(files):
    image = Image.open(file)
    image = image.convert("RGB")
    image = image.resize((image_size, image_size))
    data = np.asarray(image)
    X.append(data)

X = np.array(X)

#正規化(0-1)
X = X.astype('float32')
X = X / 255.0

for w in range(len(files)):
    result = model.predict([X])[w]
    predicted = result.argmax()
    percentage = int(result[predicted] * 100)
    print(files[w].split('\\')[-1])
    print("{0}({1} %)".format(classes[predicted],percentage))
```
判定する画像はうちの猫です。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3193507/b4a6cb38-9741-b323-8f53-3a967270e3c1.jpeg" width=50%>

![タイトルなし..jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3193507/535ac999-92b0-7ba0-44df-692b1415cd3a.jpeg)
80％の確率で猫らしいです。

# 感想
今までVB.netしか触ってこなかったため、Pythonは出来ることが多くて驚きました。
一番苦労したのが開発環境の構築です。
Anaconda＋VScodeで作成しましたが、ライブラリがインポート出来なかったり、
そもそもインストールでエラーが出ることが多々ありました。
最終的にQiitaの記事を参考に解決しました。

また、今回作成したプログラムについても完全に理解出来てはいないので、
今後も勉強を続けていきます。