{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":30833,"databundleVersionId":2640288,"sourceType":"competition"}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install nnspt &> null\n!pip install ecgmentations &> null\n!pip install torch-lr-finder &> null","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import polars as pd\n\nfrom pathlib import Path\n\ndatapath = Path('/kaggle/input/emg-handwriting-skoltech-neuroschool')\n\ntrain = pd.read_csv(datapath / 'train.csv')\ntest = pd.read_csv(datapath / 'test.csv')","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from tqdm import tqdm\n\nkeys = ['Trial','PersonId']\n\npkey = 'OnPaper'\ndkeys = [ f'EMG_{i}' for i in range(8) ] + [pkey]\n\ntkey = 'Type'\n\ntraindata = []\nvaldata = []\n\nfor (_, pidx) , group in tqdm([*train.group_by(keys)]):\n    pemg = group[dkeys].to_numpy()\n\n    target = group[tkey].to_numpy()[0] - 1\n\n    if pidx == 9 or pidx == 10:\n        valdata.append((pemg, target))\n    else:\n        traindata.append((pemg, target))\n\ntestdata = []\ntestkeys = []\n\nfor (trial, pidx) , group in tqdm([*test.group_by(keys)]):\n    pemg = group[dkeys].to_numpy()\n\n    testdata.append((pemg, ))\n    testkeys.append(f'{pidx}_{int(trial)}')","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\nimport numpy as np\nimport ecgmentations as E\n\nfrom torch.utils.data import Dataset, DataLoader\n\nclass EMGDataset(Dataset):\n    def __init__(self, data, augs=dict):\n        self.data = data\n        self.augs = augs\n\n    def __len__(self):\n        return len(self.data)\n\n    def __getitem__(self, idx):\n        emg, target = self.data[idx]\n\n        emg = emg[::2] # 1000 Hz to 500 Hz\n        target = int(target)\n\n        emg = self.augs(ecg=emg)['ecg']\n\n        return emg, target\n\ndef collate_fn(batch):\n    emgs, targets = zip(*batch)\n\n    msize = np.max([ emg.shape[0] for emg in emgs ])\n\n    pemgs = list()\n\n    for emg in emgs:\n        pemg = torch.tensor(np.ascontiguousarray(emg))\n        pemg = torch.nn.functional.pad(pemg, (0, 0, 0, msize - pemg.shape[0]),  mode='constant', value=0)\n\n        pemgs.append(pemg)\n\n    pemgs = torch.stack(pemgs).float()\n    targets = torch.tensor(targets)\n    \n    pemgs = pemgs.permute(0, 2, 1)\n\n    return pemgs, targets\n\naugs = E.Sequential([\n    E.TimeShift(p=0.1),\n    E.TimeCutout(p=0.1),\n])\n\ntrain_dataset = EMGDataset(traindata, augs)\nval_dataset = EMGDataset(valdata)\n\ntrain_dataloader = DataLoader(train_dataset, 64, shuffle=True, collate_fn=collate_fn)\nval_dataloader = DataLoader(val_dataset, 64, shuffle=False, collate_fn=collate_fn)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\n\nfrom nnspt.blocks.encoders import Encoder\n\nclass SimpleClassifier(torch.nn.Module):\n    \"\"\"The example of a simple classifier with CNN encoder\n       The architecture of the encoder is similar to timm one (timm)\n    \"\"\"\n    def __init__(self, nleads, nclasses):\n        \"\"\"\n            :args:\n                nleads (int): the number of leads of the inputed ECGs\n                nclasses (int): the number of predicted classes\n        \"\"\"\n        super().__init__()\n        self.encoder = Encoder(in_channels=nleads, depth=5, name='timm-efficientnet-b2')\n        self.head = torch.nn.Sequential(\n            torch.nn.AdaptiveAvgPool1d(1),\n            torch.nn.Flatten(),\n            torch.nn.Linear(self.encoder.out_channels[-1], nclasses)\n        )\n\n    def forward(self, x):\n        f = self.encoder(x)\n        x = self.head(f[-1])\n\n        return x","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# from itertools import repeat\n# from torch_lr_finder import LRFinder\n\n# device = 'cuda:0' if torch.cuda.is_available() else 'cpu'\n\n# model = SimpleClassifier(nleads=9, nclasses=10)\n# model.to(device)\n\n# loss = lambda *args: torch.nn.functional.cross_entropy(*args)\n# opt = torch.optim.AdamW(model.parameters(), lr=1e-5)\n\n# finder = LRFinder(model, opt, loss, device=device)\n# finder.range_test(train_dataloader, val_loader=val_dataloader, end_lr=1e-2, num_iter=len(train_dataloader), step_mode='linear')\n# finder.plot(log_lr=False)\n# finder.reset()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import copy\n\nfrom tqdm import tqdm\nfrom sklearn import metrics\n\ndevice = 'cuda:0' if torch.cuda.is_available() else 'cpu'\n\nmodel = SimpleClassifier(nleads=9, nclasses=10)\nmodel.to(device)\n\nopt = torch.optim.AdamW(model.parameters(), lr=8e-03)\n\nloss_his, train_loss = [], []\n\nbest_score = 0.\nbest_state_dict = copy.deepcopy(model.state_dict())\n\nnepochs = 250\n\nfor epoch in range(nepochs):\n    model.train()\n\n    for leads_batch, target_batch in (train_dataloader):\n        leads_batch = leads_batch.to(device)\n        target_batch = target_batch.long().to(device)\n\n        pred_logit_batch = model(leads_batch)\n\n        loss = torch.nn.functional.cross_entropy(pred_logit_batch, target_batch)\n        loss.backward()\n\n        opt.step()\n        opt.zero_grad()\n        \n        train_loss.append(loss.item())\n\n    loss_his.append(np.mean(train_loss))\n    train_loss.clear()\n\n    print('[Epoch {}/{}] [Loss: {}]'.format(epoch+1, nepochs, loss_his[-1]))\n    \n    if (epoch + 1) % 5 == 0: \n        with torch.no_grad():\n            model.eval()\n\n            y_pred = []\n            y_true = []\n\n            for leads_batch, target_batch in (val_dataloader):\n                leads_batch = leads_batch.to(device)\n\n                pred_logit_batch = model(leads_batch)\n\n                y_true.append(target_batch)\n                y_pred.append(pred_logit_batch.argmax(dim=1).detach().cpu().numpy())\n\n            y_true = np.concatenate(y_true, axis=0)\n            y_pred = np.concatenate(y_pred, axis=0)\n\n            score = metrics.accuracy_score(y_true, y_pred)\n\n            print('[Epoch {}/{}] [Score: {}] [Best score: {}]'.format(epoch+1, nepochs, score, best_score))\n\n            if score > best_score:\n                best_score = score\n                best_state_dict = copy.deepcopy(model.state_dict())\n\nmodel.load_state_dict(best_state_dict)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.eval()\n\ny_pred = []\n\nfor key, pemg in tqdm(zip(testkeys, testdata)):\n    leads_batch = torch.tensor([pemg[0][::2].T]).float()    \n    leads_batch = leads_batch.to(device)\n    pred_logit_batch = model(leads_batch)\n\n    y_pred.append(pred_logit_batch.argmax(dim=1).detach().cpu().numpy()[0])\n\nsub = pd.DataFrame({'Id': testkeys, 'Category': y_pred})\nsub.write_csv('sub.csv', separator=',')","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!head sub.csv","metadata":{},"execution_count":null,"outputs":[]}]}