{"cells":[{"metadata":{},"cell_type":"markdown","source":"## [PyTorch Inference Submission] EfficientNet Baseline From [original notebook](https://www.kaggle.com/rhtsingh/pytorch-training-inference-efficientnet-baseline)\n\n*Note: I have exhausted my GPU for this week and so was unable to complete training. When training started I had only 1/30hr left.*\n","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"### Settup Dependencies","execution_count":null},{"metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"# !pip install efficientnet_pytorch\n# !pip install torch_optimizer","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"!pip install ../input/efficientnet-pytorch/EfficientNet-PyTorch-master/ > /dev/null # no output","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"_kg_hide-output":true,"_kg_hide-input":true},"cell_type":"code","source":"import os\nimport gc\ngc.enable()\nimport sys\nimport math\nimport json\nimport time\nimport random\nfrom glob import glob\nfrom datetime import datetime\n\nimport cv2\nimport csv\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom PIL import Image\nimport multiprocessing\nfrom sklearn.preprocessing import LabelEncoder\n\nimport torch\nimport torchvision\nfrom torch import Tensor\nfrom torchvision import transforms\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.autograd import Variable\nfrom torch.nn.parameter import Parameter\nfrom torch.optim import lr_scheduler\nfrom torch.utils.data import DataLoader, Dataset\nfrom torch.utils.data.sampler import SequentialSampler\nfrom tqdm import tqdm\n\nimport efficientnet_pytorch\n\n# import torch_optimizer as optim\nimport torch.optim as optim\nimport albumentations as A\n\nimport sklearn\n\nimport warnings\nwarnings.filterwarnings(\"ignore\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Train Configuration\n\n*Note: Lots of improvement can be done simply here. e.g.*\n\n* MIN SAMPLES PER CLASS - This variable is a threshold for total number of images in a class. If has class has less than this count then it will be discarded from training set.\n* BATCH SIZE            - The number of images in each training batch.\n* EPOCHS                - Total number of epochs.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"IN_KERNEL = os.environ.get('KAGGLE_WORKING_DIR') is not None\nMIN_SAMPLES_PER_CLASS = 1 \nBATCH_SIZE = 32\nNUM_WORKERS = multiprocessing.cpu_count()\nNUM_EPOCHS = 2\nLOG_FREQ = 10\nNUM_TOP_PREDICTS = 5\nNUM_PUBLIC_TRAIN_IMAGES = 1580470","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Read Train and Test DataFrame","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"# when re-running code, classes_num(num of landmark_ids) of the private train set  is less than the public train set, so we need to reload the original train.csv\npublic_train = pd.read_csv('../input/glr-train-csv/train.csv') # The public train.csv\ntrain = pd.read_csv('../input/landmark-recognition-2020/train.csv')  # Treat as the private train.csv when submit to re-run code\ntest = pd.read_csv('../input/landmark-recognition-2020/sample_submission.csv')\ntrain_dir = '../input/landmark-recognition-2020/train/'\ntest_dir = '../input/landmark-recognition-2020/test/'","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"> ### Dataset\n\nimage size=(3, 224, 224)","execution_count":null},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"class ImageDataset(torch.utils.data.Dataset):\n    def __init__(self, dataframe: pd.DataFrame, image_dir:str, mode: str):\n        self.df = dataframe\n        self.mode = mode\n        self.image_dir = image_dir\n        \n        transforms_list = []\n        if self.mode == 'train':\n            # Increase image size from (64,64) to higher resolution,\n            # Make sure to change in RandomResizedCrop as well.\n            transforms_list = [\n                transforms.Resize((224,224)),\n                transforms.RandomHorizontalFlip(),\n                transforms.RandomChoice([\n                    transforms.RandomResizedCrop(224),\n                    transforms.ColorJitter(0.2, 0.2, 0.2, 0.2),\n                    transforms.RandomAffine(degrees=15, translate=(0.2, 0.2),\n                                            scale=(0.8, 1.2), shear=15,\n                                            resample=Image.BILINEAR)\n                ]),\n                transforms.ToTensor(),\n                transforms.Normalize(mean=[0.485, 0.456, 0.406],\n                                      std=[0.229, 0.224, 0.225]),\n            ]\n        else:\n            transforms_list.extend([\n                # Keep this resize same as train\n                transforms.Resize((224,224)),\n                transforms.ToTensor(),\n                transforms.Normalize(mean=[0.485, 0.456, 0.406],\n                                      std=[0.229, 0.224, 0.225]),\n            ])\n        self.transforms = transforms.Compose(transforms_list)\n\n    def __getitem__(self, index: int):\n        image_id = self.df.iloc[index].id\n        image_path = f\"{self.image_dir}/{image_id[0]}/{image_id[1]}/{image_id[2]}/{image_id}.jpg\"\n        image = Image.open(image_path)\n        image = self.transforms(image)\n\n        if self.mode == 'test':\n            return {'image':image}\n        else:\n            return {'image':image, \n                    'target':self.df.iloc[index].landmark_id}\n\n    def __len__(self) -> int:\n        return self.df.shape[0]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Load Data","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"def load_data(train, test, train_dir, test_dir):\n    counts = train.landmark_id.value_counts()\n    selected_classes = counts[counts >= MIN_SAMPLES_PER_CLASS].index\n    num_classes = selected_classes.shape[0]\n    print('classes with at least N samples:', num_classes)\n\n    train = train.loc[train.landmark_id.isin(selected_classes)]\n    print('train_df', train.shape)\n    print('test_df', test.shape)\n\n    # filter non-existing test images\n    exists = lambda img: os.path.exists(f'{test_dir}/{img[0]}/{img[1]}/{img[2]}/{img}.jpg')\n    test = test.loc[test.id.apply(exists)]\n    print('test_df after filtering', test.shape)\n\n    label_encoder = LabelEncoder()\n    label_encoder.fit(train.landmark_id.values)\n    print('found classes', len(label_encoder.classes_))\n    assert len(label_encoder.classes_) == num_classes\n\n    train.landmark_id = label_encoder.transform(train.landmark_id)\n\n    train_dataset = ImageDataset(train, train_dir, mode='train')\n    test_dataset = ImageDataset(test, test_dir, mode='test')\n\n    train_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE,\n                              shuffle=False, num_workers=4, drop_last=True)\n\n    test_loader = DataLoader(test_dataset, batch_size=BATCH_SIZE,\n                             shuffle=False, num_workers=NUM_WORKERS)\n\n    return train_loader, test_loader, label_encoder, num_classes, train.shape[0]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Model\n\n*Note: Used efficientnet-b0. Experimenting with different archs can yield different results*","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"class EfficientNetEncoderHead(nn.Module):\n    def __init__(self, depth, num_classes=81313):\n        super(EfficientNetEncoderHead, self).__init__()\n        self.depth = depth\n        model_name = 'efficientnet-b' + str(self.depth)\n#         self.base = efficientnet_pytorch.EfficientNet.from_pretrained(f'efficientnet-b{self.depth}')\n        self.base = efficientnet_pytorch.EfficientNet.from_name(f'efficientnet-b{self.depth}')\n#         self.base.load_state_dict(torch.load(efn_weights[model_name]))\n        self.avg_pool = nn.AdaptiveAvgPool2d(1)\n        self.output_filter = self.base._fc.in_features\n        self.classifier = nn.Linear(self.output_filter, num_classes)\n    def forward(self, x):\n        x = self.base.extract_features(x)\n        x = self.avg_pool(x).squeeze(-1).squeeze(-1)\n        x = self.classifier(x)\n        return x","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Inference Function","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"def inference(data_loader, model):\n    model.eval()\n\n    activation = nn.Softmax(dim=1)\n    all_predicts, all_confs, all_targets = [], [], []\n\n    with torch.no_grad():\n        for i, data in enumerate(tqdm(data_loader, disable=IN_KERNEL)):\n            if data_loader.dataset.mode != 'test':\n                input_, target = data['image'], data['target']\n            else:\n                input_, target = data['image'], None\n\n            output = model(input_.cuda())\n            output = activation(output)\n\n            confs, predicts = torch.topk(output, NUM_TOP_PREDICTS)\n            all_confs.append(confs)\n            all_predicts.append(predicts)\n\n            if target is not None:\n                all_targets.append(target)\n\n    predicts = torch.cat(all_predicts)\n    confs = torch.cat(all_confs)\n    targets = torch.cat(all_targets) if len(all_targets) else None\n\n    return predicts, confs, targets","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Generate Submission","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"def generate_submission(test_loader, model, label_encoder):\n    sample_sub = pd.read_csv('../input/landmark-recognition-2020/sample_submission.csv')\n\n    predicts_gpu, confs_gpu, _ = inference(test_loader, model)\n    predicts, confs = predicts_gpu.cpu().numpy(), confs_gpu.cpu().numpy()\n\n    labels = [label_encoder.inverse_transform(pred) for pred in predicts]\n    print('labels', np.array(labels).shape)\n    print('confs', np.array(confs).shape)\n\n    sub = test_loader.dataset.df\n    \n    def concat(label: np.ndarray, conf: np.ndarray) -> str:\n#         result = ' '.join([f'{L} {c}' for L, c in zip(label, conf)])\n        result = ''\n        for L, c in zip(label, conf):\n            if L in private_counts:\n                result = f'{L} {c}'\n                break\n        \n        return result\n    \n    sub['landmarks'] = [concat(label, conf) for label, conf in zip(labels, confs)]\n#     sub['landmarks'] = [concat(label, conf) if label[0] in private_counts else '' for label, conf in zip(labels, confs)]\n\n    sample_sub = sample_sub.set_index('id')\n    sub = sub.set_index('id')\n    sample_sub.update(sub)\n\n    sample_sub.to_csv('submission.csv')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Process","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"if __name__ == '__main__':\n    global_start_time = time.time()\n    private_counts = train.landmark_id.value_counts()\n    print('Private train set landmark_ids lenght:', len(private_counts))\n#     train_loader, test_loader, label_encoder, num_classes, train_len = load_data(train, test, train_dir, test_dir)\n    train_loader, test_loader, label_encoder, num_classes, train_len = load_data(public_train, test, train_dir, test_dir)\n#     print(label_encoder.inverse_transform([100]))\n\n    model = EfficientNetEncoderHead(depth=0, num_classes=num_classes)\n    try:\n        model_path = '../input/efnb0-10-gap05917pth/efn-b0_10_GAP0.5917.pth'\n        model.load_state_dict(torch.load(model_path, map_location='cpu'))\n        print('Model found in {}'.format(model_path))\n    except:\n        print('Random initialize model!')\n        \n    model.cuda()\n#     print(model)\n\n    print('inference mode')\n    generate_submission(test_loader, model, label_encoder)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"*Note: Will be publishing better kernels soon with more advanced techniques for landmark recognition.*\n### More To Come. Stay Tuned. !!","execution_count":null}],"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":4,"nbformat_minor":4}