{"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"},{"sourceId":242895646,"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 a baseline kernel using the contents in this [discussion](https://www.kaggle.com/competitions/cmi-detect-behavior-with-sensor-data/discussion/582344) here. I try and do the below here-\n- Use the last row across sequences\n- Train a single boosted tree model\n- Evaluate the binary and macro-f1 score\n- Submit and check the score\n\nLet's get started and check out the method! All the best!\n\n**I have simplified the Trainer class to suit the competition. This is better useful than the previous version!**\n\n#### **OTHER REFERENCES**\n\n- https://www.kaggle.com/code/ravi20076/cmi2025-baseline-submission-v1\n- https://www.kaggle.com/code/ravi20076/cmi2025-data-v1\n- https://www.kaggle.com/code/ravi20076/cmi2025-public-imports-v2\n- https://www.kaggle.com/code/ravi20076/cmi2025-public-imports-v1\n- https://www.kaggle.com/code/ravi20076/cmi2025-baseline-train-v1\n\nIn this version, we try to use a combination of binary and multiclass classifier and check the impact! ","metadata":{}},{"cell_type":"markdown","source":"# **IMPORTS**\n\n","metadata":{}},{"cell_type":"code","source":"%%time \n\n!pip install -q polars==1.29.0      --no-index --find-links=/kaggle/input/cmi2025-public-imports-v2/packages\n!pip install -q xgboost==3.0.1      --no-index --find-links=/kaggle/input/cmi2025-public-imports-v2/packages\n!pip install -q scikit-learn==1.6.1 --no-index --find-links=/kaggle/input/cmi2025-public-imports-v2/packages\n!pip install -q pytorch_tabnet      --no-index --find-links=/kaggle/input/cmi2025-public-imports-v2/packages\n!pip install -q tabpfn              --no-index --find-links=/kaggle/input/cmi2025-public-imports-v2/packages\n\nexec( open(f\"/kaggle/input/cmi2025-public-imports-v2/myimports.py\", \"r\").read() )\nexec( open(f\"/kaggle/input/cmi2025-public-imports-v2/myutils.py\", \"r\").read() )\nexec( open(f\"/kaggle/input/cmi2025-public-imports-v2/training.py\", \"r\").read() )\n\nprint()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-31T17:26:33.562467Z","iopub.execute_input":"2025-05-31T17:26:33.562871Z","iopub.status.idle":"2025-05-31T17:31:07.039167Z","shell.execute_reply.started":"2025-05-31T17:26:33.562840Z","shell.execute_reply":"2025-05-31T17:31:07.037575Z"},"_kg_hide-input":true,"_kg_hide-output":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **GLOBAL VARIABLES**\n\n","metadata":{}},{"cell_type":"code","source":"%%time \n\ntarget       = \"gesture\"\nversion_lbl  = \"V1_3\"\nmodel_lbl    = \"ML\"\nstate        = 42\nn_splits     = 5\nverbosity    = 1\n\nmapper = {\n    \"Text on phone\"            : 0,\n    \"Forehead - pull hairline\" : 1,    \n    \"Neck - scratch\"           : 2,        \n    \"Neck - pinch skin\"        : 3,\n    \"Eyelash - pull hair\"      : 4,        \n    \"Forehead - scratch\"       : 5,          \n    \"Eyebrow - pull hair\"      : 6,\n    \"Above ear - pull hair\"    : 7,   \n    \"Cheek - pinch skin\"       : 8,\n}","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-31T17:31:07.041307Z","iopub.execute_input":"2025-05-31T17:31:07.044275Z","iopub.status.idle":"2025-05-31T17:31:07.053457Z","shell.execute_reply.started":"2025-05-31T17:31:07.044221Z","shell.execute_reply":"2025-05-31T17:31:07.051932Z"},"_kg_hide-output":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **PREPROCESSING**","metadata":{}},{"cell_type":"code","source":"%%time \n\ntrain  = pd.read_csv(\n    f\"/kaggle/input/cmi-detect-behavior-with-sensor-data/train.csv\", \n    index_col = [\"row_id\"]\n)\n\ntest   = pd.read_csv(\n    f\"/kaggle/input/cmi-detect-behavior-with-sensor-data/test.csv\", \n    index_col = [\"row_id\"]\n)\n\ntraind = pd.read_csv(\n    f\"/kaggle/input/cmi-detect-behavior-with-sensor-data/train_demographics.csv\"\n)\ntestd  = pd.read_csv(\n    f\"/kaggle/input/cmi-detect-behavior-with-sensor-data/test_demographics.csv\"\n)\n\ntrain  = reduce_mem_usage(train,  \"train\")\ntest   = reduce_mem_usage(test,   \"test\")\ntraind = reduce_mem_usage(traind, \"traind\")\ntestd  = reduce_mem_usage(testd,  \"testd\")\n\nPrintColor(\n    f\"\\n---> Shapes = {train.shape} {test.shape} {traind.shape} {testd.shape}\"\n)\n\n_ = utils.CleanMemory()\nprint()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-31T17:31:07.054891Z","iopub.execute_input":"2025-05-31T17:31:07.055304Z","iopub.status.idle":"2025-05-31T17:31:54.973995Z","shell.execute_reply.started":"2025-05-31T17:31:07.055269Z","shell.execute_reply":"2025-05-31T17:31:54.972895Z"},"_kg_hide-input":true,"_kg_hide-output":false},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **FEATURE CREATION**\n\nWe create the last value per sequence across train and test sets as aggregators. <br>\nWe also join with the demographics table and complete the process","metadata":{}},{"cell_type":"code","source":"def make_ftre(\n    df: pd.DataFrame, demo_df : pd.DataFrame, sel_cols: list\n) :\n    \"Creates aggregate features and joins with the demographics table\"\n    \n    df_out = \\\n    (\n        df.\n        groupby([\"sequence_id\"])[sel_cols + [\"subject\", \"sequence_id\"]].\n        tail(1).\n        set_index([\"subject\", \"sequence_id\"]).\n        astype(np.float32).\n        add_prefix(\"last_\").\n        reset_index().\n        merge(\n            demo_df, on = [\"subject\"], how = \"left\"\n        ).\n        drop(\"subject\", axis=1)\n    )\n\n    return df_out","metadata":{"trusted":true,"_kg_hide-input":true,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2025-05-31T17:31:54.976319Z","iopub.execute_input":"2025-05-31T17:31:54.976668Z","iopub.status.idle":"2025-05-31T17:31:54.983320Z","shell.execute_reply.started":"2025-05-31T17:31:54.976634Z","shell.execute_reply":"2025-05-31T17:31:54.982201Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%%time \n\nsel_cols = list(test.columns[3:])\nXtrain   = make_ftre(train, traind, sel_cols)\nXtest    = make_ftre(test,  testd,  sel_cols)\n\nYtrain = \\\ntrain[[\"sequence_id\", \"sequence_type\", target]].drop_duplicates().set_index(\"sequence_id\")\n\nYtrain[target] = \\\nnp.where(\n    Ytrain.sequence_type == \"Non-Target\", \n    'Text on phone', \n    Ytrain[target].values\n)\n\nPrintColor(f\"\\n---> Multiclass targets\\n\")\ndisplay( Ytrain[target].value_counts() )\n\nPrintColor(f\"\\n---> Binary targets\\n\")\ndisplay( Ytrain[\"sequence_type\"].value_counts())\n\nXtrain = Xtrain.merge(Ytrain, how = \"left\", left_on = [\"sequence_id\"], right_index = True)\nYtrain = Xtrain[[\"sequence_id\", target, \"sequence_type\"]]\nXtrain = Xtrain.drop([target, \"sequence_type\"], axis=1)\n\nPrintColor(\n    f\"\\n---> Shapes = {Xtrain.shape} {Xtest.shape}\"\n)\n\nygrp = np.zeros(len(Ytrain))\ncv   = StratifiedKFold(n_splits = n_splits, random_state = state, shuffle = True)\n\nfor fold_nb, (_, dev_idx) in enumerate(cv.split(Xtrain, Ytrain[target])):\n    ygrp[dev_idx] = fold_nb\n\nygrp = pd.Series(ygrp, name = \"fold_nb\", dtype = np.uint8)\n\nPrintColor(\n    f\"\\n---> Shapes = {Xtrain.shape} {Ytrain.shape} {Xtest.shape} {ygrp.shape}\"\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-31T17:31:54.984414Z","iopub.execute_input":"2025-05-31T17:31:54.984781Z","iopub.status.idle":"2025-05-31T17:31:55.602382Z","shell.execute_reply.started":"2025-05-31T17:31:54.984755Z","shell.execute_reply":"2025-05-31T17:31:55.601366Z"},"_kg_hide-input":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **BASELINE MODEL**","metadata":{}},{"cell_type":"markdown","source":"## **BINARY CLASSIFIER**\n\nThis is a binary classifier for the sequence type of target and other kinesiology types","metadata":{}},{"cell_type":"code","source":"%%time \n\nmodel = CBC(\n    iterations    = 400,\n    loss_function = \"Logloss\",\n    eval_metric   = \"Logloss\",\n    learning_rate = 0.025,\n    max_depth     = 5,\n    l2_leaf_reg   = 0.25,\n    random_seed   = state,\n    verbose       = 0,    \n)\n\nmethod = \"BC_CB1C\"\nytrain = Ytrain[\"sequence_type\"]\n\nytrain = \\\npd.Series( \n    np.where( ytrain == \"Non-Target\", 0, 1), \n    name = \"sequence_type\",\n    dtype = np.uint8\n)\n\ndrop_cols = [\"subject\", \"sequence_id\", \"Label\", \"Source\", \"id\", \"Id\"]\n\nmd = ModelTrainer(\n    problem_type   = \"binary\", \n    test_preds_req = False, \n    drop_cols      = drop_cols,\n    verbosity      = verbosity,\n)\n\nbinary_models, bc_oof_preds, _ = \\\nmd.fit_predict(\n    Xtrain, ytrain, None, ygrp, model, method, \n    **{\"verbose\" : 0}\n)\n\njoblib.dump( binary_models , \"BCModels.joblib\")\nbc_oof_preds.to_csv(\"BC_OOF_Preds.csv\")\n\ntry:\n    del Xtrain[method]\nexcept:\n    pass\n\n_ = utils.CleanMemory()\nprint()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-31T17:31:55.604167Z","iopub.execute_input":"2025-05-31T17:31:55.604638Z","iopub.status.idle":"2025-05-31T17:32:56.557949Z","shell.execute_reply.started":"2025-05-31T17:31:55.604584Z","shell.execute_reply":"2025-05-31T17:32:56.556988Z"},"_kg_hide-input":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## **MULTICLASS CLASSIFIER**\n\nWe train a single boosted tree model to start with the process. This is a 9-class classifier, 8 target classes and 1 general class for all other kinesiology <br>\nWe coalesce all non-target movements into a general class named **Text on phone**","metadata":{}},{"cell_type":"code","source":"%%time \n\nmodel = CBC(\n    iterations    = 400,\n    loss_function = \"MultiClass\",\n    eval_metric   = \"MultiClass\",\n    learning_rate = 0.025,\n    max_depth     = 5,\n    l2_leaf_reg   = 0.25,\n    random_seed   = state,\n    verbose       = 0,    \n)\n\nmethod    = \"MC_CB1C\"\nytrain    = Ytrain[target].map(mapper).astype(np.uint8)\ndrop_cols = [\"subject\", \"sequence_id\", \"Label\", \"Source\", \"id\", \"Id\"]\nXtrain    = pd.concat([Xtrain, bc_oof_preds], axis=1)\n\nmd = ModelTrainer(\n    problem_type   = \"multiclass\", \n    test_preds_req = False, \n    drop_cols      = drop_cols,\n    verbosity      = verbosity,\n)\n\nmc_models, mc_oof_preds, _ = \\\nmd.fit_predict(\n    Xtrain, ytrain, None, ygrp, model, method, \n    **{\"verbose\" : 0}\n)\n\njoblib.dump( mc_models , \"MCModels.joblib\")\nmc_oof_preds.to_csv(\"MC_OOF_Preds.csv\")\n\n_ = utils.CleanMemory()\nprint()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-31T17:32:56.559209Z","iopub.execute_input":"2025-05-31T17:32:56.559582Z","iopub.status.idle":"2025-05-31T17:39:44.322242Z","shell.execute_reply.started":"2025-05-31T17:32:56.559544Z","shell.execute_reply":"2025-05-31T17:39:44.320917Z"},"_kg_hide-input":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **INFERENCE AND SUBMISSION**\n\nLet's use the API and submit to the leaderboard!","metadata":{}},{"cell_type":"code","source":"import kaggle_evaluation.cmi_inference_server\n\ndef predict(\n    sequence     : pl.DataFrame, \n    demographics : pl.DataFrame,\n) -> str:\n    \"\"\"\n    Prediction function for Kaggle evaluation\n    \"\"\"\n\n    print()\n    test  = reduce_mem_usage( sequence.to_pandas(), \"test-sequence\" )\n    testd = reduce_mem_usage( demographics.to_pandas(), \"test-demographics\" )\n    Xtest = make_ftre(test, testd, sel_cols)\n\n    if verbosity > 0: \n        print(f\"---> Shape = {Xtest.shape}\")\n  \n    vmm   = VotingModelMaker(binary_models, \"BC_CB1C\")\n    preds = vmm.predict(Xtest).to_numpy()[:,1].flatten()\n    Xtest[\"BC_CB1C\"] = preds\n\n    mdl_preds = []\n    vmm   = VotingModelMaker(mc_models, \"MC_CB1C\")\n    preds = vmm.predict(Xtest)\n    mdl_preds.append(preds)\n    \n    mdl_preds = pd.concat(mdl_preds, axis = 0, ignore_index = False)\n    mdl_preds = mdl_preds.groupby(level = 0).mean().to_numpy()\n    mdl_preds = \\\n    ( \n        pd.Series(np.argmax( mdl_preds , axis = 1 ), name = target).\n        map({v: k for k,v in mapper.items() })\n    )\n    \n    if verbosity > 0: \n        print(f\"---> Prediction = {mdl_preds.values[0]}\")\n\n    if verbosity == 0: \n        clear_output()\n    return mdl_preds.values[0]\n","metadata":{"trusted":true,"_kg_hide-input":true,"execution":{"iopub.status.busy":"2025-05-31T17:52:06.098995Z","iopub.execute_input":"2025-05-31T17:52:06.099860Z","iopub.status.idle":"2025-05-31T17:52:06.112574Z","shell.execute_reply.started":"2025-05-31T17:52:06.099817Z","shell.execute_reply":"2025-05-31T17:52:06.110801Z"}},"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    )\n\nprint()","metadata":{"trusted":true,"_kg_hide-input":true,"execution":{"iopub.status.busy":"2025-05-31T17:52:09.699825Z","iopub.execute_input":"2025-05-31T17:52:09.700252Z","iopub.status.idle":"2025-05-31T17:52:11.441011Z","shell.execute_reply.started":"2025-05-31T17:52:09.700222Z","shell.execute_reply":"2025-05-31T17:52:11.439698Z"}},"outputs":[],"execution_count":null}]}