{"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,"sourceType":"competition"}],"dockerImageVersionId":31040,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# CMI - Detect Behavior with Sensors\n---\n### This notebook will walk you through completing your first submission in this contest using the competition server, employing a single LightGBM model as an example.  This notebook will also demonstrate global variable use to ensure training time is incorporated into the first predict call.\n\n### If you found this notebook helpful, please vote and / or leave a comment for improvement.  \n### For Complete EDA for this competition, see this [notebook](https://www.kaggle.com/code/chrisk321/cmi-detect-behavior-complete-eda).","metadata":{}},{"cell_type":"code","source":"import os\nimport lightgbm as lgb\nimport pandas as pd\nimport polars as pl\nimport numpy as np\nfrom sklearn.preprocessing import LabelEncoder\nimport kaggle_evaluation.cmi_inference_server\nfrom sklearn.model_selection import cross_val_score","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-06-02T13:06:02.491462Z","iopub.execute_input":"2025-06-02T13:06:02.491808Z","iopub.status.idle":"2025-06-02T13:06:10.729080Z","shell.execute_reply.started":"2025-06-02T13:06:02.491769Z","shell.execute_reply":"2025-06-02T13:06:10.728202Z"},"_kg_hide-input":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# train_model is a helper function we will only call once during the first 'predict' call","metadata":{}},{"cell_type":"code","source":"def train_model(train_combined):\n\n    X = train_combined.drop(['gesture', 'row_id', 'sequence_id', 'subject'],axis=1).copy()\n    y = train_combined['gesture']\n    \n    model = lgb.LGBMClassifier(boosting='gbdt', device='cpu', random_state=1,verbose=-1)\n    score = cross_val_score(model,X,y,scoring='accuracy')\n    print(np.mean(score))\n    model.fit(X,y)\n\n    print('--Training Complete.')\n\n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-02T13:06:10.730430Z","iopub.execute_input":"2025-06-02T13:06:10.731029Z","iopub.status.idle":"2025-06-02T13:06:10.736435Z","shell.execute_reply.started":"2025-06-02T13:06:10.731001Z","shell.execute_reply":"2025-06-02T13:06:10.735690Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def feat_eng(df):\n\n    skewed_cols = ['sequence_counter', 'rot_w', 'thm_2', 'thm_5', 'tof_1_v0', 'tof_1_v1', 'tof_1_v2', 'tof_1_v3', \n                   'tof_1_v4', 'tof_1_v5', 'tof_1_v6', 'tof_1_v7', 'tof_1_v8', 'tof_1_v9', 'tof_1_v10', 'tof_1_v11', \n                   'tof_1_v12', 'tof_1_v13', 'tof_1_v14', 'tof_1_v15', 'tof_1_v16', 'tof_1_v17', 'tof_1_v18', 'tof_1_v19', \n                   'tof_1_v20', 'tof_1_v21', 'tof_1_v22', 'tof_1_v23', 'tof_1_v24', 'tof_1_v25', 'tof_1_v26', 'tof_1_v27', \n                   'tof_1_v28', 'tof_1_v29', 'tof_1_v30', 'tof_1_v31', 'tof_1_v32', 'tof_1_v33', 'tof_1_v34', 'tof_1_v35', \n                   'tof_1_v36', 'tof_1_v37', 'tof_1_v38', 'tof_1_v39', 'tof_1_v40', 'tof_1_v41', 'tof_1_v42', 'tof_1_v43', \n                   'tof_1_v44', 'tof_1_v45', 'tof_1_v46', 'tof_1_v47', 'tof_1_v48', 'tof_1_v49', 'tof_1_v50', 'tof_1_v51', \n                   'tof_1_v52', 'tof_1_v53', 'tof_1_v54', 'tof_1_v55', 'tof_1_v56', 'tof_1_v57', 'tof_1_v58', 'tof_1_v59', \n                   'tof_1_v60', 'tof_1_v61', 'tof_1_v62', 'tof_1_v63', 'tof_2_v0', 'tof_2_v1', 'tof_2_v2', 'tof_2_v3', \n                   'tof_2_v4', 'tof_2_v5', 'tof_2_v6', 'tof_2_v7', 'tof_2_v8', 'tof_2_v9', 'tof_2_v10', 'tof_2_v11', \n                   'tof_2_v12', 'tof_2_v13', 'tof_2_v14', 'tof_2_v15', 'tof_2_v16', 'tof_2_v17', 'tof_2_v18', 'tof_2_v19', \n                   'tof_2_v20', 'tof_2_v21', 'tof_2_v22', 'tof_2_v23', 'tof_2_v24', 'tof_2_v25', 'tof_2_v26', 'tof_2_v27', \n                   'tof_2_v28', 'tof_2_v29', 'tof_2_v30', 'tof_2_v31', 'tof_2_v32', 'tof_2_v33', 'tof_2_v34', 'tof_2_v35', \n                   'tof_2_v36', 'tof_2_v37', 'tof_2_v38', 'tof_2_v39', 'tof_2_v40', 'tof_2_v41', 'tof_2_v42', 'tof_2_v43', \n                   'tof_2_v44', 'tof_2_v45', 'tof_2_v46', 'tof_2_v47', 'tof_2_v48', 'tof_2_v49', 'tof_2_v50', 'tof_2_v51', \n                   'tof_2_v52', 'tof_2_v53', 'tof_2_v54', 'tof_2_v55', 'tof_2_v56', 'tof_2_v57', 'tof_2_v58', 'tof_2_v59', \n                   'tof_2_v60', 'tof_2_v61', 'tof_2_v62', 'tof_2_v63', 'tof_3_v0', 'tof_3_v1', 'tof_3_v2', 'tof_3_v3', \n                   'tof_3_v4', 'tof_3_v5', 'tof_3_v6', 'tof_3_v7', 'tof_3_v8', 'tof_3_v9', 'tof_3_v10', 'tof_3_v11', \n                   'tof_3_v12', 'tof_3_v13', 'tof_3_v14', 'tof_3_v15', 'tof_3_v16', 'tof_3_v17', 'tof_3_v18', 'tof_3_v19',\n                   'tof_3_v20', 'tof_3_v21', 'tof_3_v22', 'tof_3_v23', 'tof_3_v24', 'tof_3_v25', 'tof_3_v26', 'tof_3_v27', \n                   'tof_3_v28', 'tof_3_v29', 'tof_3_v30', 'tof_3_v31', 'tof_3_v32', 'tof_3_v33', 'tof_3_v34', 'tof_3_v35', \n                   'tof_3_v36', 'tof_3_v37', 'tof_3_v38', 'tof_3_v39', 'tof_3_v40', 'tof_3_v41', 'tof_3_v42', 'tof_3_v43', \n                   'tof_3_v44', 'tof_3_v45', 'tof_3_v46', 'tof_3_v47', 'tof_3_v48', 'tof_3_v49', 'tof_3_v50', 'tof_3_v51', \n                   'tof_3_v52', 'tof_3_v53', 'tof_3_v54', 'tof_3_v55', 'tof_3_v56', 'tof_3_v57', 'tof_3_v58', 'tof_3_v59', \n                   'tof_3_v60', 'tof_3_v61', 'tof_3_v62', 'tof_3_v63', 'tof_4_v0', 'tof_4_v1', 'tof_4_v2', 'tof_4_v3', \n                   'tof_4_v4', 'tof_4_v5', 'tof_4_v6', 'tof_4_v7', 'tof_4_v8', 'tof_4_v9', 'tof_4_v10', 'tof_4_v11', \n                   'tof_4_v12', 'tof_4_v13', 'tof_4_v14', 'tof_4_v15', 'tof_4_v16', 'tof_4_v17', 'tof_4_v18', 'tof_4_v19', \n                   'tof_4_v20', 'tof_4_v21', 'tof_4_v22', 'tof_4_v23', 'tof_4_v24', 'tof_4_v25', 'tof_4_v26', 'tof_4_v27', \n                   'tof_4_v28', 'tof_4_v29', 'tof_4_v30', 'tof_4_v31', 'tof_4_v32', 'tof_4_v33', 'tof_4_v34', 'tof_4_v35', \n                   'tof_4_v36', 'tof_4_v37', 'tof_4_v38', 'tof_4_v39', 'tof_4_v40', 'tof_4_v41', 'tof_4_v42', 'tof_4_v43', \n                   'tof_4_v44', 'tof_4_v45', 'tof_4_v46', 'tof_4_v47', 'tof_4_v48', 'tof_4_v49', 'tof_4_v50', 'tof_4_v51', \n                   'tof_4_v52', 'tof_4_v53', 'tof_4_v54', 'tof_4_v55', 'tof_4_v56', 'tof_4_v57', 'tof_4_v58', 'tof_4_v59', \n                   'tof_4_v60', 'tof_4_v61', 'tof_4_v62', 'tof_4_v63', 'tof_5_v0', 'tof_5_v1', 'tof_5_v2', 'tof_5_v3', \n                   'tof_5_v4', 'tof_5_v5', 'tof_5_v6', 'tof_5_v7', 'tof_5_v8', 'tof_5_v9', 'tof_5_v10', 'tof_5_v11', \n                   'tof_5_v12', 'tof_5_v13', 'tof_5_v14', 'tof_5_v15', 'tof_5_v16', 'tof_5_v17', 'tof_5_v18', 'tof_5_v19', \n                   'tof_5_v20', 'tof_5_v21', 'tof_5_v22', 'tof_5_v23', 'tof_5_v24', 'tof_5_v25', 'tof_5_v26', 'tof_5_v27', \n                   'tof_5_v28', 'tof_5_v29', 'tof_5_v30', 'tof_5_v31', 'tof_5_v32', 'tof_5_v33', 'tof_5_v34', 'tof_5_v35', \n                   'tof_5_v36', 'tof_5_v37', 'tof_5_v38', 'tof_5_v39', 'tof_5_v40', 'tof_5_v41', 'tof_5_v42', 'tof_5_v43', \n                   'tof_5_v44', 'tof_5_v45', 'tof_5_v46', 'tof_5_v47', 'tof_5_v48', 'tof_5_v49', 'tof_5_v50', 'tof_5_v51', \n                   'tof_5_v52', 'tof_5_v53', 'tof_5_v54', 'tof_5_v55', 'tof_5_v56', 'tof_5_v57', 'tof_5_v58', 'tof_5_v59', \n                   'tof_5_v60', 'tof_5_v61', 'tof_5_v62', 'tof_5_v63', 'age', 'elbow_to_wrist_cm']\n\n    for col in skewed_cols:\n        df[col] = np.log1p(df[col])\n        \n    return df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-02T13:06:10.737301Z","iopub.execute_input":"2025-06-02T13:06:10.738092Z","iopub.status.idle":"2025-06-02T13:06:10.772038Z","shell.execute_reply.started":"2025-06-02T13:06:10.738058Z","shell.execute_reply":"2025-06-02T13:06:10.771071Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"first_predict = True\n\ndef predict(sequence: pl.DataFrame, demographics: pl.DataFrame) -> str:\n    # By using a global variables, we can locate our training inside the first predict call.\n    global first_predict, model\n    \n    try:\n        # This section will read & prepare the test files\n        # Convert to pandas\n        test = sequence.to_pandas()\n        test_demographics = demographics.to_pandas()\n    \n        # Combine test & test_demographics\n        test_combined = pd.merge(test, test_demographics, how='left', on='subject')\n        print(f'Test shape is {test_combined.shape}')\n\n        # Future feature engineering goes here\n        test_combined = feat_eng(test_combined)\n        print(f'Test shape is {test_combined.shape}')\n        \n    except:\n        print('Problem reading / processing test files.')\n\n    if first_predict:\n        # We execute this code block only once.\n        le = LabelEncoder()\n        \n        # Read competition files\n        train = pd.read_csv('//kaggle/input/cmi-detect-behavior-with-sensor-data/train.csv')\n        train_demographics = pd.read_csv('/kaggle/input/cmi-detect-behavior-with-sensor-data/train_demographics.csv')\n        \n        # Merge\n        train_combined = pd.merge(train.drop(['sequence_type', 'orientation', 'behavior', 'phase'],axis=1), train_demographics, how='left', on='subject')\n        print(f'Train shape is {train_combined.shape}')\n        \n        # Future feature engineering goes here\n        train_combined = feat_eng(train_combined)\n        print(f'Train shape is {train_combined.shape}')\n        \n        # Model training\n        model = train_model(train_combined)\n        \n        first_predict = False\n\n    # predict\n    X_test = test_combined.drop(['row_id', 'sequence_id', 'subject'],axis=1).copy()\n    y_pred = model.predict(X_test)\n\n    return y_pred[0]\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-02T13:06:10.773293Z","iopub.execute_input":"2025-06-02T13:06:10.773553Z","iopub.status.idle":"2025-06-02T13:06:10.797477Z","shell.execute_reply.started":"2025-06-02T13:06:10.773527Z","shell.execute_reply":"2025-06-02T13:06:10.796556Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"inference_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,"execution":{"iopub.status.busy":"2025-06-02T13:06:10.799300Z","iopub.execute_input":"2025-06-02T13:06:10.799564Z","iopub.status.idle":"2025-06-02T14:28:52.683637Z","shell.execute_reply.started":"2025-06-02T13:06:10.799543Z","shell.execute_reply":"2025-06-02T14:28:52.681519Z"}},"outputs":[],"execution_count":null}]}