{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":59093,"databundleVersionId":7469972,"sourceType":"competition"},{"sourceId":7392733,"sourceType":"datasetVersion","datasetId":4297749},{"sourceId":7465251,"sourceType":"datasetVersion","datasetId":4317718}],"dockerImageVersionId":30664,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"\n<div style=\"background-color: #e0f8e6; padding: 20px; border-radius: 50px; border: 5px solid #ffa76e;\">\n\n<html>\n<head>\n</head>\n<body>\n    <h1>If you find my Kaggle notebook helpful, please consider giving it an upvote! 👍</h1>\n    \nThis is a super clean code and the Purpose of this notebook is to help beginners and new competitors on Kaggle to give an idea of how a EEG signal Classification training notebook looks like, this notebook is just a baseline but can be modified into a solid submission.    \n    \n</body>\n</html>","metadata":{}},{"cell_type":"markdown","source":"# CREDITS \n#### * Thanks to [Nischay Dhankhar]() for providing such an insightful notebook for EEg signals training\n#### * Kudos to [Chris Deotte](http://https://www.kaggle.com/code/cdeotte/wavenet-starter-lb-0-52?scriptVersionId=160158478) for sharing raw EEG signals, this notebook uses 8 channels signals generated by Chris' notebook","metadata":{}},{"cell_type":"markdown","source":"<div style=\"background-color: #e0f8e6; padding: 20px; border-radius: 50px; border: 5px solid #ffa76e;\">\n    \n# Import libraries","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport torch\nimport os\nimport pytorch_lightning as pl\nfrom torch.utils.data import Dataset, DataLoader\nfrom sklearn import model_selection\nimport torchvision.transforms as transforms\nimport torchvision.io \nimport librosa\nfrom PIL import Image\nimport albumentations as alb\nimport torch.multiprocessing as mp\nimport warnings\nwarnings.filterwarnings('ignore')\n\nfrom pytorch_lightning.callbacks import ModelCheckpoint, BackboneFinetuning, EarlyStopping","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"background-color: #e0f8e6; padding: 20px; border-radius: 50px; border: 5px solid #ffa76e;\">\n    \n# Config Class Constructor\n    \n<strong>Do play with these parameters to tune your model, make sure to track the progress<strong>","metadata":{}},{"cell_type":"code","source":"class Config:\n    def __init__(self):\n        self.optimizers =\"adam\"\n        self.scheduler =\"CosineAnnealingWarmRestarts\"\n        self.min_lr = 1e-6\n        self.T_0 = 25\n\n        self.use_aug = True\n        self.num_classes = 6\n        self.batch_size = 32\n        self.epochs = 10\n        self.PRECISION = 16\n        self.PATIENCE = 20\n        self.seed = 20\n        self.pretrained = False\n        self.weight_decay = 1e-2\n        self.use_mixup = False\n        self.mixup_alpha = 0.1\n        self.num_channels = 8 #8#1\n        self.data_root = \"/kaggle/input/hms-harmful-brain-activity-classification/\"\n        self.raw_eeg_path = \"/kaggle/input/brain-eegs/eegs.npy\"\n        self.cols_interest = ['Fp1', 'C3', 'F7', 'T5', 'Fz', 'Cz', 'Pz', 'Fp2', 'F4', 'C4', 'P4', 'F8', 'T4', 'T6', 'O2', 'EKG']\n        self.LR = 2e-4\n        self.processed_train = None\n        self.output_dir = '/kaggle/working/'\n        self.trn_folds = [0, 1, 2, 3, 4]\n        \nConfig = Config()\n     ","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df = pd.read_csv(f'{Config.data_root}train.csv')\ndf.shape","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"background-color: #e0f8e6; padding: 20px; border-radius: 50px; border: 5px solid #ffa76e;\">\n    \n# Classes distributions","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize =(15,5))\ncategories = df['expert_consensus'].value_counts().index\ncounts = df['expert_consensus'].value_counts()\nplt.bar(categories, counts)\nplt.xlabel('Categories')\nplt.ylabel('Count')\nplt.title('Bar Plot of expert_consensus Value Counts')\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df[df['eeg_id']==1000913311]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"unique_eeg_samples = df['eeg_id'].unique()\nprint(\"Unique EEG samples = \", len(unique_eeg_samples))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Reading EEG signals","metadata":{}},{"cell_type":"code","source":"pqt = pd.read_parquet('/kaggle/input/hms-harmful-brain-activity-classification/train_eegs/1000913311.parquet')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"background-color: #e0f8e6; padding: 20px; border-radius: 50px; border: 5px solid #ffa76e;\">\n    \n# Data Preprocessing","metadata":{}},{"cell_type":"code","source":"class DataProcessor:\n    def __init__(self, df):\n        self.df = df\n        self.EEG_IDS = self.df['eeg_id'].unique()\n        self.TARGETS = self.df.columns[-6:]\n        self.TARS = {'Seizure': 0, 'LPD': 1, 'GPD': 2, 'LRDA': 3, 'GRDA': 4, 'Other': 5}\n        self.TARS_INV = {x: y for y, x in self.TARS.items()}\n        self.train = self.process_data()\n\n    def process_data(self):\n        train = self.df.groupby('eeg_id')[['patient_id']].agg('first')\n\n        tmp = self.df.groupby('eeg_id')[self.TARGETS].agg('sum')\n        for t in self.TARGETS:\n            train[t] = tmp[t].values\n\n        y_data = train[self.TARGETS].values\n        y_data = y_data / y_data.sum(axis=1, keepdims=True)\n        train[self.TARGETS] = y_data\n\n        tmp = self.df.groupby('eeg_id')[['expert_consensus']].agg('first')\n        train['target'] = tmp\n\n        train = train.reset_index()\n        train = train.loc[train.eeg_id.isin(self.EEG_IDS)]\n        print('Train Data with unique eeg_id shape:', train.shape)\n\n        return train\n\ndata_processor = DataProcessor(df)\ntrain= data_processor.train\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train.head()\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%time\n\nCREATE_EEGS = True\ndf = pd.read_parquet(f'{Config.data_root}train_eegs/1000913311.parquet')\nFEATS = df.columns\nprint(f'There are {len(FEATS)} raw eeg features')\nprint( list(FEATS) )\n\nif Config.raw_eeg_path is not None:\n    raw_eegs = np.load(Config.raw_eeg_path, allow_pickle=True).item()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"background-color: #e0f8e6; padding: 20px; border-radius: 50px; border: 5px solid #ffa76e;\">\n    \n# Signal Augmentations\n    \n #### The combination of colored noise, Gaussian noise, and frequency/time masking provides a comprehensive set of transformations to enhance the model's robustness and performance across various scenarios. ","metadata":{}},{"cell_type":"code","source":"!pip install albumentations\n!pip install torch_audiomentations","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from torch_audiomentations import Compose\nimport torch_audiomentations as t\n\ndef get_eeg_transforms(data='train'):\n    if data == 'train':\n        eeg_transform = t.Compose([\n            t.AddColoredNoise(\n                p=0.15,\n                mode=\"per_channel\",\n                p_mode=\"per_channel\",\n                max_snr_in_db=15,\n                sample_rate=200\n            ),\n            t.AddGaussianNoise(\n                p=0.2,\n                max_amplitude=0.5\n            ),\n            t.FrequencyMask(\n                p=0.2,\n                max_mask_percentage=0.1\n            ),\n            t.TimeMask(\n                p=0.2,\n                max_mask_percentage=0.1\n            ),\n        ])\n    elif data == 'valid':\n        eeg_transform = t.Compose([])  \n    else:\n        raise ValueError(f\"Invalid data split: {data}\")\n\n    return eeg_transform\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"background-color: #e0f8e6; padding: 20px; border-radius: 50px; border: 5px solid #ffa76e;\">\n    \n# Model Builder","metadata":{}},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\nclass EEGNet(nn.Module):\n    def __init__(self, in_channels, classes_num):\n        super(EEGNet, self).__init__()\n        self.drop_out = 0.25\n        \n        self.block_1 = nn.Sequential(\n            nn.ZeroPad2d((24, 24, 0, 0)),\n            nn.Conv2d(\n                in_channels=Config.num_channels,  # 使用传入的in_channels参数\n                out_channels=8, \n                kernel_size=(1,64),\n                bias=False\n            ),\n            nn.BatchNorm2d(8)\n        )\n        \n        self.block_2 = nn.Sequential(\n            nn.Conv2d(\n                in_channels=8, \n                out_channels=16, \n                kernel_size=(1,64), \n                groups=8,\n                bias=False\n            ),\n            nn.BatchNorm2d(16), \n            nn.ELU(),\n            nn.AvgPool2d((1, 4)), \n            nn.Dropout(self.drop_out)\n        )\n        \n        self.block_3 = nn.Sequential(\n            nn.ZeroPad2d((7, 8, 0, 0)),\n            nn.Conv2d(\n               in_channels=16, \n               out_channels=16, \n               kernel_size=(1, 16), \n               groups=16,\n               bias=False\n            ),\n            nn.Conv2d(\n                in_channels=16, \n                out_channels=16, \n                kernel_size=(1, 1), \n                bias=False\n            ),\n            nn.BatchNorm2d(16), \n            nn.ELU(),\n            nn.AvgPool2d((1, 8)), \n            nn.Dropout(self.drop_out)\n        )\n        \n        # 假设 block_3 的输出是 (batch_size, 16, 1, T//32)，需要动态计算 T//32\n        # 这里用假设的数值 155 替代 T//32，您需要根据实际情况确定这个值\n        self.flattened_size = 16 * 1 * 155\n        \n        self.out = nn.Linear(4960, classes_num)\n    \n    def forward(self, x):\n        x = self.block_1(x)\n        #print(x.shape)\n        x = self.block_2(x)\n        #print(x.shape)\n        x = self.block_3(x)\n        #print(x.shape)\n        # 打印 x 的形状以验证 flatten 之前的尺寸\n        print(x.shape)\n        \n        x = x.view(x.size(0), -1)\n        \n        # 确认扁平化后的特征数量\n        print(x.shape)\n        \n        x = self.out(x)\n        \n        return x\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import gc\niot = torch.randn(6, Config.num_channels, 1,10000)#.cuda()\nmodel = EEGNet(in_channels=Config.num_channels,classes_num =6)#.cuda()\noutput = model(iot)\nprint(output.shape)\n\ndel iot, model\ngc.collect()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"background-color: #e0f8e6; padding: 20px; border-radius: 50px; border: 5px solid #ffa76e;\">\n    \n# Signal Processing \n    \n**ML models often require significant computational resources, and reducing the precision of the input data through quantization can lead to more efficient model training and inference. By representing numerical values with fewer bits, the memory requirements are reduced, allowing for faster processing**\n* **You can play with other filters and quantizers as well** ","metadata":{}},{"cell_type":"code","source":"from scipy.signal import cheby1, butter, lfilter\n\n\ndef quantize_data_linear(data, classes):\n    min_val, max_val = np.min(data), np.max(data)\n    bins = np.linspace(min_val, max_val, classes + 1)\n    quantized = np.digitize(data, bins) - 1\n    return quantized\n\n\ndef chebyshev_lowpass_filter(data, cutoff_freq=20, sampling_rate=200, order=4, rp=0.5):\n    nyquist = 0.5 * sampling_rate\n    normal_cutoff = cutoff_freq / nyquist\n    b, a = cheby1(order, rp, normal_cutoff, btype='low', analog=False)\n    filtered_data = lfilter(b, a, data, axis=0)\n    return filtered_data\n\nclass Dataset(torch.utils.data.Dataset):\n\n    def __init__(self, data, eegs=None, augmentations=None, test=False): \n        self.data = data\n        self.eegs = eegs\n        self.augmentations = augmentations\n        self.test = test\n        \n    def __len__(self):\n        return len(self.data)\n    \n    def __getitem__(self, index):\n        row = self.data.iloc[index]      \n        data = self.eegs[row.eeg_id]\n\n        data = np.clip(data, -1024, 1024)\n        data = np.nan_to_num(data, nan=0) / 32.0\n        \n        data = chebyshev_lowpass_filter(data, order=4, rp=0.5)\n        data = quantize_data_linear(data, 256)\n\n        samples = torch.from_numpy(data).float()\n        samples = samples.unsqueeze(1)  # 添加一个维度，现在形状应该是 (time_series_length, 1, channels)\n        samples = samples.permute(2, 1, 0)\n        if not self.test:\n            label = row[TARGETS] \n            label = torch.tensor(label).float()  \n            return samples, label\n        else:\n            return samples","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"background-color: #e0f8e6; padding: 20px; border-radius: 50px; border: 5px solid #ffa76e;\">\n    \n# CV Split Block\n    \n####  stratified k-fold cross-validation is advantageous when working with datasets containing imbalanced class distributions, and it helps in obtaining more reliable performance estimates, improving the generalization of the model, and identifying potential issues related to overfitting or underfitting.\n    \n    ","metadata":{}},{"cell_type":"code","source":"from sklearn.model_selection import StratifiedKFold\n\nskf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)\ntrain['fold'] = 0\n\nfor fold, (tr_idx, val_idx) in enumerate(skf.split(train, train['target'])):\n    train.loc[val_idx, 'fold'] = fold\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_fold_dls(df_train, df_valid):\n\n    \n    train_data = Dataset(\n        df_train, \n        eegs=raw_eegs,\n        augmentations =  get_eeg_transforms(data='valid'),\n        test = False\n    )\n    \n    val_data = Dataset(\n        df_valid, \n        eegs=raw_eegs,\n        augmentations = get_eeg_transforms(data='valid'),\n        test = False\n    )\n    train_dataloader = DataLoader(train_data, batch_size=Config.batch_size , shuffle=True, num_workers = 2)    \n    val_dataloader = DataLoader(val_data, batch_size=Config.batch_size, num_workers = 2)\n    return train_dataloader, val_dataloader, train_data, val_data","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"background-color: #e0f8e6; padding: 20px; border-radius: 50px; border: 5px solid #ffa76e;\">\n\n# <strong>Learning Rate Schduler and Optimizer</strong>\n\n#### **Common learning rate schedulers include:**\n\n**StepLR: Reduces the learning rate by a factor after a fixed number of epochs.**\n\n**MultiStepLR: Similar to StepLR but allows the learning rate to be reduced at specific epochs.**\n\n**ExponentialLR: Multiplies the learning rate by a constant factor at each epoch.**\n\n**ReduceLROnPlateau: Adjusts the learning rate based on a validation metric (e.g., reducing the learning rate if the validation loss plateaus).**\n\n**CyclicLR: Alternates between lower and upper learning rate values in a cycle.**\n\n**CosineAnnealingLR: Gradually reduces the learning rate in a cosine-shaped manner.**\n\n #### **Optimizers:** \n    \nThe optimizer is an algorithm that adjusts the model's parameters during training to minimize the loss function. It plays a crucial role in determining how quickly the model learns, converges, and generalizes.\n\nCommon optimizers include:\n\n**Stochastic Gradient Descent (SGD): Updates the model's parameters in the opposite direction of the gradient of the loss function with respect to the parameters.**\n\n**Adam: Combines ideas from RMSprop and Momentum. It adapts the learning rates for each parameter individually.**\n\n**Adagrad: Adapts the learning rates of all model parameters based on historical gradients.**\n\n**RMSprop: Similar to Adagrad but uses a moving average of squared gradients to adapt the learning rates.**\n\n**Adadelta: An extension of Adagrad that seeks to reduce its aggressive, monotonically decreasing learning rates.**\n\nNadam: Nesterov Adam optimizer, a variant of Adam incorporating Nesterov momentum</strong>","metadata":{}},{"cell_type":"code","source":"import torch.optim as optim\nimport torch.optim.lr_scheduler as lr_scheduler\n\ndef get_optimizer_and_scheduler(lr, params):\n    if Config.optimizers == 'adam':\n        optimizer = optim.Adam(params, lr=lr, weight_decay=Config.weight_decay)\n    elif Config.optimizers == 'nadam':\n        optimizer = optim.Nadam(params, lr=lr, weight_decay=Config.weight_decay)\n    elif Config.optimizers == 'adamW':\n        optimizer = optim.AdamW(params, lr=lr, weight_decay=Config.weight_decay)\n    elif Config.optimizers == 'sgd':\n        optimizer = optim.SGD(params, lr=lr, weight_decay=Config.weight_decay)\n    else:\n        return None  # Return None if an unsupported optimizer is specified\n    \n    if Config.scheduler == 'CosineAnnealingLR':\n        scheduler = lr_scheduler.CosineAnnealingLR(optimizer, T_max=Config.T_max, eta_min=Config.min_lr)\n    elif Config.scheduler == 'CosineAnnealingWarmRestarts':\n        scheduler = lr_scheduler.CosineAnnealingWarmRestarts(optimizer, T_0=Config.epochs, eta_min=Config.min_lr)\n    elif Config.scheduler == 'ReduceLROnPlateau':\n        scheduler = lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', factor=0.1, patience=7, \n                                                   threshold=0.0001, min_lr=Config.min_lr)\n    elif Config.scheduler == 'ExponentialLR':\n        scheduler = lr_scheduler.ExponentialLR(optimizer, gamma=0.85)\n    else:\n        scheduler = None  # Return None if an unsupported scheduler is specified\n\n    interval = \"epoch\"\n    return {\n        \"optimizer\": optimizer,\n        \"lr_scheduler\": {\n            \"scheduler\": scheduler,\n            \"interval\": interval,\n            \"monitor\": \"val_loss\",\n            \"frequency\": 1\n        }\n    }","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install torchtoolbox","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from torchtoolbox.tools import mixup_data, mixup_criterion\nimport torch.nn as nn\nfrom torch.nn.functional import cross_entropy\nimport torchmetrics\nimport timm\nimport sklearn.metrics\nimport sys\nsys.path.append('/kaggle/input/kaggle-kl-div')\n\nfrom kaggle_kl_div import score\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"background-color: #e0f8e6; padding: 20px; border-radius: 50px; border: 5px solid #ffa76e;\">\n    \n# Loss ","metadata":{}},{"cell_type":"code","source":"class KLDivLossWithLogits(nn.KLDivLoss):\n\n    def __init__(self):\n        super().__init__(reduction=\"batchmean\")\n\n    def forward(self, y, t):\n        y = nn.functional.log_softmax(y,  dim=1)\n        loss = super().forward(y, t)\n\n        return loss","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"background-color: #e0f8e6; padding: 20px; border-radius: 50px; border: 5px solid #ffa76e;\">\n    \n# Training Block\n    ","metadata":{}},{"cell_type":"code","source":"class EEGModel(pl.LightningModule):\n    def __init__(self, num_classes = Config.num_classes, pretrained = Config.pretrained, fold = fold):\n        super().__init__()\n        self.num_classes = num_classes\n        self.fold = fold\n        self.backbone = EEGNet(in_channels=Config.num_channels, classes_num=Config.num_classes)\n        self.loss_function = KLDivLossWithLogits()\n        self.validation_step_outputs = []\n        self.lin = nn.Softmax(dim=1)\n        self.best_score = 1000.0\n    def forward(self,images):\n        logits = self.backbone(images)\n        return logits\n        \n    def configure_optimizers(self):\n        return get_optimizer_and_scheduler(lr=Config.LR, params=self.parameters())\n\n    def train_with_mixup(self, X, y):\n        X, y_a, y_b, lam = mixup_data(X, y, alpha=Config.mixup_alpha)\n        y_pred = self(X)\n        loss_mixup = mixup_criterion(KLDivLossWithLogits(), y_pred, y_a, y_b, lam)\n        return loss_mixup\n\n    def training_step(self, batch, batch_idx):\n        image, target = batch        \n        if Config.use_mixup:\n            loss = self.train_with_mixup(image, target)\n        else:\n            y_pred = self(image)\n            loss = self.loss_function(y_pred,target)\n\n        self.log(\"train_loss\", loss, on_step=True, on_epoch=True, prog_bar=True)\n        return loss        \n\n    def validation_step(self, batch, batch_idx):\n        image, target = batch \n        y_pred = self(image)\n        val_loss = self.loss_function(y_pred, target)\n        self.log(\"val_loss\", val_loss, on_step=True, on_epoch=True, logger=True, prog_bar=True)\n        self.validation_step_outputs.append({\"val_loss\": val_loss, \"logits\": y_pred, \"targets\": target})\n\n        return {\"val_loss\": val_loss, \"logits\": y_pred, \"targets\": target}\n    \n    def train_dataloader(self):\n        return self._train_dataloader \n    \n    def validation_dataloader(self):\n        return self._validation_dataloader\n    \n    def on_validation_epoch_end(self):\n        outputs = self.validation_step_outputs\n        # print(len(outputs))\n        avg_loss = torch.stack([x['val_loss'] for x in outputs]).mean()\n        output_val = nn.Softmax(dim=1)(torch.cat([x['logits'] for x in outputs],dim=0)).cpu().detach().numpy()\n        target_val = torch.cat([x['targets'] for x in outputs],dim=0).cpu().detach().numpy()\n        self.validation_step_outputs = []\n\n        val_df = pd.DataFrame(target_val, columns = list(TARGETS))\n        pred_df = pd.DataFrame(output_val, columns = list(TARGETS))\n\n        val_df['id'] = [f'id_{i}' for i in range(len(val_df))] \n        pred_df['id'] = [f'id_{i}' for i in range(len(pred_df))] \n\n\n        avg_score = score(val_df, pred_df, row_id_column_name = 'id')\n\n        if avg_score < self.best_score:\n            print(f'Fold {self.fold}: Epoch {self.current_epoch} validation loss {avg_loss}')\n            print(f'Fold {self.fold}: Epoch {self.current_epoch} validation KDL score {avg_score}')\n            self.best_score = avg_score\n        return {'val_loss': avg_loss,'val_cmap':avg_score}","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from tqdm import tqdm\ntqdm.pandas()\n\n\ndef predict(data_loader, model):\n        \n    model.to('cuda')\n    model.eval()    \n    predictions = []\n    for batch in tqdm(data_loader):\n\n        with torch.no_grad():\n            x, y = batch\n            x = x.cuda()\n            # inputs = {key:val.reshape(val.shape[0], -1).to(config.device) for key,val in batch.items()}\n            outputs = model(x)\n            outputs = nn.Softmax(dim=1)(outputs)\n        predictions.extend(outputs.detach().cpu().numpy())\n    predictions = np.vstack(predictions)\n    return predictions\n\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from pytorch_lightning.loggers import WandbLogger\nimport gc\ntorch.set_float32_matmul_precision('high')\n\nTARGETS = data_processor.TARGETS\ndef run_training(fold_id, Config):\n    print(f\"Running training for fold {fold_id}...\")\n    logger = None\n    pred_cols = [f'pred_{t}' for t in TARGETS]\n    \n    df_train = train[train['fold']!=fold_id].copy()\n    df_valid = train[train['fold']==fold_id].copy()\n\n    print(len(df_train),'train length')\n    print(len(df_valid),'valid length')\n    \n    dl_train, dl_val, ds_train, ds_val = get_fold_dls(df_train, df_valid)\n    \n    eeg_model = EEGModel(num_classes = Config.num_classes, pretrained = Config.pretrained, fold = fold_id)\n\n    \n    early_stop_callback = EarlyStopping(monitor=\"val_loss\", min_delta=0.00, patience=Config.PATIENCE, verbose= True, mode=\"min\")\n    checkpoint_callback = ModelCheckpoint(monitor='val_loss',\n                                          dirpath= f\"{Config.output_dir}/\",\n                                      save_top_k=1,\n                                      save_last= True,\n                                      save_weights_only=False,\n                                      filename= f'eegnet_best_loss_fold{fold_id}',\n                                      verbose= True,\n                                      mode='min')\n    \n    callbacks_to_use = [checkpoint_callback,early_stop_callback]\n\n\n    trainer = pl.Trainer(\n        devices=[0],\n        \n        val_check_interval=0.5,\n        deterministic=True,\n        max_epochs=Config.epochs,        \n        logger=logger,\n        callbacks=callbacks_to_use,\n        precision=Config.PRECISION*2,\n        accelerator=\"gpu\" \n    )\n    \n\n    print(\"Running trainer.fit\")\n    trainer.fit(eeg_model, train_dataloaders = dl_train, val_dataloaders = dl_val)                \n    # trainer.\n\n    model = EEGModel.load_from_checkpoint(f'{Config.output_dir}/eegnet_best_loss_fold{fold_id}.ckpt',train_dataloader=None,validation_dataloader=None,config=Config)    \n    preds = predict(dl_val, model)  \n    print(preds.shape)\n    df_valid[pred_cols] = preds\n    df_valid.to_csv(f'{Config.output_dir}/pred_df_f{fold_id}.csv',index=False)\n    gc.collect()\n    # torch.cuda.empty_cache()\n    return preds","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\noof_df = train.copy()\npred_cols = [f'pred_{t}' for t in TARGETS]\noof_df[pred_cols] = 0.0\nfor f in Config.trn_folds:\n    val_idx = list(train[train['fold']==f].index)\n    print(len(val_idx))\n    val_preds = run_training(f, Config)    \n    oof_df.loc[val_idx, pred_cols] = val_preds\n    ","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}