{"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":"gpu","dataSources":[{"sourceId":102335,"databundleVersionId":12518947,"sourceType":"competition"},{"sourceId":12954062,"sourceType":"datasetVersion","datasetId":8051733}],"dockerImageVersionId":31040,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow.keras.models import load_model\nimport numpy as np\nimport pandas as pd\nimport polars as pl\nimport joblib\nimport os\nimport warnings\nwarnings.filterwarnings('ignore')\n\n# Download the form and saved weights\nprint(\"--- Loading Saved Model and Artifacts ---\")\nfinal_lstm_model = load_model('/kaggle/input/cmi-ptmodels/CMI_PTModels_ver9/CMI_PTModels_ver9/trained_model.h5')\nscaler = joblib.load('/kaggle/input/cmi-ptmodels/CMI_PTModels_ver9/CMI_PTModels_ver9/scaler.pkl')\nlabel_encoders_multi_output = joblib.load('/kaggle/input/cmi-ptmodels/CMI_PTModels_ver9/CMI_PTModels_ver9/label_encoders.pkl')\nglobal_mean_imputer_values = joblib.load('/kaggle/input/cmi-ptmodels/CMI_PTModels_ver9/CMI_PTModels_ver9/global_mean_imputer_values.pkl')\nglobal_mode_imputer_values = joblib.load('/kaggle/input/cmi-ptmodels/CMI_PTModels_ver9/CMI_PTModels_ver9/global_mode_imputer_values.pkl')\nimu_features = joblib.load('/kaggle/input/cmi-ptmodels/CMI_PTModels_ver9/CMI_PTModels_ver9/imu_features.pkl')\nthm_features = joblib.load('/kaggle/input/cmi-ptmodels/CMI_PTModels_ver9/CMI_PTModels_ver9/thm_features.pkl')\ntof_features = joblib.load('/kaggle/input/cmi-ptmodels/CMI_PTModels_ver9/CMI_PTModels_ver9/tof_features.pkl')\ncategorical_cols_for_ohe_seq = joblib.load('/kaggle/input/cmi-ptmodels/CMI_PTModels_ver9/CMI_PTModels_ver9/categorical_cols_for_ohe_seq.pkl')\nnumeric_feature_cols_after_ohe = joblib.load('/kaggle/input/cmi-ptmodels/CMI_PTModels_ver9/CMI_PTModels_ver9/numeric_feature_cols_after_ohe.pkl')\n\n# Definition of global variables\nsensor_cols_prefix = ['acc_', 'rot_', 'thm_', 'tof_']\nmax_sequence_length = 210\ntarget_cols_multi_output = ['sequence_type', 'phase', 'gesture', 'orientation', 'behavior']\nwindow_size = 5\n\n# 6. Define prediction function for API - IMPORTANT: Apply Feature Engineering here too!\n# ----------------------------------------------------------------------------------------------------\nprint(\"\\n---6. Defining Prediction Function for API ---\")\n\ndef predict(sequence: pl.DataFrame, demographics: pl.DataFrame) -> str:\n    current_sequence_df = sequence.to_pandas()\n    current_demographics_df = demographics.to_pandas()\n\n    for col in current_sequence_df.select_dtypes(include=['float64']).columns:\n        current_sequence_df[col] = current_sequence_df[col].astype(np.float32)\n    for col in current_sequence_df.select_dtypes(include=['int64']).columns:\n        if 'id' not in col and 'counter' not in col:\n            current_sequence_df[col] = current_sequence_df[col].astype(np.int16)\n        else:\n            current_sequence_df[col] = current_sequence_df[col].astype(np.int32)\n    if 'subject' in current_sequence_df.columns and 'subject' in current_demographics_df.columns:\n        current_test_batch = current_sequence_df.merge(current_demographics_df, on='subject', how='left')\n    else:\n        current_test_batch = current_sequence_df.copy()\n        for col in current_demographics_df.columns:\n            if col != 'subject':\n                current_test_batch[col] = current_demographics_df[col].iloc[0]\n\n    sensor_columns_to_process_in_predict = [col for col in current_test_batch.columns if any(s in col for s in sensor_cols_prefix)]\n    for col in sensor_columns_to_process_in_predict:\n        if col in current_test_batch.columns:\n            current_test_batch[col] = current_test_batch[col].replace(-1.0, np.nan).astype(np.float32)\n    for col in current_test_batch.columns:\n        if current_test_batch[col].isnull().any():\n            if col in global_mode_imputer_values:\n                current_test_batch[col] = current_test_batch[col].fillna(global_mode_imputer_values[col])\n            elif col in global_mean_imputer_values:\n                current_test_batch[col] = current_test_batch[col].fillna(global_mean_imputer_values[col]).astype(np.float32)\n    \n    # --- START FEATURE ENGINEERING FOR PREDICTION (Optimized & Enhanced) ---\n    temp_features_df_predict = current_test_batch.copy()\n    \n    acc_rot_base_cols_predict = ['acc_x', 'acc_y', 'acc_z', 'rot_w', 'rot_x', 'rot_y', 'rot_z']\n    acc_rot_base_cols_predict = [col for col in acc_rot_base_cols_predict if col in temp_features_df_predict.columns]\n\n    # Calculate first-order features\n    temp_features_df_predict['acc_magnitude'] = np.sqrt(\n        temp_features_df_predict['acc_x']**2 +\n        temp_features_df_predict['acc_y']**2 +\n        temp_features_df_predict['acc_z']**2).astype(np.float32)\n    temp_features_df_predict['rot_magnitude'] = np.sqrt(\n        temp_features_df_predict['rot_w']**2 +\n        temp_features_df_predict['rot_x']**2 +\n        temp_features_df_predict['rot_y']**2 +\n        temp_features_df_predict['rot_z']**2).astype(np.float32)\n    temp_features_df_predict['thm_1_diff'] = temp_features_df_predict.groupby('sequence_id')['thm_1'].diff().fillna(0).astype(np.float32)\n\n    for col in acc_rot_base_cols_predict:\n        temp_features_df_predict[f'{col}_diff_1'] = temp_features_df_predict.groupby('sequence_id')[col].diff(1).fillna(0).astype(np.float32)\n        temp_features_df_predict[f'{col}_diff_2'] = temp_features_df_predict.groupby('sequence_id')[col].diff(2).fillna(0).astype(np.float32)\n        \n    for col in acc_rot_base_cols_predict:\n        if col in temp_features_df_predict.columns:\n            grouped_predict = temp_features_df_predict.groupby('sequence_id')\n            temp_features_df_predict[f'{col}_roll_mean'] = grouped_predict[col].transform(lambda x: x.rolling(window=window_size, min_periods=1).mean()).astype(np.float32)\n            temp_features_df_predict[f'{col}_roll_std'] = grouped_predict[col].transform(lambda x: x.rolling(window=window_size, min_periods=1).std()).fillna(0).astype(np.float32)\n            temp_features_df_predict[f'{col}_roll_min'] = grouped_predict[col].transform(lambda x: x.rolling(window=window_size, min_periods=1).min()).astype(np.float32)\n            temp_features_df_predict[f'{col}_roll_max'] = grouped_predict[col].transform(lambda x: x.rolling(window=window_size, min_periods=1).max()).astype(np.float32)\n            temp_features_df_predict[f'{col}_roll_median'] = grouped_predict[col].transform(lambda x: x.rolling(window=window_size, min_periods=1).median()).astype(np.float32)\n            temp_features_df_predict[f'{col}_roll_skew'] = grouped_predict[col].transform(lambda x: x.rolling(window=window_size, min_periods=1).skew()).fillna(0).astype(np.float32)\n            temp_features_df_predict[f'{col}_roll_kurt'] = grouped_predict[col].transform(lambda x: x.rolling(window=window_size, min_periods=1).kurt()).fillna(0).astype(np.float32)\n            temp_features_df_predict[f'{col}_zero_cross_rate'] = grouped_predict[col].transform(lambda x: x.rolling(window=window_size, min_periods=1).apply(lambda y: np.sum(np.diff(np.sign(y)) != 0), raw=True)).fillna(0).astype(np.float32)\n            temp_features_df_predict[f'{col}_diff1_roll_mean'] = grouped_predict[f'{col}_diff_1'].transform(lambda x: x.rolling(window=window_size, min_periods=1).mean()).astype(np.float32)\n    \n    # Feature Interactions\n    if 'acc_x' in temp_features_df_predict.columns and 'acc_y' in temp_features_df_predict.columns:\n        temp_features_df_predict['acc_x_acc_y_prod'] = (temp_features_df_predict['acc_x'] * temp_features_df_predict['acc_y']).astype(np.float32)\n        temp_features_df_predict['acc_x_y_diff'] = (temp_features_df_predict['acc_x'] - temp_features_df_predict['acc_y']).astype(np.float32)\n    if 'acc_x' in temp_features_df_predict.columns and 'acc_z' in temp_features_df_predict.columns:\n        temp_features_df_predict['acc_x_acc_z_prod'] = (temp_features_df_predict['acc_x'] * temp_features_df_predict['acc_z']).astype(np.float32)\n        temp_features_df_predict['acc_x_z_diff'] = (temp_features_df_predict['acc_x'] - temp_features_df_predict['acc_z']).astype(np.float32)\n    if 'acc_y' in temp_features_df_predict.columns and 'acc_z' in temp_features_df_predict.columns:\n        temp_features_df_predict['acc_y_acc_z_prod'] = (temp_features_df_predict['acc_y'] * temp_features_df_predict['acc_z']).astype(np.float32)\n        temp_features_df_predict['acc_y_z_diff'] = (temp_features_df_predict['acc_y'] - temp_features_df_predict['acc_z']).astype(np.float32)\n    if 'rot_x' in temp_features_df_predict.columns and 'rot_y' in temp_features_df_predict.columns:\n        temp_features_df_predict['rot_x_rot_y_prod'] = (temp_features_df_predict['rot_x'] * temp_features_df_predict['rot_y']).astype(np.float32)\n        temp_features_df_predict['rot_x_y_diff'] = (temp_features_df_predict['rot_x'] - temp_features_df_predict['rot_y']).astype(np.float32)\n    if 'rot_x' in temp_features_df_predict.columns and 'rot_z' in temp_features_df_predict.columns:\n        temp_features_df_predict['rot_x_rot_z_prod'] = (temp_features_df_predict['rot_x'] * temp_features_df_predict['rot_z']).astype(np.float32)\n        temp_features_df_predict['rot_x_z_diff'] = (temp_features_df_predict['rot_x'] - temp_features_df_predict['rot_z']).astype(np.float32)\n    if 'rot_y' in temp_features_df_predict.columns and 'rot_z' in temp_features_df_predict.columns:\n        temp_features_df_predict['rot_y_rot_z_prod'] = (temp_features_df_predict['rot_y'] * temp_features_df_predict['rot_z']).astype(np.float32)\n        temp_features_df_predict['rot_y_z_diff'] = (temp_features_df_predict['rot_y'] - temp_features_df_predict['rot_z']).astype(np.float32)\n        \n    # ToF Global Statistics\n    for i in range(1, 6):\n        tof_cols_i = [f'tof_{i}_v{j}' for j in range(64)]\n        existing_tof_cols_i = [col for col in tof_cols_i if col in temp_features_df_predict.columns]\n        if existing_tof_cols_i:\n            temp_features_df_predict[f'tof_{i}_mean'] = temp_features_df_predict[existing_tof_cols_i].mean(axis=1).astype(np.float32)\n            temp_features_df_predict[f'tof_{i}_std'] = temp_features_df_predict[existing_tof_cols_i].std(axis=1).fillna(0).astype(np.float32)\n\n    # FFT Features\n    fft_cols_to_process_predict = ['acc_x', 'acc_y', 'acc_z', 'rot_x', 'rot_y', 'rot_z', 'rot_w']\n    fft_cols_to_process_predict = [col for col in fft_cols_to_process_predict if col in current_test_batch.columns]\n    fft_features_predict_list = []\n    grouped_df_fft_predict = current_test_batch.groupby('sequence_id')\n    for seq_id, group in grouped_df_fft_predict:\n        fft_row = {'sequence_id': seq_id}\n        for col in fft_cols_to_process_predict:\n            signal = group[col].values\n            if len(signal) < max_sequence_length:\n                signal = np.pad(signal, (0, max_sequence_length - len(signal)), 'constant')\n            elif len(signal) > max_sequence_length:\n                signal = signal[:max_sequence_length]\n            yf = np.fft.fft(signal)\n            magnitude = np.abs(yf[1:max_sequence_length//2])\n            if len(magnitude) > 0:\n                fft_row[f'{col}_fft_mean_mag'] = np.mean(magnitude).astype(np.float32)\n                fft_row[f'{col}_fft_std_mag'] = np.std(magnitude).astype(np.float32)\n                fft_row[f'{col}_fft_max_mag'] = np.max(magnitude).astype(np.float32)\n                fft_row[f'{col}_fft_peak_freq_idx'] = np.argmax(magnitude).astype(np.float32)\n                fft_row[f'{col}_fft_total_energy'] = np.sum(magnitude**2).astype(np.float32)\n                fft_row[f'{col}_fft_low_freq_energy'] = np.sum(magnitude[:len(magnitude)//4]**2).astype(np.float32)\n                fft_row[f'{col}_fft_high_freq_energy'] = np.sum(magnitude[len(magnitude)//2:]**2).astype(np.float32)\n            else:\n                fft_row.update({f'{col}_fft_mean_mag': 0.0, f'{col}_fft_std_mag': 0.0, f'{col}_fft_max_mag': 0.0,\n                                f'{col}_fft_peak_freq_idx': 0.0, f'{col}_fft_total_energy': 0.0,\n                                f'{col}_fft_low_freq_energy': 0.0, f'{col}_fft_high_freq_energy': 0.0})\n        fft_features_predict_list.append(fft_row)\n    fft_df_predict = pd.DataFrame(fft_features_predict_list)\n    temp_features_df_predict = pd.merge(temp_features_df_predict, fft_df_predict, on='sequence_id', how='left')\n    \n    # --- END FEATURE ENGINEERING FOR PREDICTION (Optimized & Enhanced) ---\n    \n    for col in categorical_cols_for_ohe_seq:\n        temp_dummies = pd.get_dummies(temp_features_df_predict[col], prefix=col)\n        expected_dummy_cols = [c for c in numeric_feature_cols_after_ohe if c.startswith(f\"{col}_\")]\n        for expected_col in expected_dummy_cols:\n            if expected_col not in temp_dummies.columns:\n                temp_dummies[expected_col] = 0.0\n        temp_dummies = temp_dummies[expected_dummy_cols]\n        temp_features_df_predict = pd.concat([temp_features_df_predict.drop(columns=[col]), temp_dummies], axis=1)\n\n    # Ensure all columns exist before scaling and re-ordering\n    for col in numeric_feature_cols_after_ohe:\n        if col not in temp_features_df_predict.columns:\n            temp_features_df_predict[col] = 0.0\n            \n    # Scaling\n    temp_features_df_predict[numeric_feature_cols_after_ohe] = scaler.transform(temp_features_df_predict[numeric_feature_cols_after_ohe]).astype(np.float32)\n    \n    def prepare_input_matrix(df, feature_list, max_seq_len):\n        current_matrix = df[feature_list].values.astype(np.float32)\n        if current_matrix.shape[0] < max_seq_len:\n            padding_needed = max_seq_len - current_matrix.shape[0]\n            padding_matrix = np.zeros((padding_needed, current_matrix.shape[1]), dtype=np.float32)\n            current_matrix = np.vstack((current_matrix, padding_matrix))\n        elif current_matrix.shape[0] > max_seq_len:\n            current_matrix = current_matrix[:max_seq_len, :]\n        return np.expand_dims(current_matrix, axis=0)\n\n    imu_input_tensor = prepare_input_matrix(temp_features_df_predict, imu_features, max_sequence_length)\n    thm_input_tensor = prepare_input_matrix(temp_features_df_predict, thm_features, max_sequence_length)\n    tof_input_tensor = prepare_input_matrix(temp_features_df_predict, tof_features, max_sequence_length)\n\n    predictions = final_lstm_model.predict([imu_input_tensor, thm_input_tensor, tof_input_tensor])\n    gesture_preds = predictions[target_cols_multi_output.index('gesture')]\n    predicted_gesture_index = np.argmax(gesture_preds, axis=1)[0]\n    predicted_gesture_label = label_encoders_multi_output['gesture'].inverse_transform([predicted_gesture_index])[0]\n    return predicted_gesture_label\n\nprint(\"Prediction function for API defined.\")\n\n# 7. Setup and Run Server\n# ----------------------------------------------------------------------------------------------------\nprint(\"\\n---7. Setting up and Running Server ---\")\nimport kaggle_evaluation.cmi_inference_server\ninference_server = kaggle_evaluation.cmi_inference_server.CMIInferenceServer(predict)\nif os.getenv('KAGGLE_IS_COMPETITION_RERUN'):\n    print(\"Running server in official evaluation mode...\")\n    inference_server.serve()\nelse:\n    print(\"Running server locally for testing.\")\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,"execution":{"iopub.status.busy":"2025-09-03T20:34:29.319731Z","iopub.execute_input":"2025-09-03T20:34:29.320226Z","iopub.status.idle":"2025-09-03T20:34:39.512704Z","shell.execute_reply.started":"2025-09-03T20:34:29.320202Z","shell.execute_reply":"2025-09-03T20:34:39.511862Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}