{"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":12180958,"sourceType":"datasetVersion","datasetId":7671825}],"dockerImageVersionId":31040,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"### BFRB Detection Submission (IMU + Thermopile CNN-GRU Model)\n\nThis notebook implements the competition's required inference API using our trained CNN-GRU model.","metadata":{}},{"cell_type":"code","source":"import os\nimport torch\nimport torch.nn as nn\nimport numpy as np\nimport pandas as pd\nimport polars as pl\nimport joblib\nimport kaggle_evaluation.cmi_inference_server\n\n# Only keep IMU + thermopile features\nfeature_cols = [  # Same features as training: IMU + thermopile\n    'acc_x', 'acc_y', 'acc_z', 'rot_w', 'rot_x', 'rot_y', 'rot_z',\n    'thm_1', 'thm_2', 'thm_3', 'thm_4', 'thm_5'\n]\n\n# Set the same max_len as in training (hardcoded from training stats)\nmax_len = 700  \n\n# Load model components\nclass CNN_GRU_Model(nn.Module):\n    def __init__(self, input_dim, num_classes):\n        super().__init__()\n        self.cnn = nn.Sequential(\n            nn.Conv1d(input_dim, 64, kernel_size=3, padding=1),\n            nn.BatchNorm1d(64),\n            nn.ReLU(),\n            nn.Conv1d(64, 64, kernel_size=3, padding=1),\n            nn.BatchNorm1d(64),\n            nn.ReLU(),\n        )\n        self.gru = nn.GRU(64, 128, batch_first=True, bidirectional=True)\n        self.dropout = nn.Dropout(0.3)\n        self.fc = nn.Linear(128*2, num_classes)\n\n    def forward(self, x):\n        x = x.permute(0, 2, 1)  # (B, F, T)\n        x = self.cnn(x)         # (B, C, T)\n        x = x.permute(0, 2, 1)  # (B, T, C)\n        _, h = self.gru(x)\n        h = torch.cat((h[0], h[1]), dim=1)\n        h = self.dropout(h)\n        return self.fc(h)\n\n# Initialize model\nDEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'\nle = joblib.load('/kaggle/input/cnn-gru-model/label_encoder.pkl')\nmodel = CNN_GRU_Model(input_dim = len(feature_cols), num_classes = len(le.classes_))\nmodel.load_state_dict(torch.load('/kaggle/input/cnn-gru-model/best_model.pt', map_location = DEVICE))\nmodel.to(DEVICE)\nmodel.eval()\n\n\ndef preprocess_sequence(df: pd.DataFrame) -> np.ndarray:\n    # Ensure all feature columns are present\n    for col in feature_cols:\n        if col not in df.columns:\n            df[col] = 0.0\n\n    values = df[feature_cols].fillna(0).values.astype(np.float32)\n    padded = np.zeros((max_len, len(feature_cols)), dtype=np.float32)\n    length = min(len(values), max_len)\n    padded[:length] = values[:length]\n    return padded\n\ndef predict(sequence: pl.DataFrame, demographics: pl.DataFrame) -> str:\n    df = sequence.to_pandas()\n    x = preprocess_sequence(df)\n    x_tensor = torch.tensor(x).unsqueeze(0).to(DEVICE)  # (1, T, F)\n    with torch.no_grad():\n        logits = model(x_tensor)\n        pred = logits.argmax(dim=1).item()\n    return le.inverse_transform([pred])[0]    \n    #return le.classes_[pred]\n\ninference_server = kaggle_evaluation.cmi_inference_server.CMIInferenceServer(predict)\n\nif os.getenv('KAGGLE_IS_COMPETITION_RERUN'):\n    inference_server.serve()\nelse:\n    try:\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    except Exception as e:\n         print(f\"An unexpected error occurred: {e}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-19T07:39:32.829516Z","iopub.execute_input":"2025-06-19T07:39:32.829766Z","iopub.status.idle":"2025-06-19T07:39:40.675123Z","shell.execute_reply.started":"2025-06-19T07:39:32.829747Z","shell.execute_reply":"2025-06-19T07:39:40.674517Z"}},"outputs":[],"execution_count":null}]}