Open3

野鳥の羽を識別してくれる何かを作りたい

nekopiyonekopiyo

今の自分

  • 機会学習ド初心者
  • Pythonド初心者

やりたいこと

  • 拾ってきた鳥の羽を識別したい
  • 機械学習の世界をちょっと知りたい

作戦

  • Claudeに家庭教師をお願いする

ゴール

  • この前拾ってきたフクロウの羽を識別できること
nekopiyonekopiyo

Claude先生に相談

🎯 基本的な学習ロードマップ

  • Step 1: 機械学習の基礎理解
    • 画像分類の概念 → CNNの仕組み → 転移学習 → 実装
  • Step 2: 実践的な技術スタック
    • フレームワーク: TensorFlow/Keras(初心者向け)
    • ベースモデル: EfficientNet-B0(軽量で高性能)
    • データ: まず5-10種類の野鳥から開始
    • 環境: Google Colabで無料で始められます

所感

とりあえずプロトタイプ作りつつ調べつつ行ってみよー(おー)

nekopiyonekopiyo

ダミーデータを使用した簡単なデモ実装

アプローチ

  1. 初期化
  2. モデル作成
  3. ダミーデータ作成
  4. クイック訓練
  5. 予測テスト
  6. 結果可視化
  7. モデル情報表示

コード

# 野鳥羽識別システム - シンプル動作確認版

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
from tensorflow.keras.applications import EfficientNetB0
from tensorflow.keras import layers, models

print(f"TensorFlow Version: {tf.__version__}")
print(f"GPU Available: {len(tf.config.list_physical_devices('GPU')) > 0}")

class SimpleBirdClassifier:
    def __init__(self, num_classes=5):
        self.num_classes = num_classes
        self.species = ['スズメ', 'カラス', 'ハト', 'シジュウカラ', 'ヒヨドリ']
        self.model = None
        
    def create_model(self):
        """シンプルなEfficientNetベースモデル"""
        print("モデル作成中...")
        
        # EfficientNetB0をベースに使用
        base_model = EfficientNetB0(
            weights='imagenet',
            include_top=False,
            input_shape=(224, 224, 3)
        )
        
        # 転移学習のため、ベースモデルの重みは固定
        base_model.trainable = False
        
        # シンプルな分類ヘッドを追加
        self.model = models.Sequential([
            base_model,
            layers.GlobalAveragePooling2D(),
            layers.Dropout(0.2),
            layers.Dense(128, activation='relu'),
            layers.Dropout(0.1),
            layers.Dense(self.num_classes, activation='softmax')
        ])
        
        # コンパイル(シンプルなメトリクスのみ)
        self.model.compile(
            optimizer='adam',
            loss='categorical_crossentropy',
            metrics=['accuracy']
        )
        
        print(f"✓ モデル作成完了")
        print(f"  パラメータ数: {self.model.count_params():,}")
        print(f"  分類クラス数: {self.num_classes}")
        
        return self.model
    
    def create_dummy_data(self, train_samples=100, val_samples=30):
        """テスト用のダミーデータ作成"""
        print("ダミーデータ作成中...")
        
        # 訓練データ
        X_train = np.random.random((train_samples, 224, 224, 3))
        y_train = np.random.randint(0, self.num_classes, train_samples)
        y_train = tf.keras.utils.to_categorical(y_train, self.num_classes)
        
        # 検証データ
        X_val = np.random.random((val_samples, 224, 224, 3))
        y_val = np.random.randint(0, self.num_classes, val_samples)
        y_val = tf.keras.utils.to_categorical(y_val, self.num_classes)
        
        print(f"✓ ダミーデータ作成完了")
        print(f"  訓練データ: {X_train.shape}")
        print(f"  検証データ: {X_val.shape}")
        
        return (X_train, y_train), (X_val, y_val)
    
    def train_quick_test(self, train_data, val_data, epochs=3):
        """クイックテスト用の軽い訓練"""
        X_train, y_train = train_data
        X_val, y_val = val_data
        
        print(f"クイック訓練開始({epochs}エポック)...")
        
        # シンプルな訓練
        history = self.model.fit(
            X_train, y_train,
            validation_data=(X_val, y_val),
            epochs=epochs,
            batch_size=32,
            verbose=1
        )
        
        print("✓ 訓練完了")
        return history
    
    def test_prediction(self, test_data):
        """予測テスト"""
        X_test, y_test = test_data
        
        print("予測テスト中...")
        
        # 予測実行
        predictions = self.model.predict(X_test[:5], verbose=0)
        
        print("✓ 予測結果(最初の5サンプル):")
        for i, pred in enumerate(predictions):
            predicted_class = np.argmax(pred)
            confidence = np.max(pred)
            actual_class = np.argmax(y_test[i])
            
            print(f"  サンプル{i+1}:")
            print(f"    予測: {self.species[predicted_class]} (信頼度: {confidence:.3f})")
            print(f"    実際: {self.species[actual_class]}")
            print()
    
    def plot_simple_results(self, history):
        """シンプルな結果可視化"""
        plt.figure(figsize=(12, 4))
        
        # 精度
        plt.subplot(1, 2, 1)
        plt.plot(history.history['accuracy'], label='Train')
        plt.plot(history.history['val_accuracy'], label='Validation')
        plt.title('精度の推移')
        plt.xlabel('Epoch')
        plt.ylabel('Accuracy')
        plt.legend()
        plt.grid(True)
        
        # 損失
        plt.subplot(1, 2, 2)
        plt.plot(history.history['loss'], label='Train')
        plt.plot(history.history['val_loss'], label='Validation')
        plt.title('損失の推移')
        plt.xlabel('Epoch')
        plt.ylabel('Loss')
        plt.legend()
        plt.grid(True)
        
        plt.tight_layout()
        plt.show()

def run_complete_demo():
    """完全なデモ実行"""
    print("=" * 50)
    print("🐦 野鳥羽識別システム - デモ実行")
    print("=" * 50)
    
    # 1. 分類器初期化
    classifier = SimpleBirdClassifier(num_classes=5)
    
    # 2. モデル作成
    model = classifier.create_model()
    
    # 3. ダミーデータ作成
    train_data, val_data = classifier.create_dummy_data()
    
    # 4. クイック訓練
    history = classifier.train_quick_test(train_data, val_data, epochs=3)
    
    # 5. 予測テスト
    classifier.test_prediction(val_data)
    
    # 6. 結果可視化
    classifier.plot_simple_results(history)
    
    # 7. モデル情報表示
    print("\n" + "=" * 30)
    print("📊 モデル情報")
    print("=" * 30)
    model.summary()
    
    return classifier, history

if __name__ == "__main__":
    # デモ実行
    classifier, history = run_complete_demo()

モデル作成

  • 転移学習を使用
    • 転移学習とは既存の学習済みモデルを利用し、出力層のみカスタマイズする手法
  • 今回はEfficientNetB0というモデルを土台にする
  • 最終層(出力層)だけを野鳥識別用にカスタマイズする

学んだこと

  • Pythonの__init__メソッドはJavaのコンストラクタ的なものらしい
  • メソッドの第一引数に渡されるselfはこのオブジェクト自身のことらしい(this的な?)
  • ダミーデータを作成して使うと実際の羽画像がなくても動かせるのでお試しに良さげ

所感

  • 転移学習とか出力層とかEfficientNetB0とかよく分かっていないので勉強したい
  • モデルのコンパイルもよく分からん
  • エポックもあまりピンときてない
  • 静的型付け言語しか使ったことがないせいかすごくムズムズする
  • Google Colabって諸々楽ちんだけどなんか使いにくさも感じる
  • 知らないこと多すぎるので一つずつ調べていきましょー