{"cells":[{"metadata":{"_uuid":"88e20188ddffce29fc4c3af7b6fc09bb48cc1fee"},"cell_type":"markdown","source":"## Whales recognition ResNext baseline"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"from fastai.conv_learner import *\nfrom fastai.dataset import *\nfrom tqdm import tqdm\nimport pandas as pd\nimport numpy as np\nimport os\nfrom sklearn.model_selection import train_test_split, StratifiedShuffleSplit\nimport matplotlib.pyplot as plt\nimport math\n\n\nMODEL_NAME = 'Resnext50'\nTRAIN = '../input/humpback-whale-identification/train/'\nTEST = '../input/humpback-whale-identification/test/'\nLABELS = '../input/humpback-whale-identification/train.csv'\nSAMPLE_SUB = '../input/humpback-whale-identification/sample_submission.csv'\n\n# Backbone architecture\narch = resnext50\n# Number of workers for data preprocessing\nnum_workers = 4","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"bf2a5c5342e855974efaeb7fe5c2b90f2cf636cf"},"cell_type":"markdown","source":"Next, we prapare out dataset to work with Fastai's pipeline."},{"metadata":{"trusted":true,"_uuid":"d9adfc15b56c7f80f291c66dc6d6f38d4d55e6a2"},"cell_type":"code","source":"df = pd.read_csv(LABELS).set_index('Image')\nnew_whale_df = df[df.Id == \"new_whale\"] # only new_whale dataset\ntrain_df = df[~(df.Id == \"new_whale\")] # no new_whale dataset, used for training\nunique_labels = np.unique(train_df.Id.values)\n\nlabels_dict = dict()\nlabels_list = []\nfor i in range(len(unique_labels)):\n    labels_dict[unique_labels[i]] = i\n    labels_list.append(unique_labels[i])\nprint(\"Number of classes: {}\".format(len(unique_labels)))\ntrain_df.Id = train_df.Id.apply(lambda x: labels_dict[x])\ntrain_labels = np.asarray(train_df.Id.values)\ntest_names = [f for f in os.listdir(TEST)]","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"6a910097d19053c50d60ea7ee9496ed2a55746e2"},"cell_type":"markdown","source":"Let's draw a simple histogram to see the sample-per-class distribution."},{"metadata":{"trusted":true,"_uuid":"ddef1744553be7723709a1e14253612a18c6f7e2"},"cell_type":"code","source":"labels_count = train_df.Id.value_counts()\n\nplt.figure(figsize=(18, 4))\nplt.subplot(121)\n_, _,_ = plt.hist(labels_count.values)\nplt.ylabel(\"frequency\")\nplt.xlabel(\"class size\")\n\nplt.title('class distribution; log scale')\nlabels_count.head()\n\nplt.subplot(122)\n_ = plt.plot(labels_count[1:].values)\nplt.title('w/o class new_whale; log scale')\nplt.xlabel(\"class\")\nplt.ylabel(\"log(size)\")\nplt.gca().set_yscale('log')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"a494334eac4b3e32e4756395dd2167ee94368f86"},"cell_type":"code","source":"train_df['image_name'] = train_df.index\n\nrs = np.random.RandomState(42) # set random seed to be equal to the sense of life\nperm = rs.permutation(len(train_df))\n\ntr_n = train_df['image_name'].values\n# Yes, we will validate on the subset of training data\nval_n = train_df['image_name'].values[perm][:1000]\n\nprint('Train/val:', len(tr_n), len(val_n))\nprint('Train classes', len(train_df.loc[tr_n].Id.unique()))\nprint('Val classes', len(train_df.loc[val_n].Id.unique()))","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d4a7285f61c1e90b90c3db8ba15c2f4c6ff57ce8"},"cell_type":"markdown","source":"**Crop**"},{"metadata":{"trusted":true,"_uuid":"3a454d66649029276b795f318b272f22201b51e7"},"cell_type":"code","source":"import pickle\n\nPICKL = '../input/cropimg/bounding-box.pickle'\nwith open(PICKL, 'rb') as f:\n    crop_boxs = pickle.load(f)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"b0563b58320ef76685eb1115b7d40a4017b8165e"},"cell_type":"code","source":"from PIL import Image\n\ndef make_bbox_image(img_path):  \n    img_name = img_path.split('/')[-1]                \n    \n    main_img = Image.open(img_path)\n    bb = crop_boxs[img_name]\n    \n    # заменяем отрицательные на 1\n    f=lambda a: int((abs(a)+a)/2) + 1\n    \n    # Ограничение по размеру иначе вылетает ошибка\n    max_sz = 2500\n    ms = lambda x: max_sz if x > max_sz else x\n    \n    lst_bb = list(bb)\n    lst_bb[0] = f(lst_bb[0])\n    lst_bb[0] = ms(lst_bb[0])\n    \n    lst_bb[1] = f(lst_bb[1])\n    lst_bb[1] = ms(lst_bb[1])\n    \n    lst_bb[2] = f(lst_bb[2])\n    lst_bb[2] = ms(lst_bb[2])\n    \n    lst_bb[3] = f(lst_bb[3])\n    lst_bb[3] = ms(lst_bb[3])\n    bb = tuple(lst_bb)\n\n    if (bb[0] < bb[2]) & (bb[1] < bb[3]):\n        img_crop = main_img.crop(tuple(bb))\n    else:\n        img_crop = main_img\n        \n    return img_crop","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"94af91d70819db979d39a4d77b2e30493498978b"},"cell_type":"code","source":"import torchvision.transforms as transforms\npil2tensor = transforms.ToTensor()\n\nclass HWIDataset(FilesDataset):\n    def __init__(self, fnames, path, transform):\n        self.train_df = train_df\n        super().__init__(fnames, transform, path)\n\n    def get_x(self, i):\n        # Применяем Crop\n        img_cr = make_bbox_image(os.path.join(self.path, self.fnames[i])).convert(\"RGB\")\n        img = pil2tensor(img_cr).numpy().transpose(1, 2, 0)\n        \n        try:\n            img = cv2.resize(img, (self.sz, self.sz))\n        except Exception as e:\n            print(self.fnames[i])\n            print(str(e))\n        return img\n\n    def get_y(self, i):\n        if (self.path == TEST): return 0\n        return self.train_df.loc[self.fnames[i]]['Id']\n\n    def get_c(self):\n        return len(unique_labels)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"140dfae2b41cbe4f770f8d80fcaba0ebc772e983"},"cell_type":"code","source":"class RandomLighting(Transform):\n    def __init__(self, b, c, tfm_y=TfmType.NO):\n        super().__init__(tfm_y)\n        self.b, self.c = b, c\n\n    def set_state(self):\n        self.store.b_rand = rand0(self.b)\n        self.store.c_rand = rand0(self.c)\n\n    def do_transform(self, x, is_y):\n        if is_y and self.tfm_y != TfmType.PIXEL: return x  # add this line to fix the bug\n        b = self.store.b_rand\n        c = self.store.c_rand\n        c = -1 / (c - 1) if c < 0 else c + 1\n        x = lighting(x, b, c)\n        return x\n    \ndef get_data(sz, batch_size):\n    \"\"\"\n    Read data and do augmentations\n    \"\"\"\n    aug_tfms = [RandomRotateZoom(deg=20, zoom=2, stretch=1),\n                RandomLighting(0.2, 0.2, tfm_y=TfmType.NO),\n                RandomBlur(blur_strengths=3,tfm_y=TfmType.NO),\n                RandomFlip(tfm_y=TfmType.NO)]\n    tfms = tfms_from_model(arch, sz, crop_type=CropType.NO, tfm_y=TfmType.NO,\n                           aug_tfms=aug_tfms)\n    ds = ImageData.get_ds(HWIDataset, (tr_n[:-(len(tr_n) % batch_size)], TRAIN),\n                          (val_n, TRAIN), tfms, test=(test_names, TEST))\n    md = ImageData(\"./\", ds, batch_size, num_workers=num_workers, classes=None)\n    return md\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"4cb1115fc85d0c8820e0f81741e972ae6fed9bc1"},"cell_type":"code","source":"!wget http://files.fast.ai/models/weights.tgz\n!tar -zxvf weights.tgz\n!mkdir /opt/conda/lib/python3.6/site-packages/fastai/weights/\n!cp weights/resnext_50_32x4d.pth /opt/conda/lib/python3.6/site-packages/fastai/weights/\n!rm -rf weights weights.tgz","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"f8258255beb8fb608abb8a292b07c7161580007e"},"cell_type":"code","source":"image_size = 384\nbatch_size = 16\nmd = get_data(image_size, batch_size)\nextra_fc_layers_size = []\nlearn = ConvLearner.pretrained(arch, md, xtra_fc=extra_fc_layers_size) \nlearn.opt_fn = optim.Adam","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"11aa429a9c4938b946d2ffa5c0e93074d4ef6473"},"cell_type":"code","source":"md.is_multi, md.is_reg","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false,"_uuid":"ab2d2e0e0a6a15f38a983993486316040e9afb38"},"cell_type":"code","source":"print('Number of layer groups:', len(learn.get_layer_groups()), '\\t(first 2 groups is pretrained backbone)')\nprint('This is our extra thin on top of the backbone Resnet50 architecture:')\nlearn.get_layer_groups()[2]","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"ddc1f6a3bf90cf20ffdf566fd852a5f15022c9e2"},"cell_type":"markdown","source":"# Nothing fancy, just train in here\nWe start by training only the newly initialized weights (classifier), then unfreeze the model and finetune the pretrained weights"},{"metadata":{"trusted":true,"_uuid":"f3118d5e2dbe61c8d51d0e33642ea5bb0b516a54","scrolled":true},"cell_type":"code","source":"base_lr = 1e-4 # lr for the backbone\nfc_lr = 1e-3 # lr for the classifer\n\nlrs = [base_lr, base_lr, fc_lr]\n# Freeze backbone and train the classifier for 2 epochs\nlearn.fit(lrs=lrs, n_cycle=2, cycle_len=None)\n\n# Unfreeze backbone and continue training for 9 epochs\nlearn.unfreeze()\nlearn.fit(lrs, n_cycle=9, cycle_len=None)\nlearn.save(MODEL_PATH)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"eabe555ace20e3b36c6266432108d31de1e58282"},"cell_type":"markdown","source":"## No Fuss Prediction with 8 TTA"},{"metadata":{"trusted":true,"_uuid":"3ed2aa4395dd4f573aeaad5ac25a5f4f5078354f"},"cell_type":"code","source":"best_th = 0.4","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"6cbfaedbad6bac01b06d87eaf3723dd260b7a51e"},"cell_type":"code","source":"preds_t,y_t = learn.TTA(is_test=True,n_aug=8)\npreds_t = np.stack(preds_t, axis=-1)\npreds_t = np.exp(preds_t)\npreds_t = preds_t.mean(axis=-1)\npreds_t = np.concatenate([np.zeros((preds_t.shape[0],1))+best_th, preds_t],axis=1)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"82b4e79d2a05267790200de4860fd25a0a669f7f"},"cell_type":"markdown","source":"Finally, our submission."},{"metadata":{"trusted":true,"_uuid":"f5fbd91e970d375debc3270ebd5b08bb41eeb66e"},"cell_type":"code","source":"sample_df = pd.read_csv(SAMPLE_SUB)\nsample_list = list(sample_df.Image)\nlabels_list = [\"new_whale\"]+labels_list\npred_list = [[labels_list[i] for i in p.argsort()[-5:][::-1]] for p in preds_t]\npred_dic = dict((key, value) for (key, value) in zip(learn.data.test_ds.fnames,pred_list))\npred_list_cor = [' '.join(pred_dic[id]) for id in sample_list]\ndf = pd.DataFrame({'Image':sample_list,'Id': pred_list_cor})\ndf.to_csv('submission_{}.csv'.format(MODEL_NAME), header=True, index=False)\ndf.head()","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}