{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"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":"gpu","dataSources":[{"sourceId":102335,"databundleVersionId":12518947,"sourceType":"competition"},{"sourceId":242954653,"sourceType":"kernelVersion"},{"sourceId":556821,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":423454,"modelId":440989},{"sourceId":556834,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":423460,"modelId":440989},{"sourceId":556871,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":423481,"modelId":441015}],"dockerImageVersionId":31041,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import Dataset, DataLoader, Subset, random_split\nimport pandas as pd\nimport numpy as np\nfrom sklearn.preprocessing import StandardScaler, LabelEncoder\nimport os\nfrom pathlib import Path\nimport matplotlib.pyplot as plt\nfrom torch.utils.tensorboard import SummaryWriter\nfrom datetime import datetime\nfrom sklearn.metrics import precision_score, recall_score, f1_score, confusion_matrix, precision_recall_curve\nimport io\nfrom PIL import Image\nimport polars as pl\n\nimport kaggle_evaluation.cmi_inference_server\nimport torch.nn.functional as F","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:29:43.457945Z","iopub.execute_input":"2025-09-02T20:29:43.458195Z","iopub.status.idle":"2025-09-02T20:30:02.514899Z","shell.execute_reply.started":"2025-09-02T20:29:43.458170Z","shell.execute_reply":"2025-09-02T20:30:02.514294Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\nprint(f\"Using {device} device\")\nif device == \"cuda\":\n    print(f\"GPU: {torch.cuda.get_device_name()}\")\n\ndevice = \"cuda\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:30:02.516118Z","iopub.execute_input":"2025-09-02T20:30:02.516699Z","iopub.status.idle":"2025-09-02T20:30:02.612573Z","shell.execute_reply.started":"2025-09-02T20:30:02.516678Z","shell.execute_reply":"2025-09-02T20:30:02.611903Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Data loading","metadata":{}},{"cell_type":"code","source":"training_data_path = Path(\"data/CMI/train.csv\")\ntraining_data_demo_path = Path(\"data/CMI/train_demographics.csv\")\n\ntest_data_path = Path(\"data/CMI/test.csv\")\ntest_data_demo_path = Path(\"data/CMI/test_demographics.csv\")\n\n\ntrain_df = pd.read_csv('/kaggle/input/cmi-detect-behavior-with-sensor-data/train.csv')\ntrain_demo_df = pd.read_csv('/kaggle/input/cmi-detect-behavior-with-sensor-data/train_demographics.csv')\ntest_df = pd.read_csv('/kaggle/input/cmi-detect-behavior-with-sensor-data/test.csv')\ntest_demo_df = pd.read_csv('/kaggle/input/cmi-detect-behavior-with-sensor-data/test_demographics.csv')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:30:09.502478Z","iopub.execute_input":"2025-09-02T20:30:09.502748Z","iopub.status.idle":"2025-09-02T20:30:41.238668Z","shell.execute_reply.started":"2025-09-02T20:30:09.502732Z","shell.execute_reply":"2025-09-02T20:30:41.238065Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Data visualization","metadata":{}},{"cell_type":"code","source":"train_df.head(60)","metadata":{},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Dataset","metadata":{}},{"cell_type":"code","source":"class CMIIMUDataset(Dataset):\n    def __init__(self, df, transform=None, device='cpu'):\n        self.device = device\n        self.df = df\n        self.transform = transform\n        self.features = []  # Initialize features list\n        self.scaler = StandardScaler()\n\n        excluded_cols = {\n            'gesture', 'sequence_type', 'behavior', 'orientation',  # train-only\n            'row_id', 'subject', 'phase',  # metadata\n            'sequence_id', 'sequence_counter'  # identifiers\n        }\n        \n        thermal_tof_cols = [col for col in df.columns if col.startswith('thm_') or col.startswith('tof_')]\n        excluded_cols.update(thermal_tof_cols)\n\n        # Get feature columns (all columns except excluded ones)\n        feature_cols = [col for col in df.columns if col not in excluded_cols]\n\n        # Build sequences\n        for seq_id, group in df.groupby('sequence_id'):\n            data = group[feature_cols].copy()\n\n            # fill missing values forward and backward\n            data = pd.DataFrame(data).ffill().bfill().fillna(0).values\n\n            # Standardize features\n            data = self.scaler.fit_transform(data)\n            self.features.append(data)  # Convert to numpy array\n\n        # Store both gesture labels and sequence types\n        self.labels = df.groupby('sequence_id')['gesture'].first().values\n        self.sequence_types = df.groupby('sequence_id')['sequence_type'].first().values\n\n        # Pad sequences to the same length\n        self.features = nn.utils.rnn.pad_sequence([torch.tensor(f, dtype=torch.float32, device=self.device) for f in self.features], batch_first=True)\n\n        # Encode labels\n        self.label_encoder = LabelEncoder()\n        self.labels = self.label_encoder.fit_transform(self.labels)\n        self.labels = torch.tensor(self.labels, dtype=torch.long, device=self.device)\n\n    def print(self):\n        print(self.features.shape)\n        print(self.labels.shape)\n        print(self.features[:3])\n        print(self.labels[:3])\n        print(\"\\nSequence types sample:\", self.sequence_types[:3])\n\n    def __len__(self):\n        return len(self.features)\n\n    def __getitem__(self, idx):\n        feature = self.features[idx]\n        label = self.labels[idx]\n\n        if self.transform:\n            feature = self.transform(feature)\n\n        return feature, label","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:30:41.239725Z","iopub.execute_input":"2025-09-02T20:30:41.240006Z","iopub.status.idle":"2025-09-02T20:30:41.248983Z","shell.execute_reply.started":"2025-09-02T20:30:41.239978Z","shell.execute_reply":"2025-09-02T20:30:41.248349Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"dataset = CMIIMUDataset(train_df, device=device)\ndataset.print()\n\n# Print unique gestures and their encoded values\nprint(\"\\nUnique gestures and their encoded values:\")\nfor gesture, encoded in zip(np.unique(train_df['gesture']), torch.unique(dataset.labels)):\n    print(f\"Gesture: {gesture} -> Encoded: {encoded}\")\n\nprint(\"\\nNumber of unique gestures:\", len(np.unique(train_df['gesture'])))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:30:41.249731Z","iopub.execute_input":"2025-09-02T20:30:41.249938Z","iopub.status.idle":"2025-09-02T20:30:51.230424Z","shell.execute_reply.started":"2025-09-02T20:30:41.249922Z","shell.execute_reply":"2025-09-02T20:30:51.229813Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"\\nUnique sequence types:\")\nprint(train_df['sequence_type'].unique())\n\n# Print example gestures for each sequence type\nprint(\"\\nExample gestures for each sequence type:\")\nfor seq_type in train_df['sequence_type'].unique():\n    gestures = train_df[train_df['sequence_type'] == seq_type]['gesture'].unique()\n    print(f\"\\n{seq_type}:\")\n    for g in gestures:\n        print(f\"- {g}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:30:51.231796Z","iopub.execute_input":"2025-09-02T20:30:51.232073Z","iopub.status.idle":"2025-09-02T20:30:52.059988Z","shell.execute_reply.started":"2025-09-02T20:30:51.232055Z","shell.execute_reply":"2025-09-02T20:30:52.059323Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"\\nChecking for NaN values in features:\")\nprint(\"NaN values in features:\", torch.isnan(dataset.features).any())\nprint(\"Number of NaN values:\", torch.isnan(dataset.features).sum())\n\nprint(\"\\nChecking feature statistics:\")\nprint(\"Min value:\", torch.min(dataset.features))\nprint(\"Max value:\", torch.max(dataset.features))\nprint(\"Mean value:\", torch.mean(dataset.features))\nprint(\"Std value:\", torch.std(dataset.features))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:30:52.060648Z","iopub.execute_input":"2025-09-02T20:30:52.060856Z","iopub.status.idle":"2025-09-02T20:30:52.127301Z","shell.execute_reply.started":"2025-09-02T20:30:52.060839Z","shell.execute_reply":"2025-09-02T20:30:52.126441Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(dataset.features.shape)\nprint(dataset.labels.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:30:52.128104Z","iopub.execute_input":"2025-09-02T20:30:52.128317Z","iopub.status.idle":"2025-09-02T20:30:52.132167Z","shell.execute_reply.started":"2025-09-02T20:30:52.128300Z","shell.execute_reply":"2025-09-02T20:30:52.131473Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"type(dataset.features), type(dataset.labels)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:30:52.132749Z","iopub.execute_input":"2025-09-02T20:30:52.132997Z","iopub.status.idle":"2025-09-02T20:30:52.147000Z","shell.execute_reply.started":"2025-09-02T20:30:52.132974Z","shell.execute_reply":"2025-09-02T20:30:52.146394Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Model","metadata":{}},{"cell_type":"markdown","source":"## Linear","metadata":{}},{"cell_type":"code","source":"class SimpleNN(nn.Module):\n    def __init__(self, input_size, hidden_size, num_classes):\n        super(SimpleNN, self).__init__()\n        self.fc1 = nn.Linear(input_size, hidden_size)\n        self.relu = nn.ReLU()\n        self.fc2 = nn.Linear(hidden_size, num_classes)\n\n    def forward(self, x):\n        out = self.fc1(x)\n        out = self.relu(out)\n        out = self.fc2(out)\n        return out","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:30:52.147576Z","iopub.execute_input":"2025-09-02T20:30:52.147773Z","iopub.status.idle":"2025-09-02T20:30:52.160944Z","shell.execute_reply.started":"2025-09-02T20:30:52.147736Z","shell.execute_reply":"2025-09-02T20:30:52.160206Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class ComplexNN(nn.Module):\n    def __init__(self, input_size, hidden_size, num_classes):\n        super(ComplexNN, self).__init__()\n        self.fc1 = nn.Linear(input_size, hidden_size)\n        self.relu = nn.ReLU()\n        self.fc2 = nn.Linear(hidden_size, hidden_size // 2)\n        self.relu2 = nn.ReLU()\n        self.fc3 = nn.Linear(hidden_size // 2, hidden_size // 4)\n        self.relu3 = nn.ReLU()\n        self.fc4 = nn.Linear(hidden_size // 4, hidden_size // 4)\n        self.relu4 = nn.ReLU()\n        self.fcout = nn.Linear(hidden_size // 4, num_classes)\n\n    def forward(self, x):\n        out = self.fc1(x)\n        out = self.relu(out)\n        out = self.fc2(out)\n        out = self.relu2(out)\n        out = self.fc3(out)\n        out = self.relu3(out)\n        out = self.fc4(out)\n        out = self.relu4(out)\n        out = self.fcout(out)\n        return out","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:30:52.161508Z","iopub.execute_input":"2025-09-02T20:30:52.161721Z","iopub.status.idle":"2025-09-02T20:30:52.175683Z","shell.execute_reply.started":"2025-09-02T20:30:52.161705Z","shell.execute_reply":"2025-09-02T20:30:52.175164Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## LSTMs","metadata":{}},{"cell_type":"code","source":"class LSTMModel(nn.Module):\n    def __init__(self, input_size, hidden_size, num_layers, num_classes, dropout_rate=0.2):\n        super(LSTMModel, self).__init__()\n        self.hidden_size = hidden_size\n        self.num_layers = num_layers\n        \n        # Batch normalization dla danych wejściowych\n        self.batch_norm_input = nn.BatchNorm1d(input_size)\n        \n        # LSTM z dropout\n        self.lstm = nn.LSTM(\n            input_size, \n            hidden_size, \n            num_layers, \n            batch_first=True,\n            dropout=dropout_rate if num_layers > 1 else 0\n        )\n        \n        # Batch normalization po LSTM\n        self.batch_norm_hidden = nn.BatchNorm1d(hidden_size)\n        \n        # Dropout przed warstwą liniową\n        self.dropout = nn.Dropout(dropout_rate)\n        \n        # Warstwa liniowa\n        self.fc1 = nn.Linear(hidden_size, hidden_size)\n        self.relu1 = nn.ReLU()\n        self.fc2 = nn.Linear(hidden_size, hidden_size // 4)\n        self.relu2 = nn.ReLU()\n        self.dropout2 = nn.Dropout(dropout_rate)\n        self.fcout = nn.Linear(hidden_size // 4, num_classes)\n\n    def forward(self, x):\n\n        x = x.permute(0, 2, 1)  # (batch_size, input_size, seq_length)\n        # Normalizacja danych wejściowych\n        x = self.batch_norm_input(x)\n\n        x = x.permute(0, 2, 1)  # (batch_size, seq_length, input_size)\n        \n        # Forward LSTM\n        out, _ = self.lstm(x, None)  # None oznacza, że stany ukryte są inicjalizowane zerami\n        \n        # Bierzemy ostatni output\n        out = out[:, -1, :]\n        \n        # Normalizacja po LSTM\n        out = self.batch_norm_hidden(out)\n        \n        # Dropout\n        out = self.dropout(out)\n        \n        # Warstwa liniowa\n        out = self.fc1(out)\n        out = self.relu1(out)\n        out = self.fc2(out)\n        out = self.relu2(out)\n        out = self.dropout2(out)\n        out = self.fcout(out)\n        \n        return out","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:30:52.178024Z","iopub.execute_input":"2025-09-02T20:30:52.178213Z","iopub.status.idle":"2025-09-02T20:30:52.193534Z","shell.execute_reply.started":"2025-09-02T20:30:52.178198Z","shell.execute_reply":"2025-09-02T20:30:52.192902Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 1D CNN","metadata":{}},{"cell_type":"code","source":"class CNN1D(nn.Module):\n    def __init__(self, input_size, num_classes):\n        super(CNN1D, self).__init__()\n\n        #Block in\n        self.convin = nn.Conv1d(in_channels=input_size, out_channels=512, kernel_size=7, padding=1)\n        self.bnin = nn.BatchNorm1d(512)\n        self.relubin = nn.ReLU()\n        self.poolin = nn.MaxPool1d(kernel_size=2)\n        self.dropoutin = nn.Dropout(0.2)\n        \n        #Block 1\n        self.conv1 = nn.Conv1d(in_channels=512, out_channels=768, kernel_size=5, padding=1)\n        self.bn1 = nn.BatchNorm1d(768)\n        self.relu1 = nn.ReLU()\n        self.pool1 = nn.MaxPool1d(kernel_size=2)\n        self.dropout1 = nn.Dropout(0.3)\n        \n        #Block 2\n        self.conv2 = nn.Conv1d(in_channels=768, out_channels=1024, kernel_size=3, padding=1)\n        self.bn2 = nn.BatchNorm1d(1024)\n        self.relu2 = nn.ReLU()\n        self.pool2 = nn.MaxPool1d(kernel_size=2)\n        self.dropout2 = nn.Dropout(0.3)\n        \n        #Block 3\n        self.conv3 = nn.Conv1d(in_channels=1024, out_channels=1536, kernel_size=3, padding=1)\n        self.bn3 = nn.BatchNorm1d(1536)\n        self.relu3 = nn.ReLU()\n        self.pool3 = nn.MaxPool1d(kernel_size=2)\n        self.dropout3 = nn.Dropout(0.4)\n\n        #Block 4\n        self.conv4 = nn.Conv1d(in_channels=1536, out_channels=2048, kernel_size=3, padding=1)\n        self.bn4 = nn.BatchNorm1d(2048)\n        self.relu4 = nn.ReLU()\n        self.pool4 = nn.AdaptiveAvgPool1d(1)\n        self.dropout4 = nn.Dropout(0.4)\n\n        #Dense layers\n        self.fc1 = nn.Linear(2048, 2048)\n        self.relu_fc1 = nn.ReLU()\n        self.dropout_fc1 = nn.Dropout(0.5)\n        \n        self.fc2 = nn.Linear(2048, 1024)\n        self.relu_fc2 = nn.ReLU()\n        self.dropout_fc2 = nn.Dropout(0.4)\n\n        self.fc3 = nn.Linear(1024, 512)\n        self.relu_fc3 = nn.ReLU()\n        self.dropout_fc3 = nn.Dropout(0.3)\n\n        self.fcout = nn.Linear(512, num_classes)\n\n\n    def forward(self, x):\n        # x shape: (batch_size, seq_length, input_size)\n        x = x.permute(0, 2, 1)  # Change to (batch_size, input_size, seq_length) for Conv1d\n\n        x = self.convin(x)\n        x = self.bnin(x)\n        x = self.relubin(x)\n        x = self.poolin(x)\n        x = self.dropoutin(x)\n        \n        x = self.conv1(x)\n        x = self.bn1(x)\n        x = self.relu1(x)\n        x = self.pool1(x)\n        x = self.dropout1(x)\n\n        x = self.conv2(x)\n        x = self.bn2(x)\n        x = self.relu2(x)\n        x = self.pool2(x)\n        x = self.dropout2(x)\n\n        x = self.conv3(x)\n        x = self.bn3(x)\n        x = self.relu3(x)\n        x = self.pool3(x)\n        x = self.dropout3(x)\n\n        x = self.conv4(x)\n        x = self.bn4(x)\n        x = self.relu4(x)\n        x = self.pool4(x)\n        x = self.dropout4(x)\n\n        x = x.squeeze(-1)\n\n        x = self.fc1(x)\n        x = self.relu_fc1(x)\n        x = self.dropout_fc1(x)\n\n        x = self.fc2(x)\n        x = self.relu_fc2(x)\n        x = self.dropout_fc2(x)\n\n        x = self.fc3(x)\n        x = self.relu_fc3(x)\n        x = self.dropout_fc3(x)\n\n        x = self.fcout(x)\n\n        return x","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:30:52.194100Z","iopub.execute_input":"2025-09-02T20:30:52.194341Z","iopub.status.idle":"2025-09-02T20:30:52.213638Z","shell.execute_reply.started":"2025-09-02T20:30:52.194320Z","shell.execute_reply":"2025-09-02T20:30:52.213031Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 1D CNN + LSTM + Attn","metadata":{}},{"cell_type":"code","source":"class CNNLSTMAttention(nn.Module):\n    def __init__(self, input_channels, num_classes, lstm_hidden=256, lstm_layers=1, bidirectional=False, attn_hidden=128, dropout=0.3):\n        super(CNNLSTMAttention, self).__init__()\n\n        #CNN feature extractor\n        self.cnn = nn.Sequential(\n            nn.Conv1d(in_channels=input_channels, out_channels=512, kernel_size=7, padding=3),\n            nn.ReLU(inplace=True),\n            nn.BatchNorm1d(512),\n            nn.MaxPool1d(2),\n            nn.Dropout(dropout),\n\n            nn.Conv1d(in_channels=512, out_channels=768, kernel_size=5, padding=2),\n            nn.ReLU(inplace=True),\n            nn.BatchNorm1d(768),\n            nn.MaxPool1d(2),\n            nn.Dropout(dropout),\n\n            nn.Conv1d(in_channels=768, out_channels=1024, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.BatchNorm1d(1024),\n            nn.MaxPool1d(2),\n            nn.Dropout(dropout),\n\n            nn.Conv1d(in_channels=1024, out_channels=1536, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.BatchNorm1d(1536),\n            nn.MaxPool1d(2),\n            nn.Dropout(dropout),\n        )\n\n        #LSTM\n        self.lstm = nn.LSTM(\n            input_size=1536,\n            batch_first=True,\n            bidirectional=bidirectional,\n            dropout=0.0 if lstm_layers == 1 else 0.1,\n            num_layers=lstm_layers,\n            hidden_size=lstm_hidden\n        )\n\n        self.dir_mult = 2 if bidirectional else 1\n        feat_dim = lstm_hidden * self.dir_mult\n\n        #Additive Attention\n        self.attn_fc = nn.Linear(feat_dim, attn_hidden)\n        self.attn_v = nn.Linear(attn_hidden, 1, bias=False)\n\n        #MLP\n        self.head = nn.Sequential(\n            nn.Dropout(0.5),\n            nn.Linear(feat_dim, 2048),\n            nn.ReLU(inplace=True),\n\n            nn.Dropout(0.4),\n            nn.Linear(2048, 1024),\n            nn.ReLU(inplace=True),\n\n            nn.Dropout(0.3),\n            nn.Linear(1024, 512),\n            nn.ReLU(inplace=True),\n\n            nn.Dropout(0.3),\n            nn.Linear(512, num_classes)\n        )\n\n    def forward(self, x):\n        # x: (N, L, C_in) --> (N, C_in, L)\n        x = x.permute(0, 2, 1)\n        x = self.cnn(x)\n        x = x.permute(0, 2, 1)\n        # x: (N, C_feat, L') --> (N, L', C_feat)\n\n        outputs, _ = self.lstm(x)   # (N, L', H*dirs)\n\n        # Attention\n        attn_scores = torch.tanh(self.attn_fc(outputs))     # (N, L', attn_hidden)\n        attn_scores = self.attn_v(attn_scores).squeeze(-1)  # (N, L')\n        attn_weights = F.softmax(attn_scores, dim=1)\n        context = torch.bmm(attn_weights.unsqueeze(1), outputs).squeeze(1)  # (N, H*dirs)\n\n        #MLP\n        logits = self.head(context) # (N, num_classes)\n\n        return logits","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Training loops","metadata":{}},{"cell_type":"code","source":"generator1 = torch.Generator().manual_seed(42)\ntrain_data, val_data = random_split(dataset, [0.8, 0.2], generator=generator1)\ndef get_dataloaders(train_data, val_data, batch_size=1024):\n    train_dataloader = DataLoader(\n            train_data, \n            batch_size=batch_size, \n            shuffle=True,  # Włącz shuffle dla danych treningowych\n            num_workers=0  # Użyj wielu workerów do ładowania danych\n            #pin_memory=True  # Przyspiesz transfer do GPU\n        )\n        \n    val_dataloader = DataLoader(\n            val_data, \n            batch_size=batch_size, \n            shuffle=False,  # Nie shuffle'uj danych walidacyjnych\n            num_workers=0\n            #pin_memory=True\n        )\n    return train_dataloader, val_dataloader","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:30:52.214420Z","iopub.execute_input":"2025-09-02T20:30:52.214664Z","iopub.status.idle":"2025-09-02T20:30:52.233160Z","shell.execute_reply.started":"2025-09-02T20:30:52.214640Z","shell.execute_reply":"2025-09-02T20:30:52.232633Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def calculate_training_metrics(predictions, labels):\n    accuracy = (predictions == labels).mean()\n    precision = precision_score(y_true=labels, y_pred=predictions, average='weighted', zero_division=0)\n    recall = recall_score(y_true=labels, y_pred=predictions, average='weighted', zero_division=0)\n    f1 = f1_score(y_true=labels, y_pred=predictions, average='weighted', zero_division=0)\n\n    # Add tp, fp, fn, tn calculations for each class\n    cm = confusion_matrix(labels, predictions)\n    tp = np.diag(cm)\n    fp = cm.sum(axis=0) - tp\n    fn = cm.sum(axis=1) - tp\n    tn = cm.sum() - (tp + fp + fn)\n\n    return accuracy, precision, recall, f1, cm, tp, fp, fn, tn, labels, predictions","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:30:52.233902Z","iopub.execute_input":"2025-09-02T20:30:52.234462Z","iopub.status.idle":"2025-09-02T20:30:52.248813Z","shell.execute_reply.started":"2025-09-02T20:30:52.234440Z","shell.execute_reply":"2025-09-02T20:30:52.248208Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def train_one_epoch(train_loader, model, loss_fn, optimizer, epoch_idx):\n    running_loss = 0.0\n    last_loss = 0.0\n    quarter = max(1, len(train_loader) // 4) # log every 1/4 of an epoch\n    n_in_window = 0\n    all_predictions = []\n    all_labels = []\n    all_probabilities = []\n\n    for i, data in enumerate(train_loader):\n        inputs, labels = data\n        inputs, labels = inputs.to(device), labels.to(device)\n\n        optimizer.zero_grad()\n\n        outputs = model(inputs)\n        if outputs.dim() == 2:\n            logits = outputs\n        else:\n            logits = outputs.permute(0, 2, 1).mean(dim=2)  # Adjust dimensions for CrossEntropyLoss\n        loss = loss_fn(logits, labels)\n        loss.backward()\n        optimizer.step()\n\n        # Gather data and report\n        running_loss += loss.item()\n        n_in_window += 1\n\n        probabilities = torch.softmax(logits, dim=1)\n        _, predicted = torch.max(logits.data, 1)\n        all_predictions.extend(predicted.cpu().numpy())\n        all_labels.extend(labels.cpu().numpy())\n        all_probabilities.extend(probabilities.detach().cpu().numpy())\n\n        if ((i + 1) % quarter == 0) or ((i + 1) == len(train_loader)):\n            last_loss = running_loss / n_in_window  # średnia w oknie\n            pct = int(round(100 * (i + 1) / len(train_loader)))\n            print(f\"  epoch {epoch_idx+1} {pct}% ({i+1}/{len(train_loader)}) - loss: {last_loss:.6f}\")\n\n            # reset okna\n            running_loss = 0.\n            n_in_window = 0\n\n    # Calculate metrics at the end of epoch\n    all_predictions = np.array(all_predictions)\n    all_labels = np.array(all_labels)\n    all_probabilities = np.array(all_probabilities)\n    metrics = calculate_training_metrics(all_predictions, all_labels)\n\n    return last_loss, metrics\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:30:52.249429Z","iopub.execute_input":"2025-09-02T20:30:52.249654Z","iopub.status.idle":"2025-09-02T20:30:52.268322Z","shell.execute_reply.started":"2025-09-02T20:30:52.249639Z","shell.execute_reply":"2025-09-02T20:30:52.267590Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class CompetitionMetric:\n    \"\"\"Hierarchical macro F1 for the CMI 2025 challenge.\"\"\"\n    def __init__(self):\n        self.target_gestures = [\n            'Above ear - pull hair',\n            'Cheek - pinch skin',\n            'Eyebrow - pull hair',\n            'Eyelash - pull hair',\n            'Forehead - pull hairline',\n            'Forehead - scratch',\n            'Neck - pinch skin',\n            'Neck - scratch',\n        ]\n        self.non_target_gestures = [\n            'Write name on leg',\n            'Wave hello',\n            'Glasses on/off',\n            'Text on phone',\n            'Write name in air',\n            'Feel around in tray and pull out an object',\n            'Scratch knee/leg skin',\n            'Pull air toward your face',\n            'Drink from bottle/cup',\n            'Pinch knee/leg skin'\n        ]\n        self.all_classes = self.target_gestures + self.non_target_gestures\n\n    def calculate_hierarchical_f1(\n        self,\n        sol: pd.DataFrame,\n        sub: pd.DataFrame\n    ) -> float:\n        # Validate gestures\n        invalid_types = {i for i in sub['gesture'].unique() if i not in self.all_classes}\n        if invalid_types:\n            raise ValueError(f\"Invalid gesture values in submission: {invalid_types}\")\n\n        # Compute binary F1 (Target vs Non-Target)\n        y_true_bin = sol['gesture'].isin(self.target_gestures).values\n        y_pred_bin = sub['gesture'].isin(self.target_gestures).values\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        # Build multi-class labels for gestures\n        y_true_mc = sol['gesture'].apply(lambda x: x if x in self.target_gestures else 'non_target')\n        y_pred_mc = sub['gesture'].apply(lambda x: x if x in self.target_gestures else 'non_target')\n\n        # Compute macro F1 over all gesture classes\n        f1_macro = f1_score(\n            y_true_mc,\n            y_pred_mc,\n            average='macro',\n            zero_division=0\n        )\n\n        return 0.5 * f1_binary + 0.5 * f1_macro","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:30:52.269166Z","iopub.execute_input":"2025-09-02T20:30:52.269447Z","iopub.status.idle":"2025-09-02T20:30:52.289937Z","shell.execute_reply.started":"2025-09-02T20:30:52.269426Z","shell.execute_reply":"2025-09-02T20:30:52.289353Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class EarlyStopping_Accuracy:\n    \"\"\"Early stops the training if validation accuracy doesn't improve after a given patience.\"\"\"\n    def __init__(self, patience=7, verbose=False, delta=0, path='checkpoint.pt'):\n        \"\"\"\n        Args:\n            patience (int): How long to wait after last time validation accuracy improved.\n                            Default: 7\n            verbose (bool): If True, prints a message for each validation accuracy improvement. \n                            Default: False\n            delta (float): Minimum change in the monitored quantity to qualify as an improvement.\n                            Default: 0\n            path (str): Path for the checkpoint to be saved to.\n                            Default: 'checkpoint.pt'\n        \"\"\"\n        self.patience = patience\n        self.verbose = verbose\n        self.counter = 0\n        self.best_score = None\n        self.early_stop = False\n        self.val_accuracy_max = 0.0\n        self.delta = delta\n        self.path = path\n\n    def __call__(self, val_accuracy, model):\n\n        score = val_accuracy\n\n        if self.best_score is None:\n            self.best_score = score\n            self.save_checkpoint(val_accuracy, model)\n        elif score < self.best_score + self.delta:\n            self.counter += 1\n            if self.verbose:\n                print(f'EarlyStopping counter: {self.counter} out of {self.patience}')\n            if self.counter >= self.patience:\n                self.early_stop = True\n        else:\n            self.best_score = score\n            self.save_checkpoint(val_accuracy, model)\n            self.counter = 0\n\n    def save_checkpoint(self, val_accuracy, model):\n        '''Saves model when validation accuracy increases.'''\n        if self.verbose:\n            print(f'Validation accuracy increased ({self.val_accuracy_max:.6f} --> {val_accuracy:.6f}).  Saving model ...')\n        torch.save(model.state_dict(), self.path)\n        self.val_accuracy_max = val_accuracy","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:30:52.290773Z","iopub.execute_input":"2025-09-02T20:30:52.291086Z","iopub.status.idle":"2025-09-02T20:30:52.309329Z","shell.execute_reply.started":"2025-09-02T20:30:52.291070Z","shell.execute_reply":"2025-09-02T20:30:52.308618Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def validate(val_loader: DataLoader, model: nn.Module, loss_fn: nn):\n    running_loss = 0.0\n    n_in_window = 0\n    all_predictions = []\n    all_labels = []\n    all_probabilities = []\n    \n    with torch.no_grad():\n        for i, data in enumerate(val_loader):\n            inputs, labels = data\n            inputs, labels = inputs.to(device), labels.to(device)\n\n            outputs = model(inputs)\n            if outputs.dim() == 2:\n                logits = outputs\n            else:\n                logits = outputs.permute(0, 2, 1).mean(dim=2)  # Adjust dimensions for CrossEntropyLoss\n            loss = loss_fn(logits, labels)\n\n            running_loss += loss.item()\n            n_in_window += 1\n\n            probabilities = torch.softmax(logits, dim=1)\n            _, predicted = torch.max(logits.data, 1)\n            all_predictions.extend(predicted.cpu().numpy())\n            all_labels.extend(labels.cpu().numpy())\n            all_probabilities.extend(probabilities.cpu().numpy())\n\n    # Calculate standard metrics\n    all_predictions = np.array(all_predictions)\n    all_labels = np.array(all_labels)\n    all_probabilities = np.array(all_probabilities)\n    metrics = calculate_training_metrics(all_predictions, all_labels)\n    \n    avg_loss = running_loss / n_in_window\n    print(f\"  Validation - loss: {avg_loss:.6f}, accuracy: {metrics[0]*100:.2f}%, precision: {metrics[1]:.4f}, recall: {metrics[2]:.4f}, f1: {metrics[3]:.4f}\")\n\n    return avg_loss, metrics, all_probabilities","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:30:52.310071Z","iopub.execute_input":"2025-09-02T20:30:52.310336Z","iopub.status.idle":"2025-09-02T20:30:52.331229Z","shell.execute_reply.started":"2025-09-02T20:30:52.310312Z","shell.execute_reply":"2025-09-02T20:30:52.330701Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def calculate_metrics(predictions, labels):\n\n    precision = precision_score(labels, predictions, average='weighted')\n    recall = recall_score(labels, predictions, average='weighted')\n    f1 = f1_score(labels, predictions, average='weighted')\n    cm = confusion_matrix(labels, predictions)\n\n    return precision, recall, f1, cm","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:30:52.331994Z","iopub.execute_input":"2025-09-02T20:30:52.332219Z","iopub.status.idle":"2025-09-02T20:30:52.349206Z","shell.execute_reply.started":"2025-09-02T20:30:52.332197Z","shell.execute_reply":"2025-09-02T20:30:52.348623Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def write_to_tensorboard(writer, epoch, train_loss, val_loss, val_metrics, train_metrics, learning_rate):\n    # val_metrics: accuracy, precision, recall, f1, cm, tp, fp, fn, tn, labels, predictions\n    #               0           1           2   3   4   5    6   7   8      9       10\n    # train_metrics: accuracy, precision, recall, f1, cm, tp, fp, fn, tn, labels, predictions\n    writer.add_scalar('Loss/train', train_loss, epoch + 1)\n    writer.add_scalar('Loss/val', val_loss, epoch + 1)\n    writer.add_scalars('Loss', {'Training': train_loss, 'Validation': val_loss}, epoch + 1)\n    writer.add_scalar('Accuracy/val', val_metrics[0]*100, epoch + 1)\n    writer.add_scalar('Precision/val', val_metrics[1], epoch + 1)\n    writer.add_scalar('Recall/val', val_metrics[2], epoch + 1)\n    writer.add_scalar('F1-score/val', val_metrics[3], epoch + 1)\n    writer.add_scalar('Accuracy/train', train_metrics[0]*100, epoch + 1)\n    writer.add_scalar('Precision/train', train_metrics[1], epoch + 1)\n    writer.add_scalar('Recall/train', train_metrics[2], epoch + 1)\n    writer.add_scalar('F1-score/train', train_metrics[3], epoch + 1)\n    writer.add_scalar('Learning Rate', learning_rate, epoch + 1)\n\n    writer.flush()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:30:52.349917Z","iopub.execute_input":"2025-09-02T20:30:52.350128Z","iopub.status.idle":"2025-09-02T20:30:52.365125Z","shell.execute_reply.started":"2025-09-02T20:30:52.350105Z","shell.execute_reply":"2025-09-02T20:30:52.364519Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def train_loop(train_data: Subset, val_data: Subset, model: nn.Module, loss_fn: nn, optimizer: torch.optim, scheduler: torch.optim.lr_scheduler, name: str, patience=10, epochs=10, batch_size=32, warmup_epochs=0, warmup_lr=1e-5):\n    timestamp = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')\n    writer = SummaryWriter('runs/{}_{}'.format(name, timestamp))\n\n    early_stopping = EarlyStopping_Accuracy(patience=patience, verbose=True, path='runs/{}_{}/best_accuracy_model.pt'.format(name, timestamp))\n    epoch_number = 0\n\n    all_metrics = []\n    all_vmetrics = []\n    all_probabilities = []\n\n    best_vloss = 1_000_000.\n    best_vloss_epoch = -1\n\n    train_dataloader, validation_loader = get_dataloaders(train_data, val_data, batch_size=batch_size)\n\n    for epoch in range(epochs):\n        print('EPOCH {}:'.format(epoch_number + 1))\n\n        # Make sure gradient tracking is on, and do a pass over the data\n        model.train(True)\n        avg_loss, metrics = train_one_epoch(train_dataloader, model, loss_fn, optimizer, epoch_number)\n\n        # Set the model to evaluation mode, disabling dropout and using population\n        # statistics for batch normalization.\n        model.eval()\n\n        avg_vloss, vmetrics, probabilities = validate(validation_loader, model, loss_fn)\n\n        # Step the scheduler\n        if epoch_number < warmup_epochs:\n            # Linear warmup\n            warmup_lr = (epoch_number + 1) / warmup_epochs * optimizer.defaults['lr']\n            for param_group in optimizer.param_groups:\n                param_group['lr'] = warmup_lr\n            print(f\"  Warmup LR: {warmup_lr:.6f}\")\n        else:\n            scheduler.step(vmetrics[0])  # Step based on validation accuracy\n\n        print('LOSS train {} valid {}'.format(avg_loss, avg_vloss))\n\n        # Log the running loss averaged per batch\n        # for both training and validation\n        write_to_tensorboard(writer, epoch_number, avg_loss, avg_vloss, vmetrics, metrics, \n                           optimizer.param_groups[0]['lr'])\n\n        # Track best performance, and save the model's state\n        if avg_vloss < best_vloss:\n            best_vloss = avg_vloss\n            best_vloss_epoch = epoch_number\n            model_path = 'runs/{}_{}/best_loss_model.pt'.format(name, timestamp)\n            torch.save(model.state_dict(), model_path)\n\n        all_metrics.append(metrics)\n        all_vmetrics.append(vmetrics)\n        all_probabilities.append(probabilities)\n\n        # Early stopping\n        early_stopping(vmetrics[0], model) # Monitor validation accuracy\n        if early_stopping.early_stop:\n            print(\"Early stopping\")\n            break\n\n        epoch_number += 1\n\n    print(\"Training complete.\")\n    print(f\"Best validation loss: {best_vloss:.6f} at epoch {best_vloss_epoch + 1}\")\n\n    data = {\n        \"model\": model,\n        \"writer\": writer,\n        \"all_metrics\": all_metrics,\n        \"all_vmetrics\": all_vmetrics,\n        \"all_probabilities\": all_probabilities,\n        \"model_path\": 'runs/{}_{}'.format(name, timestamp)\n    }\n    return data","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:30:52.365814Z","iopub.execute_input":"2025-09-02T20:30:52.366034Z","iopub.status.idle":"2025-09-02T20:30:52.381560Z","shell.execute_reply.started":"2025-09-02T20:30:52.366020Z","shell.execute_reply":"2025-09-02T20:30:52.380829Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def evaluate_model(model, val_data, batch_size=128):\n    \"\"\"\n    Evaluate model on validation data, calculating both standard and competition metrics\n    \"\"\"\n    model.eval()\n    val_loader = DataLoader(val_data, batch_size=batch_size, shuffle=False)\n    \n    all_predictions = []\n    all_probabilities = []\n    all_true_labels = []\n    \n    with torch.no_grad():\n        for inputs, labels in val_loader:\n            inputs = inputs.to(device)\n            outputs = model(inputs)\n            \n            if outputs.dim() == 2:\n                logits = outputs\n            else:\n                logits = outputs.permute(0, 2, 1).mean(dim=2)\n                \n            probabilities = torch.softmax(logits, dim=1)\n            _, predicted = torch.max(logits.data, 1)\n            \n            all_predictions.extend(predicted.cpu().numpy())\n            all_probabilities.extend(probabilities.cpu().numpy())\n            all_true_labels.extend(labels.cpu().numpy())\n    \n    # Convert lists to numpy arrays\n    all_predictions = np.array(all_predictions)\n    all_probabilities = np.array(all_probabilities)\n    all_true_labels = np.array(all_true_labels)\n    \n    # Get sequence types for validation samples\n    val_indices = val_data.indices\n    sequence_types = dataset.sequence_types[val_indices]\n    \n    # Convert predictions and true labels back to original gesture names\n    original_predictions = dataset.label_encoder.inverse_transform(all_predictions)\n    original_labels = dataset.label_encoder.inverse_transform(all_true_labels)\n    \n    # Calculate standard metrics\n    accuracy = (all_predictions == all_true_labels).mean()\n    precision = precision_score(all_true_labels, all_predictions, average='weighted', zero_division=0)\n    recall = recall_score(all_true_labels, all_predictions, average='weighted', zero_division=0)\n    f1 = f1_score(all_true_labels, all_predictions, average='weighted', zero_division=0)\n    \n    print(\"\\nStandard Metrics:\")\n    print(f\"Accuracy: {accuracy*100:.2f}%\")\n    print(f\"Precision: {precision:.4f}\")\n    print(f\"Recall: {recall:.4f}\")\n    print(f\"F1 Score: {f1:.4f}\")\n    \n    # Calculate competition metrics using official implementation\n    metric = CompetitionMetric()\n    val_submission = pd.DataFrame({'gesture': original_predictions})\n    val_solution = pd.DataFrame({'gesture': original_labels})\n    competition_score = metric.calculate_hierarchical_f1(val_solution, val_submission)\n    \n    print(\"\\nCompetition Metrics (Official):\")\n    print(f\"Final Score: {competition_score:.4f}\")\n    \n    # Print sequence type statistics\n    target_mask = np.isin(original_labels, metric.target_gestures)\n    print(\"\\nSequence Statistics:\")\n    print(f\"Target sequences: {target_mask.sum()}\")\n    print(f\"Non-target sequences: {len(target_mask) - target_mask.sum()}\")\n    \n    if target_mask.sum() > 0:\n        print(\"\\nTarget gestures present in validation set:\")\n        target_gestures = np.unique(original_labels[target_mask])\n        for gesture in target_gestures:\n            print(f\"- {gesture}\")\n    \n        # Print confusion matrix for target gestures\n        target_indices = np.where(target_mask)[0]\n        target_predictions = original_predictions[target_indices]\n        target_true_labels = original_labels[target_indices]\n        unique_targets = np.unique(target_true_labels)\n        \n        print(\"\\nConfusion Matrix for Target Gestures:\")\n        cm = confusion_matrix(target_true_labels, target_predictions, labels=unique_targets)\n        print(\"True labels (rows) vs Predicted labels (columns):\")\n        print(\"Labels:\", unique_targets)\n        print(cm)\n    \n    return {\n        'standard_metrics': {\n            'accuracy': accuracy,\n            'precision': precision,\n            'recall': recall,\n            'f1': f1\n        },\n        'competition_score': competition_score,\n        'predictions': all_predictions,\n        'probabilities': all_probabilities,\n        'true_labels': all_true_labels,\n        'sequence_types': sequence_types\n    }","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:30:52.382307Z","iopub.execute_input":"2025-09-02T20:30:52.382520Z","iopub.status.idle":"2025-09-02T20:30:52.407195Z","shell.execute_reply.started":"2025-09-02T20:30:52.382496Z","shell.execute_reply":"2025-09-02T20:30:52.406425Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def plot_pr_curve(y_true, probabilities, writer, class_names=None):\n    \"\"\"\n    Rysuje krzywe PR (Precision-Recall) dla każdej klasy.\n    \n    Args:\n        y_true: prawdziwe etykiety\n        probabilities: macierz prawdopodobieństw dla każdej klasy (wynik softmax)\n        writer: obiekt SummaryWriter z TensorBoard\n        class_names: opcjonalna lista nazw klas\n    \"\"\"\n    from sklearn.metrics import precision_recall_curve\n    import matplotlib.pyplot as plt\n    \n    num_classes = probabilities.shape[1]\n    \n    for i in range(num_classes):\n        class_name = class_names[i] if class_names is not None else f\"class_{i}\"\n        \n        # Konwertuj etykiety na format binarny (1 dla danej klasy, 0 dla pozostałych)\n        y_true_binary = (y_true == i).astype(np.int32)\n        \n        # Pobierz prawdopodobieństwa dla danej klasy\n        class_probs = probabilities[:, i]\n        \n        # Oblicz precision i recall dla różnych progów\n        precision, recall, thresholds = precision_recall_curve(y_true_binary, class_probs)\n        \n        # Rysuj krzywą PR\n        plt.figure(figsize=(8, 6))\n        plt.plot(recall, precision, label=f'Class {class_name}')\n        plt.xlabel('Recall')\n        plt.ylabel('Precision')\n        plt.title(f'Precision-Recall Curve - {class_name}')\n        plt.grid(True)\n        plt.legend()\n        \n        # Zapisz wykres do bufora i dodaj do TensorBoard\n        buf = io.BytesIO()\n        plt.savefig(buf, format='png')\n        buf.seek(0)\n        \n        # Konwertuj bufor na tensor\n        img = Image.open(buf)\n        img_tensor = torch.from_numpy(np.array(img))\n        img_tensor = img_tensor.permute(2, 0, 1)  # HWC -> CHW\n        \n        # Dodaj wykres do TensorBoard\n        writer.add_image(f'PR_Curves/{class_name}', img_tensor, global_step=0)\n        \n        # Dodaj też surowe dane do interaktywnego wykresu\n        writer.add_pr_curve(\n            f'PR_Curves_Raw/{class_name}',\n            y_true_binary,\n            class_probs,\n            global_step=0\n        )\n        \n        plt.close()\n    \n    writer.flush()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:30:52.407847Z","iopub.execute_input":"2025-09-02T20:30:52.408107Z","iopub.status.idle":"2025-09-02T20:30:52.428085Z","shell.execute_reply.started":"2025-09-02T20:30:52.408085Z","shell.execute_reply":"2025-09-02T20:30:52.427440Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Train","metadata":{}},{"cell_type":"code","source":"model = LSTMModel(input_size=dataset.features.shape[2], hidden_size=512, num_layers=1, num_classes=len(torch.unique(dataset.labels)), dropout_rate=0.4)\nmodel.to(device)\nprint(model)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:30:52.428711Z","iopub.execute_input":"2025-09-02T20:30:52.428946Z","iopub.status.idle":"2025-09-02T20:30:52.502689Z","shell.execute_reply.started":"2025-09-02T20:30:52.428929Z","shell.execute_reply":"2025-09-02T20:30:52.502101Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = ComplexNN(input_size=dataset.features.shape[2], hidden_size=256, num_classes=len(torch.unique(dataset.labels)))\nmodel.to(device)\nprint(model)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:30:52.503410Z","iopub.execute_input":"2025-09-02T20:30:52.503612Z","iopub.status.idle":"2025-09-02T20:30:52.510405Z","shell.execute_reply.started":"2025-09-02T20:30:52.503597Z","shell.execute_reply":"2025-09-02T20:30:52.509898Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = CNN1D(input_size=dataset.features.shape[2], num_classes=len(torch.unique(dataset.labels)))\nmodel.to(device)\nprint(model)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:38:31.560054Z","iopub.execute_input":"2025-09-02T20:38:31.560540Z","iopub.status.idle":"2025-09-02T20:38:31.752382Z","shell.execute_reply.started":"2025-09-02T20:38:31.560513Z","shell.execute_reply":"2025-09-02T20:38:31.751677Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = CNNLSTMAttention(input_channels=dataset.features.shape[2], num_classes=len(torch.unique(dataset.labels)), lstm_layers=2, bidirectional=True, dropout=0.3)\nmodel.to(device)\nprint(model)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pytorch_total_params = sum(p.numel() for p in model.parameters())\nprint(f\"Total model parameters: {pytorch_total_params:,}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:38:33.421300Z","iopub.execute_input":"2025-09-02T20:38:33.421887Z","iopub.status.idle":"2025-09-02T20:38:33.426027Z","shell.execute_reply.started":"2025-09-02T20:38:33.421848Z","shell.execute_reply":"2025-09-02T20:38:33.425260Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pytorch_trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)\nprint(f\"Trainable model parameters: {pytorch_trainable_params:,}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:38:33.938459Z","iopub.execute_input":"2025-09-02T20:38:33.939092Z","iopub.status.idle":"2025-09-02T20:38:33.943370Z","shell.execute_reply.started":"2025-09-02T20:38:33.939068Z","shell.execute_reply":"2025-09-02T20:38:33.942604Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"name = 'CMI_1D_CNN_v1'\nloss_fn = nn.CrossEntropyLoss().to(device)\nlearning_rate = 1e-3\noptimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)\nscheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(\n    optimizer, \n    mode='max', \n    factor=0.1, \n    patience=8,\n    min_lr=1e-7\n)\n\n# Inicjalizacja wag\ndef init_weights(m):\n    if isinstance(m, nn.Linear):\n        nn.init.xavier_uniform_(m.weight)\n        if hasattr(m, 'bias') and m.bias is not None:\n            nn.init.zeros_(m.bias)\n    elif isinstance(m, nn.LSTM):\n        for name, param in m.named_parameters():\n            if 'weight_ih' in name:\n                nn.init.xavier_uniform_(param)\n            elif 'weight_hh' in name:\n                nn.init.orthogonal_(param)\n            elif 'bias' in name:\n                nn.init.zeros_(param)\n    elif isinstance(m, nn.Conv1d):\n        nn.init.kaiming_uniform_(m.weight, nonlinearity='relu')\n        if hasattr(m, 'bias') and m.bias is not None:\n            nn.init.zeros_(m.bias)\n\nmodel.apply(init_weights)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:31:03.493526Z","iopub.execute_input":"2025-09-02T20:31:03.493835Z","iopub.status.idle":"2025-09-02T20:31:06.380774Z","shell.execute_reply.started":"2025-09-02T20:31:03.493807Z","shell.execute_reply":"2025-09-02T20:31:06.380144Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"torch.cuda.empty_cache()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:31:06.381982Z","iopub.execute_input":"2025-09-02T20:31:06.382625Z","iopub.status.idle":"2025-09-02T20:31:06.386366Z","shell.execute_reply.started":"2025-09-02T20:31:06.382606Z","shell.execute_reply":"2025-09-02T20:31:06.385822Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Uruchomienie treningu\n#results = train_loop(\n#    model=model,\n#    train_data=train_data,\n#    val_data=val_data,\n#    loss_fn=loss_fn,\n#    optimizer=optimizer,\n#    patience=15,\n#    epochs=150,\n#    batch_size=64,  # Zwiększony batch size dla lepszej stabilności\n#    scheduler=scheduler,\n#    warmup_epochs=15,\n#    warmup_lr=1e-4,\n#    name=name\n#)\n\n# Ewaluacja modelu na zbiorze walidacyjnym\n#print(\"\\nEwaluacja końcowa modelu:\")\n#evaluation = evaluate_model(model, val_data)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:31:06.387155Z","iopub.execute_input":"2025-09-02T20:31:06.387432Z","iopub.status.idle":"2025-09-02T20:31:06.410133Z","shell.execute_reply.started":"2025-09-02T20:31:06.387407Z","shell.execute_reply":"2025-09-02T20:31:06.409511Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#plot_pr_curve(results[\"all_vmetrics\"][-1][9], results[\"all_probabilities\"][-1], results[\"writer\"], class_names=dataset.label_encoder.classes_)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:31:06.411292Z","iopub.execute_input":"2025-09-02T20:31:06.411497Z","iopub.status.idle":"2025-09-02T20:31:06.425731Z","shell.execute_reply.started":"2025-09-02T20:31:06.411481Z","shell.execute_reply":"2025-09-02T20:31:06.425125Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"essa = torch.load('/kaggle/input/1d-cnn-lstmattention/pytorch/v1/1/best_accuracy_model.pt')\nmodel.load_state_dict(essa)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:42:22.294665Z","iopub.execute_input":"2025-09-02T20:42:22.295019Z","iopub.status.idle":"2025-09-02T20:42:23.906245Z","shell.execute_reply.started":"2025-09-02T20:42:22.294996Z","shell.execute_reply":"2025-09-02T20:42:23.905496Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"\\nEwaluacja na zbiorze walidacyjnym:\")\nval_evaluation = evaluate_model(model, val_data)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T20:42:33.887596Z","iopub.execute_input":"2025-09-02T20:42:33.888395Z","iopub.status.idle":"2025-09-02T20:42:35.190200Z","shell.execute_reply.started":"2025-09-02T20:42:33.888360Z","shell.execute_reply":"2025-09-02T20:42:35.189310Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def predict_sequence(sequence_df: pd.DataFrame, model: nn.Module, device='cuda') -> str:\n    \"\"\"\n    Predict gesture for a single sequence\n    \n    Args:\n        sequence_df: DataFrame containing single sequence data\n        model: Trained model\n        device: Device to run inference on\n    \n    Returns:\n        str: Predicted gesture name\n    \"\"\"\n    # Get feature columns (same as in dataset)\n    excluded_cols = {\n        'gesture', 'sequence_type', 'behavior', 'orientation',  # train-only\n        'row_id', 'subject', 'phase',  # metadata\n        'sequence_id', 'sequence_counter'  # identifiers\n    }\n    \n    thermal_tof_cols = [col for col in sequence_df.columns if col.startswith('thm_') or col.startswith('tof_')]\n    excluded_cols.update(thermal_tof_cols)\n    \n    feature_cols = [col for col in sequence_df.columns if col not in excluded_cols]\n    \n    # Prepare features\n    data = sequence_df[feature_cols].copy()\n    data = data.ffill().bfill().fillna(0).values\n    \n    # Standardize features using the same scaler logic\n    scaler = StandardScaler()\n    data = scaler.fit_transform(data)\n    \n    # Convert to tensor and add batch dimension\n    data = torch.tensor(data, dtype=torch.float32, device=device)\n    data = data.unsqueeze(0)  # Add batch dimension\n    \n    # Get prediction\n    model.eval()\n    with torch.no_grad():\n        outputs = model(data)\n        if outputs.dim() > 2:\n            outputs = outputs.permute(0, 2, 1).mean(dim=2)\n        probabilities = torch.softmax(outputs, dim=1)\n        predicted = torch.argmax(probabilities, dim=1)\n        \n    # Convert back to gesture name\n    predicted_gesture = dataset.label_encoder.inverse_transform(predicted.cpu().numpy())[0]\n    \n    return predicted_gesture","metadata":{},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def predict(sequence: pl.DataFrame, demographics: pl.DataFrame) -> str:\n    \"\"\"\n    Competition prediction function.\n    Called with one sequence at a time.\n    \n    Args:\n        sequence: Polars DataFrame with sequence data\n        demographics: Polars DataFrame with demographics data\n        \n    Returns:\n        str: Predicted gesture name\n    \"\"\"\n    # Convert Polars DataFrame to Pandas\n    sequence_pd = sequence.to_pandas()\n    \n    # Make prediction using our model\n    predicted_gesture = predict_sequence(sequence_pd, model)\n    \n    # Validate if prediction is valid\n    metric = CompetitionMetric()\n    if predicted_gesture not in metric.all_classes:\n        # If prediction is invalid, return a safe default\n        return 'Text on phone'  # One of the non-target gestures\n        \n    return predicted_gesture\n\n# Test the prediction function on a single sequence from validation set\ntest_sequence_idx = val_data.indices[0]\ntest_sequence = train_df[train_df['sequence_id'] == train_df['sequence_id'].unique()[test_sequence_idx]]\ntest_demographics = train_demo_df[train_demo_df['subject'] == test_sequence['subject'].iloc[0]]\n\n# Convert to Polars for testing\ntest_sequence_pl = pl.from_pandas(test_sequence)\ntest_demographics_pl = pl.from_pandas(test_demographics)\n\nprint(\"Testing prediction function:\")\nprint(f\"True gesture: {test_sequence['gesture'].iloc[0]}\")\nprint(f\"Predicted gesture: {predict(test_sequence_pl, test_demographics_pl)}\")","metadata":{},"outputs":[],"execution_count":null},{"cell_type":"code","source":"inference_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},"outputs":[],"execution_count":null}]}