Tensorflowで犬と猫の画像判定をする

**目次**
<ol>
    <li>はじめに</li>
    <li>本記事の概要</li>
    <li>作成したプログラム
        <ol>
        <li>Googleドライブから画像を読み込めるようにする</li>
        <li>モジュールのインポート</li>
        <li>ハイパーパラメータの設定</li>
        <li>乱数を固定する</li>
        <li>画像を読み込む</li>
        <li>画像を加工する</li>
        <li>正解ラベルを付与する</li>
        <li>データを結合しランダムに並び替える</li>
        <li>データをTensorflowの学習に適した形に変更する</li>
        <li>モデルの学習をし、エポックごとの予測精度を取得します</li>
        <li>予測精度をグラフで可視化する</li>
        </ol>
    </li>
    <li>おわりに</li>
    <li>参考にしたサイト</li>
</ol>

****

**１，はじめに**
※このブログはAidemy Premiumのカリキュラムの一環で、受講修了条件を満たすために公開しています。
Aidemyの「データ分析講座」で学習した成果を、本ブログに投稿させていただきます。

****

**２，本記事の概要**

本記事では、データセットに kaggle の「Cat and Dog」、モデルとして Tensorflowを使用して、犬と猫の画像判定をするモデルを作成します。

使用するデータセット「Cat and Dog」には、犬と猫の画像が大量に格納されていますが、今回は訓練データから1000枚ずつ、テストデータから100枚ずつの計2200枚をランダムに選択して使用します。

また、使用する画像は 100x100 の大きさに統一し、グレースケールを施します。

Tensorflowのバージョンは「2.15.0」を使用しています。
※以下を実行して確認できます。
```python
import tensorflow as tf
print(tf.__version__)
```

**３，作成したプログラム**
*３－１，Googleドライブから画像を読み込めるようにする*
まずはGoogleドライブから画像を読み込めるようにします。
以下のURLから、Cat and Dog のテストデータと訓練データをダウンロードします。
https://www.kaggle.com/datasets/tongpython/cat-and-dog

ダウンロードしたzipファイルを展開します。
私の場合は、マイドライブに「/Aidemy/dataset/」フォルダを作成し、その下に展開しました。

以上が終わればコーディングに移ります。

以下を実行して、Googleドライブのデータにアクセス出来るようにします。
```python
from google.colab import drive
drive.mount('/content/drive')
```

*３－２，モジュールのインポート*
必要なモジュールをインポートします。
```python
# ファイルを扱うためのモジュール
import os

# 画像を加工するためのモジュール
import cv2

# 配列を扱うためのモジュール
import numpy as np

# グラフや画像を描画するためのモジュール
import matplotlib.pyplot as plt

# 配列をランダムに並び替えるためのモジュール
import random

# Tensorflowのモデル
import tensorflow as tf

# ラベルをOne-hot表現にするための関数
from tensorflow.keras.utils import to_categorical

# Tensorflowにレイヤーを追加するためのモジュール
from tensorflow.keras.layers import Dense, Activation, Dropout
```

*３－３，ハイパーパラメータの設定*
以降で使用するパラメータを設定します。

```python
# 乱数のシード値
random_seed = 0

# 訓練データの数（犬猫それぞれ1000枚）
train_dataset_num = 1000

# テストデータの数（犬猫それぞれ100枚）
test_dataset_num = 100

# 画像データのサイズ（画像データの縦幅・横幅は同じにします）
img_size = 100

# Tensorflow の1エポックで使用する画像データの数
batch_size = 64

# Tensorflow の学習回数
epochs = 5
```

*３－４，乱数を固定する*
再現性を担保するために乱数を固定します。

```python
# Python固有の乱数シードの固定
random.seed(random_seed)

# Numpyの乱数シードの固定
np.random.seed(random_seed)

# Tensorflowの乱数シードの固定
tf.random.set_seed(random_seed)
```
*３－５，画像を読み込む*
Gドライブにダウンロードしたデータを読み込みます。
また、読み込んだデータから使用するデータをランダムに取得します。

```python
# 画像が格納されているパスを作成します
# 画像の格納場所に合わせて変更してください
train_dog_folder_path = "/content/drive/MyDrive/Aidemy/dataset/training_set/dogs/"
train_cat_folder_path = "/content/drive/MyDrive/Aidemy/dataset/training_set/cats/"
test_dog_folder_path = "/content/drive/MyDrive/Aidemy/dataset/test_set/dogs/"
test_cat_folder_path = "/content/drive/MyDrive/Aidemy/dataset/test_set/cats/"

# folder_path に格納されているファイル名一覧を取得します
train_dog_datalist = os.listdir(train_dog_folder_path)
train_cat_datalist = os.listdir(train_cat_folder_path)
test_dog_datalist = os.listdir(test_dog_folder_path)
test_cat_datalist = os.listdir(test_cat_folder_path)

# ファイル名からランダムにデータを選択します
train_dog_image_filename = random.sample(train_dog_datalist, train_dataset_num)
train_cat_image_filename = random.sample(train_cat_datalist, train_dataset_num)
test_dog_image_filename = random.sample(test_dog_datalist, test_dataset_num)
test_cat_image_filename = random.sample(test_cat_datalist, test_dataset_num)
```

*３－６，画像を加工する*
取得したデータに対して、
・リサイズ
・グレースケール
を施します

※このコードの完了には時間がかかります。

```python
# 加工後のデータを格納するための変数を定義します
train_dog_imgset = []
train_cat_imgset = []
test_dog_imgset = []
test_cat_imgset = []

# データを加工するための関数
def get_proccesing_imgset_from_filename_list(folder_path, filename_list):
  result_array = []
  for filename in filename_list:
    img = cv2.imread(folder_path + filename)
    img = cv2.resize(img, (img_size, img_size))
    img = cv2.cvtColor(src=img, code=cv2.COLOR_BGR2GRAY)
    result_array.append(img)

  return result_array

# 加工したデータを変数に格納します
train_dog_imgset = get_proccesing_imgset_from_filename_list(train_dog_folder_path, train_dog_image_filename)
train_cat_imgset = get_proccesing_imgset_from_filename_list(train_cat_folder_path, train_cat_image_filename)
test_dog_imgset = get_proccesing_imgset_from_filename_list(test_dog_folder_path, test_dog_image_filename)
test_cat_imgset = get_proccesing_imgset_from_filename_list(test_cat_folder_path, test_cat_image_filename)
```

*３－７，正解ラベルを付与する*
今回は犬のラベルを0、猫のラベルを1として付与します。

```python
train_dog_label = [0]*len(train_dog_imgset)
train_cat_label = [1]*len(train_cat_imgset)
test_dog_label = [0]*len(test_dog_imgset)
test_cat_label = [1]*len(test_cat_imgset)
```

*３－８，データを結合しランダムに並び替える*
学習の偏りが生じないようにするため、データをランダムに並び替えます。
また、データとそれに対応するラベルを同様に並び替える必要があります。
そのため、zip関数でデータとラベルを対応付けたのち並び替えを行います。

```python
# データとラベルを結合します
train_imgset = train_dog_imgset + train_cat_imgset
train_label = train_dog_label + train_cat_label
test_imgset = test_dog_imgset + test_cat_imgset
test_label = test_dog_label + test_cat_label

# データとラベルを同様に並び替えるための関数
def get_similarly_shuffled_list(list1, list2):
  temp_list = list(zip(list1, list2))
  random.shuffle(temp_list)
  list1, list2 = zip(*temp_list)
  return list1, list2

# データとラベルを対応付けて並び替えます
train_imgset, train_label = get_similarly_shuffled_list(train_imgset, train_label)
test_imgset, test_label = get_similarly_shuffled_list(test_imgset, test_label)
```
*３－９，データをTensorflowの学習に適した形へ変形する*
現在のままでは、Tensorflowでは学習できないため、データとラベルを学習に適した形へ変形します。

```python
# データとラベルをNumpy配列に変形します
train_imgset = np.array(train_imgset)
train_label = np.array(train_label)
test_imgset = np.array(test_imgset)
test_label = np.array(test_label)

# データの形を変形します (2000,50,50)→(2000,2500)
train_imgset = train_imgset.reshape(train_imgset.shape[0], train_imgset.shape[1] * train_imgset.shape[2])
test_imgset = test_imgset.reshape(test_imgset.shape[0], test_imgset.shape[1] * test_imgset.shape[2])

# ラベルをOne-hot表現に変形する
train_label = to_categorical(train_label)
test_label = to_categorical(test_label)
```
*３－１０，モデルの学習をし、エポックごとの予測精度を取得します*
モデルの設定をした後、学習を行いエポックごとの予測精度を取得します。

```python
# 最初のレイヤーで入力する要素のサイズを取得しておきます
input_dim = train_imgset.shape[1]

# モデルを定義します
model = tf.keras.Sequential()

# ニューラルネットワークを構築します
model.add(Dense(256, input_dim=input_dim))
model.add(Activation("sigmoid"))
model.add(Dropout(rate=0.2))
model.add(Dense(128))
model.add(Activation("sigmoid"))
model.add(Dropout(rate=0.2))
model.add(Dense(2))
model.add(Activation("softmax"))

# モデルを確定します
model.compile(optimizer="sgd", loss="categorical_crossentropy", metrics=['accuracy'])

# モデルの学習を実行し、エポックごとの予測精度を取得する
history = model.fit(train_imgset, train_label, batch_size=batch_size, epochs=epochs, verbose=0, validation_data=(test_imgset, test_label))
```
*３－１１，予測精度をグラフで可視化する*
取得した予測精度をグラフで可視化します。


```python
#　取得した予測精度「acc」と「val_acc」をグラフで可視化します
plt.plot(history.history['accuracy'], label="acc", ls="-", marker="o")
plt.plot(history.history["val_accuracy"], label="val_acc", ls="-", marker="x")
plt.ylabel("accuracy")
plt.xlabel("epoch")
plt.legend(loc="best")
plt.show()
```
グラフで可視化した結果は以下のようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1240468/2f6244a6-a9a8-71ba-4f97-000aaacaaaa6.png)

ハイパーパラメータの設定に問題があったのか、あまり良い精度は得られませんでした

****

**４，おわりに**

今回はTensorflowを用いて犬と猫の画像の予測精度を調べました。
また、ハイパーパラメータを変更して何回か試してみましたが、予測精度はあまり改善されませんでした。

モデルに対して最適なパラメータを探すには、グリッドサーチ・ランダムサーチ・ベイズ最適化などがありますので、そちらを試すのが良いと思われます。

これからもデータ分析やAIについての学習をすすめつつ、より最適なモデルの構築を目指していきたいと思います。

**５，参考にしたサイト**

【Python初心者が犬・猫の画像認識をやってみる】
https://qiita.com/AnnoJou/items/86ba0f91b8c2cc747573

【[TensorFlow 2.x (tf.keras)] 乱数シードを固定して再現性を向上】
https://qiita.com/bee2/items/08eab7a899c9ff56eb35

【[TensorFlow]インストールされているTensorFlowのバージョンを調べる】
https://www.curict.com/item/6c/6c3a76c.html

【【Python】 二つのリストの対応関係を保ったままシャッフルする】
https://ni4muraano.hatenablog.com/entry/2017/01/30/182447