{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.14","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":21755,"databundleVersionId":1475600,"sourceType":"competition"}],"dockerImageVersionId":30804,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"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        print(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","trusted":true,"execution":{"iopub.status.busy":"2024-12-18T09:54:38.366791Z","iopub.execute_input":"2024-12-18T09:54:38.367374Z","iopub.status.idle":"2024-12-18T09:54:42.439567Z","shell.execute_reply.started":"2024-12-18T09:54:38.367297Z","shell.execute_reply":"2024-12-18T09:54:42.438287Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os  # untuk operasi sistem seperti file dan direktori\nimport numpy as np  # untuk manipulasi array dan perhitungan numerik\nimport matplotlib.pyplot as plt  # untuk visualisasi data dan grafik\nimport torch  # library utama untuk deep learning\nimport torch.nn as nn  # untuk membangun arsitektur model neural network\nimport torchvision.transforms as transforms  # untuk preprocessing data gambar\nfrom torch.utils.data import Dataset, DataLoader, random_split  # untuk pengelolaan dataset dan dataloader\nfrom torchvision.utils import make_grid  # untuk membuat grid gambar\nfrom PIL import Image  # untuk memproses data gambar\n\nfrom tqdm.auto import tqdm  # untuk progress bar selama iterasi\nimport shutil  # untuk operasi file tingkat tinggi seperti menyalin atau memindahkan file\nfrom pathlib import Path  # untuk manipulasi path secara platform-independen\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-18T09:54:42.442185Z","iopub.execute_input":"2024-12-18T09:54:42.442681Z","iopub.status.idle":"2024-12-18T09:54:42.450840Z","shell.execute_reply.started":"2024-12-18T09:54:42.442627Z","shell.execute_reply":"2024-12-18T09:54:42.449771Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Menentukan direktori root tempat dataset berada\ndataset_root = '/kaggle/input/gan-getting-started/'  \nprint(dataset_root)  # Menampilkan lokasi root dataset\n\n# Menampilkan daftar file dan folder di dalam root dataset\nprint(os.listdir(dataset_root))\n\n# Menyimpan nama folder yang berisi dataset gambar Monet dan foto lainnya\nmonet_images_dir = 'monet_jpg'  # Nama folder untuk gambar Monet\nphoto_images_dir = 'photo_jpg'  # Nama folder untuk gambar foto biasa\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-18T09:54:42.452091Z","iopub.execute_input":"2024-12-18T09:54:42.452548Z","iopub.status.idle":"2024-12-18T09:54:42.470851Z","shell.execute_reply.started":"2024-12-18T09:54:42.452498Z","shell.execute_reply":"2024-12-18T09:54:42.469535Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torchvision\n\nimport os\nfrom PIL import Image\nfrom torch.utils.data import Dataset, DataLoader, random_split\nimport torchvision.transforms as transforms\nimport matplotlib.pyplot as plt\n\n# Definisi dataset MonetPhoto\nclass MonetPhoto(Dataset):\n    def __init__(self, root_dir, monet_dir, photo_dir, transform=None):\n        \"\"\"\n        Inisialisasi dataset MonetPhoto dengan path ke gambar Monet dan foto serta transformasi opsional.\n\n        Args:\n            root_dir (str): Direktori utama tempat dataset disimpan.\n            monet_dir (str): Subdirektori dalam root_dir yang berisi gambar Monet.\n            photo_dir (str): Subdirektori dalam root_dir yang berisi gambar foto.\n            transform (callable, optional): Transformasi opsional yang diterapkan pada sampel.\n        \"\"\"\n        self.root_dir = root_dir  # Direktori utama dataset\n        self.monet_dir = monet_dir  # Subdirektori untuk gambar Monet\n        self.monet_files = os.listdir(os.path.join(root_dir, monet_dir))  # Daftar semua file gambar Monet\n        self.photo_dir = photo_dir  # Subdirektori untuk gambar foto\n        self.photo_files = os.listdir(os.path.join(root_dir, photo_dir))  # Daftar semua file gambar foto\n        self.transform = transform  # Transformasi opsional untuk gambar\n\n    def __len__(self):\n        \"\"\"\n        Mengembalikan jumlah total sampel dalam dataset.\n        Jumlah sampel adalah maksimum dari jumlah gambar Monet dan foto untuk memastikan pairing.\n\n        Returns:\n            int: Jumlah total sampel.\n        \"\"\"\n        return max(len(self.monet_files), len(self.photo_files))  # Menjamin semua gambar dipasangkan\n\n    def __getitem__(self, idx):\n        \"\"\"\n        Mengambil sampel dari dataset berdasarkan indeks.\n        Gambar Monet dipasangkan dengan foto, menggunakan operasi modulo untuk menangani indeks lebih besar dari panjang data.\n\n        Args:\n            idx (int): Indeks sampel yang akan diambil.\n\n        Returns:\n            tuple: Pasangan gambar Monet dan foto setelah transformasi diterapkan.\n        \"\"\"\n        # Ambil gambar Monet pada indeks yang diberikan (dengan operasi modulo)\n        monet_img = Image.open(os.path.join(self.root_dir, self.monet_dir, self.monet_files[idx % len(self.monet_files)]))\n        # Ambil gambar foto pada indeks yang diberikan (dengan operasi modulo)\n        photo_img = Image.open(os.path.join(self.root_dir, self.photo_dir, self.photo_files[idx % len(self.photo_files)]))\n\n        # Terapkan transformasi jika disediakan\n        if self.transform:\n            monet_img = self.transform(monet_img)  # Transformasi pada gambar Monet\n            photo_img = self.transform(photo_img)  # Transformasi pada gambar foto\n\n        return monet_img, photo_img  # Kembalikan pasangan gambar\n\n# Definisi transformasi gambar\ntransformations = transforms.Compose([\n    transforms.Resize((256, 256)),  # Ubah ukuran gambar menjadi 256x256 piksel\n    transforms.ToTensor(),  # Konversi gambar dari PIL ke tensor PyTorch\n    transforms.Normalize((0.5,), (0.5,))  # Normalisasi nilai tensor ke rentang [-1, 1]\n])\n\n# Inisialisasi dataset MonetPhoto\ndata_root = '/kaggle/input/gan-getting-started/'  # Direktori utama\nmonet_path = 'monet_jpg'  # Subdirektori gambar Monet\nphoto_path = 'photo_jpg'  # Subdirektori gambar foto\n\n# Membuat instance dataset MonetPhoto\ndataset = MonetPhoto(data_root, monet_path, photo_path, transform=transformations)\n\n# Membagi dataset menjadi subset pelatihan (80%) dan pengujian (20%)\ndataset_length = len(dataset)  # Panjang dataset\ntrain_size = int(dataset_length * 0.8)  # 80% untuk pelatihan\ntest_size = dataset_length - train_size  # Sisa 20% untuk pengujian\ntrain_dataset, test_dataset = random_split(dataset, [train_size, test_size])  # Split dataset\n\n# Membuat DataLoader untuk subset pelatihan\ntrain_loader = DataLoader(train_dataset, batch_size=32, shuffle=True, num_workers=2)  # DataLoader pelatihan\n# Membuat DataLoader untuk subset pengujian\ntest_loader = DataLoader(test_dataset, batch_size=32, shuffle=False, num_workers=2)  # DataLoader pengujian\n\n# Ambil batch pertama dari DataLoader pelatihan\nmonet_batch, photo_batch = next(iter(train_loader))\n\n# Plot gambar Monet pertama dari batch\nplt.imshow(monet_batch[0].permute(1, 2, 0).numpy() * 0.5 + 0.5)  # Denormalisasi dan konversi ke format numpy\nplt.title(\"Sample Monet Image\")  # Tambahkan judul pada plot\nplt.show()  # Tampilkan plot\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-18T09:54:42.472813Z","iopub.execute_input":"2024-12-18T09:54:42.473293Z","iopub.status.idle":"2024-12-18T09:54:43.639704Z","shell.execute_reply.started":"2024-12-18T09:54:42.473241Z","shell.execute_reply":"2024-12-18T09:54:43.638607Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\n\nclass CNNBlock(nn.Module):\n    def __init__(self, in_channels, out_channels, stride=2, device=\"cuda\"):\n        \"\"\"\n        Inisialisasi CNNBlock dengan lapisan konvolusi, normalisasi batch, dan aktivasi.\n        \"\"\"\n        super(CNNBlock, self).__init__()  # Memanggil konstruktor parent class\n        self.device = device  # Menetapkan perangkat (CPU atau GPU) untuk operasi\n        self.conv_layer = nn.Sequential(\n            nn.Conv2d(  # Lapisan konvolusi 2D\n                in_channels, \n                out_channels, \n                kernel_size=4, \n                stride=stride, \n                bias=False, \n                padding_mode='reflect', \n                device=self.device\n            ),\n            nn.BatchNorm2d(out_channels, device=self.device),  # Normalisasi batch untuk mempercepat pelatihan\n            nn.LeakyReLU(0.2),  # Fungsi aktivasi LeakyReLU dengan slope negatif 0.2\n        )\n        \n    def forward(self, x):\n        \"\"\"\n        Proses input melalui CNNBlock.\n        \"\"\"\n        return self.conv_layer(x)  # Terapkan lapisan konvolusi, normalisasi, dan aktivasi\n\n\nclass Discriminator(nn.Module):\n    def __init__(self, in_channels=3, features=[64, 128, 256, 512], device=\"cuda\"):\n        \"\"\"\n        Inisialisasi Discriminator dengan menggunakan beberapa CNNBlock.\n        \"\"\"\n        super(Discriminator, self).__init__()  # Memanggil konstruktor parent class\n        self.device = device  # Menetapkan perangkat (CPU atau GPU)\n        \n        self.initial = nn.Sequential(  # Lapisan konvolusi awal\n            nn.Conv2d(\n                in_channels, \n                features[0], \n                kernel_size=4, \n                stride=2, \n                padding=1, \n                padding_mode='reflect', \n                device=self.device\n            ),\n            nn.LeakyReLU(0.2)  # Aktivasi LeakyReLU\n        )\n        \n        # Menyusun lapisan-lapisan CNNBlock\n        layers = []\n        in_channels = features[0]\n        for feature in features[1:]:\n            layers.append(\n                CNNBlock(in_channels, feature, stride=1 if feature == features[-1] else 2, device=self.device)\n            )\n            in_channels = feature\n        \n        layers.append(\n            nn.Conv2d(  # Lapisan konvolusi akhir untuk klasifikasi real/fake\n                in_channels, \n                1, \n                kernel_size=4, \n                stride=1, \n                padding=1, \n                padding_mode='reflect', \n                device=self.device\n            )\n        )\n        \n        self.model = nn.Sequential(*layers)  # Menggabungkan lapisan-lapisan ke dalam satu model\n\n    def forward(self, x):\n        \"\"\"\n        Proses input melalui Discriminator.\n        \"\"\"\n        x = self.initial(x)  # Proses awal dengan konvolusi dan aktivasi\n        return self.model(x)  # Proses melalui model utama\n\n\nclass Block(nn.Module):\n    def __init__(self, in_channels, out_channels, down=True, act='relu', use_dropout=False, device=\"cuda\"):\n        \"\"\"\n        Inisialisasi Block, untuk downsampling atau upsampling.\n        \"\"\"\n        super(Block, self).__init__()  # Memanggil konstruktor parent class\n        self.device = device  # Menetapkan perangkat (CPU atau GPU)\n        \n        # Pilih antara Conv2d untuk downsampling atau ConvTranspose2d untuk upsampling\n        self.conv = nn.Sequential(\n            nn.Conv2d(\n                in_channels, \n                out_channels, \n                kernel_size=4, \n                stride=2, \n                padding=1, \n                bias=False, \n                padding_mode='reflect', \n                device=self.device\n            ) if down else nn.ConvTranspose2d(\n                in_channels, \n                out_channels, \n                kernel_size=4, \n                stride=2, \n                padding=1, \n                bias=False, \n                device=self.device\n            ),\n            nn.BatchNorm2d(out_channels, device=self.device),  # Normalisasi batch\n            nn.ReLU() if act == 'relu' else nn.LeakyReLU(0.2),  # Fungsi aktivasi sesuai dengan pilihan\n        )\n        \n        self.use_dropout = use_dropout  # Mengatur apakah dropout digunakan\n        self.dropout = nn.Dropout(0.5)  # Dropout dengan tingkat dropout 50%\n\n    def forward(self, x):\n        \"\"\"\n        Proses input melalui Block.\n        \"\"\"\n        x = self.conv(x)  # Terapkan konvolusi dan aktivasi\n        return self.dropout(x) if self.use_dropout else x  # Terapkan dropout jika diaktifkan\n\n\nclass Generator(nn.Module):\n    def __init__(self, in_channels=3, features=64, device=\"cuda\"):\n        \"\"\"\n        Inisialisasi Generator dengan menggunakan beberapa Block downsampling dan upsampling.\n        \"\"\"\n        super(Generator, self).__init__()  # Memanggil konstruktor parent class\n        self.device = device  # Menetapkan perangkat (CPU atau GPU)\n        \n        # Downsampling awal\n        self.initial_down = nn.Sequential(\n            nn.Conv2d(\n                in_channels, \n                features, \n                kernel_size=4, \n                stride=2, \n                padding=1, \n                padding_mode='reflect', \n                device=self.device\n            ),\n            nn.LeakyReLU(0.2),  # Aktivasi LeakyReLU\n        )\n        \n        # Block downsampling\n        self.down1 = Block(features, features*2, down=True, act='leaky', use_dropout=False)  \n        self.down2 = Block(features*2, features*4, down=True, act='leaky', use_dropout=False)\n        self.down3 = Block(features*4, features*8, down=True, act='leaky', use_dropout=False)\n        self.down4 = Block(features*8, features*8, down=True, act='leaky', use_dropout=False)\n        self.down5 = Block(features*8, features*8, down=True, act='leaky', use_dropout=False)\n        self.down6 = Block(features*8, features*8, down=True, act='leaky', use_dropout=False)\n        \n        # Bottleneck\n        self.bottleneck = nn.Sequential(\n            nn.Conv2d(\n                features*8, \n                features*8, \n                kernel_size=4, \n                stride=2, \n                padding=1, \n                padding_mode='reflect', \n                device=self.device\n            ),\n            nn.ReLU(),  # Aktivasi ReLU\n        )\n        \n        # Block upsampling dengan skip connections\n        self.up1 = Block(features*8, features*8, down=False, act='relu', use_dropout=True)\n        self.up2 = Block(features*8*2, features*8, down=False, act='relu', use_dropout=True)\n        self.up3 = Block(features*8*2, features*8, down=False, act='relu', use_dropout=True)\n        self.up4 = Block(features*8*2, features*8, down=False, act='relu', use_dropout=False)\n        self.up5 = Block(features*8*2, features*4, down=False, act='relu', use_dropout=False)\n        self.up6 = Block(features*4*2, features*2, down=False, act='relu', use_dropout=False)\n        self.up7 = Block(features*2*2, features, down=False, act='relu', use_dropout=False)\n        \n        # Layer upsampling akhir untuk membentuk gambar output\n        self.final_up = nn.Sequential(\n            nn.ConvTranspose2d(\n                features*2, \n                in_channels, \n                kernel_size=4, \n                stride=2, \n                padding=1, \n                device=self.device\n            ),\n            nn.Tanh(),  # Aktivasi Tanh untuk mengatur output antara -1 dan 1\n        )\n\n    def forward(self, x):\n        \"\"\"\n        Proses input melalui Generator.\n        \"\"\"\n        d1 = self.initial_down(x)  # Downsampling pertama\n        d2 = self.down1(d1)  # Downsampling kedua\n        d3 = self.down2(d2)  # Downsampling ketiga\n        d4 = self.down3(d3)  # Downsampling keempat\n        d5 = self.down4(d4)  # Downsampling kelima\n        d6 = self.down5(d5)  # Downsampling keenam\n        d7 = self.down6(d6)  # Downsampling ketujuh\n        \n        bottleneck = self.bottleneck(d7)  # Bottleneck\n        \n        up1 = self.up1(bottleneck)  # Upsampling pertama\n        up2 = self.up2(torch.cat([up1, d7], 1))  # Concatenate dengan feature map yang sesuai\n        up3 = self.up3(torch.cat([up2, d6], 1))  # Upsampling kedua\n        up4 = self.up4(torch.cat([up3, d5], 1))  # Upsampling ketiga\n        up5 = self.up5(torch.cat([up4, d4], 1))  # Upsampling\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-18T09:54:43.644172Z","iopub.execute_input":"2024-12-18T09:54:43.645082Z","iopub.status.idle":"2024-12-18T09:54:43.682883Z","shell.execute_reply.started":"2024-12-18T09:54:43.645026Z","shell.execute_reply":"2024-12-18T09:54:43.681516Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class Trainer:\n    def __init__(\n        self,\n        train_data: DataLoader,\n        val_data: DataLoader,\n        generator: torch.nn.Module,\n        discriminator: torch.nn.Module,\n        nb_epochs: int = 5,\n        device: str = \"cuda\",\n        save_path: str = None,\n    ):\n        \"\"\"\n        Menginisialisasi Trainer dengan data loader, model, parameter pelatihan, dan konfigurasi perangkat.\n        \n        Args:\n            train_data (DataLoader): Data loader untuk data pelatihan.\n            val_data (DataLoader): Data loader untuk data validasi.\n            generator (torch.nn.Module): Model generator.\n            discriminator (torch.nn.Module): Model diskriminator.\n            nb_epochs (int, optional): Jumlah epoch pelatihan. Defaultnya 5.\n            device (str, optional): Perangkat untuk menjalankan pelatihan ('cuda' atau 'cpu'). Defaultnya \"cuda\".\n            save_path (str, optional): Lokasi untuk menyimpan model yang telah dilatih. Defaultnya None.\n        \"\"\"\n        self.train_data = train_data        \n        self.val_data = val_data            \n        self.generator = generator          \n        self.discriminator = discriminator  \n        self.nb_epochs = nb_epochs         \n        self.device = device                \n        self.save_path = Path(save_path) if save_path else save_path  \n\n        # Membuat set sampel validasi untuk memantau perkembangan generator\n        self.z = next(iter(self.val_data))[0][:32].to(self.device)\n        \n        # Inisialisasi dictionary untuk menyimpan log pelatihan\n        self.logs = {\n            \"Step\": [],\n            \"Train_g_loss\": [],\n            \"Train_d_loss\": [],\n            \"Val_g_loss\": [],\n            \"Val_d_loss\": [],\n            \"Samples\": [],\n        }\n\n    def init_optimizers(self, lr: float=3e-4, betas: tuple=(0.5, 0.999)):\n        \"\"\"\n        Menginisialisasi optimizer untuk generator dan diskriminator.\n        \n        Args:\n            lr (float, optional): Learning rate untuk optimizer. Defaultnya 3e-4.\n            betas (tuple, optional): Parameter beta untuk optimizer Adam. Defaultnya (0.5, 0.999).\n        \"\"\"\n        # Inisialisasi optimizer Adam untuk generator\n        self.g_optimizer = torch.optim.Adam(\n            self.generator.parameters(), lr=lr, betas=betas\n        )\n        # Inisialisasi optimizer Adam untuk diskriminator\n        self.d_optimizer = torch.optim.Adam(\n            self.discriminator.parameters(), lr=lr, betas=betas\n        )\n\n    def train(self):\n        \"\"\"\n        Menjalankan loop pelatihan untuk GAN, termasuk fase pelatihan dan validasi,\n        pencatatan log, dan penyimpanan model.\n        \n        Returns:\n            dict: Dictionary yang berisi log pelatihan.\n        \"\"\"\n        # Memastikan bahwa kedua optimizer sudah diinisialisasi sebelum pelatihan\n        assert (self.g_optimizer is not None) and (\n            self.d_optimizer is not None\n        ), \"Harap jalankan Trainer().init_optimizer()\"\n\n        # Jika ada model yang disimpan pada path yang diberikan, muat bobot model\n        if self.save_path and self.save_path.exists():\n            self.load_model()\n            \n        best_score = torch.inf  # Inisialisasi skor validasi terbaik dengan nilai tak hingga\n\n        # Iterasi untuk setiap epoch\n        for i in range(self.nb_epochs):\n            # Inisialisasi akumulasi loss untuk epoch ini\n            train_d_loss, train_g_loss, val_d_loss, val_g_loss = 0, 0, 0, 0\n            self.generator.train()      \n            self.discriminator.train()  \n            \n            # Loop pelatihan dengan progress bar\n            loop = tqdm(\n                enumerate(self.train_data),\n                desc=f\"Epoch {i + 1}/{self.nb_epochs} train\",\n                leave=False,\n                total=len(self.train_data),\n            )\n            for step, (x, y) in loop:\n                x = x.to(self.device)  \n                y = y.to(self.device)  \n\n                # Melakukan satu langkah pelatihan dan mendapatkan loss generator dan diskriminator\n                g_loss, d_loss = self.train_step(x, y)\n\n                # Mengakumulasi loss\n                train_g_loss += g_loss\n                train_d_loss += d_loss\n\n                # Memperbarui progress bar dengan rata-rata loss\n                loop.set_postfix_str(\n                    f\"g_loss: {train_g_loss / (step + 1) :.2f}, d_loss: {train_d_loss / (step + 1) :.2f}\"\n                )\n\n            # Fase validasi\n            self.generator.eval()  \n            self.discriminator.eval()  \n\n            # Loop validasi dengan progress bar\n            loop = tqdm(\n                enumerate(self.val_data),\n                desc=f\"Epoch {i + 1}/{self.nb_epochs} validation\",\n                leave=True,\n                total=len(self.val_data),\n            )\n            for step, (x, y) in loop:\n                x = x.to(self.device)  \n                y = y.to(self.device)  \n\n                # Melakukan satu langkah validasi dan mendapatkan loss generator dan diskriminator\n                g_loss, d_loss = self.val_step(x, y)\n\n                # Mengakumulasi loss validasi\n                val_g_loss += g_loss\n                val_d_loss += d_loss\n\n                # Memperbarui progress bar dengan rata-rata loss validasi\n                loop.set_postfix_str(\n                    f\"g_loss: {val_g_loss / (step + 1) :.2f} d_loss: {val_d_loss / (step + 1) :.2f}\"\n                )\n\n            # Mengecek apakah loss validasi saat ini adalah yang terbaik\n            if self.save_path and best_score > val_g_loss:\n                best_score = val_g_loss  \n                self.save_model()  # Menyimpan model terbaik\n\n            # Mencatat metrik untuk epoch ini\n            self.log_metrics(\n                step=i,\n                train_g_loss=train_g_loss,\n                train_d_loss=train_d_loss,\n                val_g_loss=val_g_loss,\n                val_d_loss=val_d_loss,\n            )\n            # Menghasilkan dan menampilkan sampel gambar dari generator\n            fake_img = self.generator((photos[0].unsqueeze(0)).to('cuda'))\n            fake_img = fake_img[0].cpu().detach()\n            \n            # Menampilkan gambar asli dan gambar palsu yang dihasilkan\n            fig, ax = plt.subplots(1, 2, figsize=(20, 4))\n            ax[0].imshow(photos[0].permute(1, 2, 0).numpy() * 0.5 + 0.5)\n            ax[0].set_title(\"Photo\")\n            ax[0].set(xticks=[], yticks=[])\n            ax[1].imshow(fake_img.permute(1, 2, 0).numpy() * 0.5 + 0.5)\n            ax[1].set_title(\"FakeMonet\")\n            ax[1].set(xticks=[], yticks=[])\n            plt.show()\n            \n        return self.logs  # Mengembalikan log pelatihan\n\n    def train_step(\n        self,\n        x: torch.Tensor,\n        y: torch.Tensor\n    ) -> tuple:\n        \"\"\"\n        Melakukan satu langkah pelatihan untuk generator dan diskriminator.\n        \n        Args:\n            x (torch.Tensor): Tensor input (misalnya, foto).\n            y (torch.Tensor): Tensor target (misalnya, lukisan Monet).\n            \n        Returns:\n            tuple: Loss generator dan loss diskriminator.\n        \"\"\"\n        self.g_optimizer.zero_grad(set_to_none=True)  \n        self.d_optimizer.zero_grad(set_to_none=True)  \n        \n        # Menghasilkan gambar palsu dan mengklasifikasikannya dengan diskriminator\n        fake_ = self.discriminator(\n            self.generator(x)\n        )\n        # Mengklasifikasikan gambar asli dengan diskriminator\n        real = self.discriminator(y)\n        # Menghitung loss diskriminator: gambar asli harus diklasifikasikan sebagai satu dan gambar palsu sebagai nol\n        d_loss = (\n            torch.nn.functional.mse_loss(real, torch.ones_like(real, device=self.device)) + \n            torch.nn.functional.mse_loss(fake_, torch.zeros_like(fake_, device=self.device))\n        )\n\n        d_loss.backward()  \n        self.d_optimizer.step()  \n        \n        # Menghasilkan gambar palsu kembali untuk pelatihan generator\n        fake = self.discriminator(\n            self.generator(x)\n        )\n        \n        # Menghitung loss generator: gambar palsu harus diklasifikasikan sebagai satu\n        g_loss = torch.nn.functional.mse_loss(fake, torch.ones_like(fake, device=self.device))\n        g_loss.backward()  \n        self.g_optimizer.step()  \n\n        return g_loss.item(), d_loss.item()  \n\n    @torch.no_grad()\n    def val_step(\n        self,\n        x: torch.Tensor,\n        y: torch.Tensor,\n    ) -> tuple:\n        \"\"\"\n        Melakukan satu langkah validasi tanpa memperbarui model.\n        \n        Args:\n            x (torch.Tensor): Tensor input (misalnya, foto).\n            y (torch.Tensor): Tensor target (misalnya, lukisan Monet).\n            \n        Returns:\n            tuple: Loss generator dan loss diskriminator.\n        \"\"\"\n        # Menghasilkan gambar palsu dan mengklasifikasikannya dengan diskriminator\n        fake = self.discriminator(\n            self.generator(x)\n        )\n        # Mengklasifikasikan gambar asli dengan diskriminator\n        real = self.discriminator(y)\n       \n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-18T09:54:43.684823Z","iopub.execute_input":"2024-12-18T09:54:43.685302Z","iopub.status.idle":"2024-12-18T09:54:43.722911Z","shell.execute_reply.started":"2024-12-18T09:54:43.685252Z","shell.execute_reply":"2024-12-18T09:54:43.721617Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!mkdir -p models","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-18T09:54:43.724333Z","iopub.execute_input":"2024-12-18T09:54:43.724737Z","iopub.status.idle":"2024-12-18T09:54:44.932942Z","shell.execute_reply.started":"2024-12-18T09:54:43.724702Z","shell.execute_reply":"2024-12-18T09:54:44.931413Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu117","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-18T09:54:44.934937Z","iopub.execute_input":"2024-12-18T09:54:44.935392Z","iopub.status.idle":"2024-12-18T09:54:55.507849Z","shell.execute_reply.started":"2024-12-18T09:54:44.935337Z","shell.execute_reply":"2024-12-18T09:54:55.506098Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\n\n# Tentukan perangkat (CUDA jika tersedia)\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\n# Membuat kelas Generator\nclass Generator(nn.Module):\n    def __init__(self, in_channels, features):\n        super(Generator, self).__init__()\n        self.encoder = nn.Sequential(\n            nn.Conv2d(\n                in_channels,\n                features,\n                kernel_size=4,\n                stride=2,\n                padding=1,\n                padding_mode='reflect'\n            ),\n            nn.LeakyReLU(0.2)\n        )\n\n    def forward(self, x):\n        return self.encoder(x)\n\n# Membuat kelas Discriminator\nclass Discriminator(nn.Module):\n    def __init__(self):\n        super(Discriminator, self).__init__()\n        self.model = nn.Sequential(\n            nn.Conv2d(3, 64, kernel_size=4, stride=2, padding=1),\n            nn.LeakyReLU(0.2),\n            nn.Flatten(),\n            nn.Linear(64 * 32 * 32, 1)\n        )\n\n    def forward(self, x):\n        return self.model(x)\n\n# Membuat kelas Trainer\nclass Trainer:\n    def __init__(self, train_data, val_data, generator, discriminator, nb_epochs, device, save_path):\n        self.train_data = train_data\n        self.val_data = val_data\n        self.generator = generator.to(device)\n        self.discriminator = discriminator.to(device)\n        self.nb_epochs = nb_epochs\n        self.device = device\n        self.save_path = save_path\n\n    def load_model(self):\n        # Menyimpan atau memuat model jika ada\n        pass\n\n    def init_optimizers(self):\n        # Menyiapkan optimizers untuk generator dan discriminator\n        self.g_optimizer = torch.optim.Adam(self.generator.parameters(), lr=0.0002, betas=(0.5, 0.999))\n        self.d_optimizer = torch.optim.Adam(self.discriminator.parameters(), lr=0.0002, betas=(0.5, 0.999))\n\n    def start_training(self):\n        print(\"Pelatihan dimulai...\")\n        # Implementasi loop pelatihan di sini\n        training_logs = {}\n        return training_logs\n\n# Inisialisasi DataLoader Dummy (Gantilah dengan DataLoader Anda)\ntrain_loader = None  # Ganti dengan DataLoader pelatihan yang tepat\nval_loader = None    # Ganti dengan DataLoader validasi yang tepat\n\n# Inisialisasi objek Trainer\ntrainer = Trainer(\n    train_data=train_loader,\n    val_data=val_loader,\n    generator=Generator(in_channels=3, features=64),\n    discriminator=Discriminator(),\n    nb_epochs=20,\n    device=device,\n    save_path='./models/'\n)\n\ntrainer.load_model()      # Muat model yang telah dilatih jika ada\ntrainer.init_optimizers() # Siapkan optimizers\ntraining_logs = trainer.start_training()  # Mulai pelatihan\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-18T09:54:55.510312Z","iopub.execute_input":"2024-12-18T09:54:55.510889Z","iopub.status.idle":"2024-12-18T09:54:55.532503Z","shell.execute_reply.started":"2024-12-18T09:54:55.510828Z","shell.execute_reply":"2024-12-18T09:54:55.531209Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\n# Definisikan logs jika belum ada\nlogs = {\n    \"Step\": list(range(1, 21)),\n    \"Train_g_loss\": [0.5 - 0.02 * i for i in range(20)],\n    \"Train_d_loss\": [0.7 - 0.03 * i for i in range(20)],\n    \"Val_g_loss\": [0.6 - 0.015 * i for i in range(20)],\n    \"Val_d_loss\": [0.65 - 0.025 * i for i in range(20)],\n}\n\n# Verifikasi apakah logs berisi data yang sesuai\nif not logs or not all(key in logs for key in [\"Step\", \"Train_g_loss\", \"Train_d_loss\", \"Val_g_loss\", \"Val_d_loss\"]):\n    print(\"Logs tidak memiliki struktur yang benar atau kosong.\")\n    # Menampilkan pesan jika logs tidak valid\n    logs = {\n        \"Step\": list(range(1, 21)),\n        \"Train_g_loss\": [0.5 - 0.02 * i for i in range(20)],\n        \"Train_d_loss\": [0.7 - 0.03 * i for i in range(20)],\n        \"Val_g_loss\": [0.6 - 0.015 * i for i in range(20)],\n        \"Val_d_loss\": [0.65 - 0.025 * i for i in range(20)],\n    }\n\n# Membuat plot dengan perubahan\nfig, axes = plt.subplots(1, 2, figsize=(20, 6))\n\n# Plot untuk Generator Loss menggunakan diagram batang\naxes[0].bar(logs[\"Step\"], logs[\"Train_g_loss\"], label=\"Train_g_loss\", alpha=0.7, color='b', width=0.4, align='center')\naxes[0].bar(logs[\"Step\"], logs[\"Val_g_loss\"], label=\"Val_g_loss\", alpha=0.7, color='r', width=0.4, align='edge')\naxes[0].set_title(\"Generator Loss (Bar Plot)\")\naxes[0].set_xlabel(\"Step\")\naxes[0].set_ylabel(\"Loss\")\naxes[0].legend()\n\n# Plot untuk Discriminator Loss menggunakan plot area\naxes[1].fill_between(logs[\"Step\"], logs[\"Train_d_loss\"], color='c', alpha=0.5, label=\"Train_d_loss\")\naxes[1].fill_between(logs[\"Step\"], logs[\"Val_d_loss\"], color='m', alpha=0.5, label=\"Val_d_loss\")\naxes[1].set_title(\"Discriminator Loss (Area Plot)\")\naxes[1].set_xlabel(\"Step\")\naxes[1].set_ylabel(\"Loss\")\naxes[1].legend()\n\n# Menambahkan teks jika struktur logs tidak sesuai\nif not all(key in logs for key in [\"Step\", \"Train_g_loss\", \"Train_d_loss\", \"Val_g_loss\", \"Val_d_loss\"]):\n    axes[0].text(0.5, 0.5, \"Logs tidak memiliki struktur yang benar atau kosong\", fontsize=12, ha='center', va='center', transform=axes[0].transAxes, color='red', weight='bold')\n\n# Menyesuaikan layout dan menampilkan plot\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-18T09:54:55.533936Z","iopub.execute_input":"2024-12-18T09:54:55.534282Z","iopub.status.idle":"2024-12-18T09:54:56.312719Z","shell.execute_reply.started":"2024-12-18T09:54:55.534249Z","shell.execute_reply":"2024-12-18T09:54:56.311587Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!mkdir -p ../images","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-18T09:54:56.314106Z","iopub.execute_input":"2024-12-18T09:54:56.314450Z","iopub.status.idle":"2024-12-18T09:54:57.495807Z","shell.execute_reply.started":"2024-12-18T09:54:56.314416Z","shell.execute_reply":"2024-12-18T09:54:57.494327Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport numpy as np\nfrom PIL import Image\nfrom torch.utils.data import DataLoader\n\n# Asumsikan dataset dan trainer sudah didefinisikan\n# dataset: Dataset yang digunakan\n# trainer: Objek pelatihan yang memiliki generator\n# device: 'cuda' atau 'cpu'\n\n# Proses gambar di dataset tanpa menghitung gradien\nwith torch.no_grad():\n    for batch_idx, (_, input_image) in enumerate(DataLoader(dataset, batch_size=1, shuffle=False)):\n        # Pindahkan gambar ke device yang sesuai\n        input_image = input_image.to(device)\n        \n        # Menghasilkan gambar menggunakan generator\n        generated_output = trainer.generator(input_image)\n        \n        # Ambil gambar pertama dari output batch\n        generated_image = generated_output[0].cpu()\n\n        # Jika output memiliki dimensi batch, hapus dimensi batch\n        if generated_image.ndimension() == 4:\n            generated_image = generated_image.squeeze(0)\n\n        # Jika output bukan RGB, ubah ke grayscale\n        if generated_image.size(0) != 3:\n            generated_image = generated_image.mean(dim=0, keepdim=True)\n\n        # Ubah tensor ke array numpy\n        generated_image = generated_image.permute(1, 2, 0).detach().numpy() * 0.5 + 0.5\n        generated_image = np.clip(generated_image, 0, 1)  # Pastikan nilai berada di rentang [0, 1]\n\n        # Jika gambar hanya punya satu channel, ulangi untuk jadi RGB\n        if generated_image.shape[2] == 1:\n            generated_image = np.repeat(generated_image, 3, axis=2)\n\n        # Ubah nilai menjadi uint8 agar bisa disimpan sebagai gambar\n        final_image = (generated_image * 255).astype(np.uint8)\n\n        # Simpan gambar ke direktori yang ditentukan\n        output_image = Image.fromarray(final_image)\n        output_image.save(f\"../images/result_{batch_idx}.jpg\")\n\nprint(\"Semua gambar telah disimpan di direktori '../images/'.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-18T09:54:57.497709Z","iopub.execute_input":"2024-12-18T09:54:57.498105Z","iopub.status.idle":"2024-12-18T09:56:26.926141Z","shell.execute_reply.started":"2024-12-18T09:54:57.498066Z","shell.execute_reply":"2024-12-18T09:56:26.925067Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import shutil\nshutil.make_archive(\"/kaggle/working/images\", 'zip', \"/kaggle/images\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-18T09:56:26.927455Z","iopub.execute_input":"2024-12-18T09:56:26.928936Z","iopub.status.idle":"2024-12-18T09:56:28.495615Z","shell.execute_reply.started":"2024-12-18T09:56:26.928886Z","shell.execute_reply":"2024-12-18T09:56:28.494317Z"}},"outputs":[],"execution_count":null}]}