{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load in \n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the \"../input/\" directory.\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# Any results you write to the current directory are saved as output.","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# RaLSGAN on DOG images\nWe are implementing Relativistic Average Least Square GAN on dog images which is hosted on kaggle. Dont get scared by the title. Basically we are implementing DCGAN or Deep Convolution GAN on the dog dataset. However it is inefficient and will take a lot of time and memory. There are several key things to take care about - \n+ No fully connected layers in both the networks\n+ ReLU for generator Leaky for Discriminator\n+ Batch Norm between layers ensuring no mode collapse\n+ Instead of pooling, strided conv(discriminator) and fractional strided conv(generator)\n\nNow we use an improved loss function. Till now we have ensured that the generator improves the probability to fool the discriminator. However, in order to stabilize it, we introduce RaLSGAN which is an improvement over the loss function. Now what we do is that we also decrease the probability of a real image being real. This not only stabilizes it but reduces training time as it doesnt require multiple updates on discriminator for a single update on generator. We are also able to scale easily from 64x64 to 256x256. Read more [here](https://www.kaggle.com/c/generative-dog-images/discussion/99485)"},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"from __future__ import print_function\nimport time\nimport torch\nimport torch.nn as nn\nimport torch.nn.parallel\nimport torch.optim as optim\nimport torch.utils.data\nimport torchvision.utils as vutils\nfrom torch.autograd import Variable\nimport torch.nn.functional as F\nfrom torchvision import datasets, transforms\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision.utils import save_image\nimport matplotlib.pyplot as plt\nfrom tqdm import tqdm_notebook as tqdm\nfrom PIL import Image","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import zipfile\n\n\n\n# Will unzip the files so that you can see them..\nwith zipfile.ZipFile(\"../input/generative-dog-images/all-dogs.zip\",\"r\") as z:\n    z.extractall(\".\")\n# !ls ../input/generative-dog-images/","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"images = os.listdir('all-dogs/')\nfig, axes = plt.subplots(3,3,figsize=(12,10))\nfor indx, axis in enumerate(axes.flatten()):\n#     rnd_indx = np.random.randint(0, len(os.listdir(PATH)))\n    img = plt.imread(\"all-dogs/\"+images[indx])\n    imgplot = axis.imshow(img)\n    axis.set_title(images[indx])\n    axis.set_axis_off()\nplt.tight_layout(rect=[0, 0.03, 1, 0.95])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Insights\nAlthough it is not very important but the dataset contains imbalanced classes of dogs as well as images with dogs having distinguishible background. In some of the images, there are humans holding the dogs. You can read more about them [here](https://www.kaggle.com/witold1/quick-data-explanation-and-eda)"},{"metadata":{"trusted":true},"cell_type":"code","source":"class DogDataset(Dataset):\n    def __init__(self, img_dir, transform1=None, transform2=None):\n    \n        self.img_dir = img_dir\n        self.img_names = os.listdir(img_dir)\n        self.transform1 = transform1\n        self.transform2 = transform2\n        \n        self.imgs = []\n        for img_name in self.img_names:\n            img = Image.open(os.path.join(img_dir, img_name))\n            \n            if self.transform1 is not None:\n                img = self.transform1(img)\n                \n            self.imgs.append(img)\n\n    def __getitem__(self, index):\n        img = self.imgs[index]\n        \n        if self.transform2 is not None:\n            img = self.transform2(img)\n        \n        return img\n\n    def __len__(self):\n        return len(self.imgs)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We read the entire data into RAM for faster I/O. Therefore it will take time initially. Note: This is not desirable"},{"metadata":{"trusted":true},"cell_type":"code","source":"batch = 32\nimg_size = 64\ntransform1 = transforms.Compose([transforms.Resize(64),\n                                transforms.CenterCrop(64)])\n\n# Data augmentation and converting to tensors\nrandom_transforms = [transforms.RandomRotation(degrees=5)]\ntransform2 = transforms.Compose([transforms.RandomHorizontalFlip(p=0.5),\n                                 transforms.RandomApply(random_transforms, p=0.3), \n                                 transforms.ToTensor(),\n                                 transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])\n                                 \ntrain_dataset = DogDataset(img_dir='all-dogs/',\n                           transform1=transform1,\n                           transform2=transform2)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_loader = torch.utils.data.DataLoader(train_dataset, shuffle=True,\n                                           batch_size=batch, num_workers=4)\nimgs = next(iter(train_loader))\nimgs = imgs.numpy().transpose(0,2,3,1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Images after augmentation and all the changes\nfig = plt.figure(figsize=(25, 16))\nfor ii, img in enumerate(imgs):\n    ax = fig.add_subplot(4, 8, ii + 1, xticks=[], yticks=[])\n    \n    plt.imshow((img+1)/2)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"lr = 2e-4\nbeta1 = 0.5\n\n\n# Note in standard gan, it would have been 1\nreal_label = 0.9\nfake_label = 0\n# size of the latent space\nnz = 128","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Models"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Special exception keeping leaky relu as its performing well\nclass Generator(nn.Module):\n    def __init__(self,nz=128,channels=3):\n        super(Generator,self).__init__()\n        self.nz = nz\n        self.channels = channels\n        self.kernel = 4\n        self.stride = 2\n        self.padding = 1\n        self.hidden0 = nn.Sequential(nn.ConvTranspose2d(self.nz,1024,kernel_size = self.kernel,stride = self.stride,padding=0,bias=False),\n                                    nn.BatchNorm2d(1024),\n                                    nn.LeakyReLU(0.2,inplace=True))\n        self.hidden1 = nn.Sequential(nn.ConvTranspose2d(1024,512,kernel_size = self.kernel,stride = self.stride,padding=self.padding,bias=False),\n                                    nn.BatchNorm2d(512),\n                                    nn.LeakyReLU(0.2,inplace=True))\n        self.hidden2 = nn.Sequential(nn.ConvTranspose2d(512,256,kernel_size = self.kernel,stride = self.stride,padding=self.padding,bias=False),\n                                    nn.BatchNorm2d(256),\n                                    nn.LeakyReLU(0.2,inplace=True),\n                                    nn.Dropout(p=0.25))\n        self.hidden3 = nn.Sequential(nn.ConvTranspose2d(256,128,kernel_size = self.kernel,stride = self.stride,padding=self.padding,bias=False),\n                                    nn.BatchNorm2d(128),\n                                    nn.LeakyReLU(0.2,inplace=True),\n                                    nn.Dropout(0.25))\n        self.hidden4 = nn.Sequential(nn.ConvTranspose2d(128,64,kernel_size = self.kernel,stride = self.stride,padding=self.padding,bias=False),\n                                    nn.BatchNorm2d(64),\n                                    nn.LeakyReLU(0.2,inplace=True),\n                                    nn.Dropout(0.25))\n        self.output = nn.Sequential(nn.ConvTranspose2d(64,self.channels,kernel_size = 3,stride = 1,padding=1),\n                                    nn.Tanh())\n    def forward(self,z):\n        z = z.view(-1,self.nz,1,1)\n        z = self.hidden0(z)\n        z = self.hidden1(z)\n        z = self.hidden2(z)\n        z = self.hidden3(z)\n        z = self.hidden4(z)\n        z = self.output(z)\n        return z\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Typically in DCGAN, the last layer is the FCNN with sigmoid, however in this case its not working\nclass Discriminator(nn.Module):\n    def __init__(self,channels=3):\n        super(Discriminator,self).__init__()\n        \n        self.channels = channels\n        self.kernel = 4\n        self.stride = 2\n        self.padding = 1\n        self.hidden0 = nn.Sequential(nn.Conv2d(self.channels,32,kernel_size=self.kernel,stride=self.stride,padding=self.padding,bias=False),\n                                    nn.LeakyReLU(0.2,inplace=True))\n        self.hidden1 = nn.Sequential(nn.Conv2d(32,64,kernel_size=self.kernel,stride=self.stride,padding=self.padding,bias=False),\n                                    nn.LeakyReLU(0.2,inplace=True))\n        self.hidden2 = nn.Sequential(nn.Conv2d(64,128,kernel_size=self.kernel,stride=self.stride,padding=self.padding,bias=False),\n                                    nn.BatchNorm2d(128),\n                                     nn.LeakyReLU(0.2,inplace=True))\n        self.hidden3 = nn.Sequential(nn.Conv2d(128,256,kernel_size=self.kernel,stride=self.stride,padding=self.padding,bias=False),\n                                    nn.BatchNorm2d(256),\n                                     nn.LeakyReLU(0.2,inplace=True))\n        self.output = nn.Sequential(nn.Conv2d(256,1,kernel_size=self.kernel,stride=1,padding=0,bias=False),\n                                   )\n        self.out = nn.Sequential(\n            nn.Linear(256*4*4, 1),\n            nn.Sigmoid(),\n        )\n    def forward(self,x):\n        x = self.hidden0(x)\n        x = self.hidden1(x)\n        x = self.hidden2(x)\n        x = self.hidden3(x)\n        logits = self.output(x)\n        return logits.view(-1,1)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def show_generated_img(title):\n    noise = torch.randn(1, nz, 1, 1, device=device)\n    gen_image = netG(noise).to(\"cpu\").clone().detach().squeeze(0)\n    gen_image = gen_image.numpy().transpose(1, 2, 0)\n    plt.title(title)\n    plt.imshow((gen_image+1)/2)\n    plt.axis('off')\n#     plt.savefig('results/'+title+'.png')\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def weights_init(m):\n    \"\"\"\n    Takes as input a neural network m that will initialize all its weights.\n    \"\"\"\n    classname = m.__class__.__name__\n    if classname.find('Conv') != -1:\n        m.weight.data.normal_(0.0, 0.02)\n    elif classname.find('BatchNorm') != -1:\n        m.weight.data.normal_(1.0, 0.02)\n        m.bias.data.fill_(0)\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\n\nnetG = Generator(nz).to(device)\nnetG.apply(weights_init)\nnetD = Discriminator().to(device)\nnetD = netD.to(device)\n\nn_epochs = 300\ncriterion = nn.BCELoss()\ndoptim = optim.Adam(netD.parameters(), lr=2*lr, betas=(beta1, 0.999))\ngoptim = optim.Adam(netG.parameters(), lr=lr, betas=(beta1, 0.999))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for epoch in range(n_epochs):\n    \n    for ii, real_images in tqdm(enumerate(train_loader), total=len(train_loader)):\n        ############################\n        # (1) Update D network\n        ###########################\n        netD.zero_grad()\n        real_images = real_images.to(device)\n        batch_size = real_images.size(0)\n        labels = torch.full((batch_size, 1), real_label, device=device)\n        outputR = netD(real_images)\n        noise = torch.randn(batch_size, nz, 1, 1, device=device)\n        fake = netG(noise)\n        outputF = netD(fake.detach())\n        errD = (torch.mean((outputR - torch.mean(outputF) - labels) ** 2) + \n                torch.mean((outputF - torch.mean(outputR) + labels) ** 2))/2\n        errD.backward(retain_graph=True)\n        doptim.step()\n        ############################\n        # (2) Update G network\n        ###########################\n        netG.zero_grad()\n        outputF = netD(fake)   \n        errG = (torch.mean((outputR - torch.mean(outputF) + labels) ** 2) +\n                torch.mean((outputF - torch.mean(outputR) - labels) ** 2))/2\n        errG.backward()\n        goptim.step()\n        \n        if (ii+1) % (len(train_loader)//2) == 0:\n            print('[%d/%d][%d/%d] Loss_D: %.4f Loss_G: %.4f'\n                  % (epoch + 1, epochs, ii+1, len(train_loader),\n                     errD.item(), errG.item()))\n\n    show_generated_img('%s_image'%epoch)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Visualize Generated Results"},{"metadata":{"trusted":true},"cell_type":"code","source":"gen_z = torch.randn(32, nz, 1, 1, device=device)\ngen_images = (netG(gen_z).to(\"cpu\").clone().detach() + 1)/2\ngen_images = gen_images.numpy().transpose(0, 2, 3, 1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = plt.figure(figsize=(25, 16))\nfor ii, img in enumerate(gen_images):\n    ax = fig.add_subplot(4, 8, ii + 1, xticks=[], yticks=[])\n    plt.imshow(img)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Make Prediction"},{"metadata":{"trusted":true},"cell_type":"code","source":"if not os.path.exists('../output_images'):\n    os.mkdir('../output_images')\nim_batch_size = 50\nn_images=100\nfor i_batch in range(0, n_images, im_batch_size):\n    gen_z = torch.randn(im_batch_size, nz, 1, 1, device=device)\n    gen_images = (netG(gen_z) + 1)/2\n    images = gen_images.to(\"cpu\").clone().detach()\n    images = images.numpy().transpose(0, 2, 3, 1)\n    for i_image in range(gen_images.size(0)):\n        save_image(gen_images[i_image, :, :, :], os.path.join('../output_images', f'image_{i_batch+i_image:05d}.png'))\n\n\nimport shutil\nshutil.make_archive('images', 'zip', '../output_images')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","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":4}