{"cells":[{"cell_type":"markdown","metadata":{},"source":"# V13 TensorFlow Inference - LB 0.80 Two-Branch\n\nTensorFlow Two-Branch推論ノートブック（5モデルアンサンブル）"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"import os\nimport sys\nimport numpy as np\nimport pandas as pd\nimport tensorflow as tf\nfrom sklearn.preprocessing import StandardScaler, LabelEncoder\nfrom tensorflow.keras.utils import to_categorical\nimport joblib\nfrom pathlib import Path\nimport gc\nimport warnings\nwarnings.filterwarnings('ignore')\n\n# TensorFlow設定\ntf.keras.backend.clear_session()\ntf.config.optimizer.set_jit(True)\n\n# GPUメモリ制御\ngpus = tf.config.experimental.list_physical_devices('GPU')\nif gpus:\n    try:\n        for gpu in gpus:\n            tf.config.experimental.set_memory_growth(gpu, True)\n    except RuntimeError as e:\n        print(e)\n\n# Kaggle環境の確認\nIS_KAGGLE = os.path.exists('/kaggle/input')\nprint(f\"Running on Kaggle: {IS_KAGGLE}\")\nprint(f\"TensorFlow version: {tf.__version__}\")\nprint(f\"GPU available: {len(gpus) > 0}\")"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# パスの設定\nif IS_KAGGLE:\n    # Kaggle環境\n    DATA_DIR = Path('/kaggle/input/cmi-detect-behavior-with-sensor-data')\n    MODEL_DIR = Path('/kaggle/input/cmi-bfrb-v13-tf-models')  # 学習済みモデル\n    SOURCE_DIR = Path('/kaggle/input/cmi-bfrb-v13-hybrid-source')\n    \n    # ソースコードの追加\n    sys.path.append(str(SOURCE_DIR))\nelse:\n    # ローカル環境\n    DATA_DIR = Path('../../../data/cmi-detect-behavior-with-sensor-data')\n    MODEL_DIR = Path('../models/tf')\n    sys.path.append('../src')\n\nprint(f\"Data directory: {DATA_DIR}\")\nprint(f\"Model directory: {MODEL_DIR}\")\nprint(f\"Model directory exists: {MODEL_DIR.exists()}\")"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# モジュールのインポート\nfrom feature_engineering_v13 import (\n    engineer_features_v13,\n    aggregate_tof_features,\n    get_feature_columns_v13,\n    add_dummy_thermopile_features,\n    prepare_sequences_v13\n)\nfrom models_v13_tf import build_two_branch_model"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# モデルとオブジェクトの読み込み\ndef load_models_and_metadata(model_dir):\n    \"\"\"保存されたモデルとメタデータの読み込み\"\"\"\n    models = []\n    \n    # メタデータの読み込み\n    metadata = joblib.load(model_dir / 'metadata_tf.pkl')\n    pad_len = metadata['pad_len']\n    feature_cols = metadata['feature_cols']\n    imu_dim = metadata['imu_dim']\n    tof_dim = metadata['tof_dim']\n    n_classes = metadata['n_classes']\n    \n    print(f\"Pad length: {pad_len}\")\n    print(f\"IMU dimensions: {imu_dim}\")\n    print(f\"ToF/Thermal dimensions: {tof_dim}\")\n    print(f\"Number of classes: {n_classes}\")\n    \n    # モデルファイルの検索と読み込み\n    for i in range(10):  # 最大10モデルまで対応\n        model_file = model_dir / f'tf_model_{i}.keras'\n        if model_file.exists():\n            print(f\"Loading model from {model_file}\")\n            model = tf.keras.models.load_model(str(model_file), compile=False)\n            models.append(model)\n        else:\n            break\n    \n    print(f\"Loaded {len(models)} models\")\n    \n    # スケーラーとエンコーダーの読み込み\n    scaler = joblib.load(model_dir / 'scaler_tf.pkl')\n    label_encoder = joblib.load(model_dir / 'label_encoder.pkl')\n    \n    return models, scaler, label_encoder, metadata\n\n# モデルの読み込み\nmodels, scaler, label_encoder, metadata = load_models_and_metadata(MODEL_DIR)\npad_len = metadata['pad_len']\nfeature_cols = metadata['feature_cols']\nimu_dim = metadata['imu_dim']\ntof_dim = metadata['tof_dim']"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# テストデータの読み込み\ntest_df = pd.read_csv(DATA_DIR / 'test.csv')\nsubmission = pd.read_csv(DATA_DIR / 'sample_submission.csv')\n\nprint(f\"Test data shape: {test_df.shape}\")\nprint(f\"Number of test sequences: {test_df['sequence_id'].nunique()}\")"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# 特徴量エンジニアリング\nprint(\"Applying feature engineering...\")\ntest_df = engineer_features_v13(test_df)\ntest_df = aggregate_tof_features(test_df)\ntest_df = add_dummy_thermopile_features(test_df)\n\nprint(f\"Features after engineering: {len(test_df.columns)}\")"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# データの準備\ndef pad_sequences_custom(sequences, pad_len):\n    \"\"\"カスタムパディング関数\"\"\"\n    padded = np.zeros((len(sequences), pad_len, sequences[0].shape[1]))\n    \n    for i, seq in enumerate(sequences):\n        if len(seq) > pad_len:\n            # 長いシーケンスは切り詰め\n            padded[i] = seq[:pad_len]\n        else:\n            # 短いシーケンスは最後の値でパディング\n            padded[i, :len(seq)] = seq\n            if len(seq) > 0:\n                padded[i, len(seq):] = seq[-1]\n    \n    return padded\n\n# シーケンスの準備\nsequences, _, lengths = prepare_sequences_v13(\n    test_df,\n    feature_cols=feature_cols,\n    sequence_id_col='sequence_id',\n    label_col=None  # テストデータなのでラベルなし\n)\n\nprint(f\"Number of sequences: {len(sequences)}\")\nprint(f\"Feature dimensions: {sequences[0].shape[1]}\")"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# データのパディングとスケーリング\nX_padded = pad_sequences_custom(sequences, pad_len)\nprint(f\"Padded shape: {X_padded.shape}\")\n\n# スケーリング\nX_scaled = scaler.transform(X_padded.reshape(-1, X_padded.shape[-1])).reshape(X_padded.shape)\n\n# IMUとToF/Thermalの分離\n# IMU特徴量のインデックス\nimu_feature_names = [\n    'linear_acc_x', 'linear_acc_y', 'linear_acc_z',\n    'rot_x', 'rot_y', 'rot_z', 'rot_w',\n    'acc_mag', 'rot_angle', 'acc_mag_jerk', 'rot_angle_vel',\n    'linear_acc_mag', 'linear_acc_mag_jerk',\n    'angular_vel_x', 'angular_vel_y', 'angular_vel_z',\n    'angular_distance'\n]\n\n# 特徴量インデックスの取得\nimu_indices = [i for i, col in enumerate(feature_cols) if col in imu_feature_names]\ntof_thermal_indices = [i for i, col in enumerate(feature_cols) if col not in imu_feature_names]\n\nX_imu = X_scaled[:, :, imu_indices]\nX_tof_thermal = X_scaled[:, :, tof_thermal_indices]\n\nprint(f\"IMU shape: {X_imu.shape}\")\nprint(f\"ToF/Thermal shape: {X_tof_thermal.shape}\")"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# バッチ予測の設定\nBATCH_SIZE = 128\nn_batches = (len(X_imu) + BATCH_SIZE - 1) // BATCH_SIZE\n\nprint(f\"Predicting with batch size: {BATCH_SIZE}\")\nprint(f\"Number of batches: {n_batches}\")"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# 各モデルで予測\nall_predictions = []\n\nfor i, model in enumerate(models):\n    print(f\"\\nPredicting with model {i+1}/{len(models)}...\")\n    \n    model_predictions = []\n    \n    # バッチごとに予測\n    for batch_idx in range(n_batches):\n        start_idx = batch_idx * BATCH_SIZE\n        end_idx = min((batch_idx + 1) * BATCH_SIZE, len(X_imu))\n        \n        batch_imu = X_imu[start_idx:end_idx]\n        batch_tof = X_tof_thermal[start_idx:end_idx]\n        \n        # 予測\n        pred = model.predict(\n            [batch_imu, batch_tof],\n            batch_size=BATCH_SIZE,\n            verbose=0\n        )\n        model_predictions.append(pred)\n        \n        if (batch_idx + 1) % 10 == 0:\n            print(f\"  Processed {batch_idx + 1}/{n_batches} batches\")\n    \n    # バッチ予測を結合\n    model_predictions = np.vstack(model_predictions)\n    all_predictions.append(model_predictions)\n    \n    # メモリクリーンアップ\n    gc.collect()\n    if gpus:\n        tf.keras.backend.clear_session()\n\nprint(\"\\nAll predictions completed\")"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# アンサンブル予測\nensemble_predictions = np.mean(all_predictions, axis=0)\nprint(f\"Ensemble predictions shape: {ensemble_predictions.shape}\")\n\n# クラス名の取得\nclass_names = label_encoder.classes_\nprint(f\"Classes: {class_names}\")"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# 提出ファイルの作成\nsubmission_df = pd.DataFrame({\n    'sequence_id': test_df['sequence_id'].unique()\n})\n\n# 各クラスの確率を追加\nfor i, class_name in enumerate(class_names):\n    submission_df[class_name] = ensemble_predictions[:, i]\n\n# 確認\nprint(f\"Submission shape: {submission_df.shape}\")\nprint(f\"\\nFirst few rows:\")\nprint(submission_df.head())\n\n# 予測値の合計確認（各行が1.0になるはず）\nrow_sums = submission_df[class_names].sum(axis=1)\nprint(f\"\\nRow sums - min: {row_sums.min():.4f}, max: {row_sums.max():.4f}\")"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# 予測の分布確認\nimport matplotlib.pyplot as plt\n\n# 各クラスの平均予測確率\nmean_probs = submission_df[class_names].mean().sort_values(ascending=False)\nprint(\"\\nMean prediction probabilities:\")\nfor class_name, prob in mean_probs.items():\n    print(f\"{class_name}: {prob:.4f}\")\n\n# プロット\nplt.figure(figsize=(10, 6))\nmean_probs.plot(kind='bar')\nplt.title('Mean Prediction Probabilities by Class')\nplt.xlabel('Gesture')\nplt.ylabel('Mean Probability')\nplt.xticks(rotation=45)\nplt.tight_layout()\nplt.show()"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# 提出ファイルの保存\nsubmission_df.to_csv('submission_v13_tf.csv', index=False)\nprint(\"Submission file saved as 'submission_v13_tf.csv'\")\n\n# メモリクリーンアップ\ndel X_padded, X_scaled, X_imu, X_tof_thermal\ndel all_predictions, ensemble_predictions\ngc.collect()\nif gpus:\n    tf.keras.backend.clear_session()"}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.8.0"}},"nbformat":4,"nbformat_minor":4}