{"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":"!pip install timm","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## imports","metadata":{}},{"cell_type":"code","source":"import os\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom sklearn.metrics import f1_score\nimport random\nimport matplotlib\nimport cv2\n\nimport timm\nimport torch\nimport torch.optim as optim\nfrom torch.optim import Adam\nfrom torch import nn\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms, utils\nimport torchvision\n\nfrom tqdm.notebook import tqdm\nimport albumentations as A\nfrom torch.optim.lr_scheduler import ReduceLROnPlateau","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Initialization\ntrain_csv_loc= '../input/plantpathology288x288/train.csv' \ntest_csv_loc='../input/plantpathology288x288/sample_submission.csv'\ntrain_image_loc = '../input/plantpathology288x288/train'\ntest_image_loc = '../input/plantpathology288x288/test'\n\ntrain_csv = pd.read_csv(train_csv_loc)\ntest_csv = pd.read_csv(test_csv_loc)\n\nimage_size = 288\nmodel_name = 'resnet200d'\nseed = 719\nbatch_size = 32\nnum_workers = 0","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Processing the dataset csv","metadata":{}},{"cell_type":"code","source":"lbl_map = {'0': 'complex', '1': 'frog_eye_leaf_spot', '2': 'healthy', '3': 'powdery_mildew', '4': 'rust', '5': 'scab'}\ninv_lbl_map = {'complex': 0, 'frog_eye_leaf_spot': 1, 'healthy': 2, 'powdery_mildew': 3, 'rust': 4, 'scab': 5}\ndata_labels = list(lbl_map.values())\n\ndef create_file_mapping(mapping_csv):\n    mapping = dict()\n    for i in range(len(mapping_csv)):\n        name, tags = mapping_csv['image'][i], mapping_csv['labels'][i]\n        mapping[name] = tags.split(' ')\n    return mapping\n\nds_mapping = create_file_mapping(train_csv)\n\ndef one_hot_encode(tags, mapping):\n\t# create empty vector\n\tencoding = np.zeros(len(mapping), dtype='uint8')\n\t# mark 1 for each tag in the vector\n\tfor tag in tags:\n\t\tencoding[mapping[tag]] = 1\n\treturn encoding\n\ndef prepare_csv_dict(file_mapping, tag_mapping):\n    ds_dict = {}\n    for filename in file_mapping.keys():\n        # get tags\n        tags = file_mapping[filename]\n        # one hot encode tags\n        target = one_hot_encode(tags, tag_mapping)\n        # store\n        ds_dict[filename] = target\n\n    created_csv = pd.DataFrame.from_dict(ds_dict, orient='index').reset_index()\n    created_csv.columns = ['image'] + data_labels\n    return created_csv\n\none_hot_csv = prepare_csv_dict(ds_mapping, inv_lbl_map)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# GPU settings\ndevice = \"cuda\" if torch.cuda.is_available() else \"cpu\"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def seed_everything(seed):\n    random.seed(seed)\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    torch.backends.cudnn.deterministic = True\n    torch.backends.cudnn.benchmark = True","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class parseDataset(Dataset):\n    def __init__(self, out_csv, image_loc, transform=None, test=False, aug=None):\n        self.out_csv = out_csv\n        self.image_loc = image_loc\n        self.transform = transform\n        self.test = test\n        self.aug = aug\n\n    def __len__(self):\n        return len(self.out_csv)\n\n    def __getitem__(self, idx):\n        if torch.is_tensor(idx):\n            idx = idx.tolist()\n\n        img_name = os.path.join(self.image_loc,\n                                self.out_csv.iloc[idx, 0])\n        image = cv2.imread(img_name)\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n#         image = image.astype(np.uint8)\n\n        if self.aug:\n            image = self.aug(image=image)[\"image\"]\n            \n        if self.transform:\n            image = self.transform(image)\n        \n        if self.test:\n            return image\n        else:\n            categories = self.out_csv.iloc[idx,1:]\n            categories = np.array(categories)\n            categories = categories.astype(np.uint8)\n            sample = [image, categories]\n\n            return sample","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data_transform = transforms.Compose([\n                 transforms.ToTensor(),\n                 transforms.Normalize(\n                     mean=[0.485, 0.456, 0.406],\n                     std=[0.229, 0.224, 0.225],\n     ),\n    ])\n\naug_transform = A.Compose([\n                A.RandomSizedCrop(min_max_height=(200, 270), height=288, width=288, p=0.5),\n                A.PadIfNeeded(min_height=288, min_width=288, p=1),\n                A.HorizontalFlip(p=0.5),\n                A.VerticalFlip(p=0.5),\n                A.RandomBrightnessContrast(p=0.8), \n                A.RandomGamma(p=0.8)\n])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## load dataset","metadata":{}},{"cell_type":"code","source":"def prep_dataloader(data_csv, train_index, valid_index, image_loc):\n\n    train_dataset = parseDataset(train_index, image_loc, transform = data_transform, aug=aug_transform)\n    valid_dataset =  parseDataset(valid_index, image_loc, transform = data_transform)\n    \n    train_dataloader = DataLoader(train_dataset, batch_size = batch_size, shuffle=True, num_workers=2)\n    valid_dataloader = DataLoader(valid_dataset, batch_size = batch_size, shuffle=True, num_workers=2)   \n    \n    return train_dataloader, valid_dataloader ","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## define model","metadata":{}},{"cell_type":"code","source":"class ResNet200D(nn.Module):\n    def __init__(self, model_name='resnet200d'):\n        super().__init__()\n        self.model = timm.create_model(model_name, pretrained=False)\n        \n        # load pretrained weights\n        self.model.load_state_dict(torch.load('../input/resnet200dpretrainedweights/resnet200d_ra2-bdba9bf9.pth'))\n        \n#         self.model.conv1[0].in_channels = 1\n        n_features = self.model.fc.in_features\n        self.model.global_pool = nn.Identity()\n        self.model.fc = nn.Identity()\n        self.pooling = nn.AdaptiveAvgPool2d(1)\n      \n        self.fc = nn.Sequential(\n                        nn.Linear(n_features, 256),\n                        nn.Dropout(p=0.2),\n                        nn.Linear(256, 6),\n                    )\n    \n#         for param in self.model.parameters():\n#             param.requires_grad = False\n\n    def forward(self, x):\n        bs = x.size(0)\n        features = self.model(x)\n        pooled_features = self.pooling(features).view(bs, -1)\n        output = self.fc(pooled_features)\n        return output","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"net = ResNet200D(model_name = 'resnet200d')\nnet = net.to(device)\n# print(net)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!ls ../input/plantpathweightsresnet288x288","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"LAST_WEIGHT_PATH = '../input/plantpathweightsresnet288x288/resnet200d_fold_0_epoch_50.pth'\nnet.load_state_dict(torch.load(LAST_WEIGHT_PATH))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Training and Validation Loop","metadata":{}},{"cell_type":"code","source":"def train_loop(train_dataloader, net, criterion, optimizer, device, ths):\n    net.train()\n    preds = []\n    targets = []\n    losses = []\n    t = tqdm(train_dataloader, desc=\"Training: \")\n    for images, labels in t:\n        images, labels = images.to(device), labels.to(device)\n        optimizer.zero_grad()\n\n        outputs = net(images)\n        loss = criterion(outputs, labels.float())\n        loss.backward()\n        optimizer.step()\n\n        preds += [ outputs.sigmoid() ]\n        targets += [ labels.detach().cpu() ]\n        \n        losses.append(loss.item())\n        t.set_postfix_str(\"training loss: {:.3f}\".format(loss.item()))\n        \n    preds = torch.cat(preds).detach().cpu().numpy()\n    targets = torch.cat(targets).cpu().numpy() # undo batching\n    out = f1_score(targets, preds > ths, average = 'macro')\n    \n\n    print(f\"train F1: {out:.3f}\")\n    loss_train = np.mean(losses)\n    return loss_train","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def valid_loop(valid_dataloader, net, criterion, optimizer, device, ths):\n    net.eval()\n    preds = []\n    targets = []\n    losses = []\n    \n    with torch.no_grad():\n        t = tqdm(valid_dataloader, desc='Validating: ', colour=\"#ff7f50\")\n        for images, labels_ in t:\n            images, labels_ = images.to(device), labels_.to(device)\n            \n            outputs = net(images)\n            loss = criterion(outputs, labels_.float())\n            \n            preds += [ outputs.sigmoid() ] # **\n            targets += [ labels_.detach().cpu() ]\n            \n            losses.append(loss.item())\n            t.set_postfix_str('validation loss: {:.3f}'.format(loss.item()))\n            \n    preds = torch.cat(preds).cpu().numpy() # [ tensor1, tensor2, ... ] where tensor i = 2*11 matrix\n    \n    targets = torch.cat(targets).cpu().numpy() # undo batching\n    \n    out = f1_score(targets, preds > ths, average = 'macro')\n    \n    if debug:\n        print(preds > ths)\n        print(targets)\n\n    print(f\"val F1: {out:.3f}\")\n    \n    loss_valid = np.mean(losses)\n    return loss_valid","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"epochs = 60\ntrain = True\ndebug = False\n\nbest_loss = np.inf\n\ncriterion = nn.BCEWithLogitsLoss()\noptimizer = Adam(net.parameters(), lr=0.0001, weight_decay = 1e-6, amsgrad = False)\nscheduler = ReduceLROnPlateau(optimizer, 'min', patience=6)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"split_frac = 0.8\nfold = 0","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def run_training(net, device, in_csv, split_frac, epochs, criterion, optimizer, img_loc, best_loss):\n    seed_everything(seed)\n\n    in_csv = in_csv.sample(frac=1)\n    train_index, valid_index = np.split(in_csv, [int(len(in_csv) * split_frac)])\n    print('train set count: {} \\nvalidation set count: {}'.format(len(train_index), len(valid_index)))\n\n    for epoch in range(1, epochs + 1):\n        print('Epoch: {}/{}'.format(epoch, epochs))\n        \n        # print LR\n        for param_group in optimizer.param_groups:\n            print(\"LR:\", param_group['lr'])\n\n        train_loader, valid_loader = prep_dataloader(in_csv, train_index, valid_index, img_loc)\n\n        avg_loss = train_loop(train_loader, net, criterion, optimizer, device, 0.4)\n        print(\"avg loss: {:.3f}\".format(avg_loss))\n\n        avg_val_loss = valid_loop(valid_loader, net, criterion, optimizer, device, 0.4)\n        print(\"val loss: {:.3f}\\n\".format(avg_val_loss))\n        scheduler.step(avg_val_loss)\n\n        if avg_loss < best_loss:\n            best_loss = avg_loss\n            torch.save(net.state_dict(), \"/kaggle/working/{}_fold_{}_epoch_{}.pth\".format(model_name, fold, epoch))\n\nif train:\n    run_training(net, device, one_hot_csv, split_frac, epochs, criterion, optimizer, train_image_loc, best_loss)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## testing\nhelper functions","metadata":{}},{"cell_type":"code","source":"def get_labels(row, labels, ths=[0.5] * len(lbl_map)):\n    try:\n        row = [i for i, x in enumerate(row) if x > ths]\n        row = [labels[str(i)] for i in row]\n        row = 'healthy' if ('healthy' in row or len(row) == 0) else ' '.join(row)\n    except:\n        print(row)\n    return row","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_transform = transforms.Compose([\n        transforms.ToTensor(),\n        transforms.Resize((image_size, image_size)),\n        transforms.Normalize(\n         mean=[0.485, 0.456, 0.406],\n         std=[0.229, 0.224, 0.225],\n     ),\n    ])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## testing for thresholds\n(this part is run only if `train = False`) \npredictions are made on the entire dataset and the best thresholds for each class are recorded","metadata":{}},{"cell_type":"code","source":"criterion = nn.BCEWithLogitsLoss()\n\ndef get_thresholds(in_csv, img_loc, device, net, criterion):\n    net.eval()\n    batch_size = 16\n\n    # parse dataset and create dataloader\n    test_dataset = parseDataset(in_csv, img_loc, transform = test_transform)\n    test_dataloader = DataLoader(test_dataset, batch_size = batch_size, shuffle=False, num_workers=2)\n\n    preds_list = []\n    labels_list = []\n\n    # get output\n    t = tqdm(test_dataloader, desc='testing: ', colour=\"#557f50\")\n    for images, labels in t:\n        images = images.to(device)\n\n        outputs = net(images)\n\n        preds = outputs.sigmoid().detach().cpu().numpy()\n        preds_list.append(preds)\n        labels_list.append(labels)\n    \n    return np.concatenate(preds_list), np.concatenate(labels_list)\n    \nif not train:\n    p_list, l_list = get_thresholds(one_hot_csv, train_image_loc, device, net, criterion)\n\n    thresholds = np.arange(.01, 1., .01)\n    scores = {}\n\n    for threshold in thresholds:\n        out_score = f1_score(l_list, p_list > threshold, average = None)\n        scores[ threshold ] = list(out_score)\n        \n    ths_idxs = np.argmax(list(scores.values()), axis=0)\n    best_ths = [thresholds[i] for i in ths_idxs]\n    print(best_ths)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}