{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","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":258972565,"sourceType":"kernelVersion"},{"sourceId":259185678,"sourceType":"kernelVersion"}],"dockerImageVersionId":31090,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import sys\nimport pandas as pd\nimport polars as pl\nimport torch\nimport matplotlib.pyplot as plt\nimport scipy as sp\nimport numpy as np\nimport sys\nimport os\nimport gc\nimport random\nimport time\nimport pickle\nfrom typing import Optional, Tuple\nfrom contextlib import contextmanager\nimport importlib.util\nimport copy\nimport math\nfrom sklearn.model_selection import StratifiedGroupKFold\nfrom tqdm import tqdm\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.utils.data import Dataset, DataLoader\nfrom torch.amp import GradScaler, autocast\nfrom sklearn.metrics import f1_score\nimport warnings\nwarnings.filterwarnings(\"ignore\", category=UserWarning)\nwarnings.filterwarnings(\"ignore\", category=DeprecationWarning)\nwarnings.filterwarnings(\"ignore\", category=RuntimeWarning)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-09-02T13:40:32.978247Z","iopub.execute_input":"2025-09-02T13:40:32.978431Z","iopub.status.idle":"2025-09-02T13:40:38.17612Z","shell.execute_reply.started":"2025-09-02T13:40:32.978413Z","shell.execute_reply":"2025-09-02T13:40:38.175155Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"torch.set_num_threads(4)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T13:40:38.177097Z","iopub.execute_input":"2025-09-02T13:40:38.17754Z","iopub.status.idle":"2025-09-02T13:40:38.190832Z","shell.execute_reply.started":"2025-09-02T13:40:38.17751Z","shell.execute_reply":"2025-09-02T13:40:38.189934Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# patch for pkl load from local env\nimport numpy\nsys.modules['numpy._core'] = numpy.core\nsys.modules['numpy._core.numeric'] = numpy.core.numeric","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T13:40:38.192889Z","iopub.execute_input":"2025-09-02T13:40:38.193113Z","iopub.status.idle":"2025-09-02T13:40:38.208982Z","shell.execute_reply.started":"2025-09-02T13:40:38.193095Z","shell.execute_reply":"2025-09-02T13:40:38.208439Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"CLASS_CODE={\n    ### target\n    'Cheek - pinch skin': 0,\n    'Forehead - pull hairline': 1,\n    'Neck - scratch': 2,\n    'Neck - pinch skin': 3,\n    'Eyelash - pull hair': 4,\n    'Eyebrow - pull hair': 5,\n    'Forehead - scratch': 6,\n    'Above ear - pull hair': 7,\n    ### non-target\n    'Write name on leg': 8,\n    'Feel around in tray and pull out an object': 9,\n    'Wave hello': 10,\n    'Write name in air': 11,\n    'Text on phone': 12,\n    'Pull air toward your face': 13,\n    'Pinch knee/leg skin': 14,\n    'Scratch knee/leg skin': 15,\n    'Drink from bottle/cup': 16,\n    'Glasses on/off': 17\n}\nLABEL_MAP={v:k for k,v in CLASS_CODE.items()}","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T13:40:38.209428Z","iopub.execute_input":"2025-09-02T13:40:38.209978Z","iopub.status.idle":"2025-09-02T13:40:38.224493Z","shell.execute_reply.started":"2025-09-02T13:40:38.209958Z","shell.execute_reply":"2025-09-02T13:40:38.223821Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def comp_metric(pred,target):\n    pred=np.array(pred)\n    target=np.array(target)\n    \n    y_true_bin = target<8\n    y_pred_bin = pred<8\n    f1_binary = f1_score(\n        y_true_bin,\n        y_pred_bin,\n        pos_label=True,\n        zero_division=0,\n        average='binary'\n    )\n\n    y_true_mc = np.clip(target,a_max=8,a_min=None)\n    y_pred_mc = np.clip(pred,a_max=8,a_min=None)\n\n    f1_macro = f1_score(\n        y_true_mc,\n        y_pred_mc,\n        average='macro',\n        zero_division=0\n    )\n    return 0.5 * f1_binary + 0.5 * f1_macro\n\n\ndef value_to_rank(X):\n    return torch.argsort(torch.argsort(X,dim=-1),dim=-1).type(torch.float32)\n\ndef soft_cross_entropy(pred, soft_targets,label_smoothing=0.0,weight=None,reduce=True):\n    class_num=soft_targets.shape[1]\n    soft_targets=soft_targets+label_smoothing/class_num\n    soft_targets=soft_targets/soft_targets.sum(axis=1,keepdims=True)\n    \n    log_probs = F.log_softmax(pred, dim=1)\n    if weight is not None:\n        log_probs=log_probs*weight.reshape((-1,1))\n    loss = -(soft_targets * log_probs).sum(dim=1)\n    if reduce:\n        return loss.mean()\n    else:\n        return loss","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T13:40:38.224919Z","iopub.execute_input":"2025-09-02T13:40:38.225091Z","iopub.status.idle":"2025-09-02T13:40:38.241967Z","shell.execute_reply.started":"2025-09-02T13:40:38.225077Z","shell.execute_reply":"2025-09-02T13:40:38.241248Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class InferenceWrapper:\n    def __init__(self,path='',device='cuda'):\n        self.device=device\n        modules={}\n        for k in ['CFG','model','data_pipline']:\n            spec = importlib.util.spec_from_file_location(f'_{k}', f'{path}/_{k}.py')\n            module = importlib.util.module_from_spec(spec)\n            modules[k] = module\n            spec.loader.exec_module(module)\n        self.modules=modules\n        self.prep_data=modules['data_pipline'].extract_data_dict\n        self.dataset=modules['data_pipline'].CMI3Dataset([],index=[],max_len=modules['CFG'].CFG.max_len)\n        self.models={}\n        val_res=pd.read_pickle(f'{path}/res.pkl')\n        for i in range(len(val_res)):\n            model=modules['model'].CMI3Net(\n                    **modules['CFG'].CFG.model_cfg,\n            )\n            model.load_state_dict(val_res[i]['best_weight'])\n            model.to(self.device)\n            model.eval()\n            self.models[i]=model\n        return\n\n    def predict(self,df,meta,model_idx=None):\n        df=copy.deepcopy(df)\n        meta=copy.deepcopy(meta)\n        data_dict=self.prep_data(df,meta,test_mode=True)\n        batch_data=self.dataset.proc_data_dict(data_dict)\n        for k,v in batch_data.items():\n            batch_data[k]=torch.as_tensor(v).unsqueeze(0).to(self.device)\n            \n        with torch.inference_mode():\n            with autocast(device_type=self.device, dtype=torch.float32):\n                if model_idx is not None:\n                    pred=value_to_rank(self.models[model_idx](batch_data))\n                else:\n                    pred=0.0\n                    for v in self.models.values():\n                        pred+=value_to_rank(v(batch_data))/len(self.models)\n        return pred[0].cpu()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T13:40:38.24283Z","iopub.execute_input":"2025-09-02T13:40:38.24306Z","iopub.status.idle":"2025-09-02T13:40:38.26437Z","shell.execute_reply.started":"2025-09-02T13:40:38.243034Z","shell.execute_reply":"2025-09-02T13:40:38.263839Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class TestCFG:\n    device='cuda'\n    extra_cols=[]\nthm_cols=['thm_1','thm_2','thm_3','thm_4','thm_5',]\ntof_cols=[]\nfor i in range(1,6):\n    for j in range(64):\n        tof_cols.append(f'tof_{i}_v{j}')\nTestCFG.extra_cols=thm_cols+tof_cols\nTestCFG.tof_cols=tof_cols","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T13:40:38.265247Z","iopub.execute_input":"2025-09-02T13:40:38.265405Z","iopub.status.idle":"2025-09-02T13:40:38.282858Z","shell.execute_reply.started":"2025-09-02T13:40:38.265392Z","shell.execute_reply":"2025-09-02T13:40:38.282266Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df=pd.read_csv('/kaggle/input/cmi-detect-behavior-with-sensor-data/train.csv')\ntrain_meta=pd.read_csv('/kaggle/input/cmi-detect-behavior-with-sensor-data/train_demographics.csv')\ntrain_meta.set_index('subject',inplace=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T13:40:38.283504Z","iopub.execute_input":"2025-09-02T13:40:38.284043Z","iopub.status.idle":"2025-09-02T13:41:07.882168Z","shell.execute_reply.started":"2025-09-02T13:40:38.284016Z","shell.execute_reply":"2025-09-02T13:41:07.881562Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model1=InferenceWrapper(path='/kaggle/input/m21-cnn-gru-train')\nmodel2=InferenceWrapper(path='/kaggle/input/all-m14-cnn-gru-train')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T13:41:07.89287Z","iopub.execute_input":"2025-09-02T13:41:07.893716Z","iopub.status.idle":"2025-09-02T13:42:16.918395Z","shell.execute_reply.started":"2025-09-02T13:41:07.89369Z","shell.execute_reply":"2025-09-02T13:42:16.917801Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"check_list=[\n    ('model1',model1),\n    ('model2',model2),\n]\noof_preds=[]\noof_target=None\nfor model_name,model in check_list:\n    meta_df=train_meta\n    seq_df=train_df\n    data_dicts=[]\n    info={'sequence_id':[],'subject':[],'target':[]}\n    for seq_id,sub_df in tqdm(seq_df.groupby('sequence_id')):\n        subject=sub_df['subject'].iloc[0]\n        sub_meta=meta_df.loc[subject].copy()\n        data_dict=model.prep_data(sub_df,sub_meta,test_mode=False)\n        ### append dict_data\n        data_dict['teacher_logits']=np.nan\n        data_dicts.append(data_dict)\n        info['sequence_id'].append(seq_id)\n        info['subject'].append(subject)\n        info['target'].append(sub_df['gesture'].iloc[0] if 'gesture' in sub_df.columns else np.nan)\n    info=pd.DataFrame(info)\n    info=info.merge(train_meta,on='subject',how='left')\n    info=info.merge(\n        train_df[['orientation','gesture','sequence_id']].drop_duplicates('sequence_id'),\n        on='sequence_id',how='left'\n    )\n    # check val score\n    sgkf = StratifiedGroupKFold(n_splits=5,random_state=777, shuffle=True)\n    splits=sgkf.split(info, info['target'].values, info['subject'].values)\n    Y_val=[]\n    Y_pred_val=[]\n    for i,(train_index,val_index) in enumerate(splits):\n        val_dataset = model.modules['data_pipline'].CMI3Dataset(data_dicts,index=val_index,max_len=model.modules['CFG'].CFG.max_len)\n        val_loader = DataLoader(val_dataset, batch_size=64, shuffle=False, pin_memory=True, drop_last=False, num_workers=4, persistent_workers=True)\n        for batch_data in val_loader:\n            for k,v in batch_data.items():\n                batch_data[k]=v.to(TestCFG.device)\n            with torch.inference_mode():\n                with autocast(device_type=TestCFG.device, dtype=torch.float32):\n                    Y_pred = model.models[i](batch_data)\n            Y_val.append(batch_data['Y_class'])\n            Y_pred_val.append(Y_pred)\n    \n            del batch_data\n            del Y_pred\n    \n    Y_val=torch.cat(Y_val).cpu()\n    Y_pred_val=torch.cat(Y_pred_val).cpu()\n    # compute metric\n    score=comp_metric(torch.argmax(Y_pred_val,dim=-1),Y_val)\n    print(f'{model_name} val score:{score:.4f}')\n    oof_preds.append(Y_pred_val)\n    oof_target=Y_val","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T13:42:16.919765Z","iopub.execute_input":"2025-09-02T13:42:16.91999Z","iopub.status.idle":"2025-09-02T14:04:40.776338Z","shell.execute_reply.started":"2025-09-02T13:42:16.919973Z","shell.execute_reply":"2025-09-02T14:04:40.775064Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"inference_comb=None","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T14:04:41.65347Z","iopub.execute_input":"2025-09-02T14:04:41.653681Z","iopub.status.idle":"2025-09-02T14:04:41.669149Z","shell.execute_reply.started":"2025-09-02T14:04:41.653665Z","shell.execute_reply":"2025-09-02T14:04:41.668425Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def predict(sequence, demographics):\n    global inference_comb\n    global LABEL_MAP\n    if inference_comb is None:\n        inference_comb={\n            'IMU':[\n                (1.0,InferenceWrapper(path='/kaggle/input/m21-cnn-gru-train')),\n            ],\n            'All':[\n                (1.0,InferenceWrapper(path='/kaggle/input/all-m14-cnn-gru-train')),\n            ],\n        }\n\n    #####################\n    df = sequence.to_pandas()\n    meta = demographics.to_pandas()\n    meta = meta.loc[meta.index[0]]\n    imu_only=df[TestCFG.extra_cols].isna().mean().mean()==1\n    #imu_only=True\n    \n    if imu_only:\n        pred_comb=inference_comb['IMU']\n    else:\n        pred_comb=inference_comb['All']\n    ### voting\n    pred_indices=[]\n    for _,model in pred_comb:\n        pred_indices.append(int(torch.argmax(model.predict(df,meta)).cpu()))\n    pred_idx = int(sp.stats.mode(pred_indices).mode)\n    pred_class = LABEL_MAP[pred_idx]\n    return pred_class","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T14:04:41.669993Z","iopub.execute_input":"2025-09-02T14:04:41.670257Z","iopub.status.idle":"2025-09-02T14:04:41.687367Z","shell.execute_reply.started":"2025-09-02T14:04:41.670227Z","shell.execute_reply":"2025-09-02T14:04:41.686638Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Kaggle competition interface\nimport kaggle_evaluation.cmi_inference_server\ninference_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-09-02T14:04:41.688221Z","iopub.execute_input":"2025-09-02T14:04:41.688472Z","iopub.status.idle":"2025-09-02T14:05:24.881044Z","shell.execute_reply.started":"2025-09-02T14:04:41.688456Z","shell.execute_reply":"2025-09-02T14:05:24.880409Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}