{"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"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np\nfrom zipfile import ZipFile\nimport matplotlib.pyplot as plt\nimport os\nimport pandas as pd\nimport torch\nfrom torch.utils.data import DataLoader, Dataset\nimport torch.nn as nn\nimport torch.optim as optim\nimport random\nimport torch.nn.functional as F\nimport argparse\nfrom sklearn import metrics\nfrom tqdm.notebook import tqdm\nimport gc\nimport shutil ","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%time\nFILE_PATH = '../input/grasp-and-lift-eeg-detection'\nlist_dir = os.listdir(FILE_PATH)\n\nfor zipfile in list_dir:\n    with ZipFile(os.path.join(FILE_PATH, zipfile), 'r') as z:\n        z.extractall()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels = ['HandStart', 'FirstDigitTouch', 'BothStartLoadPhase', 'LiftOff',\n       'Replace', 'BothReleased']","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"torch.manual_seed(2021)\nnp.random.seed(2021)\ndevice = 'cuda:0' if torch.cuda.is_available() else 'cpu'\n\nparser = argparse.ArgumentParser()\nparser.add_argument(\"--n_epochs\", type=int, default=1, help=\"number of epochs of training\")\nparser.add_argument(\"--batch_size\", type=int, default=1024, help=\"size of the batches\")\nparser.add_argument(\"--lr\", type=float, default=0.005, help=\"adam's learning rate\")\nparser.add_argument(\"--b1\", type=float, default=0.5, help=\"adam: decay of first order momentum of gradient\")\nparser.add_argument(\"--b2\", type=float, default=0.99, help=\"adam: decay of first order momentum of gradient\")\nparser.add_argument(\"--n_cpu\", type=int, default=4, help=\"number of cpu threads to use during batch generation\")\nparser.add_argument(\"--in_len\", type=int, default=2**10, help=\"length of the input fed to neural net\")\nparser.add_argument(\"--in_channels\", type=int, default=32, help=\"number of signal channels\")\nparser.add_argument(\"--out_channels\", type=int, default=6, help=\"number of classes\")\nparser.add_argument(\"--chunk\", type=int, default=1000, help=\"length of splited chunks\")\nopt, unknown = parser.parse_known_args()\nprint(device)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%time\ndef read_csv(data, events):\n    x = pd.read_csv(data)\n    y = pd.read_csv(events)\n    id = '_'.join(x.iloc[0, 0].split('_')[:-1])\n    x = x.iloc[:,1:].values\n    y = y.iloc[:,1:].values\n    return x, y\n    \n\ntrainset = []\ngt = []\nfor filename in tqdm(os.listdir('./train')):\n    if 'data' in filename:\n        data_file_name = os.path.join('./train', filename)\n        id = filename.split('.')[0]\n        events_file_name = os.path.join('./train', '_'.join(id.split('_')[:-1]) + '_events.csv')\n        x, y = read_csv(data_file_name, events_file_name)\n        trainset.append(x.T.astype(np.float32))\n        gt.append(y.T.astype(np.float32))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"valid_dataset = trainset[-2:]\nvalid_gt = gt[-2:]\ntrainset = trainset[:-2]\ngt = gt[:-2]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def resample_data(gt, chunk_size=opt.chunk):\n    \"\"\"\n    split long signals to smaller chunks, discard no-events chunks  \n    \"\"\"\n    total_discard_chunks = 0\n    mean_val = []\n    threshold = 0.01\n    index = []\n    \n    for i in range(len(gt)):\n        for j in range(0, gt[i].shape[1], chunk_size):\n            mean_val.append(np.mean(gt[i][:, j:min(gt[i].shape[1],j+chunk_size)]))\n            if mean_val[-1] < threshold and j > 0:  # discard chunks with low events time\n                total_discard_chunks += 1\n                index.extend([(i, k) for k in range(j, min(gt[i].shape[1],j+chunk_size), chunk_size//100)])\n            else:\n                index.extend([(i, k) for k in range(j, min(gt[i].shape[1],j+chunk_size))])\n\n    plt.plot([0, len(mean_val)], [threshold, threshold], color='r')\n    plt.scatter(range(len(mean_val)), mean_val, s=1)\n    plt.show()\n    print('Total number of chunks discarded: {} chunks'.format(total_discard_chunks))\n    print('{}% data'.format(total_discard_chunks/len(mean_val)))\n    del mean_val\n    gc.collect()\n    return index","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%time\nclass EEGSignalDataset(Dataset):\n    def __init__(self, data, gt, soft_label=True, train=True):\n        self.data = data\n        self.gt = gt\n        self.train = train\n        self.soft_label = soft_label\n        if train:\n            self.index = resample_data(gt)\n        else:\n            self.index = [(i, j) for i in range(len(data)) for j in range(data[i].shape[1])]\n    \n    def __getitem__(self, i):\n        i, j = self.index[i]\n        raw_data, label = self.data[i][:,max(0, j-opt.in_len+1):j+1], \\\n                self.gt[i][:,j]\n        \n        pad = opt.in_len - raw_data.shape[1]\n        if pad:\n            raw_data = np.pad(raw_data, ((0,0),(pad,0)), 'constant',constant_values=0)\n\n        raw_data, label = torch.from_numpy(raw_data.astype(np.float32)),\\\n                            torch.from_numpy(label.astype(np.float32))\n        if self.soft_label:\n            label[label < .05] = .05\n        return raw_data, label\n            \n    \n    def __len__(self):\n        return len(self.index)\n    \ndataset = EEGSignalDataset(trainset, gt) \ndataloader = DataLoader(dataset, batch_size = opt.batch_size,\\\n                                       num_workers = opt.n_cpu, shuffle=True)\nprint(len(dataset))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class NNet(nn.Module):\n    def __init__(self, in_channels=opt.in_channels, out_channels=opt.out_channels):\n        super(NNet, self).__init__()\n        self.hidden = 32\n        self.net = nn.Sequential(\n            nn.BatchNorm1d(opt.in_channels),\n            nn.Conv1d(opt.in_channels, opt.in_channels, 5, padding=2),\n            nn.Conv1d(self.hidden, self.hidden, 16, stride=16),\n            nn.LeakyReLU(0.1),\n            nn.Conv1d(self.hidden, self.hidden, 7, padding=3),\n        )\n        for i in range(2):\n            self.net.add_module('conv{}'.format(i), \\\n                                self.__block(self.hidden, self.hidden)) # 16\n        \n        self.mid = nn.Sequential(\n            self.__block(self.hidden, self.hidden),\n            self.__block(self.hidden, self.hidden)\n        ) # 4\n        self.final = nn.Sequential(\n            nn.Linear(256, 64),\n            nn.LeakyReLU(0.1),\n            nn.Linear(64, 6),\n            nn.Sigmoid()\n        )\n        \n    def __block(self, inchannels, outchannels):\n        return nn.Sequential(\n            nn.MaxPool1d(2, 2),\n            nn.Conv1d(inchannels, outchannels, 5, padding=2),\n            nn.LeakyReLU(0.1),\n            nn.BatchNorm1d(outchannels),\n            nn.Conv1d(outchannels, outchannels, 5, padding=2),\n            nn.LeakyReLU(0.1)\n        )\n    \n    def forward(self, x):\n        x = self.net(x)\n        y = self.mid(x)\n        y = torch.cat((x[..., -4:], y), dim=-1).view(x.shape[0], -1)\n        return self.final(y)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"testset = EEGSignalDataset(valid_dataset, valid_gt, train=False, soft_label=False) \ntestloader = DataLoader(testset, batch_size = opt.batch_size,\\\n                                       num_workers = opt.n_cpu, shuffle=False)\nvalid_gt = np.concatenate(valid_gt, axis=1)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Train","metadata":{}},{"cell_type":"code","source":"def calc_valid_auc(nnet):\n    nnet.eval()\n    y_pred = []\n    with torch.no_grad():\n        for x, _ in testloader:\n            x = x.to(device)\n            pred = nnet(x).detach().cpu().numpy()\n            y_pred.append(pred)\n    \n    y_pred = np.concatenate(y_pred, axis=0)\n    return metrics.roc_auc_score(valid_gt.T, y_pred)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"nnet = NNet()\nnnet.to(device)\nloss_fnc = nn.BCELoss()\nadam = optim.Adam(nnet.parameters(), lr=opt.lr, betas=(opt.b1, opt.b2))\nloss_his, train_loss, valid_auc, train_auc = [], [], [], []\nnnet.train()\nfor epoch in range(opt.n_epochs):\n    p_bar = tqdm(dataloader)\n    for i, (x, y) in enumerate(p_bar):\n        x, y = x.to(device), y.to(device)\n        pred = nnet(x)\n        loss = loss_fnc(pred.squeeze(dim=-1), y)\n        adam.zero_grad()\n        loss.backward()\n        nn.utils.clip_grad_value_(nnet.parameters(), 2.)\n        adam.step()\n        train_loss.append(loss.item())\n        p_bar.set_description('[Loss: {}]'.format(train_loss[-1]))\n        if i % 50 == 0:\n            loss_his.append(np.mean(train_loss))\n            train_loss.clear()\n#         if i % 1500 == 0:\n#             y[y<0.1]=0\n#             train_auc.append(metrics.roc_auc_score(y.detach().cpu().numpy(), \\\n#                                 pred.detach().cpu().numpy()))\n#             valid_auc.append(calc_valid_auc(nnet))\n    print('[Epoch {}/{}] [Loss: {}]'.format(epoch+1, opt.n_epochs, loss_his[-1]))\n    \ntorch.save(nnet.state_dict(), 'model.pt')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.plot(range(len(loss_his)), loss_his, label='loss')\nplt.legend()\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# plt.plot(range(len(train_auc)), train_auc, label='train AUC')\n# plt.plot(range(len(valid_auc)), valid_auc, label='valid AUC')\n# plt.legend()\n# plt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Test on validset","metadata":{}},{"cell_type":"code","source":"nnet.eval()\ny_pred = []\nwith torch.no_grad():\n    for x, _ in tqdm(testloader):\n        x = x.to(device)\n        pred = nnet(x).detach().cpu().numpy()\n        y_pred.append(pred)\n        ","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def plot_roc(y_true, y_pred):\n    fig, axs = plt.subplots(3, 2, figsize=(15,13))\n    for i, label in enumerate(labels):\n        fpr, tpr, _ = metrics.roc_curve(y_true[i], y_pred[i])\n        ax = axs[i//2, i%2]\n        ax.plot(fpr, tpr)\n        ax.set_title(label+\" ROC\")\n        ax.plot([0, 1], [0, 1], 'k--')\n\n    plt.show()\n    \ny_pred = np.concatenate(y_pred, axis=0)\nplot_roc(valid_gt, y_pred.T)\nprint('auc roc: ', metrics.roc_auc_score(valid_gt.T, y_pred))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"del y_pred\ndel testset\ndel testloader\ndel valid_dataset\ndel valid_gt\ngc.collect()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"i = 1231\nwith torch.no_grad():\n    input = dataset[i][0].unsqueeze(dim=0)\n    print(nnet(input.to(device)))\n    print(dataset[i][1])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"del dataset\ndel dataloader\ndel trainset\ndel gt\ngc.collect()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Create submission","metadata":{}},{"cell_type":"code","source":"class EEGSignalTestset(Dataset):\n    def __init__(self, data):\n        self.data = data\n        self.index = [(i, j) for i in range(len(data)) for j in range(data[i].shape[1])]\n    \n    def __getitem__(self, i):\n        i, j = self.index[i]\n        raw_data = self.data[i][:,max(0, j-opt.in_len+1):j+1]\n        \n        pad = opt.in_len - raw_data.shape[1]\n        if pad:\n            raw_data = np.pad(raw_data, ((0,0),(pad,0)), 'constant',constant_values=0)\n\n        raw_data = torch.from_numpy(raw_data.astype(np.float32))\n        return raw_data\n            \n    \n    def __len__(self):\n        return len(self.index)\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%time\ntestset = []\ntrial_len = {}\nFNAME = \"./test/subj{}_series{}_{}.csv\"\n\nfor subj in range(1,13):\n    for series in [9, 10]:\n        data_file_name = FNAME.format(subj, series, 'data')\n        x = pd.read_csv(data_file_name).iloc[:,1:].values\n        testset.append(x.T.astype(np.float32))\n        trial_len['{}_{}'.format(subj, series)] = testset[-1].shape[-1]\n        ","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"testset = EEGSignalTestset(testset)\ndataloader = DataLoader(testset, batch_size = opt.batch_size,\\\n                                       num_workers = opt.n_cpu, shuffle=False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"y_pred = []\nwith torch.no_grad():\n    for x in tqdm(dataloader):\n        x = x.to(device)\n        pred = nnet(x).detach().cpu().numpy()\n        y_pred.append(pred)\n        \ny_pred = np.concatenate(y_pred, axis=0)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%time\nsubmission = pd.DataFrame(y_pred, index=\\\n    ['subj{}_series{}_{}'.format(sbj, i, j) for sbj in range(1,13) for i in [9,10] for j in range(trial_len['{}_{}'.format(sbj, i)])],\\\n                         columns=labels)\nsubmission.to_csv('Submission.csv',index_label='id',float_format='%.3f')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"a = pd.read_csv('Submission.csv')\na.tail()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"try:\n    shutil.rmtree('./train')\n    shutil.rmtree('./test')\n    os.remove('sample_submission.csv')\nexcept:\n    pass","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}