{"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":"# Overview\n\nIn this notebook, I used Pytorch Lightning to solve it as a multi-label problem.\nI used the following [notebook](https://www.kaggle.com/demetrypascal/better-train-csv-format-keras-starter) as a reference.\n\nThe accuracy of the multi-label solution is about the same as that of the simple solution, and I think the accuracy can be improved by post-processing.\n\n[Training Notebook](https://www.kaggle.com/pegasos/plant2021-multi-label-model-training)","metadata":{}},{"cell_type":"markdown","source":"## Version Notes\n\n- train  V4, inference v4, Model: Resnet50,           IMAGE_SIZE: 512, BS: 32, LB: 0.616\n- train  V6, inference V5, Model: SE-ResNeXt50_32x4d, IMAGE_SIZE: 512, BS: 16, LB: 0.555\n- train  V8, inference v6, Model: Resnet50,           IMAGE_SIZE: 512, BS: 32, LB: 0.584\n  - Add processing to remove duplicates\n- train V11, inference v7, Model: Resnet50,           IMAGE_SIZE: 512, BS: 32, LB: 0.585\n  - More epoch, change lr_scheduler\n- train V14, inference v8, Model: Resnet50,           IMAGE_SIZE: 512, BS: 32, LB: 0.572\n  - torchmetrics F1\n- train V15, inference v9, Model: Resnet50,           IMAGE_SIZE: 512, BS: 32, LB: 0.560\n  - Focal Loss(alpha=1, gamma=2)\n- train V16, inference v11, Model: Resnet50,          IMAGE_SIZE: 512, BS: 32, LB: 0.580\n  - iterative-stratification(cross validators with stratification for multilabel data)\n- train V17, inference v12, Model: Resnet50           IMAGE_SIZE: 512, BS: 32, LB: ???\n  - epoch 60\n- train V18, inference v14, Model: EfficientNetB5 NS, IMAGE_SIZE: 512, BS: 16, LB: ???","metadata":{}},{"cell_type":"code","source":"package_paths = [\n    \"../input/pytorch-image-library/pytorch-image-models-master/pytorch-image-models-master\",\n]\nimport sys;\n\nfor pth in package_paths:\n    sys.path.append(pth)\n\nimport timm","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Import","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport cv2\nimport torch\nimport torch.nn as nn\nimport albumentations as A\n\nfrom torch.utils.data import Dataset, DataLoader\nfrom albumentations.core.composition import Compose, OneOf\nfrom albumentations.pytorch import ToTensorV2","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Config","metadata":{}},{"cell_type":"code","source":"class CFG:\n    seed = 42\n    model_name = 'tf_efficientnet_b5_ns'\n    pretrained = False\n    img_size = 512\n    num_classes = 6\n    lr = 2.5e-4\n    min_lr = 1e-6\n    t_max = 20\n    num_epochs = 20\n    batch_size = 32\n    accum = 1\n    precision = 16\n    n_fold = 5\n    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"PATH = \"../input/plant-pathology-2021-fgvc8/\"\n\nTEST_DIR = PATH + 'test_images/'","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_all = pd.read_csv(PATH + \"train.csv\")\ndf_all.shape","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from collections import defaultdict\n\n\ndct = defaultdict(list)\n\nfor i, label in enumerate(df_all.labels):\n    for category in label.split():\n        dct[category].append(i)\n \ndct = {key: np.array(val) for key, val in dct.items()}\ndct","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"new_df = pd.DataFrame(np.zeros((df_all.shape[0], len(dct.keys())), dtype=np.int8), columns=dct.keys())\n\nfor key, val in dct.items():\n    new_df.loc[val, key] = 1\n    \ndf_all = pd.concat([df_all, new_df], axis=1)\ndf_all.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"multi_labels = new_df.columns\nmulti_labels","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub = pd.read_csv(PATH + \"sample_submission.csv\")\nsub.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"tmp = pd.DataFrame(np.zeros([len(sub), len(new_df.columns)]), columns=multi_labels)\nsub = pd.concat([sub, tmp], axis=1)\nsub.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Define Dataset","metadata":{}},{"cell_type":"code","source":"class PlantDataset(Dataset):\n    def __init__(self, df, transform=None):\n        self.image_id = df['image'].values\n        self.labels = df.iloc[:, 2:].values\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.labels)\n\n    def __getitem__(self, idx):\n        image_id = self.image_id[idx]\n        label = torch.tensor(self.labels[idx], dtype=torch.float32)\n        \n        image_path = TEST_DIR + image_id\n        image = cv2.imread(image_path)\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n        \n        augmented = self.transform(image=image)\n        image = augmented['image']\n        return {'image':image, 'target': label}","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_transform(phase: str):\n    if phase == 'train':\n        return Compose([\n            A.RandomResizedCrop(height=CFG.img_size, width=CFG.img_size),\n            A.HorizontalFlip(p=0.5),\n            A.ShiftScaleRotate(p=0.5),\n            A.RandomBrightnessContrast(p=0.5),\n            A.Normalize(),\n            ToTensorV2(),\n        ])\n    else:\n        return Compose([\n            A.Resize(height=CFG.img_size, width=CFG.img_size),\n            A.Normalize(),\n            ToTensorV2(),\n        ])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_dataset = PlantDataset(sub, get_transform('valid'))\ntest_loader = DataLoader(test_dataset, batch_size=CFG.batch_size, shuffle=False, num_workers=2)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Define Model","metadata":{}},{"cell_type":"code","source":"class CustomResNet(nn.Module):\n    def __init__(self, model_name='resnet18', pretrained=True):\n        super().__init__()\n        self.model = timm.create_model(model_name, pretrained=pretrained)\n        in_features = self.model.get_classifier().in_features\n#         self.model.fc = nn.Linear(in_features, CFG.num_classes)\n        self.model.fc = nn.Sequential(\n            nn.Linear(in_features, in_features),\n            nn.ReLU(inplace=True),\n            nn.Dropout(0.5),\n            nn.Linear(in_features, CFG.num_classes)\n        )\n\n    def forward(self, x):\n        x = self.model(x)\n        return x","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class CustomEffNet(nn.Module):\n    def __init__(self, model_name='tf_efficientnet_b0_ns', pretrained=True):\n        super().__init__()\n        self.model = timm.create_model(model_name, pretrained=pretrained)\n        in_features = self.model.get_classifier().in_features\n#         self.model.fc = nn.Linear(in_features, CFG.num_classes)\n        self.model.classifier = nn.Sequential(\n            nn.Linear(in_features, in_features),\n            nn.ReLU(inplace=True),\n            nn.Dropout(0.5),\n            nn.Linear(in_features, CFG.num_classes)\n        )\n\n    def forward(self, x):\n        x = self.model(x)\n        return x","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from collections import OrderedDict\n\ndef fix_model_state_dict(state_dict):\n    new_state_dict = OrderedDict()\n    for k, v in state_dict.items():\n        name = k\n        if name.startswith('model.'):\n            name = name[6:]  # remove 'model.' of dataparallel\n        new_state_dict[name] = v\n    return new_state_dict","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# model = CustomResNet(model_name=CFG.model_name, pretrained=False)\nmodel = CustomEffNet(model_name=CFG.model_name, pretrained=False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"checkpoint = \"../input/plant2021-multi-label-model-training/logs/tf_efficientnet_b5_ns/version_0/checkpoints/last.ckpt\"\nmodel.load_state_dict(torch.load(checkpoint)['state_dict'])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Inference","metadata":{}},{"cell_type":"code","source":"model.cuda()\nmodel.eval()\n\nsigmoid = nn.Sigmoid()\n\npredictions = []\nfor batch in test_loader:\n    image = batch['image'].cuda()\n    with torch.no_grad():\n        outputs = model(image)\n        preds = outputs.detach().cpu()\n        # The probability of 0.5 or more is considered positive.\n        predictions.append(sigmoid(preds).numpy() > 0.5)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"predictions = pd.DataFrame(np.concatenate(predictions).astype(np.int), columns=new_df.columns)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub.iloc[:, 2:] = predictions\nsub","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels = []\nfor i, row in sub.iloc[:, 2:].iterrows():\n    if ((row['healthy'] == 1) or row.sum() == 0):\n        tmp = 'healthy'\n    else:\n        tmp = ' '.join(multi_labels[row==row.max()])\n    labels.append(tmp)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub['labels'] = labels\nsub[['image', 'labels']].to_csv('submission.csv', index=False)\nsub.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}