{"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":242735499,"sourceType":"kernelVersion"},{"sourceId":242760209,"sourceType":"kernelVersion"}],"dockerImageVersionId":31040,"isInternetEnabled":true,"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. ","metadata":{}},{"cell_type":"markdown","source":"# **IMPORTS AND INSTALLATIONS**","metadata":{}},{"cell_type":"code","source":"!uv pip install -q --system -r /kaggle/input/cmi2025-public-imports-v1/req_kaggle.txt\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\nprint()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T15:51:18.777641Z","iopub.execute_input":"2025-05-30T15:51:18.777942Z","iopub.status.idle":"2025-05-30T15:52:01.690972Z","shell.execute_reply.started":"2025-05-30T15:51:18.777917Z","shell.execute_reply":"2025-05-30T15:52:01.689877Z"},"_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-30T15:52:01.692046Z","iopub.execute_input":"2025-05-30T15:52:01.692713Z","iopub.status.idle":"2025-05-30T15:52:01.943670Z","shell.execute_reply.started":"2025-05-30T15:52:01.692679Z","shell.execute_reply":"2025-05-30T15:52:01.942522Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## **KEY CONFIG PARAMETERS**\n\n|Configuration parameter| Explanation| Data type| Sample values |  \n| ---------------------- | ------------------------------- | --------------------- | --------------- |\n| version_nb    | Version Number | int | 1 | \n| model_id      | Model ID    | string | V1_1 | \n| model_label   | Model Label | string | ML | \n| test_req      | Test Required| bool | True / False | \n| test_iter     | Test case iterations for models | int | 50 |\n| gpu_switch      | Do we need GPU support | bool | True / False |\n| state           | Random state | int | 42 |\n| target          | Target column | str |  |\n| grouper         | CV grouper column | str |  |\n| ip_path, op_path | Data paths  | str | |\n| pstprcs_* | Do we need post-processing  | bool |True / False |\n| ML| Do we need machine learning models  | bool |True / False |\n| test_preds_req| Do we need test set predictions (training in inference kernel)  | bool |True / False |\n| n_splits/ n_repeats | N-splits and repeats for CV scheme | int | 3/5/10|\n| nbrnd_erly_stp | Early stopping rounds | int | 40|\n| mdlcv_mthd | Model CV method | str | RSKF|\n| ensemble_req | Do we need ensemble | bool | True / False |\n| metric_obj   | Metric direction | str | minimize/ maximize |","metadata":{}},{"cell_type":"markdown","source":"# **PREPROCESSING**","metadata":{}},{"cell_type":"code","source":"%%time\n\ntest = \\\npl.scan_parquet( os.path.join(CFG.ip_path, \"test.parquet\" )).collect(engine = \"streaming\").to_pandas()\n\ntrain = \\\n(\n    pl.scan_parquet( os.path.join(CFG.ip_path, \"train.parquet\" )).\n    filter(pl.col(\"sequence_type\").eq(\"Target\")).\n    collect(engine = \"streaming\").\n    to_pandas()\n)\n\nXtrain  = train[test.columns]\nXtest   = test.copy()\nPrintColor(f\"---> Shapes = {Xtrain.shape} {Xtest.shape}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T15:52:01.946835Z","iopub.execute_input":"2025-05-30T15:52:01.947162Z","iopub.status.idle":"2025-05-30T15:52:06.250638Z","shell.execute_reply.started":"2025-05-30T15:52:01.947139Z","shell.execute_reply":"2025-05-30T15:52:06.249660Z"},"_kg_hide-input":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **FEATURE CREATION**","metadata":{}},{"cell_type":"code","source":"%%writefile fe.py\n\nclass FeatureMaker:\n    def __init__(self, fill_miss_val : bool = True):\n        self.fill_miss_val = fill_miss_val\n        \n    def make_ftre(self, df):\n        \"\"\"\n        Create comprehensive features from sensor data\n        Source - https://www.kaggle.com/code/mohanapavanbezawada/detect-behavior-with-sensor-data\n        \"\"\"\n        \n        sensor_cols     = ['acc_x', 'acc_y', 'acc_z', 'rot_w', 'rot_x', 'rot_y', 'rot_z']\n        all_sensor_cols = [col for col in df.columns if any(s in col for s in ['acc_', 'rot_', 'thm_', 'tof_'])]\n        features_df     = df[sensor_cols].copy()\n    \n        if self.fill_miss_val:\n            features_df = features_df.fillna(method='ffill').fillna(method='bfill').fillna(-1)\n        \n        seq_features = []\n        for seq_id in tqdm( df['sequence_id'].unique() ):\n            seq_data = df[df['sequence_id'] == seq_id][sensor_cols]\n           \n            seq_feat = {}\n            \n            seq_feat.update({f'{col}_mean': seq_data[col].mean() for col in sensor_cols})\n            seq_feat.update({f'{col}_std': seq_data[col].std() for col in sensor_cols})\n            seq_feat.update({f'{col}_min': seq_data[col].min() for col in sensor_cols})\n            seq_feat.update({f'{col}_max': seq_data[col].max() for col in sensor_cols})\n            seq_feat.update({f'{col}_median': seq_data[col].median() for col in sensor_cols})\n            seq_feat.update({f'{col}_q25': seq_data[col].quantile(0.25) for col in sensor_cols})\n            seq_feat.update({f'{col}_q75': seq_data[col].quantile(0.75) for col in sensor_cols})\n            seq_feat.update({f'{col}_skew': seq_data[col].skew() for col in sensor_cols})\n            seq_feat.update({f'{col}_kurt': seq_data[col].kurtosis() for col in sensor_cols})\n            \n            seq_data_np = seq_data[['acc_x', 'acc_y', 'acc_z']].values\n            magnitude = np.sqrt(np.sum(seq_data_np**2, axis=1))\n            seq_feat['acc_magnitude_mean'] = np.mean(magnitude)\n            seq_feat['acc_magnitude_std'] = np.std(magnitude)\n            seq_feat['acc_magnitude_max'] = np.max(magnitude)\n            \n            rot_data = seq_data[['rot_x', 'rot_y', 'rot_z']].values\n            rot_magnitude = np.sqrt(np.sum(rot_data**2, axis=1))\n            seq_feat['rot_magnitude_mean'] = np.mean(rot_magnitude)\n            seq_feat['rot_magnitude_std'] = np.std(rot_magnitude)\n            \n            for col in sensor_cols:\n                values = seq_data[col].values\n                if len(values) > 1:\n                    diff = np.diff(values)\n                    seq_feat[f'{col}_diff_mean'] = np.mean(diff)\n                    seq_feat[f'{col}_diff_std'] = np.std(diff)\n                    seq_feat[f'{col}_diff_max'] = np.max(np.abs(diff))\n            \n            for col in sensor_cols:\n                values = seq_data[col].values - seq_data[col].mean()\n                values = values[~np.isnan(values)] \n                if len(values) > 1:\n                    zero_crossings = np.sum(np.diff(np.sign(values)) != 0)\n                    seq_feat[f'{col}_zero_crossings'] = zero_crossings / len(values)\n                else:\n                    seq_feat[f'{col}_zero_crossings'] = 0\n            \n            for col in sensor_cols:\n                seq_feat[f'{col}_energy'] = np.sum(seq_data[col].values**2)\n                seq_feat[f'{col}_rms'] = np.sqrt(np.mean(seq_data[col].values**2))\n            \n            seq_feat['sequence_length'] = len(seq_data)\n            seq_feat['sequence_id']     = seq_id\n            seq_features.append(seq_feat)\n        \n        return pd.DataFrame(seq_features)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T15:52:06.251683Z","iopub.execute_input":"2025-05-30T15:52:06.251988Z","iopub.status.idle":"2025-05-30T15:52:06.260689Z","shell.execute_reply.started":"2025-05-30T15:52:06.251962Z","shell.execute_reply":"2025-05-30T15:52:06.259705Z"},"_kg_hide-input":true,"_kg_hide-output":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%%time \n\nexec( open( \"fe.py\", \"r\").read())\nfe     = FeatureMaker(True)\nXtrain = fe.make_ftre(Xtrain)\nXtest  = fe.make_ftre(Xtest)\n\nXtrain[\"Source\"], Xtest[\"Source\"] = (\"Competition\", \"Competition\")\n\nprint(f\"---> Shapes  = {Xtrain.shape} {Xtest.shape}\")\nprint()\n_ = utils.CleanMemory()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T15:52:06.261970Z","iopub.execute_input":"2025-05-30T15:52:06.262308Z","iopub.status.idle":"2025-05-30T15:55:33.590148Z","shell.execute_reply.started":"2025-05-30T15:52:06.262286Z","shell.execute_reply":"2025-05-30T15:55:33.589079Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **CV-FOLDS**","metadata":{}},{"cell_type":"code","source":"%%time \n\nytrain = train[['sequence_id', CFG.target]].drop_duplicates()\nytrain[CFG.target] = ytrain[CFG.target].map(CFG.tgt_mapper).astype(np.uint8)\nXtrain = Xtrain.merge(ytrain, on = \"sequence_id\", how = \"left\")\n\nytrain = Xtrain[CFG.target]\ndel Xtrain[CFG.target]\n\ncv   = cv_selector[CFG.mdlcv_mthd]\nygrp = np.zeros(len(Xtrain))\n\nfor fold_nb, (train_idx, dev_idx) in enumerate( cv.split(Xtrain, ytrain) ):\n    ygrp[dev_idx] = fold_nb\nygrp = pd.Series( ygrp , name = \"fold_nb\", dtype = np.uint8 )\n\nPrintColor(f\"---> Shapes  = {Xtrain.shape} {ytrain.shape} {ygrp.shape} {Xtest.shape}\")\n_ = utils.CleanMemory()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T15:55:33.591332Z","iopub.execute_input":"2025-05-30T15:55:33.591680Z","iopub.status.idle":"2025-05-30T15:55:33.925081Z","shell.execute_reply.started":"2025-05-30T15:55:33.591650Z","shell.execute_reply":"2025-05-30T15:55:33.924035Z"},"_kg_hide-input":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **BASELINE MODELS**\n\nWe train simple models here and elicit baseline predictions","metadata":{}},{"cell_type":"code","source":"%%time \n\nMdl_Master = \\\n{ \n f\"XGB1C\"   : XGBC(**{  \"device\"                 : \"cuda\" if CFG.gpu_switch == \"ON\" else \"cpu\", \n                        \"n_estimators\"           : 250,\n                        \"learning_rate\"          : 0.02,\n                        \"colsample_bylevel\"      : 0.35,\n                        \"gamma\"                  : 0.75,\n                        \"max_depth\"              : 4,\n                        \"n_jobs\"                 : -1,\n                        \"random_state\"           : CFG.state,\n                        \"reg_alpha\"              : 0.01,\n                        \"reg_lambda\"             : 0.10,\n                        \"subsample\"              : 0.25,\n                        \"verbosity\"              : 0,\n                    }\n                  ),\n    \n f'LGBM1C'  : LGBMC(**{ 'device'             : \"gpu\" if CFG.gpu_switch == \"ON\" else \"cpu\",\n                        \"n_estimators\"       : 300, \n                        \"max_depth\"          : 4,\n                        \"learning_rate\"      : 0.02,\n                        \"colsample_bytree\"   : 0.35,               \n                        \"n_jobs\"             : -1,\n                        \"num_leaves\"         : 45,\n                        \"random_state\"       : CFG.state,\n                        \"reg_alpha\"          : 0.01,\n                        \"reg_lambda\"         : 0.001,\n                        \"subsample\"          : 0.35,\n                        \"verbosity\"          : -1\n                      }\n                   ),\n\n f'LGBM2C'  : LGBMC(**{ \"data_sample_strategy\"   : \"goss\",\n                        \"n_estimators\"           : 300,\n                        \"max_depth\"              : 4, \n                        'device'                 : \"gpu\" if CFG.gpu_switch == \"ON\" else \"cpu\",\n                        \"learning_rate\"          : 0.015,\n                        \"colsample_bytree\"       : 0.35,\n                        \"min_child_samples\"      : 25,\n                        \"n_jobs\"                 : -1,\n                        \"num_leaves\"             : 30,\n                        \"random_state\"           : CFG.state,\n                        \"reg_alpha\"              : 0.001,\n                        \"reg_lambda\"             : 0.001,\n                        \"subsample\"              : 0.21,\n                        \"verbosity\"              : -1,\n                     }\n                   ),\n\n f'CB1C'    : CBC(**{'task_type'             : \"GPU\" if CFG.gpu_switch == \"ON\" else \"CPU\",\n                     'learning_rate'         : 0.02,\n                     'iterations'            : 250,\n                     'max_depth'             : 4,\n                     'min_data_in_leaf'      : 8 ,\n                     'colsample_bylevel'     : 0.30 if CFG.gpu_switch == \"OFF\" else None,\n                     'l2_leaf_reg'           : 1.05,\n                     'leaf_estimation_method': \"Newton\",\n                     'verbose'               : 0,\n                     'random_state'          : CFG.state,\n                    }\n                 ),\n\n f\"RF1C\"     : RFC(n_estimators = 300,\n                   max_depth    = 5,\n                   random_state = CFG.state,\n                   n_jobs       = -1,\n                  ),\n\n f\"HGB1C\"    : HGBC( learning_rate = 0.015,\n                     max_iter      = 350,\n                     max_depth     = 4,\n                     verbose       = 0,\n                     random_state  = CFG.state,\n                   ),\n}\n\n# Initializing model outputs\nOOF_Preds    = {}\nMdl_Preds    = {}\nFtreImp      = {}\nFittedModels = {}","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T15:55:33.926433Z","iopub.execute_input":"2025-05-30T15:55:33.926707Z","iopub.status.idle":"2025-05-30T15:55:33.939751Z","shell.execute_reply.started":"2025-05-30T15:55:33.926684Z","shell.execute_reply":"2025-05-30T15:55:33.938763Z"},"_kg_hide-input":true,"_kg_hide-output":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%%time\n\ndrop_cols = [\"Source\", \"id\", \"Id\", \"Label\", CFG.target, \"fold_nb\", \"sequence_id\"]\n\nfor method, mymodel in tqdm(Mdl_Master.items()):\n\n    PrintColor(\n        f\"\\n{'=' * 20} {method.upper()} MODEL TRAINING {'=' * 20}\\n\"\n    )\n\n    md = \\\n    ModelTrainer(\n        problem_type   = \"multiclass\",\n        es             = CFG.nbrnd_erly_stp,\n        target         = CFG.target,\n        orig_req       = False,\n        orig_all_folds = CFG.orig_all_folds,\n        metric_lbl     = \"auc_multi\",\n        drop_cols      = drop_cols,\n        pp_preds       = CFG.pstprcs_oof,\n    )\n\n    fitted_models, oof_preds, test_preds, ftreimp, _ =  \\\n    md.MakeOfflineModel(\n        Xtrain,\n        ytrain,\n        ygrp,\n        Xtest,\n        mymodel,\n        method,\n        test_preds_req   = True,\n        ftreimp_plot_req = CFG.ftre_plots_req,\n        ntop = 50,\n    )\n\n    OOF_Preds[method]    = oof_preds\n    Mdl_Preds[method]    = test_preds\n    FtreImp[method]      = ftreimp\n    FittedModels[method] = fitted_models\n\n    del fitted_models, oof_preds, test_preds, ftreimp\n    print()\n    collect()\n\n\n_ = utils.CleanMemory()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T15:55:33.940732Z","iopub.execute_input":"2025-05-30T15:55:33.941158Z","iopub.status.idle":"2025-05-30T15:59:07.507992Z","shell.execute_reply.started":"2025-05-30T15:55:33.941124Z","shell.execute_reply":"2025-05-30T15:59:07.506889Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **ENSEMBLE**\n\nWe stack up the model predictions and perform a simple logistic ","metadata":{}},{"cell_type":"code","source":"%%time \n\noof_preds = pd.DataFrame(index = range(len(Xtrain)))\nfor method, preds in OOF_Preds.items():\n    oof_preds = pd.concat([oof_preds, pd.DataFrame(preds).add_prefix(method)], axis=1)\n\nmdl_preds = pd.DataFrame(index = range(len(Xtest)))\nfor method, preds in Mdl_Preds.items():\n    mdl_preds = pd.concat([mdl_preds, pd.DataFrame(preds).add_prefix(method)], axis=1)\n\nmethod = \"L21C\"\nmodel  = LRC(C = 0.05, random_state = CFG.state, max_iter = 10000)\n\nmd = \\\nModelTrainer(\n    problem_type   = \"multiclass\",\n    es             = CFG.nbrnd_erly_stp,\n    target         = CFG.target,\n    orig_req       = False,\n    orig_all_folds = CFG.orig_all_folds,\n    metric_lbl     = \"auc_multi\",\n    drop_cols      = drop_cols,\n    pp_preds       = CFG.pstprcs_oof,\n)\n\nens_models, oof_ens_preds, test_ens_preds, _, _ =  \\\nmd.MakeOfflineModel(\n    oof_preds.assign(Source = \"Competition\"),\n    ytrain,\n    ygrp,\n    mdl_preds.assign(Source = \"Competition\"),\n    model,\n    method,\n    test_preds_req   = True,\n    ftreimp_plot_req = False,\n    ntop = 50,\n)\n\n\nsolution = pd.DataFrame(ytrain.map({v:k for k, v in CFG.tgt_mapper.items()}))\nsolution[\"sequence_id\"] = Xtrain[\"sequence_id\"].values\n\nsubmission = pd.Series(np.argmax(oof_ens_preds, axis = 1)).map({v:k for k, v in CFG.tgt_mapper.items()})\nsubmission = submission.to_frame()\nsubmission[\"sequence_id\"] = Xtrain[\"sequence_id\"].values\nsubmission = submission.rename({0 : CFG.target}, axis=1)\n\nscore = utils.ScoreMetric(solution, submission)\nPrintColor(f\"---> Ensemble OOF score = {score: ,.8f} | Competition Metric\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T15:59:07.510666Z","iopub.execute_input":"2025-05-30T15:59:07.511227Z","iopub.status.idle":"2025-05-30T15:59:08.356334Z","shell.execute_reply.started":"2025-05-30T15:59:07.511202Z","shell.execute_reply":"2025-05-30T15:59:08.355172Z"},"_kg_hide-input":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **CLOSURE**","metadata":{}},{"cell_type":"code","source":"%%time \n\noof_preds.assign(sequence_id = Xtrain[\"sequence_id\"].values).to_csv(\"OOF_Preds.csv\")\nmdl_preds.assign(sequence_id = Xtest[\"sequence_id\"].values).to_csv(\"Mdl_Preds.csv\")\n\njoblib.dump(FittedModels,  \"FittedModels.joblib\")\njoblib.dump(ens_models,    \"EnsembleModels.joblib\")\n\n!ls","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T15:59:08.357460Z","iopub.execute_input":"2025-05-30T15:59:08.357845Z","iopub.status.idle":"2025-05-30T15:59:13.438700Z","shell.execute_reply.started":"2025-05-30T15:59:08.357795Z","shell.execute_reply":"2025-05-30T15:59:13.437214Z"},"_kg_hide-input":true},"outputs":[],"execution_count":null}]}