{"cells":[{"cell_type":"markdown","metadata":{},"source":"# CMI V12 Fixed - Correct API Implementation\n\n公式デモに準拠した正しい実装"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"import os\nimport numpy as np\nimport pandas as pd\nimport polars as pl\nimport pickle\nimport warnings\nimport kaggle_evaluation.cmi_inference_server\n\nwarnings.filterwarnings(\"ignore\")\n\n# モデルパス\nV12_MODEL_PATH = \"/kaggle/input/cmi-v12-real-data-models\"\nprint(\"Loading models and metadata...\")"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"import lightgbm as lgb\nimport xgboost as xgb\nfrom catboost import CatBoostClassifier\nimport joblib\n\n# ジェスチャー名定義\nGESTURE_NAMES = [\n    \"Wave hello\",\n    \"Text on phone\",\n    \"Raise hand\",\n    \"Put hand to mouth\",\n    \"Point\",\n    \"Clap\",\n    \"Shake head\",\n    \"Nod head\",\n    \"Check watch or wrist\",\n    \"Cross arms\",\n    \"Adjust glasses\",\n    \"Touch face\",\n    \"Scratch head\",\n    \"Brush hair\",\n    \"Stretch\",\n    \"Yawn\",\n    \"Take a photo\",\n    \"Other gesture\",\n]\n\n# モデルとメタデータのロード\nprint(\"Loading V12 models...\")\nlgb_model = lgb.Booster(model_file=f\"{V12_MODEL_PATH}/lgb_model_v12_final.txt\")\nxgb_model = xgb.Booster()\nxgb_model.load_model(f\"{V12_MODEL_PATH}/xgb_model_v12_final.json\")\ncat_model = CatBoostClassifier()\ncat_model.load_model(f\"{V12_MODEL_PATH}/cat_model_v12_final.cbm\")\n\n# スケーラーと特徴量名のロード\nscaler = joblib.load(f\"{V12_MODEL_PATH}/scaler_v12.pkl\")\nfeature_names = joblib.load(f\"{V12_MODEL_PATH}/feature_names_v12.pkl\")\n\nprint(f\"Models loaded. Features: {len(feature_names)}\")"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"def extract_features_from_sequence(seq_df):\n    \"\"\"1つのシーケンスから特徴量を抽出\"\"\"\n    features = {}\n\n    # Polarsの場合はPandasに変換\n    if isinstance(seq_df, pl.DataFrame):\n        df = seq_df.to_pandas()\n    else:\n        df = seq_df\n\n    # sequence_id列を除外するカラムリスト\n    exclude_cols = [\"timestamp\", \"sequence_id\", \"SEQ_\", \"index\"]\n\n    # 基本統計量\n    for col in df.columns:\n        # 除外カラムをスキップ\n        if any(ex in col for ex in exclude_cols):\n            continue\n\n        # 数値カラムのみ処理\n        if df[col].dtype in [np.float64, np.float32, np.int64, np.int32]:\n            features[f\"{col}_mean\"] = np.mean(df[col])\n            features[f\"{col}_std\"] = np.std(df[col])\n            features[f\"{col}_min\"] = np.min(df[col])\n            features[f\"{col}_max\"] = np.max(df[col])\n            features[f\"{col}_median\"] = np.median(df[col])\n            features[f\"{col}_q25\"] = np.percentile(df[col], 25)\n            features[f\"{col}_q75\"] = np.percentile(df[col], 75)\n            features[f\"{col}_iqr\"] = features[f\"{col}_q75\"] - features[f\"{col}_q25\"]\n            features[f\"{col}_skew\"] = df[col].skew() if len(df[col]) > 2 else 0\n            features[f\"{col}_kurtosis\"] = df[col].kurtosis() if len(df[col]) > 3 else 0\n\n    # 重力除去\n    if \"acc_x\" in df.columns:\n        gravity_x = np.mean(df[\"acc_x\"])\n        gravity_y = np.mean(df[\"acc_y\"])\n        gravity_z = np.mean(df[\"acc_z\"])\n\n        linear_acc_x = df[\"acc_x\"] - gravity_x\n        linear_acc_y = df[\"acc_y\"] - gravity_y\n        linear_acc_z = df[\"acc_z\"] - gravity_z\n\n        features[\"linear_acc_x_mean\"] = np.mean(linear_acc_x)\n        features[\"linear_acc_y_mean\"] = np.mean(linear_acc_y)\n        features[\"linear_acc_z_mean\"] = np.mean(linear_acc_z)\n        features[\"linear_acc_magnitude\"] = np.mean(\n            np.sqrt(linear_acc_x**2 + linear_acc_y**2 + linear_acc_z**2)\n        )\n\n    # オイラー角\n    if all(col in df.columns for col in [\"quat_w\", \"quat_x\", \"quat_y\", \"quat_z\"]):\n        qw = df[\"quat_w\"].values\n        qx = df[\"quat_x\"].values\n        qy = df[\"quat_y\"].values\n        qz = df[\"quat_z\"].values\n\n        roll = np.arctan2(2 * (qw * qx + qy * qz), 1 - 2 * (qx**2 + qy**2))\n        pitch = np.arcsin(np.clip(2 * (qw * qy - qz * qx), -1, 1))\n        yaw = np.arctan2(2 * (qw * qz + qx * qy), 1 - 2 * (qy**2 + qz**2))\n\n        features[\"euler_roll_mean\"] = np.mean(roll)\n        features[\"euler_pitch_mean\"] = np.mean(pitch)\n        features[\"euler_yaw_mean\"] = np.mean(yaw)\n        features[\"euler_roll_std\"] = np.std(roll)\n        features[\"euler_pitch_std\"] = np.std(pitch)\n        features[\"euler_yaw_std\"] = np.std(yaw)\n\n        # 角速度\n        if len(df) > 1 and \"timestamp\" in df.columns:\n            dt = np.diff(df[\"timestamp\"].values) / 1000.0\n            dt[dt == 0] = 1e-6\n            dqx = np.diff(qx) / dt\n            dqy = np.diff(qy) / dt\n            dqz = np.diff(qz) / dt\n            features[\"angular_velocity_mean\"] = np.mean(np.sqrt(dqx**2 + dqy**2 + dqz**2))\n            features[\"angular_velocity_std\"] = np.std(np.sqrt(dqx**2 + dqy**2 + dqz**2))\n        else:\n            features[\"angular_velocity_mean\"] = 0\n            features[\"angular_velocity_std\"] = 0\n\n    # ジャーク\n    if \"acc_x\" in df.columns and len(df) > 1 and \"timestamp\" in df.columns:\n        dt = np.diff(df[\"timestamp\"].values) / 1000.0\n        dt[dt == 0] = 1e-6\n        jerk_x = np.diff(df[\"acc_x\"]) / dt\n        jerk_y = np.diff(df[\"acc_y\"]) / dt\n        jerk_z = np.diff(df[\"acc_z\"]) / dt\n        features[\"jerk_x_mean\"] = np.mean(jerk_x)\n        features[\"jerk_y_mean\"] = np.mean(jerk_y)\n        features[\"jerk_z_mean\"] = np.mean(jerk_z)\n        features[\"jerk_magnitude\"] = np.mean(np.sqrt(jerk_x**2 + jerk_y**2 + jerk_z**2))\n    else:\n        features[\"jerk_x_mean\"] = 0\n        features[\"jerk_y_mean\"] = 0\n        features[\"jerk_z_mean\"] = 0\n        features[\"jerk_magnitude\"] = 0\n\n    # ToF\n    tof_cols = [col for col in df.columns if col.startswith(\"tof_\")]\n    if tof_cols:\n        tof_values = df[tof_cols].values.flatten()\n        features[\"tof_global_mean\"] = np.mean(tof_values)\n        features[\"tof_global_std\"] = np.std(tof_values)\n        features[\"tof_global_min\"] = np.min(tof_values)\n        features[\"tof_global_max\"] = np.max(tof_values)\n\n    # シーケンス長\n    features[\"sequence_length\"] = len(df)\n    if \"timestamp\" in df.columns:\n        features[\"duration_ms\"] = df[\"timestamp\"].max() - df[\"timestamp\"].min()\n    else:\n        features[\"duration_ms\"] = 0\n\n    return features"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"def predict(sequence: pl.DataFrame, demographics: pl.DataFrame) -> str:\n    \"\"\"単一シーケンスの予測（公式API準拠）\"\"\"\n\n    # 特徴量抽出\n    features = extract_features_from_sequence(sequence)\n    X = pd.DataFrame([features])\n\n    # 特徴量を整形\n    X_aligned = X[feature_names]\n    X_scaled = scaler.transform(X_aligned)\n\n    # 予測\n    pred_lgb = lgb_model.predict(X_scaled, num_iteration=lgb_model.best_iteration)[0]\n\n    dmatrix = xgb.DMatrix(X_scaled, feature_names=feature_names)\n    pred_xgb = xgb_model.predict(dmatrix)[0]\n\n    pred_cat = cat_model.predict_proba(X_scaled)[0]\n\n    # アンサンブル\n    ensemble_probs = 0.4 * pred_lgb + 0.3 * pred_xgb + 0.3 * pred_cat\n\n    # 最も確率の高いクラス\n    pred_class = np.argmax(ensemble_probs)\n\n    # ジェスチャー名を返す（文字列）\n    gesture_name = GESTURE_NAMES[pred_class] if pred_class < 18 else \"Wave hello\"\n\n    return gesture_name"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# 推論サーバー設定（公式デモと同じ構造）\ninference_server = kaggle_evaluation.cmi_inference_server.CMIInferenceServer(predict)\n\nif os.getenv(\"KAGGLE_IS_COMPETITION_RERUN\"):\n    print(\"Competition rerun mode - serving predictions...\")\n    inference_server.serve()\nelse:\n    print(\"Test mode - running local gateway...\")\n    # ローカルテスト用\n    inference_server.run_local_gateway(\n        data_paths=(\n            \"/kaggle/input/cmi-detect-behavior-with-sensor-data/test.csv\",\n            \"/kaggle/input/cmi-detect-behavior-with-sensor-data/test_demographics.csv\",\n        )\n    )"}],"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.12"}},"nbformat":4,"nbformat_minor":4}