{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":102335,"databundleVersionId":12518947,"isSourceIdPinned":false,"sourceType":"competition"},{"sourceId":12041784,"sourceType":"datasetVersion","datasetId":7577430},{"sourceId":12051308,"sourceType":"datasetVersion","datasetId":7584391}],"dockerImageVersionId":31040,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# ─────────────────────────────────────────────────────────────────────────────\n# CELL 1: IMPORTS & ENHANCED FEATURE‐EXTRACTION HELPERS\n# ─────────────────────────────────────────────────────────────────────────────\n\nimport pandas as pd\nimport numpy as np\n\nfrom sklearn.preprocessing import LabelEncoder, RobustScaler\nfrom sklearn.model_selection import StratifiedGroupKFold\nfrom sklearn.feature_selection import SelectFromModel\nfrom sklearn.metrics import classification_report, accuracy_score\n\nimport lightgbm as lgb\n\nfrom scipy.fftpack import rfft, rfftfreq\nfrom scipy.stats import skew, kurtosis\nfrom scipy.signal import find_peaks, butter, filtfilt\n\nimport warnings\nwarnings.filterwarnings('ignore')\n\n\ndef _apply_butterworth_filter(signal, sampling_rate=50.0, lowcut=0.5, highcut=20.0, order=4):\n    \"\"\"Apply Butterworth bandpass filter to remove noise.\"\"\"\n    nyquist = 0.5 * sampling_rate\n    low = lowcut / nyquist\n    high = highcut / nyquist\n    if high >= 1.0:\n        high = 0.99\n    b, a = butter(order, [low, high], btype='band')\n    return filtfilt(b, a, signal)\n\n\ndef _compute_enhanced_basic_aggregates(df: pd.DataFrame) -> pd.DataFrame:\n    \"\"\"\n    Enhanced aggregation with additional statistical measures.\n    \"\"\"\n    acc_cols = [\"acc_x\", \"acc_y\", \"acc_z\"]\n    rot_cols = [\"rot_w\", \"rot_x\", \"rot_y\", \"rot_z\"]\n    thm_cols = [c for c in df.columns if c.startswith(\"thm_\")]\n    tof_cols_by_sensor = {\n        i: [c for c in df.columns if c.startswith(f\"tof_{i}_v\")] for i in range(1, 6)\n    }\n    all_tof_cols = [c for sub in tof_cols_by_sensor.values() for c in sub]\n\n    agg_dict = {}\n    # Enhanced aggregations with more statistics\n    for c in acc_cols + rot_cols + thm_cols + [\"acc_mag\"]:\n        agg_dict[c] = [\n            \"mean\", \"std\", \"min\", \"max\", \"median\", \"skew\",\n            lambda x: np.percentile(x, 25), lambda x: np.percentile(x, 75)\n        ]\n    # For each TOF pixel, keep fewer stats to avoid too‐many features\n    for c in all_tof_cols:\n        agg_dict[c] = [\"mean\", \"std\", \"median\"]\n\n    grouped = df.groupby(\"sequence_id\").agg(agg_dict)\n\n    # Flatten multi‐level columns into names like “acc_x_mean”, “acc_x_q25”, etc.\n    new_columns = []\n    for lvl0, func in grouped.columns.to_list():\n        if callable(func):\n            fname = func.__name__ if hasattr(func, \"__name__\") else str(func)\n            if \"percentile\" in fname:\n                # distinguish 25 vs 75 based on lambda naming convention\n                if \"25\" in fname:\n                    new_columns.append(f\"{lvl0}_q25\")\n                else:\n                    new_columns.append(f\"{lvl0}_q75\")\n            else:\n                new_columns.append(f\"{lvl0}_{fname}\")\n        else:\n            new_columns.append(f\"{lvl0}_{func}\")\n    grouped.columns = new_columns\n    return grouped\n\n\ndef _compute_enhanced_time_series_features(df: pd.DataFrame, sampling_rate: float = 50.0) -> pd.DataFrame:\n    \"\"\"\n    Enhanced time series features with filtering, peak detection, and more frequency‐domain features.\n    \"\"\"\n    use_behavior = \"behavior\" in df.columns\n    if use_behavior:\n        df_gest = df[df[\"behavior\"] == \"Gesture\"].copy()\n    else:\n        df_gest = df.copy()\n\n    if \"acc_mag\" not in df_gest.columns:\n        df_gest[\"acc_mag\"] = np.sqrt(\n            df_gest[\"acc_x\"]**2 + df_gest[\"acc_y\"]**2 + df_gest[\"acc_z\"]**2\n        )\n\n    def _extract_enhanced_for_sequence(sub: pd.DataFrame) -> pd.Series:\n        arr_x = sub[\"acc_x\"].values\n        arr_y = sub[\"acc_y\"].values\n        arr_z = sub[\"acc_z\"].values\n        arr_mag = sub[\"acc_mag\"].values\n        n = len(sub)\n\n        # If too short, just return zeros for all enhanced features\n        if n < 3:\n            zero_keys = [\n                \"acc_x_rms\", \"acc_y_rms\", \"acc_z_rms\",\n                \"acc_x_filtered_std\", \"acc_y_filtered_std\", \"acc_z_filtered_std\",\n                \"acc_xy_corr\", \"acc_xz_corr\", \"acc_yz_corr\",\n                \"acc_mag_energy_0_2Hz\", \"acc_mag_energy_2_5Hz\",\n                \"acc_mag_energy_5_10Hz\", \"acc_mag_energy_10_20Hz\",\n                \"acc_mag_spectral_centroid\", \"acc_mag_spectral_entropy\",\n                \"acc_mag_peak_count\", \"acc_mag_zero_crossing_rate\",\n                \"acc_mag_dominant_freq\", \"gesture_duration\"\n            ]\n            return pd.Series({f: 0.0 for f in zero_keys})\n\n        # 1) Apply Butterworth filter (if it fails, fall back to raw)\n        try:\n            arr_x_filt = _apply_butterworth_filter(arr_x, sampling_rate)\n            arr_y_filt = _apply_butterworth_filter(arr_y, sampling_rate)\n            arr_z_filt = _apply_butterworth_filter(arr_z, sampling_rate)\n            arr_mag_filt = _apply_butterworth_filter(arr_mag, sampling_rate)\n        except:\n            arr_x_filt = arr_x\n            arr_y_filt = arr_y\n            arr_z_filt = arr_z\n            arr_mag_filt = arr_mag\n\n        # 2) RMS of filtered signals\n        acc_x_rms = np.sqrt(np.mean(arr_x_filt**2))\n        acc_y_rms = np.sqrt(np.mean(arr_y_filt**2))\n        acc_z_rms = np.sqrt(np.mean(arr_z_filt**2))\n\n        # 3) Filtered standard deviations\n        acc_x_filtered_std = np.std(arr_x_filt)\n        acc_y_filtered_std = np.std(arr_y_filt)\n        acc_z_filtered_std = np.std(arr_z_filt)\n\n        # 4) Safe correlations on filtered axes\n        def safe_corr(a, b):\n            if len(a) > 1 and a.std() > 1e-8 and b.std() > 1e-8:\n                return np.corrcoef(a, b)[0, 1]\n            return 0.0\n\n        acc_xy_corr = safe_corr(arr_x_filt, arr_y_filt)\n        acc_xz_corr = safe_corr(arr_x_filt, arr_z_filt)\n        acc_yz_corr = safe_corr(arr_y_filt, arr_z_filt)\n\n        # 5) FFT‐based features on filtered magnitude\n        yf = rfft(arr_mag_filt)\n        xf = rfftfreq(n, 1.0 / sampling_rate)\n        magnitude_spectrum = np.abs(yf)\n\n        band1_energy = np.sum(magnitude_spectrum[(xf >= 0) & (xf < 2)]**2)\n        band2_energy = np.sum(magnitude_spectrum[(xf >= 2) & (xf < 5)]**2)\n        band3_energy = np.sum(magnitude_spectrum[(xf >= 5) & (xf < 10)]**2)\n        band4_energy = np.sum(magnitude_spectrum[(xf >= 10) & (xf < 20)]**2)\n\n        if magnitude_spectrum.sum() > 0:\n            centroid = np.sum(xf * magnitude_spectrum) / magnitude_spectrum.sum()\n            dominant_freq = xf[np.argmax(magnitude_spectrum)]\n        else:\n            centroid = 0.0\n            dominant_freq = 0.0\n\n        psd = magnitude_spectrum**2\n        psd_norm = psd / psd.sum() if psd.sum() > 0 else psd\n        spectral_entropy = -np.sum(psd_norm * np.log2(psd_norm + 1e-12))\n\n        # 6) Peak detection on magnitude\n        try:\n            peaks, _ = find_peaks(arr_mag_filt, height=np.std(arr_mag_filt))\n            peak_count = len(peaks)\n        except:\n            peak_count = 0\n\n        # 7) Zero‐crossing rate\n        zero_crossings = np.sum(np.diff(np.sign(arr_mag_filt - np.mean(arr_mag_filt))) != 0)\n        zero_crossing_rate = zero_crossings / (n - 1) if n > 1 else 0\n\n        # 8) Gesture duration\n        gesture_duration = n / sampling_rate\n\n        return pd.Series({\n            \"acc_x_rms\":                  acc_x_rms,\n            \"acc_y_rms\":                  acc_y_rms,\n            \"acc_z_rms\":                  acc_z_rms,\n            \"acc_x_filtered_std\":         acc_x_filtered_std,\n            \"acc_y_filtered_std\":         acc_y_filtered_std,\n            \"acc_z_filtered_std\":         acc_z_filtered_std,\n            \"acc_xy_corr\":                acc_xy_corr,\n            \"acc_xz_corr\":                acc_xz_corr,\n            \"acc_yz_corr\":                acc_yz_corr,\n            \"acc_mag_energy_0_2Hz\":       band1_energy,\n            \"acc_mag_energy_2_5Hz\":       band2_energy,\n            \"acc_mag_energy_5_10Hz\":      band3_energy,\n            \"acc_mag_energy_10_20Hz\":     band4_energy,\n            \"acc_mag_spectral_centroid\":  centroid,\n            \"acc_mag_spectral_entropy\":   spectral_entropy,\n            \"acc_mag_peak_count\":         peak_count,\n            \"acc_mag_zero_crossing_rate\": zero_crossing_rate,\n            \"acc_mag_dominant_freq\":      dominant_freq,\n            \"gesture_duration\":           gesture_duration\n        })\n\n    ts_feats = df_gest.groupby(\"sequence_id\").apply(_extract_enhanced_for_sequence)\n    ts_feats.index.name = \"sequence_id\"\n    return ts_feats\n\n\ndef _compute_enhanced_thermopile_features(df: pd.DataFrame) -> pd.DataFrame:\n    \"\"\"\n    Enhanced thermopile features with spatial patterns.\n    \"\"\"\n    thm_cols = [c for c in df.columns if c.startswith(\"thm_\")]\n\n    def _extract_enhanced_thm_for_sequence(sub: pd.DataFrame) -> pd.Series:\n        result = {}\n        thm_data = sub[thm_cols].values  # shape: (time, n_thm_cols)\n        for c in thm_cols:\n            vals = sub[c].dropna().values\n            if len(vals) >= 2:\n                slope = (vals[-1] - vals[0]) / (len(vals) - 1)\n                skewness = skew(vals)\n                kurtv = kurtosis(vals)\n                temp_range = vals.max() - vals.min()\n                temp_gradient = np.std(np.diff(vals)) if len(vals) > 1 else 0.0\n            else:\n                slope = skewness = kurtv = temp_range = temp_gradient = 0.0\n            result[f\"{c}_slope\"] = slope\n            result[f\"{c}_skew\"]   = skewness\n            result[f\"{c}_kurtosis\"] = kurtv\n            result[f\"{c}_range\"]  = temp_range\n            result[f\"{c}_gradient\"] = temp_gradient\n\n        # If it's an 8×8 thermopile (64 columns), compute center of heat for first few frames\n        if len(thm_cols) == 64:\n            for t in range(min(len(thm_data), 5)):\n                frame = thm_data[t].reshape(8, 8)\n                y_idx, x_idx = np.mgrid[0:8, 0:8]\n                total_heat = np.sum(frame)\n                if total_heat > 0:\n                    center_y = np.sum(y_idx * frame) / total_heat\n                    center_x = np.sum(x_idx * frame) / total_heat\n                    result[f\"thm_center_y_t{t}\"] = center_y\n                    result[f\"thm_center_x_t{t}\"] = center_x\n                else:\n                    result[f\"thm_center_y_t{t}\"] = 0.0\n                    result[f\"thm_center_x_t{t}\"] = 0.0\n\n        return pd.Series(result)\n\n    thm_feats = df.groupby(\"sequence_id\").apply(_extract_enhanced_thm_for_sequence)\n    thm_feats.index.name = \"sequence_id\"\n    return thm_feats\n\n\ndef _compute_tof_block_time_trends(df: pd.DataFrame, threshold: float = 10.0) -> pd.DataFrame:\n    \"\"\"\n    TOF block features + time‐trend for each sensor block.\n    \"\"\"\n    tof_cols_by_sensor = {\n        i: [c for c in df.columns if c.startswith(f\"tof_{i}_v\")] for i in range(1, 6)\n    }\n\n    df_masked = df.copy()\n    for i, cols in tof_cols_by_sensor.items():\n        df_masked[cols] = df_masked[cols].replace(-1, np.nan)\n\n    def _extract_tof_for_sequence(sub: pd.DataFrame) -> pd.Series:\n        out = {}\n        for i, cols in tof_cols_by_sensor.items():\n            block = sub[cols]\n            valid_mask = ~block.isna()\n            total = block.shape[0] * block.shape[1]\n            valid_count = valid_mask.sum().sum()\n            frac_valid = valid_count / total if total > 0 else 0.0\n\n            vals = block.values.flatten()\n            vals = vals[~np.isnan(vals)]\n            if vals.size > 0:\n                block_min = float(vals.min())\n                block_max = float(vals.max())\n                count_lt_T = int((vals < threshold).sum())\n            else:\n                block_min = block_max = count_lt_T = 0\n\n            # Time‐trend of average distance per frame:\n            avg_dist_per_frame = block.mean(axis=1).values\n            idxs = np.where(~np.isnan(avg_dist_per_frame))[0]\n            if len(idxs) >= 2:\n                x = idxs\n                y = avg_dist_per_frame[idxs]\n                slope = np.cov(x, y, bias=True)[0, 1] / np.var(x) if np.var(x) > 0 else 0.0\n            else:\n                slope = 0.0\n\n            out[f\"tof_{i}_frac_valid\"]        = frac_valid\n            out[f\"tof_{i}_block_min\"]         = block_min\n            out[f\"tof_{i}_block_max\"]         = block_max\n            out[f\"tof_{i}_count_lt_{int(threshold)}\"] = count_lt_T\n            out[f\"tof_{i}_time_trend\"]        = slope\n        return pd.Series(out)\n\n    tof_feats = df_masked.groupby(\"sequence_id\").apply(_extract_tof_for_sequence)\n    tof_feats.index.name = \"sequence_id\"\n    return tof_feats\n\n\ndef extract_enhanced_sequence_features(\n    df: pd.DataFrame,\n    demogs_df: pd.DataFrame = None,\n    sampling_rate: float = 50.0\n) -> pd.DataFrame:\n    \"\"\"\n    (1) Compute acc_mag + jerk terms\n    (2) Enhanced basic summary‐stat aggregates\n    (3) Enhanced time‐series features (FFT, RMS, correlations, peaks, entropy)\n    (4) Enhanced thermopile features\n    (5) TOF‐block features + time trends\n    (6) Join any {behavior, gesture, subject} labels\n    (7) Merge demographics (age, sex, etc.) if provided\n    \"\"\"\n    df = df.copy()\n    df[\"acc_mag\"] = np.sqrt(df[\"acc_x\"]**2 + df[\"acc_y\"]**2 + df[\"acc_z\"]**2)\n    # Add “jerk” terms for acc and acc_mag\n    for col in [\"acc_x\", \"acc_y\", \"acc_z\", \"acc_mag\"]:\n        df[f\"{col}_jerk\"] = df.groupby(\"sequence_id\")[col].diff().fillna(0)\n\n    # 1) Enhanced basic aggregates\n    basic_feats = _compute_enhanced_basic_aggregates(df)\n\n    # 2) Enhanced time-series features\n    ts_feats = _compute_enhanced_time_series_features(df, sampling_rate=sampling_rate)\n\n    # 3) Enhanced thermopile features\n    thm_feats = _compute_enhanced_thermopile_features(df)\n\n    # 4) TOF block-level features + time trends\n    tof_feats = _compute_tof_block_time_trends(df, threshold=10.0)\n\n    # 5) Drop overlapping thermopile columns from basic_feats (if any)\n    overlap_thm = set(basic_feats.columns).intersection(thm_feats.columns)\n    if overlap_thm:\n        basic_feats = basic_feats.drop(columns=list(overlap_thm), errors=\"ignore\")\n\n    # 6) Combine everything into one DataFrame\n    features = (\n        basic_feats\n        .join(ts_feats, how=\"left\")\n        .join(thm_feats, how=\"left\")\n        .join(tof_feats, how=\"left\")\n    )\n\n    # 7) Attach labels (if in training set)\n    if {\"behavior\", \"gesture\", \"subject\"}.issubset(df.columns):\n        seq_labels = (\n            df[[\"sequence_id\", \"behavior\", \"gesture\", \"subject\"]]\n            .drop_duplicates(subset=[\"sequence_id\"])\n            .set_index(\"sequence_id\")\n        )\n        for col in [\"behavior\", \"gesture\", \"subject\"]:\n            if col in features.columns:\n                features = features.drop(columns=[col])\n        features = features.join(seq_labels, how=\"left\")\n\n    elif \"subject\" in df.columns:\n        seq_subj = (\n            df[[\"sequence_id\", \"subject\"]]\n            .drop_duplicates(subset=[\"sequence_id\"])\n            .set_index(\"sequence_id\")\n        )\n        if \"subject\" in features.columns:\n            features = features.drop(columns=[\"subject\"])\n        features = features.join(seq_subj, how=\"left\")\n\n    # 8) Merge demographics if provided\n    if demogs_df is not None and \"subject\" in features.columns:\n        overlap = set(features.columns).intersection(set(demogs_df.columns))\n        if overlap:\n            features = features.drop(columns=list(overlap), errors=\"ignore\")\n        features = features.join(demogs_df, on=\"subject\", how=\"left\")\n\n    return features","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-03T22:06:12.529452Z","iopub.execute_input":"2025-06-03T22:06:12.529774Z","iopub.status.idle":"2025-06-03T22:06:12.574158Z","shell.execute_reply.started":"2025-06-03T22:06:12.529754Z","shell.execute_reply":"2025-06-03T22:06:12.573241Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ─────────────────────────────────────────────────────────────────────────────\n# CELL 2: LOAD TRAINED OBJECTS & DEFINE predict(...)\n# ─────────────────────────────────────────────────────────────────────────────\n\nimport os\nimport pickle\nimport traceback\n\nimport polars as pl\nimport numpy as np\nimport pandas as pd\n\n# (Assume Cell 1 has already done all imports for scikit‐learn, LightGBM, SciPy, etc.)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# 2a) Load exactly the objects you saved during training\n# ─────────────────────────────────────────────────────────────────────────────\nPICKLE_PATH = \"/kaggle/input/d/merolavtechnology/bfrb-dataset/trained_objects.pkl\"\nif not os.path.exists(PICKLE_PATH):\n    raise FileNotFoundError(f\"Cannot find '{PICKLE_PATH}'. Run training first.\")\n\nwith open(PICKLE_PATH, \"rb\") as f:\n    obj = pickle.load(f)\n\nGLOBAL_BST       = obj[\"bst\"]        # LightGBM booster (trained on selected features)\nGLOBAL_LE        = obj[\"le\"]         # LabelEncoder\nGLOBAL_SCALER    = obj[\"scaler\"]     # RobustScaler fitted on all pre‐selected features\nGLOBAL_SELECTOR  = obj[\"selector\"]   # SelectFromModel (no longer used at inference)\nGLOBAL_FEAT_LIST = obj[\"feat_list\"]  # The exact feature‐names (in order) that final_model was trained on\n\nprint(\"✅ Loaded model, encoder, scaler, selector, and feature list.\")\n\ndef predict(sequence: pl.DataFrame, demographics: pl.DataFrame) -> str:\n    \"\"\"\n    Called once per test sequence. Returns exactly one string \"gesture\".\n    \"\"\"\n    try:\n        # 1) Convert Polars → pandas\n        seq_pd = sequence.to_pandas()\n        demog_pd = demographics.to_pandas().set_index(\"subject\") if demographics is not None else None\n\n        # 2) Extract enhanced features (must match training)\n        feats = extract_enhanced_sequence_features(seq_pd, demog_pd)\n\n        # 3) Drop any \"behavior\"/\"gesture\"/\"subject\" columns if present\n        drop_cols = [c for c in (\"behavior\", \"gesture\", \"subject\") if c in feats.columns]\n        X_row = feats.drop(columns=drop_cols)\n\n        # 4) Replace infinities / NaNs with 0\n        X_row = X_row.replace([np.inf, -np.inf], np.nan).fillna(0)\n\n        # ────────────────────────────────────────────────────────────────────\n        # 5) PRE‐SCALING: reindex to exactly what the scaler was fit on\n        #    (scaler.feature_names_in_ == the DataFrame.columns that RobustScaler.fit() saw in training)\n        pre_cols = list(GLOBAL_SCALER.feature_names_in_)\n        X_row = X_row.reindex(columns=pre_cols, fill_value=0)\n\n        # 6) SCALE\n        X_scaled = pd.DataFrame(\n            GLOBAL_SCALER.transform(X_row),\n            index=X_row.index,\n            columns=pre_cols\n        )\n\n        # ────────────────────────────────────────────────────────────────────\n        # 7) ADD exactly the same \"interaction\" columns that training used:\n        #    - \"acc_x_std_to_rms\" if both \"acc_x_std\" & \"acc_x_rms\" exist\n        #    - \"acc_corr_abs_sum\" if both \"acc_xy_corr\" & \"acc_yz_corr\" exist\n\n        if {\"acc_x_std\", \"acc_x_rms\"}.issubset(X_scaled.columns):\n            X_scaled[\"acc_x_std_to_rms\"] = X_scaled[\"acc_x_std\"] / (X_scaled[\"acc_x_rms\"] + 1e-6)\n        else:\n            X_scaled[\"acc_x_std_to_rms\"] = 0.0\n\n        if {\"acc_xy_corr\", \"acc_yz_corr\"}.issubset(X_scaled.columns):\n            X_scaled[\"acc_corr_abs_sum\"] = X_scaled[\"acc_xy_corr\"].abs() + X_scaled[\"acc_yz_corr\"].abs()\n        else:\n            X_scaled[\"acc_corr_abs_sum\"] = 0.0\n\n        # ────────────────────────────────────────────────────────────────────\n        # 8) POST‐SCALING: now reindex to exactly the final feature list\n        #    that the LightGBM model was trained on (GLOBAL_FEAT_LIST).\n        #\n        #    Because GLOBAL_FEAT_LIST == selected_features in training, this\n        #    drops any extra columns and orders them correctly.\n        X_final = X_scaled.reindex(columns=GLOBAL_FEAT_LIST, fill_value=0)\n\n        # ────────────────────────────────────────────────────────────────────\n        # 9) Predict with LightGBM directly on X_final\n        proba = GLOBAL_BST.predict(X_final, num_iteration=GLOBAL_BST.best_iteration)\n        idx = int(np.argmax(proba, axis=1)[0])\n\n        # 10) Decode integer back into string label\n        return GLOBAL_LE.inverse_transform([idx])[0]\n\n    except Exception:\n        seq_id = \"unknown\"\n        try:\n            if \"sequence_id\" in sequence.columns:\n                seq_id = sequence[\"sequence_id\"][0]\n        except:\n            pass\n        print(f\"----- ERROR in predict() for sequence_id = {seq_id} -----\")\n        traceback.print_exc()\n        raise\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-03T22:18:49.822288Z","iopub.execute_input":"2025-06-03T22:18:49.822634Z","iopub.status.idle":"2025-06-03T22:18:50.508616Z","shell.execute_reply.started":"2025-06-03T22:18:49.822610Z","shell.execute_reply":"2025-06-03T22:18:50.507855Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ─────────────────────────────────────────────────────────────────────────────\n# CELL 3: RUN THE INFERENCE SERVER\n# ─────────────────────────────────────────────────────────────────────────────\n\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    )\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-03T22:18:54.642199Z","iopub.execute_input":"2025-06-03T22:18:54.642522Z","iopub.status.idle":"2025-06-03T22:18:56.490145Z","shell.execute_reply.started":"2025-06-03T22:18:54.642498Z","shell.execute_reply":"2025-06-03T22:18:56.489107Z"}},"outputs":[],"execution_count":null}]}