{"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":"markdown","source":"# Implementing a basic CNN-RNN classifier","metadata":{}},{"cell_type":"markdown","source":"Special thanks to the authors of [this](https://github.com/pytorch/ignite/blob/master/examples/notebooks/EfficientNet_Cifar100_finetuning.ipynb) notebook, and [this one](https://github.com/PacktPublishing/PyTorch-Computer-Vision-Cookbook/blob/master/Chapter10/DeployingModel.ipynb), and also [this one](https://www.kaggle.com/protan/ignite-example).","metadata":{}},{"cell_type":"code","source":"import os\nimport pandas as pd\nimport torch\nimport numpy as np\nimport random\nnp.random.seed(2021)\nrandom.seed(2021)\ntorch.manual_seed(2021)","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:19:54.467010Z","iopub.execute_input":"2021-07-26T21:19:54.467397Z","iopub.status.idle":"2021-07-26T21:19:54.475427Z","shell.execute_reply.started":"2021-07-26T21:19:54.467364Z","shell.execute_reply":"2021-07-26T21:19:54.474471Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Dataset","metadata":{}},{"cell_type":"code","source":"# Returns list containing all ids (XXXXX) in the specified folder\ndef get_ids(d=\"../input/rsna-miccai-png/train\"):\n    files = [f for f in sorted(os.listdir(d))]\n    return files\n\n# Reads the labels in the csv file\ndef get_labels(f=\"../input/rsna-miccai-brain-tumor-radiogenomic-classification/train_labels.csv\"):\n    df = pd.read_csv(f).sort_values(by=\"BraTS21ID\")\n    return df[\"MGMT_value\"].tolist()","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:19:54.477016Z","iopub.execute_input":"2021-07-26T21:19:54.477515Z","iopub.status.idle":"2021-07-26T21:19:54.483786Z","shell.execute_reply.started":"2021-07-26T21:19:54.477477Z","shell.execute_reply":"2021-07-26T21:19:54.482917Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from torch.utils.data import Dataset, DataLoader, Subset\nimport glob\nfrom PIL import Image\nimport cv2\n\n\n# Make sure vids_path does not contain a trailing /\n# vid_type is one in [\"FLAIR\", \"T1w\", \"T1wCE\", \"T2w\"]\nclass DatasetRSNE(Dataset):\n    def __init__(self, ids, labels=None, transform=None, vids_path=\"../input/rsna-miccai-png/train\",\n                 vid_type=[\"FLAIR\", \"T1w\"], d=(288, 288, 96)):      \n        self.transform = transform\n        self.ids = ids\n        self.labels = labels\n        self.vids_path = vids_path\n        self.vid_type = vid_type\n        self.d = d\n        \n    def __len__(self):\n        return len(self.ids)\n    \n    def __getitem__(self, idx):\n        all_frames = []\n        for vid in self.vid_type:\n            path2imgs = glob.glob(self.vids_path + \"/\" + self.ids[idx] + \"/\" + vid + \"/*.png\")\n            frames = []\n            for p2i in path2imgs:\n                frame = Image.open(p2i)\n                frames.append(frame)\n            frames_tr = []\n            if len(frames)==0:\n                # if no frames are available for the video, return a tensor of zeros\n                frames_tr = torch.zeros(self.d[2],1,self.d[0],self.d[1])\n            else:\n                frames = np.array([cv2.resize(np.array(frames[i]), dsize=(self.d[1],self.d[0]), interpolation=cv2.INTER_LINEAR) for i in range(len(frames))])\n                frames = np.array([cv2.resize(frames.transpose(1,2,0)[i], dsize=(self.d[2],self.d[1]), interpolation=cv2.INTER_LINEAR) for i in range(self.d[0])])\n                frames = frames.transpose(2,0,1)\n\n                seed = np.random.randint(1e9)\n                for frame in frames:\n                    random.seed(seed)\n                    np.random.seed(seed)\n                    frame = self.transform(frame)\n                    frames_tr.append(frame)\n                frames_tr = torch.stack(frames_tr)\n            all_frames.append(frames_tr)\n        all_frames = torch.squeeze(torch.stack(all_frames, dim=1))\n        if self.labels is not None:\n            label = self.labels[idx]\n            return all_frames, torch.tensor(label, dtype=torch.float32)\n        return all_frames","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:19:54.485511Z","iopub.execute_input":"2021-07-26T21:19:54.486164Z","iopub.status.idle":"2021-07-26T21:19:54.501930Z","shell.execute_reply.started":"2021-07-26T21:19:54.486127Z","shell.execute_reply":"2021-07-26T21:19:54.501059Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torchvision.transforms as transforms\nfrom sklearn.preprocessing import StandardScaler\nimport SimpleITK as sitk\n\n# Many other transforms are possible\n#            transforms.RandomHorizontalFlip(p=0.5),  \n#            transforms.RandomAffine(degrees=0, translate=(0.1,0.1)),    \n#            ...\n\n# Applies the N4 Bias Field Correction to remove radiofrequency inhomogeneity\nclass N4BiasFieldCorrect(object):\n    \"\"\" Apply SimpleITK.N4BiasFieldCorrectionImageFilter \"\"\"\n\n    def __init__(self, max_iter=30):\n        assert isinstance(max_iter, (int))\n        self.max_iter = max_iter\n\n    def __call__(self, sample):\n        inputImage = sitk.GetImageFromArray(sample)\n        maskImage = sitk.GetImageFromArray((sample > 0.1) * 1)  # idk what this 0.1 represents, perhaps should change it\n        inputImage = sitk.Cast(inputImage, sitk.sitkFloat32)\n        maskImage = sitk.Cast(maskImage, sitk.sitkUInt8)\n        corrector = sitk.N4BiasFieldCorrectionImageFilter()\n        numberFittingLevels = 1\n        if self.max_iter is not None:\n            corrector.SetMaximumNumberOfIterations([self.max_iter] * numberFittingLevels)\n        corrected_image = corrector.Execute(inputImage, maskImage)\n\n        # img_[p] = sitk.GetArrayFromImage(corrected_image)\n        img = torch.from_numpy(np.array(corrected_image))\n\n        return img\n\n\nclass std_sc(object):\n    \"\"\" Centers with the mean and scales by the \"\"\"\n    def __init__(self):\n        pass\n\n    def __call__(self, sample): \n        sc = StandardScaler()\n        img_ = sc.fit_transform(sample)\n\n\ntrain_transforms = transforms.Compose([\n            #transforms.Resize((h,w)),\n            transforms.ToTensor()\n            #N4BiasFieldCorrect()  # for the moment this doesn't work and I don't know what it does so I do not include it as a transform\n            #std_sc()  # could add a standard scaling here but I don't think it makes much sense\n            ])\ntest_transforms = transforms.Compose([\n#             transforms.Resize((h,w)),\n            transforms.ToTensor()\n            #N4BiasFieldCorrect()  # for the moment this doesn't work and I don't know what it does so I do not include it as a transform\n            #std_sc()  # could add a standard scaling here but I don't think it makes much sense\n            ])","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:19:54.505668Z","iopub.execute_input":"2021-07-26T21:19:54.506201Z","iopub.status.idle":"2021-07-26T21:19:54.518370Z","shell.execute_reply.started":"2021-07-26T21:19:54.506165Z","shell.execute_reply":"2021-07-26T21:19:54.517550Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"training_ids, training_labels = get_ids(\"../input/rsna-miccai-png/train\"), get_labels(\"../input/rsna-miccai-brain-tumor-radiogenomic-classification/train_labels.csv\")\nn = len(training_ids)\nprint(len(training_ids), len(training_labels))\n\ntrain_ids, train_labels = training_ids[:(n // 10) * 7], training_labels[:(n // 10) * 7]\nprint(len(train_ids), len(train_labels))\nval_ids, val_labels = training_ids[(n // 10) * 7:], training_labels[(n // 10) * 7:]\nprint(len(val_ids), len(val_labels))\n\ntest_ids = get_ids(\"../input/rsna-miccai-png/test\")\nprint(len(test_ids))","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:19:54.520292Z","iopub.execute_input":"2021-07-26T21:19:54.520625Z","iopub.status.idle":"2021-07-26T21:19:54.539886Z","shell.execute_reply.started":"2021-07-26T21:19:54.520598Z","shell.execute_reply":"2021-07-26T21:19:54.539084Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_ds = DatasetRSNE(ids= train_ids, labels= train_labels, transform= train_transforms)\nprint(len(train_ds))\n\nval_ds = DatasetRSNE(ids= val_ids, labels= val_labels, transform= test_transforms)\nprint(len(val_ds))\n\n\nimgs, label = train_ds[100]\nimgs.shape, label, torch.min(imgs), torch.max(imgs)","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:19:54.542921Z","iopub.execute_input":"2021-07-26T21:19:54.543194Z","iopub.status.idle":"2021-07-26T21:19:54.975169Z","shell.execute_reply.started":"2021-07-26T21:19:54.543167Z","shell.execute_reply":"2021-07-26T21:19:54.974338Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_transformer = transforms.Compose([\n            #transforms.Resize((h,w)),\n            transforms.ToTensor(),\n            ])\ntest_ds = DatasetRSNE(ids= test_ids, transform= test_transformer, vids_path= \"../input/rsna-miccai-png/test\")","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:19:54.976716Z","iopub.execute_input":"2021-07-26T21:19:54.977070Z","iopub.status.idle":"2021-07-26T21:19:54.981921Z","shell.execute_reply.started":"2021-07-26T21:19:54.977032Z","shell.execute_reply":"2021-07-26T21:19:54.980843Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"imgs = test_ds[5]\nimgs.shape, label, torch.min(imgs), torch.max(imgs)","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:19:54.984051Z","iopub.execute_input":"2021-07-26T21:19:54.984746Z","iopub.status.idle":"2021-07-26T21:19:56.157712Z","shell.execute_reply.started":"2021-07-26T21:19:54.984707Z","shell.execute_reply":"2021-07-26T21:19:56.156934Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def collate_fn_train_rnn(batch):\n    imgs_batch, label_batch = list(zip(*batch))\n    imgs_batch = [imgs for imgs in imgs_batch if len(imgs)>0]\n    label_batch = [torch.tensor(l) for l, imgs in zip(label_batch, imgs_batch) if len(imgs)>0]\n    imgs_tensor = torch.stack(imgs_batch) if len(imgs_batch) > 0 else torch.zeros(batch_size,4,h,w)\n                        # ensure that the training does not crash even if the video contains no frames\n    labels_tensor = torch.stack(label_batch)\n    return imgs_tensor,labels_tensor\n\ndef collate_fn_test_rnn(batch):\n    imgs_batch = [*batch]\n    imgs_batch = [imgs for imgs in imgs_batch if len(imgs)>0]\n    imgs_tensor = torch.stack(imgs_batch)\n    return imgs_tensor\n\n\nbatch_size = 4\ntrain_dl = DataLoader(train_ds, batch_size= batch_size,\n                      shuffle=True, collate_fn= collate_fn_train_rnn)\nval_dl = DataLoader(val_ds, batch_size= batch_size,\n                      shuffle=True, collate_fn= collate_fn_train_rnn)\ntest_dl = DataLoader(test_ds, batch_size= batch_size,\n                     shuffle=False, collate_fn= collate_fn_test_rnn)","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:19:56.159368Z","iopub.execute_input":"2021-07-26T21:19:56.159721Z","iopub.status.idle":"2021-07-26T21:19:56.167688Z","shell.execute_reply.started":"2021-07-26T21:19:56.159685Z","shell.execute_reply":"2021-07-26T21:19:56.166889Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# for xb,yb in train_dl:\n#     print(xb.shape, yb.shape)\n#     break\n    \n# for xb,yb in val_dl:\n#     print(xb.shape, yb.shape)\n#     break\n\n# for xb in test_dl:\n#     print(xb.shape)\n#     break","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:19:56.169077Z","iopub.execute_input":"2021-07-26T21:19:56.169642Z","iopub.status.idle":"2021-07-26T21:19:56.177605Z","shell.execute_reply.started":"2021-07-26T21:19:56.169604Z","shell.execute_reply":"2021-07-26T21:19:56.176776Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Define model","metadata":{}},{"cell_type":"code","source":"from torch import nn\nfrom torchvision import models\n\nclass Resnet18Rnn(nn.Module):\n    def __init__(self, params_model):\n        super(Resnet18Rnn, self).__init__()\n        out_features = params_model[\"out_features\"]\n        dr_rate= params_model[\"dr_rate\"]\n        pretrained = params_model[\"pretrained\"]\n        self.rnn_hidden_size = params_model[\"rnn_hidden_size\"]\n        rnn_num_layers = params_model[\"rnn_num_layers\"]\n        self.bidirectional = bidirectional = params_model[\"bidirectional\"]\n        self.stack_input = params_model[\"stack_input\"]  # if True, stacks the input image (which has 1 channel) to have 3 channels\n                                                        # otherwise adds a convolutional layer to go from 1 to 3 channels\n        self.n_types = params_model[\"n_types\"]\n        \n        self.In1Out3 = nn.Sequential(\n                    nn.Conv2d(in_channels= 1, out_channels= 3, kernel_size= 3, padding= 1),\n                    nn.BatchNorm2d(3),\n                    nn.ReLU())\n        self.baseModel1 = models.resnet18(pretrained=False)\n        self.baseModel2 = models.resnet18(pretrained=False)\n        self.baseModel3 = models.resnet18(pretrained=False)\n        self.baseModel4 = models.resnet18(pretrained=False)\n        self.rnn1 = nn.LSTM(self.baseModel1.fc.in_features, self.rnn_hidden_size, rnn_num_layers, bidirectional=bidirectional)\n        self.rnn2 = nn.LSTM(self.baseModel2.fc.in_features, self.rnn_hidden_size, rnn_num_layers, bidirectional=bidirectional)\n        self.rnn3 = nn.LSTM(self.baseModel3.fc.in_features, self.rnn_hidden_size, rnn_num_layers, bidirectional=bidirectional)\n        self.rnn4 = nn.LSTM(self.baseModel4.fc.in_features, self.rnn_hidden_size, rnn_num_layers, bidirectional=bidirectional)\n        self.mods = [self.baseModel1, self.baseModel2, self.baseModel3, self.baseModel4]\n        self.RNNs = [self.rnn1, self.rnn2, self.rnn3, self.rnn4]\n        for i in range(self.n_types):\n            if pretrained:\n                self.mods[i].load_state_dict(torch.load(\"../input/pretrained-model-weights-pytorch/resnet18-5c106cde.pth\"))\n            self.mods[i].fc = Identity()        \n        self.dropout= nn.Dropout(dr_rate)\n        self.fc1 = nn.Linear(self.rnn_hidden_size*self.n_types*(self.bidirectional+1), out_features)  # if two classes, just one output neuron\n        self.sigm = nn.Sigmoid()                             # with logistic activation function\n\n    def forward(self, x):\n        # dim B, T, n_types, H, W\n        b_z, ts, c, h, w = x.shape\n        outputs = []\n        for v_type in range(self.n_types):  # up to four video types (FLAIR, T1w, T1wCE, T2w)\n            xx = x[:,:,v_type,:,:]\n            ii = 0\n            if self.stack_input:\n                xx = torch.stack([xx for _ in range(3)], dim=2)\n                xx = torch.squeeze(xx, 3)\n                y = self.mods[v_type](xx[:,ii])\n            else:\n                y = self.mods[v_type](self.In1Out3(xx[:,ii]))\n            # dim B, T, 3, H, W\n            out, (hn, cn) = self.RNNs[v_type](y.unsqueeze(1))\n            for ii in range(1, ts):\n                if self.stack_input:\n                    y = self.mods[v_type](xx[:,ii])\n                else:\n                    y = self.mods[v_type](self.In1Out3(xx[:,ii]))\n                out, (hn, cn) = self.RNNs[v_type](y.unsqueeze(1), (hn, cn))\n            outputs.append(out)\n\n        outputs = torch.reshape(torch.stack(outputs), (-1,self.rnn_hidden_size*self.n_types*(self.bidirectional+1)))\n        outputs = self.sigm(self.dropout(self.fc1(outputs)))\n        return torch.reshape(outputs, (-1,))\n\nclass Identity(nn.Module):\n    def __init__(self):\n        super(Identity, self).__init__()\n    def forward(self, x):\n        return x","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:19:56.179245Z","iopub.execute_input":"2021-07-26T21:19:56.179618Z","iopub.status.idle":"2021-07-26T21:19:56.202571Z","shell.execute_reply.started":"2021-07-26T21:19:56.179582Z","shell.execute_reply":"2021-07-26T21:19:56.201668Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Select device\nif torch.cuda.is_available():\n    device = torch.device('cuda')\nelse:\n    device = torch.device('cpu')\n    print(\"WARNING: Training without GPU can be very slow!\")","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:19:56.205800Z","iopub.execute_input":"2021-07-26T21:19:56.206192Z","iopub.status.idle":"2021-07-26T21:19:56.265607Z","shell.execute_reply.started":"2021-07-26T21:19:56.206163Z","shell.execute_reply":"2021-07-26T21:19:56.264614Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"params_model={\n        \"out_features\": 1,\n        \"dr_rate\": 0.1,\n        \"pretrained\" : True,\n        \"rnn_num_layers\": 1,\n        \"rnn_hidden_size\": 150,\n        \"bidirectional\": True,\n        \"stack_input\": True,\n        \"n_types\": 1}\n\nmy_resnet18rnn = Resnet18Rnn(params_model).to(device).train()\n\n# my_resnet18rnn.load_state_dict(torch.load('../input/checkpoint5600/cnnrnn_cnnrnn_0.5600.pt')) # path of your weights\n# my_resnet18rnn.to(device).train()","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:19:56.268247Z","iopub.execute_input":"2021-07-26T21:19:56.268583Z","iopub.status.idle":"2021-07-26T21:20:03.415701Z","shell.execute_reply.started":"2021-07-26T21:19:56.268551Z","shell.execute_reply":"2021-07-26T21:20:03.414795Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# my_resnet18rnn","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:20:03.417940Z","iopub.execute_input":"2021-07-26T21:20:03.418317Z","iopub.status.idle":"2021-07-26T21:20:03.423192Z","shell.execute_reply.started":"2021-07-26T21:20:03.418282Z","shell.execute_reply":"2021-07-26T21:20:03.422142Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from itertools import chain\n\nimport torch.optim as optim\n\ncriterion = nn.BCELoss()\n\noptimizer = optim.Adam([\n                        {\"params\": my_resnet18rnn.baseModel1.parameters(), 'lr': 5e-4},\n                        {\"params\": my_resnet18rnn.baseModel2.parameters(), 'lr': 5e-4},\n                        {\"params\": my_resnet18rnn.baseModel3.parameters(), 'lr': 5e-4},\n                        {\"params\": my_resnet18rnn.baseModel4.parameters(), 'lr': 5e-4},\n                        {\"params\": my_resnet18rnn.rnn1.parameters()},\n                        {\"params\": my_resnet18rnn.rnn2.parameters()},\n                        {\"params\": my_resnet18rnn.rnn3.parameters()},\n                        {\"params\": my_resnet18rnn.rnn4.parameters()},\n                        {\"params\": my_resnet18rnn.In1Out3.parameters()},\n                        {\"params\": my_resnet18rnn.fc1.parameters()}\n                       ], lr=1e-3, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, amsgrad=False)\n\n\nfrom torch.optim.lr_scheduler import ReduceLROnPlateau\nscheduler = ReduceLROnPlateau(optimizer, mode='min', factor=0.1, patience=5, threshold=1e-5,\n                              threshold_mode='abs', min_lr=1e-6, eps=1e-08, verbose=False)","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:20:03.424747Z","iopub.execute_input":"2021-07-26T21:20:03.425301Z","iopub.status.idle":"2021-07-26T21:20:03.445881Z","shell.execute_reply.started":"2021-07-26T21:20:03.425262Z","shell.execute_reply":"2021-07-26T21:20:03.445003Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def process_function(engine, batch):\n    my_resnet18rnn.train()\n    optimizer.zero_grad()\n    x, y = batch[0].to(device), batch[1].to(device)\n    y_pred = my_resnet18rnn(x)\n    loss = criterion(y_pred, y)\n    loss.backward()\n    optimizer.step()\n    return loss.item()","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:20:03.447502Z","iopub.execute_input":"2021-07-26T21:20:03.447870Z","iopub.status.idle":"2021-07-26T21:20:03.456137Z","shell.execute_reply.started":"2021-07-26T21:20:03.447833Z","shell.execute_reply":"2021-07-26T21:20:03.455390Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def eval_function(engine, batch):\n    my_resnet18rnn.eval()\n    with torch.no_grad():\n        x, y = batch[0].to(device), batch[1].to(device)\n        y_pred = my_resnet18rnn(x)\n        return y_pred, y","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:20:03.458050Z","iopub.execute_input":"2021-07-26T21:20:03.458622Z","iopub.status.idle":"2021-07-26T21:20:03.464499Z","shell.execute_reply.started":"2021-07-26T21:20:03.458582Z","shell.execute_reply":"2021-07-26T21:20:03.463686Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from ignite.engine import Engine\ntrainer = Engine(process_function)\ntrain_evaluator = Engine(eval_function)\nvalidation_evaluator = Engine(eval_function)","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:20:03.465833Z","iopub.execute_input":"2021-07-26T21:20:03.466187Z","iopub.status.idle":"2021-07-26T21:20:03.539471Z","shell.execute_reply.started":"2021-07-26T21:20:03.466152Z","shell.execute_reply":"2021-07-26T21:20:03.538615Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from ignite.metrics import RunningAverage\n\nRunningAverage(output_transform=lambda x: x).attach(trainer, 'ce')","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:20:03.540884Z","iopub.execute_input":"2021-07-26T21:20:03.541288Z","iopub.status.idle":"2021-07-26T21:20:03.546404Z","shell.execute_reply.started":"2021-07-26T21:20:03.541249Z","shell.execute_reply":"2021-07-26T21:20:03.545471Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def thresholded_output_transform(output):\n    y_pred, y = output\n    y_pred = torch.round(y_pred)\n    return y_pred, y","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:20:03.547892Z","iopub.execute_input":"2021-07-26T21:20:03.548261Z","iopub.status.idle":"2021-07-26T21:20:03.554736Z","shell.execute_reply.started":"2021-07-26T21:20:03.548226Z","shell.execute_reply":"2021-07-26T21:20:03.553562Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from ignite.metrics import Accuracy, Loss\nfrom ignite.contrib.metrics import ROC_AUC\n\nAccuracy(output_transform=thresholded_output_transform).attach(train_evaluator, 'accuracy')\nLoss(criterion).attach(train_evaluator, 'ce')\n\nAccuracy(output_transform=thresholded_output_transform).attach(validation_evaluator, 'accuracy')\nLoss(criterion).attach(validation_evaluator, 'ce')\nROC_AUC().attach(validation_evaluator, \"AUC\")","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:20:03.556299Z","iopub.execute_input":"2021-07-26T21:20:03.556685Z","iopub.status.idle":"2021-07-26T21:20:03.619358Z","shell.execute_reply.started":"2021-07-26T21:20:03.556648Z","shell.execute_reply":"2021-07-26T21:20:03.618383Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from ignite.contrib.handlers import ProgressBar\n\npbar = ProgressBar(persist=True, bar_format=\"\")\npbar.attach(trainer, ['ce'])","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:20:03.620903Z","iopub.execute_input":"2021-07-26T21:20:03.621316Z","iopub.status.idle":"2021-07-26T21:20:03.643196Z","shell.execute_reply.started":"2021-07-26T21:20:03.621276Z","shell.execute_reply":"2021-07-26T21:20:03.642383Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from ignite.handlers import Checkpoint, DiskSaver, EarlyStopping, TerminateOnNan\nfrom ignite.engine import Events\n\ndef score_function(engine):\n    val_loss = engine.state.metrics['AUC']\n    return val_loss\n\nhandler = EarlyStopping(patience=6, score_function=score_function, trainer=trainer)\nvalidation_evaluator.add_event_handler(Events.COMPLETED, handler)","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:20:03.644464Z","iopub.execute_input":"2021-07-26T21:20:03.644829Z","iopub.status.idle":"2021-07-26T21:20:03.651955Z","shell.execute_reply.started":"2021-07-26T21:20:03.644793Z","shell.execute_reply":"2021-07-26T21:20:03.651180Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from ignite.handlers import ModelCheckpoint, EarlyStopping\n\ncheckpointer = ModelCheckpoint('checkpoint', 'textcnn', save_interval=1, n_saved=2,\n                               create_dir=True, save_as_state_dict=True, require_empty=False)\nbest_model_save = ModelCheckpoint(\n    'best_model', 'cnnrnn', n_saved=1,\n    create_dir=True, save_as_state_dict=True,\n    score_function=score_function, require_empty=False)\ntrainer.add_event_handler(Events.EPOCH_COMPLETED, checkpointer, {'cnnrnn': my_resnet18rnn})\nvalidation_evaluator.add_event_handler(Events.EPOCH_COMPLETED, best_model_save, {'cnnrnn': my_resnet18rnn})","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:20:03.653186Z","iopub.execute_input":"2021-07-26T21:20:03.653666Z","iopub.status.idle":"2021-07-26T21:20:03.669758Z","shell.execute_reply.started":"2021-07-26T21:20:03.653629Z","shell.execute_reply":"2021-07-26T21:20:03.668738Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"training_history = {'accuracy': [], 'ce': []}\nvalidation_history = {'accuracy': [], 'ce': [], 'AUC': []}\n\ndef print_logs(engine, dataloader, mode, history_dict):\n    metrics = -1\n    if \"AUC\" in history_dict.keys():\n        validation_evaluator.run(dataloader)\n        metrics = validation_evaluator.state.metrics\n        for key in validation_evaluator.state.metrics.keys():\n            history_dict[key].append(validation_evaluator.state.metrics[key])\n    else:\n        train_evaluator.run(dataloader, max_epochs=1)\n        metrics = train_evaluator.state.metrics\n        for key in train_evaluator.state.metrics.keys():\n            history_dict[key].append(train_evaluator.state.metrics[key])\n        \n    avg_acc = metrics['accuracy']\n    avg_loss = metrics['ce']\n    if \"AUC\" in history_dict.keys():\n        auc = metrics['AUC']\n        print(\n        mode + \" Results - Epoch {} - Avg accuracy: {:.2f} | Avg loss: {:.2f} | AUC: {:.2f}\"\n        .format(engine.state.epoch, avg_acc, avg_loss, auc))\n    else:\n        print(\n            mode + \" Results - Epoch {} - Avg accuracy: {:.2f} | Avg loss: {:.2f}\"\n            .format(engine.state.epoch, avg_acc, avg_loss, ))\n\n\ntrainer.add_event_handler(Events.EPOCH_COMPLETED, print_logs, train_dl, 'Training', training_history)\ntrainer.add_event_handler(Events.EPOCH_COMPLETED, print_logs, val_dl, 'Validation', validation_history)","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:20:03.671163Z","iopub.execute_input":"2021-07-26T21:20:03.671522Z","iopub.status.idle":"2021-07-26T21:20:03.684486Z","shell.execute_reply.started":"2021-07-26T21:20:03.671473Z","shell.execute_reply":"2021-07-26T21:20:03.683398Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"epochs = 4\ntrainer.run(train_dl, max_epochs=epochs)","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:20:03.686252Z","iopub.execute_input":"2021-07-26T21:20:03.686747Z","iopub.status.idle":"2021-07-26T21:20:26.191221Z","shell.execute_reply.started":"2021-07-26T21:20:03.686708Z","shell.execute_reply":"2021-07-26T21:20:26.188436Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Provar a reduir una mica el batch_size a canvi d'imatges més grans.","metadata":{}},{"cell_type":"code","source":"train_evaluator.state.metrics","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:20:26.192040Z","iopub.status.idle":"2021-07-26T21:20:26.192444Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(training_history['ce'])\nprint(validation_history['ce'])","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:20:26.193502Z","iopub.status.idle":"2021-07-26T21:20:26.193948Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\n%matplotlib inline\n\n\nplt.plot(range(epochs), training_history['ce'], 'dodgerblue', label='training')\nplt.plot(range(epochs), validation_history['ce'], 'orange', label='validation')\nplt.xlim(0, epochs);\nplt.xlabel('Epoch')\nplt.ylabel('Binary Cross Entropy Loss')\nplt.title('Binary Cross Entropy on Training/Validation Set')\nplt.legend();","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:20:26.194930Z","iopub.status.idle":"2021-07-26T21:20:26.195537Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.plot(range(epochs), validation_history['AUC'], 'orange', label='validation')\nplt.xlim(0, epochs);\nplt.xlabel('Epoch')\nplt.ylabel('AUC')\nplt.title('AUC on Validation Set')\nplt.legend();","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:20:26.196700Z","iopub.status.idle":"2021-07-26T21:20:26.197261Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Inference","metadata":{}},{"cell_type":"code","source":"import pathlib\n\nmodel_path = next(pathlib.Path('best_model').rglob('*'))\nmodel_path\n\nmodel_state_dict = torch.load(model_path)\nmy_resnet18rnn.load_state_dict(model_state_dict)\n# change model mode to 'evaluation'\n# disable dropout and use learned batch norm statistics\nmy_resnet18rnn.eval()\n\npredictions = []\nlabels = []","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:20:26.198461Z","iopub.status.idle":"2021-07-26T21:20:26.198992Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"predictions = []\n\nmy_resnet18rnn.eval()\n\nwith torch.no_grad():\n    for batch in test_dl:\n        x = batch.to(device)\n        y_pred = my_resnet18rnn(x)\n        # move from GPU to CPU and convert to numpy array\n        y_pred_numpy = y_pred.cpu().numpy()\n\n        predictions = np.concatenate([predictions, y_pred_numpy])","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:20:26.200192Z","iopub.status.idle":"2021-07-26T21:20:26.200732Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"predictions_str = [p for p in predictions]\n\n# test.csv index in a contiguous integers from 0 to len(test_set)\n# to this should work fine\nsubmission = pd.DataFrame({'id': list(range(len(predictions_str))), 'label': predictions_str})","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:20:26.201825Z","iopub.status.idle":"2021-07-26T21:20:26.202389Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission.head(10)","metadata":{"execution":{"iopub.status.busy":"2021-07-26T21:20:26.203521Z","iopub.status.idle":"2021-07-26T21:20:26.204053Z"},"trusted":true},"execution_count":null,"outputs":[]}]}