{"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":"# Intro\n\nSimple starter notebook for [Hotel-ID to Combat Human Trafficking 2022 - FGVC9](https://www.kaggle.com/competitions/hotel-id-to-combat-human-trafficking-2022-fgvc9) competition using classification model. This notebook uses preprocessed images that were resized and padded to 256x256 pixel. \n\nUsed dataset: [Hotel-ID 2022 train images 256x256](https://www.kaggle.com/datasets/michaln/hotelid-2022-train-images-256x256) created by [Hotel-ID - image preprocessing - 256x256](https://www.kaggle.com/code/michaln/hotel-id-image-preprocessing-256x256) notebook.\n\nClassification model might not be very useful in real life application because it will not be able to handle new hotels without retraining. You can find better approaches in last year competition [Hotel-ID to Combat Human Trafficking 2021 - FGVC8](https://www.kaggle.com/competitions/hotel-id-2021-fgvc8):\n- [1st place solution: Swin + Arcface + Label-constrained DBA](https://www.kaggle.com/c/hotel-id-2021-fgvc8/discussion/242087)\n- [8th place solution: arcface + cosface + classification](https://www.kaggle.com/c/hotel-id-2021-fgvc8/discussion/242207)\n- [14th place solution - fastai ensemble](https://www.kaggle.com/c/hotel-id-2021-fgvc8/discussion/242030)","metadata":{"papermill":{"duration":0.027283,"end_time":"2022-03-24T20:48:50.477346","exception":false,"start_time":"2022-03-24T20:48:50.450063","status":"completed"},"tags":[]}},{"cell_type":"markdown","source":"# Imports","metadata":{"id":"MyC4gTwZ3MKJ","papermill":{"duration":0.025985,"end_time":"2022-03-24T20:48:50.529483","exception":false,"start_time":"2022-03-24T20:48:50.503498","status":"completed"},"tags":[]}},{"cell_type":"code","source":"!pip install timm","metadata":{"papermill":{"duration":10.171751,"end_time":"2022-03-24T20:49:00.726903","exception":false,"start_time":"2022-03-24T20:48:50.555152","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-05-28T22:49:40.178603Z","iopub.execute_input":"2022-05-28T22:49:40.178905Z","iopub.status.idle":"2022-05-28T22:49:49.466984Z","shell.execute_reply.started":"2022-05-28T22:49:40.178874Z","shell.execute_reply":"2022-05-28T22:49:49.465978Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport random\nimport os","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","id":"u0Bz2ktn2_ap","papermill":{"duration":0.035964,"end_time":"2022-03-24T20:49:00.792252","exception":false,"start_time":"2022-03-24T20:49:00.756288","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-05-28T22:49:49.470303Z","iopub.execute_input":"2022-05-28T22:49:49.470623Z","iopub.status.idle":"2022-05-28T22:49:49.47598Z","shell.execute_reply.started":"2022-05-28T22:49:49.470581Z","shell.execute_reply":"2022-05-28T22:49:49.474914Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from PIL import Image as pil_image\nfrom tqdm import tqdm\n\nimport matplotlib\nimport matplotlib.pyplot as plt\n\nimport plotly.graph_objects as go\nimport plotly.express as px\nfrom plotly.subplots import make_subplots","metadata":{"id":"tOszKuxt3PXn","papermill":{"duration":3.563044,"end_time":"2022-03-24T20:49:04.3837","exception":false,"start_time":"2022-03-24T20:49:00.820656","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-05-28T22:49:49.477745Z","iopub.execute_input":"2022-05-28T22:49:49.478051Z","iopub.status.idle":"2022-05-28T22:49:49.490866Z","shell.execute_reply.started":"2022-05-28T22:49:49.478015Z","shell.execute_reply":"2022-05-28T22:49:49.489925Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nfrom torch.utils.data import DataLoader\n\nimport timm","metadata":{"id":"uQE7wYFR3QxV","papermill":{"duration":2.299011,"end_time":"2022-03-24T20:49:06.713519","exception":false,"start_time":"2022-03-24T20:49:04.414508","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-05-28T22:49:49.493755Z","iopub.execute_input":"2022-05-28T22:49:49.494549Z","iopub.status.idle":"2022-05-28T22:49:49.501364Z","shell.execute_reply.started":"2022-05-28T22:49:49.494491Z","shell.execute_reply":"2022-05-28T22:49:49.50065Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Global","metadata":{"id":"tirOg6jm3aIB","papermill":{"duration":0.02881,"end_time":"2022-03-24T20:49:06.772367","exception":false,"start_time":"2022-03-24T20:49:06.743557","status":"completed"},"tags":[]}},{"cell_type":"code","source":"IMG_SIZE = 256\nSEED = 42\nN_MATCHES = 5\n\nPROJECT_FOLDER = \"../input/hotel-id-to-combat-human-trafficking-2022-fgvc9/\"\nDATA_FOLDER = \"../input/hotelid-2022-train-images-256x256/\"\nIMAGE_FOLDER = DATA_FOLDER + \"images/\"\nOUTPUT_FOLDER = \"\"\n\ntrain_df = pd.read_csv(os.path.join(DATA_FOLDER, 'train.csv'))","metadata":{"id":"DV7qHDuYGoJH","papermill":{"duration":0.083526,"end_time":"2022-03-24T20:49:06.884494","exception":false,"start_time":"2022-03-24T20:49:06.800968","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-05-28T22:49:49.502569Z","iopub.execute_input":"2022-05-28T22:49:49.503213Z","iopub.status.idle":"2022-05-28T22:49:49.546498Z","shell.execute_reply.started":"2022-05-28T22:49:49.503173Z","shell.execute_reply":"2022-05-28T22:49:49.545709Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(os.listdir(PROJECT_FOLDER))","metadata":{"id":"TB9CXg8U3bbQ","papermill":{"duration":0.037414,"end_time":"2022-03-24T20:49:06.951565","exception":false,"start_time":"2022-03-24T20:49:06.914151","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-05-28T22:49:49.547816Z","iopub.execute_input":"2022-05-28T22:49:49.548589Z","iopub.status.idle":"2022-05-28T22:49:49.554393Z","shell.execute_reply.started":"2022-05-28T22:49:49.548546Z","shell.execute_reply":"2022-05-28T22:49:49.553559Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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","papermill":{"duration":0.036294,"end_time":"2022-03-24T20:49:07.016733","exception":false,"start_time":"2022-03-24T20:49:06.980439","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-05-28T22:49:49.555685Z","iopub.execute_input":"2022-05-28T22:49:49.556549Z","iopub.status.idle":"2022-05-28T22:49:49.564448Z","shell.execute_reply.started":"2022-05-28T22:49:49.556508Z","shell.execute_reply":"2022-05-28T22:49:49.563638Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Dataset and transformations","metadata":{"id":"8V_xuoN73lON","papermill":{"duration":0.029047,"end_time":"2022-03-24T20:49:07.075261","exception":false,"start_time":"2022-03-24T20:49:07.046214","status":"completed"},"tags":[]}},{"cell_type":"markdown","source":"Coarse dropout with fill_value=(255,0,0) (full red channel) is used to simulate the occlussions like the one in test dataset. \n```python\nA.CoarseDropout(p=0.75, max_holes=1, \n                min_height=IMG_SIZE//4, max_height=IMG_SIZE//2,\n                min_width=IMG_SIZE//4,  max_width=IMG_SIZE//2, \n                fill_value=(255,0,0))\n```","metadata":{"papermill":{"duration":0.029334,"end_time":"2022-03-24T20:49:07.137074","exception":false,"start_time":"2022-03-24T20:49:07.10774","status":"completed"},"tags":[]}},{"cell_type":"code","source":"import albumentations as A\nimport albumentations.pytorch as APT\nimport cv2 \n\n# used for training dataset - augmentations and occlusions\ntrain_transform = A.Compose([\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, min_holes=1, max_holes=6, \n                    min_height=IMG_SIZE//16, max_height=IMG_SIZE//4,\n                    min_width=IMG_SIZE//16,  max_width=IMG_SIZE//4), # normal coarse dropout\n    \n    A.CoarseDropout(p=0.75, max_holes=1, \n                    min_height=IMG_SIZE//4, max_height=IMG_SIZE//2,\n                    min_width=IMG_SIZE//4,  max_width=IMG_SIZE//2, \n                    fill_value=(255,0,0)),# simulating occlusions in test data\n\n    A.RandomBrightnessContrast(p=0.75),\n    A.ToFloat(),\n    APT.transforms.ToTensorV2(),\n])\n\n# used for validation dataset - only occlusions\nval_transform = A.Compose([\n    A.CoarseDropout(p=0.75, max_holes=1, \n                    min_height=IMG_SIZE//4, max_height=IMG_SIZE//2,\n                    min_width=IMG_SIZE//4,  max_width=IMG_SIZE//2, \n                    fill_value=(255,0,0)),# simulating occlusions\n    A.ToFloat(),\n    APT.transforms.ToTensorV2(),\n])\n\n# no augmentations\nbase_transform = A.Compose([\n    A.ToFloat(),\n    APT.transforms.ToTensorV2(),\n])","metadata":{"id":"8ucWZHeG2_as","papermill":{"duration":0.909794,"end_time":"2022-03-24T20:49:08.07621","exception":false,"start_time":"2022-03-24T20:49:07.166416","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-05-28T22:49:49.566214Z","iopub.execute_input":"2022-05-28T22:49:49.566801Z","iopub.status.idle":"2022-05-28T22:49:49.579342Z","shell.execute_reply.started":"2022-05-28T22:49:49.566763Z","shell.execute_reply":"2022-05-28T22:49:49.578492Z"},"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\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        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        return {\n            \"image\" : image,\n            \"target\" : record['hotel_id_code'],\n        }","metadata":{"id":"EiLYsfKq2_at","papermill":{"duration":0.038645,"end_time":"2022-03-24T20:49:08.144005","exception":false,"start_time":"2022-03-24T20:49:08.10536","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-05-28T22:49:49.582504Z","iopub.execute_input":"2022-05-28T22:49:49.582933Z","iopub.status.idle":"2022-05-28T22:49:49.594928Z","shell.execute_reply.started":"2022-05-28T22:49:49.582896Z","shell.execute_reply":"2022-05-28T22:49:49.592862Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model","metadata":{"id":"FpR2HfK93pvS","papermill":{"duration":0.028518,"end_time":"2022-03-24T20:49:08.201461","exception":false,"start_time":"2022-03-24T20:49:08.172943","status":"completed"},"tags":[]}},{"cell_type":"code","source":"class HotelIdModel(nn.Module):\n    def __init__(self, n_classes=100, backbone_name=\"efficientnet_b4\"):\n        super(HotelIdModel, self).__init__()\n        \n        self.backbone = timm.create_model(backbone_name, num_classes=n_classes, pretrained=True)\n\n    def forward(self, x):\n        return self.backbone(x)","metadata":{"id":"_2mse3zX3pFQ","papermill":{"duration":0.036511,"end_time":"2022-03-24T20:49:08.266876","exception":false,"start_time":"2022-03-24T20:49:08.230365","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-05-28T22:49:49.602795Z","iopub.execute_input":"2022-05-28T22:49:49.612963Z","iopub.status.idle":"2022-05-28T22:49:49.620118Z","shell.execute_reply.started":"2022-05-28T22:49:49.612904Z","shell.execute_reply":"2022-05-28T22:49:49.619374Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model helper functions","metadata":{"id":"mTFCinps35ci","papermill":{"duration":0.029013,"end_time":"2022-03-24T20:49:08.324634","exception":false,"start_time":"2022-03-24T20:49:08.295621","status":"completed"},"tags":[]}},{"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    checkpoint = torch.load(f\"{OUTPUT_FOLDER}checkpoint-{name}.pt\")\n\n    model.load_state_dict(checkpoint[\"model\"])\n    scheduler.load_state_dict(checkpoint[\"scheduler\"])\n    return model, scheduler, optimizer, checkpoint[\"epoch\"]","metadata":{"id":"ryZ6wE0zKPiz","papermill":{"duration":0.038745,"end_time":"2022-03-24T20:49:08.392388","exception":false,"start_time":"2022-03-24T20:49:08.353643","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-05-28T22:49:49.621951Z","iopub.execute_input":"2022-05-28T22:49:49.62261Z","iopub.status.idle":"2022-05-28T22:49:49.639565Z","shell.execute_reply.started":"2022-05-28T22:49:49.62257Z","shell.execute_reply":"2022-05-28T22:49:49.638531Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Train functions","metadata":{"papermill":{"duration":0.028868,"end_time":"2022-03-24T20:49:08.450051","exception":false,"start_time":"2022-03-24T20:49:08.421183","status":"completed"},"tags":[]}},{"cell_type":"code","source":"def train_epoch(args, model, loader, criterion, optimizer, scheduler, epoch, batch_multiplier=4):\n    losses = []\n    targets_all = []\n    outputs_all = []\n    \n    model.train()\n    t = tqdm(loader)\n    \n    count = 0\n    for i, sample in enumerate(t):\n        if count <= 0:\n            optimizer.step()\n            optimizer.zero_grad()\n            count = batch_multiplier\n\n        #optimizer.zero_grad()\n        \n        images = sample['image'].to(args.device)\n        targets = sample['target'].to(args.device)\n        \n        outputs = model(images)\n        loss = criterion(outputs, targets) / batch_multiplier\n        #print(outputs[:3], targets[:3])\n        \n        loss.backward()\n        #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}, accuracy: {score:0.4f}\"\n        t.set_description(desc)\n        \n        count -= 1\n        \n    return np.mean(losses), score\n\n\ndef test(loader, model):\n    targets_all = []\n    outputs_all = []\n    \n    model.eval()\n    t = tqdm(loader)\n    \n    for i, sample in enumerate(t):\n        images = sample['image'].to(args.device)\n        targets = sample['target'].to(args.device)\n        \n        outputs = model(images)\n        \n        targets_all.extend(targets.cpu().numpy())\n        outputs_all.extend(torch.sigmoid(outputs).detach().cpu().numpy())\n        \n    \n    # repeat targets to N_MATCHES for easy calculation of MAP@5\n    y = np.repeat([targets_all], repeats=N_MATCHES, axis=0).T\n    # sort predictions and get top 5\n    preds = np.argsort(-np.array(outputs_all), axis=1)[:, :5]\n    # check if any of top 5 predictions are correct and calculate mean accuracy\n    acc_top_5 = (preds == y).any(axis=1).mean()\n    # calculate prediction accuracy\n    acc_top_1 = np.mean(targets_all == np.argmax(outputs_all, axis=1))\n\n    print(f\"Accuracy: {acc_top_1:0.4f}, MAP@5: {acc_top_5:0.4f}\")","metadata":{"id":"SntLH82s2_au","papermill":{"duration":0.043635,"end_time":"2022-03-24T20:49:08.522498","exception":false,"start_time":"2022-03-24T20:49:08.478863","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-05-28T22:49:49.641764Z","iopub.execute_input":"2022-05-28T22:49:49.642349Z","iopub.status.idle":"2022-05-28T22:49:49.672573Z","shell.execute_reply.started":"2022-05-28T22:49:49.642309Z","shell.execute_reply":"2022-05-28T22:49:49.670224Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Prepare data","metadata":{"id":"F2xgmwBW4LjC","papermill":{"duration":0.028772,"end_time":"2022-03-24T20:49:08.582012","exception":false,"start_time":"2022-03-24T20:49:08.55324","status":"completed"},"tags":[]}},{"cell_type":"code","source":"data_df = pd.read_csv(DATA_FOLDER + \"train.csv\")\n# encode hotel ids\ndata_df[\"hotel_id_code\"] = data_df[\"hotel_id\"].astype('category').cat.codes.values.astype(np.int64)","metadata":{"id":"Sn6HrWKQ2_aw","papermill":{"duration":0.075972,"end_time":"2022-03-24T20:49:08.687047","exception":false,"start_time":"2022-03-24T20:49:08.611075","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-05-28T22:49:49.679399Z","iopub.execute_input":"2022-05-28T22:49:49.680278Z","iopub.status.idle":"2022-05-28T22:49:49.751398Z","shell.execute_reply.started":"2022-05-28T22:49:49.679999Z","shell.execute_reply":"2022-05-28T22:49:49.750307Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# save hotel_id encoding for later decoding\nhotel_id_code_df = data_df.drop(columns=[\"image_id\"]).drop_duplicates().reset_index(drop=True)\nhotel_id_code_df.to_csv(OUTPUT_FOLDER + 'hotel_id_code_mapping.csv', index=False)\n# hotel_id_code_map = hotel_id_code_df.set_index('hotel_id_code').to_dict()[\"hotel_id\"]","metadata":{"papermill":{"duration":0.048938,"end_time":"2022-03-24T20:49:08.765179","exception":false,"start_time":"2022-03-24T20:49:08.716241","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-05-28T22:49:49.753478Z","iopub.execute_input":"2022-05-28T22:49:49.754112Z","iopub.status.idle":"2022-05-28T22:49:49.78445Z","shell.execute_reply.started":"2022-05-28T22:49:49.754068Z","shell.execute_reply":"2022-05-28T22:49:49.782675Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Quick look at data\n\nWe can see that most hotels have less than 20 images while there are few with over hundreds and one with over 1000. In case of classification weights or sampling might be useful to handle the imbalance.","metadata":{"papermill":{"duration":0.028959,"end_time":"2022-03-24T20:49:08.823337","exception":false,"start_time":"2022-03-24T20:49:08.794378","status":"completed"},"tags":[]}},{"cell_type":"markdown","source":"## Image count per hotel","metadata":{"papermill":{"duration":0.028707,"end_time":"2022-03-24T20:49:08.880948","exception":false,"start_time":"2022-03-24T20:49:08.852241","status":"completed"},"tags":[]}},{"cell_type":"code","source":"group_df = data_df.groupby([\"hotel_id\"]).size().to_frame(\"image_count\").sort_values(\"image_count\")[::-1].reset_index()\n\n# top and low\nlow_df = group_df.iloc[-50:]\ntop_df = group_df.iloc[:50]\n\nfig = make_subplots(rows=2, cols=2, \n                    specs=[[{\"colspan\": 2}, None], [{}, {}]],\n                    horizontal_spacing=0.02, vertical_spacing=0.2, \n                    shared_yaxes=False,\n                    subplot_titles=(\"\", \"Top 50\", \"Bottom 50\"))\n\n\nfig.add_trace(go.Scatter(x=group_df[\"hotel_id\"], y=group_df[\"image_count\"], showlegend = False), 1, 1)\nfig.add_trace(go.Bar(x=top_df[\"hotel_id\"], y=top_df[\"image_count\"], showlegend = False), 2, 1)\nfig.add_trace(go.Bar(x=low_df[\"hotel_id\"], y=low_df[\"image_count\"], showlegend = False), 2, 2)\n\nfig.update_yaxes(title_text=\"Image count\", row=1, col=1)\nfig.update_yaxes(title_text=\"Image count\", row=2, col=1)\nfig.update_xaxes(type=\"category\", visible=False, row=1, col=1)\nfig.update_xaxes(title_text=\"Hotel ID\", type=\"category\", row=2, col=1)\nfig.update_xaxes(title_text=\"Hotel ID\", type=\"category\", row=2, col=2)\n\nfig.update_layout(title=\"Image count per hotel\", height=550)\nfig.show()","metadata":{"_kg_hide-input":true,"papermill":{"duration":0.300498,"end_time":"2022-03-24T20:49:09.210419","exception":false,"start_time":"2022-03-24T20:49:08.909921","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-05-28T22:49:49.791243Z","iopub.execute_input":"2022-05-28T22:49:49.791551Z","iopub.status.idle":"2022-05-28T22:49:49.934173Z","shell.execute_reply.started":"2022-05-28T22:49:49.791515Z","shell.execute_reply":"2022-05-28T22:49:49.933295Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig = px.histogram(group_df, x=\"image_count\", nbins=100, marginal=\"box\", height=350)\nfig.update_layout(title=\"Distribution of image count per hotel\")\nfig.update_traces(hovertemplate=\"Image count: %{x} <br>Hotel count: %{y}\")\nfig.show()","metadata":{"_kg_hide-input":true,"papermill":{"duration":0.936723,"end_time":"2022-03-24T20:49:10.17804","exception":false,"start_time":"2022-03-24T20:49:09.241317","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-05-28T22:49:49.938732Z","iopub.execute_input":"2022-05-28T22:49:49.939018Z","iopub.status.idle":"2022-05-28T22:49:50.109709Z","shell.execute_reply.started":"2022-05-28T22:49:49.938983Z","shell.execute_reply":"2022-05-28T22:49:50.108529Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Example of images","metadata":{"papermill":{"duration":0.030976,"end_time":"2022-03-24T20:49:10.240398","exception":false,"start_time":"2022-03-24T20:49:10.209422","status":"completed"},"tags":[]}},{"cell_type":"code","source":"def show_images(ds, title_text, n_images=5):\n    fig, ax = plt.subplots(1,5, figsize=(22,8))\n    \n    ax[0].set_ylabel(title_text)\n    \n    for i in range(5):\n        d = ds.__getitem__(i)\n        ax[i].imshow(d[\"image\"].T)","metadata":{"papermill":{"duration":0.038456,"end_time":"2022-03-24T20:49:10.310479","exception":false,"start_time":"2022-03-24T20:49:10.272023","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-05-28T22:49:50.114736Z","iopub.execute_input":"2022-05-28T22:49:50.115076Z","iopub.status.idle":"2022-05-28T22:49:50.126105Z","shell.execute_reply.started":"2022-05-28T22:49:50.115009Z","shell.execute_reply":"2022-05-28T22:49:50.125047Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Train images without and with augmentations","metadata":{"papermill":{"duration":0.030859,"end_time":"2022-03-24T20:49:10.372747","exception":false,"start_time":"2022-03-24T20:49:10.341888","status":"completed"},"tags":[]}},{"cell_type":"code","source":"train_dataset = HotelTrainDataset(data_df, base_transform, data_path=IMAGE_FOLDER)\nshow_images(train_dataset, 'No augmentations')\n\ntrain_dataset = HotelTrainDataset(data_df, train_transform, data_path=IMAGE_FOLDER)\nshow_images(train_dataset, 'Train augmentations')","metadata":{"papermill":{"duration":1.611251,"end_time":"2022-03-24T20:49:12.015268","exception":false,"start_time":"2022-03-24T20:49:10.404017","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-05-28T22:49:50.131866Z","iopub.execute_input":"2022-05-28T22:49:50.132174Z","iopub.status.idle":"2022-05-28T22:49:51.834034Z","shell.execute_reply.started":"2022-05-28T22:49:50.132138Z","shell.execute_reply":"2022-05-28T22:49:51.833322Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Test image example with occlusion","metadata":{"papermill":{"duration":0.05238,"end_time":"2022-03-24T20:49:12.118534","exception":false,"start_time":"2022-03-24T20:49:12.066154","status":"completed"},"tags":[]}},{"cell_type":"code","source":"test_image = np.array(pil_image.open('../input/hotel-id-to-combat-human-trafficking-2022-fgvc9/test_images/abc.jpg')).astype(np.uint8)\nplt.figure(figsize=(6,6))\nplt.imshow(test_image)","metadata":{"papermill":{"duration":0.399487,"end_time":"2022-03-24T20:49:12.566935","exception":false,"start_time":"2022-03-24T20:49:12.167448","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-05-28T22:49:51.83546Z","iopub.execute_input":"2022-05-28T22:49:51.835858Z","iopub.status.idle":"2022-05-28T22:49:52.216312Z","shell.execute_reply.started":"2022-05-28T22:49:51.835821Z","shell.execute_reply":"2022-05-28T22:49:52.215547Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Train and evaluate","metadata":{"id":"EMVYKwZ64zUN","papermill":{"duration":0.051194,"end_time":"2022-03-24T20:49:12.668015","exception":false,"start_time":"2022-03-24T20:49:12.616821","status":"completed"},"tags":[]}},{"cell_type":"code","source":"def train_and_validate(args, model, data_df):\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    train_dataset = HotelTrainDataset(train_df, train_transform, data_path=IMAGE_FOLDER)\n    train_loader = DataLoader(train_dataset, num_workers=args.num_workers, batch_size=args.batch_size, shuffle=True, drop_last=True)\n    val_dataset = HotelTrainDataset(val_df, val_transform, data_path=IMAGE_FOLDER)\n    valid_loader = DataLoader(val_dataset, num_workers=args.num_workers, batch_size=args.batch_size, shuffle=False)\n\n    criterion = nn.CrossEntropyLoss()\n    optimizer = torch.optim.AdamW(model.parameters(), lr=args.lr)\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    for epoch in range(start_epoch, args.epochs+1):\n        train_loss, train_score = train_epoch(args, model, train_loader, criterion, optimizer, scheduler, epoch, batch_multiplier=args.batch_multiplier)\n        save_checkpoint(model, scheduler, optimizer, epoch, model_name, train_loss, train_score)\n        test(valid_loader, model)","metadata":{"papermill":{"duration":0.062332,"end_time":"2022-03-24T20:49:12.779289","exception":false,"start_time":"2022-03-24T20:49:12.716957","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-05-28T22:49:52.217583Z","iopub.execute_input":"2022-05-28T22:49:52.218405Z","iopub.status.idle":"2022-05-28T22:49:52.23037Z","shell.execute_reply.started":"2022-05-28T22:49:52.218366Z","shell.execute_reply":"2022-05-28T22:49:52.229557Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Efficientnet-b0 training","metadata":{"papermill":{"duration":0.049188,"end_time":"2022-03-24T20:49:12.877115","exception":false,"start_time":"2022-03-24T20:49:12.827927","status":"completed"},"tags":[]}},{"cell_type":"markdown","source":"## %%time \n\nclass args:\n    epochs = 5\n    lr = 1e-3\n    batch_size = 7\n    num_workers = 2\n    val_samples = 1\n    backbone_name = \"convnext_tiny\"\n    n_classes = data_df[\"hotel_id_code\"].nunique()\n    device = ('cuda' if torch.cuda.is_available() else 'cpu')\n\ntrain_and_validate(args, data_df)","metadata":{"id":"YONzJBtG2_a0","papermill":{"duration":1776.621084,"end_time":"2022-03-24T21:18:49.546722","exception":false,"start_time":"2022-03-24T20:49:12.925638","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-05-09T18:33:06.582629Z","iopub.execute_input":"2022-05-09T18:33:06.583196Z","iopub.status.idle":"2022-05-09T18:33:48.018731Z","shell.execute_reply.started":"2022-05-09T18:33:06.583155Z","shell.execute_reply":"2022-05-09T18:33:48.017918Z"}}},{"cell_type":"code","source":"class args:\n    epochs = 30\n    lr = 1e-3\n    batch_size = 32\n    \n    \n    num_workers = 2\n    val_samples = 1\n    batch_multiplier = 4\n    backbone_name = \"regnety_080\"\n    \n    n_classes = data_df[\"hotel_id_code\"].nunique()\n    device = ('cuda' if torch.cuda.is_available() else 'cpu')","metadata":{"execution":{"iopub.status.busy":"2022-05-28T22:49:52.2333Z","iopub.execute_input":"2022-05-28T22:49:52.233509Z","iopub.status.idle":"2022-05-28T22:49:52.24616Z","shell.execute_reply.started":"2022-05-28T22:49:52.233479Z","shell.execute_reply":"2022-05-28T22:49:52.245364Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model_name = f\"classification-model-{args.backbone_name}-{IMG_SIZE}x{IMG_SIZE}\"\nprint(model_name)\nmodel = HotelIdModel(args.n_classes, args.backbone_name)\nmodel = model.to(args.device)","metadata":{"execution":{"iopub.status.busy":"2022-05-28T22:49:52.247552Z","iopub.execute_input":"2022-05-28T22:49:52.248213Z","iopub.status.idle":"2022-05-28T22:49:53.654209Z","shell.execute_reply.started":"2022-05-28T22:49:52.248171Z","shell.execute_reply":"2022-05-28T22:49:53.65344Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%time\n\ntrain_and_validate(args, model, data_df)\n# Final - Accuracy: 0.4339, MAP@5: 0.6098","metadata":{"execution":{"iopub.status.busy":"2022-05-28T22:49:53.655757Z","iopub.execute_input":"2022-05-28T22:49:53.656002Z","iopub.status.idle":"2022-05-29T05:29:40.337122Z","shell.execute_reply.started":"2022-05-28T22:49:53.655967Z","shell.execute_reply":"2022-05-29T05:29:40.335412Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}