{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n%matplotlib inline \n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\n\n# You can write up to 5GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"DATA_PATH = '../input/landmark-recognition-2020/'\n","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"train_data = pd.read_csv('../input/landmark-recognition-2020/train.csv')\nsample_sub = pd.read_csv('../input/landmark-recognition-2020/sample_submission.csv')\ntrain_data","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sample_sub","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_data['landmark_id'].value_counts().hist()\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# missing data in training data \ntotal = train_data.isnull().sum().sort_values(ascending = False)\npercent = (train_data.isnull().sum()/train_data.isnull().count()).sort_values(ascending = False)\nmissing_train_data = pd.concat([total, percent], axis=1, keys=['Total', 'Percent'])\nmissing_train_data.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"temp = pd.DataFrame(train_data.landmark_id.value_counts().head(10))\ntemp.reset_index(inplace=True)\ntemp.columns = ['landmark_id','count']\ntemp","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import os\nimport glob\nimport cv2\ntest_list = glob.glob('../input/landmark-recognition-2020/test/*/*/*/*')\ntrain_list = glob.glob('../input/landmark-recognition-2020/train/*/*/*/*')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(len(test_list), ' test images and ', len(train_list), 'train images')\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"\nimport shutil\nimport os\n#path = '../output/kaggle/working/'\ntest_dir = glob.glob('../input/landmark-recognition-2020/test/*')\n#test_dir = shutil.os.mkdir()\n\nfor f1 in test_dir:\n    if len(f1.split('/')[-1])==1:\n        for f2 in glob.glob(f1+'/*'):\n            for f3 in glob.glob(f2+'/*'):\n                src = f3+'/'\n                files = os.listdir(src)   \n              #  print(files)\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# EXAMPLE TEST IMAGES","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.rcParams[\"axes.grid\"] = False\nf, axarr = plt.subplots(4, 4, figsize=(24, 22))\n\ncurr_row = 0\nfor i in range(16):\n    example = cv2.imread(test_list[i])\n    example = example[:,:,::-1]\n    \n    col = i%4\n    axarr[col, curr_row].imshow(example)\n    if col == 3:\n        curr_row += 1\n            ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# ****EXAMPLE TRAIN IMAGES****","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.rcParams[\"axes.grid\"] = False\nf, axarr = plt.subplots(4, 4, figsize=(24, 22))\n\ncurr_row = 0\nfor i in range(16):\n    example = cv2.imread(train_list[i])\n    example = example[:,:,::-1]\n    \n    col = i%4\n    axarr[col, curr_row].imshow(example)\n    if col == 3:\n        curr_row += 1","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize = (8, 8))\nplt.title('Landmark id density plot')\nsns.kdeplot(train_data['landmark_id'], color=\"tomato\", shade=True)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Data Loading","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"import multiprocessing\nimport time\n\n\n\nfrom typing import Any, Optional, Tuple\n\nimport torch\nimport torch.nn as nn\nimport torchvision\nimport torchvision.transforms as transforms\nimport torch.backends.cudnn as cudnn\n\nfrom torch.utils.data import TensorDataset, DataLoader, Dataset\nfrom sklearn.preprocessing import LabelEncoder\nfrom PIL import Image\nfrom tqdm import tqdm\n\nIN_KERNEL = os.environ.get('KAGGLE_WORKING_DIR') is not None\nMIN_SAMPLES_PER_CLASS = 50\nBATCH_SIZE = 1\nLEARNING_RATE = 1e-4 \nLR_STEP = 3\nLR_FACTOR = 0.5\nNUM_WORKERS = 0\nMAX_STEPS_PER_EPOCH = 3000\nNUM_EPOCHS = 5  # increase batch size,and epoches for better results. \nLOG_FREQ = 500\nNUM_TOP_PREDICTS = 20\nTIME_LIMIT = 9 * 60 * 60\n\nclass ImageDataset(torch.utils.data.Dataset):\n    def __init__(self, dataframe: pd.DataFrame, mode: str) -> None:\n        print(f'creating data loader - {mode}')\n        assert mode in ['train', 'val', 'test']\n\n        self.df = dataframe\n        self.mode = mode\n\n        transforms_list = []\n\n        if self.mode == 'train' or 'test' or 'val':\n            transforms_list = [\n                transforms.RandomHorizontalFlip(),\n                transforms.RandomChoice([\n                    transforms.Resize((64,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            ]\n\n        transforms_list.extend([\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) -> Any:\n        ''' Returns: tuple (sample, target) '''\n        filename = self.df.id.values[index]\n        a = filename[0]\n        b = filename[1]\n        c = filename[2]\n\n        directory = 'test' if self.mode == 'test' else 'train'\n        \n        \n        sample = Image.open(f'../input/landmark-recognition-2020/{directory}/{a}/{b}/{c}/{filename}.jpg')\n        assert sample.mode == 'RGB'\n\n        image = self.transforms(sample)\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]\n\ndef 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] # FIXME: incorrect, not all test images depict landmarks\n    return res\n\nclass 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\n\ndef load_data() -> 'Tuple[DataLoader[np.ndarray], DataLoader[np.ndarray], LabelEncoder, int]':\n    torch.multiprocessing.set_sharing_strategy('file_system')\n    cudnn.benchmark = True\n\n    # only use classes which have at least MIN_SAMPLES_PER_CLASS samples\n    print('loading data...')\n    df = pd.read_csv('../input/landmark-recognition-2020/train.csv')\n    counts = df.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_df = df.loc[df.landmark_id.isin(selected_classes)].copy()\n    print('train_df', train_df.shape)\n\n    test_df = pd.read_csv('../input/landmark-recognition-2020/sample_submission.csv', dtype=str)\n    test_df.drop(columns='landmarks', inplace=True)\n    print('test_df', test_df.shape)\n\n\n    label_encoder = LabelEncoder()\n    label_encoder.fit(train_df.landmark_id.values)\n    print('found classes', len(label_encoder.classes_))\n    assert len(label_encoder.classes_) == num_classes\n\n    train_df.landmark_id = label_encoder.transform(train_df.landmark_id)\n\n    train_dataset = ImageDataset(train_df, mode='train')\n    test_dataset = ImageDataset(test_df, mode='test')\n\n    train_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE,\n                              shuffle=False, num_workers=NUM_WORKERS, 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\n\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# TRAIN MODEL","execution_count":null},{"metadata":{"trusted":true},"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 % 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}')\n\ndef 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\n\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Making submission","execution_count":null},{"metadata":{"trusted":true},"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')\n\ndef has_time_run_out() -> bool:\n    return time.time() - global_start_time > TIME_LIMIT - 500\n\nif __name__ == '__main__':\n    global_start_time = time.time()\n    train_loader, test_loader, label_encoder, num_classes = load_data()\n\n    model = torchvision.models.resnet50(pretrained=False)\n    model.avg_pool = nn.AdaptiveAvgPool2d(1)\n    model.fc = nn.Linear(model.fc.in_features, num_classes)\n    model.cuda()\n\n    criterion = nn.CrossEntropyLoss()\n\n    optimizer = torch.optim.Adam(model.parameters(), lr=LEARNING_RATE)\n    lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=LR_STEP,\n                                                   gamma=LR_FACTOR)\n\n    for epoch in range(1, NUM_EPOCHS + 1):\n        print('-' * 50)\n        train(train_loader, model, criterion, optimizer, epoch, lr_scheduler)\n        lr_scheduler.step()\n\n        if has_time_run_out():\n            break\n\n    print('inference mode')\n    generate_submission(test_loader, model, label_encoder)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Thanks for visiting my kernel, more to come soon.**","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}