{"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":"nvidiaTeslaT4","dataSources":[{"sourceId":102335,"databundleVersionId":12518947,"sourceType":"competition"},{"sourceId":443446,"sourceType":"modelInstanceVersion","modelInstanceId":357109,"modelId":378423},{"sourceId":443565,"sourceType":"modelInstanceVersion","modelInstanceId":357109,"modelId":378423},{"sourceId":444180,"sourceType":"modelInstanceVersion","modelInstanceId":357109,"modelId":378423},{"sourceId":444182,"sourceType":"modelInstanceVersion","modelInstanceId":357109,"modelId":378423},{"sourceId":444200,"sourceType":"modelInstanceVersion","modelInstanceId":357109,"modelId":378423},{"sourceId":445055,"sourceType":"modelInstanceVersion","modelInstanceId":357109,"modelId":378423},{"sourceId":446596,"sourceType":"modelInstanceVersion","modelInstanceId":357109,"modelId":378423},{"sourceId":448047,"sourceType":"modelInstanceVersion","modelInstanceId":357109,"modelId":378423},{"sourceId":449175,"sourceType":"modelInstanceVersion","modelInstanceId":357109,"modelId":378423},{"sourceId":449241,"sourceType":"modelInstanceVersion","modelInstanceId":357109,"modelId":378423},{"sourceId":449249,"sourceType":"modelInstanceVersion","modelInstanceId":357109,"modelId":378423},{"sourceId":459530,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":357109,"modelId":378423},{"sourceId":459824,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":357109,"modelId":378423},{"sourceId":464136,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":357109,"modelId":378423},{"sourceId":465428,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":357109,"modelId":378423},{"sourceId":466289,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":357109,"modelId":378423},{"sourceId":466666,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":357109,"modelId":378423},{"sourceId":466975,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":357109,"modelId":378423},{"sourceId":467811,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":357109,"modelId":378423},{"sourceId":468078,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":357109,"modelId":378423},{"sourceId":468145,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":357109,"modelId":378423},{"sourceId":468956,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":357109,"modelId":378423},{"sourceId":469959,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":357109,"modelId":378423},{"sourceId":470439,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":357109,"modelId":378423},{"sourceId":471687,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":357109,"modelId":378423},{"sourceId":471696,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":357109,"modelId":378423},{"sourceId":472043,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":357109,"modelId":378423},{"sourceId":473013,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":357109,"modelId":378423}],"dockerImageVersionId":31041,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import torch\nimport numpy as np\nimport pandas as pd\nimport joblib\nfrom pathlib import Path\nimport polars as pl\nMAX_LEN = 100","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-07-15T08:50:01.268270Z","iopub.execute_input":"2025-07-15T08:50:01.268484Z","iopub.status.idle":"2025-07-15T08:50:09.669012Z","shell.execute_reply.started":"2025-07-15T08:50:01.268457Z","shell.execute_reply":"2025-07-15T08:50:09.668254Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"    \ndef pad_sequences(sequences, maxlen, padding='pre', truncating='pre', value=0.0):\n    result = []\n    for seq in sequences:\n        seq = np.array(seq) \n        \n        if len(seq) >= maxlen:\n            if truncating == 'post':\n                seq = seq[:maxlen]\n            else:\n                seq = seq[-maxlen:]\n        else:\n            pad_len = maxlen - len(seq)\n            if seq.ndim == 1:\n                pad_shape = (pad_len,)\n            else:\n                pad_shape = (pad_len,) + seq.shape[1:]\n            \n            if padding == 'post':\n                seq = np.concatenate([seq, np.full(pad_shape, value)])\n            else: \n                seq = np.concatenate([np.full(pad_shape, value), seq])\n        \n        result.append(seq)\n    return np.array(result, dtype=np.float32)\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T08:50:09.670419Z","iopub.execute_input":"2025-07-15T08:50:09.670800Z","iopub.status.idle":"2025-07-15T08:50:09.676173Z","shell.execute_reply.started":"2025-07-15T08:50:09.670782Z","shell.execute_reply":"2025-07-15T08:50:09.675559Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\n\n\nclass SEBlock1D(nn.Module):\n    def __init__(self, channels, reduction=8):\n        super(SEBlock1D, self).__init__()\n        self.global_pool = nn.AdaptiveAvgPool1d(1)\n        self.fc = nn.Sequential(\n            nn.Linear(channels, channels // reduction, bias=False),\n            nn.ReLU(inplace=True),\n            nn.Linear(channels // reduction, channels, bias=False),\n            nn.Sigmoid()\n        )\n\n    def forward(self, x):\n        b, c, _ = x.size()\n        y = self.global_pool(x).view(b, c)\n        y = self.fc(y).view(b, c, 1)\n        return x * y.expand_as(x)\n\nclass MultiScaleTemporalConv(nn.Module):\n    def __init__(self, in_channels, out_channels):\n        super().__init__()\n        self.conv_1x1 = nn.Conv1d(in_channels, out_channels//4, 1)\n        self.conv_3x1 = nn.Conv1d(in_channels, out_channels//4, 3, padding=1)\n        self.conv_5x1 = nn.Conv1d(in_channels, out_channels//4, 5, padding=2)\n        self.conv_7x1 = nn.Conv1d(in_channels, out_channels//4, 7, padding=3)\n        \n    def forward(self, x):\n        out1 = self.conv_1x1(x)\n        out2 = self.conv_3x1(x)\n        out3 = self.conv_5x1(x)\n        out4 = self.conv_7x1(x)\n        return torch.cat([out1,out2, out3,out4], dim=1)\n\nclass ResidualBlock1D(nn.Module):\n    def __init__(self,\n                 in_channels: int,\n                 out_channels: int,\n                 kernel_size: int,\n                 stride: int = 1,\n                 dilation: int = 1,\n                 groups: int = 1,\n                 dropout: float = 0.0):\n        super().__init__()\n\n        padding = ((kernel_size - 1) // 2) * dilation\n\n        # Main path\n        self.conv1 = nn.Conv1d(in_channels, out_channels,\n                               kernel_size=kernel_size,\n                               stride=stride,\n                               padding=padding,\n                               dilation=dilation,\n                               groups=groups,\n                               bias=False)\n        self.bn1   = nn.BatchNorm1d(out_channels)\n        self.drop  = nn.Dropout(dropout) if dropout > 0 else nn.Identity()\n        self.conv2 = nn.Conv1d(out_channels, out_channels,\n                               kernel_size=kernel_size,\n                               stride=1,\n                               padding=padding,\n                               dilation=dilation,\n                               groups=groups,\n                               bias=False)\n        self.bn2   = nn.BatchNorm1d(out_channels)\n\n        # Skip (identity) path\n        if in_channels != out_channels or stride != 1:\n            # Adjust channels and/or temporal length\n            self.downsample = nn.Sequential(\n                nn.Conv1d(in_channels, out_channels,\n                          kernel_size=1,\n                          stride=stride,\n                          bias=False),\n                nn.BatchNorm1d(out_channels)\n            )\n        else:\n            self.downsample = nn.Identity()\n\n        self.activation = nn.ReLU(inplace=True)\n\n    def forward(self, x: torch.Tensor) -> torch.Tensor:\n        identity = self.downsample(x)\n\n        out = self.conv1(x)\n        out = self.bn1(out)\n        out = self.activation(out)\n        out = self.drop(out)           # optional\n        out = self.conv2(out)\n        out = self.bn2(out)\n\n        out += identity                # residual connection\n        out  = self.activation(out)\n        return out\n\n\n\nclass GestureCNN(nn.Module):\n    def __init__(self, input_dim, num_classes, max_len=100, dropout=0.3,hidden_dims=None):\n        super(GestureCNN, self).__init__()\n        \n        self.input_dim = input_dim\n        self.num_classes = num_classes\n        self.max_len = max_len\n\n        self.dense1 = nn.Linear(input_dim, 300)\n        self.bn_dense = nn.BatchNorm1d(300) \n\n        self.conv_blocks = nn.ModuleList()\n        in_channels = 300\n        if hidden_dims == None:\n            hidden_dims = [64, 128, 192, 256]\n        \n        for i, out_channels in enumerate(hidden_dims):\n            if i == len(hidden_dims) - 1:\n                kernel_size = 3\n            else:\n                kernel_size = 5 if i == 1 else 3\n            layers = []\n            if i == 0:\n                layers.append(MultiScaleTemporalConv(in_channels, out_channels))\n            else:\n                layers.extend( [\n                    ResidualBlock1D(in_channels, out_channels,\n                            kernel_size=kernel_size,\n                            # padding=kernel_size // 2,\n                            # bias=False if i < len(hidden_dims) - 1 else True,\n                            dropout=dropout),\n                    # nn.BatchNorm1d(out_channels) if i < len(hidden_dims) - 1 else nn.Identity(),\n                    # nn.ReLU(),\n                ] )\n\n            # Inject SE block in middle conv layers (e.g., i == 1 or 2)\n            # if 0 < i < len(hidden_dims) - 1:\n            #     layers.append(SEBlock1D(out_channels))\n\n            # Add pooling conditionally\n            if i in [1, 2]:\n                layers.append(nn.MaxPool1d(2))\n            elif i == len(hidden_dims) - 1:\n                layers.append(nn.AdaptiveAvgPool1d(1))\n\n            # dropout_rate = 0.2 + (0.1 * i)\n            layers.append(nn.Dropout(dropout))\n\n            conv_block = nn.Sequential(*layers)\n            self.conv_blocks.append(conv_block)\n            in_channels = out_channels\n\n        # Classifier head\n        self.classifier = nn.Sequential(\n            nn.Linear(hidden_dims[-1], hidden_dims[-1]//2),\n            nn.LeakyReLU(),\n            nn.Dropout(0.4),\n            nn.Linear(hidden_dims[-1]//2, num_classes)\n        )\n        \n    def forward(self, x):\n        # Input shape: (batch_size, seq_len, features)\n        batch_size, seq_len, features = x.shape\n        \n        # Layer 1: Dense layer applied to each time step\n        # Reshape to apply dense layer: (batch_size * seq_len, features)\n        x_reshaped = x.reshape(-1, features)\n        x_dense = torch.relu(self.bn_dense(self.dense1(x_reshaped)))\n        # Reshape back: (batch_size, seq_len, 300)\n        x = x_dense.reshape(batch_size, seq_len, 300)\n        \n        # Input shape: (batch_size, seq_len, features)\n        x = x.transpose(1, 2)  # Convert to (batch_size, features, seq_len)\n        \n        for conv_block in self.conv_blocks:\n            x = conv_block(x)\n        \n        x = x.view(x.size(0), -1)  # Flatten\n        x = self.classifier(x)\n        return x\n\n\n\n# class GestureTHMCNN(nn.Module):\n#     def __init__(self, input_dim, num_classes=18, hidden_dims=None, max_len=100, dropout=0.3):\n#         super(GestureTHMCNN, self).__init__()\n        \n#         self.input_dim = 16\n#         self.num_classes = num_classes\n#         self.max_len = max_len\n#         hidden_dims = [64, 128, 256, 512]\n\n#         # self.temporal_attention = TemporalSelfAttention(input_dim)\n\n#         self.conv_blocks = nn.ModuleList()\n#         in_channels = 16\n        \n#         for i, out_channels in enumerate(hidden_dims):\n#             if i == len(hidden_dims) - 1:\n#                 kernel_size = 3\n#             else:\n#                 kernel_size = 5 if i == 1 else 3\n#             layers = []\n#             if i == 0:\n#                 layers.append(MultiScaleTemporalConv(in_channels, out_channels))\n#             else:\n#                 layers.extend( [\n#                     nn.Conv1d(in_channels, out_channels,\n#                             kernel_size=kernel_size,\n#                             padding=kernel_size // 2,\n#                             bias=False if i < len(hidden_dims) - 1 else True),\n#                     nn.BatchNorm1d(out_channels) if i < len(hidden_dims) - 1 else nn.Identity(),\n#                     nn.GELU() if i == 0 else nn.ReLU(),\n#                 ] )\n\n#             # Inject SE block in middle conv layers (e.g., i == 1 or 2)\n#             if 0 < i < len(hidden_dims) - 1:\n#                 layers.append(SEBlock1D(out_channels))\n\n#             # Add pooling conditionally\n#             if i in [1, 2]:\n#                 layers.append(nn.MaxPool1d(2))\n#             elif i == len(hidden_dims) - 1:\n#                 layers.append(nn.AdaptiveAvgPool1d(1))\n\n#             # dropout_rate = 0.2 + (0.1 * i)\n#             layers.append(nn.Dropout(dropout))\n\n#             conv_block = nn.Sequential(*layers)\n#             self.conv_blocks.append(conv_block)\n#             in_channels = out_channels\n        \n#         # Classifier head\n#         self.classifier = nn.Sequential(\n#             nn.Linear(hidden_dims[-1], hidden_dims[-1]//2),\n#             nn.LeakyReLU(),\n#             nn.Dropout(0.4),\n#             nn.Linear(hidden_dims[-1]//2, num_classes)\n#         )\n        \n#     def forward(self, x):\n#         # Input shape: (batch_size, seq_len, features)\n#         x = x.transpose(1, 2)  # Convert to (batch_size, features, seq_len)\n        \n#         # x = self.temporal_attention(x)\n        \n#         for conv_block in self.conv_blocks:\n#             x = conv_block(x)\n        \n#         x = x.view(x.size(0), -1)  # Flatten\n#         x = self.classifier(x)\n#         return x\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T08:50:58.458828Z","iopub.execute_input":"2025-07-15T08:50:58.459417Z","iopub.status.idle":"2025-07-15T08:50:58.476867Z","shell.execute_reply.started":"2025-07-15T08:50:58.459392Z","shell.execute_reply":"2025-07-15T08:50:58.476335Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\nclass ModifiedGatedTwoBranchCNN(nn.Module):\n    def __init__(self, input_dim, num_classes, max_len=100, dropout=0.3, checkpoint_path=None, hidden_dims=[48, 96, 192,256]):\n        super(ModifiedGatedTwoBranchCNN, self).__init__()\n        \n        self.input_dim = input_dim\n        self.num_classes = num_classes\n        self.imu_dim = input_dim - 5\n        self.tof_dim = 5\n        self.hidden_dims = hidden_dims \n\n        # TOF dense layer (trainable from scratch)\n        self.tof_dense = nn.Linear(self.tof_dim, 150)\n        self.tof_bn = nn.BatchNorm1d(150)\n        \n        # IMU feature extractor: Use GestureCNN without classifier\n        gesture_cnn = GestureCNN(\n            input_dim=self.imu_dim,\n            num_classes=num_classes,\n            hidden_dims=self.hidden_dims,\n            max_len=max_len,\n            dropout=dropout\n        )\n        \n        self.imu_dense1 = gesture_cnn.dense1\n        self.imu_bn_dense = gesture_cnn.bn_dense\n        self.imu_conv_blocks = gesture_cnn.conv_blocks\n        \n        # TOF branch (trainable from scratch)\n        self.tof_branch = nn.Sequential(\n            MultiScaleTemporalConv(150, 48),\n            ResidualBlock1D(48, 96, kernel_size=3, dropout=dropout),\n            nn.MaxPool1d(2),\n            nn.Dropout(0.3),\n            ResidualBlock1D(96, 192, kernel_size=5, dropout=dropout),\n            nn.MaxPool1d(2),\n            nn.Dropout(0.3),\n        )\n        \n        # Auxiliary unimodal networks\n        self.aux_imu = nn.Sequential(\n            nn.AdaptiveAvgPool1d(1),\n            nn.Flatten(),\n            nn.Linear(self.hidden_dims[-1], 64),\n            nn.ReLU(),\n            nn.Dropout(0.5),\n            nn.Linear(64, num_classes)\n        )\n        \n        self.aux_tof = nn.Sequential(\n            nn.AdaptiveAvgPool1d(1),\n            nn.Flatten(),\n            nn.Linear(192, 64),  # Assuming 192 for TOF\n            nn.ReLU(),\n            nn.Dropout(0.5),\n            nn.Linear(64, num_classes)\n        )\n        \n        # Fusion and main classifier (unchanged)\n        self.fusion_weight_net = nn.Sequential(\n            nn.Linear(self.hidden_dims[-1]+192, 128),  # Adjust for hidden_dims[-1]\n            nn.ReLU(),\n            nn.Dropout(0.4),\n            nn.Linear(128, 2),\n            nn.Softmax(dim=1)\n        )\n        \n        self.global_pool = nn.AdaptiveAvgPool1d(1)\n        \n        self.classifier = nn.Sequential(\n            nn.Linear(self.hidden_dims[-1]+192, 128),\n            nn.BatchNorm1d(128),\n            nn.ReLU(),\n            nn.Dropout(0.4),\n            nn.Linear(128, num_classes)\n        )\n        \n    def forward(self, x):\n        x = x.transpose(1, 2)  # (batch, input_dim, seq_len)\n        imu = x[:, :self.imu_dim, :]\n        tof = x[:, self.imu_dim:, :]\n\n        # Process IMU with feature extractor (no classifier)\n        batch_size, _, seq_len = imu.shape\n        imu = imu.transpose(1, 2)  # (batch, seq_len, imu_dim)\n        imu_reshaped = imu.reshape(-1, self.imu_dim)\n        imu_dense = F.relu(self.imu_bn_dense(self.imu_dense1(imu_reshaped)))\n        imu = imu_dense.reshape(batch_size, seq_len, 300)\n        imu = imu.transpose(1, 2)  # (batch, 300, seq_len)\n        \n        for conv_block in self.imu_conv_blocks:\n            imu = conv_block(imu)\n        \n        imu_features = imu  \n        \n        # Process TOF\n        tof = tof.transpose(1, 2)  # (batch, seq_len, tof_dim)\n        tof_reshaped = tof.reshape(-1, self.tof_dim)\n        tof_dense = F.relu(self.tof_bn(self.tof_dense(tof_reshaped)))\n        tof = tof_dense.reshape(batch_size, seq_len, 150)\n        tof = tof.transpose(1, 2)  # (batch, 150, seq_len)\n        tof_features = self.tof_branch(tof)\n        \n        # Global pooling\n        imu_pooled = self.global_pool(imu_features).flatten(1)\n        tof_pooled = self.global_pool(tof_features).flatten(1)\n        \n        # Auxiliary outputs\n        aux_imu_out = self.aux_imu(imu_features)\n        aux_tof_out = self.aux_tof(tof_features)\n        \n        # Fusion\n        concatenated = torch.cat([imu_pooled, tof_pooled], dim=1)\n        fusion_weights = self.fusion_weight_net(concatenated)\n        \n        weighted_imu = imu_pooled * fusion_weights[:, 0:1]\n        weighted_tof = tof_pooled * fusion_weights[:, 1:2]\n        \n        fused_features = torch.cat([weighted_imu, weighted_tof], dim=1)\n        main_out = self.classifier(fused_features)\n        \n        return main_out, aux_imu_out, aux_tof_out, fusion_weights\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T08:52:51.816557Z","iopub.execute_input":"2025-07-15T08:52:51.817191Z","iopub.status.idle":"2025-07-15T08:52:51.829572Z","shell.execute_reply.started":"2025-07-15T08:52:51.817157Z","shell.execute_reply":"2025-07-15T08:52:51.828758Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\nclass GestureTHMCNN(nn.Module):\n    def __init__(self, input_dim, num_classes, hidden_dims=None, max_len=100, dropout=0.3):\n        super(GestureTHMCNN, self).__init__()\n        \n        self.input_dim = input_dim\n        self.non_thm_dim = input_dim - 5\n        self.thm_dim = 5\n        self.num_classes = num_classes\n        self.max_len = max_len\n        hidden_dims = [48, 96, 192, 256]\n\n        # Main branch (non-THM features)\n        self.main_dense1 = nn.Linear(self.non_thm_dim, 300)\n        self.main_bn_dense = nn.BatchNorm1d(300)\n\n        self.main_conv_blocks = nn.ModuleList()\n        main_in_channels = 300\n        \n        for i, out_channels in enumerate(hidden_dims):\n            if i == len(hidden_dims) - 1:\n                kernel_size = 3\n            else:\n                kernel_size = 5 if i == 1 else 3\n            layers = []\n            if i == 0:\n                layers.append(MultiScaleTemporalConv(main_in_channels, out_channels))\n            else:\n                layers.extend([\n                    ResidualBlock1D(main_in_channels, out_channels,\n                                    kernel_size=kernel_size,\n                                    dropout=dropout),\n                ])\n\n            # Add pooling conditionally\n            if i in [1, 2]:\n                layers.append(nn.MaxPool1d(2))\n            elif i == len(hidden_dims) - 1:\n                layers.append(nn.AdaptiveAvgPool1d(1))\n\n            layers.append(nn.Dropout(dropout))\n\n            conv_block = nn.Sequential(*layers)\n            self.main_conv_blocks.append(conv_block)\n            main_in_channels = out_channels\n\n        # THM branch (THM features, different structure)\n        self.thm_dense1 = nn.Linear(self.thm_dim, 100) if self.thm_dim > 0 else None\n        self.thm_bn_dense = nn.BatchNorm1d(100) if self.thm_dim > 0 else None\n        \n        self.thm_conv_blocks = nn.ModuleList()\n        thm_in_channels = 100\n        thm_hidden_dims = [32, 64, 128]  # Different (lighter) than main\n        \n        for i, out_channels in enumerate(thm_hidden_dims):\n            kernel_size = 3  # Simpler kernels for THM\n            layers = []\n            if i == 0:\n                layers.append(MultiScaleTemporalConv(thm_in_channels, out_channels))\n            else:\n                layers.extend([\n                    ResidualBlock1D(thm_in_channels, out_channels,\n                                    kernel_size=kernel_size,\n                                    dropout=dropout),\n                ])\n\n            # Different pooling: fewer pools\n            if i in [1]:\n                layers.append(nn.MaxPool1d(2))\n            elif i == len(thm_hidden_dims) - 1:\n                layers.append(nn.AdaptiveAvgPool1d(1))\n\n            layers.append(nn.Dropout(dropout))\n\n            conv_block = nn.Sequential(*layers)\n            self.thm_conv_blocks.append(conv_block)\n            thm_in_channels = out_channels\n\n        # Classifier head with merged dimensions\n        main_dim = hidden_dims[-1]\n        thm_dim_final = thm_hidden_dims[-1] if self.thm_dim > 0 else 0\n        merged_dim = main_dim + thm_dim_final\n        self.classifier = nn.Sequential(\n            nn.Linear(merged_dim, merged_dim // 2),\n            nn.ReLU(),\n            nn.Dropout(0.4),\n            nn.Linear(merged_dim // 2, num_classes)\n        )\n        \n    def forward(self, x):\n        # Input: (batch_size, seq_len, total_features)\n        batch_size, seq_len, total_features = x.shape\n        \n        # Internal split\n        main_x = x[:, :, :self.non_thm_dim]\n        thm_x = x[:, :, self.non_thm_dim:] if self.thm_dim > 0 else None\n\n        # Main branch processing\n        main_reshaped = main_x.reshape(-1, self.non_thm_dim)\n        main_dense = torch.relu(self.main_bn_dense(self.main_dense1(main_reshaped)))\n        main_x = main_dense.reshape(batch_size, seq_len, 300)\n        main_x = main_x.transpose(1, 2)  # (batch_size, 300, seq_len)\n        \n        for conv_block in self.main_conv_blocks:\n            main_x = conv_block(main_x)\n        \n        main_x = main_x.view(batch_size, -1)  # Flatten\n\n        # THM branch processing\n        if self.thm_dim > 0:\n            thm_reshaped = thm_x.reshape(-1, self.thm_dim)\n            thm_dense = torch.relu(self.thm_bn_dense(self.thm_dense1(thm_reshaped)))\n            thm_x = thm_dense.reshape(batch_size, seq_len, 100)\n            thm_x = thm_x.transpose(1, 2)  # (batch_size, 100, seq_len)\n            \n            for conv_block in self.thm_conv_blocks:\n                thm_x = conv_block(thm_x)\n            \n            thm_x = thm_x.view(batch_size, -1)  # Flatten\n        else:\n            thm_x = torch.empty(batch_size, 0, device=x.device)  # Empty if no THM\n\n        # Concatenate and classify\n        merged = torch.cat((main_x, thm_x), dim=1)\n        out = self.classifier(merged)\n        return out\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T08:52:57.099621Z","iopub.execute_input":"2025-07-15T08:52:57.100468Z","iopub.status.idle":"2025-07-15T08:52:57.113633Z","shell.execute_reply.started":"2025-07-15T08:52:57.100426Z","shell.execute_reply":"2025-07-15T08:52:57.112888Z"}},"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":"import pytorch_lightning as pl_\n\nclass GestureLightningModule(pl_.LightningModule):\n    def __init__(self, input_dim, num_classes=18, max_len=100, dropout=0.3):\n        super().__init__()\n        self.model = GestureCNN(input_dim, num_classes)\n    \n    def forward(self, x):\n        return self.model(x)\n\nclass GestureTHMLightningModule(pl_.LightningModule):\n    def __init__(self, input_dim, num_classes=18, max_len=100, dropout=0.3):\n        super().__init__()\n        self.model = ModifiedGatedTwoBranchCNN(input_dim, num_classes, max_len, dropout)\n    \n    def forward(self, x):\n        return self.model(x)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T08:53:42.152306Z","iopub.execute_input":"2025-07-15T08:53:42.152559Z","iopub.status.idle":"2025-07-15T08:53:55.396587Z","shell.execute_reply.started":"2025-07-15T08:53:42.152542Z","shell.execute_reply":"2025-07-15T08:53:55.396018Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from pathlib import Path\nimport torch\nfrom dataclasses import dataclass\nimport sys\nfrom types import ModuleType\n\n# Define your config classes exactly as they were during training\n@dataclass\nclass DataConfig:\n    raw_dir: str = \"./input\"\n    max_len: int = 100\n    batch_size: int = 64\n    num_workers: int = 8\n    mixup_alpha: float = 0.4\n    pin_memory: bool = True\n    prefetch_factor: int = 2\n\n@dataclass\nclass ModelConfig:\n    input_dim: int = 11\n    num_classes: int = 20\n    dropout: float = 0.3\n    hidden_dims: list = None\n    \n    def __post_init__(self):\n        if self.hidden_dims is None:\n            self.hidden_dims = [48, 96, 192, 256]\n\n@dataclass\nclass TrainingConfig:\n    lr: float = 5e-4\n    weight_decay: float = 2e-3\n    epochs: int = 350\n    patience: int = 40\n    label_smoothing: float = 0.1\n    gradient_clip_val: float = None\n    accumulate_grad_batches: int = 1\n\n@dataclass\nclass ExperimentConfig:\n    seed: int = 1408\n    n_folds: int = 5\n    export_dir: str = \"./models\"\n    project_name: str = \"gesture_recognition\"\n    experiment_name: str = \"cnn_mixup\"\n    \n    data: DataConfig = None\n    model: ModelConfig = None\n    training: TrainingConfig = None\n    \n    def __post_init__(self):\n        if self.data is None:\n            self.data = DataConfig()\n        if self.model is None:\n            self.model = ModelConfig()\n        if self.training is None:\n            self.training = TrainingConfig()\n\n# Register config module\nconfig_module = ModuleType('config')\nconfig_module.DataConfig = DataConfig\nconfig_module.ModelConfig = ModelConfig\nconfig_module.TrainingConfig = TrainingConfig\nconfig_module.ExperimentConfig = ExperimentConfig\nsys.modules['config'] = config_module\n\ndef load_lightning_checkpoint(model_path, device):\n    \"\"\"Load model from Lightning checkpoint\"\"\"\n    try:\n        model_ = GestureCNN(input_dim=15, num_classes=num_classes)\n        model = GestureLightningModule.load_from_checkpoint(\n            str(model_path),\n            input_dim = 15,\n            strict=False,\n            \n        )\n        model.to(device)\n        model.eval()\n        \n        print(f\"✓ Loaded Lightning model from {model_path.name}\")\n        return model.model  # Return the inner GestureCNN model\n        \n    except Exception as e:\n        print(f\"❌ Failed to load {model_path.name}: {e}\")\n        return None\n\n# Loading code\n# Define base export path\nEXPORT_ROOT = Path(\"/kaggle/input/cmi-1dcnn/pytorch/default/26\")\nscaler = joblib.load(EXPORT_ROOT / \"scaler.pkl\")\ngesture_classes = np.load(EXPORT_ROOT / \"gesture_classes.npy\", allow_pickle=True)\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\ninput_dim = scaler.n_features_in_\nnum_classes = len(gesture_classes)\nprint(input_dim)\n\nfold_models = []\nfor fold in [0,2,3,4,1]:\n    path = f\"imu_fold_{fold}.ckpt\"\n    model_path = EXPORT_ROOT / path\n    model = load_lightning_checkpoint(model_path, device)\n    if model is not None:\n        fold_models.append(model)\n\nprint(f\"✓ Successfully loaded {len(fold_models)} models\")\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T08:54:00.195817Z","iopub.execute_input":"2025-07-15T08:54:00.196304Z","iopub.status.idle":"2025-07-15T08:54:01.655625Z","shell.execute_reply.started":"2025-07-15T08:54:00.196281Z","shell.execute_reply":"2025-07-15T08:54:01.655040Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from pathlib import Path\nimport torch\nfrom dataclasses import dataclass\nimport sys\nfrom types import ModuleType\n\n# Loading code\n# Define base export path\nEXPORT_ROOT = Path(\"/kaggle/input/cmi-1dcnn/pytorch/default/32\")\nscaler_all = joblib.load(\"/kaggle/input/cmi-1dcnn/pytorch/default/32/scaler.pkl\")\ngesture_classes_all = np.load(EXPORT_ROOT / \"gesture_classes.npy\", allow_pickle=True)\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\ninput_dim = scaler_all.n_features_in_\nnum_classes = len(gesture_classes_all)\nprint(input_dim)\n\n# Define your config classes exactly as they were during training\n@dataclass\nclass DataConfig:\n    raw_dir: str = \"./input\"\n    max_len: int = 100\n    batch_size: int = 64\n    num_workers: int = 8\n    mixup_alpha: float = 0.4\n    pin_memory: bool = True\n    prefetch_factor: int = 2\n\n@dataclass\nclass ModelConfig:\n    input_dim: int = 11\n    num_classes: int = 20\n    dropout: float = 0.3\n    hidden_dims: list = None\n    \n    def __post_init__(self):\n        if self.hidden_dims is None:\n            self.hidden_dims = [64, 128, 256, 512]\n\n@dataclass\nclass TrainingConfig:\n    lr: float = 5e-4\n    weight_decay: float = 2e-3\n    epochs: int = 350\n    patience: int = 40\n    label_smoothing: float = 0.1\n    gradient_clip_val: float = None\n    accumulate_grad_batches: int = 1\n\n@dataclass\nclass ExperimentConfig:\n    seed: int = 1408\n    n_folds: int = 5\n    export_dir: str = \"./models\"\n    project_name: str = \"gesture_recognition\"\n    experiment_name: str = \"cnn_mixup\"\n    \n    data: DataConfig = None\n    model: ModelConfig = None\n    training: TrainingConfig = None\n    \n    def __post_init__(self):\n        if self.data is None:\n            self.data = DataConfig()\n        if self.model is None:\n            self.model = ModelConfig()\n        if self.training is None:\n            self.training = TrainingConfig()\n\n# Register config module\nconfig_module = ModuleType('config')\nconfig_module.DataConfig = DataConfig\nconfig_module.ModelConfig = ModelConfig\nconfig_module.TrainingConfig = TrainingConfig\nconfig_module.ExperimentConfig = ExperimentConfig\nsys.modules['config'] = config_module\n\ndef load_lightning_checkpoint(model_path, device):\n    \"\"\"Load model from Lightning checkpoint\"\"\"\n    try:\n        model_ = ModifiedGatedTwoBranchCNN(input_dim=input_dim, num_classes=num_classes)\n        model = GestureTHMLightningModule.load_from_checkpoint(\n            str(model_path),\n            input_dim = input_dim,\n            strict=False,\n            \n        )\n        model.to(device)\n        model.eval()\n        \n        print(f\"✓ Loaded Lightning model from {model_path.name}\")\n        return model.model  # Return the inner GestureCNN model\n        \n    except Exception as e:\n        print(f\"❌ Failed to load {model_path.name}: {e}\")\n        return None\n\n\n\nthm_fold_models = []\nfor fold in [0,1,2,3,4]:\n    path = f\"thm_imu_fold_{fold}.ckpt\"\n    model_path = EXPORT_ROOT / path\n    model = load_lightning_checkpoint(model_path, device)\n    if model is not None:\n        thm_fold_models.append(model)\n\nprint(f\"✓ Successfully loaded {len(thm_fold_models)} models\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T08:54:31.438707Z","iopub.execute_input":"2025-07-15T08:54:31.439450Z","iopub.status.idle":"2025-07-15T08:54:32.951818Z","shell.execute_reply.started":"2025-07-15T08:54:31.439425Z","shell.execute_reply":"2025-07-15T08:54:32.951068Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# def load_trained_model(model_path, scaler_path, classes_path, device):\n#     \"\"\"Load trained model and preprocessing components\"\"\"\n    \n#     # Load model checkpoint\n#     checkpoint = torch.load(model_path, map_location=device)\n    \n#     # Load preprocessing components\n#     scaler = joblib.load(scaler_path)\n#     gesture_classes = np.load(classes_path,allow_pickle=True)\n    \n#     # Initialize model architecture (same as training)\n#     input_dim = scaler.n_features_in_  # Get from fitted scaler\n#     num_classes = 18\n    \n#     model = GestureCNN(input_dim=input_dim, num_classes=num_classes)\n#     model.load_state_dict(checkpoint['model_state_dict'])\n#     model.to(device)\n#     model.eval()\n    \n#     print(f\"✓ Model loaded from {model_path}\")\n#     print(f\"✓ Model trained for {len(gesture_classes)} gesture classes\")\n    \n#     return model, scaler, gesture_classes\n\n# EXPORT_DIR = Path(\"/kaggle/input/cmi-1dcnn/pytorch/default/10\")\n# device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n# model, scaler, gesture_classes = load_trained_model(\n#     model_path=EXPORT_DIR / 'best_gesture_model.pth',\n#     scaler_path=EXPORT_DIR / 'scaler.pkl', \n#     classes_path=EXPORT_DIR / 'gesture_classes.npy',\n#     device=device\n# )\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T08:54:36.015141Z","iopub.execute_input":"2025-07-15T08:54:36.015415Z","iopub.status.idle":"2025-07-15T08:54:36.019765Z","shell.execute_reply.started":"2025-07-15T08:54:36.015397Z","shell.execute_reply":"2025-07-15T08:54:36.018946Z"}},"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":"def preprocess_sequence(df_seq: pd.DataFrame, feature_cols: list, scaler):\n    mat = df_seq[feature_cols].ffill().bfill().fillna(0).values\n    mat = scaler.transform(mat)\n    return mat.astype('float32')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T08:54:36.791409Z","iopub.execute_input":"2025-07-15T08:54:36.791959Z","iopub.status.idle":"2025-07-15T08:54:36.795647Z","shell.execute_reply.started":"2025-07-15T08:54:36.791937Z","shell.execute_reply":"2025-07-15T08:54:36.794952Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import polars as pl\nfrom collections import Counter\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T08:54:36.981274Z","iopub.execute_input":"2025-07-15T08:54:36.981479Z","iopub.status.idle":"2025-07-15T08:54:36.984620Z","shell.execute_reply.started":"2025-07-15T08:54:36.981462Z","shell.execute_reply":"2025-07-15T08:54:36.984110Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def _imu_features(df):\n    df = df.with_columns([\n    (\n            (pl.col(\"acc_x\")**2 + pl.col(\"acc_y\")**2 + pl.col(\"acc_z\")**2).sqrt()\n            .fill_null(0)\n        ).alias(\"acc_mag\"),\n        (2 * pl.col(\"rot_w\").clip(-1, 1).arccos()).alias(\"rot_angle\")\n    ])\n    \n    df = df.with_columns([\n        pl.col(\"acc_mag\").diff().over(\"sequence_id\").fill_null(0).alias(\"acc_mag_jerk\"),\n        pl.col(\"rot_angle\").diff().over(\"sequence_id\").fill_null(0).alias(\"rot_angle_vel\")\n    ])\n    return df\n\n\ndef calculate_angular_velocity_from_quat(df, time_delta=1/200): # Assuming 200Hz sampling rate\n    from scipy.spatial.transform import Rotation as R\n\n    quat_values = df[['rot_x', 'rot_y', 'rot_z', 'rot_w']].values\n    n_samples   = quat_values.shape[0]\n    angular_vel = np.zeros((n_samples, 3))\n\n    for i in range(n_samples - 1):\n        q_t = quat_values[i]\n        q_t_plus_dt = quat_values[i+1]\n\n        if np.all(np.isnan(q_t)) or np.all(np.isclose(q_t, 0)) or \\\n        np.all(np.isnan(q_t_plus_dt)) or np.all(np.isclose(q_t_plus_dt, 0)):\n            continue\n\n        try:\n            rot_t = R.from_quat(q_t)\n            rot_t_plus_dt = R.from_quat(q_t_plus_dt)\n\n            # Calculate the relative rotation\n            delta_rot = rot_t.inv() * rot_t_plus_dt\n            \n            # Convert delta rotation to angular velocity vector\n            # The rotation vector (Euler axis * angle) scaled by 1/dt\n            # is a good approximation for small delta_rot\n            angular_vel[i, :] = delta_rot.as_rotvec() / time_delta\n        except ValueError:\n            # If quaternion is invalid, angular velocity remains zero\n            pass\n            \n    return angular_vel\n\ndef calculate_angular_distance(df):\n    \"\"\"\n    Calculates a scalar angular distance for each sample.\n    \"\"\"\n    from scipy.spatial.transform import Rotation as R\n    quat_values  = df[['rot_x', 'rot_y', 'rot_z', 'rot_w']].values\n    n_samples    = quat_values.shape[0]\n    angular_dist = np.zeros(n_samples)\n\n    for i in range(n_samples - 1):\n        q1, q2 = quat_values[i], quat_values[i+1]\n\n        if np.all(np.isnan(q1)) or np.all(np.isclose(q1, 0)) or \\\n        np.all(np.isnan(q2)) or np.all(np.isclose(q2, 0)):\n            angular_dist[i] = 0 \n            continue\n        try:\n            r1 = R.from_quat(q1)\n            r2 = R.from_quat(q2)\n            relative_rotation = r1.inv() * r2\n            angle = np.linalg.norm(relative_rotation.as_rotvec())\n            angular_dist[i] = angle\n        except ValueError:\n            angular_dist[i] = 0 \n            pass\n            \n    return angular_dist\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T08:54:37.449396Z","iopub.execute_input":"2025-07-15T08:54:37.449837Z","iopub.status.idle":"2025-07-15T08:54:37.459335Z","shell.execute_reply.started":"2025-07-15T08:54:37.449817Z","shell.execute_reply":"2025-07-15T08:54:37.458591Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\nacc_cols = [f'acc_{axis}' for axis in ['x', 'y', 'z']]\nlinear_acc_cols = [f'linear_acc_{axis}' for axis in ['x', 'y', 'z']]\nworld_acc_cols = [f'world_acc_{axis}' for axis in ['x', 'y', 'z']]\nrot_cols = [f'rot_{axis}' for axis in ['w', 'x', 'y', 'z']]\nthm_cols = [f'thm_{i}' for i in range(1, 5)]\ntof_cols = [f'tof_{i}_v{j}' for i in range(1, 6) for j in range(64)]\n\nimu_cols = linear_acc_cols + rot_cols\nimu_cols = [f'linear_acc_{axis}' for axis in ['x', 'y', 'z']] + \\\n                   [f'rot_{axis}' for axis in ['w', 'x', 'y', 'z']] + ['ang_dist','acc_mag', 'rot_angle', 'acc_mag_jerk', 'rot_angle_vel','ang_vel_x', 'ang_vel_y', 'ang_vel_z']\nimu_cols_thm = linear_acc_cols + rot_cols + ['ang_dist','acc_mag', 'rot_angle', 'acc_mag_jerk', 'rot_angle_vel','ang_vel_x', 'ang_vel_y', 'ang_vel_z'] + thm_cols\n\ndef predict(sequence: pl.DataFrame, demographics: pl.DataFrame) -> str:\n    global scaler, model, device,gesture_classes\n    \n    # has_thm_data = not sequence.select(thm_cols).null_count().row(0).values().count(sequence.height) == len(thm_cols)\n    \n    thm_nulls = sequence.select(thm_cols).null_count().row(0)\n    thm_non_null_ratio = [\n        1.0 - (null_count / sequence.height) for null_count in thm_nulls\n    ]\n    # Check if all thermal columns have more than 85% non-null data\n    has_thm_data = all(ratio > 0.85 for ratio in thm_non_null_ratio)\n\n    sequence = sequence.with_columns([\n            # Compute gravity components\n            (2.0 * (pl.col('rot_x') * pl.col('rot_z') - pl.col('rot_w') * pl.col('rot_y'))).alias('gx'),\n            (2.0 * (pl.col('rot_w') * pl.col('rot_x') + pl.col('rot_y') * pl.col('rot_z'))).alias('gy'),\n            (pl.col('rot_w')**2 - pl.col('rot_x')**2 - pl.col('rot_y')**2 + pl.col('rot_z')**2).alias('gz')\n        ]).with_columns([\n            # Compute norm and scale\n            (pl.col('gx')**2 + pl.col('gy')**2 + pl.col('gz')**2).sqrt().alias('norm')\n        ]).with_columns([\n            # Apply scaling (avoid division by zero)\n            pl.when(pl.col('norm') < 1e-6)\n            .then(0.0)\n            .otherwise(9.81 / pl.col('norm'))\n            .alias('scale')\n        ]).with_columns([\n            # Scale gravity components\n            (pl.col('gx') * pl.col('scale')).alias('gravity_x'),\n            (pl.col('gy') * pl.col('scale')).alias('gravity_y'),\n            (pl.col('gz') * pl.col('scale')).alias('gravity_z')\n        ]).with_columns([\n            # Remove gravity from accelerometer data\n            (pl.col('acc_x') - pl.col('gravity_x')).alias('linear_acc_x'),\n            (pl.col('acc_y') - pl.col('gravity_y')).alias('linear_acc_y'),\n            (pl.col('acc_z') - pl.col('gravity_z')).alias('linear_acc_z')\n        ]).drop(['gx', 'gy', 'gz', 'norm', 'scale', 'gravity_x', 'gravity_y', 'gravity_z'])\n    sequence = _imu_features(sequence)\n    df_seq = sequence.to_pandas()\n    df_seq[[\"ang_vel_x\", \"ang_vel_y\", \"ang_vel_z\"]] = calculate_angular_velocity_from_quat(df_seq) \n    df_seq[\"ang_dist\"] = calculate_angular_distance(df_seq)\n\n    if not has_thm_data:\n        print(\"IMU ONLY\")\n        processed = preprocess_sequence(df_seq, imu_cols,scaler)\n        padded_sequence = pad_sequences([processed], maxlen=100, \n                                      padding='pre', truncating='pre')\n        input_tensor = torch.tensor(padded_sequence, dtype=torch.float32).to(device)\n    \n        preds = []\n        for model in fold_models:\n            model.eval()\n            with torch.no_grad():\n                logits = model(input_tensor)\n                pred_idx = int(torch.argmax(logits, dim=1).cpu().item())\n                preds.append(pred_idx)\n        final_idx = Counter(preds).most_common(1)[0][0]\n        return str(gesture_classes[final_idx])\n    else:\n        print(\"THM USED\")\n        processed = preprocess_sequence(df_seq, imu_cols_thm,scaler_all)\n        padded_sequence = pad_sequences([processed], maxlen=100, \n                                      padding='pre', truncating='pre')\n        input_tensor = torch.tensor(padded_sequence, dtype=torch.float32).to(device)\n        preds = []\n        for model in thm_fold_models:\n            model.eval()\n            with torch.no_grad():\n                logits,_,_,_ = model(input_tensor)\n                pred_idx = int(torch.argmax(logits, dim=1).cpu().item())\n                preds.append(pred_idx)\n        final_idx = Counter(preds).most_common(1)[0][0]\n        return str(gesture_classes_all[final_idx])\n                \n\n    # Majority vote\n    \n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T08:54:41.603801Z","iopub.execute_input":"2025-07-15T08:54:41.604278Z","iopub.status.idle":"2025-07-15T08:54:41.618647Z","shell.execute_reply.started":"2025-07-15T08:54:41.604256Z","shell.execute_reply":"2025-07-15T08:54:41.617998Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# sequence_id\tgesture\n# str\tstr\n# \"SEQ_000011\"\t\"Eyelash - pull hair\"\n# \"SEQ_000001\"\t\"Eyebrow - pull hair\"\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T08:54:41.997570Z","iopub.execute_input":"2025-07-15T08:54:41.997770Z","iopub.status.idle":"2025-07-15T08:54:42.001033Z","shell.execute_reply.started":"2025-07-15T08:54:41.997755Z","shell.execute_reply":"2025-07-15T08:54:42.000327Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Kaggle competition interface\nimport kaggle_evaluation.cmi_inference_server\nimport os\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-07-15T08:54:43.469353Z","iopub.execute_input":"2025-07-15T08:54:43.470113Z","iopub.status.idle":"2025-07-15T08:54:47.730204Z","shell.execute_reply.started":"2025-07-15T08:54:43.470081Z","shell.execute_reply":"2025-07-15T08:54:47.729567Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# pl.read_parquet(\"/kaggle/working/submission.parquet\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-15T08:54:48.811411Z","iopub.execute_input":"2025-07-15T08:54:48.812034Z","iopub.status.idle":"2025-07-15T08:54:48.857247Z","shell.execute_reply.started":"2025-07-15T08:54:48.812010Z","shell.execute_reply":"2025-07-15T08:54:48.856689Z"}},"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}]}