{"cells":[{"metadata":{"_uuid":"bfe1d8d752d2f79ce6999eb2f8e99004696ba151"},"cell_type":"markdown","source":"## Reference:\n* https://www.kaggle.com/phhasian0710/create-bounding-box-images-whale-recognition\n* https://www.kaggle.com/satian/seresnext101-pytorch-starter\n* https://www.kaggle.com/martinpiotte/bounding-box-model\n\n* Experimented with ResNet 18 instead of the SEResNet used by the original author 0.292\n* Experimented with Xception too. 0.352\n"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"%matplotlib inline\n#%reload_ext autoreload        <------------— comment out \n#%autoreload 0   \n\nimport os\nimport warnings\nimport pandas as pd\nimport numpy as np\nfrom tqdm import tqdm\nfrom matplotlib import pyplot as plt\nfrom PIL import Image\nimport torch\nfrom torch.utils.data import DataLoader, Dataset\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.optim as optim\nfrom torch.optim import lr_scheduler\nfrom torchvision import transforms\n\n\nimport cv2\nfrom sklearn.preprocessing import LabelEncoder, OneHotEncoder\n\n\nwarnings.filterwarnings(\"ignore\",category=DeprecationWarning)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"15416f8a67861078ff9ec3b33f3ce8d6ca6de640"},"cell_type":"code","source":"os.listdir('../input')","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true},"cell_type":"code","source":"train_df = pd.read_csv('../input/humpback-whale-identification/train.csv')\nsub = pd.read_csv('../input/humpback-whale-identification/sample_submission.csv')\n\nbest_model_path = '../input/bbox-seresnext101-pytorch-0-657/best_model.pth'\n\nTRN_IMGS_DIR = '../input/humpback-whale-identification/train/'\nTST_IMGS_DIR = '../input/humpback-whale-identification/test/'\n\nBBOX_TRAIN_CSV = '../input/box-whale/bounding/bounding_boxes_train.csv'\nBBOX_TEST_CSV = '../input/box-whale/bounding/bounding_boxes_test.csv'\nbbox_train = pd.read_csv(BBOX_TRAIN_CSV)\nbbox_test = pd.read_csv(BBOX_TEST_CSV)\nbbox_train = train_df.join(bbox_train.set_index('Image'), on='Image')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"90a555e56bdb4a5d64783b5242d8e443e5cb5c08"},"cell_type":"markdown","source":"## Creating Labels"},{"metadata":{"trusted":true,"_uuid":"604b0187753fe9ef8d1b50d40427b7ec10c67c0c"},"cell_type":"code","source":"def prepare_labels(y):\n    # From here: https://www.kaggle.com/pestipeti/keras-cnn-starter\n    values = np.array(y)\n    label_encoder = LabelEncoder()\n    integer_encoded = label_encoder.fit_transform(values)\n\n    onehot_encoder = OneHotEncoder(sparse=False)\n    integer_encoded = integer_encoded.reshape(len(integer_encoded), 1)\n    onehot_encoded = onehot_encoder.fit_transform(integer_encoded)\n\n    y = onehot_encoded\n    return y, label_encoder","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"5b783ac5c9fe72ab58d2d45a81629e8b398ec377"},"cell_type":"code","source":"y, label_encoder = prepare_labels(train_df['Id'])\nNCLASSES = len(y[0])","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"bcb4e47f2484a04c0e7625eafa6df8c054ed8052"},"cell_type":"markdown","source":"## Creating Augmentations"},{"metadata":{"trusted":true,"_uuid":"ad9bdb298eb5a1eb23695eb8e00fec0aab9cf4fe"},"cell_type":"code","source":"trn_trnsfms = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomVerticalFlip(),\n    transforms.RandomAffine(degrees=30),\n    transforms.ToTensor(),\n    transforms.Normalize(\n        mean=[0.485, 0.456, 0.406],\n        std=[0.229, 0.224, 0.225]\n    )\n])\n\ntst_trnsfms = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.ToTensor(),\n    transforms.Normalize(\n        mean=[0.485, 0.456, 0.406],\n        std=[0.229, 0.224, 0.225]\n    )\n])","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"a47b91a0a2a166e71fddb069ff30b691a27d080b"},"cell_type":"markdown","source":"## Creating PyTorch Dataloader"},{"metadata":{"trusted":true,"_uuid":"6b52bac58231f6fb304f1d21e360530c6af91995"},"cell_type":"code","source":"class WhaleDataLoader(Dataset):\n    def __init__(self, image_folder, process='train', bbox=None, df=None, transform=None, y=None):\n        self.image_folder = image_folder\n        self.imgs_list = [img for img in os.listdir(image_folder)]\n        self.process = process\n        self.transform = transform\n        self.y = y\n        if self.process == 'train':\n            self.df = df.values\n        self.bbox = bbox.values\n    \n    def __len__(self):\n        return len(self.imgs_list)\n    \n    def __getitem__(self, idx):\n        if self.process == 'train':\n            img_name = os.path.join(self.image_folder, self.df[idx][0])\n            label = self.y[idx]\n        \n        elif self.process == 'test':\n            img_name = os.path.join(self.image_folder, self.imgs_list[idx])\n            label = np.zeros((NCLASSES,))\n        \n        img = Image.open(img_name).convert('RGB')\n        if self.process == 'train':\n            area = (self.bbox[idx][2], self.bbox[idx][3], self.bbox[idx][4], self.bbox[idx][5])\n        elif self.process == 'test':\n            area = (self.bbox[idx][1], self.bbox[idx][2], self.bbox[idx][3], self.bbox[idx][4])\n        img = img.crop(area)\n        \n        img = self.transform(img)\n        if self.process == 'train':\n            return img, label\n        elif self.process == 'test':\n            return img, label, self.imgs_list[idx]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"cd40fc8811995ccb1174d05e2f8e4a2e37a5b222"},"cell_type":"code","source":"train_dataloader = WhaleDataLoader(image_folder = TRN_IMGS_DIR, process='train', bbox=bbox_train, df=train_df, transform=trn_trnsfms, y=y)\ntest_dataloader = WhaleDataLoader(image_folder = TST_IMGS_DIR, process='test', bbox=bbox_test,transform=tst_trnsfms)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"c37075b224e3f10fc03b83c35221a14065752c8f"},"cell_type":"code","source":"batch_size = 16\nnum_workers = 4\n\ntrain_loader = DataLoader(train_dataloader, batch_size=batch_size, num_workers=num_workers, pin_memory=True, shuffle=True)\ntest_loader = DataLoader(test_dataloader, batch_size=batch_size, num_workers=num_workers, pin_memory=True)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"bb592612d89777f48377531d4a4d5b064b0e4d8d"},"cell_type":"markdown","source":"# Creating Xception Model\n"},{"metadata":{"trusted":true,"_uuid":"bc94f2504fac85e5a8a2c76b2aecd9f40de09c90"},"cell_type":"code","source":"from __future__ import print_function, division, absolute_import\nfrom collections import OrderedDict\nimport math\n\nimport torch.nn as nn\nfrom torch.utils import model_zoo\n\npretrained_settings = {\n    'xception': {\n        'imagenet': {\n            'url': 'http://data.lip6.fr/cadene/pretrainedmodels/xception-43020ad28.pth',\n            'input_space': 'RGB',\n            'input_size': [3, 299, 299],\n            'input_range': [0, 1],\n            'mean': [0.5, 0.5, 0.5],\n            'std': [0.5, 0.5, 0.5],\n            'num_classes': 1000,\n            'scale': 0.8975 # The resize parameter of the validation transform should be 333, and make sure to center crop at 299x299\n        }\n    }\n}\n\nclass SeparableConv2d(nn.Module):\n    def __init__(self,in_channels,out_channels,kernel_size=1,stride=1,padding=0,dilation=1,bias=False):\n        super(SeparableConv2d,self).__init__()\n        self.conv1 = nn.Conv2d(in_channels,in_channels,kernel_size,stride,padding,dilation,groups=in_channels,bias=bias)\n        self.pointwise = nn.Conv2d(in_channels,out_channels,1,1,0,1,1,bias=bias)\n    \n    def forward(self, x):\n        x = self.conv1(x)\n        x = self.pointwise(x)\n        return x\n\n    \nclass Block(nn.Module):\n    def __init__(self,in_filters,out_filters,reps,strides=1,start_with_relu=True,grow_first=True):\n        super(Block, self).__init__()\n        \n        if out_filters != in_filters or strides!=1:\n            self.skip = nn.Conv2d(in_filters,out_filters,1,stride=strides, bias=False)\n            self.skipbn = nn.BatchNorm2d(out_filters)\n        else:\n            self.skip = None\n        \n        self.relu = nn.ReLU(inplace=True)\n        rep = []\n        \n        filters = in_filters\n        if grow_first:\n            rep.append(self.relu)\n            rep.append(SeparableConv2d(in_filters,out_filters,3,stride=1,padding=1,bias=False))\n            rep.append(nn.BatchNorm2d(out_filters))\n            filters = out_filters\n\n        for i in range(reps-1):\n            rep.append(self.relu)\n            rep.append(SeparableConv2d(filters,filters,3,stride=1,padding=1,bias=False))\n            rep.append(nn.BatchNorm2d(filters))\n\n        if not grow_first:\n            rep.append(self.relu)\n            rep.append(SeparableConv2d(in_filters,out_filters,3,stride=1,padding=1,bias=False))\n            rep.append(nn.BatchNorm2d(out_filters))\n\n        if not start_with_relu:\n            rep = rep[1:]\n        else:\n            rep[0] = nn.ReLU(inplace=False)\n\n        if strides != 1:\n            rep.append(nn.MaxPool2d(3,strides,1))\n        self.rep = nn.Sequential(*rep)\n    \n    def forward(self, inp):\n        x = self.rep(inp)\n        \n        if self.skip is not None:\n            skip = self.skip(inp)\n            skip = self.skipbn(skip)\n        else:\n            skip = inp\n        \n        x += skip\n        return x\n\nclass Xception(nn.Module):\n    \"\"\"\n    Xception optimized for the ImageNet dataset, as specified in\n    https://arxiv.org/pdf/1610.02357.pdf\n    \"\"\"\n    def __init__(self, num_classes=1000):\n        \"\"\" Constructor\n        Args:\n            num_classes: number of classes\n        \"\"\"\n        super(Xception, self).__init__()\n        self.num_classes = num_classes\n\n        self.conv1 = nn.Conv2d(3, 32, 3,2, 0, bias=False)\n        self.bn1 = nn.BatchNorm2d(32)\n        self.relu = nn.ReLU(inplace=True)\n\n        self.conv2 = nn.Conv2d(32,64,3,bias=False)\n        self.bn2 = nn.BatchNorm2d(64)\n        #do relu here\n\n        self.block1=Block(64,128,2,2,start_with_relu=False,grow_first=True)\n        self.block2=Block(128,256,2,2,start_with_relu=True,grow_first=True)\n        self.block3=Block(256,728,2,2,start_with_relu=True,grow_first=True)\n\n        self.block4=Block(728,728,3,1,start_with_relu=True,grow_first=True)\n        self.block5=Block(728,728,3,1,start_with_relu=True,grow_first=True)\n        self.block6=Block(728,728,3,1,start_with_relu=True,grow_first=True)\n        self.block7=Block(728,728,3,1,start_with_relu=True,grow_first=True)\n\n        self.block8=Block(728,728,3,1,start_with_relu=True,grow_first=True)\n        self.block9=Block(728,728,3,1,start_with_relu=True,grow_first=True)\n        self.block10=Block(728,728,3,1,start_with_relu=True,grow_first=True)\n        self.block11=Block(728,728,3,1,start_with_relu=True,grow_first=True)\n\n        self.block12=Block(728,1024,2,2,start_with_relu=True,grow_first=False)\n\n        self.conv3 = SeparableConv2d(1024,1536,3,1,1)\n        self.bn3 = nn.BatchNorm2d(1536)\n\n        #do relu here\n        self.conv4 = SeparableConv2d(1536,2048,3,1,1)\n        self.bn4 = nn.BatchNorm2d(2048)\n\n        self.fc = nn.Linear(2048, num_classes)\n\n        # #------- init weights --------\n        # for m in self.modules():\n        #     if isinstance(m, nn.Conv2d):\n        #         n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels\n        #         m.weight.data.normal_(0, math.sqrt(2. / n))\n        #     elif isinstance(m, nn.BatchNorm2d):\n        #         m.weight.data.fill_(1)\n        #         m.bias.data.zero_()\n        # #-----------------------------\n\n    def features(self, input):\n        x = self.conv1(input)\n        x = self.bn1(x)\n        x = self.relu(x)\n\n        x = self.conv2(x)\n        x = self.bn2(x)\n        x = self.relu(x)\n\n        x = self.block1(x)\n        x = self.block2(x)\n        x = self.block3(x)\n        x = self.block4(x)\n        x = self.block5(x)\n        x = self.block6(x)\n        x = self.block7(x)\n        x = self.block8(x)\n        x = self.block9(x)\n        x = self.block10(x)\n        x = self.block11(x)\n        x = self.block12(x)\n\n        x = self.conv3(x)\n        x = self.bn3(x)\n        x = self.relu(x)\n\n        x = self.conv4(x)\n        x = self.bn4(x)\n        return x\n\n    def logits(self, features):\n        x = self.relu(features)\n\n        x = F.adaptive_avg_pool2d(x, (1, 1))\n        x = x.view(x.size(0), -1)\n        x = self.last_linear(x)\n        return x\n\n    def forward(self, input):\n        x = self.features(input)\n        x = self.logits(x)\n        return x\n    \n\n\n# def initialize_pretrained_model(model, num_classes, settings):\n#     assert num_classes == settings['num_classes'], \\\n#         'num_classes should be {}, but is {}'.format(\n#             settings['num_classes'], num_classes)\n#     model.load_state_dict(model_zoo.load_url(settings['url']))\n#     model.input_space = settings['input_space']\n#     model.input_size = settings['input_size']\n#     model.input_range = settings['input_range']\n#     model.mean = settings['mean']\n#     model.std = settings['std']\n\ndef xception(num_classes=1000, pretrained='imagenet'):\n    model = Xception(num_classes=num_classes)\n    if pretrained:\n        settings = pretrained_settings['xception'][pretrained]\n        assert num_classes == settings['num_classes'], \\\n            \"num_classes should be {}, but is {}\".format(settings['num_classes'], num_classes)\n\n        model = Xception(num_classes=num_classes)\n        model.load_state_dict(model_zoo.load_url(settings['url']))\n\n        model.input_space = settings['input_space']\n        model.input_size = settings['input_size']\n        model.input_range = settings['input_range']\n        model.mean = settings['mean']\n        model.std = settings['std']\n\n    # TODO: ugly\n    model.last_linear = model.fc\n    del model.fc\n    return model\n\ndef save_checkpoint(state, is_best, fpath='gdrive/My Drive/checkpoint.pth'):\n    torch.save(state, fpath)\n    if is_best:\n        torch.save(state, 'best_model.pth')\nclass Xception_base(nn.Module):\n    \"\"\"\n    Xception optimized for the ImageNet dataset, as specified in\n    https://arxiv.org/pdf/1610.02357.pdf\n    \"\"\"\n    def __init__(self, num_classes=1000, pretrained='imagenet'):\n        \"\"\" Constructor\n        Args:\n            num_classes: number of classes\n        \"\"\"\n        super(Xception_base, self).__init__()\n        if not pretrained is None:\n            print(\"here\")\n            base_num_classes = 1000\n            self.base_model = Xception(num_classes = base_num_classes)\n            settings = pretrained_settings['xception'][pretrained]\n            assert base_num_classes == settings['num_classes'], \\\n                \"num_classes should be {}, but is {}\".format(settings['num_classes'], num_classes)\n    #         model = Xception(num_classes=num_classes)\n            self.base_model.load_state_dict(model_zoo.load_url(settings['url']))\n            self.base_model.input_space = settings['input_space']\n            self.base_model.input_size = settings['input_size']\n            self.base_model.input_range = settings['input_range']\n            self.base_model.mean = settings['mean']\n            self.base_model.std = settings['std']\n        else:\n            self.base_model = Xception(num_classes=num_classes)\n        del self.base_model.fc #= SeparableConv2d(2048,1536,3,1,1)\n        self.conv5 = SeparableConv2d(2048, 3072,3,1,1)\n        self.bn5 = nn.BatchNorm2d(3072)\n        self.conv6 = SeparableConv2d(3072, 5120,3,1,1)\n        self.bn6 = nn.BatchNorm2d(5120)\n        self.last_linear = nn.Linear(5120, num_classes)\n#         self.fc = nn.Linear(2048, num_classes)\n    def forward(self, input):\n        x = self.base_model.features(input)\n        x = self.base_model.relu(x)\n        x = self.conv5(x)\n        x = self.bn5(x)\n        x = self.base_model.relu(x)\n        x = self.conv6(x)\n        x = self.bn6(x)\n        x = self.base_model.relu(x)\n        x = F.adaptive_avg_pool2d(x, (1, 1))\n        x = x.view(x.size(0), -1)\n        x = self.last_linear(x)\n        return x","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"0b331f5a8d6fee6e86c3e84d94f71a18701d3202"},"cell_type":"code","source":"model = Xception_base(5005, pretrained=None)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"939c1043b420c21cbbf642171be8dd01300ec81b"},"cell_type":"code","source":"model = model.cuda()\ncriterion = nn.BCEWithLogitsLoss()\noptimizer = optim.Adam(model.parameters(), lr=0.0005)\nscheduler = lr_scheduler.StepLR(optimizer, step_size=3, gamma=0.1)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"04b495f25d06374b14b08c0e604332ae6b8d28ef"},"cell_type":"markdown","source":"## Predict"},{"metadata":{"trusted":true,"_uuid":"0b1054f578dd04a6100c3cf5d147ab7b5f12ef8a"},"cell_type":"code","source":"n_epochs = 13\nmean_losss = 99\nis_best = False\nfor epoch in range(1, n_epochs+1):\n    train_loss = []\n    \n    for batch_i, (data, target) in tqdm(enumerate(train_loader), total = len(train_loader)):\n        data, target = data.cuda(), target.cuda()\n\n        optimizer.zero_grad()\n        output = model(data)\n        loss = criterion(output, target.float())\n        train_loss.append(loss.item())\n\n        loss.backward()\n        optimizer.step()\n    \n    scheduler.step()\n    if mean_losss > np.mean(train_loss):\n        is_best = True\n        mean_losss = np.mean(train_loss)\n    else:\n        is_best = False\n    save_checkpoint({'epoch': epoch,\n                         'state_dict': model.state_dict(),\n                         'optimizer' : optimizer.state_dict(),\n                        }, is_best)\n    print(f'Epoch {epoch}, train loss: {np.mean(train_loss):.4f}')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"ea109cc5cea0a8fdc0240b3932e50addf3132657"},"cell_type":"code","source":"model.eval()\nfor (data, target, name) in tqdm(test_loader):\n    data = data.cuda()\n    output = model(data)\n    output = output.cpu().detach().numpy()\n    for i, (e, n) in enumerate(list(zip(output, name))):\n        sub.loc[sub['Image'] == n, 'Id'] = ' '.join(label_encoder.inverse_transform(e.argsort()[-5:][::-1]))\n        \nsub.to_csv('submission_xception_1.csv', index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"1d4b66c1828b660823a06ddaeb72d023a82bfa92"},"cell_type":"code","source":"","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}