{"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":"Соревнование:\nhttps://www.kaggle.com/c/gan-getting-started\n\nНоутбки, с которых я ~~частично украл код~~ вдохновлялся:\n* https://www.kaggle.com/code/nachiket273/cyclegan-pytorch/notebook\n* https://www.kaggle.com/code/amyjang/monet-cyclegan-tutorial\n\n\nСтатейки и полезные ссылки:\n* https://arxiv.org/pdf/1703.10593.pdf\n* https://neptune.ai/blog/6-gan-architectures/amp\n* https://www.tensorflow.org/tutorials/generative/cyclegan\n* https://towardsdatascience.com/overview-of-cyclegan-architecture-and-training-afee31612a2f\n* https://towardsdatascience.com/cyclegan-learning-to-translate-images-without-paired-training-data-5b4e93862c8d\n* https://arxiv.org/pdf/1607.08022.pdf","metadata":{"id":"ys_Y8fGcbWWW"}},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.optim as optim\nfrom torch.utils.data import Dataset, DataLoader\n\nfrom PIL import Image\nimport matplotlib.pyplot as plt\n%matplotlib inline\n\nimport torchvision.transforms as transforms\nimport torchvision.models as models\n\nfrom tqdm.autonotebook import tqdm\nfrom PIL import Image\nimport os\nimport itertools\n\nimport copy\nimport numpy as np","metadata":{"id":"xg8_D0WuZAKB","executionInfo":{"status":"ok","timestamp":1651780939474,"user_tz":-180,"elapsed":4024,"user":{"displayName":"КМБО 01-18","userId":"01002590319070773226"}},"execution":{"iopub.status.busy":"2022-05-11T11:10:04.957182Z","iopub.execute_input":"2022-05-11T11:10:04.957468Z","iopub.status.idle":"2022-05-11T11:10:06.931501Z","shell.execute_reply.started":"2022-05-11T11:10:04.957438Z","shell.execute_reply":"2022-05-11T11:10:06.930308Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"SEED = 42\nnp.random.seed(SEED)\nnp.random.seed(SEED)\ntorch.manual_seed(SEED)\ntorch.cuda.manual_seed(SEED)","metadata":{"id":"hf0aEqGcaULP","executionInfo":{"status":"ok","timestamp":1651780939476,"user_tz":-180,"elapsed":16,"user":{"displayName":"КМБО 01-18","userId":"01002590319070773226"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(device)","metadata":{"id":"ncvyws0kZNcg","outputId":"eeabf822-65ed-4790-fd80-bd2e6873d577","executionInfo":{"status":"ok","timestamp":1651780941745,"user_tz":-180,"elapsed":379,"user":{"displayName":"КМБО 01-18","userId":"01002590319070773226"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from google.colab import drive\nimport os\n\ndrive.mount(\"/content/gdrive/\")","metadata":{"id":"-QDLCWYSabIw","outputId":"768472a9-820a-40be-9b82-d11eed5f991b","executionInfo":{"status":"ok","timestamp":1651780959053,"user_tz":-180,"elapsed":16356,"user":{"displayName":"КМБО 01-18","userId":"01002590319070773226"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Работал на двух гугл аккаунтах, поэтому такая двойственность с путями:\n# !ls /content/gdrive/MyDrive/'Colab Notebooks'/'Tinkoff DL'/'Занятие 6'/\n!ls /content/gdrive/MyDrive/'Colab Notebooks'/","metadata":{"id":"7SBF1gAvZzHp","outputId":"867d0e67-6fb9-4d4a-d398-3d4f6d1ce0bd","executionInfo":{"status":"ok","timestamp":1651780960206,"user_tz":-180,"elapsed":252,"user":{"displayName":"КМБО 01-18","userId":"01002590319070773226"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# !unzip -q /content/gdrive/MyDrive/'Colab Notebooks'/'Tinkoff DL'/'Занятие 6'/gan-getting-started.zip\n!unzip -q /content/gdrive/MyDrive/'Colab Notebooks'/gan-getting-started.zip","metadata":{"id":"Gg-ZgRR5cgki","executionInfo":{"status":"ok","timestamp":1651780965224,"user_tz":-180,"elapsed":3538,"user":{"displayName":"КМБО 01-18","userId":"01002590319070773226"}}},"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 channel, m, s in zip(img, mean, std):\n        channel *= s\n        channel += m\n    return img","metadata":{"id":"EK2XmW3jGnSg","executionInfo":{"status":"ok","timestamp":1651780965226,"user_tz":-180,"elapsed":14,"user":{"displayName":"КМБО 01-18","userId":"01002590319070773226"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class ImageDataset(Dataset):\n    def __init__(self, monet_path, photo_path, size=256):\n        \n        self.size = size\n        self.monet_path = monet_path\n        self.photo_path = photo_path\n\n        self.transforms = transforms.Compose([\n            transforms.Resize(self.size),\n            transforms.ToTensor(),\n            transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),\n        ])\n\n        self.monet = self.load(monet_path)\n        self.photo = self.load(photo_path, len(self.monet))\n\n      \n    def load(self, path, count=-1):\n        filenames = []\n        for root, dirs, files in os.walk(path):\n            for file in files:\n                if file.endswith('.jpg'):\n                    filenames.append(os.path.join(root, file))\n\n        images = []\n        for filename in tqdm(filenames):\n            try:\n                with Image.open(filename) as image:\n                    images.append(image.copy())\n            except:\n                pass\n        return images\n        \n    def __len__(self):\n        return min(len(self.monet), len(self.photo))\n\n    def get_photo(self):\n        return self.photo\n\n    def __getitem__(self, idx):\n        assert idx < len(self), \"Index exceeds dataset size!\"\n        monet_img, photo_img = self.monet[idx], self.photo[idx]\n        monet_img, photo_img = self.transforms(monet_img), self.transforms(photo_img)\n        return monet_img, photo_img","metadata":{"id":"l6b_QUJ8RPVs","executionInfo":{"status":"ok","timestamp":1651780966446,"user_tz":-180,"elapsed":7,"user":{"displayName":"КМБО 01-18","userId":"01002590319070773226"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"RESCALE_SIZE = 256\n\ndataset = ImageDataset('monet_jpg', 'photo_jpg', RESCALE_SIZE)","metadata":{"id":"lFj9coLjl0Ad","outputId":"e455c592-d8ac-4345-e578-221b69a8ca5e","executionInfo":{"status":"ok","timestamp":1651780978891,"user_tz":-180,"elapsed":11099,"user":{"displayName":"КМБО 01-18","userId":"01002590319070773226"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"len(dataset)","metadata":{"id":"W6bgyXd-zNs0","outputId":"54a061fc-2af5-4cc1-f43e-eb1108db2e04","executionInfo":{"status":"ok","timestamp":1651780978893,"user_tz":-180,"elapsed":52,"user":{"displayName":"КМБО 01-18","userId":"01002590319070773226"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"loader = DataLoader(dataset, batch_size=1, pin_memory=True)","metadata":{"id":"eZiFklFjUhdf","executionInfo":{"status":"ok","timestamp":1651780980869,"user_tz":-180,"elapsed":313,"user":{"displayName":"КМБО 01-18","userId":"01002590319070773226"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"len(loader)","metadata":{"id":"TRXItC24fEe0","outputId":"eb53d2ed-93b2-44fe-8ff5-2f6ea5b4f4b2","executionInfo":{"status":"ok","timestamp":1651780981253,"user_tz":-180,"elapsed":19,"user":{"displayName":"КМБО 01-18","userId":"01002590319070773226"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig = plt.figure(figsize=(12, 12))\n\nmonet_img, photo_img  = next(iter(loader))\n\nfig.add_subplot(1, 2, 1)\nplt.title('Monet')\nplt.imshow(unnorm(monet_img[0]).permute(1, 2, 0))\nplt.axis(\"off\")\n\nfig.add_subplot(1, 2, 2)\nplt.title('Photo')\nplt.imshow(unnorm(photo_img[0]).permute(1, 2, 0))\nplt.axis(\"off\")\nplt.show()","metadata":{"id":"TsiRd0qKVqlY","outputId":"77ab0703-ea04-45b9-ac5a-e02bd15ac614","executionInfo":{"status":"ok","timestamp":1651781049198,"user_tz":-180,"elapsed":634,"user":{"displayName":"КМБО 01-18","userId":"01002590319070773226"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def ConvLayer(channels_in, channels_out, kernel_size=3, stride=2, pad=1, use_leaky=False):\n    return nn.Sequential(\n        nn.Conv2d(channels_in, channels_out, kernel_size, stride, pad),\n        nn.InstanceNorm2d(channels_out),\n        nn.LeakyReLU() if use_leaky else nn.ReLU(),\n    )\n\nclass ResidualBlock(nn.Module):\n    def __init__(self, channels_in):\n        super().__init__()\n        self.channels_in = channels_in\n        self.block = nn.Sequential(\n            ConvLayer(channels_in, channels_in, kernel_size=3, stride=1, pad=1, use_leaky=False),\n            nn.Conv2d(channels_in, channels_in, kernel_size=3, stride=1, padding=1),\n            nn.InstanceNorm2d(channels_in),\n        )\n\n    def forward(self, x):\n        res = x + self.block(x)\n        res = nn.InstanceNorm2d(self.channels_in)(res)\n        res = nn.ReLU()(res)\n        return res\n\ndef Upsample(channels_in, channels_out, kernel_size=3, stride=2, pad=1, out_pad=1):\n    return nn.Sequential(\n        nn.ConvTranspose2d(channels_in, channels_out, kernel_size, stride, padding=pad, output_padding=out_pad),\n        nn.InstanceNorm2d(channels_out),\n        nn.ReLU()\n    )","metadata":{"id":"_TOgXn-f6arH","executionInfo":{"status":"ok","timestamp":1651781054096,"user_tz":-180,"elapsed":313,"user":{"displayName":"КМБО 01-18","userId":"01002590319070773226"}}},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Используем такой генератор:","metadata":{"id":"aODo1ry796l8"}},{"cell_type":"markdown","source":"<img src=\"https://i0.wp.com/nttuan8.com/wp-content/uploads/2020/05/4-2.png?fit=1024%2C641&ssl=1\">\n","metadata":{"id":"Hs4IMLRDRLkf"}},{"cell_type":"code","source":"class Generator(nn.Module):\n    def __init__(self):\n        super().__init__()\n\n        # Convolution output size = 1 + (Input Size - Filter size + 2 * Padding) / Stride\n        # Transpose Convolution Output Size = (Input Size - 1) * Strides + Filter Size - 2 * Padding + Ouput Padding\n\n        self.encoder = nn.Sequential(\n            # Input: 3, 256, 256\n            ConvLayer(channels_in=3, channels_out=64, kernel_size=7, stride=1, pad=3, use_leaky=False), # -> 64, 256, 256\n            ConvLayer(channels_in=64, channels_out=128), # 128, 128, 128\n            ConvLayer(channels_in=128, channels_out=256), # 256, 64, 64\n        )\n\n        self.transformer = nn.Sequential(\n            ResidualBlock(256), # -> 256, 64, 64\n            ResidualBlock(256), # -> 256, 64, 64\n            ResidualBlock(256), # -> 256, 64, 64\n            ResidualBlock(256), # -> 256, 64, 64\n            ResidualBlock(256), # -> 256, 64, 64\n            ResidualBlock(256), # -> 256, 64, 64\n        )\n\n        self.decoder = nn.Sequential(\n            Upsample(256, 128), # -> 128, 128, 128\n            Upsample(128, 64), # -> 64, 256, 256\n            nn.Conv2d(64, 3, kernel_size=7, stride=1, padding=3), # -> 3, 256, 256\n            nn.Tanh(),\n        )\n    \n    def forward(self, x):\n        res = self.encoder(x)\n        res = self.transformer(res)\n        res = self.decoder(res)\n        return res","metadata":{"id":"s0CX7ht7WhOr","executionInfo":{"status":"ok","timestamp":1651781056969,"user_tz":-180,"elapsed":308,"user":{"displayName":"КМБО 01-18","userId":"01002590319070773226"}}},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"И такой дискриминатор:","metadata":{"id":"SKgBtEJI-Cb3"}},{"cell_type":"markdown","source":"<img src=\"https://images.viblo.asia/retina/1696ebe2-b162-41a8-8f0b-92fc8bc88fdf.png\">\n","metadata":{"id":"aX6b8rApRdpx"}},{"cell_type":"code","source":"class Discriminator(nn.Module):\n    def __init__(self):\n        super().__init__()\n\n        # Convolution output size = 1 + (Input Size - Filter size + 2 * Padding) / Stride\n\n        self.layers  = nn.Sequential(\n            # Input: 3, 256, 256\n            ConvLayer(channels_in=3, channels_out=64, kernel_size=4, stride=2, pad=1, use_leaky=True), # -> 64, 128, 128\n            ConvLayer(channels_in=64, channels_out=128, kernel_size=4, stride=2, pad=1, use_leaky=True), # -> 128, 64, 64\n            ConvLayer(channels_in=128, channels_out=256, kernel_size=4, stride=2, pad=1, use_leaky=True), # -> 256, 32, 32\n            ConvLayer(channels_in=256, channels_out=512, kernel_size=4, stride=2, pad=1, use_leaky=True), # -> 512, 16, 16\n            # nn.Conv2d(512, 1, kernel_size=3, stride=1, padding=1), # -> 1, 16, 16 - можно и так, но это не по схеме\n            nn.ZeroPad2d((1,0,1,0)),  # -> 512, 17, 17\n            nn.Conv2d(512, 1, kernel_size=4, stride=1, padding=1), # -> 1, 16, 16\n            nn.Sigmoid(),\n        )\n    \n    def forward(self, x):\n        return self.layers(x)","metadata":{"id":"yicI5cOjPDBV","executionInfo":{"status":"ok","timestamp":1651781060041,"user_tz":-180,"elapsed":256,"user":{"displayName":"КМБО 01-18","userId":"01002590319070773226"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def weights_init(m):\n    classname = m.__class__.__name__\n    if classname.find('Conv') != -1:\n        nn.init.normal_(m.weight.data, 0.0, 0.02)\n    elif classname.find('Instance') != -1:\n        nn.init.normal_(m.weight.data, 1.0, 0.02)\n        nn.init.constant_(m.bias.data, 0)","metadata":{"id":"qL7IiuLF6pkg","executionInfo":{"status":"ok","timestamp":1651781073560,"user_tz":-180,"elapsed":320,"user":{"displayName":"КМБО 01-18","userId":"01002590319070773226"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class CycleGAN(object):\n    def __init__(self, epochs, device, lr=2e-4, lmbda=10, id_coef=0.5, decay_epoch=0):\n        \n        self.epochs = epochs\n        self.start_epoch = 0\n        self.decay_epoch = decay_epoch if decay_epoch > 0 else self.epochs // 2\n        self.lmbda = lmbda\n        self.id_coef = id_coef\n        self.device = device\n\n        # Определим модели\n        self.gen_m2p = Generator()\n        self.gen_p2m = Generator()\n        self.disc_m = Discriminator()\n        self.disc_p = Discriminator()\n\n        self.init_models()\n\n        # Функции потерь\n        self.gen_criterion = nn.MSELoss() \n        self.disc_criterion = nn.MSELoss() \n        self.cycle_criterion = nn.L1Loss()\n        self.identity_criterion = nn.L1Loss()\n\n        # Оптимайзеры\n        self.generator_optimizer = torch.optim.Adam(\n            itertools.chain(self.gen_m2p.parameters(), self.gen_p2m.parameters()), \n            lr = lr, \n            betas=(0.5, 0.999)\n        )\n        self.discriminator_optimizer = torch.optim.Adam(\n            itertools.chain(self.disc_m.parameters(), self.disc_p.parameters()), \n            lr = lr, \n            betas=(0.5, 0.999)\n        )\n\n        # Вот тут раньше было сэмплирование\n\n        # Lr_schedulers\n        self.gen_lr_sched = torch.optim.lr_scheduler.LambdaLR(self.generator_optimizer, \n                                                              lr_lambda=lambda epoch: 1 - max(0, epoch-decay_epoch) / (epochs-decay_epoch))\n        self.disc_lr_sched = torch.optim.lr_scheduler.LambdaLR(self.discriminator_optimizer, \n                                                               lr_lambda=lambda epoch: 1 - max(0, epoch-decay_epoch) / (epochs-decay_epoch))\n        \n        # Для статистики\n        self.gen_history = []\n        self.disc_history = []\n        \n    def init_models(self):\n        weights_init(self.gen_m2p)\n        weights_init(self.gen_p2m)\n        weights_init(self.disc_m)\n        weights_init(self.disc_p)\n        self.gen_m2p = self.gen_m2p.to(self.device)\n        self.gen_p2m = self.gen_p2m.to(self.device)\n        self.disc_m = self.disc_m.to(self.device)\n        self.disc_p = self.disc_p.to(self.device)\n    \n    def update_req_grad(self, models, requires_grad=True):\n        for model in models:\n            for param in model.parameters():\n                param.requires_grad = requires_grad\n    \n    def upload_model(self, PATH):\n        checkpoint = torch.load(PATH) if os.path.exists(PATH) else 0\n\n        if checkpoint:\n            self.start_epoch = checkpoint['last_epoch'] + 1\n            self.gen_m2p.load_state_dict(checkpoint['gen_m2p'])\n            self.gen_p2m.load_state_dict(checkpoint['gen_p2m'])\n            self.disc_m.load_state_dict(checkpoint['disc_m'])\n            self.disc_p.load_state_dict(checkpoint['disc_p'])\n            self.generator_optimizer.load_state_dict(checkpoint['generator_optimizer'])\n            self.discriminator_optimizer.load_state_dict(checkpoint['discriminator_optimizer'])\n            self.gen_history = checkpoint['gen_history']\n            self.disc_history = checkpoint['disc_history']\n            return True\n\n        return False\n\n    # 0. распакавать данные на нужное устройство\n    # 1. сбросить градиент\n    # 2. прогнать данные через сеть\n    # 3. посчитать loss\n    # 4. залоггировать его куда-нибудь\n    # 5. сделать .backward()\n    # 6. optimizer.step()\n    # (7. вывести пример ) \n    def train(self, pm_loader, PATH, fname='temp_checkpoint.pt'):\n\n        checkpoint_exists = self.upload_model(PATH+fname)\n    \n        if checkpoint_exists:\n            for e, (g, h) in enumerate(zip(self.gen_history, self.disc_history)):\n                print(f\"Epoch: {e} | Generator Loss: {g} | Discriminator Loss: {h}\")\n            print(f\"\\nContinue training from {self.start_epoch} epoch\")\n        \n        for epoch in range(self.start_epoch, self.epochs):\n            \n            avg_gen_loss, avg_disc_loss = 0, 0\n            data = tqdm(pm_loader, leave=False, total=len(pm_loader))\n            for i, (real_monet, real_photo) in enumerate(data):\n\n                real_monet, real_photo = real_monet.to(device), real_photo.to(device) \n\n                # Пропустим через генератор и посчитаем все лоссы\n\n                self.update_req_grad([self.disc_m, self.disc_p], False)\n                self.generator_optimizer.zero_grad()\n\n                # monet -> photo -> monet\n                fake_photo = self.gen_m2p(real_monet)\n                cycled_monet = self.gen_p2m(fake_photo)\n\n                # photo -> monet -> photo\n                fake_monet = self.gen_p2m(real_photo)\n                cycled_photo = self.gen_m2p(fake_monet)\n\n                # monet -> monet, photo -> photo\n                id_monet = self.gen_p2m(real_monet)\n                id_photo = self.gen_m2p(real_photo)\n\n                # Пропустим fake_monet и fake_photo через дискриминатор, чтобы оценить его лосс\n                disc_monet = self.disc_m(fake_monet)\n                disc_photo = self.disc_p(fake_photo)\n                real = torch.ones(disc_monet.size()).to(self.device) # - идельный ответ дискриминатора\n\n                # Посчитаем лоссы\n                id_loss_monet = self.identity_criterion(id_monet, real_monet) * self.lmbda * self.id_coef\n                id_loss_photo = self.identity_criterion(id_photo, real_photo) * self.lmbda * self.id_coef\n\n                cycle_loss_monet = self.cycle_criterion(cycled_monet, real_monet) * self.lmbda\n                cycle_loss_photo = self.cycle_criterion(cycled_photo, real_photo) * self.lmbda\n\n                gen_loss_monet = self.gen_criterion(disc_monet, real)\n                gen_loss_photo = self.gen_criterion(disc_photo, real)\n\n                total_gen_loss = cycle_loss_monet + cycle_loss_photo + gen_loss_monet + gen_loss_photo + id_loss_monet + id_loss_photo\n                avg_gen_loss += total_gen_loss.item()\n\n                total_gen_loss.backward()\n                self.generator_optimizer.step()\n                \n                # Переходим к дискриминатору\n\n                self.update_req_grad([self.disc_m, self.disc_p], True)\n                self.discriminator_optimizer.zero_grad()\n\n                # Если передавать те же самые тензоры fake_monet и fake_photo, потом считать лоссы и делать .backward(),\n                # то python будет ругаться, что .backward() делается два раза:\n                # \"Trying to backward through the graph a second time ...\".\n                # Выход из ситуации: fake_monet и fake_photo провести через какие-нибудь \"дебри\", чтобы python решил,\n                # что это новые тензоры, хотя они по значениям одинаковые. - хз, насколько я прав.\n                # Также хз, сработает ли данный метод, если все обучается на cpu(), но пока это не моя проблема.\n                fake_monet = torch.tensor(fake_monet.cpu().data.numpy()).to(self.device)\n                fake_photo = torch.tensor(fake_photo.cpu().data.numpy()).to(self.device)\n                disc_real_monet = self.disc_m(real_monet)\n                disc_fake_monet = self.disc_m(fake_monet)\n                disc_real_photo = self.disc_p(real_photo)\n                disc_fake_photo = self.disc_p(fake_photo)\n\n                real = torch.ones(disc_real_monet.size()).to(self.device)\n                fake = torch.zeros(disc_fake_monet.size()).to(self.device)\n\n                disc_real_monet_loss = self.disc_criterion(disc_real_monet, real)\n                disc_fake_monet_loss = self.disc_criterion(disc_fake_monet, fake)\n                disc_real_photo_loss = self.disc_criterion(disc_real_photo, real)\n                disc_fake_photo_loss = self.disc_criterion(disc_fake_photo, fake)\n\n                monet_disc_loss = 0.5 * (disc_real_monet_loss + disc_fake_monet_loss)\n                photo_disc_loss = 0.5 * (disc_real_photo_loss + disc_fake_photo_loss)\n                total_disc_loss = monet_disc_loss + photo_disc_loss\n                avg_disc_loss += total_disc_loss.item()\n\n                monet_disc_loss.backward(retain_graph=True)\n                photo_disc_loss.backward()\n                self.discriminator_optimizer.step()\n\n                data.set_postfix(gen_loss=total_gen_loss.item(), disc_loss=total_disc_loss.item())\n              \n            avg_gen_loss /= len(pm_loader)\n            avg_disc_loss /= len(pm_loader)\n            \n            self.gen_history.append(avg_gen_loss)\n            self.disc_history.append(avg_disc_loss)\n\n            torch.save({\n                'last_epoch': epoch,\n                'gen_m2p': self.gen_m2p.state_dict(),\n                'gen_p2m': self.gen_p2m.state_dict(),\n                'disc_m': self.disc_m.state_dict(),\n                'disc_p': self.disc_p.state_dict(),\n                'generator_optimizer': self.generator_optimizer.state_dict(),\n                'discriminator_optimizer': self.discriminator_optimizer.state_dict(),\n                'gen_history': self.gen_history,\n                'disc_history': self.disc_history\n            }, PATH+fname)\n            \n            print(f\"Epoch: {epoch} | Generator Loss: {avg_gen_loss} | Discriminator Loss: {avg_disc_loss}\")\n\n            if epoch % 2 == 0:\n\n                test_monet, test_photo = next(iter(loader))\n\n                pred_monet = self.gen_p2m(test_photo.to(device)).cpu().detach()\n                pred_photo = self.gen_m2p(test_monet.to(device)).cpu().detach()\n                pred_monet, pred_photo = unnorm(pred_monet[0]), unnorm(pred_photo[0])\n                test_monet, test_photo = unnorm(test_monet[0]), unnorm(test_photo[0])\n\n                fig, ax = plt.subplots(2, 2, figsize=(10, 10))\n                ax[0, 0].imshow(test_monet.permute(1, 2, 0))\n                ax[0, 0].set_title(\"Input monet\")\n                ax[0, 0].axis(\"off\")\n\n                ax[0, 1].imshow(pred_photo.permute(1, 2, 0))\n                ax[0, 1].set_title(\"Pred photo\")\n                ax[0, 1].axis(\"off\")\n\n                ax[1, 0].imshow(test_photo.permute(1, 2, 0))\n                ax[1, 0].set_title(\"Input photo\")\n                ax[1, 0].axis(\"off\")\n\n                ax[1, 1].imshow(pred_monet.permute(1, 2, 0))\n                ax[1, 1].set_title(\"Pred monet\")\n                ax[1, 1].axis(\"off\")\n                \n                plt.show()\n\n            self.gen_lr_sched.step()\n            self.disc_lr_sched.step()","metadata":{"id":"jehhQCyFgeTJ","executionInfo":{"status":"ok","timestamp":1651781062286,"user_tz":-180,"elapsed":496,"user":{"displayName":"КМБО 01-18","userId":"01002590319070773226"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"gan = CycleGAN(31, device)","metadata":{"id":"KWpHse2kj1fi","executionInfo":{"status":"ok","timestamp":1651781078984,"user_tz":-180,"elapsed":270,"user":{"displayName":"КМБО 01-18","userId":"01002590319070773226"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# PATH = '/content/gdrive/MyDrive/Colab Notebooks/Tinkoff DL/Занятие 6/'\nPATH = '/content/gdrive/MyDrive/Colab Notebooks/'","metadata":{"id":"r6VeiJq-f0ly","executionInfo":{"status":"ok","timestamp":1651781081712,"user_tz":-180,"elapsed":220,"user":{"displayName":"КМБО 01-18","userId":"01002590319070773226"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Так как при обучении я пользовался другим гугл аккаунтом, то в dataloader был другой порядок картинок, поэтому\n# первые картинки в процессе обучения (на которой чекаем прогресс генератора и дискриманатора) и первые картинки в даталоадере сейчас различаются.\ngan.train(loader, PATH)","metadata":{"id":"DgeARrFjecRu","executionInfo":{"status":"ok","timestamp":1651766757122,"user_tz":-180,"elapsed":13203644,"user":{"displayName":"КМБО 01-18","userId":"01002590319070773226"}},"outputId":"511c57fd-57fe-4a43-8334-d322fa98b158"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Для загрузки весов модели\ngan.train(loader, PATH)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.xlabel(\"Epochs\")\nplt.ylabel(\"Losses\")\nplt.plot(gan.gen_history, 'r', label='Generator Loss')\nplt.plot(gan.disc_history, 'b', label='Descriminator Loss')\nplt.legend()\nplt.show()","metadata":{"id":"K_J_cNZa9lxf","outputId":"d94451cd-c922-4b9c-debd-71b982ab625e","executionInfo":{"status":"ok","timestamp":1651781149019,"user_tz":-180,"elapsed":755,"user":{"displayName":"КМБО 01-18","userId":"01002590319070773226"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def print_ex(test_monet, test_photo):\n\n    with torch.no_grad():\n        pred_monet = gan.gen_p2m(test_photo.to(device)).cpu().detach().to(torch.float32)\n        pred_photo = gan.gen_m2p(test_monet.to(device)).cpu().detach().to(torch.float32)\n\n    test_monet, test_photo = unnorm(test_monet), unnorm(test_photo)\n    pred_monet, pred_photo = unnorm(pred_monet), unnorm(pred_photo)\n\n    fig, ax = plt.subplots(2, 2, figsize=(8, 8))\n    ax[0, 0].imshow(test_monet.permute(1, 2, 0))\n    ax[0, 0].set_title(\"Input monet\")\n    ax[0, 0].axis(\"off\")\n\n    ax[0, 1].imshow(pred_photo.permute(1, 2, 0))\n    ax[0, 1].set_title(\"Pred photo\")\n    ax[0, 1].axis(\"off\")\n\n    ax[1, 0].imshow(test_photo.permute(1, 2, 0))\n    ax[1, 0].set_title(\"Input photo\")\n    ax[1, 0].axis(\"off\")\n\n    ax[1, 1].imshow(pred_monet.permute(1, 2, 0))\n    ax[1, 1].set_title(\"Pred monet\")\n    ax[1, 1].axis(\"off\")\n\n    plt.show()\n\ndef print_exs(idxs, source):\n    for i in idxs:\n        assert len(source[i]) == 2, \"Not two photos passed\"\n        print_ex(*source[i])\n        print('_'*100)","metadata":{"id":"1KmC-8AT_jRE","executionInfo":{"status":"ok","timestamp":1651781923782,"user_tz":-180,"elapsed":337,"user":{"displayName":"КМБО 01-18","userId":"01002590319070773226"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print_exs(range(8, 108, 20), dataset)","metadata":{"id":"L7sYcAHFIJMw","executionInfo":{"status":"ok","timestamp":1651781929152,"user_tz":-180,"elapsed":3626,"user":{"displayName":"КМБО 01-18","userId":"01002590319070773226"}},"outputId":"a7a42858-1756-4141-8db3-f3b6438c0a05"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Мне кажется, что получилось хорошо!","metadata":{"id":"iF3WJxBnCQ91"}},{"cell_type":"code","source":"class PhotoDataset(Dataset):\n    def __init__(self, photo_path, size=256):\n        \n        self.size = size\n        self.photo_path = photo_path\n\n        self.transforms = transforms.Compose([\n            transforms.Resize(self.size),\n            transforms.ToTensor(),\n            transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),\n        ])\n\n        self.photo = self.load(photo_path)\n\n      \n    def load(self, path, count=-1):\n        filenames = []\n        for root, dirs, files in os.walk(path):\n            for file in files:\n                if file.endswith('.jpg'):\n                    filenames.append(os.path.join(root, file))\n\n        images = []\n        for filename in tqdm(filenames):\n            try:\n                with Image.open(filename) as image:\n                    images.append(image.copy())\n            except:\n                pass\n        return images\n        \n    def __len__(self):\n        return len(self.photo)\n\n    def __getitem__(self, idx):\n        return self.transforms(self.photo[idx])","metadata":{"id":"ZL6b9_qgzmqq","executionInfo":{"status":"ok","timestamp":1651782226943,"user_tz":-180,"elapsed":9,"user":{"displayName":"КМБО 01-18","userId":"01002590319070773226"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# photo_dataset = PhotoDataset('photo_jpg', RESCALE_SIZE)\nphoto_loader = DataLoader(photo_dataset, batch_size=1, pin_memory=True)","metadata":{"id":"fT1HjzZf2L6v","executionInfo":{"status":"ok","timestamp":1651782238100,"user_tz":-180,"elapsed":10030,"user":{"displayName":"КМБО 01-18","userId":"01002590319070773226"}},"outputId":"7a951a67-7f9c-4999-8104-42cb8851f126"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"len(photo_loader)","metadata":{"id":"CHPaJgWg4wp0","executionInfo":{"status":"ok","timestamp":1651782238102,"user_tz":-180,"elapsed":57,"user":{"displayName":"КМБО 01-18","userId":"01002590319070773226"}},"outputId":"cf76d4d9-177d-441a-9afa-4fbed228a120"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# !mkdir /content/gdrive/MyDrive/'Colab Notebooks'/'Tinkoff DL'/'Занятие 6'/images\n!mkdir /content/gdrive/MyDrive/'Colab Notebooks'/images","metadata":{"id":"MGk_ZXG_2dN1","executionInfo":{"status":"ok","timestamp":1651782531582,"user_tz":-180,"elapsed":626,"user":{"displayName":"КМБО 01-18","userId":"01002590319070773226"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data = tqdm(photo_loader, leave=False, total=len(photo_loader))\nfor i, photo in enumerate(data):\n    with torch.no_grad():\n        pred_monet = gan.gen_p2m(photo.to(device)).cpu().detach()\n    pred_monet = unnorm(pred_monet)\n    img = transforms.ToPILImage()(pred_monet[0]).convert(\"RGB\")\n    # img.save(\"/content/gdrive/MyDrive/Colab Notebooks/Tinkoff DL/Занятие 6/images/\" + str(i+1) + \".jpg\")\n    img.save(\"/content/gdrive/MyDrive/Colab Notebooks/images/\" + str(i+1) + \".jpg\")","metadata":{"id":"anVTmAZS2mHc","executionInfo":{"status":"ok","timestamp":1651783643578,"user_tz":-180,"elapsed":845139,"user":{"displayName":"КМБО 01-18","userId":"01002590319070773226"}},"outputId":"b17a5e02-f281-4571-f422-d1a29e5df267"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!ls /content/gdrive/MyDrive/'Colab Notebooks'/images/","metadata":{"id":"SWn4synIA_S0","executionInfo":{"status":"ok","timestamp":1651784305406,"user_tz":-180,"elapsed":1465,"user":{"displayName":"КМБО 01-18","userId":"01002590319070773226"}},"outputId":"3ed4a328-d83c-4868-e29c-93233b60bb0f"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# ! ls ../input/hw6-results/images","metadata":{"id":"3tKwXg_PBCPM","execution":{"iopub.status.busy":"2022-05-11T11:03:30.688306Z","iopub.execute_input":"2022-05-11T11:03:30.688622Z","iopub.status.idle":"2022-05-11T11:03:30.693960Z","shell.execute_reply.started":"2022-05-11T11:03:30.688585Z","shell.execute_reply":"2022-05-11T11:03:30.692870Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# import shutil\n\n# shutil.make_archive(\"./images\", 'zip', \"../input/hw6-results/images\")","metadata":{"execution":{"iopub.status.busy":"2022-05-11T11:06:36.039143Z","iopub.execute_input":"2022-05-11T11:06:36.040010Z","iopub.status.idle":"2022-05-11T11:06:45.551528Z","shell.execute_reply.started":"2022-05-11T11:06:36.039963Z","shell.execute_reply":"2022-05-11T11:06:45.550751Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}