{"cells":[{"metadata":{},"cell_type":"markdown","source":"### Transfer learning with ResNet18\nSource: https://www.kaggle.com/pintu161/transfer-learning-in-pytorch-using-resnet18"},{"metadata":{"trusted":true},"cell_type":"code","source":"%matplotlib inline\n%config InlineBackend.figure_format = 'retina'\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport matplotlib.pyplot as plt\nimport os\nfrom collections import OrderedDict\nimport torch\nfrom torch import nn\nfrom torch import optim\nimport torch.nn.functional as F\nfrom torchvision import datasets, transforms, models","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Most of the pretrained models require the input to be 224x224 images. Also, we'll need to match the normalization used when the models were trained. Each color channel was normalized separately, the means are `[0.485, 0.456, 0.406]` and the standard deviations are `[0.229, 0.224, 0.225]`."},{"metadata":{"trusted":true},"cell_type":"code","source":"# download the pretrained model\nimport torchvision.models as models\nmodel = models.resnet18(pretrained = True)\nprint(model)\n\n#switch device to gpu if available\ndevice = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Load Data"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Unzip files\nimport zipfile\nextract_files = ['train', 'test1']\nfor file in extract_files:\n    with zipfile.ZipFile(\"../input/\"+file+\".zip\", \"r\") as z:\n        z.extractall(\".\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Load the list of files\ntrain_dir = 'train'\ntest_dir = 'test1'\ntrain_files = os.listdir(train_dir)\ntest_files = os.listdir(test_dir)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"len(train_files)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"len(test_files)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from torch.utils.data import Dataset, DataLoader, ConcatDataset\nfrom PIL import Image\nimport torchvision\n\nclass CatDogDataset(Dataset):\n    def __init__(self, file_list, dir, mode='train', transform = None):\n        self.file_list = file_list\n        self.dir = dir\n        self.mode= mode\n        self.transform = transform\n        if self.mode == 'train':\n            if 'dog' in self.file_list[0]:\n                self.label = 1\n            else:\n                self.label = 0\n            \n    def __len__(self):\n        return len(self.file_list)\n    \n    def __getitem__(self, idx):\n        img = Image.open(os.path.join(self.dir, self.file_list[idx]))\n        if self.transform:\n            img = self.transform(img)\n        if self.mode == 'train':\n            img = img.numpy()\n            return img.astype('float32'), self.label\n        else:\n            img = img.numpy()\n            return img.astype('float32'), self.file_list[idx]\n        \ndata_transform = transforms.Compose([\n    transforms.Resize(256),\n    transforms.ColorJitter(),\n    transforms.RandomCrop(224),\n    transforms.RandomHorizontalFlip(),\n    transforms.Resize(128),\n    transforms.ToTensor()\n])\n\ncat_files = [tf for tf in train_files if 'cat' in tf]\ndog_files = [tf for tf in train_files if 'dog' in tf]\n\ncats = CatDogDataset(cat_files, train_dir, transform = data_transform)\ndogs = CatDogDataset(dog_files, train_dir, transform = data_transform)\n\ncatdogs = ConcatDataset([cats, dogs])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dataloader = DataLoader(catdogs, batch_size = 64, shuffle=True, num_workers=4)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"samples, labels = iter(dataloader).next()\nplt.figure(figsize=(16,24))\ngrid_imgs = torchvision.utils.make_grid(samples[:24])\nnp_grid_imgs = grid_imgs.numpy()\n# in tensor, image is (batch, width, height), so you have to transpose it to (width, height, batch) in numpy to show it.\nplt.imshow(np.transpose(np_grid_imgs, (1,2,0)))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Simple CNN model"},{"metadata":{"trusted":true},"cell_type":"code","source":"class CnnNet(nn.Module):\n    def __init__(self):\n        super(CnnNet, self).__init__()\n        self.conv1 = nn.Conv2d(3, 64, 7, 2)\n        self.conv2 = nn.Conv2d(64, 128, 3, 2)\n        self.dropout1 = nn.Dropout2d(0.1)\n        self.dropout2 = nn.Dropout2d(0.1)\n        self.fc1 = nn.Linear(4608, 128)\n        self.fc2 = nn.Linear(128, 2)\n\n    def forward(self, x):\n        x = self.conv1(x)\n        x = F.relu(x)\n        x = F.max_pool2d(x, 3, 2)\n        x = self.conv2(x)\n        x = F.relu(x)\n        x = F.max_pool2d(x, 3, 2)\n        x = self.dropout1(x)\n        x = torch.flatten(x, 1)\n        x = self.fc1(x)\n        x = F.relu(x)\n        x = self.dropout2(x)\n        x = self.fc2(x)\n        output = F.log_softmax(x, dim=1)\n        return output","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"net = CnnNet()\nnet = net.to(device)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Training Only the Classifier"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Freeze the parameters \nfor param in model.parameters():\n    param.requires_grad = False \n    \n# Classifier architecture to put on top of resnet18\nfc = nn.Sequential(OrderedDict([\n    ('fc1', nn.Linear(512,100)),\n    ('relu', nn.ReLU()),\n    ('fc2', nn.Linear(100,2)),\n    ('output', nn.LogSoftmax(dim=1))\n]))\n\nmodel.fc = fc\n\n# shifting model to gpu\nmodel.to(device)\nmodel","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"device","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from tqdm import tqdm_notebook as tqdm\n# function to train the model\ndef train(model, trainloader, criterion, optimizer, epochs = 5, log_interval=100):\n    train_loss = []\n    all_loss = []\n    for epoch in range(epochs):\n        running_loss = 0\n        tq = tqdm(trainloader)\n        for batch_idx, (images, labels) in enumerate(tq):\n            inputs, labels = images.to(device), labels.to(device)\n\n            optimizer.zero_grad()\n            img = model(inputs)\n            \n            loss = criterion(img, labels)\n            all_loss.append(loss.item())\n            running_loss += loss\n            loss.backward()\n            optimizer.step()\n            \n            if batch_idx % log_interval == 0:\n                tq.set_description(f'Epoch {epoch}: {(running_loss / (batch_idx+1)).item():.3f}')\n            \n        train_loss.append(running_loss/len(trainloader))\n        \n    return train_loss, all_loss","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Train simple CNN\nepochs = 1\nnet.train()\noptimizer = optim.Adam(net.parameters(), lr=0.001)\ncriterion = nn.NLLLoss()    \ntrain_loss, all_loss = train(net,dataloader,criterion, optimizer, epochs, log_interval=10) ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Train pre-trained ResNet\nepochs = 1\nmodel.train()\noptimizer = optim.Adam(model.fc.parameters(), lr=0.001)\ncriterion = nn.NLLLoss()    \nft_train_loss, ft_all_loss = train(model,dataloader,criterion, optimizer, epochs, log_interval=10) ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"391 * 64","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Plot the 2 loss curve\nplt.figure(figsize=(20, 8))\nplt.plot(all_loss)\nplt.plot(ft_all_loss)\nplt.legend(['Simple CNN from scratch', 'Finetuned ResNet'])\nplt.ylabel('loss')\nplt.xlabel('batch number')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Save the model\n# filename_pth = 'ckpt_resnet18_catdog.pth'\n# torch.save(model.state_dict(), filename_pth)\n\n\n# Transform the test dataset\ntest_transform = transforms.Compose([\n    transforms.Resize((128,128)),\n    transforms.ToTensor()\n])\n\ntestset = CatDogDataset(test_files, test_dir, mode='test', transform = test_transform)\ntestloader = DataLoader(testset, batch_size = 64, shuffle=False, num_workers=4)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Prediction on Testset"},{"metadata":{"trusted":true},"cell_type":"code","source":"model.eval()\nfn_list = []\npred_list = []\nfor x, fn in testloader:\n    with torch.no_grad():\n        x = x.to(device)\n        output = model(x)\n        pred = torch.argmax(output, dim=1)\n        fn_list += [n[:-4] for n in fn]\n        pred_list += [p.item() for p in pred]\n\nsubmission = pd.DataFrame({\"id\":fn_list, \"label\":pred_list})\n# submission.to_csv('preds_resnet18.csv', index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"samples, _ = iter(testloader).next()\nsamples = samples.to(device)\nfig = plt.figure(figsize=(24, 16))\nfig.tight_layout()\noutput = model(samples[:24])\npred = torch.argmax(output, dim=1)\npred = [p.item() for p in pred]\nad = {0:'cat', 1:'dog'}\nfor num, sample in enumerate(samples[:24]):\n    plt.subplot(4,6,num+1)\n    plt.title(ad[pred[num]])\n    plt.axis('off')\n    sample = sample.cpu().numpy()\n    plt.imshow(np.transpose(sample, (1,2,0)))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"samples, _ = iter(testloader).next()\nsamples = samples.to(device)\nfig = plt.figure(figsize=(24, 16))\nfig.tight_layout()\noutput = net(samples[:24])\npred = torch.argmax(output, dim=1)\npred = [p.item() for p in pred]\nad = {0:'cat', 1:'dog'}\nfor num, sample in enumerate(samples[:24]):\n    plt.subplot(4,6,num+1)\n    plt.title(ad[pred[num]])\n    plt.axis('off')\n    sample = sample.cpu().numpy()\n    plt.imshow(np.transpose(sample, (1,2,0)))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"References:<br>\n[densenet](https://www.kaggle.com/jaeboklee/pytorch-cat-vs-dog) <br>\n[Udacity Transfer Learning](https://classroom.udacity.com/courses/ud188/lessons/c5706f76-0e30-4b48-b74e-c19fafc33a75/concepts/c33dec4c-ff16-465f-88e9-e95365e7b522)"}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.8"}},"nbformat":4,"nbformat_minor":4}