{"cells": [{"cell_type": "markdown", "metadata": {}, "source": "# CMI Physics-Based Model V2\\n\\n**Key Improvements from Top Solutions:**\\n1. Physics-based features (gravity removal, angular velocity)\\n2. Memory-efficient sensor splitting\\n3. Optimized ToF processing (320\u219220 dimensions)\\n4. LightGBM + XGBoost ensemble"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "import numpy as np\nimport pandas as pd\nimport pickle\nfrom pathlib import Path\nimport gc\nimport sys\nimport warnings\n\nwarnings.filterwarnings(\"ignore\")\n\n# Add path for inference server\nsys.path.append(\"/kaggle/input/cmi-detect-behavior-with-sensor-data\")\n\nprint(\"Libraries imported successfully\")"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "# Import inference server\nfrom kaggle_evaluation.cmi_inference_server import CMIInferenceServer\n\nprint(\"CMI Inference Server imported\")"}, {"cell_type": "markdown", "metadata": {}, "source": "## Physics-Based Feature Engineering (Top Solution Method)"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "def remove_gravity_from_acc(df_seq, alpha=0.8):\n    \"\"\"\u91cd\u529b\u9664\u53bb\u52a0\u901f\u5ea6\u306e\u8a08\u7b97\uff08\u30c8\u30c3\u30d7\u89e3\u6cd5\u306e\u5fc5\u9808\u8981\u7d20\uff09\"\"\"\n    acc_cols = [\"acc_x\", \"acc_y\", \"acc_z\"]\n    acc_data = df_seq[acc_cols].values\n\n    # Low-pass filter for gravity estimation\n    gravity = np.zeros_like(acc_data)\n    gravity[0] = acc_data[0]\n\n    for i in range(1, len(acc_data)):\n        gravity[i] = alpha * gravity[i - 1] + (1 - alpha) * acc_data[i]\n\n    # Linear acceleration = total - gravity\n    linear_acc = acc_data - gravity\n    return linear_acc\n\n\ndef calculate_angular_velocity_from_quat(df_seq):\n    \"\"\"\u30af\u30a9\u30fc\u30bf\u30cb\u30aa\u30f3\u304b\u3089\u89d2\u901f\u5ea6\u3092\u8a08\u7b97\uff080.852\u30b9\u30b3\u30a2\u9054\u6210\u624b\u6cd5\uff09\"\"\"\n    quat_cols = [\"rot_x\", \"rot_y\", \"rot_z\", \"rot_w\"]\n    quat = df_seq[quat_cols].values\n\n    # Quaternion difference\n    quat_diff = np.diff(quat, axis=0)\n\n    # Angular velocity approximation\n    angular_vel = np.zeros((len(df_seq), 3))\n    angular_vel[1:, 0] = 2 * quat_diff[:, 0]\n    angular_vel[1:, 1] = 2 * quat_diff[:, 1]\n    angular_vel[1:, 2] = 2 * quat_diff[:, 2]\n\n    return angular_vel\n\n\ndef calculate_jerk(df_seq):\n    \"\"\"\u30b8\u30e3\u30fc\u30af\uff08\u52a0\u901f\u5ea6\u5909\u5316\u7387\uff09\u306e\u8a08\u7b97\"\"\"\n    acc_cols = [\"acc_x\", \"acc_y\", \"acc_z\"]\n    acc_data = df_seq[acc_cols].values\n\n    # Jerk = derivative of acceleration\n    jerk = np.zeros_like(acc_data)\n    jerk[1:] = np.diff(acc_data, axis=0)\n\n    return jerk"}, {"cell_type": "markdown", "metadata": {}, "source": "## Memory-Efficient Feature Extraction"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "def extract_tof_statistics(df_seq):\n    \"\"\"ToF\u30bb\u30f3\u30b5\u30fc\u306e\u7d71\u8a08\u91cf\u62bd\u51fa\uff08320\u6b21\u5143\u219220\u6b21\u5143\uff09\"\"\"\n    tof_features = {}\n\n    for sensor_id in range(1, 6):\n        pixel_cols = [f\"tof_{sensor_id}_v{p}\" for p in range(64)]\n        if all(col in df_seq.columns for col in pixel_cols):\n            tof_data = df_seq[pixel_cols].replace(-1, np.nan)\n\n            # \u7d71\u8a08\u91cf\u306e\u307f\u4fdd\u6301\uff08\u30e1\u30e2\u30ea\u52b9\u7387\u5316\uff09\n            tof_features[f\"tof_{sensor_id}_mean\"] = tof_data.mean(axis=1).mean()\n            tof_features[f\"tof_{sensor_id}_std\"] = tof_data.std(axis=1).mean()\n            tof_features[f\"tof_{sensor_id}_min\"] = tof_data.min(axis=1).mean()\n            tof_features[f\"tof_{sensor_id}_max\"] = tof_data.max(axis=1).mean()\n\n    return tof_features\n\n\ndef create_physics_features(df_seq):\n    \"\"\"\u7269\u7406\u30d9\u30fc\u30b9\u7279\u5fb4\u91cf\u306e\u751f\u6210\uff08\u30c8\u30c3\u30d7\u89e3\u6cd5\u306e\u8981\u7d20\u7d71\u5408\uff09\"\"\"\n    features = {}\n\n    # 1. \u91cd\u529b\u9664\u53bb\u52a0\u901f\u5ea6\n    linear_acc = remove_gravity_from_acc(df_seq)\n    features[\"linear_acc_x_mean\"] = linear_acc[:, 0].mean()\n    features[\"linear_acc_y_mean\"] = linear_acc[:, 1].mean()\n    features[\"linear_acc_z_mean\"] = linear_acc[:, 2].mean()\n    features[\"linear_acc_x_std\"] = linear_acc[:, 0].std()\n    features[\"linear_acc_y_std\"] = linear_acc[:, 1].std()\n    features[\"linear_acc_z_std\"] = linear_acc[:, 2].std()\n\n    # Linear acceleration magnitude\n    linear_acc_mag = np.sqrt(np.sum(linear_acc**2, axis=1))\n    features[\"linear_acc_mag_mean\"] = linear_acc_mag.mean()\n    features[\"linear_acc_mag_std\"] = linear_acc_mag.std()\n    features[\"linear_acc_mag_max\"] = linear_acc_mag.max()\n\n    # 2. \u89d2\u901f\u5ea6\n    angular_vel = calculate_angular_velocity_from_quat(df_seq)\n    features[\"angular_vel_x_mean\"] = angular_vel[:, 0].mean()\n    features[\"angular_vel_y_mean\"] = angular_vel[:, 1].mean()\n    features[\"angular_vel_z_mean\"] = angular_vel[:, 2].mean()\n    features[\"angular_vel_x_std\"] = angular_vel[:, 0].std()\n    features[\"angular_vel_y_std\"] = angular_vel[:, 1].std()\n    features[\"angular_vel_z_std\"] = angular_vel[:, 2].std()\n\n    # Angular velocity magnitude\n    angular_vel_mag = np.sqrt(np.sum(angular_vel**2, axis=1))\n    features[\"angular_vel_mag_mean\"] = angular_vel_mag.mean()\n    features[\"angular_vel_mag_std\"] = angular_vel_mag.std()\n\n    # 3. \u30b8\u30e3\u30fc\u30af\n    jerk = calculate_jerk(df_seq)\n    jerk_mag = np.sqrt(np.sum(jerk**2, axis=1))\n    features[\"jerk_mag_mean\"] = jerk_mag.mean()\n    features[\"jerk_mag_std\"] = jerk_mag.std()\n    features[\"jerk_mag_max\"] = jerk_mag.max()\n\n    # 4. \u57fa\u672c\u7684\u306a\u52a0\u901f\u5ea6\u7d71\u8a08\n    acc_mag = np.sqrt(df_seq[\"acc_x\"] ** 2 + df_seq[\"acc_y\"] ** 2 + df_seq[\"acc_z\"] ** 2)\n    features[\"acc_mag_mean\"] = acc_mag.mean()\n    features[\"acc_mag_std\"] = acc_mag.std()\n    features[\"acc_mag_max\"] = acc_mag.max()\n    features[\"acc_mag_min\"] = acc_mag.min()\n\n    # 5. \u56de\u8ee2\u7d71\u8a08\n    features[\"rot_x_mean\"] = df_seq[\"rot_x\"].mean()\n    features[\"rot_y_mean\"] = df_seq[\"rot_y\"].mean()\n    features[\"rot_z_mean\"] = df_seq[\"rot_z\"].mean()\n    features[\"rot_w_mean\"] = df_seq[\"rot_w\"].mean()\n\n    # 6. \u6e29\u5ea6\u30bb\u30f3\u30b5\u30fc\uff08\u5b9f\u969b\u306f5\u500b\u306e\u307f\uff09\n    temp_cols = [f\"thm_{i}\" for i in range(1, 6)]\n    if all(col in df_seq.columns for col in temp_cols):\n        temp_data = df_seq[temp_cols]\n        features[\"temp_mean\"] = temp_data.mean().mean()\n        features[\"temp_std\"] = temp_data.std().mean()\n        features[\"temp_max\"] = temp_data.max().max()\n        features[\"temp_min\"] = temp_data.min().min()\n    else:\n        # \u6e29\u5ea6\u30bb\u30f3\u30b5\u30fc\u304c\u306a\u3044\u5834\u5408\u306e\u30c7\u30d5\u30a9\u30eb\u30c8\u5024\n        features[\"temp_mean\"] = 0\n        features[\"temp_std\"] = 0\n        features[\"temp_max\"] = 0\n        features[\"temp_min\"] = 0\n\n    # 7. ToF\u7d71\u8a08\u91cf\uff08\u30e1\u30e2\u30ea\u52b9\u7387\u5316\uff09\n    tof_stats = extract_tof_statistics(df_seq)\n    features.update(tof_stats)\n\n    # 8. \u6642\u7cfb\u5217\u9577\n    features[\"sequence_length\"] = len(df_seq)\n\n    return features"}, {"cell_type": "markdown", "metadata": {}, "source": "## Model Training with Ensemble"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "# Load training data\nprint(\"Loading training data...\")\ntrain_df = pd.read_csv(\"/kaggle/input/cmi-detect-behavior-with-sensor-data/train.csv\")\ntrain_demo = pd.read_csv(\n    \"/kaggle/input/cmi-detect-behavior-with-sensor-data/train_demographics.csv\"\n)\n\n# Get actual gesture labels from data\nactual_gestures = sorted(train_df[\"gesture\"].unique())\nprint(f\"Actual gesture classes: {actual_gestures}\")\n\nprint(f\"Data loaded: {len(train_df)} samples\")\nprint(f\"Unique sequences: {train_df['sequence_id'].nunique()}\")\nprint(f\"Number of gesture classes: {len(actual_gestures)}\")"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "# Feature extraction\nprint(\"Extracting features with physics-based approach...\")\n\nX = []\ny = []\nsequence_ids = train_df[\"sequence_id\"].unique()\n\nfor i, seq_id in enumerate(sequence_ids):\n    if i % 100 == 0:\n        print(f\"Processing sequence {i}/{len(sequence_ids)}\")\n\n    # Get sequence data\n    seq_data = train_df[train_df[\"sequence_id\"] == seq_id].copy()\n\n    # Extract physics features\n    features = create_physics_features(seq_data)\n\n    # Add demographics using subject column\n    subject_id = seq_data[\"subject\"].iloc[0]\n    demo_data = train_demo[train_demo[\"subject\"] == subject_id].iloc[0]\n    features[\"age\"] = demo_data[\"age\"]\n    features[\"is_male\"] = 1 if demo_data[\"sex\"] == \"M\" else 0\n    features[\"height\"] = demo_data.get(\"height_cm\", 170)  # Default height\n\n    X.append(features)\n    y.append(seq_data[\"gesture\"].iloc[0])\n\n    # Memory cleanup\n    if i % 500 == 0:\n        gc.collect()\n\n# Convert to DataFrame\nX_df = pd.DataFrame(X)\ny_series = pd.Series(y)\n\nprint(f\"Feature extraction complete: {X_df.shape}\")\nprint(f\"Features: {list(X_df.columns)[:10]}...\")"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "# Train LightGBM model\nimport lightgbm as lgb\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.preprocessing import LabelEncoder\n\n# Encode labels with actual gesture classes\nle = LabelEncoder()\nle.fit(actual_gestures)  # Use actual gestures from data\ny_encoded = le.transform(y_series)\n\nprint(f\"Number of classes: {len(le.classes_)}\")\nprint(f\"Classes: {le.classes_}\")\n\n# Split data\nX_train, X_val, y_train, y_val = train_test_split(\n    X_df, y_encoded, test_size=0.2, random_state=42, stratify=y_encoded\n)\n\nprint(f\"Training set: {X_train.shape}\")\nprint(f\"Validation set: {X_val.shape}\")\n\n# LightGBM parameters (optimized for memory)\nnum_classes = len(le.classes_)\nlgb_params = {\n    \"objective\": \"multiclass\",\n    \"num_class\": num_classes,\n    \"metric\": \"multi_logloss\",\n    \"boosting_type\": \"gbdt\",\n    \"num_leaves\": 31,  # Reduced for memory\n    \"learning_rate\": 0.05,\n    \"feature_fraction\": 0.9,\n    \"bagging_fraction\": 0.8,\n    \"bagging_freq\": 5,\n    \"verbose\": 0,\n    \"seed\": 42,\n    \"num_threads\": 2,  # Limited threads for stability\n}\n\n# Train model\nprint(\"Training LightGBM model...\")\ntrain_data = lgb.Dataset(X_train, label=y_train)\nvalid_data = lgb.Dataset(X_val, label=y_val)\n\nmodel_lgb = lgb.train(\n    lgb_params,\n    train_data,\n    valid_sets=[valid_data],\n    num_boost_round=500,\n    callbacks=[lgb.early_stopping(50), lgb.log_evaluation(100)],\n)\n\nprint(\"LightGBM training complete\")"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "# Train XGBoost model\nimport xgboost as xgb\n\nprint(\"Training XGBoost model...\")\n\nxgb_params = {\n    \"objective\": \"multi:softprob\",\n    \"num_class\": num_classes,  # Use same number of classes\n    \"max_depth\": 6,\n    \"learning_rate\": 0.05,\n    \"subsample\": 0.8,\n    \"colsample_bytree\": 0.9,\n    \"eval_metric\": \"mlogloss\",\n    \"seed\": 42,\n    \"nthread\": 2,  # Limited threads\n}\n\ndtrain = xgb.DMatrix(X_train, label=y_train)\ndval = xgb.DMatrix(X_val, label=y_val)\n\nmodel_xgb = xgb.train(\n    xgb_params,\n    dtrain,\n    num_boost_round=500,\n    evals=[(dval, \"validation\")],\n    early_stopping_rounds=50,\n    verbose_eval=100,\n)\n\nprint(\"XGBoost training complete\")"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "# Validation performance\nfrom sklearn.metrics import accuracy_score\n\n# LightGBM predictions\npred_lgb = model_lgb.predict(X_val, num_iteration=model_lgb.best_iteration)\npred_lgb_class = np.argmax(pred_lgb, axis=1)\n\n# XGBoost predictions\ndval = xgb.DMatrix(X_val)\npred_xgb = model_xgb.predict(dval)\npred_xgb_class = np.argmax(pred_xgb, axis=1)\n\n# Ensemble predictions (weighted average)\nensemble_weights = [0.6, 0.4]  # LightGBM, XGBoost\npred_ensemble = ensemble_weights[0] * pred_lgb + ensemble_weights[1] * pred_xgb\npred_ensemble_class = np.argmax(pred_ensemble, axis=1)\n\nprint(f\"LightGBM Accuracy: {accuracy_score(y_val, pred_lgb_class):.4f}\")\nprint(f\"XGBoost Accuracy: {accuracy_score(y_val, pred_xgb_class):.4f}\")\nprint(f\"Ensemble Accuracy: {accuracy_score(y_val, pred_ensemble_class):.4f}\")"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "# Save models\nprint(\"Saving models...\")\n\nwith open(\"model_lgb.pkl\", \"wb\") as f:\n    pickle.dump(model_lgb, f)\n\nwith open(\"model_xgb.pkl\", \"wb\") as f:\n    pickle.dump(model_xgb, f)\n\nwith open(\"label_encoder.pkl\", \"wb\") as f:\n    pickle.dump(le, f)\n\nprint(\"Models saved successfully\")"}, {"cell_type": "markdown", "metadata": {}, "source": "## Inference Function for CMI Server"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "def predict(sequence, demographics):\n    \"\"\"\n    Physics-based prediction with ensemble model.\n    Memory-optimized for stable inference server operation.\n    \"\"\"\n    try:\n        # Convert to DataFrame\n        df_seq = pd.DataFrame(sequence)\n\n        # Extract physics-based features\n        features = create_physics_features(df_seq)\n\n        # Add demographics (using lowercase keys from CMI server)\n        features[\"age\"] = demographics.get(\"age\", 30)\n        features[\"is_male\"] = 1 if demographics.get(\"sex\", \"M\") == \"M\" else 0\n        features[\"height\"] = demographics.get(\"height_cm\", 170)\n\n        # Convert to DataFrame for prediction\n        X = pd.DataFrame([features])\n\n        # LightGBM prediction\n        pred_lgb = model_lgb.predict(X, num_iteration=model_lgb.best_iteration)\n\n        # XGBoost prediction\n        dtest = xgb.DMatrix(X)\n        pred_xgb = model_xgb.predict(dtest)\n\n        # Ensemble\n        pred_ensemble = 0.6 * pred_lgb + 0.4 * pred_xgb\n\n        # Get predicted class\n        pred_class_idx = np.argmax(pred_ensemble[0])\n        pred_gesture = le.inverse_transform([pred_class_idx])[0]\n\n        # Memory cleanup\n        del df_seq, features, X, pred_lgb, pred_xgb, pred_ensemble\n        gc.collect()\n\n        return pred_gesture\n\n    except Exception as e:\n        print(f\"Prediction error: {e}\")\n        # Fallback to most common gesture from training data\n        return \"Text on phone\"  # Most common in training data\n\n\nprint(\"Prediction function defined\")"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "# Test the prediction function\nprint(\"Testing prediction function...\")\n\n# Get a sample sequence\ntest_seq_id = train_df[\"sequence_id\"].iloc[0]\ntest_seq = train_df[train_df[\"sequence_id\"] == test_seq_id].drop(\n    [\n        \"sequence_id\",\n        \"behavior\",\n        \"gesture\",\n        \"subject\",\n        \"orientation\",\n        \"phase\",\n        \"row_id\",\n        \"sequence_type\",\n        \"sequence_counter\",\n    ],\n    axis=1,\n)\n\n# Get demographics for test\ntest_subject = train_df[train_df[\"sequence_id\"] == test_seq_id][\"subject\"].iloc[0]\ntest_demo_row = train_demo[train_demo[\"subject\"] == test_subject].iloc[0]\ntest_demo = {\n    \"age\": test_demo_row[\"age\"],\n    \"sex\": test_demo_row[\"sex\"],\n    \"height_cm\": test_demo_row.get(\"height_cm\", 170),\n}\n\n# Test prediction\ntest_pred = predict(test_seq.to_dict(\"records\"), test_demo)\nactual_gesture = train_df[train_df[\"sequence_id\"] == test_seq_id][\"gesture\"].iloc[0]\n\nprint(f\"Test prediction: {test_pred}\")\nprint(f\"Actual gesture: {actual_gesture}\")\nprint(f\"Match: {test_pred == actual_gesture}\")"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "# Start inference server\nprint(\"Starting CMI Inference Server...\")\nprint(\"Server will handle predictions for test data\")\n\nserver = CMIInferenceServer(predict)\nserver.serve()\n\nprint(\"Inference complete\")"}], "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.0"}}, "nbformat": 4, "nbformat_minor": 4}