{"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":242735499,"sourceType":"kernelVersion"},{"sourceId":242760209,"sourceType":"kernelVersion"},{"sourceId":242762665,"sourceType":"kernelVersion"}],"dockerImageVersionId":31040,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **FOREWORD**\n\nThis is my starter baseline kernel for the [CMI - Detect Behavior with Sensor Data](https://www.kaggle.com/competitions/cmi-detect-behavior-with-sensor-data) competition. This is a multiclass classifier for with a custom metric based on binary + macro-FScore average as illustrated [here](https://www.kaggle.com/code/metric/cmi-2025?_gl=1*8q1wl9*_ga*MTEyMDI5NjU4LjE2NjMwMDc0Nzk.*_ga_T7QHS60L4Q*czE3NDg2MDczNjUkbzEwMzEkZzEkdDE3NDg2MDg5ODAkajEzJGwwJGgw) <br>\n\nI start off with simple features and simple boosted tree models for the assignment with a simple linear blend. I try and elicit basic insights using the data in parquet form and then build the pipeline for inference. <br>\n\nThis is a code competition with an API based submission. We need to save the trained models and FE scripts for inference and deploy them in a separate inference kernel later. I consider this as a good practice to work with code competitions. \n\nWe could consider using a proxy-metric for early stopping while training the baseline model. We could then consider blending and then calculating the actual CV score on the relevant metric after adjusting for cutoffs here.\n\nThis is my submission kernel. I use the feature engineering script from my training kernel, fitted models and predictions here. We also use the imports from the imports kernel and infer and submit.","metadata":{}},{"cell_type":"markdown","source":"# **IMPORTS AND INSTALLATIONS**","metadata":{}},{"cell_type":"code","source":"!pip install -q polars==1.29.0      --no-index --find-links=/kaggle/input/cmi2025-public-imports-v1/packages\n!pip install -q xgboost==3.0.1      --no-index --find-links=/kaggle/input/cmi2025-public-imports-v1/packages\n!pip install -q scikit-learn==1.6.1 --no-index --find-links=/kaggle/input/cmi2025-public-imports-v1/packages\n!pip install -q pytorch_tabnet      --no-index --find-links=/kaggle/input/cmi2025-public-imports-v1/packages\n!pip install -q tabpfn              --no-index --find-links=/kaggle/input/cmi2025-public-imports-v1/packages\n\nexec( open(f\"/kaggle/input/cmi2025-public-imports-v1/myimports.py\", \"r\").read() )\nexec( open(f\"/kaggle/input/cmi2025-public-imports-v1/myutils.py\", \"r\").read() )\nexec( open(f\"/kaggle/input/cmi2025-public-imports-v1/training.py\", \"r\").read() )\n\nexec( open(f\"/kaggle/input/cmi2025-baseline-train-v1/fe.py\", \"r\").read() )\nprint()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T20:30:52.478018Z","iopub.execute_input":"2025-05-30T20:30:52.478591Z","iopub.status.idle":"2025-05-30T20:35:04.766230Z","shell.execute_reply.started":"2025-05-30T20:30:52.478553Z","shell.execute_reply":"2025-05-30T20:35:04.765019Z"},"_kg_hide-input":true,"_kg_hide-output":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **CONFIGURATION**","metadata":{}},{"cell_type":"code","source":"%%time\n\nclass CFG:\n    \"\"\"\n    Configuration class for parameters and CV strategy for tuning and training\n    Some parameters may be unused here as this is a general configuration class\n    \"\"\";\n\n    # Data preparation:-\n    version_nb         = 1\n    model_id           = \"V1_1\"\n    model_label        = \"ML\"\n    test_req           = False\n    test_iter          = 200\n    gpu_switch         = \"ON\" if torch.cuda.is_available() else \"OFF\"\n    state              = 42\n    target             = f'gesture'\n    grouper            = f\"\"\n    tgt_mapper         = {'Neck - pinch skin'        : 0, \n                          'Cheek - pinch skin'       : 1, \n                          'Above ear - pull hair'    : 2,\n                          'Eyelash - pull hair'      : 3, \n                          'Eyebrow - pull hair'      : 4,\n                          'Forehead - pull hairline' : 5, \n                          'Forehead - scratch'       : 6, \n                          'Neck - scratch'           : 7,\n                         }\n    ip_path            = f\"/kaggle/input/cmi2025-data-v1\"\n    op_path            = f\"/kaggle/working\"\n    orig_path          = f\"\"\n    data_path          = f\"\"\n    dtl_preproc_req    = True\n    ftre_plots_req     = True\n    ftre_imp_req       = True\n    nb_orig            = 0\n    orig_all_folds     = False\n\n    # Model Training:-\n    pstprcs_oof        = False\n    pstprcs_train      = False\n    pstprcs_test       = False\n    ML                 = True\n    test_preds_req     = True\n    n_splits           = 5\n    n_repeats          = 1\n    nbrnd_erly_stp     = 0\n    mdlcv_mthd         = 'SKF'\n    metric_obj         = 'maximize'\n\n    # Global variables for plotting:-\n    grid_specs = {'visible'  : True,\n                  'which'    : 'both',\n                  'linestyle': '--',\n                  'color'    : 'lightgrey',\n                  'linewidth': 0.75\n                 }\n\n    title_specs = {'fontsize'   : 9,\n                   'fontweight' : 'bold',\n                   'color'      : '#992600',\n                  }\n\n\ncv_selector = \\\n{\n \"RKF\"   : RepeatedKFold(n_splits   = CFG.n_splits, n_repeats= CFG.n_repeats, random_state= CFG.state),\n \"RSKF\"  : RepeatedStratifiedKFold(n_splits  = CFG.n_splits, n_repeats= CFG.n_repeats, random_state= CFG.state),\n \"SKF\"   : StratifiedKFold(n_splits   = CFG.n_splits, shuffle = True, random_state= CFG.state),\n \"KF\"    : KFold(n_splits = CFG.n_splits, shuffle = True, random_state= CFG.state),\n \"GKF\"   : GroupKFold(n_splits   = CFG.n_splits)\n}\n\ncollect()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T20:35:04.768530Z","iopub.execute_input":"2025-05-30T20:35:04.769325Z","iopub.status.idle":"2025-05-30T20:35:05.049739Z","shell.execute_reply.started":"2025-05-30T20:35:04.769291Z","shell.execute_reply":"2025-05-30T20:35:05.048582Z"},"_kg_hide-input":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **MODEL AND DATA LOADS**","metadata":{}},{"cell_type":"code","source":"%%time\n\nl1_models = joblib.load(f\"/kaggle/input/cmi2025-baseline-train-v1/FittedModels.joblib\")\nl2_models = joblib.load(f\"/kaggle/input/cmi2025-baseline-train-v1/EnsembleModels.joblib\")\nfe        = FeatureMaker(True)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T20:35:05.050622Z","iopub.execute_input":"2025-05-30T20:35:05.051035Z","iopub.status.idle":"2025-05-30T20:35:09.270721Z","shell.execute_reply.started":"2025-05-30T20:35:05.050998Z","shell.execute_reply":"2025-05-30T20:35:09.269683Z"},"_kg_hide-input":true,"_kg_hide-output":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%%time \n\nclass VotingModelMaker:\n    \"\"\"\n    This class collates the test set predictions per model method and averages the predictions through the folds\n    \"\"\"\n    \n    def __init__(self, estimators: list, method: str):\n        \"Defines the estimators and method for the predictions\"\n\n        self.estimators = estimators\n        self.method     = method\n\n    def fit(self, X : pd.DataFrame, y = None, **fit_params):\n        return self\n\n    def predict(self, X: pd.DataFrame, y = None, **params) -> pd.DataFrame:\n        \"Returns probability predictions from the fitted models\"\n\n        df_list = []\n        for model in self.estimators :\n            df = \\\n            pd.DataFrame( \n                model.predict_proba(\n                    X.drop(\"sequence_id\", axis=1, errors = \"ignore\")\n                ) \n            )\n            df.index = range( len(df) )\n            \n        df_list.append(df)\n        df = pd.concat(df_list, axis=0).groupby(level = 0).mean().add_prefix(self.method)\n        return df\n\n    def fit_predict(self, X: pd.DataFrame, y = None, **params ) -> pd.DataFrame:\n        self.fit(X, y)\n        return self.predict(X)\n     ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T20:35:09.271726Z","iopub.execute_input":"2025-05-30T20:35:09.272007Z","iopub.status.idle":"2025-05-30T20:35:09.281065Z","shell.execute_reply.started":"2025-05-30T20:35:09.271984Z","shell.execute_reply":"2025-05-30T20:35:09.279489Z"},"_kg_hide-input":true,"_kg_hide-output":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **INFERENCE AND SUBMISSION**","metadata":{}},{"cell_type":"code","source":"%%time \n\nimport kaggle_evaluation.cmi_inference_server\nverbosity = 0\n\ndef predict(\n    sequence     : pl.DataFrame, \n    demographics : pl.DataFrame,\n) -> str:\n    \"\"\"\n    Prediction function for Kaggle evaluation\n    \"\"\"\n\n    test  = sequence.to_pandas()\n    Xtest = fe.make_ftre(test)\n\n    if verbosity > 0: \n        print(f\"---> Shape = {Xtest.shape}\")\n\n\n    mdl_preds = []  \n    for method, estimators in l1_models.items() :\n        vmm   = VotingModelMaker(estimators, method)\n        preds = vmm.predict(Xtest)\n        mdl_preds.append(preds)\n        del preds\n\n    mdl_preds = pd.concat(mdl_preds, axis = 1)\n    if verbosity > 0: \n        print(f\"---> Shape = {mdl_preds.shape}\")\n\n    mdl_ens_preds = 0\n    vmm = VotingModelMaker(l2_models, \"L21C\")\n    mdl_ens_preds = vmm.predict(mdl_preds).to_numpy()\n    mdl_ens_preds = pd.DataFrame(np.argmax(mdl_ens_preds, axis=1), columns = [CFG.target])  \n    mdl_ens_preds[CFG.target] = mdl_ens_preds[CFG.target].map({v: k for k,v in CFG.tgt_mapper.items() })\n\n    if verbosity > 0: \n        print(f\"---> Prediction = {mdl_ens_preds[CFG.target].values[0]}\")\n\n    return mdl_ens_preds[CFG.target].values[0]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T20:35:09.282852Z","iopub.execute_input":"2025-05-30T20:35:09.283280Z","iopub.status.idle":"2025-05-30T20:35:11.401910Z","shell.execute_reply.started":"2025-05-30T20:35:09.283241Z","shell.execute_reply":"2025-05-30T20:35:11.400523Z"},"_kg_hide-input":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%%time \n\ninference_server = \\\nkaggle_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-05-30T20:35:11.403185Z","iopub.execute_input":"2025-05-30T20:35:11.404190Z","iopub.status.idle":"2025-05-30T20:35:14.358770Z","shell.execute_reply.started":"2025-05-30T20:35:11.404117Z","shell.execute_reply":"2025-05-30T20:35:14.357837Z"},"_kg_hide-input":true},"outputs":[],"execution_count":null}]}