{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import os\n\nimport numpy as np\nimport pandas as pd\nfrom matplotlib import pyplot as plt\n\nimport cv2\nimport torch\nfrom tqdm import tqdm_notebook\nfrom PIL import Image\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import models\nfrom sklearn.model_selection import train_test_split\n\n%matplotlib inline","execution_count":1,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def kaggle_commit_logger(str_to_log, need_print = True):\n    if need_print:\n        print(str_to_log)\n    os.system('echo ' + str_to_log)","execution_count":2,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"SEED = 42","execution_count":3,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"IMG_SIZE = 256","execution_count":4,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"torch.manual_seed(SEED)\nnp.random.seed(0)","execution_count":5,"outputs":[]},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true},"cell_type":"code","source":"KAGGLE_INPUT_DIR = '/kaggle/input/'\n\nIMET_DIRNAME = 'imet-2019-fgvc6'\nPYTORCH_PRETRAIED_WEIGHTS_DIRNAME = 'pretrained-pytorch-models'\n\nTEST_DIR = 'test'\nTRAIN_DIR = 'train'\n\nSAMPLE_SUBMISSION_FILENAME = 'sample_submission.csv'\nTRAIN_ANS_FILENAME = 'train.csv'\n\nIMG_EXTENSION = '.png'","execution_count":6,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"TRAIN_IMGS_DIR = os.path.join(KAGGLE_INPUT_DIR, IMET_DIRNAME, TRAIN_DIR)\nTEST_IMGS_DIR = os.path.join(KAGGLE_INPUT_DIR, IMET_DIRNAME, TEST_DIR)","execution_count":7,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"ID_COLNAME = 'id'\nANSWER_COLNAME = 'attribute_ids'","execution_count":8,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"os.environ['TORCH_MODEL_ZOO'] = os.path.join(KAGGLE_INPUT_DIR, PYTORCH_PRETRAIED_WEIGHTS_DIRNAME)","execution_count":9,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sample_sumbission_filepath = os.path.join(KAGGLE_INPUT_DIR,\n                                          IMET_DIRNAME, SAMPLE_SUBMISSION_FILENAME)\ntrain_ans_filepath = os.path.join(KAGGLE_INPUT_DIR, IMET_DIRNAME, TRAIN_ANS_FILENAME)","execution_count":10,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"TRAIN_ANS_DF = pd.read_csv(train_ans_filepath)","execution_count":11,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"SAMPLE_SUBMISSION_DF = pd.read_csv(sample_sumbission_filepath)","execution_count":12,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"flatten = lambda l: [item for sublist in l for item in sublist]\n\ndef get_flatten_labels(series: pd.Series):\n    flatten_vals = flatten(series.apply(lambda x: x.split(' ')))\n    return pd.Series(flatten_vals).value_counts()","execution_count":13,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_labels_vc = get_flatten_labels(TRAIN_ANS_DF[ANSWER_COLNAME])","execution_count":14,"outputs":[]},{"metadata":{"_kg_hide-output":false,"trusted":true},"cell_type":"code","source":"plt.plot(train_labels_vc[:100]);\nplt.xticks([0, 50, 99]);","execution_count":15,"outputs":[{"output_type":"display_data","data":{"text/plain":"<Figure size 432x288 with 1 Axes>","image/png":"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\n"},"metadata":{}}]},{"metadata":{"trusted":true},"cell_type":"code","source":"N_PICS_TH_TO_USE = 250\n\nCLASSES_TO_USE = list(train_labels_vc[train_labels_vc > N_PICS_TH_TO_USE].index)\nNUM_FIXED_CLASSES = len(CLASSES_TO_USE)\nprint(NUM_FIXED_CLASSES)","execution_count":16,"outputs":[{"output_type":"stream","text":"208\n","name":"stdout"}]},{"metadata":{"trusted":true},"cell_type":"code","source":"OLD_NEW_CLASSMAP = dict(\n    [(i, j) for i, j\n     in zip(CLASSES_TO_USE, range(NUM_FIXED_CLASSES))\n    ]\n)","execution_count":17,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"REVERSE_CLASSMAP = dict([(v, k) for k, v in OLD_NEW_CLASSMAP.items()])","execution_count":18,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def delete_rare_labels(base_labels_str: str, classes_to_use = CLASSES_TO_USE):\n    set_of_classes = set(base_labels_str.split(' '))\n    return_list = list(set_of_classes.intersection(set(classes_to_use)))\n    return ' '.join(\n        [str(i)\n         for i in\n         sorted([int(i) for i in return_list])\n        ]\n    )","execution_count":19,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"FIXED_ANSWER_COLNAME = 'fixed_attribute_ids'\n\nTRAIN_ANS_DF[FIXED_ANSWER_COLNAME] = TRAIN_ANS_DF[ANSWER_COLNAME].map(delete_rare_labels)","execution_count":20,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = models.resnet18(pretrained='imagenet')","execution_count":21,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"new_head = torch.nn.Linear(model.fc.in_features, NUM_FIXED_CLASSES)\nmodel.fc = new_head","execution_count":22,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.cuda();","execution_count":23,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"TRAIN_ANS_DF = TRAIN_ANS_DF[TRAIN_ANS_DF[FIXED_ANSWER_COLNAME] != '']","execution_count":24,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"TEST_SIZE = 0.15\n\ntrain_df, test_df = train_test_split(TRAIN_ANS_DF[[ID_COLNAME, FIXED_ANSWER_COLNAME]],\n                                     test_size = TEST_SIZE,\n                                     random_state = SEED,\n                                     shuffle = True\n                                    )","execution_count":25,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from torchvision import transforms","execution_count":26,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"normalizer = transforms.Normalize(mean=[0.485, 0.456, 0.406],\n                                 std=[0.229, 0.224, 0.225])\n\ntrain_augmentation = transforms.Compose([\n    transforms.Resize((IMG_SIZE,IMG_SIZE)),\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomRotation(13),\n    transforms.ToTensor(),\n    normalizer,\n])\n\nval_augmentation = transforms.Compose([\n    transforms.Resize((IMG_SIZE,IMG_SIZE)),\n    transforms.ToTensor(),\n    normalizer,\n])","execution_count":27,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class IMetDataset(Dataset):\n    \n    def __init__(self,\n                 df,\n                 images_dir,\n                 n_classes = NUM_FIXED_CLASSES,\n                 id_colname = ID_COLNAME,\n                 answer_colname = FIXED_ANSWER_COLNAME,\n                 img_ext = IMG_EXTENSION,\n                 label_to_network_id_dict = OLD_NEW_CLASSMAP,\n                 transforms = None\n                ):\n        self.df = df\n        self.images_dir = images_dir\n        self.n_classes = n_classes\n        self.id_colname = id_colname\n        self.answer_colname = answer_colname\n        self.img_ext = img_ext\n        self.label_to_network_id_dict = label_to_network_id_dict\n        self.transforms = transforms\n    \n    def __len__(self):\n        return self.df.shape[0]\n    \n    def __getitem__(self, idx):\n        cur_idx_row = self.df.iloc[idx]\n        img_id = cur_idx_row[self.id_colname]\n        img_name = img_id + self.img_ext\n        img_path = os.path.join(self.images_dir, img_name)\n        \n        img = cv2.imread(img_path)\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n        img = Image.fromarray(img)\n        \n        if self.transforms is not None:\n            img = self.transforms(img)\n        \n        if self.answer_colname is not None:\n            labels_list = cur_idx_row[self.answer_colname].split(' ')\n            updated_ids = [self.label_to_network_id_dict[cur_ans]\n                           for cur_ans in labels_list]\n\n            FILLVAL = 1.0\n            label = torch.zeros((self.n_classes,), dtype=torch.float32)\n\n            for cur_idx_to_repair in updated_ids:\n                label[cur_idx_to_repair] = FILLVAL\n\n            return img, label\n        \n        else:\n            return img, img_id","execution_count":28,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_dataset = IMetDataset(train_df, TRAIN_IMGS_DIR, transforms = train_augmentation)\ntest_dataset = IMetDataset(test_df, TRAIN_IMGS_DIR, transforms = val_augmentation)","execution_count":29,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"BS = 24\n\n\ntrain_loader = DataLoader(train_dataset, batch_size=BS, shuffle=True, num_workers=2, pin_memory=True)\ntest_loader = DataLoader(test_dataset, batch_size=BS, shuffle=False, num_workers=2, pin_memory=True)\n# train_loader = DataLoader(train_dataset, batch_size=BS, shuffle=True, pin_memory=True)\n# test_loader = DataLoader(test_dataset, batch_size=BS, shuffle=False, pin_memory=True)","execution_count":30,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#plt.figure(figsize=(15,8));\n#plt.imshow(train_dataset[92851 - 322][0]);","execution_count":31,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def cuda(x):\n    return x.cuda(non_blocking=True)","execution_count":32,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# from https://www.kaggle.com/igormq/f-beta-score-for-pytorch\n\ndef f2_score(y_true, y_pred, threshold=0.5):\n    return fbeta_score(y_true, y_pred, 2, threshold)\n\n\ndef fbeta_score(y_true, y_pred, beta, threshold, eps=1e-9):\n    beta2 = beta**2\n\n    y_pred = torch.ge(y_pred.float(), threshold).float()\n    y_true = y_true.float()\n\n    true_positive = (y_pred * y_true).sum(dim=1)\n    precision = true_positive.div(y_pred.sum(dim=1).add(eps))\n    recall = true_positive.div(y_true.sum(dim=1).add(eps))\n\n    return torch.mean(\n        (precision*recall).\n        div(precision.mul(beta2) + recall + eps).\n        mul(1 + beta2))","execution_count":33,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def train_one_epoch(model, train_loader, criterion, optimizer, steps_upd_logging = 250):\n    model.train();\n    \n    total_loss = 0.0\n    \n    train_tqdm = tqdm_notebook(train_loader)\n    \n    for step, (features, targets) in enumerate(train_tqdm):\n        features, targets = cuda(features), cuda(targets)\n        \n        optimizer.zero_grad()\n        \n        logits = model(features)\n        \n        loss = criterion(logits, targets)\n        loss.backward()\n        optimizer.step()\n        \n        total_loss += loss.item()\n        \n        if (step + 1) % steps_upd_logging == 0:\n            logstr = f'Train loss on step {step + 1} was {round(total_loss / (step + 1), 5)}'\n            train_tqdm.set_description(logstr)\n            kaggle_commit_logger(logstr, need_print=False)\n        \n    return total_loss / (step + 1)","execution_count":34,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def validate(model, valid_loader, criterion, need_tqdm = False):\n    model.eval();\n    \n    test_loss = 0.0\n    TH_TO_ACC = 0.5\n    \n    true_ans_list = []\n    preds_cat = []\n    \n    with torch.no_grad():\n        \n        if need_tqdm:\n            valid_iterator = tqdm_notebook(valid_loader)\n        else:\n            valid_iterator = valid_loader\n        \n        for step, (features, targets) in enumerate(valid_iterator):\n            features, targets = cuda(features), cuda(targets)\n\n            logits = model(features)\n            loss = criterion(logits, targets)\n\n            test_loss += loss.item()\n            true_ans_list.append(targets)\n            preds_cat.append(torch.sigmoid(logits))\n\n        all_true_ans = torch.cat(true_ans_list)\n        all_preds = torch.cat(preds_cat)\n        \n        f2_eval = f2_score(all_true_ans, all_preds).item()\n\n    logstr = f'Mean val f2: {round(f2_eval, 5)}'\n    kaggle_commit_logger(logstr)\n    return test_loss / (step + 1), f2_eval","execution_count":35,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"criterion = torch.nn.BCEWithLogitsLoss()\n# TODO: LRFinder and OneCycle implementation\noptimizer = torch.optim.Adam(model.parameters(), lr=0.002)\nsheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, factor=0.5, patience=3)","execution_count":36,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"N_EPOCHS = 20\nTRAIN_LOGGING_EACH = 500\n\ntrain_losses = []\nvalid_losses = []\nvalid_f2s = []\n\nbest_model_f2 = 0.0\nbest_model = None\nbest_model_ep = 0\n\nfor epoch in range(1, N_EPOCHS + 1):\n    ep_logstr = f\"Starting {epoch} epoch...\"\n    kaggle_commit_logger(ep_logstr)\n    tr_loss = train_one_epoch(model, train_loader, criterion, optimizer, TRAIN_LOGGING_EACH)\n    train_losses.append(tr_loss)\n    tr_loss_logstr = f'Mean train loss: {round(tr_loss,5)}'\n    kaggle_commit_logger(tr_loss_logstr)\n    \n    valid_loss, valid_f2 = validate(model, test_loader, criterion)\n    valid_losses.append(valid_loss)\n    valid_f2s.append(valid_f2)\n    val_loss_logstr = f'Mean valid loss: {round(valid_loss,5)}'\n    kaggle_commit_logger(val_loss_logstr)\n    sheduler.step(valid_loss)\n    \n    if valid_f2 >= best_model_f2:\n        best_model = model\n        best_model_f2 = valid_f2\n        best_model_ep = epoch","execution_count":37,"outputs":[{"output_type":"stream","text":"Starting 1 epoch...\n","name":"stdout"},{"output_type":"display_data","data":{"text/plain":"HBox(children=(IntProgress(value=0, max=3814), HTML(value='')))","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"0b933a131ae24a18aa9eb736f05e5b24"}},"metadata":{}},{"output_type":"stream","text":"\nMean train loss: 0.05437\nMean val f2: 0.06627\nMean valid loss: 0.05005\nStarting 2 epoch...\n","name":"stdout"},{"output_type":"display_data","data":{"text/plain":"HBox(children=(IntProgress(value=0, max=3814), HTML(value='')))","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"25de47719aae4ed4af5ba72ad71a1249"}},"metadata":{}},{"output_type":"stream","text":"\nMean train loss: 0.04709\nMean val f2: 0.1648\nMean valid loss: 0.04496\n","name":"stdout"}]},{"metadata":{"trusted":true},"cell_type":"code","source":"bestmodel_logstr = f'Best f2 is {round(best_model_f2, 5)} on epoch {best_model_ep}'\nkaggle_commit_logger(bestmodel_logstr)","execution_count":38,"outputs":[{"output_type":"stream","text":"Best f2 is 0.1648 on epoch 2\n","name":"stdout"}]},{"metadata":{"trusted":true},"cell_type":"code","source":"# import gc\n# del  train_loader, test_loader, train_dataset, test_dataset\n# del train_df, test_df\n# gc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"xs = list(range(1, len(train_losses) + 1))\n\nplt.plot(xs, train_losses, label = 'Train loss');\nplt.plot(xs, valid_losses, label = 'Val loss');\nplt.plot(xs, valid_f2s, label = 'Val f2');\nplt.legend();\nplt.xticks(xs);\nplt.xlabel('Epochs');","execution_count":39,"outputs":[{"output_type":"display_data","data":{"text/plain":"<Figure size 432x288 with 1 Axes>","image/png":"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\n"},"metadata":{}}]},{"metadata":{"trusted":true},"cell_type":"code","source":"subm_dataset = IMetDataset(SAMPLE_SUBMISSION_DF,\n                           TEST_IMGS_DIR,\n                           transforms = val_augmentation,\n                           answer_colname=None\n                          )","execution_count":40,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"SUMB_BS = 48\n\nsubm_dataloader = DataLoader(subm_dataset,\n                             batch_size=SUMB_BS,\n                             shuffle=False,\n                             pin_memory=True)","execution_count":41,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_subm_answers(model, subm_dataloader, TH = 0.5, need_tqdm = False):\n    model.eval();\n    preds_cat = []\n    ids = []\n    \n    with torch.no_grad():\n        \n        if need_tqdm:\n            subm_iterator = tqdm_notebook(subm_dataloader)\n        else:\n            subm_iterator = subm_dataloader\n        \n        for step, (features, subm_ids) in enumerate(subm_iterator):\n            features = cuda(features)\n\n            logits = model(features)\n            preds_cat.append(torch.sigmoid(logits))\n            ids += subm_ids\n\n        all_preds = torch.cat(preds_cat)\n        all_preds = torch.ge(all_preds, TH).int().cpu().numpy()\n    return all_preds, ids","execution_count":42,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"best_model.cuda();\n\nsubm_preds, submids = get_subm_answers(best_model, subm_dataloader, 0.5, True)","execution_count":43,"outputs":[{"output_type":"display_data","data":{"text/plain":"HBox(children=(IntProgress(value=0, max=156), HTML(value='')))","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"4e92c719d44a409095fd0aea30c2e402"}},"metadata":{}},{"output_type":"stream","text":"\n","name":"stdout"}]},{"metadata":{"trusted":true},"cell_type":"code","source":"ans_dict = dict([cur_id, ''] for cur_id in submids)\n\nnonzero_idx, nonzero_classes = np.nonzero(subm_preds)\n\nfor cur_id, cur_class in zip(np.array(submids)[nonzero_idx],\n                            nonzero_classes):\n    ans_dict[cur_id] += str(cur_class) + ' '\n","execution_count":44,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_to_process = (\n    pd.DataFrame\n    .from_dict(ans_dict, orient='index', columns=[ANSWER_COLNAME])\n    .reset_index()\n    .rename({'index':ID_COLNAME}, axis=1)\n)","execution_count":45,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def reverse_class_to_submission(old_class, rev_classmap=REVERSE_CLASSMAP):\n    return REVERSE_CLASSMAP[int(old_class)]","execution_count":46,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def process_one_id(id_classes_str):\n    if id_classes_str:\n        parsed_ids = id_classes_str.strip().split(' ')\n        new_ids = [reverse_class_to_submission(i) for i in parsed_ids]\n        ret_val = ' '.join(new_ids)\n        return ret_val\n    else:\n        return id_classes_str","execution_count":47,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_to_process['attribute_ids'] = df_to_process['attribute_ids'].apply(process_one_id)","execution_count":48,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_to_process['attribute_ids'].head(10)","execution_count":49,"outputs":[{"output_type":"execute_result","execution_count":49,"data":{"text/plain":"0                   \n1                   \n2                   \n3                   \n4                   \n5    1092 13 896 405\n6       813 1092 194\n7           813 1092\n8                   \n9                655\nName: attribute_ids, dtype: object"},"metadata":{}}]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_to_process.to_csv('submission.csv', index=False)","execution_count":50,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.4","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}