{"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":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        os.path.join(dirname, filename)\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-06-09T05:44:31.905745Z","iopub.execute_input":"2022-06-09T05:44:31.906168Z","iopub.status.idle":"2022-06-09T05:44:33.157087Z","shell.execute_reply.started":"2022-06-09T05:44:31.906137Z","shell.execute_reply":"2022-06-09T05:44:33.156108Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nimport datetime\nimport os, sys, random\nimport matplotlib.pyplot as plt\nfrom matplotlib.pyplot import imshow, imsave\n%matplotlib inline\n\nimport torch\nimport torchvision\nimport torch.nn as nn\nfrom torch.nn import init\nimport torch.nn.functional as F\nfrom torchvision import datasets\nfrom torchvision import transforms\nfrom torchvision.utils import make_grid\nfrom torchvision.utils import save_image\nfrom torch.utils.data import DataLoader, Dataset\n\nfrom kaggle_datasets import KaggleDatasets\n\nimport PIL\nfrom PIL import Image\n","metadata":{"execution":{"iopub.status.busy":"2022-06-09T05:44:33.159904Z","iopub.execute_input":"2022-06-09T05:44:33.160643Z","iopub.status.idle":"2022-06-09T05:44:34.030715Z","shell.execute_reply.started":"2022-06-09T05:44:33.160596Z","shell.execute_reply":"2022-06-09T05:44:34.029705Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# DataLoaders\nIMG_SIZE = 224\nbatch_size_test_dataloader = 8\nbatch_size_dataloader = 16\nworkers = 2 # number of worker threads for loading the data with the DataLoader\nmean_ = 0.5\nstd_ = 0.5\n\n# Model params\nSEED = 42\nEPOCHS = 120\nMODEL_NAME = 'ConditionalGAN'\nn_res_blocks = 6\ng_conv_dim=64\nd_conv_dim=64\nbatch_size = 64\ncondition_size = 10\nmax_epoch = 30 # need more than 100 epochs for training generator\nstep = 0\nn_critic = 1 # for training more k steps about Discriminator\nn_noise = 100\nlr = 0.0002\nBETAS = (0.5, 0.999)","metadata":{"execution":{"iopub.status.busy":"2022-06-09T05:44:34.035125Z","iopub.execute_input":"2022-06-09T05:44:34.035461Z","iopub.status.idle":"2022-06-09T05:44:34.045106Z","shell.execute_reply.started":"2022-06-09T05:44:34.035427Z","shell.execute_reply":"2022-06-09T05:44:34.043412Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Read data\n\nLoad dataset and plot some of the images","metadata":{}},{"cell_type":"code","source":"# Get the competition given data\nGCS_PATH = KaggleDatasets().get_gcs_path('gan-getting-started') \nGCS_PATH_MONET = '../input/gan-getting-started/monet_jpg/'\nGCS_PATH_PHOTO = '../input/gan-getting-started/photo_jpg/'\n\nprint(GCS_PATH)","metadata":{"execution":{"iopub.status.busy":"2022-06-09T05:44:34.047706Z","iopub.execute_input":"2022-06-09T05:44:34.049095Z","iopub.status.idle":"2022-06-09T05:44:34.611934Z","shell.execute_reply.started":"2022-06-09T05:44:34.049047Z","shell.execute_reply":"2022-06-09T05:44:34.610381Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":" class ImageDataset(Dataset):\n        \"\"\"\n        Class to load a custom dataset\n        \"\"\"\n        \n        def __init__(self, img_path, img_size=IMG_SIZE, normalize=True):\n            self.img_path = img_path\n            \n            if normalize:\n                self.transform = transforms.Compose([\n                    transforms.Resize(IMG_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(IMG_SIZE),\n                    transforms.ToTensor()\n                ])\n            \n            #Dictionary entries\n            self.img_idx = dict()\n            for number_, img_ in enumerate(os.listdir(self.img_path)):\n                self.img_idx[number_] = img_\n                \n        def __len__(self):\n            #Length of dataset --> number of images\n            return len(self.img_idx)\n        \n        def __getitem__(self, idx):\n            img_path = os.path.join(self.img_path, self.img_idx[idx])\n            img = Image.open(img_path)\n            img = self.transform(img)\n            \n            return img\n        \ndef reverse_normalize(image, mean_=mean_, std_=std_):\n    if torch.is_tensor(image):\n        image = image.detach().numpy()\n    un_normalized_img = image * std_ + mean_\n    un_normalized_img = un_normalized_img * 255\n    return np.uint8(un_normalized_img)\n\n\n# Create datasets\ndataset_monet = ImageDataset(GCS_PATH_MONET, img_size=IMG_SIZE, normalize=True) #monet\ndataset_original = ImageDataset(GCS_PATH_PHOTO, img_size=IMG_SIZE, normalize=True) #photo","metadata":{"execution":{"iopub.status.busy":"2022-06-09T05:44:34.622190Z","iopub.execute_input":"2022-06-09T05:44:34.625299Z","iopub.status.idle":"2022-06-09T05:44:34.666161Z","shell.execute_reply.started":"2022-06-09T05:44:34.625150Z","shell.execute_reply":"2022-06-09T05:44:34.664805Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create test and train loaders\n\n# Test DataLoader\ntest_dataloader_Y = DataLoader(dataset_monet,\n                               batch_size=batch_size_test_dataloader,\n                               shuffle=False, \n                               num_workers=workers,\n                               pin_memory=True)\ntest_dataloader_X = DataLoader(dataset_original,\n                               batch_size=batch_size_test_dataloader,\n                               shuffle=False, \n                               num_workers=workers,\n                               pin_memory=True)\n\n# Train DataLoader\ndataloader_Y = DataLoader(dataset_monet,\n                          batch_size=batch_size_dataloader,\n                          shuffle=True, \n                          num_workers=workers,\n                          pin_memory=True)\ndataloader_X = DataLoader(dataset_original,\n                          batch_size=batch_size_dataloader,\n                          shuffle=True, \n                          num_workers=workers,\n                          pin_memory=True)","metadata":{"execution":{"iopub.status.busy":"2022-06-09T05:44:34.670674Z","iopub.execute_input":"2022-06-09T05:44:34.671323Z","iopub.status.idle":"2022-06-09T05:44:34.682048Z","shell.execute_reply.started":"2022-06-09T05:44:34.671275Z","shell.execute_reply":"2022-06-09T05:44:34.680368Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Check images from monet dataset\ndataiter = iter(test_dataloader_Y)\nimages_normalized = dataiter.next()\ngrid_normalized = make_grid(images_normalized, nrow=4).permute(1, 2, 0).detach().numpy()\ngrid_original = reverse_normalize(grid_normalized)\nfig = plt.figure(figsize=(12, 8))\nplt.imshow(grid_original)\nplt.axis('off')\nplt.title('Monet paintings')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-06-09T05:44:34.684158Z","iopub.execute_input":"2022-06-09T05:44:34.684960Z","iopub.status.idle":"2022-06-09T05:44:40.150746Z","shell.execute_reply.started":"2022-06-09T05:44:34.684914Z","shell.execute_reply":"2022-06-09T05:44:40.149120Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Check photo images from dataset \ndataiter = iter(test_dataloader_X)\nimages_normalized = dataiter.next()\ngrid_normalized = make_grid(images_normalized, nrow=4).permute(1, 2, 0).detach().numpy()\ngrid_original = reverse_normalize(grid_normalized)\nfig = plt.figure(figsize=(12, 8))\nplt.imshow(grid_original)\nplt.axis('off')\nplt.title('photo')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-06-09T05:44:40.152791Z","iopub.execute_input":"2022-06-09T05:44:40.153185Z","iopub.status.idle":"2022-06-09T05:44:40.663705Z","shell.execute_reply.started":"2022-06-09T05:44:40.153146Z","shell.execute_reply":"2022-06-09T05:44:40.662558Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Check device \n# Get the GPU device name if available.\nif torch.cuda.is_available():    \n\n    # Tell PyTorch to use the GPU.    \n    device = torch.device(\"cuda\")\n\n    print('There are %d GPU(s) available. {}'.format(torch.cuda.device_count()))\n\n    print('We will use the GPU: {}'.format(torch.cuda.get_device_name(0)))\n\n# If we dont have GPU but a CPU, training will take place on CPU instead\nelse:\n    print('No GPU available, using the CPU instead.')\n    device = torch.device(\"cpu\")\n    \n# Set the seed value all over the place to make this reproducible.\nrandom.seed(SEED)\nnp.random.seed(SEED)\ntorch.manual_seed(SEED)\ntorch.cuda.manual_seed_all(SEED)","metadata":{"execution":{"iopub.status.busy":"2022-06-09T05:44:40.666278Z","iopub.execute_input":"2022-06-09T05:44:40.667111Z","iopub.status.idle":"2022-06-09T05:44:40.685307Z","shell.execute_reply.started":"2022-06-09T05:44:40.667053Z","shell.execute_reply":"2022-06-09T05:44:40.683693Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Support functions\nTo define the generators, you're expected to use the above <i>conv</i> function, <i>ResidualBlock</i> class, and the below <i>deconv</i> helper function, which creates a transpose convolutional layer + an optional batch norm layer.","metadata":{}},{"cell_type":"code","source":"# helper conv function\ndef conv(in_channels, out_channels, kernel_size, stride=2, padding=1, batch_norm=True):\n    \"\"\"Creates a convolutional layer, with optional batch normalization.\n    \"\"\"\n    layers = []\n    conv_layer = nn.Conv2d(in_channels=in_channels, out_channels=out_channels, \n                           kernel_size=kernel_size, stride=stride, padding=padding, bias=False)\n    \n    layers.append(conv_layer)\n\n    if batch_norm:\n        layers.append(nn.BatchNorm2d(out_channels))\n    return nn.Sequential(*layers)","metadata":{"execution":{"iopub.status.busy":"2022-06-09T05:44:40.687957Z","iopub.execute_input":"2022-06-09T05:44:40.689081Z","iopub.status.idle":"2022-06-09T05:44:40.700443Z","shell.execute_reply.started":"2022-06-09T05:44:40.689017Z","shell.execute_reply":"2022-06-09T05:44:40.699276Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# helper deconv function\ndef deconv(in_channels, out_channels, kernel_size, stride=2, padding=1, batch_norm=True):\n    \"\"\"Creates a transpose convolutional layer, with optional batch normalization.\n    \"\"\"\n    layers = []\n    # append transpose conv layer\n    layers.append(nn.ConvTranspose2d(in_channels, out_channels, kernel_size, stride, padding, bias=False))\n    # optional batch norm layer\n    if batch_norm:\n        layers.append(nn.BatchNorm2d(out_channels))\n    return nn.Sequential(*layers)","metadata":{"execution":{"iopub.status.busy":"2022-06-09T05:44:40.704797Z","iopub.execute_input":"2022-06-09T05:44:40.705142Z","iopub.status.idle":"2022-06-09T05:44:40.715161Z","shell.execute_reply.started":"2022-06-09T05:44:40.705111Z","shell.execute_reply":"2022-06-09T05:44:40.713908Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class ResidualBlock(nn.Module):\n    def __init__(self, input_features):\n        super(ResidualBlock, self).__init__()\n\n        conv_layers = [\n                nn.ReflectionPad2d(1),\n                *conv(input_features, input_features, kernel_size=3, stride=1, padding=0),\n                nn.ReflectionPad2d(1),\n                *conv(input_features, input_features, kernel_size=3, stride=1, padding=0)\n            ]\n        self.model = nn.Sequential(*conv_layers)\n\n    def forward(self, input_data):\n        return input_data + self.model(input_data)","metadata":{"execution":{"iopub.status.busy":"2022-06-09T05:44:40.717061Z","iopub.execute_input":"2022-06-09T05:44:40.717689Z","iopub.status.idle":"2022-06-09T05:44:40.729131Z","shell.execute_reply.started":"2022-06-09T05:44:40.717657Z","shell.execute_reply":"2022-06-09T05:44:40.727705Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_sample_image(G, n_noise=100):\n    \"\"\"\n        save sample 100 images\n        G refers to the generator.\n    \"\"\"\n    img = np.zeros([280, 280])\n    for j in range(10):\n        c = torch.zeros([10, 10]).to(device)\n        c[:, j] = 1\n        z = torch.randn(10, n_noise).to(device)\n        y_hat = G(z,c).view(10, 28, 28)\n        result = y_hat.cpu().data.numpy()\n        img[j*28:(j+1)*28] = np.concatenate([x for x in result], axis=-1)\n    return img","metadata":{"execution":{"iopub.status.busy":"2022-06-09T05:44:40.731338Z","iopub.execute_input":"2022-06-09T05:44:40.732265Z","iopub.status.idle":"2022-06-09T05:44:40.745279Z","shell.execute_reply.started":"2022-06-09T05:44:40.732218Z","shell.execute_reply":"2022-06-09T05:44:40.744045Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def to_onehot(x, num_classes=4):\n    assert isinstance(x, int) or isinstance(x, (torch.LongTensor, torch.cuda.LongTensor))\n    if isinstance(x, int):\n        c = torch.zeros(1, num_classes).long()\n        c[0][x] = 1\n    else:\n        x = x.cpu()\n        c = torch.cuda.LongTensor(x.size(0), num_classes)\n        c.zero_()\n        c.scatter_(1, x, 1) # dim, index, src value\n    return c","metadata":{"execution":{"iopub.status.busy":"2022-06-09T05:44:40.748800Z","iopub.execute_input":"2022-06-09T05:44:40.749878Z","iopub.status.idle":"2022-06-09T05:44:40.760002Z","shell.execute_reply.started":"2022-06-09T05:44:40.749818Z","shell.execute_reply":"2022-06-09T05:44:40.758926Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def weights_init_normal(m):\n    \"\"\"\n    Applies initial weights to certain layers in a model.\n    The weights are taken from a normal distribution with mean = 0, std dev = 0.02.\n    Param m: A module or layer in a network    \n    \"\"\"\n    #classname will be something like: `Conv`, `BatchNorm2d`, `Linear`, etc.\n    classname = m.__class__.__name__\n    \n    #normal distribution with given paramters\n    std_dev = 0.02\n    mean = 0.0\n    \n    # Initialize conv layer\n    if hasattr(m, 'weight') and (classname.find('Conv') != -1):\n        init.normal_(m.weight.data, mean, std_dev)","metadata":{"execution":{"iopub.status.busy":"2022-06-09T05:44:40.762363Z","iopub.execute_input":"2022-06-09T05:44:40.763184Z","iopub.status.idle":"2022-06-09T05:44:40.773856Z","shell.execute_reply.started":"2022-06-09T05:44:40.763133Z","shell.execute_reply":"2022-06-09T05:44:40.772125Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Define Discriminator and Generator classes","metadata":{}},{"cell_type":"code","source":"class Discriminator(nn.Module):\n    \n    def __init__(self, conv_dim=64):\n        super(Discriminator, self).__init__()\n        \"\"\"\n        Input is RGB image (256x256x3) while output is a single value\n        \n        determine size = [(W−K+2P)/S]+1\n        W: input=256\n        K: kernel_size=4\n        P: padding=1\n        S: stride=2\n        \"\"\"\n        \n        #convolutional layers, increasing in depth\n        self.conv1 = conv(in_channels=3, out_channels=conv_dim, kernel_size=4) # (128, 128, 64)\n        self.conv2 = conv(in_channels=conv_dim, out_channels=conv_dim*2, kernel_size=4) # (64, 64, 128)\n        self.conv3 = conv(in_channels=conv_dim*2, out_channels=conv_dim*4, kernel_size=4) # (32, 32, 256)\n        self.conv4 = conv(in_channels=conv_dim*4, out_channels=conv_dim*8, kernel_size=4) # (16, 16, 512)\n        self.conv5 = conv(in_channels=conv_dim*8, out_channels=conv_dim*8, kernel_size=4) # (8, 8, 512)\n        \n        #final classification layer\n        self.conv6 = conv(conv_dim*8, out_channels=1, kernel_size=4, stride=1) # (8, 8, 1)\n    \n    def forward(self, x):\n        \n        #leaky relu applied to all conv layers but last\n        out = F.leaky_relu(self.conv1(x), negative_slope=0.2)\n        out = F.leaky_relu(self.conv2(out), negative_slope=0.2)\n        out = F.leaky_relu(self.conv3(out), negative_slope=0.2)\n        out = F.leaky_relu(self.conv4(out), negative_slope=0.2)\n#         out = F.leaky_relu(self.conv5(out), negative_slope=0.2)\n        \n        #classification layer (--> depending on the loss function we might want to use an activation function here, e.g. sigmoid)\n        out = self.conv6(out)\n        return out","metadata":{"execution":{"iopub.status.busy":"2022-06-09T05:44:40.776220Z","iopub.execute_input":"2022-06-09T05:44:40.777041Z","iopub.status.idle":"2022-06-09T05:44:40.793945Z","shell.execute_reply.started":"2022-06-09T05:44:40.776978Z","shell.execute_reply":"2022-06-09T05:44:40.792355Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Generator(nn.Module):\n    \n    def __init__(self, conv_dim, n_res_blocks):\n        super(Generator, self).__init__()\n\n        # 1. Define the encoder part of the generator\n        \n        # initial convolutional layer given, below\n        self.conv1 = conv(3, conv_dim, 4)\n        self.conv2 = conv(conv_dim, conv_dim*2, 4)\n        self.conv3 = conv(conv_dim*2, conv_dim*4, 4)\n\n        # 2. Define the resnet part of the generator\n        # Residual blocks\n        res_layers = []\n        for layer in range(n_res_blocks):\n            res_layers.append(ResidualBlock(conv_dim*4))\n        # use sequential to create these layers\n        self.res_blocks = nn.Sequential(*res_layers)\n\n        # 3. Define the decoder part of the generator\n        # two transpose convolutional layers and a third that looks a lot like the initial conv layer\n        self.deconv1 = deconv(conv_dim*4, conv_dim*2, 4)\n        self.deconv2 = deconv(conv_dim*2, conv_dim, 4)\n        # no batch norm on last layer\n        self.deconv3 = deconv(conv_dim, 3, 4, batch_norm=False)\n\n    def forward(self, x):\n        \"\"\"Given an image x, returns a transformed image.\"\"\"\n        # define feedforward behavior, applying activations as necessary\n\n        out = F.relu(self.conv1(x))\n        out = F.relu(self.conv2(out))\n        out = F.relu(self.conv3(out))\n\n        out = self.res_blocks(out)\n\n        out = F.relu(self.deconv1(out))\n        out = F.relu(self.deconv2(out))\n        # tanh applied to last layer\n        out = torch.tanh(self.deconv3(out))\n\n        return out","metadata":{"execution":{"iopub.status.busy":"2022-06-09T05:44:40.796311Z","iopub.execute_input":"2022-06-09T05:44:40.797173Z","iopub.status.idle":"2022-06-09T05:44:40.814752Z","shell.execute_reply.started":"2022-06-09T05:44:40.797123Z","shell.execute_reply":"2022-06-09T05:44:40.813559Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def build_model(g_conv_dim=g_conv_dim, d_conv_dim=d_conv_dim, n_res_blocks=n_res_blocks):\n    \"\"\"\n    Builds generators G_XtoY & G_YtoX and discriminators D_X & D_Y \n    \"\"\"\n    ngpu=1\n    \n    #Generators G_XtoY and G_YtoX\n    G_XtoY = Generator(conv_dim=g_conv_dim, \n                            n_res_blocks=n_res_blocks)\n    G_YtoX = Generator(conv_dim=g_conv_dim, \n                            n_res_blocks=n_res_blocks)\n    \n    #Discriminators\n    D_X = Discriminator(conv_dim=d_conv_dim) # Y-->X\n    D_Y = Discriminator(conv_dim=d_conv_dim) # X-->Y\n\n    #Weight initialization\n    G_XtoY.apply(weights_init_normal)\n    G_YtoX.apply(weights_init_normal)\n    D_X.apply(weights_init_normal)\n    D_Y.apply(weights_init_normal)\n    \n    #Moves models to GPU, if available\n    if torch.cuda.is_available():\n        device = torch.device(\"cuda:0\")\n        G_XtoY.to(device)\n        G_YtoX.to(device)\n        D_X.to(device)\n        D_Y.to(device)\n        print('Models moved to GPU.')\n    else:\n        print('Only CPU available.')\n\n    return G_XtoY, G_YtoX, D_X, D_Y","metadata":{"execution":{"iopub.status.busy":"2022-06-09T05:44:40.816863Z","iopub.execute_input":"2022-06-09T05:44:40.817665Z","iopub.status.idle":"2022-06-09T05:44:40.831883Z","shell.execute_reply.started":"2022-06-09T05:44:40.817622Z","shell.execute_reply":"2022-06-09T05:44:40.830695Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Define the loss functions","metadata":{}},{"cell_type":"code","source":"def real_mse_loss(D_out):\n    # how close is the produced output from being \"real\"?\n    return torch.mean((D_out-1)**2)\n\ndef fake_mse_loss(D_out):\n    # how close is the produced output from being \"false\"?\n    return torch.mean(D_out**2)\n\ndef cycle_consistency_loss(real_im, reconstructed_im, lambda_weight):\n    # calculate reconstruction loss \n    # as absolute value difference between the real and reconstructed images\n    reconstr_loss = torch.mean(torch.abs(real_im - reconstructed_im))\n    # return weighted loss\n    return lambda_weight*reconstr_loss\ndef identity_loss(real_img, generated_img, identity_weight=1):\n    ident_loss = torch.mean(torch.abs(real_img - generated_img))\n    return identity_weight*ident_loss","metadata":{"execution":{"iopub.status.busy":"2022-06-09T05:44:40.833818Z","iopub.execute_input":"2022-06-09T05:44:40.834607Z","iopub.status.idle":"2022-06-09T05:44:40.846645Z","shell.execute_reply.started":"2022-06-09T05:44:40.834556Z","shell.execute_reply":"2022-06-09T05:44:40.845495Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Function call\nG_XtoY, G_YtoX, D_X, D_Y = build_model()","metadata":{"execution":{"iopub.status.busy":"2022-06-09T05:44:40.848852Z","iopub.execute_input":"2022-06-09T05:44:40.849760Z","iopub.status.idle":"2022-06-09T05:44:41.575261Z","shell.execute_reply.started":"2022-06-09T05:44:40.849662Z","shell.execute_reply":"2022-06-09T05:44:41.574014Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def show_test_result(fixed_Y, fixed_X, G_YtoX, G_XtoY, mean_=mean_, std_=std_):\n    \"\"\"\n    Shows results of generates based on test image input. \n    \"\"\"\n    #Identify correct device\n    device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n    \n    #Create fake pictures for both cycles\n    fake_X = G_YtoX(fixed_Y.to(device))\n    fake_Y = G_XtoY(fixed_X.to(device))\n    \n    #Generate grids\n    grid_x =  make_grid(fixed_X, nrow=4).permute(1, 2, 0).detach().cpu().numpy()\n    grid_y =  make_grid(fixed_Y, nrow=4).permute(1, 2, 0).detach().cpu().numpy()\n    grid_fake_x =  make_grid(fake_X, nrow=4).permute(1, 2, 0).detach().cpu().numpy()\n    grid_fake_y =  make_grid(fake_Y, nrow=4).permute(1, 2, 0).detach().cpu().numpy()\n    \n    #Normalize pictures to pixel range rom 0 to 255\n    X, fake_X = reverse_normalize(grid_x, mean_, std_), reverse_normalize(grid_fake_x, mean_, std_)\n    Y, fake_Y = reverse_normalize(grid_y, mean_, std_), reverse_normalize(grid_fake_y, mean_, std_)\n    \n    #Transformation from X -> Y\n    fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True, sharey=True, figsize=(20, 10))\n    ax1.imshow(X)\n    ax1.axis('off')\n    ax1.set_title('X')\n    ax2.imshow(fake_Y)\n    ax2.axis('off')\n    ax2.set_title('Fake Y  (Monet-esque)')\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2022-06-09T05:44:41.576948Z","iopub.execute_input":"2022-06-09T05:44:41.577716Z","iopub.status.idle":"2022-06-09T05:44:41.593912Z","shell.execute_reply.started":"2022-06-09T05:44:41.577666Z","shell.execute_reply":"2022-06-09T05:44:41.593039Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Define the training loop","metadata":{}},{"cell_type":"code","source":"def training_loop(dataloader_X, dataloader_Y, test_dataloader_X, test_dataloader_Y, n_epochs=1000):\n    \n    #Losses over time\n    losses = []\n    \n    #Additional weighting parameters (in reality only 2 are required as the third is kind of \"given relatively\" by the other two)\n    adverserial_weight = 0.5\n    lambda_weight = 10\n    identity_weight = 5\n    \n    #Get some fixed data from domains X and Y for sampling. Images are held constant throughout training and allow us to inspect the model's performance.\n    test_iter_X = iter(test_dataloader_X)\n    test_iter_Y = iter(test_dataloader_Y)\n    fixed_X = test_iter_X.next()\n    fixed_Y = test_iter_Y.next()\n    \n    # batches per epoch\n    iter_X = iter(dataloader_X)\n    iter_Y = iter(dataloader_Y)\n    batches_per_epoch = min(len(iter_X), len(iter_Y))\n    \n    #Average loss over batches per epoch runs\n    d_total_loss_avg = 0.0\n    g_total_loss_avg = 0.0\n    d_loss_avg_X = 0.0\n    d_loss_avg_Y = 0.0\n    \n    # Optimizer Adam for Generator and Discriminator\n    g_params = list(G_XtoY.parameters()) + list(G_YtoX.parameters())\n    Gen_opt = torch.optim.Adam(g_params, lr=lr, betas=BETAS)\n    DX_opt = torch.optim.Adam(D_X.parameters(), lr=lr, betas=BETAS)\n    DY_opt = torch.optim.Adam(D_Y.parameters(), lr=lr, betas=BETAS)\n    \n    #Loop through epochs\n    for epoch in range(1, n_epochs+1):\n        \n        #reset iterators for each epoch\n        if epoch % batches_per_epoch == 0:\n            iter_X = iter(dataloader_X)\n            iter_Y = iter(dataloader_Y)\n        \n        #Get images from domain X\n        images_X = iter_X.next()\n        \n        #Get images from domain Y\n        images_Y = iter_Y.next()\n        \n        #move images to GPU if available (otherwise stay on CPU)\n        device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n        images_X = images_X.to(device)\n        images_Y = images_Y.to(device)\n        \n        \n        # ============================================\n        #            TRAIN THE DISCRIMINATORS\n        # ============================================\n        \n        \n        # --------------------------------------------\n        ## First: D_X, real and fake loss components\n        # --------------------------------------------\n        \n        # Train with real images\n        DX_opt.zero_grad()\n        \n        # 1. Compute the discriminator losses on real images\n        out_x = D_X(images_X)\n        D_X_real_loss = real_mse_loss(out_x)\n        \n        # Train with fake images\n        # 2. Generate fake images that look like domain X based on real images in domain Y\n        fake_X = G_YtoX(images_Y)\n\n        # 3. Compute the fake loss for D_X\n        out_x = D_X(fake_X)\n        D_X_fake_loss = fake_mse_loss(out_x)\n        \n        # 4. Compute the total loss and perform backpropagation\n        d_x_loss = D_X_real_loss + D_X_fake_loss\n        d_x_loss.backward()\n        DX_opt.step()\n        \n        # --------------------------------------------\n        ## Second: D_Y, real and fake loss components\n        # --------------------------------------------\n        \n        # Train with real images\n        DY_opt.zero_grad()\n        \n        # 1. Compute the discriminator losses on real images\n        out_y = D_Y(images_Y)\n        D_Y_real_loss = real_mse_loss(out_y)\n        \n        # Train with fake images\n        # 2. Generate fake images that look like domain Y based on real images in domain X\n        fake_Y = G_XtoY(images_X)\n        \n        # 3. Compute the fake loss for D_Y\n        out_y = D_Y(fake_Y)\n        D_Y_fake_loss = fake_mse_loss(out_y)\n        \n        # 4. Compute the total loss and perform backprop\n        d_y_loss = D_Y_real_loss + D_Y_fake_loss\n        d_y_loss.backward()\n        DY_opt.step()\n        \n        # 5. Compute total discriminator loss\n        d_total_loss_X = D_X_real_loss + D_X_fake_loss\n        d_total_loss_Y = D_Y_real_loss + D_Y_fake_loss\n        d_total_loss = d_total_loss_X + d_total_loss_Y\n        \n        # TRAIN THE GENERATORS\n        \n        # --------------------------------------------\n        ## First: generate fake X images and reconstructed Y images\n        # --------------------------------------------\n        \n        #Back to the start\n        Gen_opt.zero_grad()\n        \n        # 1. Generate fake images that look like domain X based on real images in domain Y\n        fake_X = G_YtoX(images_Y)\n        \n        # 2. Compute the generator loss based on domain X\n        out_x = D_X(fake_X)\n        g_YtoX_loss = real_mse_loss(out_x)\n\n        # 3. Create a reconstructed y\n        reconstructed_Y = G_XtoY(fake_X)\n        \n        # 4. Compute the cycle consistency loss (the reconstruction loss)\n        reconstructed_y_loss = cycle_consistency_loss(images_Y, reconstructed_Y, lambda_weight=lambda_weight)\n        \n        # 5. Compute the identity loss from transformation Y-->X\n        identity_y_loss = identity_loss(images_Y, fake_X, identity_weight=identity_weight)\n        \n        # --------------------------------------------\n        ## Second: generate fake Y images and reconstructed X images\n        # --------------------------------------------\n        \n        # 1. Generate fake images that look like domain Y based on real images in domain X\n        fake_Y = G_XtoY(images_X)\n        \n        # 2. Compute the generator loss based on domain Y\n        out_y = D_Y(fake_Y) #if discriminator believes picture to be from domain Y it returns values cloer to 1, else closer to 0\n        g_XtoY_loss = real_mse_loss(out_y)\n        \n        # 3. Create a reconstructed x\n        reconstructed_X = G_YtoX(fake_Y)\n        \n        # 4. Compute the cycle consistency loss (the reconstruction loss)\n        reconstructed_x_loss = cycle_consistency_loss(images_X, reconstructed_X, lambda_weight=lambda_weight)\n        \n        # 5. Compute the identity loss from transformation X-->Y\n        identity_x_loss = identity_loss(images_X, fake_Y, identity_weight=identity_weight)\n        \n        # 6. Add up all generator and reconstructed losses and perform backprop\n        g_total_loss = g_YtoX_loss + g_XtoY_loss + reconstructed_y_loss + reconstructed_x_loss + identity_y_loss + identity_x_loss\n        g_total_loss.backward()\n        Gen_opt.step()\n\n        \n        #Average loss\n        d_loss_avg_X = d_loss_avg_X + d_total_loss_X / batches_per_epoch\n        d_loss_avg_Y = d_loss_avg_Y + d_total_loss_Y / batches_per_epoch\n        d_total_loss_avg =  d_total_loss_avg + d_total_loss / batches_per_epoch\n        g_total_loss_avg = g_total_loss_avg + g_total_loss / batches_per_epoch\n        \n        # Print log info\n        print_every = batches_per_epoch\n        if epoch % print_every == 0:\n            # append real and fake discriminator losses and the generator loss\n            losses.append((d_total_loss_avg.item(), g_total_loss_avg.item(),d_loss_avg_X.item(), d_loss_avg_Y.item()))\n            true_epoch_n = int(epoch/batches_per_epoch)\n            true_epoch_total = int(n_epochs/batches_per_epoch)\n            print('Epoch [{:4d}/{:4d}] | d_total_loss_avg: {:6.4f} | g_total_loss: {:6.4f}'.format(\n                    true_epoch_n, true_epoch_total, d_total_loss_avg.item(), g_total_loss_avg.item()))\n        \n        #Show the generated samples\n        show_every = (batches_per_epoch*50)\n        if epoch % show_every == 0:\n            #set generators to eval mode for image generation\n            G_YtoX.eval()\n            G_XtoY.eval()\n            test_images = show_test_result(fixed_Y, fixed_X, G_YtoX, G_XtoY)\n            #set generators to train mode to continue training\n            G_YtoX.train()\n            G_XtoY.train()\n        \n#         #save the model parameters\n#         checkpoint_every=3000\n#         if epoch % checkpoint_every == 0:\n#             save_checkpoint(epoch, G_XtoY, G_YtoX, D_X, D_Y)\n    \n        #reset average loss for each epoch\n        if epoch % batches_per_epoch == 0:\n            d_total_loss_avg = 0.0\n            g_total_loss_avg = 0.0\n            d_loss_avg_X = 0.0\n            d_loss_avg_Y = 0.0\n    \n    return losses","metadata":{"execution":{"iopub.status.busy":"2022-06-09T05:44:41.596636Z","iopub.execute_input":"2022-06-09T05:44:41.597336Z","iopub.status.idle":"2022-06-09T05:44:41.634269Z","shell.execute_reply.started":"2022-06-09T05:44:41.597287Z","shell.execute_reply":"2022-06-09T05:44:41.633220Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"batches_per_epoch = min(len(dataloader_X), len(dataloader_Y))\nepochs = EPOCHS\nn_epochs = epochs * batches_per_epoch","metadata":{"execution":{"iopub.status.busy":"2022-06-09T05:44:41.635595Z","iopub.execute_input":"2022-06-09T05:44:41.636121Z","iopub.status.idle":"2022-06-09T05:44:41.652532Z","shell.execute_reply.started":"2022-06-09T05:44:41.636073Z","shell.execute_reply":"2022-06-09T05:44:41.651388Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Train","metadata":{}},{"cell_type":"code","source":"losses = training_loop(dataloader_X, \n                       dataloader_Y, \n                       test_dataloader_X, \n                       test_dataloader_Y, \n                       n_epochs=n_epochs)","metadata":{"execution":{"iopub.status.busy":"2022-06-09T05:44:41.654780Z","iopub.execute_input":"2022-06-09T05:44:41.655729Z","iopub.status.idle":"2022-06-09T06:10:38.076285Z","shell.execute_reply.started":"2022-06-09T05:44:41.655682Z","shell.execute_reply":"2022-06-09T06:10:38.074915Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Plot losses","metadata":{}},{"cell_type":"code","source":"plt.xlabel(\"Epochs\")\nplt.ylabel(\"Losses\")\nplt.plot(losses[0], 'red', label='Generator Total Loss', alpha=0.5)\nplt.plot(losses[1], 'blue', label='Descriminator Total Loss', alpha=0.5)\nplt.plot(losses[2], 'green', label='Descriminator X Loss', alpha=0.5)\nplt.plot(losses[3], 'orange', label='Descriminator Y Loss', alpha=0.5)\nplt.title(\"Training Losses\")\nplt.plot()\nplt.legend()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-06-09T06:10:38.078407Z","iopub.execute_input":"2022-06-09T06:10:38.078732Z","iopub.status.idle":"2022-06-09T06:10:38.282232Z","shell.execute_reply.started":"2022-06-09T06:10:38.078701Z","shell.execute_reply":"2022-06-09T06:10:38.281363Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Pic and Monet-esque version in test set","metadata":{}},{"cell_type":"code","source":"test_iter_X = iter(test_dataloader_X)\ntest_iter_Y = iter(test_dataloader_Y)","metadata":{"execution":{"iopub.status.busy":"2022-06-09T06:10:38.283912Z","iopub.execute_input":"2022-06-09T06:10:38.284291Z","iopub.status.idle":"2022-06-09T06:10:38.436318Z","shell.execute_reply.started":"2022-06-09T06:10:38.284248Z","shell.execute_reply":"2022-06-09T06:10:38.433729Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Sample\nfixed_X = test_iter_X.next()\n\n#Evaluation\nG_XtoY.eval()\n\n#Identify correct device\ndevice = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n    \n#Create fake pictures\nfake_Y = G_XtoY(fixed_X.to(device))\n    \n#Generate grids\ngrid_x =  make_grid(fixed_X, nrow=4).permute(1, 2, 0).detach().cpu().numpy()\ngrid_fake_y =  make_grid(fake_Y, nrow=4).permute(1, 2, 0).detach().cpu().numpy()  \n    \n#Normalize pictures to pixel range rom 0 to 255\nY, fake_X_ = reverse_normalize(grid_x, mean_, std_), reverse_normalize(grid_fake_y, mean_, std_)\n\n#Transformation from Y -> X\nfig, (ax1, ax2) = plt.subplots(2, 1, \n                               sharex=True, \n                               sharey=True, \n                               figsize=(30, 20))\nax1.imshow(Y)\nax1.axis('off')\nax1.set_title('Original pic')\nax2.imshow(fake_X_)\nax2.axis('off')\nax2.set_title('Monet-esque')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-06-09T06:10:38.442300Z","iopub.execute_input":"2022-06-09T06:10:38.446725Z","iopub.status.idle":"2022-06-09T06:10:39.769886Z","shell.execute_reply.started":"2022-06-09T06:10:38.446675Z","shell.execute_reply":"2022-06-09T06:10:39.766566Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Sample\nfixed_Y = test_iter_Y.next()\n\n#Evaluation\nG_YtoX.eval()\n\n#Identify correct device\ndevice = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n    \n#Create fake pictures\nfake_X = G_YtoX(fixed_Y.to(device))\n    \n#Generate grids\ngrid_y =  make_grid(fixed_Y, nrow=4).permute(1, 2, 0).detach().cpu().numpy()\ngrid_fake_x =  make_grid(fake_X, nrow=4).permute(1, 2, 0).detach().cpu().numpy()  \n    \n#Normalize pictures to pixel range rom 0 to 255\nY, fake_X_ = reverse_normalize(grid_y, mean_, std_), reverse_normalize(grid_fake_x, mean_, std_)\n\n#Transformation from Y -> X\nfig, (ax1, ax2) = plt.subplots(2, 1, \n                               sharex=True, \n                               sharey=True, \n                               figsize=(30, 20))\nax1.imshow(Y)\nax1.axis('off')\nax1.set_title('Original pic')\nax2.imshow(fake_X_)\nax2.axis('off')\nax2.set_title('Monet-esque')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-06-09T06:10:39.771577Z","iopub.execute_input":"2022-06-09T06:10:39.772172Z","iopub.status.idle":"2022-06-09T06:10:40.965194Z","shell.execute_reply.started":"2022-06-09T06:10:39.772132Z","shell.execute_reply":"2022-06-09T06:10:40.962627Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Submission","metadata":{}},{"cell_type":"code","source":"#Directory to save images\n! mkdir ../images\n\n#Set model to evaluation\nG_XtoY.eval()\n\n#Get data loader for final transformation / submission\nsubmit_dataloader = DataLoader(dataset_original, batch_size=1, shuffle=False, pin_memory=True)\ndataiter = iter(submit_dataloader)\n\n#Previous normalization choosen\nmean_ = 0.5 \nstd_ = 0.5\n\n#Loop through each picture\nfor image_idx in range(0, len(submit_dataloader)):\n        \n    #Get base picture\n    fixed_X = dataiter.next()\n    \n    #Identify correct device\n    device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n    \n    #Create fake pictures (monet-esque)\n    fake_Y = G_XtoY(fixed_X.to(device))\n    fake_Y = fake_Y.detach().cpu().numpy()\n    fake_Y = reverse_normalize(fake_Y, mean_, std_)\n    fake_Y = fake_Y[0].transpose(1, 2, 0)\n    fake_Y = np.uint8(fake_Y)\n    fake_Y = Image.fromarray(fake_Y)\n    #print(fake_Y.shape)\n    \n    #Save picture\n    fake_Y.save(\"../images/\" + str(image_idx) + \".jpg\")\n\n#Back to it\nG_XtoY.train()","metadata":{"execution":{"iopub.status.busy":"2022-06-09T06:10:40.967125Z","iopub.execute_input":"2022-06-09T06:10:40.967713Z","iopub.status.idle":"2022-06-09T06:12:30.536865Z","shell.execute_reply.started":"2022-06-09T06:10:40.967672Z","shell.execute_reply":"2022-06-09T06:12:30.535585Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import shutil\nshutil.make_archive(\"/kaggle/working/images\", 'zip', \"/kaggle/images\")\n#shutil.rmtree('../images') # removes entire directory tree","metadata":{"execution":{"iopub.status.busy":"2022-06-09T06:12:30.538908Z","iopub.execute_input":"2022-06-09T06:12:30.539281Z","iopub.status.idle":"2022-06-09T06:12:33.110987Z","shell.execute_reply.started":"2022-06-09T06:12:30.539247Z","shell.execute_reply":"2022-06-09T06:12:33.109939Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}