{"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 efficientnet_pytorch\n# !pip install torch_optimizer","metadata":{"execution":{"iopub.status.busy":"2021-09-03T10:38:36.153862Z","iopub.execute_input":"2021-09-03T10:38:36.154295Z","iopub.status.idle":"2021-09-03T10:38:36.159519Z","shell.execute_reply.started":"2021-09-03T10:38:36.154262Z","shell.execute_reply":"2021-09-03T10:38:36.158017Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nimport gc\ngc.enable()\nimport sys\nimport math\nimport json\nimport time\nimport random\nimport requests\nfrom glob import glob\nfrom datetime import datetime\nfrom urllib.request import urlopen\nimport cv2\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom PIL import Image\nimport multiprocessing\nfrom sklearn.preprocessing import LabelEncoder\nfrom typing import Any, Optional, Tuple\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\n\nimport albumentations as A\n\nimport sklearn\n\nimport warnings\nwarnings.filterwarnings(\"ignore\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-09-03T10:38:36.167149Z","iopub.execute_input":"2021-09-03T10:38:36.167714Z","iopub.status.idle":"2021-09-03T10:38:36.181227Z","shell.execute_reply.started":"2021-09-03T10:38:36.167679Z","shell.execute_reply":"2021-09-03T10:38:36.179238Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nprint(device)","metadata":{"execution":{"iopub.status.busy":"2021-09-03T10:38:36.183965Z","iopub.execute_input":"2021-09-03T10:38:36.184812Z","iopub.status.idle":"2021-09-03T10:38:36.195511Z","shell.execute_reply.started":"2021-09-03T10:38:36.184759Z","shell.execute_reply":"2021-09-03T10:38:36.193806Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"IN_KERNEL = os.environ.get('KAGGLE_WORKING_DIR') is not None\nBATCH_SIZE = 64\nNUM_WORKERS = multiprocessing.cpu_count()\nMAX_STEPS_PER_EPOCH = 15000\nNUM_EPOCHS = 1\nNUM_TOP_PREDICTS = 20\nLOG_FREQ = 50\nTIME_LIMIT = 9 * 60 * 60\nLEARNING_RATE = 0.001\nLR_STEP = 3\nLR_FACTOR = 0.5","metadata":{"execution":{"iopub.status.busy":"2021-09-03T10:38:36.198673Z","iopub.execute_input":"2021-09-03T10:38:36.199277Z","iopub.status.idle":"2021-09-03T10:38:36.207452Z","shell.execute_reply.started":"2021-09-03T10:38:36.199227Z","shell.execute_reply":"2021-09-03T10:38:36.205708Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df = pd.read_csv('../input/google-landmark-in-vietnam-recognition/VN_train.csv')\ndf.head()","metadata":{"execution":{"iopub.status.busy":"2021-09-03T10:38:36.210273Z","iopub.execute_input":"2021-09-03T10:38:36.210881Z","iopub.status.idle":"2021-09-03T10:38:36.254229Z","shell.execute_reply.started":"2021-09-03T10:38:36.210806Z","shell.execute_reply":"2021-09-03T10:38:36.252680Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df.landmark_id.value_counts().shape[0]","metadata":{"execution":{"iopub.status.busy":"2021-09-03T10:38:36.256494Z","iopub.execute_input":"2021-09-03T10:38:36.257002Z","iopub.status.idle":"2021-09-03T10:38:36.268860Z","shell.execute_reply.started":"2021-09-03T10:38:36.256953Z","shell.execute_reply":"2021-09-03T10:38:36.267311Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class ImageDataset(torch.utils.data.Dataset):\n    def __init__(self, dataframe: pd.DataFrame, mode: str):\n        self.df = dataframe\n        self.mode = mode\n        \n        transforms_list = []\n        if self.mode == 'train':\n            transforms_list = [\n                transforms.Resize((64,64)),\n                transforms.RandomHorizontalFlip(),\n                transforms.RandomChoice([\n                    transforms.RandomResizedCrop(64),\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                transforms.Resize((64,64)),\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_path = self.df.iloc[index].url\n        image = Image.open(urlopen(url))\n        image = self.transforms(image)\n\n        if self.mode == 'test':\n            return image\n        else:\n            return image, self.df.landmark_id.values[index]\n\n    def __len__(self) -> int:\n        return self.df.shape[0]","metadata":{"execution":{"iopub.status.busy":"2021-09-03T10:38:36.271878Z","iopub.execute_input":"2021-09-03T10:38:36.272442Z","iopub.status.idle":"2021-09-03T10:38:36.288491Z","shell.execute_reply.started":"2021-09-03T10:38:36.272387Z","shell.execute_reply":"2021-09-03T10:38:36.287119Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class AverageMeter:\n    ''' Computes and stores the average and current value '''\n    def __init__(self) -> None:\n        self.reset()\n\n    def reset(self) -> None:\n        self.val = 0.0\n        self.avg = 0.0\n        self.sum = 0.0\n        self.count = 0\n\n    def update(self, val: float, n: int = 1) -> None:\n        self.val = val\n        self.sum += val * n\n        self.count += n\n        self.avg = self.sum / self.count","metadata":{"execution":{"iopub.status.busy":"2021-09-03T10:38:36.290627Z","iopub.execute_input":"2021-09-03T10:38:36.291152Z","iopub.status.idle":"2021-09-03T10:38:36.301429Z","shell.execute_reply.started":"2021-09-03T10:38:36.291102Z","shell.execute_reply":"2021-09-03T10:38:36.299061Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def load_data(df):\n    num_classes = df.landmark_id.value_counts().shape[0]\n    label_encoder = LabelEncoder()\n    label_encoder.fit(df.landmark_id.values)\n    print('found classes', len(label_encoder.classes_))\n    df.landmark_id = label_encoder.transform(df.landmark_id)\n\n    full_dataset = ImageDataset(df, mode='train')\n#     df.drop(columns='url', inplace=True)\n    train_size = int(0.9 * len(full_dataset))\n    test_size = len(full_dataset) - train_size\n    train_dataset, test_dataset = torch.utils.data.random_split(full_dataset, [train_size, test_size])\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","metadata":{"execution":{"iopub.status.busy":"2021-09-03T10:38:36.409236Z","iopub.execute_input":"2021-09-03T10:38:36.409663Z","iopub.status.idle":"2021-09-03T10:38:36.418852Z","shell.execute_reply.started":"2021-09-03T10:38:36.409629Z","shell.execute_reply":"2021-09-03T10:38:36.417393Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def inference(data_loader: Any, model: Any) -> Tuple[torch.Tensor, torch.Tensor,\n                                                     Optional[torch.Tensor]]:\n    ''' Returns predictions and targets, if any. '''\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\n            else:\n                input_, target = data, 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","metadata":{"execution":{"iopub.status.busy":"2021-09-03T10:38:36.430272Z","iopub.execute_input":"2021-09-03T10:38:36.430685Z","iopub.status.idle":"2021-09-03T10:38:36.443075Z","shell.execute_reply.started":"2021-09-03T10:38:36.430651Z","shell.execute_reply":"2021-09-03T10:38:36.441628Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def generate_submission(test_loader: Any, model: Any, label_encoder: Any) -> np.ndarray:\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')\n    print(np.array(labels))\n    print('confs')\n    print(np.array(confs))\n\n    sub = test_loader.dataset.df\n    def concat(label: np.ndarray, conf: np.ndarray) -> str:\n        return ' '.join([f'{L} {c}' for L, c in zip(label, conf)])\n    sub['landmarks'] = [concat(label, conf) 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')","metadata":{"execution":{"iopub.status.busy":"2021-09-03T10:38:36.445420Z","iopub.execute_input":"2021-09-03T10:38:36.446167Z","iopub.status.idle":"2021-09-03T10:38:36.460755Z","shell.execute_reply.started":"2021-09-03T10:38:36.446116Z","shell.execute_reply":"2021-09-03T10:38:36.459438Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def GAP(predicts: torch.Tensor, confs: torch.Tensor, targets: torch.Tensor) -> float:\n    ''' Simplified GAP@1 metric: only one prediction per sample is supported '''\n    assert len(predicts.shape) == 1\n    assert len(confs.shape) == 1\n    assert len(targets.shape) == 1\n    assert predicts.shape == confs.shape and confs.shape == targets.shape\n\n    _, indices = torch.sort(confs, descending=True)\n\n    confs = confs.cpu().numpy()\n    predicts = predicts[indices].cpu().numpy()\n    targets = targets[indices].cpu().numpy()\n\n    res, true_pos = 0.0, 0\n\n    for i, (c, p, t) in enumerate(zip(confs, predicts, targets)):\n        rel = int(p == t)\n        true_pos += rel\n\n        res += true_pos / (i + 1) * rel\n\n    res /= targets.shape[0]\n    return res","metadata":{"execution":{"iopub.status.busy":"2021-09-03T10:38:36.463881Z","iopub.execute_input":"2021-09-03T10:38:36.464462Z","iopub.status.idle":"2021-09-03T10:38:36.475489Z","shell.execute_reply.started":"2021-09-03T10:38:36.464397Z","shell.execute_reply":"2021-09-03T10:38:36.474024Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def has_time_run_out() -> bool:\n    return time.time() - global_start_time > TIME_LIMIT - 500","metadata":{"execution":{"iopub.status.busy":"2021-09-03T10:38:36.478401Z","iopub.execute_input":"2021-09-03T10:38:36.479104Z","iopub.status.idle":"2021-09-03T10:38:36.492303Z","shell.execute_reply.started":"2021-09-03T10:38:36.479054Z","shell.execute_reply":"2021-09-03T10:38:36.491119Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def train(train_loader: Any, model: Any, criterion: Any, optimizer: Any,\n          epoch: int, lr_scheduler: Any) -> None:\n    print(f'epoch {epoch}')\n    batch_time = AverageMeter()\n    losses = AverageMeter()\n    avg_score = AverageMeter()\n\n    model.train()\n    num_steps = min(len(train_loader), MAX_STEPS_PER_EPOCH)\n\n    print(f'total batches: {num_steps}')\n\n    end = time.time()\n    lr_str = ''\n\n    for i, (input_, target) in enumerate(train_loader):\n        if i >= num_steps:\n            break\n\n        output = model(input_.cuda())\n        loss = criterion(output, target.cuda())\n\n        confs, predicts = torch.max(output.detach(), dim=1)\n        avg_score.update(GAP(predicts, confs, target))\n\n        losses.update(loss.data.item(), input_.size(0))\n        optimizer.zero_grad()\n        loss.backward()\n        optimizer.step()\n\n        batch_time.update(time.time() - end)\n        end = time.time()\n\n        if (i+1) % LOG_FREQ == 0:\n            print(f'{epoch} [{i}/{num_steps}]\\t'\n                        f'time {batch_time.val:.3f} ({batch_time.avg:.3f})\\t'\n                        f'loss {losses.val:.4f} ({losses.avg:.4f})\\t'\n                        f'GAP {avg_score.val:.4f} ({avg_score.avg:.4f})'\n                        + lr_str)\n\n        if has_time_run_out():\n            break\n\n    print(f' * average GAP on train {avg_score.avg:.4f}')","metadata":{"execution":{"iopub.status.busy":"2021-09-03T10:38:36.494269Z","iopub.execute_input":"2021-09-03T10:38:36.494792Z","iopub.status.idle":"2021-09-03T10:38:36.509717Z","shell.execute_reply.started":"2021-09-03T10:38:36.494740Z","shell.execute_reply":"2021-09-03T10:38:36.508499Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Main process**","metadata":{}},{"cell_type":"code","source":"train_loader, test_loader, label_encoder, num_classes = load_data(df)","metadata":{"execution":{"iopub.status.busy":"2021-09-03T10:38:36.511319Z","iopub.execute_input":"2021-09-03T10:38:36.512015Z","iopub.status.idle":"2021-09-03T10:38:36.533709Z","shell.execute_reply.started":"2021-09-03T10:38:36.511965Z","shell.execute_reply":"2021-09-03T10:38:36.532521Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_loader","metadata":{"execution":{"iopub.status.busy":"2021-09-03T10:38:36.537568Z","iopub.execute_input":"2021-09-03T10:38:36.537943Z","iopub.status.idle":"2021-09-03T10:38:36.546522Z","shell.execute_reply.started":"2021-09-03T10:38:36.537884Z","shell.execute_reply":"2021-09-03T10:38:36.545020Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"label_encoder","metadata":{"execution":{"iopub.status.busy":"2021-09-03T10:38:36.548749Z","iopub.execute_input":"2021-09-03T10:38:36.549313Z","iopub.status.idle":"2021-09-03T10:38:36.560267Z","shell.execute_reply.started":"2021-09-03T10:38:36.549263Z","shell.execute_reply":"2021-09-03T10:38:36.558234Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"num_classes","metadata":{"execution":{"iopub.status.busy":"2021-09-03T10:38:36.564614Z","iopub.execute_input":"2021-09-03T10:38:36.565078Z","iopub.status.idle":"2021-09-03T10:38:36.573524Z","shell.execute_reply.started":"2021-09-03T10:38:36.565028Z","shell.execute_reply":"2021-09-03T10:38:36.572228Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"global_start_time = time.time()\nmodel = torchvision.models.resnet50(pretrained=True)\nmodel.avg_pool = nn.AdaptiveAvgPool2d(1)\nmodel.fc = nn.Linear(model.fc.in_features, num_classes)\nmodel.cuda()\n\ncriterion = nn.CrossEntropyLoss()\n\noptimizer = torch.optim.Adam(model.parameters(), lr=LEARNING_RATE)\nlr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=LR_STEP,\n                                                   gamma=LR_FACTOR)\n\nfor epoch in range(1, NUM_EPOCHS + 1):\n    print('-' * 50)\n    train(train_loader, model, criterion, optimizer, epoch, lr_scheduler)\n    lr_scheduler.step()","metadata":{"execution":{"iopub.status.busy":"2021-09-03T10:38:36.575491Z","iopub.execute_input":"2021-09-03T10:38:36.576336Z","iopub.status.idle":"2021-09-03T11:01:03.498715Z","shell.execute_reply.started":"2021-09-03T10:38:36.576284Z","shell.execute_reply":"2021-09-03T11:01:03.497216Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print('inference mode')\ngenerate_submission(test_loader, model, label_encoder)","metadata":{"execution":{"iopub.status.busy":"2021-09-03T11:01:03.504303Z","iopub.execute_input":"2021-09-03T11:01:03.504764Z","iopub.status.idle":"2021-09-03T11:02:21.047274Z","shell.execute_reply.started":"2021-09-03T11:01:03.504716Z","shell.execute_reply":"2021-09-03T11:02:21.044738Z"},"trusted":true},"execution_count":null,"outputs":[]}]}