{"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":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n        \nimport matplotlib.pyplot as plt\nimport numpy as np\nimport os\nimport pandas as pd\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, random_split, DataLoader\nimport torchvision.models as models\nimport torchvision.transforms as transforms\nfrom PIL import Image\nimport torch.nn.init as init\nimport itertools\nfrom torchvision.utils import make_grid\n\nbatch_size=3\nnum_workers=2\nlr = 0.0005\nb1 = 0.5\nb2 = 0.996\nepochs=100","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-05-04T06:01:43.500293Z","iopub.execute_input":"2022-05-04T06:01:43.501661Z","iopub.status.idle":"2022-05-04T06:01:53.603191Z","shell.execute_reply.started":"2022-05-04T06:01:43.501527Z","shell.execute_reply":"2022-05-04T06:01:53.602036Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Dataset","metadata":{}},{"cell_type":"code","source":"class ImageDataset(Dataset):\n    def __init__(self, data_dir, size=(256, 256), normalize=True):\n        super().__init__()\n        self.monet_dir = os.path.join(data_dir, 'monet_jpg')\n        self.photo_dir = os.path.join(data_dir, 'photo_jpg')\n        if normalize:\n            self.transform = transforms.Compose([\n                transforms.Resize(size),\n                transforms.ToTensor(),\n                transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))                                \n            ])\n        else:\n            self.transform = transforms.Compose([\n                transforms.Resize(size),\n                transforms.ToTensor()                               \n            ])\n        self.monet_file = [os.path.join(self.monet_dir, name) for name in sorted(os.listdir(self.monet_dir))]\n        self.photo_file = [os.path.join(self.photo_dir, name) for name in sorted(os.listdir(self.photo_dir))]\n\n    def __getitem__(self, idx):\n        rand_idx = int(np.random.uniform(0, len(self.monet_file)))\n        photo_img = Image.open(self.photo_file[rand_idx])\n        photo_img = self.transform(photo_img)\n        monet_img = Image.open(self.monet_file[idx])\n        monet_img = self.transform(monet_img)\n        return monet_img,photo_img\n\n    def __len__(self):\n        return len(self.monet_file)\n\nimg_Data = ImageDataset('/kaggle/input/gan-getting-started')\nimg_Dataset = DataLoader(img_Data, batch_size=batch_size, num_workers = num_workers)","metadata":{"execution":{"iopub.status.busy":"2022-05-04T06:01:53.607029Z","iopub.execute_input":"2022-05-04T06:01:53.607279Z","iopub.status.idle":"2022-05-04T06:01:53.643064Z","shell.execute_reply.started":"2022-05-04T06:01:53.607247Z","shell.execute_reply":"2022-05-04T06:01:53.642149Z"},"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-05-04T06:01:53.644498Z","iopub.execute_input":"2022-05-04T06:01:53.645519Z","iopub.status.idle":"2022-05-04T06:01:53.652489Z","shell.execute_reply.started":"2022-05-04T06:01:53.645471Z","shell.execute_reply":"2022-05-04T06:01:53.651418Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Model","metadata":{}},{"cell_type":"code","source":"def init_weights(net, init_type='normal', gain=0.02):\n    def init_func(m):\n        classname = m.__class__.__name__\n        if hasattr(m, 'weight') and (classname.find('Conv') != -1 or classname.find('Linear') != -1):\n            init.normal_(m.weight.data, 0.0, gain)\n            if hasattr(m, 'bias') and m.bias is not None:\n                init.constant_(m.bias.data, 0.0)\n        elif classname.find('BatchNorm2d') != -1:\n            init.normal_(m.weight.data, 1.0, gain)\n            init.constant_(m.bias.data, 0.0)\n    net.apply(init_func)","metadata":{"execution":{"iopub.status.busy":"2022-05-04T06:01:53.655908Z","iopub.execute_input":"2022-05-04T06:01:53.656455Z","iopub.status.idle":"2022-05-04T06:01:53.664749Z","shell.execute_reply.started":"2022-05-04T06:01:53.656404Z","shell.execute_reply":"2022-05-04T06:01:53.663769Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class ResidualBlock(nn.Module):\n    def __init__(self, in_channels):\n        super(ResidualBlock, self).__init__()\n        self.block = nn.Sequential(\n            nn.ReflectionPad2d(1), # padding, keep the image size constant after next conv2d\n            nn.Conv2d(in_channels, in_channels, 3),\n            nn.InstanceNorm2d(in_channels),\n            nn.ReLU(inplace=True),\n            nn.ReflectionPad2d(1),\n            nn.Conv2d(in_channels, in_channels, 3),\n            nn.InstanceNorm2d(in_channels)\n        )\n    \n    def forward(self, x):\n        return x + self.block(x)","metadata":{"execution":{"iopub.status.busy":"2022-05-04T06:01:53.666498Z","iopub.execute_input":"2022-05-04T06:01:53.667424Z","iopub.status.idle":"2022-05-04T06:01:53.678715Z","shell.execute_reply.started":"2022-05-04T06:01:53.667377Z","shell.execute_reply":"2022-05-04T06:01:53.677758Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Generator(nn.Module):\n    def __init__(self, input_channels, num_residual_blocks=6):\n        super(Generator, self).__init__()\n\n        # Initial convolution block\n        output_channels=64\n        model = [   nn.ReflectionPad2d(input_channels),\n                    nn.Conv2d(input_channels, output_channels, 2*input_channels+1),\n                    nn.InstanceNorm2d(output_channels),\n                    nn.ReLU(inplace=True) ]\n\n        # Downsampling\n        in_features = output_channels\n        out_features = in_features*2\n        for _ in range(2):\n            model += [  nn.Conv2d(in_features, out_features, 3, stride=2, padding=1),\n                        nn.InstanceNorm2d(out_features),\n                        nn.ReLU(inplace=True) ]\n            in_features = out_features\n            out_features = in_features*2\n\n        # Residual blocks\n        for _ in range(num_residual_blocks):\n            model += [ResidualBlock(in_features)]\n\n        # Upsampling\n        out_features = in_features//2\n        for _ in range(2):\n            model += [nn.ConvTranspose2d(in_features, out_features, 3, stride=2, padding=1, output_padding=1),\n                      nn.InstanceNorm2d(out_features),\n                      nn.ReLU(inplace=True) ]\n            in_features = out_features\n            out_features = in_features//2\n\n        # Output layer\n        model += [nn.ReflectionPad2d(input_channels),\n                  nn.Conv2d(output_channels, input_channels, 2*input_channels+1),\n                  nn.Tanh() ]\n\n        self.model = nn.Sequential(*model)\n\n    def forward(self, x):\n        return self.model(x)","metadata":{"execution":{"iopub.status.busy":"2022-05-04T06:01:53.68256Z","iopub.execute_input":"2022-05-04T06:01:53.683183Z","iopub.status.idle":"2022-05-04T06:01:53.698013Z","shell.execute_reply.started":"2022-05-04T06:01:53.683148Z","shell.execute_reply":"2022-05-04T06:01:53.697044Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Discriminator(nn.Module):\n    def __init__(self, input_nc):\n        super(Discriminator, self).__init__()\n\n        # A bunch of convolutions one after another\n        model = [nn.Conv2d(input_nc, 64, 4, stride=2, padding=1),\n                 nn.LeakyReLU(0.2, inplace=True) ]\n\n        model += [nn.Conv2d(64, 128, 4, stride=2, padding=1),\n                  nn.InstanceNorm2d(128), \n                  nn.LeakyReLU(0.2, inplace=True) ]\n\n        model += [nn.Conv2d(128, 256, 4, stride=2, padding=1),\n                  nn.InstanceNorm2d(256), \n                  nn.LeakyReLU(0.2, inplace=True) ]\n\n        model += [nn.Conv2d(256, 512, 4, padding=1),\n                  nn.InstanceNorm2d(512), \n                  nn.LeakyReLU(0.2, inplace=True) ]\n\n        # FCN classification layer\n        model += [nn.Conv2d(512, 1, 4, padding=1)]\n\n        self.model = nn.Sequential(*model)\n\n    def forward(self, x):\n        x =  self.model(x)\n        return x ","metadata":{"execution":{"iopub.status.busy":"2022-05-04T06:01:53.700051Z","iopub.execute_input":"2022-05-04T06:01:53.700735Z","iopub.status.idle":"2022-05-04T06:01:53.713935Z","shell.execute_reply.started":"2022-05-04T06:01:53.700668Z","shell.execute_reply":"2022-05-04T06:01:53.712857Z"},"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-05-04T06:01:53.716012Z","iopub.execute_input":"2022-05-04T06:01:53.7164Z","iopub.status.idle":"2022-05-04T06:01:53.727937Z","shell.execute_reply.started":"2022-05-04T06:01:53.716333Z","shell.execute_reply":"2022-05-04T06:01:53.72676Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class CycleGAN(object):\n    def __init__(self, in_channels, out_channels, epochs, cuda_device,lr,b1,b2,decay_epoch=0, lambda_id=5.0, lambda_cycle=10.0):\n        \n        self.device = cuda_device\n        self.epochs = epochs\n        self.lambda_id = lambda_id\n        self.lambda_cycle = lambda_cycle\n        self.G_AB = Generator(in_channels)\n        self.F_BA = Generator(in_channels)\n        self.D_A = Discriminator(in_channels)\n        self.D_B = Discriminator(in_channels)\n        init_weights(self.G_AB)\n        init_weights(self.F_BA)\n        init_weights(self.D_A)\n        init_weights(self.D_B)\n        self.G_AB = self.G_AB.to(self.device)\n        self.F_BA = self.F_BA.to(self.device)\n        self.D_A = self.D_A.to(self.device)\n        self.D_B = self.D_B.to(self.device)\n        \n        self.criterion_GAN = nn.MSELoss().cuda()\n        self.criterion_cycle = nn.L1Loss().cuda()\n        self.criterion_identity = nn.L1Loss().cuda()\n        self.optimizer_G = torch.optim.Adam(itertools.chain(self.G_AB.parameters(), self.F_BA.parameters()), lr=lr, betas=(b1, b2))\n        self.optimizer_D_A = torch.optim.Adam(self.D_A.parameters(), lr=lr, betas=(b1, b2))\n        self.optimizer_D_B = torch.optim.Adam(self.D_B.parameters(), lr=lr, betas=(b1, b2))\n\n        lambda_func = lambda epoch: 1 - max(0, epoch-decay_epoch)/(epochs-decay_epoch)\n        self.lr_scheduler_G = torch.optim.lr_scheduler.LambdaLR(self.optimizer_G, lr_lambda=lambda_func)\n        self.lr_scheduler_D_A = torch.optim.lr_scheduler.LambdaLR(self.optimizer_D_A, lr_lambda=lambda_func)\n        self.lr_scheduler_D_B = torch.optim.lr_scheduler.LambdaLR(self.optimizer_D_B, lr_lambda=lambda_func)\n        \n    def sample_images(self,real_A, real_B, figside=1.5):\n        assert real_A.size() == real_B.size(), 'The image size for two domains must be the same'\n        cuda = torch.cuda.is_available()\n        Tensor = torch.cuda.FloatTensor if cuda else torch.Tensor\n        self.G_AB.eval()\n        self.F_BA.eval()        \n        real_A = real_A.type(Tensor)\n        fake_B = self.G_AB(real_A).detach()\n        real_B = real_B.type(Tensor)\n        fake_A = self.F_BA(real_B).detach()\n        \n        nrows = real_A.size(0)\n        real_A = make_grid(real_A, nrow=nrows, normalize=True)\n        fake_B = make_grid(fake_B, nrow=nrows, normalize=True)\n        real_B = make_grid(real_B, nrow=nrows, normalize=True)\n        fake_A = make_grid(fake_A, nrow=nrows, normalize=True)\n    \n        image_grid = torch.cat((real_A, fake_B, real_B, fake_A), 1).cpu().permute(1, 2, 0)\n    \n        plt.figure(figsize=(figside*nrows, figside*4))\n        plt.imshow(image_grid)\n        plt.axis('off')\n        plt.show() \n        \n    def train(self, data):\n        for epoch in range(self.epochs):\n            for i, (A_real, B_real) in enumerate(data):\n                A_real, B_real = A_real.to(self.device), B_real.to(self.device)\n                update_req_grad([self.D_A, self.D_B], False)\n                self.optimizer_G.zero_grad()\n                self.G_AB.train()\n                self.F_BA.train() \n        \n        \n                # Forward pass through generator\n                B_fake = self.G_AB(A_real)\n                A_fake = self.F_BA(B_real)\n                 \n                A_cycle = self.F_BA(B_fake)\n                B_cycle = self.G_AB(A_fake)\n\n                B_id = self.G_AB(B_real)\n                A_id = self.F_BA(A_real)\n                \n                # generator losses - identity, Adversarial, cycle consistency\n                id_loss = (self.criterion_identity(A_id, A_real) + self.criterion_identity(B_id, B_real)) * self.lambda_id \n                cycle_loss = (self.criterion_cycle(A_cycle, A_real) + self.criterion_cycle(B_cycle, B_real)) * self.lambda_cycle\n\n                A_GAN = self.D_A(A_fake)\n                B_GAN = self.D_B(B_fake)\n                valid = torch.ones(A_GAN.size()).to(self.device)\n                G_loss = self.criterion_GAN(A_GAN, valid) + self.criterion_GAN(B_GAN, valid)\n                    \n                total_loss = G_loss + id_loss + cycle_loss\n                    \n                # backward pass\n                total_loss.backward()\n                self.optimizer_G.step()\n                    \n                # Train Discriminator A\n                update_req_grad([self.D_A, self.D_B], True)\n                fake = torch.zeros(A_GAN.size()).to(self.device)\n                self.optimizer_D_A.zero_grad()\n        \n                loss_real = self.criterion_GAN(self.D_A(A_real), valid)\n                loss_fake = self.criterion_GAN(self.D_A(A_fake.detach()), fake)\n                loss_D_A = (loss_real + loss_fake) / 2\n        \n                loss_D_A.backward()\n                self.optimizer_D_A.step()\n        \n                # Train Discriminator B\n                self.optimizer_D_B.zero_grad()\n        \n                loss_real = self.criterion_GAN(self.D_B(B_real), valid)\n                loss_fake = self.criterion_GAN(self.D_B(B_fake.detach()), fake)\n                loss_D_B = (loss_real + loss_fake) / 2\n                                               \n                loss_D_B.backward()\n                self.optimizer_D_B.step()\n    \n            self.lr_scheduler_G.step()\n            self.lr_scheduler_D_A.step()\n            self.lr_scheduler_D_B.step()\n                    \n            if (epoch+1) % 10 == 0:\n                img_monet, img_photo = next(iter(data))\n                self.sample_images(img_monet, img_photo)\n                    \n                loss_D = (loss_D_A + loss_D_B) / 2\n                print(f'[Epoch {epoch+1}/{self.epochs}]')\n                print(f'[G loss: {total_loss.item()} | identity: {id_loss.item()} GAN: {G_loss.item()} cycle: {cycle_loss.item()}]')\n                print(f'[D loss: {loss_D.item()} | D_A: {loss_D_A.item()} D_B: {loss_D_B.item()}]')       ","metadata":{"execution":{"iopub.status.busy":"2022-05-04T06:01:53.729825Z","iopub.execute_input":"2022-05-04T06:01:53.73046Z","iopub.status.idle":"2022-05-04T06:01:53.769807Z","shell.execute_reply.started":"2022-05-04T06:01:53.730412Z","shell.execute_reply":"2022-05-04T06:01:53.768776Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"cuda_device = 'cuda' if torch.cuda.is_available() else 'cpu'\nprint(cuda_device)\nGAN=CycleGAN(3, 3, epochs, cuda_device, lr, b1, b2)","metadata":{"execution":{"iopub.status.busy":"2022-05-04T06:01:53.773967Z","iopub.execute_input":"2022-05-04T06:01:53.774671Z","iopub.status.idle":"2022-05-04T06:01:57.690246Z","shell.execute_reply.started":"2022-05-04T06:01:53.774621Z","shell.execute_reply":"2022-05-04T06:01:57.68927Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"GAN.train(img_Dataset)","metadata":{"execution":{"iopub.status.busy":"2022-05-04T06:01:57.691729Z","iopub.execute_input":"2022-05-04T06:01:57.693987Z","iopub.status.idle":"2022-05-04T08:06:45.588911Z","shell.execute_reply.started":"2022-05-04T06:01:57.693955Z","shell.execute_reply":"2022-05-04T08:06:45.587782Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Test","metadata":{}},{"cell_type":"code","source":"class PhotoDataset(Dataset):\n    def __init__(self, data_dir, size=(256, 256), normalize=True):\n        super().__init__()\n        self.photo_dir = os.path.join(data_dir, 'photo_jpg')\n        if normalize:\n            self.transform = transforms.Compose([\n                transforms.Resize(size),\n                transforms.ToTensor(),\n                transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))                                \n            ])\n        else:\n            self.transform = transforms.Compose([\n                transforms.Resize(size),\n                transforms.ToTensor()                               \n            ])\n        self.photo_file = [os.path.join(self.photo_dir, name) for name in sorted(os.listdir(self.photo_dir))]\n\n    def __getitem__(self, idx):\n        photo_img = Image.open(self.photo_file[idx])\n        photo_img = self.transform(photo_img)\n        return photo_img\n\n    def __len__(self):\n        return len(self.photo_file)\n\nphoto_Data = PhotoDataset('/kaggle/input/gan-getting-started')\nphoto_Dataset = DataLoader(photo_Data, batch_size=batch_size, num_workers = num_workers)","metadata":{"execution":{"iopub.status.busy":"2022-05-04T08:57:23.063589Z","iopub.execute_input":"2022-05-04T08:57:23.064156Z","iopub.status.idle":"2022-05-04T08:57:23.096253Z","shell.execute_reply.started":"2022-05-04T08:57:23.064121Z","shell.execute_reply":"2022-05-04T08:57:23.095326Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!mkdir ../images","metadata":{"execution":{"iopub.status.busy":"2022-05-04T08:57:26.520932Z","iopub.execute_input":"2022-05-04T08:57:26.521989Z","iopub.status.idle":"2022-05-04T08:57:29.048591Z","shell.execute_reply.started":"2022-05-04T08:57:26.521942Z","shell.execute_reply":"2022-05-04T08:57:29.047291Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"GAN.F_BA.eval()\ntrans = transforms.ToPILImage()\nfor i, photo in enumerate(photo_Dataset):\n    with torch.no_grad():\n        pred_monet = GAN.F_BA(photo.to(cuda_device)).cpu().detach()\n    pred_monet = unnorm(pred_monet)\n    for j in range(batch_size):\n        img = trans(pred_monet[j]).convert(\"RGB\")\n        img.save(\"../images/\" + str(3*i+j+1) + \".jpg\")","metadata":{"execution":{"iopub.status.busy":"2022-05-04T08:58:46.435057Z","iopub.execute_input":"2022-05-04T08:58:46.436016Z","iopub.status.idle":"2022-05-04T09:01:18.509636Z","shell.execute_reply.started":"2022-05-04T08:58:46.435971Z","shell.execute_reply":"2022-05-04T09:01:18.508392Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import shutil\nshutil.make_archive(\"/kaggle/working/images\", 'zip', \"/kaggle/images\")","metadata":{"execution":{"iopub.status.busy":"2022-05-04T09:01:26.577649Z","iopub.execute_input":"2022-05-04T09:01:26.578024Z","iopub.status.idle":"2022-05-04T09:01:31.242012Z","shell.execute_reply.started":"2022-05-04T09:01:26.577989Z","shell.execute_reply":"2022-05-04T09:01:31.240933Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}