🐡

pythonで2匹の愛猫を判別するAIアプリを作成してみた

2024/08/10に公開

はじめに

私は現在2匹の愛猫達と暮らしています。
今回はその2匹の愛猫達の画像を使用し、画像から、どちらがどの猫であるか識別できるようにしました。

解決したい社会課題

ペットの健康管理は、現代において多くの飼い主が直面する重要な課題です。
複数のペットを飼っている場合、それぞれの健康状態や行動を適切に把握し、管理することが困難になることがあります。
ペットは個体ごとの性格や行動に違いがあります。健康状態の微妙な変化を見逃すことがないように、
複数のペットを簡単に識別し、それぞれの行動や健康状態を正確に記録することで、飼い主がペットの健康をより適切に管理できる様にしたいと思い、
今回は、自身の愛猫達の画像データを使用し、2匹の判別機能を作成しました。

分析するデータ

愛猫達2匹の写真をそれぞれ1000枚ずつ使用しました。

実行環境

パソコン:Windows 10
開発環境:Google Coraboratory
言語:Python
ライブラリ:Keras、Matplotlib

分析の流れ

1.データのラベル付け
2.データの前処理
3.モデルの構築
4.モデルのトレーニング
5.モデルの評価
6.モデルの利用

実行したコード

#1.データのラベル付け
# dataset/
# ├── cat1/
# │   ├── 0001.jpg
# │   ├── 0002.jpg
# │   └── ...
# └── cat2/
#     ├── 0001.jpg
#     ├── 0002.jpg
#     └── ...
# 2.データの前処理

import tensorflow as tf
from tensorflow.keras.preprocessing.image import ImageDataGenerator

# データジェネレータの設定
datagen = ImageDataGenerator(rescale=1.0/255, validation_split=0.2)

train_generator = datagen.flow_from_directory(
    'datasetの場所',
    target_size=(416, 416),
    batch_size=32,
    class_mode='binary',
    subset='training'
)

validation_generator = datagen.flow_from_directory(
    'datasetの場所',
    target_size=(416, 416),
    batch_size=32,
    class_mode='binary',
    subset='validation'
)
# 3. モデルの構築

# シンプルなCNN(Convolutional Neural Network)モデルを構築

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense

model = Sequential([
    Conv2D(32, (3, 3), activation='relu', input_shape=(416, 416, 3)),
    MaxPooling2D((2, 2)),
    Conv2D(64, (3, 3), activation='relu'),
    MaxPooling2D((2, 2)),
    Conv2D(128, (3, 3), activation='relu'),
    MaxPooling2D((2, 2)),
    Flatten(),
    Dense(128, activation='relu'),
    Dense(1, activation='sigmoid')
])

model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
# 4. モデルのトレーニング

history = model.fit(
    train_generator,
    steps_per_epoch=train_generator.samples // train_generator.batch_size,
    validation_data=validation_generator,
    validation_steps=validation_generator.samples // validation_generator.batch_size,
    epochs=10
)


import matplotlib.pyplot as plt

# トレーニングの履歴から損失と精度をプロット
def plot_training_history(history):
    fig, axs = plt.subplots(2, 1, figsize=(10, 10))

    # 損失のプロット
    axs[0].plot(history.history['loss'], label='Training Loss')
    axs[0].plot(history.history['val_loss'], label='Validation Loss')
    axs[0].set_title('Training and Validation Loss')
    axs[0].set_xlabel('Epochs')
    axs[0].set_ylabel('Loss')
    axs[0].legend()

    # 精度のプロット
    axs[1].plot(history.history['accuracy'], label='Training Accuracy')
    axs[1].plot(history.history['val_accuracy'], label='Validation Accuracy')
    axs[1].set_title('Training and Validation Accuracy')
    axs[1].set_xlabel('Epochs')
    axs[1].set_ylabel('Accuracy')
    axs[1].legend()

    plt.show()

# トレーニング履歴をプロット
plot_training_history(history)

# 特定のディレクトリに保存
import os
output_dir = '/保存先'
if not os.path.exists(output_dir):
    os.makedirs(output_dir)
model.save(os.path.join(output_dir, '名前.h5'))

可視化したデータ

Epoch=10 の結果
Epoch 1/10
50/50 [==============================] - 648s 13s/step - loss: 0.4377 - accuracy: 0.8100 - val_loss: 0.3199 - val_accuracy: 0.9115
Epoch 2/10
50/50 [==============================] - 695s 14s/step - loss: 0.2257 - accuracy: 0.9119 - val_loss: 0.2344 - val_accuracy: 0.9167
Epoch 3/10
50/50 [==============================] - 683s 14s/step - loss: 0.1523 - accuracy: 0.9369 - val_loss: 0.1884 - val_accuracy: 0.9375
Epoch 4/10
50/50 [==============================] - 683s 14s/step - loss: 0.1074 - accuracy: 0.9575 - val_loss: 0.3353 - val_accuracy: 0.8568
Epoch 5/10
50/50 [==============================] - 682s 14s/step - loss: 0.0874 - accuracy: 0.9675 - val_loss: 0.3765 - val_accuracy: 0.8672
Epoch 6/10
50/50 [==============================] - 680s 14s/step - loss: 0.0479 - accuracy: 0.9856 - val_loss: 0.3621 - val_accuracy: 0.9141
Epoch 7/10
50/50 [==============================] - 686s 14s/step - loss: 0.0315 - accuracy: 0.9906 - val_loss: 0.3686 - val_accuracy: 0.8932
Epoch 8/10
50/50 [==============================] - 684s 14s/step - loss: 0.0360 - accuracy: 0.9900 - val_loss: 0.7757 - val_accuracy: 0.8333
Epoch 9/10
50/50 [==============================] - 683s 14s/step - loss: 0.0432 - accuracy: 0.9837 - val_loss: 0.6903 - val_accuracy: 0.8125
Epoch 10/10
50/50 [==============================] - 686s 14s/step - loss: 0.0122 - accuracy: 0.9950 - val_loss: 0.7686 - val_accuracy: 0.8880

分析したデータから得られた情報

10回の結果から
Epoch=3 でトレーニング

Epoch 1/3
50/50 [==============================] - 659s 13s/step - loss: 0.8705 - accuracy: 0.6488 - val_loss: 0.3900 - val_accuracy: 0.8203
Epoch 2/3
50/50 [==============================] - 684s 14s/step - loss: 0.2907 - accuracy: 0.8825 - val_loss: 0.2672 - val_accuracy: 0.9062
Epoch 3/3
50/50 [==============================] - 645s 13s/step - loss: 0.2273 - accuracy: 0.9150 - val_loss: 0.2144 - val_accuracy: 0.9427

# 5. モデルの評価

# トレーニング後、モデルの性能を評価。
loss, accuracy = model.evaluate(validation_generator)
print(f'Validation Loss: {loss}')
print(f'Validation Accuracy: {accuracy}')

13/13 [==============================] - 46s 3s/step - loss: 0.2139 - accuracy: 0.9425
Validation Loss: 0.21391597390174866
Validation Accuracy: 0.9424999952316284
# 6. モデルの利用
drive.mount('/content/drive')
# 新しい画像に対して予測を行います。(確認)

from tensorflow.keras.models import load_model

# モデルの読み込み
# 特定のディレクトリから読み込む
loaded_model = load_model('/モデルの場所')

import numpy as np
from tensorflow.keras.preprocessing import image

def predict_image(img_path):
    img = image.load_img(img_path, target_size=(416, 416))
    img_array = image.img_to_array(img)
    img_array = np.expand_dims(img_array, axis=0)
    img_array /= 255.0

    prediction = model.predict(img_array)
    if prediction[0] > 0.5:
       # cat2
        print('This is cat2')
    else:
        # cat1
        print('This is cat1')

predict_image('/写真')

判別アプリコード

import streamlit as st
from tensorflow.keras.models import load_model
from tensorflow.keras.preprocessing.image import img_to_array, load_img
import numpy as np

# モデルのロード
model = load_model('cat_classifier_modell_n_s_e3.h5')

# 画像サイズの定義
IMG_SIZE = (416, 416)

def classify_image(uploaded_file, model):
    # 画像の前処理
    img = load_img(uploaded_file, target_size=IMG_SIZE)
    img = img_to_array(img)
    img = np.expand_dims(img, axis=0)  # バッチ次元の追加
    img /= 255.0  # 正規化
    predictions = model.predict(img)
    return predictions

# Streamlit アプリ
st.title("Cat Classification App")

uploaded_file = st.file_uploader("Choose an image...", type="jpg")

if uploaded_file is not None:
    st.image(uploaded_file, caption='Uploaded Image.', use_column_width=True)
    st.write("")
    st.write("Classifying...")
    
    predictions = classify_image(uploaded_file, model)
    
    # st.write(f"Predictions shape: {predictions.shape}")
    # st.write(f"Predictions content: {predictions}")
    
    # `predictions`の形状が1出力である場合
    if predictions.shape[1] == 1:  
        cat2_prob = predictions[0][0]
        cat1_prob = 1.0 - cat2_prob
        
        st.write(f"Cat 1: {cat1_prob * 100:.2f}%")
        st.write(f"Cat 2: {cat2_prob * 100:.2f}%")
    else:
        st.write("Unexpected prediction shape:", predictions.shape)

アプリ内で画像を判定させた結果(cat1 or cat2)

cat1の写真をアップした結果

cat2の写真をアップした結果

課題

今回は画像を判別する機能を実装しただけなので、
別途作成済みの愛猫達の健康管理アプリ(日付の選択、体重の記録やグラフ、病気の症状、通院記録の有無等を記録できるもの)と組み合わせ、
写真の管理や、日記その他機能等を追加し、より健康管理記録に特化したアプリを作製したいです。

まとめ

手持ちのデータから画像分類モデルを作製し、結果を排出するアプリを作成しました。

Discussion