{"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":"# The implement of CycleGAN","metadata":{"_uuid":"3ba723de-a83f-443f-a11b-0fc995fd6ae9","_cell_guid":"6d86351d-4740-4a60-965f-4f50a44c7893","trusted":true}},{"cell_type":"code","source":"import torch\nfrom torch import nn\nfrom torch.nn import functional as F\nfrom copy import deepcopy\nfrom matplotlib import pyplot as plt","metadata":{"_uuid":"c3342379-9c34-4901-ad32-b2dddb516a45","_cell_guid":"688d1abf-a57f-42f0-bf04-198360d7ebff","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2022-02-17T02:26:48.060027Z","iopub.execute_input":"2022-02-17T02:26:48.060283Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch.utils.data\nfrom torch.nn import functional as F\n \nimport math\nimport torch\nfrom torch.nn.parameter import Parameter\nfrom torch.nn.functional import pad\nfrom torch.nn.modules import Module\nfrom torch.nn.modules.utils import _single, _pair, _triple\n\ndef conv2d_same_padding(input, weight, bias=None, stride=1, padding=1, dilation=1, groups=1, padding_mode = \"constant\"):\n    input_rows = input.size(2)\n    filter_rows = weight.size(2)\n    effective_filter_size_rows = (filter_rows - 1) * dilation[0] + 1\n    out_rows = (input_rows + stride[0] - 1) // stride[0]\n    padding_rows = max(0, (out_rows - 1) * stride[0] +\n                        (filter_rows - 1) * dilation[0] + 1 - input_rows)\n    rows_odd = (padding_rows % 2 != 0)\n    padding_cols = max(0, (out_rows - 1) * stride[0] +\n                        (filter_rows - 1) * dilation[0] + 1 - input_rows)\n    cols_odd = (padding_rows % 2 != 0)\n \n    if rows_odd or cols_odd:\n        input = pad(input, [0, int(cols_odd), 0, int(rows_odd)], mode=padding_mode)\n \n    return F.conv2d(input, weight, bias, stride,\n                  padding=(padding_rows // 2, padding_cols // 2),\n                  dilation=dilation, groups=groups)\n \nclass _ConvNd(Module):\n \n    def __init__(self, in_channels, out_channels, kernel_size, stride,\n                 padding, dilation, transposed, output_padding, groups, bias):\n        super(_ConvNd, self).__init__()\n        if in_channels % groups != 0:\n            raise ValueError('in_channels must be divisible by groups')\n        if out_channels % groups != 0:\n            raise ValueError('out_channels must be divisible by groups')\n        self.in_channels = in_channels\n        self.out_channels = out_channels\n        self.kernel_size = kernel_size\n        self.stride = stride\n        self.padding = padding\n        self.dilation = dilation\n        self.transposed = transposed\n        self.output_padding = output_padding\n        self.groups = groups\n        if transposed:\n            self.weight = Parameter(torch.Tensor(\n                in_channels, out_channels // groups, *kernel_size))\n        else:\n            self.weight = Parameter(torch.Tensor(\n                out_channels, in_channels // groups, *kernel_size))\n        if bias:\n            self.bias = Parameter(torch.Tensor(out_channels))\n        else:\n            self.register_parameter('bias', None)\n        self.reset_parameters()\n \n    def reset_parameters(self):\n        n = self.in_channels\n        for k in self.kernel_size:\n            n *= k\n        stdv = 1. / math.sqrt(n)\n        self.weight.data.uniform_(-stdv, stdv)\n        if self.bias is not None:\n            self.bias.data.uniform_(-stdv, stdv)\n \n    def __repr__(self):\n        s = ('{name}({in_channels}, {out_channels}, kernel_size={kernel_size}'\n             ', stride={stride}')\n        if self.padding != (0,) * len(self.padding):\n            s += ', padding={padding}'\n        if self.dilation != (1,) * len(self.dilation):\n            s += ', dilation={dilation}'\n        if self.output_padding != (0,) * len(self.output_padding):\n            s += ', output_padding={output_padding}'\n        if self.groups != 1:\n            s += ', groups={groups}'\n        if self.bias is None:\n            s += ', bias=False'\n        s += ')'\n        return s.format(name=self.__class__.__name__, **self.__dict__)","metadata":{"_uuid":"b9219f81-30f9-4d2a-be5d-6c8a7d213008","_cell_guid":"b89a50d0-1769-4723-b175-1340e17bcfba","collapsed":false,"jupyter":{"outputs_hidden":false},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Conv2dWithSamePadding(_ConvNd): \n \n    def __init__(self, in_channels, out_channels, kernel_size, stride=1,\n                 padding=0, dilation=1, groups=1, bias=True, padding_mode = \"zero\"):\n        kernel_size = _pair(kernel_size)\n        stride = _pair(stride)\n        padding = _pair(padding)\n        dilation = _pair(dilation)\n        super(Conv2dWithSamePadding, self).__init__(\n            in_channels, out_channels, kernel_size, stride, padding, dilation,\n            False, _pair(0), groups, bias)\n        self.padding_mode = padding_mode\n        if self.padding_mode == \"zero\":\n            self.padding_mode == \"constant\"\n \n    def forward(self, x):\n        return conv2d_same_padding(x, self.weight, self.bias, self.stride,\n                        self.padding, self.dilation, self.groups,self.padding_mode)","metadata":{"_uuid":"e5770096-8fe7-4f84-81d4-694025d32389","_cell_guid":"101bed7f-2ffc-42cd-b84d-930bcea05a33","collapsed":false,"jupyter":{"outputs_hidden":false},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class DownsampleLayer(nn.Module):\n    def __init__(self, in_channel, out_channel, kernel_size, stride, padding, downsample_type, padding_mode, dropout, act_type=\"relu\"):\n        super(DownsampleLayer, self).__init__()\n        self.downsample_type = downsample_type\n        self.activate = None\n        if act_type == \"relu\":\n            self.activate = nn.ReLU(inplace=True)\n        elif act_type == \"l_relu\":\n            self.activate = nn.LeakyReLU(0.2,inplace=True)\n        if downsample_type == \"conv\":\n            self.main = nn.Sequential(\n                nn.Conv2d(in_channel, out_channel, kernel_size, stride, padding, padding_mode=padding_mode),\n                nn.InstanceNorm2d(out_channel),\n                self.activate,\n                nn.Dropout(dropout)\n            )\n        if downsample_type == \"conv_same_padding\":\n            self.main = nn.Sequential(\n                Conv2dWithSamePadding(in_channel, out_channel, kernel_size, stride, padding_mode=padding_mode),\n                nn.InstanceNorm2d(out_channel),\n                self.activate,\n                nn.Dropout(dropout)\n            )\n        if downsample_type == \"conv_pooling\":\n            self.stride = stride\n            self.main = nn.Sequential(\n                Conv2dWithSamePadding(in_channel, out_channel, kernel_size, 1, padding_mode=padding_mode),\n                nn.InstanceNorm2d(out_channel),\n                self.activate,\n                nn.Dropout(dropout)\n            )\n\n    def forward(self, x):\n        x = self.main(x)\n        \n        if self.downsample_type == \"conv_pooling\":\n            _, _, H, W = x.size()\n            x = F.adaptive_avg_pool2d(x, (H // self.stride, W // self.stride))\n\n        return x","metadata":{"_uuid":"eb129ba6-e3f3-4230-a940-f0617f88312b","_cell_guid":"bab62351-accd-423a-9e08-a21b8bf68d83","collapsed":false,"jupyter":{"outputs_hidden":false},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class UpsampleLayer(nn.Module):\n    def __init__(self, in_channel, out_channel, kernel_size, stride, padding, out_padding, upsample_type, padding_mode):\n        super(UpsampleLayer, self).__init__()\n        if upsample_type == \"conv\":\n            self.main = nn.Sequential(\n                nn.ConvTranspose2d(in_channel, out_channel, kernel_size, stride, padding,\n                                   output_padding=out_padding , padding_mode=padding_mode),\n                nn.InstanceNorm2d(out_channel),\n                nn.ReLU()\n            )\n        elif upsample_type == \"conv_n_pool\":\n            self.main == nn.Sequential(\n                Conv2dWithSamePadding(in_channel, in_channel, kernel_size, 1, padding_mode=padding_mode),\n                nn.Upsample(scale_factor=stride),\n                nn.ReLU()\n            )\n        self.main = self.main\n\n    def forward(self, x):\n        x = self.main(x)\n\n        return x","metadata":{"_uuid":"ce24e839-1404-43ed-899e-7a16f9e067fd","_cell_guid":"5c0e68b5-3709-41aa-a4cb-dc096beebadf","collapsed":false,"jupyter":{"outputs_hidden":false},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class ResLayer(nn.Module):\n    def __init__(self, in_channel, out_channel, kernel_size, padding_1, padding_2, stride, padding_mode):\n        super(ResLayer, self).__init__()\n        self.main = nn.Sequential(\n            nn.Conv2d(in_channel, out_channel, kernel_size=kernel_size, stride=stride,\n                      padding=padding_1, bias=False, padding_mode=padding_mode),\n            nn.InstanceNorm2d(out_channel),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(out_channel, out_channel, kernel_size=kernel_size, stride=stride,\n                      padding=padding_2, bias=False, padding_mode=padding_mode),\n            nn.InstanceNorm2d(out_channel)\n        )\n        self.side = nn.Sequential(\n            nn.Conv2d(in_channel, out_channel, kernel_size=1, stride=stride, bias=False),\n            nn.InstanceNorm2d(out_channel)\n        ) if in_channel != out_channel or stride != 1 else nn.Sequential()\n\n    def forward(self, x):\n        x = self.main(x) + self.side(x)\n        x = F.relu(x)\n\n        return x","metadata":{"_uuid":"114a6e1c-2fd1-4504-97b3-7911cd55043c","_cell_guid":"bf64a06e-584b-4f8f-ac92-d91315561504","collapsed":false,"jupyter":{"outputs_hidden":false},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def clone_layers(layer:nn.Module, num:int):\n    layers = nn.ModuleList([deepcopy(layer) for _ in range(num)])\n    return layers","metadata":{"_uuid":"f155d02d-a6c0-47f9-8d03-6417254e4705","_cell_guid":"834a9be3-bd79-4895-9567-f31a504f3afb","collapsed":false,"jupyter":{"outputs_hidden":false},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# build the generator\nclass Generator(nn.Module):  # generate 256*256 images\n    def __init__(self):\n        super(Generator, self).__init__()\n        \n        self.res_layer = ResLayer(256,256,3,1,1,1,\"reflect\")\n        self.down_sample_layers = nn.Sequential(\n            # input 3*(256**2)\n            DownsampleLayer(3, 64, 7, 1, None, \"conv_same_padding\", \"reflect\", 0),\n\n            # input 64*(256**2)\n            DownsampleLayer(64,128, 3, 2, 1, \"conv\", \"reflect\",0),\n\n            # input 128*(128**2)\n            DownsampleLayer(128,256,3,2, 1, \"conv\", \"reflect\", 0),\n\n        ) # output 256*(64**2)\n        \n        self.up_sample_layers = nn.Sequential(\n            # input 256*(64**2)\n            UpsampleLayer(256,128,3,2,1,1,\"conv\", \"zeros\"),\n\n            # input 128*(128**2)\n            UpsampleLayer(128,64,3,2,1,1,\"conv\", \"zeros\"),\n\n            # input 64*(256**2)\n            Conv2dWithSamePadding(64, 3, 7, 1, padding_mode=\"reflect\"),\n            nn.Tanh()\n        ) # output 3*(256**2)\n        self.res_layers = clone_layers(self.res_layer, 9)\n        \n    def forward(self,x):\n        x = self.down_sample_layers(x)\n        x = self.res_layer(x)\n        x = self.up_sample_layers(x)\n        return x","metadata":{"_uuid":"bbcfb329-d984-456b-85db-b5df5d382dc4","_cell_guid":"cf3f6a64-5feb-4810-8b65-af2a69656862","collapsed":false,"jupyter":{"outputs_hidden":false},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Discriminator(nn.Module):\n    def __init__(self):\n        super(Discriminator,self).__init__()\n        \n        self.main = nn.Sequential(\n            # input size 3*(256**2)\n            nn.Conv2d(3, 64, 4, 2, 1,padding_mode=\"reflect\"),\n            nn.LeakyReLU(0.2),\n            # nn.Dropout(0.25)\n\n            # input size 64*(128**2)\n            DownsampleLayer(64, 128, 4, 2, 1, \"conv\", \"reflect\", 0,act_type=\"l_relu\"),\n\n            # input size 128*(64**2)\n            DownsampleLayer(128, 256, 4, 2, 1, \"conv\", \"reflect\", 0,act_type=\"l_relu\"),\n\n            # input size 256*(32**2)\n            DownsampleLayer(256, 512, 4, 1, 1, \"conv\", \"reflect\", 0,act_type=\"l_relu\"),\n\n            # input size 512*(31**2)\n            nn.Conv2d(512, 1, 4, padding=1, padding_mode=\"reflect\")\n\n        ) # output size 1*(30**2)\n\n    def forward(self,x):\n        x = self.main(x)\n\n        return x","metadata":{"_uuid":"7821c48d-7560-41f2-9976-9c390d1fc3eb","_cell_guid":"e574dfc3-817c-42ab-a1e7-3da99230e4ad","collapsed":false,"jupyter":{"outputs_hidden":false},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# dataset\nimport os\nfrom torch.utils.data import Dataset\nfrom PIL import Image\nclass MonetPhotoDataset(Dataset):\n    def __init__(self, data_dir, mode='train', transforms=None):\n        monet_dir = os.path.join(data_dir, 'monet_jpg')\n        photo_dir = os.path.join(data_dir, 'photo_jpg')\n        \n        if mode == 'train':\n            self.monet = [os.path.join(monet_dir, name) for name in sorted(os.listdir(monet_dir))[:295]]\n            self.photo = [os.path.join(photo_dir, name) for name in sorted(os.listdir(photo_dir))[:295]]\n        elif mode == 'test':\n            self.monet = [os.path.join(monet_dir, name) for name in sorted(os.listdir(monet_dir))[295:]]\n            self.photo = [os.path.join(photo_dir, name) for name in sorted(os.listdir(photo_dir))[295:301]]\n        \n        self.transforms = transforms\n        \n    def __len__(self):\n        return len(self.monet)\n    \n    def __getitem__(self, index):\n        monet = self.monet[index]\n        photo = self.photo[index]\n        \n        monet = Image.open(monet)\n        photo = Image.open(photo)\n        \n        if self.transforms is not None:\n            monet = self.transforms(monet)\n            photo = self.transforms(photo)\n        \n        return monet, photo","metadata":{"_uuid":"aab65485-6a42-457e-97f0-c08cfd6dcfb4","_cell_guid":"83b9b4ce-0354-4d5e-afce-ac2ec792a8a6","collapsed":false,"jupyter":{"outputs_hidden":false},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# parameters' setup\n\n# Root directory for dataset\ndataroot = \"/kaggle/input/gan-getting-started\"\n# Number of workers for dataloader\nworkers = 2\n# Batch size during training\nbatch_size = 5\n# Spatial size of training images. All images will be resized to this\n# size using a transformer.\nimage_size = 256\n\nnum_epochs = 200\nlr_constant_epochs = 75\n# Learning rate for optimizers\nlr_d = 0.0002\nlr_g = 0.0002\n# Beta1 hyperparam for Adam optimizers\nbeta1 = 0.5\n# Number of GPUs available. Use 0 for CPU mode.\nngpu = 1\n# loss hyper parameter\nl = 10 # lambda","metadata":{"_uuid":"7dbe1d11-aaec-4d9d-ba1c-c3ab07b776a7","_cell_guid":"b5319943-ae57-4c34-96b8-9504e64dc00a","collapsed":false,"jupyter":{"outputs_hidden":false},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# load data\nfrom torchvision import transforms\nfrom torchvision.utils import make_grid\nfrom matplotlib import pyplot as plt\n\ndset_trans = transforms.Compose([\n                transforms.Resize(image_size),\n                transforms.CenterCrop(image_size),\n                transforms.ToTensor(),\n                transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),\n            ])\n\ndataset = MonetPhotoDataset(dataroot,\"train\",dset_trans)\ndataloader = torch.utils.data.DataLoader(dataset, batch_size=batch_size,\n                                         shuffle=True, num_workers=workers)\n\ntest_dataset = MonetPhotoDataset(dataroot,\"test\",dset_trans)\ntest_dataloader = torch.utils.data.DataLoader(test_dataset, batch_size=batch_size,\n                                         shuffle=False, num_workers=workers)","metadata":{"_uuid":"8d5b87f8-e201-47af-8ca9-7ed41f32ce74","_cell_guid":"4e6d41aa-e3ba-420e-9d3d-d626d8405714","collapsed":false,"jupyter":{"outputs_hidden":false},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# weight init\ndef weights_init(m):\n    if isinstance(m, Conv2dWithSamePadding):\n        nn.init.normal_(m.weight.data, 0.0, 0.02)\n    if isinstance(m, nn.Conv2d):\n        nn.init.normal_(m.weight.data, 0.0, 0.02)","metadata":{"_uuid":"28b23ab4-b202-43e6-a133-3a8df9f709dd","_cell_guid":"921e5a35-e94f-4638-89e3-85643d36ff25","collapsed":false,"jupyter":{"outputs_hidden":false},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create the generator\n\n# Decide which device we want to run on\ndevice = torch.device(\"cuda:0\" if (torch.cuda.is_available() and ngpu > 0) else \"cpu\")\n\nnetG_Photo2Monet = Generator().to(device)\nnetG_Monet2Photo = Generator().to(device)\n\n# Apply the weights_init function to randomly initialize all weights\n#  to mean=0, stdev=0.02.\nnetG_Photo2Monet.apply(weights_init)\nnetG_Monet2Photo.apply(weights_init)","metadata":{"_uuid":"14dee562-931a-4a49-8288-2c047e6a313c","_cell_guid":"a0003b37-96ff-4ae2-b707-8070a8e490f6","collapsed":false,"jupyter":{"outputs_hidden":false},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"netD_Photo2Monet = Discriminator().to(device)\nnetD_Monet2Photo = Discriminator().to(device)\n\n# Apply the weights_init function to randomly initialize all weights\n#  to mean=0, stdev=0.02.\nnetD_Photo2Monet.apply(weights_init)\nnetD_Monet2Photo.apply(weights_init)","metadata":{"_uuid":"b42eca32-15cf-4355-adff-3f8fcf8876e8","_cell_guid":"ab23b9a6-80a5-4d92-8345-56a0c9013f98","collapsed":false,"jupyter":{"outputs_hidden":false},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# optimizer and other setup\n# Initialize BCELoss function\nfrom torch import optim\nfrom itertools import chain\n#criterion_GAN = nn.MSELoss()\ncriterion_GAN = nn.BCEWithLogitsLoss()\ncriterion_cycle = nn.L1Loss()\ncriterion_identity = nn.L1Loss()\n\n# Establish convention for real and fake labels during training\nreal_label = .9\nfake_label = 0.\n\n# Setup Adam optimizers for both G and D\noptimizerD_Photo2Monet = optim.Adam(netD_Photo2Monet.parameters(), lr=lr_d, betas=(beta1, 0.999))\noptimizerD_Monet2Photo = optim.Adam(netD_Monet2Photo.parameters(), lr=lr_d, betas=(beta1, 0.999))\noptimizerG = optim.Adam(chain(netG_Photo2Monet.parameters(), netG_Monet2Photo.parameters()), lr=lr_g, betas=(beta1, 0.999))","metadata":{"_uuid":"134e6b9a-7477-4e8e-a487-0a3f39a72d41","_cell_guid":"db7bdc84-e7dd-4b4a-b593-05ab135c6fa7","collapsed":false,"jupyter":{"outputs_hidden":false},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(device)","metadata":{"_uuid":"f8a2f04e-e2f2-40a4-894a-472f23fab675","_cell_guid":"bb6a054e-4633-47f2-8422-215ad0fc1fde","collapsed":false,"jupyter":{"outputs_hidden":false},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Training Loop\nimport torchvision.utils as vutils\nfrom tqdm import tqdm\nimport numpy as np\ntorch.autograd.set_detect_anomaly(True)\n# Lists to keep track of progress\nimg_list_monet = []\nimg_list_photo = []\nloss_d = []\nloss_g = []\nfrom tqdm import tqdm\nprint(\"Starting Training Loop...\")\n# For each epoch\nfor epoch in tqdm(range(num_epochs)):\n    # For each batch in the dataloader\n    netD_Monet2Photo.train()\n    netD_Photo2Monet.train()\n    netG_Monet2Photo.train()\n    netG_Photo2Monet.train()\n    for i, data in enumerate(dataloader, 0):\n        ############################\n        # objective function: L(Gx2y,Gy2x,Dy,Dx) = L1(Gx2y,Dy,x,y) + L2(Gy2x,Dx,x,y) + lambda1 * L3(G,F) + lambda2 * L4(G,F)\n        # L1, L2: the original loss of GANs, here using MSE loss\n        # L3: cycle loss, here using L1 loss\n        # L4: identity loss, here using L1 loss\n        # here, lambda2 = 0.5 * lambda1\n        ############################\n        data_monet = data[0].to(device)\n        data_photo = data[1].to(device)\n        b_size = data_monet.size(0)\n        label_real = torch.full((b_size,1,30,30), real_label, dtype=torch.float, device=device)\n        label_fake = torch.full((b_size,1,30,30), fake_label, dtype=torch.float, device=device)\n        \n        ############################\n        # (1) Update generators: \n        ###########################\n        netG_Monet2Photo.zero_grad()\n        netG_Photo2Monet.zero_grad()\n        \n        Monet_from_photo = netG_Photo2Monet(data_photo)\n        photo_from_monet = netG_Monet2Photo(data_monet)\n\n        # GAN loss\n        GAN_loss_Photo2Monet = criterion_GAN(netD_Photo2Monet(Monet_from_photo), label_real)\n        GAN_loss_Monet2Photo = criterion_GAN(netD_Monet2Photo(photo_from_monet), label_real)\n        GAN_loss = GAN_loss_Photo2Monet + GAN_loss_Monet2Photo\n\n        # cycle loss\n        monet2photo2monet = netG_Photo2Monet(photo_from_monet)\n        photo2monet2photo = netG_Monet2Photo(Monet_from_photo)\n        cycle_loss_Photo2Monet = criterion_cycle(photo2monet2photo, data_photo)\n        cycle_loss_Monet2Photo = criterion_cycle(monet2photo2monet, data_monet)\n        cycle_loss = cycle_loss_Photo2Monet + cycle_loss_Monet2Photo\n\n        # identity loss (need to check more)\n        monet_from_photo_by_net_p2m = netG_Photo2Monet(data_monet)\n        photo_from_monet_by_net_m2p = netG_Monet2Photo(data_photo)\n        identity_loss_p2m = criterion_identity(monet_from_photo_by_net_p2m, data_monet)\n        identity_loss_m2p = criterion_identity(photo_from_monet_by_net_m2p, data_photo)\n        identity_loss = identity_loss_m2p + identity_loss_p2m\n\n        # update paramenter\n        total_loss_G = GAN_loss + l * cycle_loss + 0.5 * l * identity_loss\n        total_loss_G.backward()\n        optimizerG.step()\n        \n        netD_Monet2Photo.zero_grad()\n        netD_Photo2Monet.zero_grad()\n        \n        total_d_loss = 0.\n        \n        ############################\n        # (2) Update discriminators: \n        ###########################\n        \n        # training the discriminator (Photo2Monet)\n        output_real = netD_Photo2Monet(data_monet)\n        output_fake = netD_Photo2Monet(Monet_from_photo.detach())\n        loss_real = criterion_GAN(output_real, label_real)\n        loss_fake = criterion_GAN(output_fake, label_fake)\n        loss_photo2Monet = (loss_real + loss_fake)/2.\n        total_d_loss = total_d_loss + loss_photo2Monet.item()\n        loss_photo2Monet.backward()\n        optimizerD_Photo2Monet.step()\n\n        # traning the discriminator (Monet2Photo)\n        output_real = netD_Monet2Photo(data_photo)\n        output_fake = netD_Monet2Photo(photo_from_monet.detach())\n        loss_real = criterion_GAN(output_real, label_real)\n        loss_fake = criterion_GAN(output_fake, label_fake)\n        loss_Monet2Photo = (loss_real + loss_fake)/2.\n        total_d_loss = total_d_loss + loss_Monet2Photo.item()\n        loss_Monet2Photo.backward()\n        optimizerD_Monet2Photo.step()\n        \n    loss_d.append(total_d_loss)\n    loss_g.append(total_loss_G.item())\n\n    if ((epoch+1) % 10) == 0:\n        print(f\"epoch {epoch + 1}\")\n        # print losses\n        print(f\"discriminator loss: phtot2monet: {loss_photo2Monet.item()}, monet2photo: {loss_Monet2Photo.item()}, total loss: {total_d_loss}\")\n        print(f\"generator loss: GAN: {GAN_loss.item()}, cycle: {cycle_loss.item()}, identity: {identity_loss.item()}, total loss: {total_loss_G}\")\n        print(f\"generatal_loting images by generators in epoch {epoch+1}\")\n        # Checking both generators and visulization\n        netG_Monet2Photo.eval()\n        netG_Photo2Monet.eval()\n\n        ts_data = next(iter(test_dataloader))\n\n        ts_monet_data = ts_data[0].to(device)\n        ts_photo_data = ts_data[1].to(device)\n\n        monet = netG_Photo2Monet(ts_photo_data).detach()\n        photo = netG_Monet2Photo(ts_monet_data).detach()\n\n        nrows = ts_monet_data.size(0)\n\n        ts_photo_data = make_grid(ts_photo_data, nrow=nrows, normalize=True)\n        ts_monet_data = make_grid(ts_monet_data, nrow=nrows, normalize=True)\n        monet = make_grid(monet, nrow=nrows, normalize=True)\n        photo = make_grid(photo, nrow=nrows, normalize=True)\n\n        result = torch.cat((ts_photo_data, monet, ts_monet_data, photo), 1)\n        result = result.cpu().permute(1,2,0)\n\n        # show images \n        plt.axis(\"off\")\n        plt.figure(figsize=(1.5*nrows, 6))\n        plt.imshow(result)\n        plt.show()","metadata":{"_uuid":"0c910a5a-8d37-4b2f-bee6-4b68dfd8f04b","_cell_guid":"c6e0162f-3ca8-45c9-a18f-56ea06849be6","collapsed":false,"jupyter":{"outputs_hidden":false},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# plot losses of generators and discriminators\nfig = plt.figure(figsize=(6,6))\nax = fig.add_axes([0.1, 0.1, 0.9, 0.9])\nx_epoch = np.arange(0, len(loss_d))\nax.plot(x_epoch, loss_d, 'r',label=\"Discriminator\")\nax.plot(x_epoch, loss_g, 'b',label=\"Generator\")\nax.set_xlabel(\"epoch\")\nax.set_ylabel(\"loss\")\nax.legend()\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# generating images and save them, waiting for metric\nphoto_dir = os.path.join(dataroot, 'photo_jpg')\nfiles = [os.path.join(photo_dir, name) for name in os.listdir(photo_dir)]\nlen(files)","metadata":{"_uuid":"80b6258f-b412-4e71-b74d-ef04c9ab1bb7","_cell_guid":"280283ce-92dd-4a47-a8ae-d4b6752772f0","collapsed":false,"jupyter":{"outputs_hidden":false},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"save_dir = '/kaggle/images'\nif not os.path.exists(save_dir):\n    os.makedirs(save_dir)","metadata":{"_uuid":"44807279-c2bc-4763-828b-e509b3ccc096","_cell_guid":"6c0f3467-7c80-4470-a5a4-bc8b26fec2ee","collapsed":false,"jupyter":{"outputs_hidden":false},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"generate_transforms = transforms.Compose([\n    transforms.ToTensor(),\n    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))\n])\n\nto_image = transforms.ToPILImage()\n\nnetG_Photo2Monet.eval()\nfor i in range(0, len(files), batch_size):\n    # read images\n    imgs = []\n    for j in range(i, min(len(files), i+batch_size)):\n        img = Image.open(files[j])\n        img = generate_transforms(img)\n        imgs.append(img)\n    imgs = torch.stack(imgs, 0).to(device)\n    \n    # generate\n    fake_imgs = netG_Photo2Monet(imgs).detach().cpu()\n    \n    # save\n    for j in range(fake_imgs.size(0)):\n        img = fake_imgs[j].squeeze().permute(1, 2, 0)\n        img_arr = img.numpy()\n        img_arr = (img_arr - np.min(img_arr)) * 255 / (np.max(img_arr) - np.min(img_arr))\n        img_arr = img_arr.astype(np.uint8)\n        \n        img = to_image(img_arr)\n        _, name = os.path.split(files[i+j])\n        img.save(os.path.join(save_dir, name))","metadata":{"_uuid":"da9a9f72-acb4-4282-920d-625c74c705a0","_cell_guid":"f96b4369-7557-48ac-ab69-ebfcd46381a4","collapsed":false,"jupyter":{"outputs_hidden":false},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import shutil\nshutil.make_archive(\"/kaggle/working/images\", 'zip', \"/kaggle/images\")\nprint(\"finished!\")","metadata":{"_uuid":"fd75553f-bab0-4ccb-ac5a-0c03e5e418bf","_cell_guid":"96d76949-08b3-46c8-8ffd-e4ff0f697a48","collapsed":false,"jupyter":{"outputs_hidden":false},"trusted":true},"execution_count":null,"outputs":[]}]}