{"cells":[{"metadata":{"trusted":true},"cell_type":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nfrom PIL import Image\nfrom matplotlib import pyplot as plt\nfrom skimage import io, transform\nfrom collections import OrderedDict\nfrom tqdm import tqdm, tqdm_notebook\n\nimport os\nprint(os.listdir(\"../input\"))\nfrom pathlib import Path\n# Any results you write to the current directory are saved as output.\n\nimport xml.etree.ElementTree as ET\n\n#%matplotlib inline\nimport argparse\nimport os\nimport random\nimport torch\nimport torch.nn as nn\nimport torch.nn.parallel\nimport torch.backends.cudnn as cudnn\nimport torch.optim as optim\nimport torch.utils.data\nfrom torchvision import transforms, utils\nimport torchvision.datasets as dset\nimport torchvision.transforms as transforms\nimport torchvision.utils as vutils\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport matplotlib.animation as animation\nfrom IPython.display import HTML\nfrom torch.utils.data import Dataset, DataLoader\n# Set random seem for reproducibility\nmanualSeed = 999\n#manualSeed = random.randint(1, 10000) # use if you want new results\nprint(\"Random Seed: \", manualSeed)\nrandom.seed(manualSeed)\ntorch.manual_seed(manualSeed)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"anot_base = Path('../input/annotation/Annotation/')\nannot = os.listdir(anot_base)\ncrops=OrderedDict()\nfor breed in tqdm_notebook(annot):\n    files = os.listdir(anot_base/breed)\n    for file in files:\n        req = ET.parse(anot_base/breed/file)\n        res=req.getiterator('bndbox')[0].getchildren()\n        xmin=int(res[0].text)\n        ymin=int(res[1].text)\n        xmax=int(res[2].text)\n        ymax=int(res[3].text)\n        crops[file]=(xmin, ymin, xmax, ymax)\n        \nprint(len(crops))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"doc_path = '../input/annotation/Annotation/n02092002-Scottish_deerhound/n02092002_5377'\nreq = ET.parse(doc_path)\nnme=list(crops.keys())[789]\nimg=io.imread('../input/all-dogs/all-dogs/'+nme+'.jpg')\nplt.imshow(img)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.imshow(img[crops[nme][1]:crops[nme][3], crops[nme][0]:crops[nme][2]])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Root directory for dataset\ndataroot = \"../input/all-dogs/all-dogs/\"\n# Number of workers for dataloader\nworkers = 2\n# Batch size during training\nbatch_size = 128\n# Spatial size of training images. All images will be resized to this\n#   size using a transformer.\nimage_size = 64\n# Number of channels in the training images. For color images this is 3\nnc = 3\n# Size of z latent vector (i.e. size of generator input)\nnz = 100\n# Size of feature maps in generator\nngf = 64\n# Size of feature maps in discriminator\nndf = 64\n# Number of training epochs\nnum_epochs = 50\n# Learning rate for optimizers\nlr = 0.0002\n# Beta1 hyperparam for Adam optimizers\nbeta1 = 0.5\n# Number of GPUs available. Use 0 for CPU mode.\nngpu = 1","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class GenerativeDogs(Dataset):\n    \"\"\"Face Landmarks dataset.\"\"\"\n\n    def __init__(self, crops, transform=None):\n        \"\"\"\n        Args:\n            csv_file (string): Path to the csv file with annotations.\n            root_dir (string): Directory with all the images.\n            transform (callable, optional): Optional transform to be applied\n                on a sample.\n        \"\"\"\n        self.crops = crops\n        self.root_dir = Path('../input/all-dogs/all-dogs/')\n        self.files = os.listdir(self.root_dir)\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.files)\n\n    def _get_image(self, image_path):\n        img = np.array(io.imread(image_path))\n        return img\n    \n    def _crop(self, image, xmin, ymin, xmax, ymax):\n        return image[ymin:ymax,xmin:xmax]\n    \n    def __getitem__(self, idx):\n        file =self.files[idx]\n        igpth = self.root_dir/(file)\n#         img = self._get_image(igpth)\n        cx=self.crops[file.split('.')[0]]\n        xmin, ymin, xmax, ymax = cx\n#         img = self._crop(img, xmin, ymin, xmax, ymax)\n#         img = transform.resize(img, (image_size, image_size))\n#         img = np.moveaxis(img, 2, 0)\n        img=Image.open(igpth)\n        img=img.crop(cx)\n        img=img.resize((image_size, image_size), Image.ANTIALIAS)\n        if self.transform:\n            img = self.transform(img)\n        return img","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dataset=GenerativeDogs(crops, \n                       transform=transforms.Compose([\n                               transforms.Resize(image_size),\n                               transforms.CenterCrop(image_size),\n                               transforms.ToTensor(),\n                               transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225)),\n                           ])\n                      )\ndl=DataLoader(dataset, batch_size=batch_size, shuffle=False)\nbatch_size","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# custom weights initialization called on netG and netD\ndef weights_init(m):\n    classname = m.__class__.__name__\n    if classname.find('Conv') != -1:\n        nn.init.normal_(m.weight.data, 0.0, 0.02)\n    elif classname.find('BatchNorm') != -1:\n        nn.init.normal_(m.weight.data, 1.0, 0.02)\n        nn.init.constant_(m.bias.data, 0)\n        \n        \n#  Generator Code\n\nclass Generator(nn.Module):\n    def __init__(self, ngpu):\n        super(Generator, self).__init__()\n        self.ngpu = ngpu\n        self.main = nn.Sequential(\n            # input is Z, going into a convolution\n            nn.ConvTranspose2d( nz, ngf * 8, 4, 1, 0, bias=False),\n            nn.BatchNorm2d(ngf * 8),\n            nn.ReLU(True),\n            # state size. (ngf*8) x 4 x 4\n            nn.ConvTranspose2d(ngf * 8, ngf * 4, 4, 2, 1, bias=False),\n            nn.BatchNorm2d(ngf * 4),\n            nn.ReLU(True),\n            # state size. (ngf*4) x 8 x 8\n            nn.ConvTranspose2d( ngf * 4, ngf * 2, 4, 2, 1, bias=False),\n            nn.BatchNorm2d(ngf * 2),\n            nn.ReLU(True),\n            # state size. (ngf*2) x 16 x 16\n            nn.ConvTranspose2d( ngf * 2, ngf, 4, 2, 1, bias=False),\n            nn.BatchNorm2d(ngf),\n            nn.ReLU(True),\n            # state size. (ngf) x 32 x 32\n            nn.ConvTranspose2d( ngf, nc, 4, 2, 1, bias=False),\n            nn.Tanh()\n            # state size. (nc) x 64 x 64\n        )\n\n    def forward(self, input):\n        return self.main(input)\n    \nclass Discriminator(nn.Module):\n    def __init__(self, ngpu):\n        super(Discriminator, self).__init__()\n        self.ngpu = ngpu\n        self.main = nn.Sequential(\n            # input is (nc) x 64 x 64\n            nn.Conv2d(nc, ndf, 4, 2, 1, bias=False),\n            nn.LeakyReLU(0.2, inplace=True),\n            # state size. (ndf) x 32 x 32\n            nn.Conv2d(ndf, ndf * 2, 4, 2, 1, bias=False),\n            nn.BatchNorm2d(ndf * 2),\n            nn.LeakyReLU(0.2, inplace=True),\n            # state size. (ndf*2) x 16 x 16\n            nn.Conv2d(ndf * 2, ndf * 4, 4, 2, 1, bias=False),\n            nn.BatchNorm2d(ndf * 4),\n            nn.LeakyReLU(0.2, inplace=True),\n            # state size. (ndf*4) x 8 x 8\n            nn.Conv2d(ndf * 4, ndf * 8, 4, 2, 1, bias=False),\n            nn.BatchNorm2d(ndf * 8),\n            nn.LeakyReLU(0.2, inplace=True),\n            # state size. (ndf*8) x 4 x 4\n            nn.Conv2d(ndf * 8, 1, 4, 1, 0, bias=False),\n            nn.Sigmoid()\n        )\n\n    def forward(self, input):\n        return self.main(input)\n\nNormType = Enum('NormType', 'Batch BatchZero Weight Spectral')\ndef conv_layer(ni:int, nf:int, ks:int=3, stride:int=1, padding:int=None, bias:bool=None, is_1d:bool=False,\n               norm_type:Optional[NormType]=NormType.Batch,  use_activ:bool=True, leaky:float=None,\n               transpose:bool=False, init:Callable=nn.init.kaiming_normal_, self_attention:bool=False):\n    \"Create a sequence of convolutional (`ni` to `nf`), ReLU (if `use_activ`) and batchnorm (if `bn`) layers.\"\n    if padding is None: padding = (ks-1)//2 if not transpose else 0\n    bn = norm_type in (NormType.Batch, NormType.BatchZero)\n    if bias is None: bias = not bn\n    conv_func = nn.ConvTranspose2d if transpose else nn.Conv1d if is_1d else nn.Conv2d\n    conv = init_default(conv_func(ni, nf, kernel_size=ks, bias=bias, stride=stride, padding=padding), init)\n    if   norm_type==NormType.Weight:   conv = weight_norm(conv)\n    elif norm_type==NormType.Spectral: conv = spectral_norm(conv)\n    layers = [conv]\n    if use_activ: layers.append(relu(True, leaky=leaky))\n    if bn: layers.append((nn.BatchNorm1d if is_1d else nn.BatchNorm2d)(nf))\n    if self_attention: layers.append(SelfAttention(nf))\n    return nn.Sequential(*layers)\n\ndef _conv(ni:int, nf:int, ks:int=3, stride:int=1, **kwargs):\n    return conv_layer(ni, nf, ks=ks, stride=stride, **_conv_args, **kwargs)\n\ndef gan_critic(n_channels:int=3, nf:int=128, n_blocks:int=3, p:int=0.15):\n    \"Critic to train a `GAN`.\"\n    layers = [\n        _conv(n_channels, nf, ks=4, stride=2),\n        nn.Dropout2d(p/2),\n        res_block(nf, dense=True,**_conv_args)]\n    nf *= 2 # after dense block\n    for i in range(n_blocks):\n        layers += [\n            nn.Dropout2d(p),\n            _conv(nf, nf*2, ks=4, stride=2, self_attention=(i==0))]\n        nf *= 2\n    layers += [\n        _conv(nf, 1, ks=4, bias=False, padding=0, use_activ=False),\n        Flatten()]\n    return nn.Sequential(*layers)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"gan_critic()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Decide which device we want to run on\ndevice = torch.device(\"cuda:0\" if (torch.cuda.is_available() and ngpu > 0) else \"cpu\")\n\n# Plot some training images\nreal_batch = iter(dl).next()\nplt.figure(figsize=(8,8))\nplt.axis(\"off\")\nplt.title(\"Training Images\")\nplt.imshow(np.transpose(vutils.make_grid(real_batch.to(device)[:64], padding=2, normalize=True).cpu(),(1,2,0)))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"netG = Generator(ngpu).to(device)\n\n# Handle multi-gpu if desired\nif (device.type == 'cuda') and (ngpu > 1):\n    netG = nn.DataParallel(netG, list(range(ngpu)))\n\n# Apply the weights_init function to randomly initialize all weights\n#  to mean=0, stdev=0.2.\nnetG.apply(weights_init)\n\n# Print the model\nprint(netG)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Create the Discriminator\nnetD = Discriminator(ngpu).to(device)\n\n# Handle multi-gpu if desired\nif (device.type == 'cuda') and (ngpu > 1):\n    netD = nn.DataParallel(netD, list(range(ngpu)))\n\n# Apply the weights_init function to randomly initialize all weights\n#  to mean=0, stdev=0.2.\nnetD.apply(weights_init)\n\n# Print the model\nprint(netD)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Initialize BCELoss function\ncriterion = nn.BCELoss()\n\n# Create batch of latent vectors that we will use to visualize\n#  the progression of the generator\nfixed_noise = torch.randn(64, nz, 1, 1, device=device)\n\n# Establish convention for real and fake labels during training\nreal_label = 1\nfake_label = 0\n\n# Setup Adam optimizers for both G and D\n# optimizerD = optim.SGD(netD.parameters(), lr=0.001)\n# Setup Adam optimizers for both G and D\noptimizerD = optim.Adam(netD.parameters(), lr=lr, betas=(beta1, 0.999))\noptimizerG = optim.Adam(netG.parameters(), lr=lr, betas=(beta1, 0.999))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import torch.nn.functional as F\n\nclass FocalLoss(nn.Module):\n    def __init__(self, alpha=1, gamma=2, logits=False, reduce=True):\n        super(FocalLoss, self).__init__()\n        self.alpha = alpha\n        self.gamma = gamma\n        self.logits = logits\n        self.reduce = reduce\n        self.loss = nn.BCELoss()\n    def forward(self, inputs, targets):\n#         if self.logits:\n#             BCE_loss = nn.BCELoss()(inputs, targets, reduce=False)\n#         else:\n#             BCE_loss = F.binary_cross_entropy(inputs, targets, reduce=False)\n        BCE_loss = self.loss(inputs, targets)\n        pt = torch.exp(-BCE_loss)\n        F_loss = self.alpha * (1-pt)**self.gamma * BCE_loss\n\n        if self.reduce:\n            return torch.mean(F_loss)\n        else:\n            return F_loss\n        \nfl = FocalLoss()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Training Loop\n\n# Lists to keep track of progress\nimg_list = []\nG_losses = []\nD_losses = []\niters = 0\n\nprint(\"Starting Training Loop...\")\n# For each epoch\nfor epoch in range(220):\n    # For each batch in the dataloader\n    for i, data in enumerate(dl, 0):\n\n        ############################\n        # (1) Update D network: maximize log(D(x)) + log(1 - D(G(z)))\n        ###########################\n        ## Train with all-real batch\n        netD.zero_grad()\n        # Format batch\n        real_cpu = data.to(device)\n        b_size = real_cpu.size(0)\n        label = torch.full((b_size,), real_label, device=device)\n        # Forward pass real batch through D\n        output = netD(real_cpu).view(-1)\n        # Calculate loss on all-real batch\n        errD_real = fl(output, label)\n        # Calculate gradients for D in backward pass\n        errD_real.backward()\n        D_x = output.mean().item()\n\n        ## Train with all-fake batch\n        # Generate batch of latent vectors\n        noise = torch.randn(b_size, nz, 1, 1, device=device)\n        # Generate fake image batch with G\n        fake = netG(noise)\n        label.fill_(fake_label)\n        # Classify all fake batch with D\n        output = netD(fake.detach()).view(-1)\n        # Calculate D's loss on the all-fake batch\n        errD_fake = criterion(output, label)\n        # Calculate the gradients for this batch\n        errD_fake.backward()\n        D_G_z1 = output.mean().item()\n        # Add the gradients from the all-real and all-fake batches\n        errD = errD_real + errD_fake\n        # Update D\n        optimizerD.step()\n\n        ############################\n        # (2) Update G network: maximize log(D(G(z)))\n        ###########################\n        netG.zero_grad()\n        label.fill_(real_label)  # fake labels are real for generator cost\n        # Since we just updated D, perform another forward pass of all-fake batch through D\n        output = netD(fake).view(-1)\n        # Calculate G's loss based on this output\n        errG = criterion(output, label)\n        # Calculate gradients for G\n        errG.backward()\n        D_G_z2 = output.mean().item()\n        # Update G\n        optimizerG.step()\n\n        # Output training stats\n        if i % 50 == 0:\n            print('[%d/%d][%d/%d]\\tLoss_D: %.4f\\tLoss_G: %.4f\\tD(x): %.4f\\tD(G(z)): %.4f / %.4f'\n                  % (epoch, num_epochs, i, len(dl),\n                     errD.item(), errG.item(), D_x, D_G_z1, D_G_z2))\n\n        # Save Losses for plotting later\n        G_losses.append(errG.item())\n        D_losses.append(errD.item())\n\n        # Check how the generator is doing by saving G's output on fixed_noise\n        if (iters % 500 == 0) or ((epoch == num_epochs-1) and (i == len(dl)-1)):\n            with torch.no_grad():\n                fake = netG(fixed_noise).detach().cpu()\n            img_list.append(vutils.make_grid(fake, padding=2, normalize=True))\n\n        iters += 1\n    os.system('echo {}'.format(epoch))\ntorch.save(netG.state_dict(), './generator_{}.pth'.format(epoch))\ntorch.save(netD.state_dict(), './dicrim_{}.pth'.format(epoch))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize=(10,5))\nplt.title(\"Generator and Discriminator Loss During Training\")\nplt.plot(G_losses,label=\"G\")\nplt.plot(D_losses,label=\"D\")\nplt.xlabel(\"iterations\")\nplt.ylabel(\"Loss\")\nplt.legend()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"markdown","source":"#%%capture\nfig = plt.figure(figsize=(8,8))\nplt.axis(\"off\")\nims = [[plt.imshow(np.transpose(i,(1,2,0)), animated=True)] for i in img_list]\nani = animation.ArtistAnimation(fig, ims, interval=1000, repeat_delay=1000, blit=True)\n\nHTML(ani.to_jshtml())"},{"metadata":{"trusted":true},"cell_type":"code","source":"!mkdir /tmp/output\nfor out in tqdm(range(100)):\n    noise = torch.randn(100, nz, 1, 1, device=device)\n    with torch.no_grad():\n        fake = netG(noise)\n        imgg = np.moveaxis(fake.cpu().numpy(), 1, 3)\n    for i in range(100):\n        io.imsave('/tmp/output/gen_{}_{}.png'.format(out,i), imgg[i])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import glob\nfrom IPython.display import Image, display\nfor imageName in glob.glob('/tmp/output/*.png')[20:30]: #assuming JPG\n    display(Image(filename=imageName))\n    print(imageName)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import shutil\nshutil.make_archive('images', 'zip', '/tmp/output/')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"torch.save(netG.state_dict(), './generator.pth')\ntorch.save(netD.state_dict(), './dicrim.pth')","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}