{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":102335,"databundleVersionId":12518947,"sourceType":"competition"}],"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport polars as pl\nfrom sklearn.preprocessing import LabelEncoder\nfrom sklearn.model_selection import StratifiedKFold, StratifiedShuffleSplit\nfrom sklearn.metrics import f1_score, classification_report\nimport catboost as cb\nfrom catboost import CatBoostClassifier\nimport os\nimport warnings\nwarnings.filterwarnings('ignore')\n\n# Import evaluation API\nimport kaggle_evaluation.cmi_inference_server","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Load data\ntrain_df = pd.read_csv(\"/kaggle/input/cmi-detect-behavior-with-sensor-data/train.csv\")\ntest_df = pd.read_csv(\"/kaggle/input/cmi-detect-behavior-with-sensor-data/test.csv\")\ntest_demographics_df = pd.read_csv(\"/kaggle/input/cmi-detect-behavior-with-sensor-data/test_demographics.csv\")\ntrain_demographics_df = pd.read_csv(\"/kaggle/input/cmi-detect-behavior-with-sensor-data/train_demographics.csv\")\n\nprint(f\"Train shape: {train_df.shape}\")\nprint(f\"Test shape: {test_df.shape}\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Filter to target sequences only (BFRB gestures)\ntrain_df = train_df.loc[train_df['sequence_type'] == 'Target'].reset_index(drop=True)\nprint(f\"Target sequences shape: {train_df.shape}\")\n\n# Analyze gesture distribution\nprint(\"\\nGesture distribution:\")\nprint(train_df['gesture'].value_counts())\n\n# Define sensor columns\nsensor_cols = ['acc_x', 'acc_y', 'acc_z', 'rot_w', 'rot_x', 'rot_y', 'rot_z']\nall_sensor_cols = [col for col in train_df.columns if any(s in col for s in ['acc_', 'rot_', 'thm_', 'tof_'])]\nprint(f\"\\nTotal sensor columns: {len(all_sensor_cols)}\")\n\n# Fill missing values\ntrain_df[all_sensor_cols] = train_df[all_sensor_cols].fillna(-1)\n\n# Encode target\nle = LabelEncoder()\ntrain_df['encoded_gesture'] = le.fit_transform(train_df['gesture'])\nprint(f\"\\nEncoded gestures: {len(le.classes_)} classes\")\nprint(\"Classes:\", le.classes_)\n\ndef create_sequence_features(df, demographics_df=None):\n    \"\"\"\n    Create comprehensive features from sensor sequences\n    \"\"\"\n    features = []\n    \n    # Group by sequence_id to create sequence-level features\n    for seq_id, group in df.groupby('sequence_id'):\n        seq_features = {'sequence_id': seq_id}\n        \n        # Basic sequence info\n        seq_features['sequence_length'] = len(group)\n        seq_features['subject'] = group['subject'].iloc[0]\n        \n        # Add demographics if available\n        if demographics_df is not None and not demographics_df.empty:\n            subject_demo = demographics_df[demographics_df['subject'] == seq_features['subject']]\n            if not subject_demo.empty:\n                seq_features['adult_child'] = subject_demo['adult_child'].iloc[0]\n                seq_features['age'] = subject_demo['age'].iloc[0]\n                seq_features['sex'] = subject_demo['sex'].iloc[0]\n                seq_features['handedness'] = subject_demo['handedness'].iloc[0]\n                seq_features['height_cm'] = subject_demo['height_cm'].iloc[0]\n                seq_features['shoulder_to_wrist_cm'] = subject_demo['shoulder_to_wrist_cm'].iloc[0]\n                seq_features['elbow_to_wrist_cm'] = subject_demo['elbow_to_wrist_cm'].iloc[0]\n            else:\n                # Set default values if demographics not found\n                seq_features['adult_child'] = -1\n                seq_features['age'] = -1\n                seq_features['sex'] = -1\n                seq_features['handedness'] = -1\n                seq_features['height_cm'] = -1\n                seq_features['shoulder_to_wrist_cm'] = -1\n                seq_features['elbow_to_wrist_cm'] = -1\n        else:\n            # Set default values if demographics not available\n            seq_features['adult_child'] = -1\n            seq_features['age'] = -1\n            seq_features['sex'] = -1\n            seq_features['handedness'] = -1\n            seq_features['height_cm'] = -1\n            seq_features['shoulder_to_wrist_cm'] = -1\n            seq_features['elbow_to_wrist_cm'] = -1\n        \n        # Behavior phase encoding (if available)\n        if 'behavior' in group.columns:\n            behavior_counts = group['behavior'].value_counts()\n            for behavior in ['Transition', 'Pause', 'Gesture']:\n                seq_features[f'{behavior.lower()}_count'] = behavior_counts.get(behavior, 0)\n                seq_features[f'{behavior.lower()}_ratio'] = behavior_counts.get(behavior, 0) / len(group)\n        else:\n            # Set default values if behavior column is not available\n            for behavior in ['Transition', 'Pause', 'Gesture']:\n                seq_features[f'{behavior.lower()}_count'] = 0\n                seq_features[f'{behavior.lower()}_ratio'] = 0\n        \n        # Statistical features for each sensor type\n        sensor_groups = {\n            'acc': ['acc_x', 'acc_y', 'acc_z'],\n            'rot': ['rot_w', 'rot_x', 'rot_y', 'rot_z'],\n            'thm': [col for col in all_sensor_cols if 'thm_' in col],\n            'tof': [col for col in all_sensor_cols if 'tof_' in col]\n        }\n        \n        for sensor_type, cols in sensor_groups.items():\n            available_cols = [col for col in cols if col in group.columns]\n            if available_cols:\n                sensor_data = group[available_cols].values\n                \n                # Basic statistics\n                seq_features[f'{sensor_type}_mean'] = np.mean(sensor_data)\n                seq_features[f'{sensor_type}_std'] = np.std(sensor_data)\n                seq_features[f'{sensor_type}_min'] = np.min(sensor_data)\n                seq_features[f'{sensor_type}_max'] = np.max(sensor_data)\n                seq_features[f'{sensor_type}_range'] = np.max(sensor_data) - np.min(sensor_data)\n                seq_features[f'{sensor_type}_median'] = np.median(sensor_data)\n                \n                # Percentiles\n                seq_features[f'{sensor_type}_q25'] = np.percentile(sensor_data, 25)\n                seq_features[f'{sensor_type}_q75'] = np.percentile(sensor_data, 75)\n                seq_features[f'{sensor_type}_iqr'] = np.percentile(sensor_data, 75) - np.percentile(sensor_data, 25)\n                \n                # Advanced statistics\n                seq_features[f'{sensor_type}_skew'] = pd.Series(sensor_data.flatten()).skew()\n                seq_features[f'{sensor_type}_kurtosis'] = pd.Series(sensor_data.flatten()).kurtosis()\n                \n                # Signal characteristics\n                seq_features[f'{sensor_type}_zero_crossings'] = np.sum(np.diff(np.sign(sensor_data.flatten())) != 0)\n                seq_features[f'{sensor_type}_energy'] = np.sum(sensor_data**2)\n                seq_features[f'{sensor_type}_rms'] = np.sqrt(np.mean(sensor_data**2))\n        \n        # Specific features for IMU data (acceleration and rotation)\n        if all(col in group.columns for col in ['acc_x', 'acc_y', 'acc_z']):\n            acc_data = group[['acc_x', 'acc_y', 'acc_z']].values\n            # Magnitude of acceleration\n            acc_magnitude = np.sqrt(np.sum(acc_data**2, axis=1))\n            seq_features['acc_magnitude_mean'] = np.mean(acc_magnitude)\n            seq_features['acc_magnitude_std'] = np.std(acc_magnitude)\n            seq_features['acc_magnitude_max'] = np.max(acc_magnitude)\n            \n        if all(col in group.columns for col in ['rot_w', 'rot_x', 'rot_y', 'rot_z']):\n            rot_data = group[['rot_w', 'rot_x', 'rot_y', 'rot_z']].values\n            # Rotation magnitude\n            rot_magnitude = np.sqrt(np.sum(rot_data**2, axis=1))\n            seq_features['rot_magnitude_mean'] = np.mean(rot_magnitude)\n            seq_features['rot_magnitude_std'] = np.std(rot_magnitude)\n            seq_features['rot_magnitude_max'] = np.max(rot_magnitude)\n        \n        # Add target if available\n        if 'encoded_gesture' in group.columns:\n            seq_features['target'] = group['encoded_gesture'].iloc[0]\n            seq_features['gesture'] = group['gesture'].iloc[0]\n        \n        features.append(seq_features)\n    \n    return pd.DataFrame(features)\n\n# Create features\nprint(\"\\nCreating sequence-level features...\")\ntrain_features = create_sequence_features(train_df, train_demographics_df)\nprint(f\"Training features shape: {train_features.shape}\")\n\n# Prepare features and target\nfeature_cols = [col for col in train_features.columns \n                if col not in ['sequence_id', 'target', 'gesture', 'subject']]\nX = train_features[feature_cols].fillna(-1)\ny = train_features['target']\n\nprint(f\"Feature matrix shape: {X.shape}\")\nprint(f\"Target shape: {y.shape}\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def competition_metric(y_true, y_pred, le_instance, all_original_gestures):\n    \"\"\"\n    Competition metric calculation\n    \"\"\"\n    bfrb_gestures = [g for g in all_original_gestures if g in le_instance.classes_]\n    \n    # Binary F1: All are Target in this filtered dataset\n    y_true_binary = np.ones_like(y_true, dtype=int)\n    y_pred_binary = np.ones_like(y_pred, dtype=int)\n    binary_f1 = f1_score(y_true_binary, y_pred_binary, average='binary', pos_label=1, zero_division=0)\n    \n    # Macro F1: specific gesture classification\n    macro_f1 = f1_score(y_true, y_pred, average='macro', zero_division=0)\n    \n    final_score = (binary_f1 + macro_f1) / 2\n    return final_score, binary_f1, macro_f1\n\n# Cross-validation setup\nprint(\"\\nSetting up cross-validation...\")\ncv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)\ncv_scores = []\nmodels = []\n\nall_original_gestures_in_train = train_df['gesture'].unique()\n\n# CatBoost model with cross-validation\nprint(\"\\nTraining CatBoost models with cross-validation...\")\nfor fold, (train_idx, val_idx) in enumerate(cv.split(X, y)):\n    print(f\"\\nFold {fold + 1}/5\")\n    \n    X_train_fold, X_val_fold = X.iloc[train_idx], X.iloc[val_idx]\n    y_train_fold, y_val_fold = y.iloc[train_idx], y.iloc[val_idx]\n    \n    # CatBoost model with GPU acceleration\n    model = CatBoostClassifier(\n        iterations=1000,\n        learning_rate=0.1,\n        depth=8,\n        l2_leaf_reg=3,\n        random_seed=42 + fold,\n        verbose=100,  # Print every 100 iterations\n        eval_metric='MultiClass',\n        early_stopping_rounds=100,\n        task_type='GPU',  # Enable GPU acceleration\n        devices='0'  # Use GPU device 0\n    )\n    \n    # Train model with verbose output\n    model.fit(\n        X_train_fold, y_train_fold,\n        eval_set=(X_val_fold, y_val_fold),\n        verbose=100  # Print every 100 iterations\n    )\n    \n    # Predict\n    y_pred_fold = model.predict(X_val_fold)\n    \n    # Calculate score\n    score, binary_f1, macro_f1 = competition_metric(\n        y_val_fold, y_pred_fold, le, all_original_gestures_in_train\n    )\n    \n    cv_scores.append(score)\n    models.append(model)\n    \n    print(f\"Fold {fold + 1} - Competition Score: {score:.4f} (Binary F1: {binary_f1:.4f}, Macro F1: {macro_f1:.4f})\")\n\nprint(f\"\\nCross-validation results:\")\nprint(f\"Mean CV Score: {np.mean(cv_scores):.4f} (+/- {np.std(cv_scores) * 2:.4f})\")\nprint(f\"Individual fold scores: {cv_scores}\")\n\n# Train final model on all data with GPU acceleration\nprint(\"\\nTraining final model on all training data...\")\nfinal_model = CatBoostClassifier(\n    iterations=1000,\n    learning_rate=0.1,\n    depth=8,\n    l2_leaf_reg=3,\n    random_seed=42,\n    verbose=100,  # Print every 100 iterations\n    eval_metric='MultiClass',\n    task_type='GPU',  # Enable GPU acceleration\n    devices='0'  # Use GPU device 0\n)\n\nfinal_model.fit(X, y, verbose=100)\n\n# Feature importance\nprint(\"\\nTop 20 most important features:\")\nfeature_importance = final_model.get_feature_importance()\nfeature_names = X.columns\nimportance_df = pd.DataFrame({\n    'feature': feature_names,\n    'importance': feature_importance\n}).sort_values('importance', ascending=False)\n\nprint(importance_df.head(20))","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Prediction function for submission\ndef predict(sequence: pl.DataFrame, demographics: pl.DataFrame) -> str:\n    \"\"\"\n    Prediction function for Kaggle evaluation\n    \"\"\"\n    try:\n        # Convert to pandas\n        sequence_pd = sequence.to_pandas()\n        demographics_pd = demographics.to_pandas()\n        \n        # Fill missing values for all sensor columns that exist\n        existing_sensor_cols = [col for col in all_sensor_cols if col in sequence_pd.columns]\n        if existing_sensor_cols:\n            sequence_pd[existing_sensor_cols] = sequence_pd[existing_sensor_cols].fillna(-1)\n        \n        # Create features for this single sequence\n        seq_features = create_sequence_features(sequence_pd, demographics_pd)\n        \n        # Prepare feature vector - ensure all expected features are present\n        X_inference = seq_features[feature_cols].fillna(-1)\n        \n        # Predict using ensemble of CV models\n        predictions = []\n        for model in models:\n            pred = model.predict(X_inference)\n            # Ensure we get a scalar value\n            if isinstance(pred, np.ndarray):\n                pred = pred[0]\n            predictions.append(int(pred))\n        \n        # Use majority vote or most confident prediction\n        predicted_label_id = max(set(predictions), key=predictions.count)\n        \n        # Convert back to gesture string\n        predicted_gesture_str = le.inverse_transform([predicted_label_id])[0]\n        \n        return predicted_gesture_str\n        \n    except Exception as e:\n        print(f\"Error in prediction: {e}\")\n        # Return a default gesture if prediction fails\n        return le.classes_[0]\n\n# Test the prediction function\nprint(\"\\nTesting prediction function...\")\nsample_sequence = train_df[train_df['sequence_id'] == train_df['sequence_id'].iloc[0]]\nsample_demographics = train_demographics_df[train_demographics_df['subject'] == sample_sequence['subject'].iloc[0]]\n\nsample_seq_pl = pl.from_pandas(sample_sequence)\nsample_demo_pl = pl.from_pandas(sample_demographics)\n\ntest_prediction = predict(sample_seq_pl, sample_demo_pl)\nactual_gesture = sample_sequence['gesture'].iloc[0]\nprint(f\"Test prediction: {test_prediction}\")\nprint(f\"Actual gesture: {actual_gesture}\")\nprint(f\"Match: {test_prediction == actual_gesture}\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Setup inference server\nprint(\"\\nSetting up inference server...\")\ninference_server = kaggle_evaluation.cmi_inference_server.CMIInferenceServer(predict)\n\nif os.getenv('KAGGLE_IS_COMPETITION_RERUN'):\n    inference_server.serve()\nelse:\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":{"trusted":true},"outputs":[],"execution_count":null}]}