{"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":12945235,"sourceType":"datasetVersion","datasetId":8192085},{"sourceId":259653389,"sourceType":"kernelVersion"},{"sourceId":259665757,"sourceType":"kernelVersion"}],"dockerImageVersionId":31040,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"from fastai.text.all import *\nfrom sklearn.impute import SimpleImputer\nfrom sklearn.preprocessing import StandardScaler\nfrom scipy.spatial.transform import Rotation as R\nfrom sklearn.metrics import f1_score, ConfusionMatrixDisplay","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T23:43:07.961051Z","iopub.execute_input":"2025-09-02T23:43:07.961325Z","iopub.status.idle":"2025-09-02T23:43:17.238193Z","shell.execute_reply.started":"2025-09-02T23:43:07.961300Z","shell.execute_reply":"2025-09-02T23:43:17.237097Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"data_folder = Path('/kaggle/input/cmi-detect-behavior-with-sensor-data')\ndf = pd.read_csv(data_folder/'train.csv')\ndemo_df = pd.read_csv(data_folder/'train_demographics.csv')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T23:43:17.239904Z","iopub.execute_input":"2025-09-02T23:43:17.240590Z","iopub.status.idle":"2025-09-02T23:43:54.441105Z","shell.execute_reply.started":"2025-09-02T23:43:17.240560Z","shell.execute_reply":"2025-09-02T23:43:54.440088Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"acc_cols = ['acc_x','acc_y','acc_z']\nrot_cols = ['rot_w','rot_x','rot_y','rot_z']\nimu_cols = acc_cols + rot_cols\nthm_cols = [f'thm_{i+1}' for i in range(5)]\ntof_cols = [f'tof_{i+1}_v{j}' for i in range(5) for j in range(64)]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T23:43:54.442277Z","iopub.execute_input":"2025-09-02T23:43:54.442611Z","iopub.status.idle":"2025-09-02T23:43:54.450091Z","shell.execute_reply.started":"2025-09-02T23:43:54.442588Z","shell.execute_reply":"2025-09-02T23:43:54.448467Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df['imu'] = df[imu_cols].to_numpy().tolist()\ndf['tof'] = df[tof_cols].to_numpy().tolist()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T23:43:54.451215Z","iopub.execute_input":"2025-09-02T23:43:54.451554Z","iopub.status.idle":"2025-09-02T23:44:11.649808Z","shell.execute_reply.started":"2025-09-02T23:43:54.451529Z","shell.execute_reply":"2025-09-02T23:44:11.648389Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"seq_df = df.groupby('sequence_id', as_index=False).agg(\n    subject = ('subject','first'),\n    imu = ('imu', list), tof = ('tof', list),\n    gesture = ('gesture', 'first')\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T23:44:11.652063Z","iopub.execute_input":"2025-09-02T23:44:11.652405Z","iopub.status.idle":"2025-09-02T23:44:14.550480Z","shell.execute_reply.started":"2025-09-02T23:44:11.652379Z","shell.execute_reply":"2025-09-02T23:44:14.549093Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"targ_gestures = df.loc[df.sequence_type=='Target', 'gesture'].unique()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T23:44:14.552068Z","iopub.execute_input":"2025-09-02T23:44:14.552560Z","iopub.status.idle":"2025-09-02T23:44:14.641536Z","shell.execute_reply.started":"2025-09-02T23:44:14.552525Z","shell.execute_reply":"2025-09-02T23:44:14.640338Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def impute_quat(imu: List):\n    \"\"\"Imputes missing quaternion values with identity quaternion\"\"\"\n    imu = np.array(imu)\n    mask = np.isnan(imu[:,3:]).all(axis=1)\n    imu[mask, 3:] = [1,0,0,0]\n    return imu","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T23:44:14.642714Z","iopub.execute_input":"2025-09-02T23:44:14.643036Z","iopub.status.idle":"2025-09-02T23:44:14.651495Z","shell.execute_reply.started":"2025-09-02T23:44:14.643006Z","shell.execute_reply":"2025-09-02T23:44:14.650179Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class THMImputer(Transform):\n    def setups(self, thms: TfmdLists):\n        thms = np.concatenate([np.array(thm) for thm in thms])\n        self.imp = SimpleImputer().fit(thms)\n\n    def encodes(self, thm):\n        thm = self.imp.transform(thm).astype(np.float32)\n        return torch.from_numpy(thm)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T23:44:14.652637Z","iopub.execute_input":"2025-09-02T23:44:14.652984Z","iopub.status.idle":"2025-09-02T23:44:14.693390Z","shell.execute_reply.started":"2025-09-02T23:44:14.652960Z","shell.execute_reply":"2025-09-02T23:44:14.691434Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def impute_tof(tof: List):\n    \"\"\"Fills Nans and replaces all instances of -1s with 254\"\"\"\n    tof = np.array(tof, dtype=np.float32)\n    tof = np.nan_to_num(tof, nan=254.0)\n    return np.where(tof==-1, 254.0, tof)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T23:44:14.695082Z","iopub.execute_input":"2025-09-02T23:44:14.695511Z","iopub.status.idle":"2025-09-02T23:44:14.725543Z","shell.execute_reply.started":"2025-09-02T23:44:14.695479Z","shell.execute_reply":"2025-09-02T23:44:14.723937Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def remove_gravity(acc: np.ndarray, quat: np.ndarray):\n    \"\"\"Removes effect of gravity from acceleration\"\"\"\n    rot = R.from_quat(quat[:, [1, 2, 3, 0]])\n    gravity_world = np.array([0, 0, 9.81])\n    gravity_sensor_frame = rot.apply(gravity_world, inverse=True)\n    return acc - gravity_sensor_frame","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T23:44:14.726544Z","iopub.execute_input":"2025-09-02T23:44:14.726842Z","iopub.status.idle":"2025-09-02T23:44:14.777905Z","shell.execute_reply.started":"2025-09-02T23:44:14.726813Z","shell.execute_reply":"2025-09-02T23:44:14.776478Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def calc_angular_vel(quat: np.ndarray, delta_t=1/10):\n    rot = R.from_quat(quat[:, [1, 2, 3, 0]])\n    rel_rot = rot[1:] * rot[:-1].inv()\n    vel = (2/delta_t)*rel_rot.as_rotvec()\n    return np.concatenate([vel, np.zeros((1,3))])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T23:44:14.779894Z","iopub.execute_input":"2025-09-02T23:44:14.780524Z","iopub.status.idle":"2025-09-02T23:44:14.825550Z","shell.execute_reply.started":"2025-09-02T23:44:14.780492Z","shell.execute_reply":"2025-09-02T23:44:14.824101Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def calc_angular_dist(quat: np.ndarray):\n    quat = quat[:, [1, 2, 3, 0]]\n    rot = R.from_quat(quat)\n    delta_rot = rot[1:] * rot[:-1].inv()\n    rotvecs = delta_rot.as_rotvec()\n    dist = np.linalg.norm(rotvecs, axis=1, keepdims=True)\n    return np.concatenate([dist, np.zeros((1,1))])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T23:44:14.827631Z","iopub.execute_input":"2025-09-02T23:44:14.827988Z","iopub.status.idle":"2025-09-02T23:44:14.861083Z","shell.execute_reply.started":"2025-09-02T23:44:14.827957Z","shell.execute_reply":"2025-09-02T23:44:14.859643Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def engineer_features(imu: np.ndarray):\n    acc, quat = imu[:, :3], imu[:, 3:]\n    acc_mag = np.linalg.norm(acc, axis=1, keepdims=True)\n    \n    lin_acc = remove_gravity(acc, quat)\n    lin_acc_mag = np.linalg.norm(lin_acc, axis=1, keepdims=True)\n    \n    rot_angle = 2*np.arccos(quat[:,0])[:, None]\n\n    return np.concatenate([\n        acc, acc_mag, lin_acc, lin_acc_mag, quat, rot_angle,\n        calc_angular_vel(quat), calc_angular_dist(quat)\n    ], axis=1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T23:44:14.862213Z","iopub.execute_input":"2025-09-02T23:44:14.862682Z","iopub.status.idle":"2025-09-02T23:44:14.902564Z","shell.execute_reply.started":"2025-09-02T23:44:14.862654Z","shell.execute_reply":"2025-09-02T23:44:14.900845Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class Normalizer(Transform):\n    \"\"\"Applies z-score normalization to all the features\"\"\"\n    def setups(self, seqs: TfmdLists):\n        seqs = np.concatenate(list(seqs))\n        self.ss = StandardScaler().fit(seqs)\n\n    def encodes(self, seq: np.ndarray):\n        normed = self.ss.transform(seq).astype(np.float32)\n        return torch.from_numpy(normed)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T23:44:14.908013Z","iopub.execute_input":"2025-09-02T23:44:14.908379Z","iopub.status.idle":"2025-09-02T23:44:14.942451Z","shell.execute_reply.started":"2025-09-02T23:44:14.908324Z","shell.execute_reply":"2025-09-02T23:44:14.940588Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def pad_or_trunc(seq: torch.Tensor, L=128):\n    \"\"\"Equalizes all the sequence lengths by padding or truncating them\"\"\"\n    # truncate by removing alternate timesteps\n    while seq.size(0)>L:\n        trunc_len = seq.size(0)-L\n        truncated = seq[:2*trunc_len:2, :]\n        seq = torch.cat([truncated, seq[2*trunc_len:]])\n    # add padding to the beginning and end of the sequence\n    pad_len = L-seq.size(0)\n    pre_pad = torch.zeros(pad_len//2, seq.size(1))\n    post_pad = torch.zeros((pad_len+1)//2, seq.size(1))\n    return torch.cat([pre_pad,seq,post_pad])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T23:44:14.943920Z","iopub.execute_input":"2025-09-02T23:44:14.944330Z","iopub.status.idle":"2025-09-02T23:44:14.981055Z","shell.execute_reply.started":"2025-09-02T23:44:14.944302Z","shell.execute_reply":"2025-09-02T23:44:14.979341Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class LinearBlock(nn.Sequential):\n    def __init__(self, ni, nf, p=0.0):\n        super().__init__(\n            nn.Linear(ni, nf, bias=False), nn.BatchNorm1d(nf),\n            nn.ReLU(), nn.Dropout(p)\n        )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T23:44:14.982646Z","iopub.execute_input":"2025-09-02T23:44:14.982954Z","iopub.status.idle":"2025-09-02T23:44:15.024427Z","shell.execute_reply.started":"2025-09-02T23:44:14.982931Z","shell.execute_reply":"2025-09-02T23:44:15.022776Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class ConvBlock(nn.Sequential):\n    def __init__(self, ni, nf, ks=3, stride=1, do_relu=True, p=0.0, is_2d=False):\n        conv, bn = (nn.Conv2d, nn.BatchNorm2d) if is_2d else (nn.Conv1d, nn.BatchNorm1d)\n        activation = [nn.ReLU()] if do_relu else []\n        super().__init__(\n            conv(ni, nf, ks, stride=stride, padding=ks//2, bias=False),\n            bn(nf), *activation, nn.Dropout(p)\n        )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T23:44:15.025316Z","iopub.execute_input":"2025-09-02T23:44:15.025606Z","iopub.status.idle":"2025-09-02T23:44:15.061646Z","shell.execute_reply.started":"2025-09-02T23:44:15.025585Z","shell.execute_reply":"2025-09-02T23:44:15.060132Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class SEBlock(nn.Module):\n    def __init__(self, c, r=8):\n        super().__init__()\n        self.squeeze = nn.AdaptiveAvgPool1d(1)\n        self.excitation = nn.Sequential(\n            nn.Linear(c, c//r, bias=False), nn.ReLU(),\n            nn.Linear(c//r, c, bias=False), nn.Sigmoid()\n        )\n\n    def forward(self, x):\n        y = self.squeeze(x).squeeze()\n        y = self.excitation(y).unsqueeze(-1)\n        return x*y","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T23:44:15.062597Z","iopub.execute_input":"2025-09-02T23:44:15.062937Z","iopub.status.idle":"2025-09-02T23:44:15.099464Z","shell.execute_reply.started":"2025-09-02T23:44:15.062907Z","shell.execute_reply":"2025-09-02T23:44:15.098037Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class ResSEBlock(nn.Module):\n    def __init__(self, ni, nf, p=0):\n        super().__init__()\n        self.cnns = nn.Sequential(ConvBlock(ni, nf), ConvBlock(nf, nf, do_relu=False))\n        self.se = SEBlock(nf)\n        self.idconv = noop if ni==nf else ConvBlock(ni, nf, ks=1, do_relu=False)\n        self.drop = nn.Dropout(p)\n\n    def forward(self, x):\n        x_ = self.se(self.cnns(x))\n        return self.drop(F.relu(x_ + self.idconv(x)))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T23:44:15.100882Z","iopub.execute_input":"2025-09-02T23:44:15.101551Z","iopub.status.idle":"2025-09-02T23:44:15.137396Z","shell.execute_reply.started":"2025-09-02T23:44:15.101513Z","shell.execute_reply":"2025-09-02T23:44:15.135143Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class CNNGRU(nn.Module):\n    def __init__(self, n_classes, imu_dim, n_convs=3, conv_dim=64, n_lins=3, lin_dim=256, p=0.1):\n        super().__init__()\n        channels = [imu_dim] + [conv_dim*(2**i) for i in range(n_convs)]\n        self.cnn = nn.Sequential(\n            *[ResSEBlock(ni, nf, p=p) for ni, nf in zip(channels, channels[1:])]\n        )\n        \n        f_dim = channels[-1]\n        self.rnn = nn.GRU(f_dim, f_dim, batch_first=True, bidirectional=True)\n        \n        neurons = [f_dim*2] + [lin_dim//(2**i) for i in range(n_lins-1)]\n        self.head = nn.Sequential(\n            nn.Dropout(p),\n            *[LinearBlock(ni, nf, p=p) for ni, nf in zip(neurons, neurons[1:])],\n            nn.Linear(neurons[-1], n_classes)\n        )\n\n    def forward(self, seqs):\n        # seqs: (bs, seq_len, imu_dim)\n        seqs = seqs.permute(0,2,1)              # (bs, imu_dim, seq_len)\n        f = self.cnn(seqs).permute(0,2,1)       # (bs, seq_len, f_dim)\n        output, _ = self.rnn(f)                 # (bs, seq_len, 2*f_dim)\n        last_step = output[:,-1,:]              # (bs, 2*f_dim)\n        return self.head(last_step)             # (bs, n_classes)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T23:44:15.139119Z","iopub.execute_input":"2025-09-02T23:44:15.139495Z","iopub.status.idle":"2025-09-02T23:44:15.185149Z","shell.execute_reply.started":"2025-09-02T23:44:15.139463Z","shell.execute_reply":"2025-09-02T23:44:15.184116Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class MultiModalModel(nn.Module):\n    def __init__(self, n_classes, imu_dim, thm_dim, tof_dim, f_dim=128, rnn_dim=256, p=0.1):\n        super().__init__()\n        self.imu_enc = nn.Sequential(\n            ResSEBlock(imu_dim, f_dim//2, p=p), ResSEBlock(f_dim//2, f_dim, p=p)\n        )\n        self.thm_enc = nn.Sequential(\n            ConvBlock(thm_dim, f_dim//2, p=p), ConvBlock(f_dim//2, f_dim, p=p)\n        )\n        self.tof_enc = self.cnn = nn.Sequential(\n            ConvBlock(tof_dim, f_dim, p=p), ConvBlock(f_dim, f_dim, p=p)\n        )\n        self.rnn = nn.GRU(3*f_dim, rnn_dim, batch_first=True, bidirectional=True)\n        self.classifier = nn.Sequential(\n            LinearBlock(2*rnn_dim, rnn_dim, p=p), LinearBlock(rnn_dim, rnn_dim//2, p=p),\n            nn.Linear(rnn_dim//2, n_classes)\n        )\n\n    def _process_cnn(self, seq, enc):\n        # seq: (bs, L, -1)\n        seq = seq.permute(0,2,1)                                # (bs, -1, L)\n        return enc(seq).permute(0,2,1)                          # (bs, L, f_dim)\n\n    def forward(self, imus, thms, tofs):\n        imu_f = self._process_cnn(imus, self.imu_enc)            # (bs, L, f_dim)\n        thm_f = self._process_cnn(thms, self.thm_enc)            # (bs, L, f_dim)\n        tof_f = self._process_cnn(tofs, self.tof_enc)            # (bs, L, f_dim)\n\n        merged = torch.concat([imu_f, thm_f, tof_f], dim=-1)     # (bs, L, 3*f_dim)\n        f = self.rnn(merged)[0]                                  # (bs, L, 2*rnn_dim)\n        f = f[:, -1, :]                                          # (bs, 2*rnn_dim)\n        return self.classifier(f)                                # (bs, n_classes)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T23:44:15.186975Z","iopub.execute_input":"2025-09-02T23:44:15.187318Z","iopub.status.idle":"2025-09-02T23:44:15.223700Z","shell.execute_reply.started":"2025-09-02T23:44:15.187292Z","shell.execute_reply":"2025-09-02T23:44:15.222157Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def hierarchical_f1(targs: pd.Series, preds: pd.Series, targ_gestures):\n    # Compute binary F1 (Target vs Non-Target)\n    y_true_bi = targs.isin(targ_gestures).values\n    y_pred_bi = preds.isin(targ_gestures).values\n    f1_binary = f1_score(y_true_bi, y_pred_bi, pos_label=True, zero_division=0, average='binary')\n\n    # Build multi-class labels for gestures\n    y_true_mc = targs.apply(lambda x: x if x in targ_gestures else 'non_target')\n    y_pred_mc = preds.apply(lambda x: x if x in targ_gestures else 'non_target')\n\n    # Compute macro F1 over all gesture classes\n    f1_macro = f1_score(y_true_mc, y_pred_mc, average='macro', zero_division=0)\n\n    return 0.5*f1_binary + 0.5*f1_macro","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T23:44:15.225154Z","iopub.execute_input":"2025-09-02T23:44:15.225892Z","iopub.status.idle":"2025-09-02T23:44:15.271207Z","shell.execute_reply.started":"2025-09-02T23:44:15.225859Z","shell.execute_reply":"2025-09-02T23:44:15.269329Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class HierarchicalF1Metric(Metric):\n    def __init__(self, targ_gestures):\n        self.targ_gestures = targ_gestures\n        self.reset()\n\n    def reset(self): self.preds, self.targs = [], []\n\n    def accumulate(self, learn):\n        _, preds = learn.pred.max(dim=1)\n        self.preds.extend(preds.cpu()); self.targs.extend(learn.y.cpu())\n\n    @property\n    def value(self):\n        preds = pd.Series(self.preds).apply(lbl_enc.decode)\n        targs = pd.Series(self.targs).apply(lbl_enc.decode)\n        return hierarchical_f1(targs, preds, self.targ_gestures)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T23:44:15.272020Z","iopub.execute_input":"2025-09-02T23:44:15.272403Z","iopub.status.idle":"2025-09-02T23:44:15.315941Z","shell.execute_reply.started":"2025-09-02T23:44:15.272337Z","shell.execute_reply":"2025-09-02T23:44:15.314722Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class CustomMixUp(MixHandler):\n    def __init__(self, alpha=0.4):\n        super().__init__(alpha)\n        \n    def before_batch(self):\n        lam = self.distrib.sample((self.y.size(0),)).squeeze().to(self.x[0].device)\n        lam = torch.stack([lam, 1-lam], 1)\n        self.lam = lam.max(1)[0]\n        shuffle = torch.randperm(self.y.size(0)).to(self.x[0].device)\n        xb1,self.yb1 = tuple(L(self.xb).itemgot(shuffle)),tuple(L(self.yb).itemgot(shuffle))\n        nx_dims = len(self.x[0].size())\n        self.learn.xb = tuple(L(xb1,self.xb).map_zip(torch.lerp,weight=unsqueeze(self.lam, n=nx_dims-1)))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T23:44:15.316730Z","iopub.execute_input":"2025-09-02T23:44:15.317094Z","iopub.status.idle":"2025-09-02T23:44:15.350129Z","shell.execute_reply.started":"2025-09-02T23:44:15.317064Z","shell.execute_reply":"2025-09-02T23:44:15.348489Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pad_or_truncate = pad_or_trunc\nuni_learn = load_learner('/kaggle/input/uni-modal-learner/cnn_gru.pkl')\nmulti_learn = load_learner('/kaggle/input/motion-and-proximity-sensor-modelling/multi_modal.pkl')\nlbl_enc = uni_learn.dls.tfms[-1][-1]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T23:44:15.351578Z","iopub.execute_input":"2025-09-02T23:44:15.351890Z","iopub.status.idle":"2025-09-02T23:44:15.686637Z","shell.execute_reply.started":"2025-09-02T23:44:15.351867Z","shell.execute_reply":"2025-09-02T23:44:15.685451Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def predict(test_df, test_demo):\n    test_df = test_df.to_pandas()\n    imu = test_df[imu_cols].to_numpy().tolist()\n    thm = test_df[thm_cols].to_numpy().tolist()\n    tof = test_df[tof_cols].to_numpy().tolist()\n    if test_df[tof_cols[0]].isnull()[0]:\n        _,_,prob = uni_learn.predict(imu)\n    else:\n        seq = pd.Series(dict(imu=imu, thm=thm, tof=tof))\n        _,_,prob = multi_learn.predict(seq)\n    pred = prob.argmax()\n    return lbl_enc.decode(pred.item())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T23:44:15.687877Z","iopub.execute_input":"2025-09-02T23:44:15.688419Z","iopub.status.idle":"2025-09-02T23:44:15.696501Z","shell.execute_reply.started":"2025-09-02T23:44:15.688387Z","shell.execute_reply":"2025-09-02T23:44:15.694816Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport kaggle_evaluation.cmi_inference_server\n\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-02T23:44:15.697698Z","iopub.execute_input":"2025-09-02T23:44:15.697980Z","iopub.status.idle":"2025-09-02T23:44:19.061741Z","shell.execute_reply.started":"2025-09-02T23:44:15.697959Z","shell.execute_reply":"2025-09-02T23:44:19.060050Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}