自分が保有する画像で画像分類Model作成してみた

<h3>きっかけ</h3>
TensorflowのDocumentからMnistを使い画像分類するコードを書いていた。

けれど事前に用意された画像を使って画像分類するのではなく、
自分が持つ画像で画像分類してみたいと思った。

僕はまだ機械学習について理解が浅いので「とりあえずやってみる」で進めました。
ツッコミどころ満載かと思いますが少しづつ理解を深めていこうと思います。

<h3>事前準備 / ゴール</h3>
今回は猫ちゃんとコップを画像分類するModelを作成しようと思います。

以下のように編集した合計130枚ほどの猫ちゃんとコップの画像を用意。
・猫ちゃんの画像名は'cat'が含まれるものとする
・コップの画像名は'cup'が含まれるものとする
・Grayscale (opencvで加工)
・サイズ : 200 x 200 (opencvで加工)

この画像を用いて、最後猫ちゃんの画像を渡し、
Modelがそれを猫ちゃんと分類してくれるかまで進めます。

<h3>ポイント</h3>

・Modelに画像と注釈（ラベリング）を渡して学習させる
・cupと名づいた画像には「0」をラベリングする
・catと名づいた画像には「1」をラベリングする
・最後猫ちゃんの画像を渡し、推論結果が0か1のどちらになるかを評価する。

以下コード。

```classify_cat_and_cup.py
import sys
import numpy as np
from glob import glob
from PIL import Image
import tensorflow as tf

#猫ちゃんとコップの画像が含まれるpath
def get_dataset_path():
    return '../datasets/'

#Mnistのように、3次元配列で画像をnumpy化する。
#画像Aは(0, 200, 200)　画像Bは(1, 200, 200)　画像Cは(2, 200, 200)..のように最終的に定義していく。
def conv_im_to_numpy(src_im):
    np_im = np.array(Image.open(src_im))
    np_im = np_im[np.newaxis, :, :]
    return np_im

#猫ちゃんとコップの画像が含まれるdatasetを作成する
#猫ちゃんの画像は'cat', コップの画像は'cup'と名づいているものとする。
def mk_train_ds():
    fl = glob(get_dataset_path() + '*')
    #画像分だけのlabelを用意。
    train_labels = np.array(np.arange(len(fl)))
    for f in fl:
        step = fl.index(f)
        if step == 0:
            #train_imagesに画像->numpy化したデータを格納していく。
            train_images = conv_im_to_numpy(f)
            #もし画像名にcupと名づいていたらlabelは0とする
            if 'cup' in f:
                train_labels[step] = 0
            #もし画像名にcatと名づいていたらlabelは1とする
            elif 'cat' in f:
                train_labels[step] = 1
        elif 'cup' in f:
            cup_im = conv_im_to_numpy(f)
            train_images = np.append(train_images, cup_im, axis = 0)
            train_labels[step] = 0
        elif 'cat' in f:
            cat_im = conv_im_to_numpy(f)
            train_images = np.append(train_images, cat_im, axis = 0)
            train_labels[step] = 1

    return train_images, train_labels

def def_model():
    model = tf.keras.Sequential([
        #input_shapeは画像サイズに合わせる。
        tf.keras.layers.Flatten(input_shape = (200, 200)),
        #この辺りはMnistのコピペ。
        tf.keras.layers.Dense(128, activation = 'relu'),
        #出力層は猫ちゃんとコップの2つなので2に指定。
        tf.keras.layers.Dense(2)
    ])
    return model

def compile_model(model):
    #Mnistのコピペ。
    model.compile(optimizer = 'adam',
                    loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits = True),
                    metrics = ['accuracy'])

#学習する
def fit_model(model, train_images, train_labels, epochs):
    model.fit(train_images, train_labels, epochs = epochs, validation_split = 0.2)

#推論する
def probability_model(model, img):
    probability_model = tf.keras.Sequential([model,
                                            tf.keras.layers.Softmax()])
    predictions = probability_model.predict(img)
    predictions_res = np.argmax(predictions[0])
    return predictions_res

def main():
    train_images, train_labels = mk_train_ds()
    model = def_model()
    compile_model(model)
    fit_model(model, train_images, train_labels, 20)

    #渡す画像は猫ちゃん。
    img_for_predictions = sys.argv[1]
    img_for_predictions = conv_im_to_numpy(img_for_predictions)
    predictions_res = probability_model(model, img_for_predictions)

    print('===========================')
    if predictions_res == 0:
        print('This is a CUP.')
    elif predictions_res == 1:
        print('This is a CAT.')
    print('===========================')

if __name__ == '__main__':
    main()

```

<h3>結果</h3>

10分の9の確率で猫ちゃんと推論してくれた。

これからはオープンソースのdatasetを活用したり、
テストデータの活用や、意味を理解しきれていない項目を噛み砕き、消化していく。

参考文献
Basic classification: Classify images of clothing
https://www.tensorflow.org/tutorials/keras/classification