{"cells":[{"metadata":{"_uuid":"3965823d76cca84c7166ce36392c592c7b6bf153"},"cell_type":"markdown","source":"This tut is based on pytorch tut for transfer learning. \nMany issues are sovled. Like correct use of data set & data loader. Usage of multiple size input. Usage of cuda. etc.\n\ncheck input."},{"metadata":{"trusted":true,"_uuid":"21940d4cef860cd4250a5a27a36f897ba012ccc6","collapsed":true},"cell_type":"code","source":"!ls ../input","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"c2d02896901ed6010571580b23f40accf95329e5"},"cell_type":"markdown","source":"import libs"},{"metadata":{"trusted":true,"_uuid":"39da081fb0412742793f115ca02131c2d77463f2","collapsed":true},"cell_type":"code","source":"# import libs\nimport glob, pylab, pandas as pd\nimport pydicom, numpy as np\nimport random\nimport json\nimport time\nimport copy\nimport pydicom\nimport torchvision\nimport sys\nimport torch\nimport torch.nn as nn\nimport matplotlib.pyplot as plt\nimport torch.optim as optim\nfrom tqdm import tqdm, tqdm_notebook\nfrom torch.optim import lr_scheduler\nfrom torch.autograd import Variable\nfrom PIL import Image, ImageDraw, ImageFont\n\nfrom matplotlib import patches, patheffects\n\nfrom sklearn.model_selection import train_test_split\nfrom torchvision import datasets, transforms\nfrom torch.utils.data import Dataset, DataLoader\nfrom torch.optim import lr_scheduler\nfrom pathlib import Path\n","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"9e5a6e423dbc26ed450d7ca1a57a72fb1924bab7"},"cell_type":"markdown","source":"create class to index dict see how to use it, https://github.com/pytorch/vision/blob/d6c7900d06c3388bf814cecbe90f91a9afecbefb/torchvision/datasets/folder.py#L54"},{"metadata":{"trusted":true,"_uuid":"84d66624808d25b4ff4f877aac4b6b96c379585c","collapsed":true},"cell_type":"code","source":"PATH = Path('../input')\nclass_df = pd.read_csv(PATH/'labels.csv')\nclass_df.head()\nclass_to_idx = {x:i for i,x in enumerate(class_df.breed.unique())}\nidx_to_class = {i:x for i,x in enumerate(class_df.breed.unique())}\ndevice = torch.cuda.set_device(0)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"db3e09bc03dee7531c72e6ba45658ff8cd3b4249"},"cell_type":"markdown","source":"We create some helper func to load & show images\nWe implement Dataset class to load and give image. We are centering the odd resized image and pasting on black background."},{"metadata":{"trusted":true,"_uuid":"adbabc7def43aa314dde705dbfb3bf46f990d0c1","collapsed":true},"cell_type":"code","source":"def show_img(im, figsize=None, ax=None):\n    if not ax: \n        fig,ax = plt.subplots(figsize=figsize)\n    ax.imshow(im)\n    ax.get_xaxis().set_visible(False)\n    ax.get_yaxis().set_visible(False)\n    return ax\n\ndef draw_outline(o, lw):\n  o.set_path_effects([patheffects.Stroke(\n      linewidth=lw, foreground='black'), patheffects.Normal()])\n\ndef draw_rect(ax, b):\n    patch = ax.add_patch(patches.Rectangle(b[:2], *b[-2:], fill=False, edgecolor='white', lw=2))\n    draw_outline(patch, 4)\n\ndef draw_text(ax, xy, txt, sz=14):\n    text = ax.text(*xy, txt, verticalalignment='top', color='white', fontsize=sz, weight='bold')\n    draw_outline(text, 1)\n\ndef black_background_thumbnail(image, thumbnail_size=(224,224)):\n    background = Image.new('RGB', thumbnail_size, \"black\")    \n    source_image = image.convert(\"RGB\")\n    source_image.thumbnail(thumbnail_size)\n    (w, h) = source_image.size\n    background.paste(source_image, (int((thumbnail_size[0] - w) / 2), int((thumbnail_size[1] - h) / 2) ))\n    return background\n\nclass CDataset(Dataset):\n    def __init__(self, ds, img_dir, class_df, class_to_idx, transform=None,): \n        self.ds = ds\n        self.img_dir = img_dir\n        self.class_df = class_df\n        self.class_to_idx = class_to_idx\n        self.transform = transform if transform else None\n        \n    def __len__(self): \n        return len(self.ds)\n    \n    def read_image(self, loc):\n        img_arr = Image.open(loc.as_posix())\n        return img_arr.convert('RGB')\n        \n    def __getitem__(self, i):\n        img = self.read_image(self.ds[i])\n        img = black_background_thumbnail(img)\n        if self.transform:\n            img = self.transform(img)\n        label = self.ds[i].name.split('.')[0]\n        kls = self.class_df[self.class_df['id'] == label]\n        return img, self.class_to_idx[kls.iloc[0].breed]","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"93e70ab337ff47e7a6bf3ef1a306a11458d7046a"},"cell_type":"markdown","source":"https://github.com/pytorch/vision/blob/master/torchvision/utils.py"},{"metadata":{"_uuid":"1f12c36fc434888bca4393effcd0e344010b9f0c"},"cell_type":"markdown","source":"Next create data loader. We have removed normalization in transformation because transformed image would give -ve value. Normalisation values should be calculated on the dataset."},{"metadata":{"trusted":true,"_uuid":"1db62bac3cd22d51f6d0c170c9e0fa157f1d948b","collapsed":true},"cell_type":"code","source":"img_dir = PATH/'train'\n# sample = random.sample(list(img_dir.iterdir()), 400) # sample\nsample = list(img_dir.iterdir())\ntrain, test = train_test_split(sample)\nbatch_size=58\nsz=224\n\n# if adding normalize with given value will cause transformed image range value in -ve to +ve.\n# avoid it.\ntransform = transforms.Compose([\n        transforms.CenterCrop(sz),\n        transforms.RandomHorizontalFlip(),\n        transforms.ToTensor(),\n#         transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])\n    ])\n\n\ntrain_ds = CDataset(train, img_dir, class_df, class_to_idx, transform=transform)\ntest_ds = CDataset(test, img_dir, class_df, class_to_idx, transform=transform)\ntrain_dl = DataLoader(train_ds, batch_size=batch_size,)\ntest_dl = DataLoader(test_ds, batch_size=batch_size,)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"50a2c6b58dda63aefd985882075674e0e3e8f02c"},"cell_type":"markdown","source":"Lets plot some images"},{"metadata":{"trusted":true,"_uuid":"b70b14ee2df47272c667c624373ba2f64cdb3849","scrolled":false,"collapsed":true},"cell_type":"code","source":"image, klass = next(iter(train_dl))\nfig, axes = plt.subplots(2, 4, figsize=(12, 8))\nfor i,ax in enumerate(axes.flat):\n    ima=image[i].numpy().transpose((1, 2, 0))\n    b = idx_to_class[klass[i]]\n    ax = show_img(ima, ax=ax)\n    draw_text(ax, (0,0), b)\nplt.tight_layout()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"ffe6c2527827a5c2c9de39e0572cda29ae7f1721"},"cell_type":"markdown","source":"Lets create a trainer"},{"metadata":{"trusted":true,"collapsed":true,"_uuid":"b758af804218c70215b730f589626319f513ecc3"},"cell_type":"code","source":"use_gpu = torch.cuda.is_available()\ndataloaders = {'train': train_dl, 'val':test_dl}\ndef train_model(model, criterion, optimizer, scheduler, num_epochs=25):\n    since = time.time()\n    best_model_wts = copy.deepcopy(model.state_dict())\n    best_acc = 0.0\n\n    for epoch in tqdm(range(num_epochs)):\n        start = time.time()\n        # Each epoch has a training and validation phase\n        for phase in ['train', 'val']:\n            if phase == 'train':\n                scheduler.step()\n                model.train(True)  # Set model to training mode\n            else:\n                model.train(False)  # Set model to evaluate mode\n            running_loss = 0.0\n            running_corrects = 0\n            # Iterate over data.\n            data_loader = dataloaders[phase]\n            for data in tqdm_notebook(data_loader):\n                # get the inputs\n                inputs, labels = data\n                # wrap them in Variable\n                if use_gpu:\n                    inputs = Variable(inputs.cuda(),)\n                    labels = Variable(labels.cuda())\n                else:\n                    inputs, labels = Variable(inputs), Variable(labels)\n\n                # zero the parameter gradients\n                optimizer.zero_grad()\n                # forward\n                outputs = model(inputs)\n                _, preds = torch.max(outputs.data, 1)\n                loss = criterion(outputs, labels)\n                # backward + optimize only if in training phase\n                if phase == 'train':\n                    loss.backward()\n                    optimizer.step()\n                # statistics\n                running_loss += loss.data[0] * inputs.size(0)\n                running_corrects += torch.sum(preds == labels.data)\n\n            epoch_loss = running_loss / len(data_loader.dataset)\n            epoch_acc = running_corrects / len(data_loader.dataset)\n            epoch_time = time.time() - start\n            tqdm.write('{} Loss: {:.4f} Acc: {:.4f} in {:.0f}m {:.0f}s'.format(\n                phase, epoch_loss, epoch_acc, epoch_time // 60, epoch_time % 60))\n            # deep copy the model\n            if phase == 'val' and epoch_acc > best_acc:\n                best_acc = epoch_acc\n                best_model_wts = copy.deepcopy(model.state_dict())\n        \n        tqdm.write('')\n\n    time_elapsed = time.time() - since\n    print('Training complete in {:.0f}m {:.0f}s'.format(\n        time_elapsed // 60, time_elapsed % 60))\n    print('Best val Acc: {:4f}'.format(best_acc))\n\n    # load best model weights\n    model.load_state_dict(best_model_wts)\n    return model","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"30ceed4ab9d44bc3bb19b9582fcfffcab19abd01"},"cell_type":"markdown","source":"We are using resnet152. \nWe are using `requeires_grad=False` to tell trainer not to train those layers.\nLets train it. "},{"metadata":{"trusted":true,"scrolled":true,"_uuid":"f6bb347d8828bf1e22b165db65f096b8767b4d5e","collapsed":true},"cell_type":"code","source":"model_ft = torchvision.models.resnet152(pretrained=True)\nfor param in model_ft.parameters():\n    param.requires_grad = False\n\nnum_ftrs = model_ft.fc.in_features\nmodel_ft.fc = nn.Linear(num_ftrs, len(class_to_idx))\n# for param in model_ft.parameters():\n#     print(param.requires_grad)\nmodel_ft = model_ft.cuda(0)\ncriterion = nn.CrossEntropyLoss()\n# Observe that all parameters are being optimized\noptimizer_ft = optim.SGD(model_ft.fc.parameters(), lr=0.001, momentum=0.9)\n# Decay LR by a factor of 0.1 every 7 epochs\nexp_lr_scheduler = lr_scheduler.StepLR(optimizer_ft, step_size=7, gamma=0.1)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"e8c2498159e2ba15e35c30ebb64b09cd7c370b99","scrolled":true,"collapsed":true},"cell_type":"code","source":"model_ft = train_model(model_ft, criterion, optimizer_ft, exp_lr_scheduler, num_epochs=25)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d191389e0c159f4a9e982392fd0f330f57de13a4"},"cell_type":"markdown","source":"I stopped because validation accuracy is not improving. "},{"metadata":{"trusted":true,"collapsed":true,"_uuid":"89cff1655a8f50d6a6ac665d9a09ffe46fd5cd67"},"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.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}