{"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":"# Setup\nThis notebook is intended to run on colab, so some things are commented out to make it work on kaggle.","metadata":{"id":"5tPFJ89V3BFT"}},{"cell_type":"code","source":"# !nvidia-smi","metadata":{"id":"vW7S-8qm3FfU","outputId":"935d39f2-dfcb-4745-de13-d5bc7568da44","execution":{"iopub.status.busy":"2022-05-25T12:47:41.692667Z","iopub.execute_input":"2022-05-25T12:47:41.693190Z","iopub.status.idle":"2022-05-25T12:47:41.698315Z","shell.execute_reply.started":"2022-05-25T12:47:41.693075Z","shell.execute_reply":"2022-05-25T12:47:41.697493Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# from google.colab import drive\n# drive.mount('/gdrive')\n# %cd /gdrive","metadata":{"id":"_ABy2M3C3HEb","outputId":"51190e45-7e72-4dc6-9a2c-57d34e2359d8","execution":{"iopub.status.busy":"2022-05-25T12:47:41.710585Z","iopub.execute_input":"2022-05-25T12:47:41.710950Z","iopub.status.idle":"2022-05-25T12:47:41.714648Z","shell.execute_reply.started":"2022-05-25T12:47:41.710914Z","shell.execute_reply":"2022-05-25T12:47:41.713809Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# !pip install efficientnet_pytorch\n# !pip install git+https://github.com/rwightman/pytorch-image-models\n# !pip install pytorch-metric-learning\n# !pip install faiss-gpu\n# !pip install imgaug -U\n# !pip install albumentations -U","metadata":{"id":"1JM4AM6M2_aj","outputId":"6b09341e-e2d6-4dda-bc2d-942c34e73fe5","execution":{"iopub.status.busy":"2022-05-25T12:47:41.729591Z","iopub.execute_input":"2022-05-25T12:47:41.729936Z","iopub.status.idle":"2022-05-25T12:47:41.733611Z","shell.execute_reply.started":"2022-05-25T12:47:41.729903Z","shell.execute_reply":"2022-05-25T12:47:41.732760Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Imports","metadata":{"id":"MyC4gTwZ3MKJ"}},{"cell_type":"code","source":"import sys\nsys.path.append('../input/timm-pytorch-image-models/pytorch-image-models-master')","metadata":{"execution":{"iopub.status.busy":"2022-05-25T12:47:41.760654Z","iopub.execute_input":"2022-05-25T12:47:41.760990Z","iopub.status.idle":"2022-05-25T12:47:41.776041Z","shell.execute_reply.started":"2022-05-25T12:47:41.760957Z","shell.execute_reply":"2022-05-25T12:47:41.775001Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport random\nimport os\nimport math","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","id":"u0Bz2ktn2_ap","execution":{"iopub.status.busy":"2022-05-25T12:47:41.781271Z","iopub.execute_input":"2022-05-25T12:47:41.781889Z","iopub.status.idle":"2022-05-25T12:47:41.789699Z","shell.execute_reply.started":"2022-05-25T12:47:41.781842Z","shell.execute_reply":"2022-05-25T12:47:41.788779Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from sklearn.metrics import accuracy_score\nfrom sklearn.utils import class_weight\nfrom PIL import Image as pil_image\nfrom tqdm import tqdm\nimport scipy\n\nimport matplotlib\nimport matplotlib.pyplot as plt\nimport plotly.express as px\nimport plotly.graph_objects as go","metadata":{"id":"tOszKuxt3PXn","execution":{"iopub.status.busy":"2022-05-25T12:47:41.799909Z","iopub.execute_input":"2022-05-25T12:47:41.800295Z","iopub.status.idle":"2022-05-25T12:47:44.906605Z","shell.execute_reply.started":"2022-05-25T12:47:41.800261Z","shell.execute_reply":"2022-05-25T12:47:44.905648Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.utils.data import DataLoader\n\nimport timm\nfrom timm.optim import Lookahead, RAdam\n#from pytorch_metric_learning import miners, losses, samplers , distances, regularizers ","metadata":{"id":"uQE7wYFR3QxV","execution":{"iopub.status.busy":"2022-05-25T12:47:44.908157Z","iopub.execute_input":"2022-05-25T12:47:44.908528Z","iopub.status.idle":"2022-05-25T12:47:47.853830Z","shell.execute_reply.started":"2022-05-25T12:47:44.908489Z","shell.execute_reply":"2022-05-25T12:47:47.852708Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Global","metadata":{"id":"tirOg6jm3aIB"}},{"cell_type":"code","source":"IMG_SIZE = 512\nSEED = 42\nPROJECT_FOLDER = \"../input/hotelid-2022-train-images-512x512/\"\nDATA_FOLDER = \"../input/hotelid-2022-train-images-512x512/images/\"\nTRAIN_DATA_FOLDER = \"../input/hotelid-2022-train-images-512x512/images/\"\nTEST_DATA_FOLDER = \"../input/hotel-id-to-combat-human-trafficking-2022-fgvc9/test_images/\"\nOUTPUT_FOLDER = \"./\"\n\n# PROJECT_FOLDER = \"/gdrive/MyDrive/Projects/Hotel-ID/\"\n# DATA_FOLDER = \"/home/data/\"\n# OUTPUT_FOLDER = PROJECT_FOLDER + \"output/\"","metadata":{"id":"DV7qHDuYGoJH","execution":{"iopub.status.busy":"2022-05-25T12:47:47.856099Z","iopub.execute_input":"2022-05-25T12:47:47.856492Z","iopub.status.idle":"2022-05-25T12:47:47.861495Z","shell.execute_reply.started":"2022-05-25T12:47:47.856452Z","shell.execute_reply":"2022-05-25T12:47:47.860257Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# !mkdir {DATA_FOLDER}\n# !unzip -qq {PROJECT_FOLDER}data/train-{IMG_SIZE}x{IMG_SIZE}.zip -d /home/data/","metadata":{"id":"TB9CXg8U3bbQ","execution":{"iopub.status.busy":"2022-05-25T12:47:47.863424Z","iopub.execute_input":"2022-05-25T12:47:47.864012Z","iopub.status.idle":"2022-05-25T12:47:47.873524Z","shell.execute_reply.started":"2022-05-25T12:47:47.863975Z","shell.execute_reply":"2022-05-25T12:47:47.872648Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(os.listdir(PROJECT_FOLDER))\nprint(len(os.listdir(DATA_FOLDER)))","metadata":{"id":"1wH0zWUS2_aq","outputId":"1ab42f45-5645-4863-d60d-2117f932a921","execution":{"iopub.status.busy":"2022-05-25T12:47:47.874920Z","iopub.execute_input":"2022-05-25T12:47:47.875274Z","iopub.status.idle":"2022-05-25T12:47:48.986077Z","shell.execute_reply.started":"2022-05-25T12:47:47.875237Z","shell.execute_reply":"2022-05-25T12:47:48.984551Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Helper functions - seed and metric calculator","metadata":{"id":"ZmZ-HheL3itu"}},{"cell_type":"code","source":"def seed_everything(seed):\n    random.seed(seed)\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    torch.backends.cudnn.deterministic = True","metadata":{"id":"csp2OMgo2_ar","execution":{"iopub.status.busy":"2022-05-25T12:47:48.987505Z","iopub.execute_input":"2022-05-25T12:47:48.987853Z","iopub.status.idle":"2022-05-25T12:47:48.993440Z","shell.execute_reply.started":"2022-05-25T12:47:48.987817Z","shell.execute_reply":"2022-05-25T12:47:48.992359Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Dataset and transformations","metadata":{"id":"8V_xuoN73lON"}},{"cell_type":"code","source":"import albumentations as A\nimport albumentations.pytorch as APT\nimport cv2 \n\n# train_transform = A.Compose([\n#     # A.Resize(IMG_SIZE, IMG_SIZE),\n#     # A.CLAHE(p=1), \n    \n# #     A.HorizontalFlip(p=0.75),\n#     A.VerticalFlip(p=0.25),\n# #     A.ShiftScaleRotate(p=0.5, border_mode=cv2.BORDER_CONSTANT),\n#     A.OpticalDistortion(p=0.25),\n#     A.Perspective (p=0.25),\n# #     A.CoarseDropout(p=0.5),\n\n#     A.RandomBrightnessContrast(p=0.75),\n#     A.ToFloat(),\n#     APT.transforms.ToTensorV2(),\n# ])\n\n\n# val_transform = A.Compose([\n#     # A.Resize(IMG_SIZE, IMG_SIZE),\n#     # A.CLAHE(p=1),\n#     A.ToFloat(),\n#     APT.transforms.ToTensorV2(),\n# ])","metadata":{"id":"8ucWZHeG2_as","execution":{"iopub.status.busy":"2022-05-25T12:47:48.995218Z","iopub.execute_input":"2022-05-25T12:47:48.995939Z","iopub.status.idle":"2022-05-25T12:47:49.776163Z","shell.execute_reply.started":"2022-05-25T12:47:48.995901Z","shell.execute_reply":"2022-05-25T12:47:49.775379Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class HotelTrainDataset:\n    def __init__(self, data, transform=None, data_path=\"train_images/\"):\n        self.data = data\n        self.data_path = data_path\n        self.transform = transform\n        self.fake_load = False\n\n    def __len__(self):\n        return len(self.data)\n    \n    def __getitem__(self, idx):\n        record = self.data.iloc[idx]\n        image_path = self.data_path + record[\"image_id\"]\n\n        if self.fake_load:\n            image = np.random.randint(0, 255, (32, 32, 3)).astype(np.uint8)\n        else:\n            image = np.array(pil_image.open(image_path)).astype(np.uint8)\n\n        if self.transform:\n            transformed = self.transform(image=image)\n        \n        return {\n            \"image\" : transformed[\"image\"],\n            \"target\" : record['hotel_id_code'],\n        }","metadata":{"id":"EiLYsfKq2_at","execution":{"iopub.status.busy":"2022-05-25T12:47:49.778984Z","iopub.execute_input":"2022-05-25T12:47:49.779369Z","iopub.status.idle":"2022-05-25T12:47:49.787111Z","shell.execute_reply.started":"2022-05-25T12:47:49.779316Z","shell.execute_reply":"2022-05-25T12:47:49.786136Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model","metadata":{"id":"FpR2HfK93pvS"}},{"cell_type":"code","source":"class EmbeddingNet(nn.Module):\n    def __init__(self, n_classes=100, embed_size=64, backbone_name=\"efficientnet_b0\"):\n        super(EmbeddingNet, self).__init__()\n\n        self.embed_size = embed_size\n        self.backbone = timm.create_model(backbone_name, pretrained=False)\n        in_features = self.backbone.get_classifier().in_features\n\n        fc_name, _ = list(self.backbone.named_modules())[-1]\n        if fc_name == 'classifier':\n            self.backbone.classifier = nn.Identity()\n        elif fc_name == 'head.fc':\n            self.backbone.head.fc = nn.Identity()\n        elif fc_name == 'fc':\n            self.backbone.fc = nn.Identity()\n        else:\n            raise Exception(\"unknown classifier layer: \" + fc_name)\n\n        self.post = nn.Sequential(\n            nn.utils.weight_norm(nn.Linear(in_features, self.embed_size*2), dim=None),\n            nn.BatchNorm1d(self.embed_size*2),\n            nn.Dropout(0.2),\n            nn.utils.weight_norm(nn.Linear(self.embed_size*2, self.embed_size)),\n        )\n\n        self.classifier = nn.Sequential(\n            nn.BatchNorm1d(self.embed_size),\n            nn.Dropout(0.2),\n            nn.Linear(self.embed_size, n_classes),\n        )\n        \n    def embed_and_classify(self, x):\n        x = self.forward(x)\n        return x, self.classifier(x)\n\n    def forward(self, x):\n        x = self.backbone(x)\n        x = x.view(x.size(0), -1)\n        x = self.post(x)\n        return x","metadata":{"id":"_2mse3zX3pFQ","execution":{"iopub.status.busy":"2022-05-25T12:47:49.789315Z","iopub.execute_input":"2022-05-25T12:47:49.789701Z","iopub.status.idle":"2022-05-25T12:47:49.803155Z","shell.execute_reply.started":"2022-05-25T12:47:49.789661Z","shell.execute_reply":"2022-05-25T12:47:49.802169Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model helper functions","metadata":{"id":"mTFCinps35ci"}},{"cell_type":"code","source":"from sklearn.metrics.pairwise import cosine_similarity\n\ndef get_embeds(loader, model, bar_desc=\"Generating embeds\"):\n    outputs_all = []\n    \n    model.eval()\n    with torch.no_grad():\n        t = tqdm(loader, desc=bar_desc)\n        for i, sample in enumerate(t):\n            input = sample['image'].to(args.device)\n            output = model(input)\n            outputs_all.extend(output.detach().cpu().numpy())\n#             outputs_all.extend(output.detach().cpu().numpy().astype(np.float16))\n            \n            \n    return outputs_all","metadata":{"id":"xW5LIe1l2_at","execution":{"iopub.status.busy":"2022-05-25T12:47:49.804786Z","iopub.execute_input":"2022-05-25T12:47:49.805346Z","iopub.status.idle":"2022-05-25T12:47:49.816371Z","shell.execute_reply.started":"2022-05-25T12:47:49.805286Z","shell.execute_reply":"2022-05-25T12:47:49.815531Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_distance_matrix(embeds, base_embeds, distance_func):\n    distance_matrix = []\n    base_embeds = torch.Tensor(base_embeds)\n    embeds_dataset = torch.utils.data.TensorDataset(torch.Tensor(embeds))\n    embeds_dataloader = DataLoader(embeds_dataset, num_workers=2, batch_size=1024, shuffle=False)\n    \n    t = tqdm(embeds_dataloader)\n    for i, sample in enumerate(t): \n        distances = distance_func(sample[0], base_embeds)\n        distance_matrix.extend(distances.numpy())\n        \n    return np.array(distance_matrix)","metadata":{"id":"syXhlJrJG2AV","execution":{"iopub.status.busy":"2022-05-25T12:47:49.817610Z","iopub.execute_input":"2022-05-25T12:47:49.817908Z","iopub.status.idle":"2022-05-25T12:47:49.825859Z","shell.execute_reply.started":"2022-05-25T12:47:49.817876Z","shell.execute_reply":"2022-05-25T12:47:49.824955Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def save_checkpoint(model, scheduler, optimizer, epoch, name, loss=None, score=None):\n    checkpoint = {\"epoch\": epoch,\n                  \"model\": model.state_dict(),\n                  \"scheduler\": scheduler.state_dict(),\n                  \"optimizer\": optimizer.state_dict(),\n                  \"loss\": loss,\n                  \"score\": score,\n                  }\n\n    torch.save(checkpoint, f\"{OUTPUT_FOLDER}checkpoint-{name}.pt\")\n\n\ndef load_checkpoint(model, scheduler, optimizer, name):\n    if torch.cuda.is_available():\n        checkpoint = torch.load(\"../input/hotelidcosfaceecaresnet50dtrained/checkpoint-cosface-model-ecaresnet50d_pruned-512x512-4096embeds-3116hotels.pt\")\n    else:\n        checkpoint = torch.load(\"../input/hotelidcosfaceecaresnet50dtrained/checkpoint-cosface-model-ecaresnet50d_pruned-512x512-4096embeds-3116hotels.pt\",map_location=torch.device('cpu'))\n\n    model.load_state_dict(checkpoint[\"model\"])\n    scheduler.load_state_dict(checkpoint[\"scheduler\"])\n    # optimizer.load_state_dict(checkpoint[\"optimizer\"])\n\n    return model, scheduler, optimizer, checkpoint[\"epoch\"]","metadata":{"id":"ryZ6wE0zKPiz","execution":{"iopub.status.busy":"2022-05-25T12:47:49.827349Z","iopub.execute_input":"2022-05-25T12:47:49.827782Z","iopub.status.idle":"2022-05-25T12:47:49.837376Z","shell.execute_reply.started":"2022-05-25T12:47:49.827688Z","shell.execute_reply":"2022-05-25T12:47:49.836441Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def iterate_loader(loader, epochs):\n    loader.dataset.fake_load = True\n    with torch.no_grad():\n        for i in range(epochs):\n            t = tqdm(loader, desc=f\"Iterating loader {i+1}/{epochs}\")\n            for j, sample in enumerate(t):\n                images = sample['image']\n                targets = sample['target']\n\n    loader.dataset.fake_load = False","metadata":{"id":"o8sQ9dtJH1fu","execution":{"iopub.status.busy":"2022-05-25T12:47:49.839106Z","iopub.execute_input":"2022-05-25T12:47:49.839716Z","iopub.status.idle":"2022-05-25T12:47:49.847232Z","shell.execute_reply.started":"2022-05-25T12:47:49.839659Z","shell.execute_reply":"2022-05-25T12:47:49.846099Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def train_epoch(args, model, loader, criterion, optimizer, loss_optimizer, scheduler, epoch):\n    losses = []\n    targets_all = []\n    outputs_all = []\n    \n    model.train()\n    t = tqdm(loader)\n    \n    for i, sample in enumerate(t):\n        optimizer.zero_grad()\n        \n        images = sample['image'].to(args.device)\n        targets = sample['target'].to(args.device)\n        \n        embeds, outputs = model.embed_and_classify(images)\n        \n        \n        loss = criterion(embeds, targets)\n        \n        loss.backward()\n        optimizer.step()\n        loss_optimizer.step()\n        \n        if scheduler:\n            scheduler.step()\n                \n        losses.append(loss.item())\n        targets_all.extend(targets.cpu().numpy())\n        outputs_all.extend(torch.sigmoid(outputs).detach().cpu().numpy())\n\n        score = np.mean(targets_all == np.argmax(outputs_all, axis=1))\n        desc = f\"Epoch {epoch}/{args.epochs} - Train loss:{loss:0.4f}, score: {score:0.4f}\"\n        t.set_description(desc)\n        \n    return np.mean(losses), score\n\n\ndef test_closest_match(base_df, base_embeds, valid_targets, valid_embeds, model, distance_func, closest, n_matches=5):\n    distance_matrix = get_distance_matrix(valid_embeds, base_embeds, distance_func)\n\n    preds = []\n    N_val = len(valid_embeds)\n    for i in tqdm(range(N_val), total=N_val, desc=\"Getting closest match\"):\n        tmp_df = base_df.copy()\n        tmp_df[\"distance\"] = distance_matrix[i]\n        tmp_df = tmp_df.sort_values(by=[\"distance\", \"hotel_id\"], ascending=closest).reset_index(drop=True)\n        preds.extend([tmp_df[\"hotel_id_code\"].unique()[:n_matches]])\n\n    y = np.repeat([valid_targets], repeats=n_matches, axis=0).T\n    preds = np.array(preds)\n    acc_top_1 = (preds[:, 0] == valid_targets).mean()\n    acc_top_5 = (preds == y).any(axis=1).mean()\n    print(f\"Accuracy: {acc_top_1:0.4f}, top 5 accuracy: {acc_top_5:0.4f}\")\n    return preds, distance_matrix\n\n\ndef test(base_loader, valid_loader, model, distance_func, closest):\n    base_targets, base_embeds = get_embeds(base_loader, model, \"Generating embeds for train\")\n    valid_targets, valid_embeds = get_embeds(valid_loader, model, \"Generating embeds for test\")\n    val_preds, distance_matrix = test_closest_match(base_loader.dataset.data, base_embeds, valid_targets, valid_embeds, model, distance_func, closest)\n\n    return base_embeds, valid_embeds, base_targets, valid_targets, val_preds, distance_matrix","metadata":{"id":"SntLH82s2_au","execution":{"iopub.status.busy":"2022-05-25T12:47:49.849357Z","iopub.execute_input":"2022-05-25T12:47:49.850135Z","iopub.status.idle":"2022-05-25T12:47:49.867302Z","shell.execute_reply.started":"2022-05-25T12:47:49.850091Z","shell.execute_reply":"2022-05-25T12:47:49.866449Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Prepare data","metadata":{"id":"F2xgmwBW4LjC"}},{"cell_type":"code","source":"def sample_data(n_hotels, min_images, max_images):\n    data_df = pd.read_csv(PROJECT_FOLDER + \"train.csv\")\n    sample_df = data_df.groupby(\"hotel_id\").filter(lambda x: (x[\"image_id\"].nunique() > min_images) & (x[\"image_id\"].nunique() < max_images))\n    sample_df[\"hotel_id_code\"] = sample_df[\"hotel_id\"].astype('category').cat.codes.values.astype(np.int64)\n    sample_df = sample_df[sample_df[\"hotel_id_code\"] < n_hotels]\n\n    print(f\"Subsample with {len(sample_df.hotel_id.unique())} hotels out of {len(data_df.hotel_id.unique())}\" + \n          f\" with total {len(sample_df)} images ({len(sample_df) / len(data_df) * 100:0.2f} %)\")\n    \n    return sample_df","metadata":{"id":"JBkHrXYy2_av","execution":{"iopub.status.busy":"2022-05-25T12:47:49.868930Z","iopub.execute_input":"2022-05-25T12:47:49.869409Z","iopub.status.idle":"2022-05-25T12:47:49.879136Z","shell.execute_reply.started":"2022-05-25T12:47:49.869370Z","shell.execute_reply":"2022-05-25T12:47:49.878268Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# FOR TESTING DIFFERENT SETTING\n#data_df = sample_data(1000, 15, 50)\n\n# FOR FINAL TRAINING\ndata_df = pd.read_csv(PROJECT_FOLDER + \"train.csv\")\ndata_df[\"hotel_id_code\"] = data_df[\"hotel_id\"].astype('category').cat.codes.values.astype(np.int64)\n\nfig = go.Figure()\nfig.add_trace(go.Histogram(x=data_df[\"hotel_id_code\"]))\nfig.update_xaxes(type=\"category\")\nfig.show()","metadata":{"id":"Sn6HrWKQ2_aw","outputId":"2061b6a4-e02f-4644-a24f-33b32c9a1162","execution":{"iopub.status.busy":"2022-05-25T12:47:49.880272Z","iopub.execute_input":"2022-05-25T12:47:49.882598Z","iopub.status.idle":"2022-05-25T12:47:50.193053Z","shell.execute_reply.started":"2022-05-25T12:47:49.882571Z","shell.execute_reply":"2022-05-25T12:47:50.192133Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def train_and_validate(args, data_df):\n    model_name = f\"cosface-model-{args.backbone_name}-{IMG_SIZE}x{IMG_SIZE}-{args.embed_size}embeds-{args.n_classes}hotels\"\n    print(model_name)\n\n    seed_everything(seed=SEED)\n\n#     val_df = data_df.groupby(\"hotel_id\").sample(args.val_samples, random_state=SEED)\n    #     train_df = data_df[~data_df[\"image_id\"].isin(val_df[\"image_id\"])]\n    \n    val_df = data_df.groupby(\"hotel_id\").sample(args.val_samples, random_state=SEED)\n#     train_df = data_df[~data_df[\"image_id\"].isin(val_df[\"image_id\"])]\n    train_df = data_df[~data_df[\"image_id\"].isin(val_df[\"image_id\"])]\n\n    train_dataset = HotelTrainDataset(train_df, train_transform, data_path=DATA_FOLDER)\n    train_loader = DataLoader(train_dataset, num_workers=args.num_workers, batch_size=args.batch_size, shuffle=True, drop_last=True)\n    base_dataset = HotelTrainDataset(train_df, val_transform, data_path=DATA_FOLDER)\n    base_loader = DataLoader(base_dataset, num_workers=args.num_workers, batch_size=args.batch_size, shuffle=False)\n    val_dataset = HotelTrainDataset(val_df, val_transform, data_path=DATA_FOLDER)\n    valid_loader = DataLoader(val_dataset, num_workers=args.num_workers, batch_size=args.batch_size, shuffle=False)\n\n    print(f\"Base: {len(base_dataset)}\\nValidation: {len(val_dataset)}\")\n\n    model = EmbeddingNet(args.n_classes, args.embed_size, args.backbone_name)\n    model = model.to(args.device)\n\n    distance = distances.CosineSimilarity()\n\n    criterion = losses.CosFaceLoss(num_classes=args.n_classes, embedding_size=args.embed_size, embedding_regularizer = regularizers.RegularFaceRegularizer()).to(args.device) # Accuracy: 0.7200, top 5 accuracy: 0.8460\n    loss_optimizer = torch.optim.AdamW(criterion.parameters(), lr=args.lr)\n    optimizer = Lookahead(torch.optim.AdamW(model.parameters(), lr=args.lr), k=3)\n\n    scheduler = torch.optim.lr_scheduler.OneCycleLR(\n                    optimizer,\n                    max_lr=args.lr,\n                    epochs=args.epochs,\n                    steps_per_epoch=len(train_loader),\n                    div_factor=10,\n                    final_div_factor=1,\n                    pct_start=0.1,\n                    anneal_strategy=\"cos\",\n                )\n    \n    start_epoch = 1\n\n    if args.continue_from_checkpoint:\n        model, scheduler, optimizer, last_epoch = load_checkpoint(model, scheduler, optimizer, model_name)\n        iterate_loader(train_loader, last_epoch)\n        start_epoch = start_epoch + last_epoch\n\n    torch.cuda.empty_cache()\n\n    for epoch in range(start_epoch, args.epochs+1):\n        train_loss, train_score = train_epoch(args, model, train_loader, criterion, optimizer, loss_optimizer, scheduler, epoch)\n        save_checkpoint(model, scheduler, optimizer, epoch, model_name, train_loss, train_score)\n        if (epoch == 1):\n            _ = test(base_loader, valid_loader, model, distance, closest=False)\n\n    base_embeds, valid_embeds, base_targets, valid_targets, val_preds, distance_matrix = test(base_loader, valid_loader, model, distance, closest=False)\n    \n    output = {\"base_embeds\": base_embeds,\n              \"valid_embeds\": valid_embeds,\n              \"base_targets\": base_targets,\n              \"valid_targets\": valid_targets,\n              \"val_preds\": val_preds,\n              \"distance_matrix\": distance_matrix,\n              \"train_df\" : train_df,\n              \"valid_df\": val_df,\n              }\n\n    torch.save(output, f\"{OUTPUT_FOLDER}output-{model_name}.pt\")","metadata":{"id":"3aEmY6K7KY3H","execution":{"iopub.status.busy":"2022-05-25T12:47:50.194293Z","iopub.execute_input":"2022-05-25T12:47:50.194629Z","iopub.status.idle":"2022-05-25T12:47:50.212008Z","shell.execute_reply.started":"2022-05-25T12:47:50.194594Z","shell.execute_reply":"2022-05-25T12:47:50.211063Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def test_and_validate(args, data_df):\n    model_name = f\"cosface-model-{args.backbone_name}-{IMG_SIZE}x{IMG_SIZE}-{args.embed_size}embeds-{args.n_classes}hotels\"\n    print(model_name)\n    \n    model_path = \"../input/hotelidcosfaceecaresnet50dtrained/checkpoint-cosface-model-ecaresnet50d_pruned-512x512-4096embeds-3116hotels.pt\"\n    \n    seed_everything(seed=SEED)\n\n#     val_df = data_df.groupby(\"hotel_id\").sample(args.val_samples, random_state=SEED)\n    #     train_df = data_df[~data_df[\"image_id\"].isin(val_df[\"image_id\"])]\n    \n    val_df = data_df.groupby(\"hotel_id\").sample(args.val_samples, random_state=SEED)\n#     train_df = data_df[~data_df[\"image_id\"].isin(val_df[\"image_id\"])]\n    train_df = data_df[~data_df[\"image_id\"].isin(val_df[\"image_id\"])]\n\n    train_dataset = HotelTrainDataset(train_df, train_transform, data_path=DATA_FOLDER)\n    train_loader = DataLoader(train_dataset, num_workers=args.num_workers, batch_size=args.batch_size, shuffle=True, drop_last=True)\n    base_dataset = HotelTrainDataset(train_df, val_transform, data_path=DATA_FOLDER)\n    base_loader = DataLoader(base_dataset, num_workers=args.num_workers, batch_size=args.batch_size, shuffle=False)\n    val_dataset = HotelTrainDataset(val_df, val_transform, data_path=DATA_FOLDER)\n    valid_loader = DataLoader(val_dataset, num_workers=args.num_workers, batch_size=args.batch_size, shuffle=False)\n\n    print(f\"Base: {len(base_dataset)}\\nValidation: {len(val_dataset)}\")\n\n    model = EmbeddingNet(args.n_classes, args.embed_size, args.backbone_name)\n    model = model.to(args.device)\n    \n#     if torch.cuda.is_available():\n#         model.load_state_dict(torch.load(model_path))\n#     else:\n#         model.load_state_dict(torch.load(model_path,map_location=torch.device('cpu')))\n    \n\n    distance = distances.CosineSimilarity()\n\n    criterion = losses.CosFaceLoss(num_classes=args.n_classes, embedding_size=args.embed_size, embedding_regularizer = regularizers.RegularFaceRegularizer()).to(args.device) # Accuracy: 0.7200, top 5 accuracy: 0.8460\n    loss_optimizer = torch.optim.AdamW(criterion.parameters(), lr=args.lr)\n    optimizer = Lookahead(torch.optim.AdamW(model.parameters(), lr=args.lr), k=3)\n\n    scheduler = torch.optim.lr_scheduler.OneCycleLR(\n                    optimizer,\n                    max_lr=args.lr,\n                    epochs=args.epochs,\n                    steps_per_epoch=len(train_loader),\n                    div_factor=10,\n                    final_div_factor=1,\n                    pct_start=0.1,\n                    anneal_strategy=\"cos\",\n                )\n    \n    model, scheduler, optimizer, last_epoch = load_checkpoint(model, scheduler, optimizer, model_name)\n\n    base_embeds, valid_embeds, base_targets, valid_targets, val_preds, distance_matrix = test(base_loader, valid_loader, model, distance, closest=False)\n    \n    output = {\"base_embeds\": base_embeds,\n              \"valid_embeds\": valid_embeds,\n              \"base_targets\": base_targets,\n              \"valid_targets\": valid_targets,\n              \"val_preds\": val_preds,\n              \"distance_matrix\": distance_matrix,\n              \"train_df\" : train_df,\n              \"valid_df\": val_df,\n              }\n\n#     torch.save(output, f\"{OUTPUT_FOLDER}output-{model_name}.pt\")","metadata":{"execution":{"iopub.status.busy":"2022-05-25T12:47:50.214031Z","iopub.execute_input":"2022-05-25T12:47:50.214521Z","iopub.status.idle":"2022-05-25T12:47:50.231864Z","shell.execute_reply.started":"2022-05-25T12:47:50.214483Z","shell.execute_reply":"2022-05-25T12:47:50.230981Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Train and evaluate","metadata":{"id":"EMVYKwZ64zUN"}},{"cell_type":"code","source":"# %%time \n\n# class args:\n#     epochs = 9\n#     lr = 1e-3\n#     batch_size = 24\n#     num_workers = 2\n#     embed_size = 4096\n#     val_samples = 1\n#     continue_from_checkpoint = False\n#     backbone_name = \"ecaresnet50d_pruned\"\n#     n_classes = data_df[\"hotel_id_code\"].nunique()\n#     device = ('cuda' if torch.cuda.is_available() else 'cpu')\n\n    \n# # print(data_df[\"hotel_id\"].nunique())\n\n# # val_df = data_df.groupby(\"hotel_id\").sample(args.val_samples, random_state=SEED)\n# # train_df = data_df\n\n# # print(val_df[\"hotel_id\"].nunique())\n# # print(train_df[\"hotel_id\"].nunique())\n\n\n# test_and_validate(args, data_df)\n\n","metadata":{"id":"YONzJBtG2_a0","execution":{"iopub.status.busy":"2022-05-25T12:47:50.233883Z","iopub.execute_input":"2022-05-25T12:47:50.234305Z","iopub.status.idle":"2022-05-25T12:47:50.240905Z","shell.execute_reply.started":"2022-05-25T12:47:50.234264Z","shell.execute_reply":"2022-05-25T12:47:50.239951Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_distances(input, base_embeds, model):\n    distances = None\n    output = model(input)\n    output = output.detach().cpu().numpy()\n#         output = output.detach().cpu().numpy().astype(np.float16)\n    model_base_embeds = base_embeds[0]\n    output_distances = cosine_similarity(output, model_base_embeds)\n        \n    if distances is None:\n        distances = output_distances\n    else:\n        distances = distances * output_distances\n            \n    return distances\n\ndef predict(loader, base_df, base_embeds, model, n_matches=5, bar_desc=\"Generating embeds\"):\n    preds = []\n    with torch.no_grad():\n        t = tqdm(loader, desc=bar_desc)\n        for i, sample in enumerate(t):\n            input = sample['image'].to(args.device)\n            distances = get_distances(input, base_embeds, model)\n            \n            for j in range(len(distances)):\n                tmp_df = base_df.copy()\n                tmp_df[\"distance\"] = distances[j]\n                tmp_df = tmp_df.sort_values(by=[\"distance\", \"hotel_id\"], ascending=False).reset_index(drop=True)\n                preds.extend([tmp_df[\"hotel_id\"].unique()[:n_matches]])\n\n    return preds\n","metadata":{"execution":{"iopub.status.busy":"2022-05-25T12:47:50.242267Z","iopub.execute_input":"2022-05-25T12:47:50.243568Z","iopub.status.idle":"2022-05-25T12:47:50.254926Z","shell.execute_reply.started":"2022-05-25T12:47:50.243524Z","shell.execute_reply":"2022-05-25T12:47:50.254049Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def find_closest_match(args, test_loader, base_loader, model, n_matches=5):\n    base_embeds = {}\n    base_embeds[0] = get_embeds(base_loader, model, \"Generating embeds for train\")\n    \n    preds = predict(test_loader, base_loader.dataset.data, base_embeds, model, n_matches, f\"Generating predictions\")\n        \n    return preds","metadata":{"execution":{"iopub.status.busy":"2022-05-25T12:49:11.431245Z","iopub.execute_input":"2022-05-25T12:49:11.431671Z","iopub.status.idle":"2022-05-25T12:49:11.437833Z","shell.execute_reply.started":"2022-05-25T12:49:11.431639Z","shell.execute_reply":"2022-05-25T12:49:11.436648Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\nsample_submission_df = pd.read_csv(\"../input/hotel-id-to-combat-human-trafficking-2022-fgvc9/sample_submission.csv\")\ntest_df = pd.DataFrame(data={\"image_id\": os.listdir(TEST_DATA_FOLDER), \"hotel_id\": \"\"}).sort_values(by=\"image_id\")\nprint(test_df)","metadata":{"execution":{"iopub.status.busy":"2022-05-25T12:47:50.266543Z","iopub.execute_input":"2022-05-25T12:47:50.266923Z","iopub.status.idle":"2022-05-25T12:47:50.287069Z","shell.execute_reply.started":"2022-05-25T12:47:50.266886Z","shell.execute_reply":"2022-05-25T12:47:50.286056Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"base_transform = A.Compose([\n    A.ToFloat(),\n    APT.transforms.ToTensorV2(),\n])\n\ntest_tta_transforms = {\n    \"base\": A.Compose([A.ToFloat(), APT.transforms.ToTensorV2(),]),\n    \"h_flip\": A.Compose([A.ToFloat(), A.HorizontalFlip(p=1), APT.transforms.ToTensorV2(),]),\n    \"v_flip\": A.Compose([A.ToFloat(), A.VerticalFlip(p=1), APT.transforms.ToTensorV2(),]),\n    \"rotate+90\": A.Compose([A.ToFloat(), A.Rotate(limit=90, p=1), APT.transforms.ToTensorV2(),]),\n    \"rotate-90\": A.Compose([A.ToFloat(), A.Rotate(limit=-90, p=1), APT.transforms.ToTensorV2(),]),\n#     \"rand_bright\": A.Compose([A.ToFloat(), A.RandomBrightness(p=1), APT.transforms.ToTensor(),]),\n}\n\ndef pad_image(img):\n    w, h, c = np.shape(img)\n    if w > h:\n        pad = int((w - h) / 2)\n        img = cv2.copyMakeBorder(img, 0, 0, pad, pad, cv2.BORDER_CONSTANT, value=0)\n    else:\n        pad = int((h - w) / 2)\n        img = cv2.copyMakeBorder(img, pad, pad, 0, 0, cv2.BORDER_CONSTANT, value=0)\n        \n    return img\ndef open_and_preprocess_image(image_path):\n    img = cv2.imread(image_path)\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    img = pad_image(img)\n    return cv2.resize(img, (IMG_SIZE, IMG_SIZE))\n\nclass HotelImageDataset:\n    def __init__(self, data, transform=None, data_folder=\"train_images/\"):\n        self.data = data\n        self.data_folder = data_folder\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.data)\n    \n    def __getitem__(self, idx):\n        record = self.data.iloc[idx]\n\n        if self.data_folder == \"../input/hotel-id-to-combat-human-trafficking-2022-fgvc9/test_images/\":\n            image_path = self.data_folder + record[\"image_id\"]\n        else:\n            image_path = self.data_folder + record[\"image_id\"]\n        \n        if \"test\" in self.data_folder:\n            image = np.array(open_and_preprocess_image(image_path)).astype(np.uint8)\n        else:\n            image = np.array(pil_image.open(image_path)).astype(np.uint8)\n\n        if self.transform:\n            transformed = self.transform(image=image)\n        \n        return {\n            \"image\" : transformed[\"image\"],\n        }","metadata":{"execution":{"iopub.status.busy":"2022-05-25T12:49:13.841057Z","iopub.execute_input":"2022-05-25T12:49:13.841439Z","iopub.status.idle":"2022-05-25T12:49:13.857602Z","shell.execute_reply.started":"2022-05-25T12:49:13.841400Z","shell.execute_reply":"2022-05-25T12:49:13.856666Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class args:\n    batch_size = 32\n    num_workers = 4\n    n_classes = data_df[\"hotel_id\"].nunique()\n    device = ('cuda' if torch.cuda.is_available() else 'cpu')\n    backbone_name = \"ecaresnet50d_pruned\"\n    embed_size = 4096\n    lr = 1e-3\n    epochs = 9\n    n_classes = data_df[\"hotel_id_code\"].nunique()\n    \n    \n    \nseed_everything(seed=SEED)\n\nbase_dataset = HotelImageDataset(data_df, base_transform, data_folder=TRAIN_DATA_FOLDER)\nbase_loader = DataLoader(base_dataset, num_workers=args.num_workers, batch_size=args.batch_size, shuffle=False)\n\ntest_dataset = HotelImageDataset(test_df, test_tta_transforms[\"base\"], data_folder=TEST_DATA_FOLDER)\ntest_loader = DataLoader(test_dataset, num_workers=args.num_workers, batch_size=args.batch_size, shuffle=False)","metadata":{"execution":{"iopub.status.busy":"2022-05-25T12:47:50.310200Z","iopub.execute_input":"2022-05-25T12:47:50.310958Z","iopub.status.idle":"2022-05-25T12:47:50.373041Z","shell.execute_reply.started":"2022-05-25T12:47:50.310916Z","shell.execute_reply":"2022-05-25T12:47:50.372104Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model_name = f\"cosface-model-{args.backbone_name}-{IMG_SIZE}x{IMG_SIZE}-{args.embed_size}embeds-{args.n_classes}hotels\"\n\nmodel = EmbeddingNet(args.n_classes, args.embed_size, args.backbone_name)\nmodel = model.to(args.device)\noptimizer = Lookahead(torch.optim.AdamW(model.parameters(), lr=args.lr), k=3)\n\nscheduler = torch.optim.lr_scheduler.OneCycleLR(\n                    optimizer,\n                    max_lr=args.lr,\n                    epochs=args.epochs,\n                    steps_per_epoch=len(test_loader),\n                    div_factor=10,\n                    final_div_factor=1,\n                    pct_start=0.1,\n                    anneal_strategy=\"cos\",\n                )\n\nmodel, scheduler, optimizer, last_epoch = load_checkpoint(model, scheduler, optimizer, model_name)\n\nif len(test_df) > 0:\n    print(\"predicting full test set\")\n    preds = find_closest_match(args, test_loader, base_loader, model, n_matches=5)\n    test_df[\"hotel_id\"] = [str(list(l)).strip(\"[]\").replace(\",\", \"\") for l in preds]\n\ntest_df.to_csv(\"submission.csv\", index=False)\ntest_df.head()","metadata":{"execution":{"iopub.status.busy":"2022-05-25T12:48:52.692418Z","iopub.execute_input":"2022-05-25T12:48:52.692726Z","iopub.status.idle":"2022-05-25T12:48:55.562124Z","shell.execute_reply.started":"2022-05-25T12:48:52.692699Z","shell.execute_reply":"2022-05-25T12:48:55.559344Z"},"trusted":true},"execution_count":null,"outputs":[]}]}