{"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":"# Imports","metadata":{}},{"cell_type":"code","source":"!pip install timm\nimport timm\nfrom timm.optim import Lookahead, RAdam","metadata":{"execution":{"iopub.status.busy":"2022-05-27T16:44:01.708691Z","iopub.execute_input":"2022-05-27T16:44:01.708985Z","iopub.status.idle":"2022-05-27T16:44:10.203981Z","shell.execute_reply.started":"2022-05-27T16:44:01.708935Z","shell.execute_reply":"2022-05-27T16:44:10.203206Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install pytorch_metric_learning \nfrom pytorch_metric_learning import losses","metadata":{"execution":{"iopub.status.busy":"2022-05-27T16:44:10.21634Z","iopub.execute_input":"2022-05-27T16:44:10.216593Z","iopub.status.idle":"2022-05-27T16:44:18.47674Z","shell.execute_reply.started":"2022-05-27T16:44:10.216558Z","shell.execute_reply":"2022-05-27T16:44:18.475946Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Shared Imports\nimport random\nimport math\nimport os\nimport pathlib\nfrom tqdm import tqdm\nfrom typing import Iterator, List, Optional, Tuple\nimport json\n\nimport numpy as np\nimport pandas as pd\nimport matplotlib\nimport matplotlib.pyplot as plt\n\nfrom PIL import Image as pil_image\nimport cv2 \n\n# Pre-processing imports\nfrom joblib import Parallel, delayed\n\n# Train imports\n## Pytorch/modell stuff\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchmetrics import Accuracy\nfrom torchmetrics import MAP#.detection.mean_ap import MeanAveragePrecision\nimport torchvision.models as models\nimport pytorch_lightning as pl\nfrom pytorch_lightning.callbacks import ModelCheckpoint, LearningRateMonitor\nfrom pytorch_lightning.loggers import TensorBoardLogger\n## Sim method\nfrom sklearn.metrics.pairwise import cosine_similarity\n\n## Loading and processing data\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.feature_extraction import image as sk_img\n\nimport albumentations as A\nimport albumentations.pytorch as APT","metadata":{"execution":{"iopub.status.busy":"2022-05-27T16:44:18.479078Z","iopub.execute_input":"2022-05-27T16:44:18.479339Z","iopub.status.idle":"2022-05-27T16:44:19.452938Z","shell.execute_reply.started":"2022-05-27T16:44:18.479306Z","shell.execute_reply":"2022-05-27T16:44:19.452182Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Globals","metadata":{}},{"cell_type":"code","source":"SEED = 42\n# Wheter to PAD the images\nPAD = True\n# The size of the images\nPATCH = (512, 512)\n# The number of matches to consider\nN_MATCHES = 5\n\n# Set random Seed\npl.seed_everything(SEED)","metadata":{"execution":{"iopub.status.busy":"2022-05-27T16:44:19.454293Z","iopub.execute_input":"2022-05-27T16:44:19.454545Z","iopub.status.idle":"2022-05-27T16:44:19.4673Z","shell.execute_reply.started":"2022-05-27T16:44:19.454511Z","shell.execute_reply":"2022-05-27T16:44:19.466622Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Training Configuration\nVAL_SIZE = 0.1\nBATCH_SIZE = 16\nEPOCHS = 10\nLEARNING_RATE = 0.001\nN_CHECKPOINTS = 2\nEMBEDDING_SIZE = 4096\n\n# Basemodel and number of workers for the dataloaders\nNUM_WORKERS = 2\n#BASE_MODEL = \"efficientnet_b1\"\nBASE_MODEL = \"eca_nfnet_l0\"\n#BASE_MODEL = \"efficientnet_b2_pruned\"\n#BASE_MODEL=\"resnet50\"\n#BASE_MODEL=\"regnety_120\"\n#BASE_MODEL=\"swin_small_patch4_window7_224\"\n#BASE_MODEL=\"resnest101e\"\n\n# Wheter to freeze layers or not\nFREEZE = False\nFREEZE_UNTIL = -1","metadata":{"execution":{"iopub.status.busy":"2022-05-27T16:44:19.468444Z","iopub.execute_input":"2022-05-27T16:44:19.46877Z","iopub.status.idle":"2022-05-27T16:44:19.475994Z","shell.execute_reply.started":"2022-05-27T16:44:19.46873Z","shell.execute_reply":"2022-05-27T16:44:19.475295Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# directory of the data\nDATA_DIR = pathlib.Path(\"../input/mlip-pad-resize-512x512\")\n# Work directory, where to store the data\nWORKING_DIR = pathlib.Path(\"\")\n# Locations of the original train set, to derive the chain names\nCHAIN_DIR = pathlib.Path(\"../input/hotel-id-to-combat-human-trafficking-2022-fgvc9/train_images\")\n# Locations of the train images in the data directory\nTRAIN_DIR = DATA_DIR / pathlib.Path(\"images\")","metadata":{"execution":{"iopub.status.busy":"2022-05-27T16:44:19.477378Z","iopub.execute_input":"2022-05-27T16:44:19.477677Z","iopub.status.idle":"2022-05-27T16:44:19.485304Z","shell.execute_reply.started":"2022-05-27T16:44:19.477642Z","shell.execute_reply":"2022-05-27T16:44:19.484484Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Loading Data","metadata":{}},{"cell_type":"code","source":"chain_names = os.listdir(CHAIN_DIR)\ntrain_file = DATA_DIR / pathlib.Path(\"train.csv\")\n\n# Encode the chain identifiers so that the model can work with it and save it so it can be retrieved\ntrain_df = pd.read_csv(train_file)    \ntrain_df[\"hotel_code\"] = train_df[\"hotel_id\"].astype('category').cat.codes.values.astype(np.int64)\n\nhotel_id_code_df = train_df.drop(columns=[\"image_id\"]).drop_duplicates().reset_index(drop=True)\nhotel_id_code_df.to_csv(WORKING_DIR / 'hotel_id_code_mapping.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2022-05-27T16:44:19.486284Z","iopub.execute_input":"2022-05-27T16:44:19.48649Z","iopub.status.idle":"2022-05-27T16:44:19.536173Z","shell.execute_reply.started":"2022-05-27T16:44:19.48646Z","shell.execute_reply":"2022-05-27T16:44:19.535522Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(\"Number of images:\", len(train_df))\nprint(\"Number of different classes:\", len(chain_names))\ntrain_df.head()","metadata":{"execution":{"iopub.status.busy":"2022-05-27T16:44:19.537164Z","iopub.execute_input":"2022-05-27T16:44:19.538755Z","iopub.status.idle":"2022-05-27T16:44:19.551045Z","shell.execute_reply.started":"2022-05-27T16:44:19.538721Z","shell.execute_reply":"2022-05-27T16:44:19.550204Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Train val split","metadata":{}},{"cell_type":"code","source":"train_set, val_set = train_test_split(train_df, test_size=VAL_SIZE, random_state=SEED)\nprint(\"Number of train images:\", len(train_set))\nprint(\"Number of val images:\", len(val_set))\ntrain_set.head()","metadata":{"execution":{"iopub.status.busy":"2022-05-27T16:44:19.552644Z","iopub.execute_input":"2022-05-27T16:44:19.552901Z","iopub.status.idle":"2022-05-27T16:44:19.569632Z","shell.execute_reply.started":"2022-05-27T16:44:19.552867Z","shell.execute_reply":"2022-05-27T16:44:19.568903Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Dataset Class","metadata":{}},{"cell_type":"code","source":"class ImageDataset(Dataset):\n    def __init__(self,\n                 data: pd.DataFrame,\n                 data_path: pathlib.Path,\n                 transform: Optional = None,\n                ):\n        self.data = data\n        self.data_path = data_path\n        self.transform = transform\n\n    def __len__(self) -> int:\n        return len(self.data)\n\n    def __getitem__(self, idx: int):\n        record = self.data.iloc[idx]\n\n        image_path = self.data_path / record[\"image_id\"]\n        image = np.array(pil_image.open(image_path)).astype(np.uint8)\n        \n        if self.transform:\n            transformed = self.transform(image=image)\n            image = transformed[\"image\"]\n        \n        label = record['hotel_code']\n        return image, label","metadata":{"execution":{"iopub.status.busy":"2022-05-27T16:44:19.572187Z","iopub.execute_input":"2022-05-27T16:44:19.572372Z","iopub.status.idle":"2022-05-27T16:44:19.579329Z","shell.execute_reply.started":"2022-05-27T16:44:19.572348Z","shell.execute_reply":"2022-05-27T16:44:19.578578Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Data augmentation","metadata":{}},{"cell_type":"code","source":"# Train augmentation\ntrain_aug = A.Compose([A.HorizontalFlip(p=0.25),\n                       #A.RandomCrop(224,224, p=1),\n                       A.VerticalFlip(p=0.01),\n                      A.RandomBrightnessContrast(brightness_limit=[-0.2,0.2], contrast_limit=[-0.2,0.2],p=0.5),\n                      A.ShiftScaleRotate(p=0.5, border_mode=cv2.BORDER_CONSTANT),\nA.OpticalDistortion(p=0.25),\nA.IAAPerspective(p=0.25),\nA.Blur(blur_limit=4, p=0.2),                       \nA.RandomSizedCrop(min_max_height=[PATCH[1]//4, PATCH[1]], height=PATCH[1], width=PATCH[0], p=0.5),\nA.CoarseDropout(p=0.5),A.CoarseDropout(p=1., \n                                      max_holes=1, \n                                      min_height=PATCH[0]//4, \n                                      max_height=PATCH[0]//2,\n                                      min_width=PATCH[1]//4,  \n                                      max_width=PATCH[1]//2, \n                                      fill_value=(255,0,0)\n                                     ),\n                      A.ToFloat(),\n                      APT.transforms.ToTensorV2(),\n                      ])\n\n# Validation augmentation, thus only occulsion\nval_aug = A.Compose([#A.RandomCrop(224,224, p=1),\n    A.CoarseDropout(p=0.75, max_holes=1, \n                    min_height=PATCH[0]//4, max_height=PATCH[0]//2,\n                    min_width=PATCH[1]//4,  max_width=PATCH[1]//2, \n                    fill_value=(255,0,0)),# simulating occlusions\n    A.ToFloat(),\n    APT.transforms.ToTensorV2(),\n])\n\n# Test augmentation, no occulsion\ntest_aug = A.Compose([#A.RandomCrop(224,224, p=1),\n                    A.ToFloat(),\n                     APT.transforms.ToTensorV2(),\n                     ])\n\n# # Training augmentation\n# train_aug = A.Compose([A.HorizontalFlip(p=0.25),\n#                        A.VerticalFlip(p=0.01),\n#                       A.CoarseDropout(p=1., \n#                                       max_holes=1, \n#                                       min_height=PATCH[0]//4, \n#                                       max_height=PATCH[0]//2,\n#                                       min_width=PATCH[1]//4,  \n#                                       max_width=PATCH[1]//2, \n#                                       fill_value=(255,0,0)\n#                                      ),\n#                       A.RandomBrightnessContrast(brightness_limit=[-0.2,0.2], contrast_limit=[-0.2,0.2],p=0.5),\n# #                       A.Blur(blur_limit=4, p=0.2),\n#                       #A.RandomCrop(PATCH[0]//2, PATCH[1]//2, p=0.5),\n#                       #A.Resize(PATCH[0], PATCH[1], p=1.),\n#                       A.RandomSizedCrop(min_max_height=[PATCH[1]//4, PATCH[1]], height=PATCH[1], width=PATCH[0], p=0.5),\n#                       A.ToFloat(),\n#                       APT.transforms.ToTensorV2(),\n#                       ])\n\n# # Validation augmentation, thus only occulsion\n# val_aug = A.Compose([A.CoarseDropout(p=1.,\n#                                      max_holes=1, \n#                                      min_height=PATCH[0]//4, \n#                                      max_height=PATCH[0]//2,\n#                                      min_width=PATCH[1]//4,  \n#                                      max_width=PATCH[1]//2, \n#                                      fill_value=(255,0,0)\n#                                     ),\n#                     A.ToFloat(),\n#                     APT.transforms.ToTensorV2(),\n#                     ])\n\n# # Test augmentation, no occulsion\n# test_aug = A.Compose([A.ToFloat(),\n#                      APT.transforms.ToTensorV2(),\n#                      ])","metadata":{"execution":{"iopub.status.busy":"2022-05-27T16:44:19.580586Z","iopub.execute_input":"2022-05-27T16:44:19.580945Z","iopub.status.idle":"2022-05-27T16:44:19.597585Z","shell.execute_reply.started":"2022-05-27T16:44:19.580908Z","shell.execute_reply":"2022-05-27T16:44:19.596827Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model","metadata":{}},{"cell_type":"code","source":"# source: https://github.com/ronghuaiyang/arcface-pytorch/blob/master/models/metrics.py\nclass ArcMarginProduct(nn.Module):\n    r\"\"\"Implement of large margin arc distance: :\n        Args:\n            in_features: size of each input sample\n            out_features: size of each output sample\n            s: norm of input feature\n            m: margin\n            cos(theta + m)\n        \"\"\"\n    def __init__(self, in_features, out_features, s=30.0, m=0.50, easy_margin=False):\n        super(ArcMarginProduct, self).__init__()\n        self.in_features = in_features\n        self.out_features = out_features\n        self.s = s\n        self.m = m\n        self.weight = nn.Parameter(torch.FloatTensor(out_features, in_features))\n        nn.init.xavier_uniform_(self.weight)\n\n        self.easy_margin = easy_margin\n        self.cos_m = math.cos(m)\n        self.sin_m = math.sin(m)\n        self.th = math.cos(math.pi - m)\n        self.mm = math.sin(math.pi - m) * m\n\n    def forward(self, input, label):\n        # --------------------------- cos(theta) & phi(theta) ---------------------------\n        cosine = F.linear(F.normalize(input), F.normalize(self.weight))\n        sine = torch.sqrt((1.0 - torch.pow(cosine, 2)).clamp(0, 1))\n        phi = cosine * self.cos_m - sine * self.sin_m\n        if self.easy_margin:\n            phi = torch.where(cosine > 0, phi, cosine)\n        else:\n            phi = torch.where(cosine > self.th, phi, cosine - self.mm)\n        # --------------------------- convert label to one-hot ---------------------------\n        # one_hot = torch.zeros(cosine.size(), requires_grad=True, device='cuda')\n        one_hot = torch.zeros(cosine.size(), device='cuda')\n        one_hot.scatter_(1, label.view(-1, 1).long(), 1)\n        # -------------torch.where(out_i = {x_i if condition_i else y_i) -------------\n        output = (one_hot * phi) + ((1.0 - one_hot) * cosine)  # you can use torch.where if your torch.__version__ is 0.4\n        output *= self.s\n\n        return output\n","metadata":{"execution":{"iopub.status.busy":"2022-05-27T16:44:19.601302Z","iopub.execute_input":"2022-05-27T16:44:19.6031Z","iopub.status.idle":"2022-05-27T16:44:19.614867Z","shell.execute_reply.started":"2022-05-27T16:44:19.603069Z","shell.execute_reply":"2022-05-27T16:44:19.614041Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class HotelModel(pl.LightningModule):\n    def __init__(self,\n                n_hotels: int,\n                steps_per_epoch: int,\n                n_embeddings: int = 256,\n                base_model = None,\n                pretrained: bool = False,\n                learning_rate: float = 0.003,\n                \n                ):\n        super().__init__()\n        \n        # Hyperparams\n        self.n_embeddings = n_embeddings\n        self.n_hotels = n_hotels\n        self.learning_rate = learning_rate\n        self.steps_per_epoch = steps_per_epoch\n        \n        # Metrics\n        self.loss_fn = nn.CrossEntropyLoss()\n        self.train_acc = Accuracy()\n        self.val_acc = Accuracy()\n        \n        # Model Definition \n        ## Base model\n        self.base_model = timm.create_model(base_model, pretrained=True)        \n        in_features = self.base_model.get_classifier().in_features\n        \n        fc_name, _ = list(self.base_model.named_modules())[-1]\n        if fc_name == 'classifier':\n            self.base_model.classifier = nn.Identity()\n        elif fc_name == 'head.fc':\n            self.base_model.head.fc = nn.Identity()\n        elif fc_name == 'fc':\n            self.base_model.fc = nn.Identity()\n        #else:\n             #raise Exception(\"unknown classifier layer: \" + fc_name)\n        \n        ## Arcface module\n        self.arc_face = ArcMarginProduct(self.n_embeddings, n_hotels, s=30.0, m=0.20, easy_margin=False)\n        \n        ## Top model\n        self.top_model = nn.Sequential(\n            nn.utils.weight_norm(nn.Linear(1000, self.n_embeddings*2), dim=None),\n            nn.BatchNorm1d(self.n_embeddings*2),\n            nn.Dropout(0.2),\n            nn.utils.weight_norm(nn.Linear(self.n_embeddings*2, self.n_embeddings)),\n            nn.BatchNorm1d(self.n_embeddings),\n        )\n        \n        # Save hyper params\n        self.save_hyperparameters()\n\n    def configure_optimizers(self):\n        optimizer = Lookahead(torch.optim.AdamW(self.parameters(), lr=self.learning_rate), k=3)\n        \n        scheduler = torch.optim.lr_scheduler.OneCycleLR(\n                    optimizer,\n                    max_lr=self.learning_rate,\n                    epochs=EPOCHS,\n                    steps_per_epoch=self.steps_per_epoch,\n                    div_factor=10,\n                    final_div_factor=1,\n                    pct_start=0.1,\n                    anneal_strategy=\"cos\",\n                )\n        \n        schedule = {\n            # Required: the scheduler instance.\n            \"scheduler\": scheduler,\n        }\n        return [optimizer], [schedule]\n    \n    def forward(self, x, targets = None):\n        y_hat = self.base_model(x)\n        y_hat = y_hat.view(y_hat.size(0), -1)\n        y_hat = self.top_model(y_hat)\n        \n        if targets is not None:\n            y_hat = self.arc_face(y_hat, targets)\n\n        return y_hat\n\n    def training_step(self, batch, batch_idx):\n        x, y = batch\n        \n        # Forward pass\n        y_hat = self.forward(x, y)\n        loss = self.loss_fn(y_hat, y)\n        self.train_acc(y_hat, y)\n\n        # Store results\n        self.log(\"train_loss\", loss, prog_bar=False)\n        \n        return loss\n    \n    def training_epoch_end(self, train_step_outputs) -> None:\n        # Log metrics\n        self.log(\"train_acc\", self.train_acc, prog_bar=True)\n\n    def validation_step(self, batch, batch_idx):\n        x, y = batch\n        \n        # Forward pass\n        y_hat = self.forward(x, y)\n        loss = self.loss_fn(y_hat, y)\n        self.val_acc(y_hat, y)\n\n        # Store results\n        self.log(\"val_loss\", loss, prog_bar=False)\n        return y_hat\n        \n    def validation_epoch_end(self, validation_step_outputs) -> None:\n        self.log(\"val_acc\", self.val_acc, prog_bar=True)\n        \n    def predict_step(self, batch, batch_idx):\n        y_hat = self.forward(batch)\n        return y_hat","metadata":{"execution":{"iopub.status.busy":"2022-05-27T16:44:19.616429Z","iopub.execute_input":"2022-05-27T16:44:19.616748Z","iopub.status.idle":"2022-05-27T16:44:19.638981Z","shell.execute_reply.started":"2022-05-27T16:44:19.616715Z","shell.execute_reply":"2022-05-27T16:44:19.63832Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Training","metadata":{}},{"cell_type":"markdown","source":"## Data loader","metadata":{}},{"cell_type":"code","source":"train_data = ImageDataset(train_set, TRAIN_DIR, transform=train_aug)\nval_data = ImageDataset(val_set, TRAIN_DIR, transform=val_aug)","metadata":{"execution":{"iopub.status.busy":"2022-05-27T16:44:19.640333Z","iopub.execute_input":"2022-05-27T16:44:19.640821Z","iopub.status.idle":"2022-05-27T16:44:19.64949Z","shell.execute_reply.started":"2022-05-27T16:44:19.640784Z","shell.execute_reply":"2022-05-27T16:44:19.648832Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_dataloader = DataLoader(train_data, batch_size=BATCH_SIZE, shuffle=True, num_workers=NUM_WORKERS, drop_last=True)\nval_dataloader = DataLoader(val_data, batch_size=BATCH_SIZE, shuffle=False, num_workers=NUM_WORKERS)","metadata":{"execution":{"iopub.status.busy":"2022-05-27T16:44:19.652035Z","iopub.execute_input":"2022-05-27T16:44:19.652577Z","iopub.status.idle":"2022-05-27T16:44:19.658519Z","shell.execute_reply.started":"2022-05-27T16:44:19.652544Z","shell.execute_reply":"2022-05-27T16:44:19.657854Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Model","metadata":{}},{"cell_type":"code","source":"model = HotelModel(n_hotels=len(chain_names),\n                   n_embeddings=EMBEDDING_SIZE,\n                   base_model=BASE_MODEL,\n                   learning_rate=LEARNING_RATE,\n                   pretrained=True,\n                   steps_per_epoch=len(train_dataloader)\n                  )\n                   \n\npattern = \"epoch_{epoch:04d}.step_{step:09d}.val-map_{val_acc:.4f}\"\nModelCheckpoint.CHECKPOINT_NAME_LAST = pattern + \".last\"\ncheckpointer = ModelCheckpoint(\n        monitor=\"val_acc\",\n        filename=pattern + \".best\",\n        save_last=True,\n        auto_insert_metric_name=False,\n        save_top_k=N_CHECKPOINTS,\n    )","metadata":{"execution":{"iopub.status.busy":"2022-05-27T16:44:19.661177Z","iopub.execute_input":"2022-05-27T16:44:19.661411Z","iopub.status.idle":"2022-05-27T16:44:20.768997Z","shell.execute_reply.started":"2022-05-27T16:44:19.661358Z","shell.execute_reply":"2022-05-27T16:44:20.768204Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Calculate Distance","metadata":{}},{"cell_type":"code","source":"def generate_embeddings(loader, model, bar_desc=\"Generating embeds\"):\n    targets_all = []\n    outputs_all = []\n    \n    model = model.to(args.device)\n    model.eval()\n    with torch.no_grad():\n        x = tqdm(loader, desc=bar_desc)\n        for i, sample in enumerate(x):\n            input = sample[0].to(args.device)\n            target = sample[1].to(args.device)\n            output = model(input)\n            \n            targets_all.extend(target.cpu().numpy())\n            outputs_all.extend(output.detach().cpu().numpy())\n\n    targets_all = np.array(targets_all).astype(np.float32)\n    outputs_all = np.array(outputs_all).astype(np.float32)\n            \n    return outputs_all, targets_all","metadata":{"execution":{"iopub.status.busy":"2022-05-27T16:44:20.770299Z","iopub.execute_input":"2022-05-27T16:44:20.770556Z","iopub.status.idle":"2022-05-27T16:44:20.778935Z","shell.execute_reply.started":"2022-05-27T16:44:20.770523Z","shell.execute_reply":"2022-05-27T16:44:20.778212Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# find 5 most similar images from different hotels and return their hotel_id_code\ndef find_matches(query, base_embeds, base_targets, k=N_MATCHES):\n    distance_df = pd.DataFrame(index=np.arange(len(base_targets)), data={\"hotel_id_code\": base_targets})\n    # calculate cosine distance of query embeds to all base embeds\n    distance_df[\"distance\"] = cosine_similarity([query], base_embeds)[0]\n    # sort by distance and hotel_id\n    distance_df = distance_df.sort_values(by=[\"distance\", \"hotel_id_code\"], ascending=False).reset_index(drop=True)\n    # return first 5 different hotel_id_codes\n    return distance_df[\"hotel_id_code\"].unique()[:N_MATCHES]","metadata":{"execution":{"iopub.status.busy":"2022-05-27T16:44:20.780227Z","iopub.execute_input":"2022-05-27T16:44:20.780669Z","iopub.status.idle":"2022-05-27T16:44:20.789278Z","shell.execute_reply.started":"2022-05-27T16:44:20.780633Z","shell.execute_reply":"2022-05-27T16:44:20.788543Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def test_similarity(args, base_loader, test_loader, model):\n    base_embeds, base_targets = generate_embeddings(base_loader, model, \"Generate base embeddings\")\n    test_embeds, test_targets = generate_embeddings(test_loader, model, \"Generate test embeddings\")\n    \n    preds = []\n    for query_embeds in tqdm(test_embeds, desc=\"Similarity - match finding\"):\n        tmp = find_matches(query_embeds, base_embeds, base_targets)\n        preds.extend([tmp])\n        \n    preds = np.array(preds)\n    test_targets_N = np.repeat([test_targets], repeats=N_MATCHES, axis=0).T\n    # check if any of top 5 predictions are correct and calculate mean accuracy\n    acc_top_5 = (preds == test_targets_N).any(axis=1).mean()\n    # calculate prediction accuracy\n    acc_top_1 = np.mean(test_targets == preds[:, 0])\n    print(f\"Similarity accuracy: {acc_top_1:0.4f}, Top 5 acc: {acc_top_5:0.4f}\") # NEED TO CORRECT THE MAP CALCULATION","metadata":{"execution":{"iopub.status.busy":"2022-05-27T16:44:20.801326Z","iopub.execute_input":"2022-05-27T16:44:20.801546Z","iopub.status.idle":"2022-05-27T16:44:20.810515Z","shell.execute_reply.started":"2022-05-27T16:44:20.801512Z","shell.execute_reply":"2022-05-27T16:44:20.809664Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Freeze Layers","metadata":{}},{"cell_type":"code","source":"if FREEZE:\n    for parameter in model.parameters():\n        parameter.requires_grad = False\n    for parameter in model.base_model.blocks[FREEZE_UNTIL:-1].parameters():\n        parameter.requires_grad = True","metadata":{"execution":{"iopub.status.busy":"2022-05-27T16:44:20.811899Z","iopub.execute_input":"2022-05-27T16:44:20.812398Z","iopub.status.idle":"2022-05-27T16:44:20.818342Z","shell.execute_reply.started":"2022-05-27T16:44:20.81235Z","shell.execute_reply":"2022-05-27T16:44:20.817637Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Train loop","metadata":{}},{"cell_type":"code","source":"trainer = pl.Trainer(\n    max_epochs=EPOCHS,\n    gpus=torch.cuda.device_count(),\n    callbacks=[checkpointer, LearningRateMonitor()],\n    default_root_dir=\"logs/\",\n)\n\ntrainer.fit(model, train_dataloaders=train_dataloader, val_dataloaders=val_dataloader)","metadata":{"execution":{"iopub.status.busy":"2022-05-27T16:44:20.820026Z","iopub.execute_input":"2022-05-27T16:44:20.820313Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Test Model","metadata":{}},{"cell_type":"code","source":"val_test_dataloader = DataLoader(val_data, batch_size=1, shuffle=False, num_workers=2)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"base_dataset   = ImageDataset(train_df, TRAIN_DIR, transform=test_aug)\nbase_loader    = DataLoader(base_dataset, num_workers=NUM_WORKERS, batch_size=BATCH_SIZE, shuffle=False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class args:\n    epochs = 5\n    lr = 1e-3\n    batch_size = 64\n    num_workers = 2\n    val_samples = 1\n    embedding_size = 128\n    backbone_name = \"efficientnet_b0\"\n    n_classes = train_df[\"hotel_code\"].nunique()\n    device = ('cuda' if torch.cuda.is_available() else 'cpu')\n    \ntest_similarity(args, train_dataloader, val_test_dataloader, model)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Save embeddings","metadata":{}},{"cell_type":"code","source":"# generate embeddings for all train images and save them for inference\nbase_embeds, _ = generate_embeddings(base_loader, model, \"Generate embeddings for all images\")\ntrain_df[\"embeddings\"] = list(base_embeds)\ntrain_df.to_pickle(f\"base_image-embeddings.pkl\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}