{"cells":[{"metadata":{"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"# Install Pytorch-Lightning\n!pip install pytorch_lightning","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"%matplotlib inline\nimport numpy as np\nimport pandas as pd\nimport os, glob, time, copy, random, zipfile\nimport matplotlib.pyplot as plt\nfrom PIL import Image\nfrom sklearn.model_selection import train_test_split\nfrom tqdm import tqdm_notebook as tqdm\n\n\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import Dataset, DataLoader\nimport torch.nn.functional as F\nimport torchvision\nfrom torchvision import models, transforms\n\nimport pytorch_lightning as pl\nfrom pytorch_lightning import Trainer\nfrom pytorch_lightning.callbacks import ModelCheckpoint, EarlyStopping","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"torch.__version__","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"---\n## Data Confirming"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Check Current Directory\nos.listdir('../input/dogs-vs-cats-redux-kernels-edition')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Make Directory for extracting from Zip\nos.makedirs('../data', exist_ok=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Train_dir, Test_dir\nbase_dir = '../input/dogs-vs-cats-redux-kernels-edition'\ntrain_dir = '../data/train'\ntest_dir = '../data/test'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Extract All Data From Zip to \"../data\" Directory\nwith zipfile.ZipFile(os.path.join(base_dir, 'train.zip')) as train_zip:\n    train_zip.extractall('../data')\n    \nwith zipfile.ZipFile(os.path.join(base_dir, 'test.zip')) as test_zip:\n    test_zip.extractall('../data')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Check File Name\nos.listdir(train_dir)[:5]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# FilePath List\ntrain_img_path = glob.glob(os.path.join(train_dir, '*.jpg'))\ntest_img_path = glob.glob(os.path.join(test_dir, '*.jpg'))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"img = Image.open(train_img_path[0])\nplt.imshow(img)\nplt.axis('off')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"img = Image.open(test_img_path[0])\nplt.imshow(img)\nplt.axis('off')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Label is contained in filepath\ntrain_img_path[:5]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Image_Id is contained in filepath\ntest_img_path[:5]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Get Label\nprint(train_img_path[0].split('/')[-1].split('.')[0])\n# Get Image_Id\nprint(int(test_img_path[0].split('/')[-1].split('.')[0]))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Number of Train Image\nprint('Train Data Num: ', len(train_img_path))\n# Number of Test Image\nprint('Test Data Num: ', len(test_img_path))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"---\n## Data Augmentation"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Data Augmentation\nclass ImageTransform():\n    def __init__(self, img_size=224, mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)):\n        self.data_transform = {\n            'train': transforms.Compose([\n                transforms.RandomResizedCrop(img_size, scale=(0.5, 1.0)),\n                transforms.RandomHorizontalFlip(),\n                transforms.ToTensor(),\n                transforms.Normalize(mean, std)\n            ]),\n            'val': transforms.Compose([\n                transforms.Resize(256),\n                transforms.CenterCrop(img_size),\n                transforms.ToTensor(),\n                transforms.Normalize(mean, std)\n            ]),\n            'test': transforms.Compose([\n                transforms.Resize(256),\n                transforms.CenterCrop(img_size),\n                transforms.ToTensor(),\n                transforms.Normalize(mean, std)\n            ])\n        }\n        \n    def __call__(self, img, phase):\n        return self.data_transform[phase](img)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"---\n## Dataset"},{"metadata":{"trusted":true},"cell_type":"code","source":"class DogvsCatDataset(Dataset):\n    def __init__(self, file_list, transform=None, phase='train'):    \n        self.file_list = file_list\n        self.transform = transform\n        self.phase = phase\n        \n    def __len__(self):\n        return len(self.file_list)\n    \n    def __getitem__(self, idx):\n        \n        img_path = self.file_list[idx]\n        img = Image.open(img_path)\n        # Transformimg Image\n        img_transformed = self.transform(img, self.phase)\n        \n        # Get Label\n        label = img_path.split('/')[-1].split('.')[0]\n        if label == 'dog':\n            label = 1\n        elif label == 'cat':\n            label = 0\n\n        return img_transformed, label","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"---\n## Pytorch Lightning"},{"metadata":{"trusted":true},"cell_type":"code","source":"class DogCatModelSystem(pl.LightningModule):\n    \n    def __init__(self, img_path, criterion, batch_size, img_size):\n        super(DogCatModelSystem, self).__init__()\n        self.criterion = criterion\n        self.batch_size = batch_size\n        self.img_size = img_size\n        \n        # Load Data  ###############################################################################\n        self.img_path = img_path\n        # Split Train/Val Data\n        self.train_img_path, self.val_img_path = train_test_split(self.img_path, test_size=0.1)\n        # Dataset\n        self.train_dataset = DogvsCatDataset(self.train_img_path, \n                                             ImageTransform(self.img_size), \n                                             phase='train')\n        \n        self.val_dataset = DogvsCatDataset(self.val_img_path, \n                                           ImageTransform(self.img_size), \n                                           phase='val')\n        \n        # Model  ###############################################################################\n        # Pretrained VGG16\n        use_pretrained = True\n        self.net = models.vgg16(pretrained=use_pretrained)\n        # Change Output Size of Last FC Layer (4096 -> 1)\n        self.net.classifier[6] = nn.Linear(in_features=self.net.classifier[6].in_features, out_features=2)\n        # Specify The Layers for updating\n        params_to_update = []\n        update_params_name = ['classifier.6.weight', 'classifier.6.bias']\n\n        for name, param in self.net.named_parameters():\n            if name in update_params_name:\n                param.requires_grad = True\n                params_to_update.append(param)\n            else:\n                param.requires_grad = False\n        # Set Optimizer\n        self.optimizer = optim.SGD(params=params_to_update, lr=0.001, momentum=0.9)\n    \n    # Method  ###############################################################################\n    # Set Train Dataloader\n    @pl.data_loader\n    def train_dataloader(self):\n        '''\n        REQUIRED\n        Set Train Dataloader\n        '''\n        return DataLoader(self.train_dataset, batch_size=self.batch_size,\n                          shuffle=True, num_workers=4, pin_memory=True)\n    \n    # Set Valid Dataloader\n    @pl.data_loader\n    def val_dataloader(self):\n        '''\n        REQUIRED\n        Set Validation Dataloader\n        '''\n        return DataLoader(self.val_dataset, batch_size=self.batch_size,\n                          shuffle=False, num_workers=4, pin_memory=True)\n    \n    def forward(self, x):\n        return self.net(x)\n    \n    # Set optimizer, and schedular\n    def configure_optimizers(self):\n        # [optimizer], [schedular]\n        return [self.optimizer], []\n    \n    # Train Loop\n    def training_step(self, batch, batch_idx):\n        '''\n        REQUIRED\n        batch: Output from DataLoader\n        batch_idx: Index of Batch\n        '''\n        \n        # Output from Dataloader\n        imgs, labels = batch\n        \n        # Prediction\n        preds = self.forward(imgs)\n        # Calc Loss\n        loss = self.criterion(preds, labels)\n        \n        # Calc Correct\n        _, preds = torch.max(preds, 1)\n        correct = torch.sum(preds == labels).float() / preds.size(0)\n        \n        logs = {'train_loss': loss, 'train_correct': correct}\n        \n        return {'loss': loss, 'log': logs, 'progress_bar': logs}\n    \n    # Validation Loop\n    def validation_step(self, batch, batch_idx):\n        '''\n        OPTIONAL\n        SAME AS \"trainning_step\"\n        '''\n        # Output from Dataloader\n        imgs, labels = batch\n        \n        # Prediction\n        preds = self.forward(imgs)\n        # Calc Loss\n        loss = self.criterion(preds, labels)\n        \n        # Calc Correct\n        _, preds = torch.max(preds, 1)\n        correct = torch.sum(preds == labels).float() / preds.size(0)\n        \n        logs = {'val_loss': loss, 'val_correct': correct}\n        \n        return {'val_loss': loss, 'val_correct': correct, 'log': logs, 'progress_bar': logs}\n    \n    # Aggegate Validation Result\n    def validation_end(self, outputs):\n        avg_loss = torch.stack([x['val_loss'] for x in outputs]).mean()\n        avg_correct = torch.stack([x['val_correct'] for x in outputs]).mean()\n        logs = {'avg_val_loss': avg_loss, 'avg_val_correct': avg_correct}\n        torch.cuda.empty_cache()\n\n        return {'avg_val_loss': avg_loss, 'log': logs}","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Config  ################################################\ncriterion = nn.CrossEntropyLoss()\nbatch_size = 32\nimg_size = 224\nepoch = 2\n\n# Set LightningSystem  ################################################\nmodel = DogCatModelSystem(train_img_path, criterion, batch_size, img_size)\n\n# Callbacks  ################################################\n# Save Model\n# checkpoint_callback = ModelCheckpoint(filepath='SAVE_FILE_PATH', monitor='val_loss',\n#                                       save_best_only=True, mode='min', save_weights_only=True)\n# EarlyStopping\n# earlystopping = EarlyStopping(monitor='val_loss', min_delta=0.0, patience=2)\n\n# Trainer  ################################################\ntrainer = Trainer(\n    max_nb_epochs=epoch,                        # Set Num Epoch\n#     default_save_path=output_path,            # Path for save lightning_logs\n#     checkpoint_callback=checkpoint_callback,  # Set Checkpoint-Callback\n#     early_stop_callback=earlystopping,        # Set EarlyStopping-Callback\n#     gpus=[0]                                  # GPU\n)\n\n# Start Training!!  ################################################\ntrainer.fit(model)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"---\n## Prediction"},{"metadata":{"trusted":true},"cell_type":"code","source":"def prediction(test_img_path, model, img_size, device):\n    id_list = []\n    pred_list = []\n\n    with torch.no_grad():\n        for path in tqdm(test_img_path):\n            \n            # Preprocessing  #########################################\n            img = Image.open(path)\n            _id = int(path.split('/')[-1].split('.')[0])\n\n            transform = ImageTransform(img_size)\n            img = transform(img, phase='test')\n            img = img.unsqueeze(0)\n            img = img.to(device)\n            \n            # Predict  ##############################################\n            model.eval()\n\n            outputs = model(img)\n            preds = F.softmax(outputs, dim=1)[:, 1].tolist()\n\n            id_list.append(_id)\n            pred_list.append(preds[0])\n\n    # Result DataFrame\n    res = pd.DataFrame({\n        'id': id_list,\n        'label': pred_list\n    })\n    \n    # Submit\n    res.sort_values(by='id', inplace=True)\n    res.reset_index(drop=True, inplace=True)\n    res.to_csv('submission.csv', index=False)\n    \n    return res","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')\nres = prediction(test_img_path, model, img_size, device)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Visualize Prediction\nid_list = []\nclass_ = {0: 'cat', 1: 'dog'}\nfig, axes = plt.subplots(2, 5, figsize=(20, 12), facecolor='w')\n\nfor ax in axes.ravel():\n    # Select Image\n    i = random.choice(res['id'].values)\n    \n    label = res.loc[res['id'] == i, 'label'].values[0]\n    if label > 0.5:\n        label = 1\n    else:\n        label = 0\n        \n    img_path = os.path.join(test_dir, '{}.jpg'.format(i))\n    img = Image.open(img_path)\n    \n    ax.set_title(class_[label])\n    ax.imshow(img)","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":1}