{"cells": [{"cell_type": "markdown", "metadata": {}, "source": "# CMI V11 Final - Synthetic Data Model Submission\n\n\u4e8b\u524d\u5b66\u7fd2\u6e08\u307f\u30e2\u30c7\u30eb\uff08\u5b9f\u30c7\u30fc\u30bf50% + \u5408\u6210\u30c7\u30fc\u30bf50%\uff09\u306b\u3088\u308b\u63a8\u8ad6"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "source": "import numpy as np\nimport pandas as pd\nimport polars as pl\nimport pickle\nimport json\nimport os\nimport warnings\nfrom pathlib import Path\nimport lightgbm as lgb\nimport xgboost as xgb\nfrom catboost import CatBoostClassifier\nfrom sklearn.preprocessing import StandardScaler\nimport kaggle_evaluation\nimport kaggle_evaluation.cmi_inference_server\n\nwarnings.filterwarnings('ignore')\nprint('Libraries loaded successfully')"}, {"cell_type": "markdown", "metadata": {}, "source": "## 1. \u4e8b\u524d\u5b66\u7fd2\u6e08\u307f\u30e2\u30c7\u30eb\u306e\u8aad\u307f\u8fbc\u307f"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "source": "# Load pre-trained models from Dataset\nMODEL_DIR = Path('/kaggle/input/cmi-v11-models')\n\n# Load models\nprint('Loading pre-trained models...')\nlgb_model = lgb.Booster(model_file=str(MODEL_DIR / 'lgb_model_v11.txt'))\nprint('  LightGBM loaded')\n\nxgb_model = xgb.Booster()\nxgb_model.load_model(str(MODEL_DIR / 'xgb_model_v11.json'))\nprint('  XGBoost loaded')\n\ncat_model = CatBoostClassifier()\ncat_model.load_model(str(MODEL_DIR / 'cat_model_v11.cbm'))\nprint('  CatBoost loaded')\n\n# Load scaler and feature names\nwith open(MODEL_DIR / 'scaler_v11.pkl', 'rb') as f:\n    scaler = pickle.load(f)\nprint('  Scaler loaded')\n\nwith open(MODEL_DIR / 'feature_names_v11.pkl', 'rb') as f:\n    feature_names = pickle.load(f)\nprint('  Feature names loaded')\n\n# Load model info\nwith open(MODEL_DIR / 'model_info_v11.json', 'r') as f:\n    model_info = json.load(f)\n\nprint(f'\\nModel Configuration:')\nprint(f'  Version: {model_info[\"version\"]}')\nprint(f'  Training samples: {model_info[\"training_data\"][\"total_samples\"]}')\nprint(f'  Validation accuracy: {model_info[\"validation_accuracy\"][\"ensemble\"]:.2%}')\n\n# \u30a2\u30f3\u30b5\u30f3\u30d6\u30eb\u91cd\u307f\nlgb_weight = model_info['ensemble_weights']['lgb']\nxgb_weight = model_info['ensemble_weights']['xgb']\ncat_weight = model_info['ensemble_weights']['catboost']"}, {"cell_type": "markdown", "metadata": {}, "source": "## 2. \u30b8\u30a7\u30b9\u30c1\u30e3\u30fc\u30e9\u30d9\u30eb\u5b9a\u7fa9"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "source": "# Gesture labels mapping (18 classes)\ngesture_labels = [\n    'Above ear - pull hair',\n    'Cheek - pinch skin',\n    'Drink from bottle/cup',\n    'Eyelash - pull hair',\n    'Eyebrow - pull hair',\n    'Feel around in tray and pull out an object',\n    'Forehead - pull hairline',\n    'Forehead - scratch',\n    'Glasses on/off',\n    'Neck - pinch skin',\n    'Neck - scratch',\n    'Pinch knee/leg skin',\n    'Pull air toward your face',\n    'Scratch knee/leg skin',\n    'Text on phone',\n    'Wave hello',\n    'Write name in air',\n    'Write name on leg'\n]\n\nprint(f'Gesture labels: {len(gesture_labels)} classes')"}, {"cell_type": "markdown", "metadata": {}, "source": "## 3. \u7279\u5fb4\u91cf\u62bd\u51fa\u95a2\u6570"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "source": "def extract_features(sequence_df, demographics_df):\n    \"\"\"Extract features from a single sequence (polars DataFrames)\"\"\"\n    features = {}\n    \n    # Convert polars to pandas for processing\n    seq_pd = sequence_df.to_pandas()\n    demo_pd = demographics_df.to_pandas()\n    \n    # Metadata features\n    features['subject_id'] = hash(demo_pd['subject'].iloc[0]) % 1000\n    features['seq_length'] = len(seq_pd)\n    \n    # Accelerometer features\n    for axis in ['x', 'y', 'z']:\n        col = f'acc_{axis}'\n        if col in seq_pd.columns:\n            values = seq_pd[col].values\n            features[f'acc_{axis}_mean'] = np.mean(values)\n            features[f'acc_{axis}_std'] = np.std(values)\n            features[f'acc_{axis}_max'] = np.max(values)\n            features[f'acc_{axis}_min'] = np.min(values)\n            features[f'acc_{axis}_range'] = np.ptp(values)\n            features[f'acc_{axis}_skew'] = pd.Series(values).skew()\n            features[f'acc_{axis}_kurt'] = pd.Series(values).kurt()\n            \n            # FFT features\n            if len(values) > 0:\n                fft_vals = np.abs(np.fft.fft(values))[:len(values)//2]\n                features[f'acc_{axis}_fft_max'] = np.max(fft_vals) if len(fft_vals) > 0 else 0\n                features[f'acc_{axis}_fft_mean'] = np.mean(fft_vals) if len(fft_vals) > 0 else 0\n            else:\n                features[f'acc_{axis}_fft_max'] = 0\n                features[f'acc_{axis}_fft_mean'] = 0\n            \n            # Jerk\n            if len(values) > 1:\n                jerk = np.diff(values)\n                features[f'acc_{axis}_jerk_mean'] = np.mean(np.abs(jerk))\n                features[f'acc_{axis}_jerk_std'] = np.std(jerk)\n            else:\n                features[f'acc_{axis}_jerk_mean'] = 0\n                features[f'acc_{axis}_jerk_std'] = 0\n    \n    # Acceleration magnitude\n    if all(f'acc_{axis}' in seq_pd.columns for axis in ['x', 'y', 'z']):\n        acc_magnitude = np.sqrt(seq_pd['acc_x']**2 + seq_pd['acc_y']**2 + seq_pd['acc_z']**2)\n        features['acc_magnitude_mean'] = np.mean(acc_magnitude)\n        features['acc_magnitude_std'] = np.std(acc_magnitude)\n        features['acc_magnitude_max'] = np.max(acc_magnitude)\n    else:\n        features['acc_magnitude_mean'] = 0\n        features['acc_magnitude_std'] = 0\n        features['acc_magnitude_max'] = 0\n    \n    # Gyroscope features\n    for axis in ['x', 'y', 'z']:\n        col = f'gyr_{axis}'\n        if col in seq_pd.columns:\n            values = seq_pd[col].values\n            features[f'gyr_{axis}_mean'] = np.mean(values)\n            features[f'gyr_{axis}_std'] = np.std(values)\n            features[f'gyr_{axis}_max'] = np.max(values)\n            features[f'gyr_{axis}_energy'] = np.sum(values**2)\n        else:\n            features[f'gyr_{axis}_mean'] = 0\n            features[f'gyr_{axis}_std'] = 0\n            features[f'gyr_{axis}_max'] = 0\n            features[f'gyr_{axis}_energy'] = 0\n    \n    # Angular velocity magnitude\n    if all(f'gyr_{axis}' in seq_pd.columns for axis in ['x', 'y', 'z']):\n        gyr_magnitude = np.sqrt(seq_pd['gyr_x']**2 + seq_pd['gyr_y']**2 + seq_pd['gyr_z']**2)\n        features['gyr_magnitude_mean'] = np.mean(gyr_magnitude)\n        features['gyr_magnitude_std'] = np.std(gyr_magnitude)\n    else:\n        features['gyr_magnitude_mean'] = 0\n        features['gyr_magnitude_std'] = 0\n    \n    # Quaternion features\n    for comp in ['w', 'x', 'y', 'z']:\n        col = f'quat_{comp}'\n        if col in seq_pd.columns:\n            values = seq_pd[col].values\n            features[f'quat_{comp}_mean'] = np.mean(values)\n            features[f'quat_{comp}_std'] = np.std(values)\n            features[f'quat_{comp}_change'] = values[-1] - values[0] if len(values) > 0 else 0\n        else:\n            features[f'quat_{comp}_mean'] = 0\n            features[f'quat_{comp}_std'] = 0\n            features[f'quat_{comp}_change'] = 0\n    \n    # ToF sensor features (simplified)\n    tof_features = []\n    for i in range(8):\n        col = f'tof_{i}'\n        if col in seq_pd.columns:\n            values = seq_pd[col].values\n            tof_features.extend([\n                np.mean(values),\n                np.std(values),\n                np.median(values)\n            ])\n    \n    # ToF principal components (first 5)\n    if tof_features:\n        for i in range(min(5, len(tof_features))):\n            features[f'tof_pc_{i}'] = tof_features[i]\n    else:\n        for i in range(5):\n            features[f'tof_pc_{i}'] = 0\n    \n    # Ensure all features are present\n    for fname in feature_names:\n        if fname not in features:\n            features[fname] = 0\n    \n    return features"}, {"cell_type": "markdown", "metadata": {}, "source": "## 4. \u4e88\u6e2c\u95a2\u6570\uff08CMI\u6e96\u62e0\uff09"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "source": "def predict(sequence: pl.DataFrame, demographics: pl.DataFrame) -> str:\n    \"\"\"CMI inference function - returns gesture label string\"\"\"\n    \n    try:\n        # Extract features\n        features = extract_features(sequence, demographics)\n        \n        # Create feature vector in correct order\n        X = np.array([[features[fname] for fname in feature_names]])\n        \n        # Scale features\n        X_scaled = scaler.transform(X)\n        \n        # Get predictions from each model\n        lgb_proba = lgb_model.predict(X_scaled)\n        xgb_proba = xgb_model.predict(xgb.DMatrix(X_scaled))\n        cat_proba = cat_model.predict_proba(X_scaled)\n        \n        # Ensure shape is (1, n_classes)\n        if lgb_proba.ndim == 1:\n            lgb_proba = lgb_proba.reshape(1, -1)\n        if xgb_proba.ndim == 1:\n            xgb_proba = xgb_proba.reshape(1, -1)\n        if cat_proba.ndim == 1:\n            cat_proba = cat_proba.reshape(1, -1)\n        \n        # Ensemble predictions\n        ensemble_proba = lgb_weight * lgb_proba + xgb_weight * xgb_proba + cat_weight * cat_proba\n        \n        # Get predicted class\n        predicted_class = np.argmax(ensemble_proba[0])\n        \n        return gesture_labels[predicted_class]\n        \n    except Exception as e:\n        print(f'Error in prediction: {e}')\n        # Fallback to most common gesture\n        return 'Forehead - pull hairline'"}, {"cell_type": "markdown", "metadata": {}, "source": "## 5. CMI\u63a8\u8ad6\u30b5\u30fc\u30d0\u30fc\u306e\u8d77\u52d5"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "source": "# Initialize and run CMI Inference Server\nprint('=' * 50)\nprint('CMI V11 Model - Final Submission')\nprint('=' * 50)\nprint(f'\\nModel Configuration:')\nprint(f'  - Real data: 50% ({model_info[\"training_data\"][\"real_samples\"]} samples)')\nprint(f'  - Synthetic data: 50% ({model_info[\"training_data\"][\"synthetic_samples\"]} samples)')\nprint(f'  - Total samples: {model_info[\"training_data\"][\"total_samples\"]}')\nprint(f'  - Validation accuracy: {model_info[\"validation_accuracy\"][\"ensemble\"]:.2%}')\nprint(f'  - Ensemble weights: LightGBM={lgb_weight:.0%}, XGBoost={xgb_weight:.0%}, CatBoost={cat_weight:.0%}')\n\n# Create inference server\ninference_server = kaggle_evaluation.cmi_inference_server.CMIInferenceServer(predict)\n\n# Check if running in Kaggle environment\nif os.getenv('KAGGLE_IS_COMPETITION_RERUN'):\n    # Competition submission environment\n    print('\\n[Competition Mode] Starting inference server...')\n    inference_server.serve()\n    print('Inference completed - submission.parquet generated')\nelse:\n    # Local test environment\n    print('\\n[Local Test Mode] Running local gateway test...')\n    print('Note: In actual submission, server will generate submission.parquet')\n    \n    # Try local test if data is available\n    test_path = '/kaggle/input/cmi-detect-behavior-with-sensor-data/test.csv'\n    demo_path = '/kaggle/input/cmi-detect-behavior-with-sensor-data/test_demographics.csv'\n    \n    if os.path.exists(test_path) and os.path.exists(demo_path):\n        try:\n            inference_server.run_local_gateway(\n                data_paths=(test_path, demo_path)\n            )\n            print('Local test completed successfully')\n        except Exception as e:\n            print(f'Local test error (expected in notebook environment): {e}')\n    else:\n        print('Test data not found - skipping local test')\n        print('Server is ready for competition submission')\n\nprint('\\n' + '=' * 50)\nprint('Notebook execution complete')\nprint('=' * 50)"}], "metadata": {"kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}, "language_info": {"name": "python", "version": "3.10.0"}}, "nbformat": 4, "nbformat_minor": 4}