{"cells":[{"cell_type":"markdown","metadata":{},"source":"# CMI-BFRB Competition: Inference v1.1.0 Fixed V2\n\nGroupByを使わない単一シーケンス対応版。\n\n**改善点**:\n- GroupByを使わずに単一シーケンスで特徴量作成\n- シンプルで直接的な実装\n- IMU推論の成功パターンを参考\n\n## 必要なDatasets:\n- Competition Data (自動)\n- `cmi-bfrb-utils-v110` (共通モジュール)\n- `cmi-bfrb-baseline-training-v1-1-0-fixed` (学習ノートブックの出力)"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"import os\nimport sys\nimport numpy as np\nimport pandas as pd\nimport polars as pl\nimport pickle\nfrom pathlib import Path\n\n# パスの設定\nDATA_PATH = '/kaggle/input/cmi-detect-behavior-with-sensor-data'\nMODEL_PATH = '/kaggle/input/cmi-bfrb-baseline-training-v1-1-0-fixed'\n\nprint(f\"Data path: {DATA_PATH}\")\nprint(f\"Model path: {MODEL_PATH}\")"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# モデルの読み込み\nprint(\"Loading trained model...\")\n\nwith open(os.path.join(MODEL_PATH, 'baseline_model_v110.pkl'), 'rb') as f:\n    model = pickle.load(f)\n\nwith open(os.path.join(MODEL_PATH, 'training_results_v110.pkl'), 'rb') as f:\n    results = pickle.load(f)\n\nprint(f\"✓ Model loaded successfully!\")\nprint(f\"  Number of models (folds): {len(model.models)}\")\nprint(f\"  CV Score: {results['mean_cv_score']:.4f}\")"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# ジェスチャー定義\nTARGET_GESTURES = [\n    'Above ear - pull hair',\n    'Cheek - pinch skin',\n    'Eyebrow - pull hair',\n    'Eyelash - pull hair',\n    'Forehead - pull hairline',\n    'Forehead - scratch',\n    'Neck - pinch skin',\n    'Neck - scratch',\n]\n\nNON_TARGET_GESTURES = [\n    'Write name on leg',\n    'Wave hello',\n    'Glasses on/off',\n    'Text on phone',\n    'Write name in air',\n    'Feel around in tray and pull out an object',\n    'Scratch knee/leg skin',\n    'Pull air toward your face',\n    'Drink from bottle/cup',\n    'Pinch knee/leg skin'\n]\n\nALL_GESTURES = TARGET_GESTURES + NON_TARGET_GESTURES"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"def create_features_single_sequence(df: pd.DataFrame) -> pd.DataFrame:\n    \"\"\"\n    単一シーケンスから特徴量を作成（GroupByを使わない）\n    \"\"\"\n    # センサーカラムの定義\n    imu_cols = ['acc_x', 'acc_y', 'acc_z', 'rot_w', 'rot_x', 'rot_y', 'rot_z']\n    thm_cols = [f'thm_{i}' for i in range(1, 6)]\n    \n    # 利用可能なカラムのみ使用\n    available_imu = [col for col in imu_cols if col in df.columns]\n    available_thm = [col for col in thm_cols if col in df.columns]\n    \n    features = {}\n    \n    # シーケンスIDとsubject\n    features['sequence_id'] = df['sequence_id'].iloc[0] if 'sequence_id' in df.columns else 'unknown'\n    features['subject'] = df['subject'].iloc[0] if 'subject' in df.columns else 'unknown'\n    \n    # IMU特徴量（基本統計量）\n    for col in available_imu:\n        features[f'{col}_mean'] = df[col].mean()\n        features[f'{col}_std'] = df[col].std()\n        features[f'{col}_max'] = df[col].max()\n        features[f'{col}_min'] = df[col].min()\n    \n    # サーモパイル特徴量\n    for col in available_thm:\n        features[f'{col}_mean'] = df[col].mean()\n        features[f'{col}_std'] = df[col].std()\n    \n    # ToF特徴量（センサーごとに集約）\n    for i in range(1, 6):\n        tof_sensor_cols = [col for col in df.columns if f'tof_{i}_' in col]\n        if tof_sensor_cols:\n            # 各センサーの平均値\n            tof_mean = df[tof_sensor_cols].mean().mean()\n            features[f'tof_{i}_mean'] = tof_mean\n            # 有効な測定値の割合\n            tof_valid = (df[tof_sensor_cols] != -1).mean().mean()\n            features[f'tof_{i}_valid_ratio'] = tof_valid\n    \n    # シーケンス長\n    features['sequence_length'] = len(df)\n    \n    # DataFrameに変換（1行）\n    return pd.DataFrame([features])"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"def predict(sequence: pl.DataFrame, demographics: pl.DataFrame) -> str:\n    \"\"\"\n    評価API用の予測関数（シンプル版）\n    \"\"\"\n    try:\n        # Polars DataFrameをPandasに変換\n        sequence_df = sequence.to_pandas()\n        demographics_df = demographics.to_pandas() if demographics is not None else None\n        \n        # subjectを追加\n        if demographics_df is not None and 'subject' in demographics_df.columns:\n            sequence_df['subject'] = demographics_df['subject'].iloc[0]\n        else:\n            sequence_df['subject'] = 'test_subject'\n        \n        # 特徴量を作成（単一シーケンス用）\n        features = create_features_single_sequence(sequence_df)\n        \n        # デモグラフィック情報を結合\n        if demographics_df is not None:\n            # 必要な列のみ選択\n            demo_cols = [col for col in demographics_df.columns if col != 'subject']\n            if demo_cols:\n                for col in demo_cols:\n                    features[col] = demographics_df[col].iloc[0]\n        \n        # 欠損値の処理\n        numeric_cols = features.select_dtypes(include=[np.number]).columns\n        features[numeric_cols] = features[numeric_cols].fillna(0)\n        \n        # モデルが期待する特徴量のみを選択\n        if hasattr(model, 'feature_cols'):\n            # 存在する特徴量のみを使用\n            available_features = [col for col in model.feature_cols if col in features.columns]\n            missing_features = [col for col in model.feature_cols if col not in features.columns]\n            \n            # 不足している特徴量を0で埋める\n            for col in missing_features:\n                features[col] = 0\n            \n            features = features[model.feature_cols]\n        \n        # 予測\n        gesture = model.predict_gesture(features)[0]\n        \n        return gesture\n        \n    except Exception as e:\n        print(f\"Error in prediction: {e}\")\n        # フォールバック\n        return \"Text on phone\"\n\nprint(\"✓ Prediction function defined (single sequence version)\")"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# 推論サーバーの設定\nimport kaggle_evaluation.cmi_inference_server\n\ninference_server = kaggle_evaluation.cmi_inference_server.CMIInferenceServer(predict)\n\nif os.getenv('KAGGLE_IS_COMPETITION_RERUN'):\n    print(\"Running in submission mode...\")\n    inference_server.serve()\nelse:\n    print(\"Running in local test mode...\")\n    inference_server.run_local_gateway(\n        data_paths=(\n            os.path.join(DATA_PATH, 'test.csv'),\n            os.path.join(DATA_PATH, 'test_demographics.csv'),\n        )\n    )"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# モデル情報の表示\nprint(\"\\n=== Model Information ===\")\nprint(f\"Model type: {type(model).__name__}\")\nprint(f\"Number of features expected: {len(model.feature_cols) if hasattr(model, 'feature_cols') else 'Unknown'}\")\nprint(f\"\\nNote: This version uses a simplified feature creation process\")\nprint(\"      without GroupBy to ensure compatibility with single sequence inference.\")"}],"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.10.8"}},"nbformat":4,"nbformat_minor":4}