{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":21755,"databundleVersionId":1475600,"sourceType":"competition"}],"dockerImageVersionId":31012,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# CycleGAN Tutorial from Scratch\ncyclegan is a pix2pix image generation model that generates images from datasets that do not require pairs.\n\nWe will implement this cyclegan directly from the ground up with pytorch, and practice converting monet pictures into pictures and pictures into monet pictures.\n","metadata":{}},{"cell_type":"markdown","source":"# Main Reference\n1. [PyTorch-GAN | Github/eriklindernoren | Collection of PyTorch implementations of GAN](https://github.com/sw-song/PyTorch-GAN)\n2. [CycleGAN | Github/junyanz | Torch implementation for learning an image-to-image translation without input-output pairs](https://github.com/junyanz/CycleGAN)","metadata":{}},{"cell_type":"markdown","source":"![image](https://miro.medium.com/max/1838/0*S5gn5i6UhfyoRr9S.png)","metadata":{}},{"cell_type":"markdown","source":"### Index\n```\nStep 1. Import Libraries\nStep 2. Initial Setting\nStep 3. Define Generator\nStep 4. Define Discriminator\nStep 5. Define Loss Function\nStep 6. Initialize Generator and Discriminator\nStep 7. GPU Setting\nStep 8. Weight Setting\nStep 9. Configure Optimizer\nStep 10. Learning Rate Scheduler Setting\nStep 11. Image Transformation Setting\nStep 12. DataLoader Setting\nStep 13. Define function to get sample images\nStep 14. Training\n```\n---","metadata":{}},{"cell_type":"markdown","source":"### Step 1-> Importing Libraries","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport torchvision.transforms as transforms\nfrom torchvision.utils import make_grid\nfrom torch.utils.data import DataLoader\nfrom torchvision import datasets\nfrom torch.autograd import Variable\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T12:54:01.167191Z","iopub.execute_input":"2025-05-05T12:54:01.167416Z","iopub.status.idle":"2025-05-05T12:54:11.613614Z","shell.execute_reply.started":"2025-05-05T12:54:01.167393Z","shell.execute_reply":"2025-05-05T12:54:11.613005Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import kagglehub\n#gan_getting_started_path = kagglehub.competition_download('gan-getting-started')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T12:54:11.614269Z","iopub.execute_input":"2025-05-05T12:54:11.614591Z","iopub.status.idle":"2025-05-05T12:54:11.973731Z","shell.execute_reply.started":"2025-05-05T12:54:11.614566Z","shell.execute_reply":"2025-05-05T12:54:11.972991Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Step 2-> Initial Setting","metadata":{}},{"cell_type":"code","source":"dataset_name = 'gan-getting-started'\nroot = '../input/'+dataset_name\n\nimg_height = 256\nimg_width = 256\nchannels = 3\n\nepoch = 0   #epoch to start training from\nn_epochs = 5   #number of epoch of the training\nbatch_size = 1   #Size of the batches\nlr = 0.0002\nb1 = 0.5  #adam: decay of first order momentum of gradient\nb2 = 0.999 #adam: decay of first order momentum of gradient\ndecay_epoch = 3  #epoch from which to start lr decay\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T12:54:11.975234Z","iopub.execute_input":"2025-05-05T12:54:11.975435Z","iopub.status.idle":"2025-05-05T12:54:11.979410Z","shell.execute_reply.started":"2025-05-05T12:54:11.975418Z","shell.execute_reply":"2025-05-05T12:54:11.978885Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### Step 3-> Define Generator","metadata":{}},{"cell_type":"code","source":"class ResidualBlock(nn.Module):\n    def __init__(self, in_features):\n        super(ResidualBlock, self).__init__()\n\n        self.block = nn.Sequential(\n            nn.ReflectionPad2d(1), # Pads the input tensor using the reflection of the input boundary\n            nn.Conv2d(in_features, in_features, 3),\n            nn.InstanceNorm2d(in_features),\n            nn.ReLU(inplace=True),\n            nn.ReflectionPad2d(1),\n            nn.Conv2d(in_features, in_features, 3),\n            nn.InstanceNorm2d(in_features)\n        )\n    def forward(self, x):\n        return x + self.block(x)\n\nclass GeneratorResNet(nn.Module):\n    def __init__(self, input_shape, num_residual_block):\n        super(GeneratorResNet, self).__init__()\n\n        channels = input_shape[0]\n        #Initial Convolutional Block\n        out_features = 64\n        model = [\n            nn.ReflectionPad2d(channels),\n            nn.Conv2d(channels, out_features, 7),\n            nn.InstanceNorm2d(out_features),\n            nn.ReLU(inplace=True)\n        ]\n        in_features = out_features\n\n        #Downsampling\n        for _ in range(2):\n            out_features *= 2\n            model += [\n                nn.Conv2d(in_features, out_features, 3, stride=2, padding=1),\n                nn.InstanceNorm2d(out_features),\n                nn.ReLU(inplace=True)\n            ]\n            in_features = out_features\n\n        #Residual Block\n        for _ in range(num_residual_block):\n            model += [ResidualBlock(out_features)]\n\n\n        #Upsampling\n        for _ in range(2):\n            out_features //= 2\n            model += [\n                nn.Upsample(scale_factor=2),\n                nn.Conv2d(in_features, out_features, 3, stride=1, padding=1),\n                nn.ReLU(inplace=True)\n            ]\n            in_features = out_features\n\n        #output layers\n        model += [\n            nn.ReflectionPad2d(channels),\n            nn.Conv2d(out_features, channels, 7),\n            nn.Tanh()\n        ]\n\n        #Unpacking\n        self.model = nn.Sequential(*model)\n\n    def forward(self, x):\n        return self.model(x)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T12:54:11.980102Z","iopub.execute_input":"2025-05-05T12:54:11.980588Z","iopub.status.idle":"2025-05-05T12:54:12.001965Z","shell.execute_reply.started":"2025-05-05T12:54:11.980553Z","shell.execute_reply":"2025-05-05T12:54:12.001283Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### Step 4-> Define Discriminator","metadata":{}},{"cell_type":"code","source":"class Discriminator(nn.Module):\n    def __init__(self, input_shape):\n        super(Discriminator, self).__init__()\n        \n        channels, height, width = input_shape\n        \n        # Calculate output shape of image discriminator (PatchGAN)\n        self.output_shape = (1, height//2**4, width//2**4)\n        \n        def discriminator_block(in_filters, out_filters, normalize=True):\n            \"\"\"Returns downsampling layers of each discriminator block\"\"\"\n            layers = [nn.Conv2d(in_filters, out_filters, 4, stride=2, padding=1)]\n            if normalize:\n                layers.append(nn.InstanceNorm2d(out_filters))\n            layers.append(nn.LeakyReLU(0.2, inplace=True))\n            return layers\n        \n        self.model = nn.Sequential(\n            *discriminator_block(channels, 64, normalize=False),\n            *discriminator_block(64, 128),\n            *discriminator_block(128,256),\n            *discriminator_block(256,512),\n            nn.ZeroPad2d((1,0,1,0)),\n            nn.Conv2d(512, 1, 4, padding=1)\n        )\n        \n    def forward(self, img):\n        return self.model(img)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T12:54:12.002580Z","iopub.execute_input":"2025-05-05T12:54:12.002747Z","iopub.status.idle":"2025-05-05T12:54:12.023176Z","shell.execute_reply.started":"2025-05-05T12:54:12.002734Z","shell.execute_reply":"2025-05-05T12:54:12.022496Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### Step 5-> Define Loss","metadata":{}},{"cell_type":"code","source":"criterion_GAN = torch.nn.MSELoss()\ncriterion_cycle = torch.nn.L1Loss()\ncriterion_identity = torch.nn.L1Loss()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T12:54:12.023771Z","iopub.execute_input":"2025-05-05T12:54:12.023959Z","iopub.status.idle":"2025-05-05T12:54:12.047042Z","shell.execute_reply.started":"2025-05-05T12:54:12.023945Z","shell.execute_reply":"2025-05-05T12:54:12.046551Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### Step 6-> Initialize Generator and Discriminator","metadata":{}},{"cell_type":"code","source":"input_shape = (channels, img_height, img_width)\nn_residual_blocks = 9\n\nG_AB = GeneratorResNet(input_shape, n_residual_blocks)\nG_BA = GeneratorResNet(input_shape, n_residual_blocks)\nD_A = Discriminator(input_shape)\nD_B = Discriminator(input_shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T12:54:12.047622Z","iopub.execute_input":"2025-05-05T12:54:12.047837Z","iopub.status.idle":"2025-05-05T12:54:12.360954Z","shell.execute_reply.started":"2025-05-05T12:54:12.047818Z","shell.execute_reply":"2025-05-05T12:54:12.360372Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### Step 7-> GPU Setting","metadata":{}},{"cell_type":"code","source":"cuda = torch.cuda.is_available()\nif cuda:\n    G_AB = G_AB.cuda()\n    G_BA = G_BA.cuda()\n    D_A = D_A.cuda()\n    D_B = D_B.cuda()\n\n    criterion_GAN.cuda()\n    criterion_cycle.cuda()\n    criterion_identity.cuda()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T12:54:12.361766Z","iopub.execute_input":"2025-05-05T12:54:12.362042Z","iopub.status.idle":"2025-05-05T12:54:12.723596Z","shell.execute_reply.started":"2025-05-05T12:54:12.362018Z","shell.execute_reply":"2025-05-05T12:54:12.722809Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### Step 8-> Weight Setting","metadata":{}},{"cell_type":"code","source":"def weights_init_normal(m):\n    classname = m.__class__.__name__\n    if classname.find('Conv') != -1:\n        torch.nn.init.normal_(m.weight.data, 0.0, 0.02) # reset Conv2d's weight(tensor) with Gaussian Distribution\n        if hasattr(m, 'bias') and m.bias is not None:\n            torch.nn.init.constant_(m.bias.data, 0.0) # reset Conv2d's bias(tensor) with Constant(0)\n        elif classname.find('BatchNorm2d') != -1:\n            torch.nn.init.normal_(m.weight.data, 1.0, 0.02) # reset BatchNorm2d's weight(tensor) with Gaussian Distribution\n            torch.nn.init.constant_(m.bias.data, 0.0) # reset BatchNorm2d's bias(tensor) with Constant(0)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T12:54:12.726591Z","iopub.execute_input":"2025-05-05T12:54:12.726843Z","iopub.status.idle":"2025-05-05T12:54:12.732330Z","shell.execute_reply.started":"2025-05-05T12:54:12.726825Z","shell.execute_reply":"2025-05-05T12:54:12.731575Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"G_AB.apply(weights_init_normal)\nG_BA.apply(weights_init_normal)\nD_A.apply(weights_init_normal)\nD_B.apply(weights_init_normal)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T12:54:12.733172Z","iopub.execute_input":"2025-05-05T12:54:12.733460Z","iopub.status.idle":"2025-05-05T12:54:12.828625Z","shell.execute_reply.started":"2025-05-05T12:54:12.733436Z","shell.execute_reply":"2025-05-05T12:54:12.828104Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### Step9-> Configure Optimizers","metadata":{}},{"cell_type":"code","source":"import itertools\n\noptimizer_G = torch.optim.Adam(\n    itertools.chain(G_AB.parameters(), G_BA.parameters()), lr=lr, betas=(b1, b2)\n)\noptimizer_D_A = torch.optim.Adam(\n    D_A.parameters(), lr=lr, betas=(b1, b2)\n)\noptimizer_D_B = torch.optim.Adam(\n    D_B.parameters(), lr=lr, betas=(b1, b2)\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T12:54:12.829188Z","iopub.execute_input":"2025-05-05T12:54:12.829346Z","iopub.status.idle":"2025-05-05T12:54:12.834372Z","shell.execute_reply.started":"2025-05-05T12:54:12.829333Z","shell.execute_reply":"2025-05-05T12:54:12.833723Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### Step 10-> Learning Rate Scheduler Setting","metadata":{}},{"cell_type":"code","source":"class LambdaLR:\n    def __init__(self, n_epochs, offset, decay_start_epoch):\n        assert(n_epochs - decay_start_epoch) > 0, \"Decay must start before the training session ends!\"\n        self.n_epochs = n_epochs\n        self.offset = offset\n        self.decay_start_epoch = decay_start_epoch\n    def step(self, epoch):\n        return 1.0 - max(0, epoch+self.offset - self.decay_start_epoch) / (self.n_epochs-self.decay_start_epoch)\n        ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T12:54:12.835062Z","iopub.execute_input":"2025-05-05T12:54:12.835259Z","iopub.status.idle":"2025-05-05T12:54:12.854024Z","shell.execute_reply.started":"2025-05-05T12:54:12.835237Z","shell.execute_reply":"2025-05-05T12:54:12.853556Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# n_epochs = 10\n# epoch = 0\n# decay_epoch = 5\n\n\nlr_scheduler_G = torch.optim.lr_scheduler.LambdaLR(\n    optimizer_G,\n    lr_lambda=LambdaLR(n_epochs, epoch, decay_epoch).step\n)\n\nlr_scheduler_D_A = torch.optim.lr_scheduler.LambdaLR(\n    optimizer_D_A,\n    lr_lambda=LambdaLR(n_epochs, epoch, decay_epoch).step\n)\nlr_scheduler_D_B = torch.optim.lr_scheduler.LambdaLR(\n    optimizer_D_B,\n    lr_lambda=LambdaLR(n_epochs, epoch, decay_epoch).step\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T12:54:12.854720Z","iopub.execute_input":"2025-05-05T12:54:12.854899Z","iopub.status.idle":"2025-05-05T12:54:12.875128Z","shell.execute_reply.started":"2025-05-05T12:54:12.854885Z","shell.execute_reply":"2025-05-05T12:54:12.874665Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### Step 11-> Image Transformation Setting","metadata":{}},{"cell_type":"code","source":"from PIL import Image\nimport torchvision.transforms as transforms\n\ntransforms_ = [\n    transforms.Resize(int(img_height*1.12), Image.BICUBIC),\n    transforms.RandomCrop((img_height, img_width)),\n    transforms.RandomHorizontalFlip(),\n    transforms.ToTensor(),\n    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))\n]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T12:54:12.875680Z","iopub.execute_input":"2025-05-05T12:54:12.875834Z","iopub.status.idle":"2025-05-05T12:54:12.893997Z","shell.execute_reply.started":"2025-05-05T12:54:12.875821Z","shell.execute_reply":"2025-05-05T12:54:12.893455Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### Step 12-> Data Loader Setting","metadata":{}},{"cell_type":"code","source":"def to_rgb(image):\n    rgb_image = Image.new(\"RGB\", image.size)\n    rgb_image.paste(image)\n    return rgb_image","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T12:54:12.894572Z","iopub.execute_input":"2025-05-05T12:54:12.894752Z","iopub.status.idle":"2025-05-05T12:54:12.914879Z","shell.execute_reply.started":"2025-05-05T12:54:12.894739Z","shell.execute_reply":"2025-05-05T12:54:12.914222Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport glob","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T12:54:12.915584Z","iopub.execute_input":"2025-05-05T12:54:12.916077Z","iopub.status.idle":"2025-05-05T12:54:12.932810Z","shell.execute_reply.started":"2025-05-05T12:54:12.916053Z","shell.execute_reply":"2025-05-05T12:54:12.932250Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(root+'/monet_jpg')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T12:54:12.933416Z","iopub.execute_input":"2025-05-05T12:54:12.933678Z","iopub.status.idle":"2025-05-05T12:54:12.949800Z","shell.execute_reply.started":"2025-05-05T12:54:12.933652Z","shell.execute_reply":"2025-05-05T12:54:12.949291Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"len(glob.glob(os.path.join(root+'/photo_jpg')+'/*.*'))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T12:54:12.950362Z","iopub.execute_input":"2025-05-05T12:54:12.950599Z","iopub.status.idle":"2025-05-05T12:54:13.052044Z","shell.execute_reply.started":"2025-05-05T12:54:12.950584Z","shell.execute_reply":"2025-05-05T12:54:13.051392Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torch.utils.data import Dataset\nclass ImageDataset(Dataset):\n    def __init__(self, root, transforms_=None, unaligned=False, mode='train'):\n        self.transform=transforms.Compose(transforms_)\n        self.unaligned=unaligned\n        self.mode=mode\n        if self.mode == 'train':\n            self.files_A = sorted(glob.glob(os.path.join(root+'/monet_jpg')+'/*.*')[:250])\n            self.files_B = sorted(glob.glob(os.path.join(root+'/photo_jpg')+'/*.*')[:250])\n        elif self.mode == 'test':\n            self.files_A = sorted(glob.glob(os.path.join(root+'/monet_jpg')+'/*.*')[250:])\n            self.files_B = sorted(glob.glob(os.path.join(root+'/photo_jpg')+'/*.*')[250:301])\n\n    def __getitem__(self, index):\n        image_A = Image.open(self.files_A[index % len(self.files_A)])\n\n        if self.unaligned:\n            image_B = Image.open(self.files_B[np.random.randint(0, len(self.files_B)-1)])\n        else:\n            image_B = Image.open(self.files_B[index % len(self.files_B)])\n        if image_A.mode != 'RGB':\n            image_A = to_rgb(image_A)\n        if image_B.mode != 'RGB':\n            image_B = to_rgb(image_B)\n        item_A = self.transform(image_A)\n        item_B = self.transform(image_B)\n\n        return {'A':item_A, 'B':item_B}\n    def __len__(self):\n        return max(len(self.files_A), len(self.files_B))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T12:54:13.052727Z","iopub.execute_input":"2025-05-05T12:54:13.052963Z","iopub.status.idle":"2025-05-05T12:54:13.059826Z","shell.execute_reply.started":"2025-05-05T12:54:13.052947Z","shell.execute_reply":"2025-05-05T12:54:13.059219Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"dataloader = DataLoader(\n    ImageDataset(root, transforms_=transforms_, unaligned=True),\n    batch_size=1, # 1\n    shuffle=True,\n    num_workers=3 # 3\n)\n\nval_dataloader = DataLoader(\n    ImageDataset(root, transforms_=transforms_, unaligned=True, mode='test'),\n    batch_size=5,\n    shuffle=True,\n    num_workers=3\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T12:54:13.060666Z","iopub.execute_input":"2025-05-05T12:54:13.060866Z","iopub.status.idle":"2025-05-05T12:54:13.115494Z","shell.execute_reply.started":"2025-05-05T12:54:13.060853Z","shell.execute_reply":"2025-05-05T12:54:13.115056Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### Step 13-> Define function to get sample images","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T12:54:13.116084Z","iopub.execute_input":"2025-05-05T12:54:13.116262Z","iopub.status.idle":"2025-05-05T12:54:13.119490Z","shell.execute_reply.started":"2025-05-05T12:54:13.116248Z","shell.execute_reply":"2025-05-05T12:54:13.118823Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"Tensor = torch.cuda.FloatTensor if cuda else torch.Tensor","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T12:54:13.120210Z","iopub.execute_input":"2025-05-05T12:54:13.120392Z","iopub.status.idle":"2025-05-05T12:54:13.138043Z","shell.execute_reply.started":"2025-05-05T12:54:13.120372Z","shell.execute_reply":"2025-05-05T12:54:13.137567Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def sample_images():\n    \"\"\"show a generated sample from the test set\"\"\"\n    imgs = next(iter(val_dataloader))\n    G_AB.eval()\n    G_BA.eval()\n    real_A = imgs['A'].type(Tensor) # A : monet\n    fake_B = G_AB(real_A).detach()\n    real_B = imgs['B'].type(Tensor) # B : photo\n    fake_A = G_BA(real_B).detach()\n    # Arange images along x-axis\n    real_A = make_grid(real_A, nrow=5, normalize=True)\n    fake_B = make_grid(fake_B, nrow=5, normalize=True)\n    real_B = make_grid(real_B, nrow=5, normalize=True)\n    fake_A = make_grid(fake_A, nrow=5, normalize=True)\n    # Arange images along y-axis    \n    image_grid = torch.cat((real_A, fake_B, real_B, fake_A), 1)\n    plt.imshow(image_grid.cpu().permute(1,2,0))\n    plt.title('Real A vs Fake B | Real B vs Fake A')\n    plt.axis('off')\n    plt.show();","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T12:54:13.138582Z","iopub.execute_input":"2025-05-05T12:54:13.138756Z","iopub.status.idle":"2025-05-05T12:54:13.157331Z","shell.execute_reply.started":"2025-05-05T12:54:13.138743Z","shell.execute_reply":"2025-05-05T12:54:13.156633Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### Step 14-> Training","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nfrom tqdm.notebook import tqdm\nimport warnings","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T12:54:13.158071Z","iopub.execute_input":"2025-05-05T12:54:13.158319Z","iopub.status.idle":"2025-05-05T12:54:13.176008Z","shell.execute_reply.started":"2025-05-05T12:54:13.158297Z","shell.execute_reply":"2025-05-05T12:54:13.175494Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for epoch in range(epoch, n_epochs):\n    for i, batch in enumerate(tqdm(dataloader)):\n        \n        # Set model input\n        real_A = batch['A'].type(Tensor)\n        real_B = batch['B'].type(Tensor)\n        \n        # Adversarial ground truths\n        valid = Tensor(np.ones((real_A.size(0), *D_A.output_shape))) # requires_grad = False. Default.\n        fake = Tensor(np.zeros((real_A.size(0), *D_A.output_shape))) # requires_grad = False. Default.\n        \n# -----------------\n# Train Generators\n# -----------------\n        G_AB.train() # train mode\n        G_BA.train() # train mode\n        \n        optimizer_G.zero_grad() # Integrated optimizer(G_AB, G_BA)\n        \n        # Identity Loss\n        loss_id_A = criterion_identity(G_BA(real_A), real_A) # If you put A into a generator that creates A with B,\n        loss_id_B = criterion_identity(G_AB(real_B), real_B) # then of course A must come out as it is.\n                                                             # Taking this into consideration, add an identity loss that simply compares 'A and A' (or 'B and B').\n        loss_identity = (loss_id_A + loss_id_B)/2\n        \n        # GAN Loss\n        fake_B = G_AB(real_A) # fake_B is fake-photo that generated by real monet-drawing\n        loss_GAN_AB = criterion_GAN(D_B(fake_B), valid) # tricking the 'fake-B' into 'real-B'\n        fake_A = G_BA(real_B)\n        loss_GAN_BA = criterion_GAN(D_A(fake_A), valid) # tricking the 'fake-A' into 'real-A'\n        \n        loss_GAN = (loss_GAN_AB + loss_GAN_BA)/2\n        \n        # Cycle Loss\n        recov_A = G_BA(fake_B) # recov_A is fake-monet-drawing that generated by fake-photo\n        loss_cycle_A = criterion_cycle(recov_A, real_A) # Reduces the difference between the restored image and the real image\n        recov_B = G_AB(fake_A)\n        loss_cycle_B = criterion_cycle(recov_B, real_B)\n        \n        loss_cycle = (loss_cycle_A + loss_cycle_B)/2\n        \n# ------> Total Loss\n        loss_G = loss_GAN + (10.0*loss_cycle) + (5.0*loss_identity) # multiply suggested weight(default cycle loss weight : 10, default identity loss weight : 5)\n        \n        loss_G.backward()\n        optimizer_G.step()\n        \n# -----------------\n# Train Discriminator A\n# -----------------\n        optimizer_D_A.zero_grad()\n    \n        loss_real = criterion_GAN(D_A(real_A), valid) # train to discriminate real images as real\n        loss_fake = criterion_GAN(D_A(fake_A.detach()), fake) # train to discriminate fake images as fake\n        \n        loss_D_A = (loss_real + loss_fake)/2\n        \n        loss_D_A.backward()\n        optimizer_D_A.step()\n\n# -----------------\n# Train Discriminator B\n# -----------------\n        optimizer_D_B.zero_grad()\n    \n        loss_real = criterion_GAN(D_B(real_B), valid) # train to discriminate real images as real\n        loss_fake = criterion_GAN(D_B(fake_B.detach()), fake) # train to discriminate fake images as fake\n        \n        loss_D_B = (loss_real + loss_fake)/2\n        \n        loss_D_B.backward()\n        optimizer_D_B.step()\n        \n# ------> Total Loss\n        loss_D = (loss_D_A + loss_D_B)/2\n    \n# -----------------\n# Show Progress\n# -----------------\n        if (i+1) % 50 == 0:\n            sample_images()\n            print('[Epoch %d/%d] [Batch %d/%d] [D loss : %f] [G loss : %f - (adv : %f, cycle : %f, identity : %f)]'\n                    %(epoch+1,n_epochs,       # [Epoch -]\n                      i+1,len(dataloader),   # [Batch -]\n                      loss_D.item(),       # [D loss -]\n                      loss_G.item(),       # [G loss -]\n                      loss_GAN.item(),     # [adv -]\n                      loss_cycle.item(),   # [cycle -]\n                      loss_identity.item(),# [identity -]\n                     ))\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T12:54:13.176586Z","iopub.execute_input":"2025-05-05T12:54:13.176754Z","iopub.status.idle":"2025-05-05T13:06:33.990426Z","shell.execute_reply.started":"2025-05-05T12:54:13.176742Z","shell.execute_reply":"2025-05-05T13:06:33.989620Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}