{"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_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Setup","metadata":{}},{"cell_type":"code","source":"import os\nimport numpy as np\nimport torch\nimport torch.nn as nn\nfrom torch.nn.utils import clip_grad_norm_\n\nfrom torch.utils.data import Dataset, DataLoader\nimport torchvision.transforms as transforms\nfrom tqdm.notebook import tqdm\n\nimport matplotlib.pyplot as plt\nfrom PIL import Image\n\nimport shutil","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-06-16T10:19:12.766037Z","iopub.execute_input":"2022-06-16T10:19:12.766427Z","iopub.status.idle":"2022-06-16T10:19:12.773651Z","shell.execute_reply.started":"2022-06-16T10:19:12.76637Z","shell.execute_reply":"2022-06-16T10:19:12.772628Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"seed = 142\n\nnp.random.seed(seed)\ntorch.manual_seed(seed)\ntorch.cuda.manual_seed(seed)\ntorch.backends.cudnn.deterministic = True\n\ndevice = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\nprint(device)","metadata":{"execution":{"iopub.status.busy":"2022-06-16T10:19:12.930476Z","iopub.execute_input":"2022-06-16T10:19:12.932813Z","iopub.status.idle":"2022-06-16T10:19:12.944016Z","shell.execute_reply.started":"2022-06-16T10:19:12.932765Z","shell.execute_reply":"2022-06-16T10:19:12.94255Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Data preparation","metadata":{}},{"cell_type":"code","source":"class ImageDataset(Dataset):\n    def __init__(self, data_dir, transforms=None):\n        monet_dir = os.path.join(data_dir, 'monet_jpg')\n        photo_dir = os.path.join(data_dir, 'photo_jpg')\n        \n        self.files_monet = [os.path.join(monet_dir, name) for name in sorted(os.listdir(monet_dir))]\n        self.files_photo = [os.path.join(photo_dir, name) for name in sorted(os.listdir(photo_dir))]\n        \n        self.transforms = transforms\n        \n    def __len__(self):\n        # we know that len(files_monet) = 300 < 7038 = len(files_photo)\n        return len(self.files_monet)\n    \n    def __getitem__(self, index):\n        # we will use only 300 (=len(files_monet)) photos during training\n        # randomly picking them from the first 300 photos\n        random_index = np.random.randint(0, len(self.files_monet))\n        file_monet = self.files_monet[index]\n        file_photo = self.files_photo[random_index]\n        \n        image_monet = Image.open(file_monet)\n        image_photo = Image.open(file_photo)\n        \n        if self.transforms is not None:\n            image_monet = self.transforms(image_monet)\n            image_photo = self.transforms(image_photo)\n        \n        return image_monet, image_photo","metadata":{"execution":{"iopub.status.busy":"2022-06-16T10:19:13.235865Z","iopub.execute_input":"2022-06-16T10:19:13.236137Z","iopub.status.idle":"2022-06-16T10:19:13.246151Z","shell.execute_reply.started":"2022-06-16T10:19:13.236109Z","shell.execute_reply":"2022-06-16T10:19:13.245475Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data_dir = '/kaggle/input/gan-getting-started'\nbatch_size = 5","metadata":{"execution":{"iopub.status.busy":"2022-06-16T10:19:14.431143Z","iopub.execute_input":"2022-06-16T10:19:14.432216Z","iopub.status.idle":"2022-06-16T10:19:14.436799Z","shell.execute_reply.started":"2022-06-16T10:19:14.432162Z","shell.execute_reply":"2022-06-16T10:19:14.43569Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"transforms_ = transforms.Compose([\n    #transforms.Resize((256, 256)), # photos already have the same size\n    transforms.RandomHorizontalFlip(p=0.5),\n    transforms.RandomVerticalFlip(p=0.3),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=(0.5, 0.5, 0.5), std=(0.5, 0.5, 0.5)),\n])\n\ndataloader = DataLoader(\n    ImageDataset(data_dir, transforms=transforms_),\n    batch_size=batch_size,\n    shuffle=True,\n    num_workers=2,\n)","metadata":{"execution":{"iopub.status.busy":"2022-06-16T10:19:31.539464Z","iopub.execute_input":"2022-06-16T10:19:31.540083Z","iopub.status.idle":"2022-06-16T10:19:31.964158Z","shell.execute_reply.started":"2022-06-16T10:19:31.540033Z","shell.execute_reply":"2022-06-16T10:19:31.96347Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def unnorm(img, mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]):\n    for t, m, s in zip(img, mean, std):\n        t.mul_(s).add_(s)\n        \n    return img","metadata":{"execution":{"iopub.status.busy":"2022-06-16T10:19:32.417632Z","iopub.execute_input":"2022-06-16T10:19:32.418469Z","iopub.status.idle":"2022-06-16T10:19:32.422989Z","shell.execute_reply.started":"2022-06-16T10:19:32.41843Z","shell.execute_reply":"2022-06-16T10:19:32.422024Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Build CycleGAN","metadata":{}},{"cell_type":"markdown","source":"## Auxiliary blocks","metadata":{}},{"cell_type":"code","source":"class ConvBlock(nn.Module):\n    def __init__(self, in_channels, out_channels, kernel_size=3, stride=1, padding=0,\n                 transpose=False, use_leaky=True, use_dropout=False, normalize=True):\n        \n        super(ConvBlock, self).__init__()\n        self.block = []\n        \n        if transpose:\n            self.block += [nn.ConvTranspose2d(in_channels, out_channels, kernel_size,\n                                              stride, padding, output_padding=1)]\n        else:\n            self.block += [nn.Conv2d(in_channels, out_channels, kernel_size,\n                                     stride, padding, bias=True)]\n            \n        if normalize:\n            self.block += [nn.InstanceNorm2d(out_channels)]\n            \n        if use_dropout:\n            self.block += [nn.Dropout(0.5)]\n            \n        if use_leaky:\n            self.block += [nn.LeakyReLU(negative_slope=0.2, inplace=True)]\n        else:\n            self.block += [nn.ReLU(inplace=True)]\n            \n        self.block = nn.Sequential(*self.block)\n    \n    \n    def forward(self, x):\n        return self.block(x)\n    \n\n    \nclass ResidualBlock(nn.Module):\n    def __init__(self, channels):\n        super(ResidualBlock, self).__init__()\n        self.block = nn.Sequential(\n            nn.ReflectionPad2d(1),\n            ConvBlock(in_channels=channels, out_channels=channels,\n                      kernel_size=3, use_leaky=False, use_dropout=True),\n            nn.ReflectionPad2d(1),\n            nn.Conv2d(in_channels=channels, out_channels=channels, kernel_size=3),\n            nn.InstanceNorm2d(channels)\n        )\n    \n    \n    def forward(self, x):\n        return x + self.block(x)","metadata":{"execution":{"iopub.status.busy":"2022-06-16T10:19:34.551293Z","iopub.execute_input":"2022-06-16T10:19:34.551577Z","iopub.status.idle":"2022-06-16T10:19:34.565129Z","shell.execute_reply.started":"2022-06-16T10:19:34.551548Z","shell.execute_reply":"2022-06-16T10:19:34.564224Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Define Generator","metadata":{}},{"cell_type":"code","source":"class Generator(nn.Module):\n    def __init__(self, in_channels, out_channels, num_residual_blocks=9):\n        super(Generator, self).__init__()\n        \n        ''' Encoder '''\n        # Inital layer:  3*256*256 -> 64*256*256\n        self.initial = [\n            nn.ReflectionPad2d(in_channels),\n            ConvBlock(in_channels=in_channels, out_channels=64,\n                      kernel_size=2*in_channels+1, use_leaky=False),\n        ]\n        self.initial = nn.Sequential(*self.initial)\n        \n        # Downsampling:  64*256*256 -> 128*128*128 -> 256*64*64\n        self.down = [\n            ConvBlock(in_channels=64, out_channels=128, kernel_size=3,\n                      stride=2, padding=1, use_leaky=False),\n            ConvBlock(in_channels=128, out_channels=256, kernel_size=3,\n                      stride=2, padding=1, use_leaky=False),\n        ]\n        self.down = nn.Sequential(*self.down)\n        \n        \n        \"\"\" Transformer \"\"\"\n        # ResNet:  256*64*64 -> 256*64*64\n        self.transform = [ResidualBlock(256) for _ in range(num_residual_blocks)]\n        self.transform = nn.Sequential(*self.transform)\n        \n        \n        \"\"\" Decoder \"\"\"\n        # Upsampling:  256*64*64 -> 128*128*128 -> 64*256*256\n        self.up = [\n            ConvBlock(in_channels=256, out_channels=128, kernel_size=3, stride=2,\n                      padding=1, transpose=True, use_leaky=False),\n            ConvBlock(in_channels=128, out_channels=64, kernel_size=3, stride=2,\n                      padding=1, transpose=True, use_leaky=False),\n        ]\n        self.up = nn.Sequential(*self.up)\n        \n        # Out layer:  64*256*256 -> 3*256*256\n        self.out = nn.Sequential(\n            nn.ReflectionPad2d(out_channels),\n            nn.Conv2d(in_channels=64, out_channels=out_channels, kernel_size=2*out_channels+1),\n            nn.Tanh()\n        )\n    \n    \n    def forward(self, x):\n        x = self.down(self.initial(x))\n        x = self.transform(x)\n        x = self.out(self.up(x))\n        return x","metadata":{"execution":{"iopub.status.busy":"2022-06-16T10:22:19.702181Z","iopub.execute_input":"2022-06-16T10:22:19.702511Z","iopub.status.idle":"2022-06-16T10:22:19.903171Z","shell.execute_reply.started":"2022-06-16T10:22:19.702474Z","shell.execute_reply":"2022-06-16T10:22:19.902181Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Define Discriminator","metadata":{}},{"cell_type":"code","source":"class Discriminator(nn.Module):\n    def __init__(self, in_channels):\n        super(Discriminator, self).__init__()\n        \n        self.model = nn.Sequential(\n            # 3*256*256 -> 64*128*128 \n            ConvBlock(in_channels=in_channels, out_channels=64, kernel_size=4,\n                      stride=2, padding=1, normalize=False),\n            \n            # 64*128*128 -> 128*64*64\n            ConvBlock(in_channels=64, out_channels=128, kernel_size=4, stride=2, padding=1),\n            \n            # 128*64*64 -> 256*32*32\n            ConvBlock(in_channels=128, out_channels=256, kernel_size=4, stride=2, padding=1),\n            \n            # 256*32*32 -> 512*31*31\n            ConvBlock(in_channels=256, out_channels=512, kernel_size=4, stride=1, padding=1),\n            \n            # 512*31*31 -> 1*30*30\n            nn.Conv2d(in_channels=512, out_channels=1, kernel_size=4, stride=1, padding=1),\n        )\n        \n        \n    def forward(self, x):    \n        return self.model(x)","metadata":{"execution":{"iopub.status.busy":"2022-06-16T10:22:58.730584Z","iopub.execute_input":"2022-06-16T10:22:58.730875Z","iopub.status.idle":"2022-06-16T10:22:58.739803Z","shell.execute_reply.started":"2022-06-16T10:22:58.730844Z","shell.execute_reply":"2022-06-16T10:22:58.738962Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Training tools","metadata":{}},{"cell_type":"markdown","source":"## Model initialization","metadata":{}},{"cell_type":"code","source":"generator_monet2photo = Generator(in_channels=3, out_channels=3, num_residual_blocks=9).to(device)\ngenerator_photo2monet = Generator(in_channels=3, out_channels=3, num_residual_blocks=9).to(device)\n\ndiscriminator_monet = Discriminator(in_channels=3).to(device)\ndiscriminator_photo = Discriminator(in_channels=3).to(device)","metadata":{"execution":{"iopub.status.busy":"2022-06-12T17:58:18.903774Z","iopub.execute_input":"2022-06-12T17:58:18.906153Z","iopub.status.idle":"2022-06-12T17:58:22.276752Z","shell.execute_reply.started":"2022-06-12T17:58:18.906113Z","shell.execute_reply":"2022-06-12T17:58:22.276021Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Define Losses","metadata":{}},{"cell_type":"code","source":"criterion_GAN = nn.MSELoss()\ncriterion_cycle = nn.L1Loss()\ncriterion_identity = nn.L1Loss()","metadata":{"execution":{"iopub.status.busy":"2022-06-12T17:58:22.277963Z","iopub.execute_input":"2022-06-12T17:58:22.27825Z","iopub.status.idle":"2022-06-12T17:58:22.283183Z","shell.execute_reply.started":"2022-06-12T17:58:22.278205Z","shell.execute_reply":"2022-06-12T17:58:22.282474Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Define Optimizers","metadata":{}},{"cell_type":"code","source":"lr = 2e-4\nb1 = 0.5\nb2 = 0.999\n\noptim_generators = torch.optim.Adam(\n    list(generator_monet2photo.parameters()) + list(generator_photo2monet.parameters()),\n    lr=lr, betas=(b1, b2)\n)\n\noptim_discriminators = torch.optim.Adam(\n    list(discriminator_monet.parameters()) + list(discriminator_photo.parameters()),\n    lr=lr, betas=(b1, b2)\n)","metadata":{"execution":{"iopub.status.busy":"2022-06-12T17:58:22.284465Z","iopub.execute_input":"2022-06-12T17:58:22.284935Z","iopub.status.idle":"2022-06-12T17:58:22.297137Z","shell.execute_reply.started":"2022-06-12T17:58:22.284898Z","shell.execute_reply":"2022-06-12T17:58:22.29639Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Define Learning rate schedulers","metadata":{}},{"cell_type":"code","source":"num_epochs = 80\ndecay_epoch = 25\n\nlr_sched_step = lambda epoch: 1 - max(0, epoch - decay_epoch) / (num_epochs - decay_epoch)\n\nlr_sched_generators = torch.optim.lr_scheduler.LambdaLR(optim_generators, lr_lambda=lr_sched_step)\nlr_sched_discriminators = torch.optim.lr_scheduler.LambdaLR(optim_discriminators, lr_lambda=lr_sched_step)","metadata":{"execution":{"iopub.status.busy":"2022-06-12T17:58:22.300289Z","iopub.execute_input":"2022-06-12T17:58:22.300518Z","iopub.status.idle":"2022-06-12T17:58:22.30851Z","shell.execute_reply.started":"2022-06-12T17:58:22.300484Z","shell.execute_reply":"2022-06-12T17:58:22.307635Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Auxiliary tools","metadata":{}},{"cell_type":"code","source":"class History:\n    def __init__(self):\n        self.generators_loss = []\n        self.discriminators_loss = []\n    \n    def update(self, gen_loss, discr_loss):\n        self.generators_loss.append(gen_loss)\n        self.discriminators_loss.append(discr_loss)\n        \n    def show(self, title='Losses'):\n        fig = plt.figure(figsize=(20, 8))\n        plt.title(title)\n        plt.plot(self.generators_loss, 'o-', color='r',\n                 linewidth=2, markersize=3, label='Generators Loss')\n        plt.plot(self.discriminators_loss, 'o-', color='b',\n                 linewidth=2, markersize=3, label='Discriminators Loss')\n        plt.legend(loc='best')\n        plt.xlabel('Epochs')\n        plt.ylabel('Loss')\n        plt.grid(True)\n        plt.show()","metadata":{"execution":{"iopub.status.busy":"2022-06-12T17:58:22.309796Z","iopub.execute_input":"2022-06-12T17:58:22.310794Z","iopub.status.idle":"2022-06-12T17:58:22.320845Z","shell.execute_reply.started":"2022-06-12T17:58:22.310752Z","shell.execute_reply":"2022-06-12T17:58:22.320101Z"}},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We keep a buffer to store the 50 recently generated images and randomly update this buffer during training. Images from this buffer are used to feed discriminators. It allows us to prevent the model from changing strongly from iteration to iteration and diminish oscillations.","metadata":{}},{"cell_type":"code","source":"class Buffer:\n    def __init__(self, max_images=50):\n        self.max_images = max_images\n        self.images = []\n        \n    def update(self, images):\n        images = images.detach().cpu().data.numpy()\n        for image in images:\n            if len(self.images) < self.max_images:\n                self.images.append(image)\n            else:\n                if np.random.rand() > 0.5:\n                    index = np.random.randint(0, self.max_images)\n                    self.images[index] = image\n\n    def sample(self, num_images):\n        samples = np.array([self.images[np.random.randint(0, len(self.images))]\n                            for _ in range(num_images)])\n        return torch.tensor(samples)","metadata":{"execution":{"iopub.status.busy":"2022-06-12T17:58:22.324166Z","iopub.execute_input":"2022-06-12T17:58:22.324406Z","iopub.status.idle":"2022-06-12T17:58:22.333126Z","shell.execute_reply.started":"2022-06-12T17:58:22.32438Z","shell.execute_reply":"2022-06-12T17:58:22.33221Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def update_req_grad(models, requires_grad=True):\n    for model in models:\n        for param in model.parameters():\n            param.requires_grad = requires_grad","metadata":{"execution":{"iopub.status.busy":"2022-06-12T17:58:22.335284Z","iopub.execute_input":"2022-06-12T17:58:22.335782Z","iopub.status.idle":"2022-06-12T17:58:22.340453Z","shell.execute_reply.started":"2022-06-12T17:58:22.335583Z","shell.execute_reply":"2022-06-12T17:58:22.33966Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Training CycleGAN","metadata":{}},{"cell_type":"code","source":"history = History()\nbuffer_monet = Buffer()\nbuffer_photo = Buffer()\n\nfor epoch in range(num_epochs):\n    avg_generators_loss = 0\n    avg_discriminators_loss = 0\n    \n    for i, (real_monet, real_photo) in enumerate(tqdm(dataloader, leave=False, total=len(dataloader))):\n        real_monet, real_photo = real_monet.to(device), real_photo.to(device)\n                \n        \"\"\" Train Generators \"\"\"\n        # switching models parameters so that only generators are trained\n        update_req_grad([generator_monet2photo, generator_photo2monet], True)\n        update_req_grad([discriminator_monet, discriminator_photo], False)\n        \n        # zero the parameters gradients\n        optim_generators.zero_grad()\n        \n        # forward-pass\n        fake_photo = generator_monet2photo(real_monet)\n        fake_monet = generator_photo2monet(real_photo)\n        \n        cycle_photo = generator_monet2photo(fake_monet)\n        cycle_monet = generator_photo2monet(fake_photo)\n        \n        identity_photo = generator_monet2photo(real_photo)\n        identity_monet = generator_photo2monet(real_monet)\n        \n        # update photos that are used to feed up discriminators\n        buffer_photo.update(fake_photo)\n        buffer_monet.update(fake_monet)\n        \n        # discriminators outputs that are used in adversarial loss\n        discriminator_outputs_photo = discriminator_photo(fake_photo)\n        discriminator_outputs_monet = discriminator_monet(fake_monet)\n        \n        # labels that are used as ground truth\n        labels_real = torch.ones(discriminator_outputs_monet.size()).to(device)\n        labels_fake = torch.zeros(discriminator_outputs_monet.size()).to(device)\n        \n        # adversarial loss - enforces that the generated output be of the appropriate domain\n        loss_GAN_monet2photo = criterion_GAN(discriminator_outputs_photo, labels_real)\n        loss_GAN_photo2monet = criterion_GAN(discriminator_outputs_monet, labels_real)\n        loss_GAN = (loss_GAN_monet2photo + loss_GAN_photo2monet) / 2\n        \n        # cycle consistency loss - enforces that the input and output are recognizably the same\n        loss_cycle_photo = criterion_cycle(cycle_photo, real_photo)\n        loss_cycle_monet = criterion_cycle(cycle_monet, real_monet)\n        loss_cycle = (loss_cycle_photo + loss_cycle_monet) / 2\n        \n        # identity mapping loss - helps preserve the color of the input images\n        loss_identity_photo = criterion_identity(identity_photo, real_photo)\n        loss_identity_monet = criterion_identity(identity_monet, real_monet)\n        loss_identity = (loss_identity_photo + loss_identity_monet) / 2\n        \n        # total loss\n        loss_generators_total = loss_GAN + 10 * loss_cycle + 5 * loss_identity\n        \n        # backward-pass\n        loss_generators_total.backward()\n        optim_generators.step()\n        \n        # limiting gradient norms - if they exceed 100, something went wrong\n        clip_grad_norm_(generator_photo2monet.parameters(), 100)\n        clip_grad_norm_(generator_monet2photo.parameters(), 100)\n        \n        \n        \"\"\" Train Discriminators \"\"\"\n        # switching models parameters so that only discriminators are trained\n        update_req_grad([discriminator_monet, discriminator_photo], True)\n        update_req_grad([generator_monet2photo, generator_photo2monet], False)\n        \n        # zero the parameters gradients\n        optim_discriminators.zero_grad()\n        \n        # sample images from 50 stored\n        fake_photo = buffer_photo.sample(num_images=batch_size).to(device)\n        fake_monet = buffer_monet.sample(num_images=batch_size).to(device)\n        \n        # making labels noisy for discriminators so that they don't prevail over generators\n        threshold = min(1, 0.85 + (1 - 0.85) * epoch / (num_epochs // 2))\n        noisy_labels_real = (torch.rand(discriminator_outputs_monet.size()) < threshold).float().to(device)\n        \n        # forward-pass + losses\n        loss_real_photo = criterion_GAN(discriminator_photo(real_photo), noisy_labels_real)\n        loss_fake_photo = criterion_GAN(discriminator_photo(fake_photo.detach()), labels_fake)\n        loss_photo = (loss_real_photo + loss_fake_photo) / 2\n        \n        loss_real_monet = criterion_GAN(discriminator_monet(real_monet), noisy_labels_real)\n        loss_fake_monet = criterion_GAN(discriminator_monet(fake_monet.detach()), labels_fake)\n        loss_monet = (loss_real_monet + loss_fake_monet) / 2\n        \n        loss_discriminators_total = loss_monet + loss_photo\n        \n        # backward-pass\n        loss_discriminators_total.backward()\n        optim_discriminators.step()\n        \n        # clipping gradients to avoid gradients explosion\n        clip_grad_norm_(discriminator_monet.parameters(), 100)\n        clip_grad_norm_(discriminator_photo.parameters(), 100)\n        \n        # updating intermediate results\n        avg_generators_loss += loss_generators_total.item()\n        avg_discriminators_loss += loss_discriminators_total.item()\n        \n    # saving intermediate results\n    avg_generators_loss /= len(dataloader)\n    avg_discriminators_loss /= len(dataloader)\n    history.update(avg_generators_loss, avg_discriminators_loss)\n    \n    # showing intermediate results\n    print(\"Epoch: %d/%d | Generators Loss: %.4f | Discriminators Loss: %.4f\"\n              % (epoch+1, num_epochs, avg_generators_loss, avg_discriminators_loss))\n    \n    # showing generated images\n    if (epoch + 1) % 10 == 0:\n        _, sample_real_photo = next(iter(dataloader))\n        \n        sample_fake_monet = generator_photo2monet(sample_real_photo.to(device)).detach().cpu()\n        \n        num_photos = min(batch_size, 5)\n        plt.figure(figsize=(20, 8))\n        for k in range(num_photos):\n            plt.subplot(2, num_photos, k + 1)\n            plt.imshow(unnorm(sample_real_photo[k]).permute(1, 2, 0))\n            plt.title('Input photo')\n            plt.axis('off')\n\n            plt.subplot(2, num_photos, k + num_photos + 1)\n            plt.imshow(unnorm(sample_fake_monet[k]).permute(1, 2, 0))\n            plt.title('Output image')\n            plt.axis('off')\n        plt.show()\n    \n    lr_sched_generators.step()\n    lr_sched_discriminators.step()","metadata":{"execution":{"iopub.status.busy":"2022-06-12T17:58:22.342137Z","iopub.execute_input":"2022-06-12T17:58:22.342828Z","iopub.status.idle":"2022-06-12T17:58:41.78353Z","shell.execute_reply.started":"2022-06-12T17:58:22.342789Z","shell.execute_reply":"2022-06-12T17:58:41.781046Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"history.show()","metadata":{"execution":{"iopub.status.busy":"2022-06-12T17:58:41.786906Z","iopub.status.idle":"2022-06-12T17:58:41.78933Z","shell.execute_reply.started":"2022-06-12T17:58:41.789075Z","shell.execute_reply":"2022-06-12T17:58:41.789105Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Create submission file","metadata":{}},{"cell_type":"code","source":"photo_dir = os.path.join(data_dir, 'photo_jpg')\nfiles = [os.path.join(photo_dir, name) for name in os.listdir(photo_dir)]\nlen(files)","metadata":{"execution":{"iopub.status.busy":"2022-06-12T17:58:41.793446Z","iopub.status.idle":"2022-06-12T17:58:41.795858Z","shell.execute_reply.started":"2022-06-12T17:58:41.795575Z","shell.execute_reply":"2022-06-12T17:58:41.795606Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"save_dir = '../images'\nif not os.path.exists(save_dir):\n    os.makedirs(save_dir)","metadata":{"execution":{"iopub.status.busy":"2022-06-12T17:58:41.799646Z","iopub.status.idle":"2022-06-12T17:58:41.800276Z","shell.execute_reply.started":"2022-06-12T17:58:41.800041Z","shell.execute_reply":"2022-06-12T17:58:41.800067Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# NB: Here we use generator training mode to provide noise in the form of dropout\n\ngenerate_transforms = transforms.Compose([\n    transforms.ToTensor(),\n    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))\n])\n\nfor i in range(0, len(files), batch_size):\n    images = []\n    for j in range(i, min(len(files), i + batch_size)):\n        image = Image.open(files[j])\n        image = generate_transforms(image)\n        images.append(image)\n    real_photo = torch.stack(images, 0)\n    \n    fake_images = generator_photo2monet(real_photo.to(device)).detach().cpu()\n\n    for j in range(fake_images.size(0)):\n        img = unnorm(fake_images[j])\n        img = transforms.ToPILImage()(img).convert(\"RGB\")\n        img.save(os.path.join(save_dir, str(i + j + 1) + \".jpg\"))","metadata":{"execution":{"iopub.status.busy":"2022-06-12T17:58:41.801479Z","iopub.status.idle":"2022-06-12T17:58:41.802088Z","shell.execute_reply.started":"2022-06-12T17:58:41.80184Z","shell.execute_reply":"2022-06-12T17:58:41.801864Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"shutil.make_archive(\"/kaggle/working/images\", 'zip', \"/kaggle/images\")","metadata":{"execution":{"iopub.status.busy":"2022-06-12T17:58:41.809498Z","iopub.status.idle":"2022-06-12T17:58:41.810151Z","shell.execute_reply.started":"2022-06-12T17:58:41.809892Z","shell.execute_reply":"2022-06-12T17:58:41.809918Z"},"trusted":true},"execution_count":null,"outputs":[]}]}