{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":21755,"databundleVersionId":1475600,"sourceType":"competition"}],"dockerImageVersionId":30747,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"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        if not filename.endswith('.jpg'):  # Skip files with '.jpg' extension\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\n\n# modal path: /kaggle/input/0731a/pytorch/default/1/view","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2024-12-14T01:55:07.031163Z","iopub.execute_input":"2024-12-14T01:55:07.031468Z","iopub.status.idle":"2024-12-14T01:55:12.635056Z","shell.execute_reply.started":"2024-12-14T01:55:07.031444Z","shell.execute_reply":"2024-12-14T01:55:12.634129Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport torch\nimport torch.nn as nn\nimport torchvision\nimport shutil\nfrom torch.utils.data import Dataset, DataLoader, random_split\nfrom torchvision.utils import make_grid\nfrom PIL import Image\nfrom torchvision import transforms\nfrom tqdm import tqdm\nfrom pathlib import Path","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-14T01:55:12.637111Z","iopub.execute_input":"2024-12-14T01:55:12.637840Z","iopub.status.idle":"2024-12-14T01:55:17.222087Z","shell.execute_reply.started":"2024-12-14T01:55:12.637808Z","shell.execute_reply":"2024-12-14T01:55:17.221075Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Menetapkan direktori root data dari folder input Kaggle\ndata_root = '/kaggle/input/gan-getting-started/'\nprint(data_root)  # Menampilkan path direktori data root\nprint(os.listdir(data_root))  # Menampilkan daftar file yang ada dalam direktori data root\n\n# Menetapkan path untuk gambar Monet dan foto\nmonet_path = 'monet_jpg'\nphoto_path = 'photo_jpg'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-14T01:55:17.223423Z","iopub.execute_input":"2024-12-14T01:55:17.224147Z","iopub.status.idle":"2024-12-14T01:55:17.228803Z","shell.execute_reply.started":"2024-12-14T01:55:17.224122Z","shell.execute_reply":"2024-12-14T01:55:17.228013Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class MonetPhoto(Dataset):\n    def __init__(self, data_root, monet_path, photo_path, transform=None):\n        \"\"\"\n        Inisialisasi dataset MonetPhoto dengan path ke gambar Monet dan foto serta transformasi opsional.\n        \n        Args:\n            data_root (str): Direktori root tempat data disimpan.\n            monet_path (str): Subdirektori dalam data_root yang berisi gambar bergaya Monet.\n            photo_path (str): Subdirektori dalam data_root yang berisi gambar foto.\n            transform (callable, opsional): Transformasi opsional yang diterapkan pada sampel.\n        \"\"\"\n        self.data_root = data_root  # Menetapkan direktori root untuk data\n        self.monet_path = monet_path  # Menetapkan subdirektori untuk gambar Monet\n        self.monet_images = os.listdir(os.path.join(data_root, monet_path))  # Menyusun daftar nama file gambar Monet\n        self.photo_path = photo_path  # Menetapkan subdirektori untuk gambar foto\n        self.photo_images = os.listdir(os.path.join(data_root, photo_path))  # Menyusun daftar nama file gambar foto\n        self.transform = transform  # Menetapkan transformasi yang diterapkan pada gambar\n\n    def __len__(self):\n        \"\"\"\n        Mengembalikan jumlah total sampel dalam dataset.\n        Panjang dataset adalah jumlah maksimum antara gambar Monet dan gambar foto untuk memastikan pasangan gambar.\n        \n        Returns:\n            int: Jumlah total sampel.\n        \"\"\"\n        return max(len(self.monet_images), len(self.photo_images))  # Memastikan semua gambar dipasangkan\n\n    def __getitem__(self, idx):\n        \"\"\"\n        Mengambil sampel dari dataset pada indeks yang diberikan.\n        Memasangkan gambar Monet dengan gambar foto. Jika indeks melebihi jumlah gambar yang tersedia,\n        indeks akan dibungkus menggunakan operasi modulo.\n        \n        Args:\n            idx (int): Indeks dari sampel yang diambil.\n        \n        Returns:\n            tuple: Pasangan gambar Monet dan foto setelah transformasi diterapkan.\n        \"\"\"\n        # Membuka gambar Monet pada indeks yang diberikan, dengan pembungkus jika indeks melebihi panjang\n        monet_image = Image.open(os.path.join(self.data_root, self.monet_path, self.monet_images[idx % len(self.monet_images)]))\n        # Membuka gambar foto pada indeks yang diberikan, dengan pembungkus jika indeks melebihi panjang\n        photo_image = Image.open(os.path.join(self.data_root, self.photo_path, self.photo_images[idx % len(self.photo_images)]))\n        \n        # Menerapkan transformasi jika ada yang diberikan\n        if self.transform:\n            monet_image = self.transform(monet_image)  # Mentransformasi gambar Monet\n            photo_image = self.transform(photo_image)  # Mentransformasi gambar foto\n        \n        return monet_image, photo_image  # Mengembalikan pasangan gambar yang telah ditransformasi\n\n\n# Mendefinisikan urutan transformasi yang akan diterapkan pada gambar\ntransforms = torchvision.transforms.Compose([\n    torchvision.transforms.Resize((256, 256)),  # Mengubah ukuran gambar menjadi 256x256 piksel\n    torchvision.transforms.ToTensor(),  # Mengonversi gambar PIL menjadi tensor PyTorch\n    torchvision.transforms.Normalize(0.5, 0.5)  # Menormalkan nilai tensor ke rentang [-1, 1]\n])\n\n# Membuat instance dataset MonetPhoto dengan path yang ditentukan dan transformasi yang diterapkan\ndataset = MonetPhoto(data_root, monet_path, photo_path, transform=transforms)\n\n# Mendapatkan jumlah total sampel dalam dataset\nlen(dataset)\n\n# Menghitung jumlah sampel untuk pelatihan (80%) dan pengujian (20%)\ntrain_size, test_size = int(len(dataset) * 0.8), len(dataset) - int(len(dataset) * 0.8)\n# Membagi dataset menjadi subset pelatihan dan pengujian\ntraining_data, testing_data = random_split(dataset, [train_size, test_size])\n\n# Mendapatkan jumlah sampel dalam subset pelatihan\nlen(training_data)\n\n# Membuat DataLoader untuk data pelatihan dengan ukuran batch 32, pengacakan diaktifkan, dan menggunakan 2 proses worker\ntrain_dataloader = DataLoader(training_data.dataset, batch_size=32, shuffle=True, num_workers=2)\n# Membuat DataLoader untuk data pengujian dengan ukuran batch 32, pengacakan diaktifkan, dan menggunakan 2 proses worker\ntest_dataloader = DataLoader(testing_data.dataset, batch_size=32, shuffle=True, num_workers=2)\n\n# Mengambil batch pertama dari gambar Monet dan foto dari DataLoader pelatihan\nmones, photos = next(iter(train_dataloader))\n\n# Menampilkan gambar Monet pertama dalam batch\nplt.imshow(mones[0].permute(1, 2, 0).numpy() * 0.5 + 0.5)  # Mengonversi tensor menjadi array numpy dan mendenormalisasi untuk ditampilkan","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-14T01:55:17.230017Z","iopub.execute_input":"2024-12-14T01:55:17.230261Z","iopub.status.idle":"2024-12-14T01:55:18.770264Z","shell.execute_reply.started":"2024-12-14T01:55:17.230241Z","shell.execute_reply":"2024-12-14T01:55:18.769297Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class 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        Args:\n            in_channels (int): Jumlah saluran input.\n            out_channels (int): Jumlah saluran output.\n            stride (int, optional): Langkah untuk konvolusi. Defaultnya 2.\n            device (str, optional): Perangkat tempat operasi dijalankan ('cuda' atau 'cpu'). Defaultnya \"cuda\".\n        \"\"\"\n        super().__init__()  # Inisialisasi kelas induk\n        self.device = device  # Menentukan perangkat\n        self.conv = nn.Sequential(\n            nn.Conv2d(\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            ),  # Lapisan konvolusi dengan parameter yang ditentukan\n            nn.BatchNorm2d(out_channels, device=self.device),  # Normalisasi batch untuk menstabilkan dan mempercepat pelatihan\n            nn.LeakyReLU(0.2),  # Aktivasi Leaky ReLU dengan kemiringan negatif 0.2\n        )\n        \n    def forward(self, x):\n        \"\"\"\n        Proses maju melalui CNNBlock.\n        \n        Args:\n            x (torch.Tensor): Tensor input.\n        \n        Returns:\n            torch.Tensor: Tensor output setelah konvolusi, normalisasi batch, dan aktivasi.\n        \"\"\"\n        return self.conv(x)  # Terapkan lapisan-lapisan sekuensial ke input\n\n\nclass Discriminator(nn.Module):\n    def __init__(self, in_channels=3, features=[64, 128, 256, 512], device=\"cuda\"):\n        \"\"\"\n        Inisialisasi model Discriminator menggunakan serangkaian CNNBlocks.\n        \n        Args:\n            in_channels (int, optional): Jumlah saluran input. Defaultnya 3 (misalnya gambar RGB).\n            features (list, optional): Daftar ukuran fitur untuk setiap CNNBlock. Defaultnya [64, 128, 256, 512].\n            device (str, optional): Perangkat tempat operasi dijalankan ('cuda' atau 'cpu'). Defaultnya \"cuda\".\n        \"\"\"\n        super().__init__()  # Inisialisasi kelas induk\n        self.device = device  # Menentukan perangkat\n        self.initial = nn.Sequential(\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            ),  # Lapisan konvolusi awal\n            nn.LeakyReLU(0.2)  # Aktivasi Leaky ReLU\n        )\n        \n        layers = []  # Daftar untuk menyimpan CNNBlocks berikutnya\n        in_channels = features[0]  # Menentukan jumlah saluran input awal\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            )  # Menambahkan CNNBlock dengan langkah yang sesuai\n            in_channels = feature  # Memperbarui jumlah saluran input untuk blok berikutnya\n        \n        layers.append(\n            nn.Conv2d(\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        )  # Lapisan konvolusi akhir untuk menghasilkan satu saluran output (klasifikasi nyata/palsu)\n            \n        self.model = nn.Sequential(*layers)  # Menggabungkan semua lapisan menjadi model sekuensial\n        \n    def forward(self, x):\n        \"\"\"\n        Proses maju melalui Discriminator.\n        \n        Args:\n            x (torch.Tensor): Tensor input (misalnya gambar).\n        \n        Returns:\n            torch.Tensor: Tensor output yang mewakili prediksi discriminator.\n        \"\"\"\n        x = self.initial(x)  # Terapkan konvolusi awal dan aktivasi\n        return self.model(x)  # Terapkan CNNBlocks berikutnya dan konvolusi akhir\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, yang bisa berfungsi sebagai lapisan downsampling atau upsampling.\n        \n        Args:\n            in_channels (int): Jumlah saluran input.\n            out_channels (int): Jumlah saluran output.\n            down (bool, optional): Jika True, melakukan downsampling menggunakan Conv2d; jika False, upsampling menggunakan ConvTranspose2d. Defaultnya True.\n            act (str, optional): Fungsi aktivasi yang digunakan ('relu' atau 'leaky'). Defaultnya 'relu'.\n            use_dropout (bool, optional): Menentukan apakah akan menggunakan lapisan dropout. Defaultnya False.\n            device (str, optional): Perangkat tempat operasi dijalankan ('cuda' atau 'cpu'). Defaultnya \"cuda\".\n        \"\"\"\n        super().__init__()  # Inisialisasi kelas induk\n        self.device = device  # Menentukan perangkat\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            ),  # Pilih Conv2d untuk downsampling atau ConvTranspose2d untuk upsampling\n            nn.BatchNorm2d(out_channels, device=self.device),  # Normalisasi batch\n            nn.ReLU() if act == 'relu' else nn.LeakyReLU(0.2),  # Fungsi aktivasi\n        )\n        self.use_dropout = use_dropout  # Menentukan apakah akan menggunakan dropout\n        self.dropout = nn.Dropout(0.5)  # Lapisan dropout dengan tingkat dropout 50%\n    \n    def forward(self, x):\n        \"\"\"\n        Proses maju melalui Block.\n        \n        Args:\n            x (torch.Tensor): Tensor input.\n        \n        Returns:\n            torch.Tensor: Tensor output setelah konvolusi, normalisasi, aktivasi, dan dropout (jika diaktifkan).\n        \"\"\"\n        x = self.conv(x)  # Terapkan konvolusi, normalisasi, dan aktivasi\n        return self.dropout(x) if self.use_dropout else x  # Terapkan dropout jika diaktifkan, jika tidak, kembalikan tensor\n\n\nclass Generator(nn.Module):\n    def __init__(self, in_channels=3, features=64, device=\"cuda\"):\n        \"\"\"\n        Inisialisasi model Generator menggunakan serangkaian Block untuk downsampling dan upsampling.\n        \n        Args:\n            in_channels (int, optional): Jumlah saluran input. Defaultnya 3 (misalnya gambar RGB).\n            features (int, optional): Jumlah saluran fitur dasar. Defaultnya 64.\n            device (str, optional): Perangkat tempat operasi dijalankan ('cuda' atau 'cpu'). Defaultnya \"cuda\".\n        \"\"\"\n        super().__init__()  # Inisialisasi kelas induk\n        self.device = device  # Menentukan perangkat\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            ),  # Lapisan konvolusi awal\n            nn.LeakyReLU(0.2),  # Aktivasi Leaky ReLU\n        )  # Ukuran output: 128\n        \n        # Menentukan Block downsampling\n        self.down1 = Block(features, features*2, down=True, act='leaky', use_dropout=False)   # Ukuran output: 64\n        self.down2 = Block(features*2, features*4, down=True, act='leaky', use_dropout=False) # Ukuran output: 32\n        self.down3 = Block(features*4, features*8, down=True, act='leaky', use_dropout=False) # Ukuran output: 16\n        self.down4 = Block(features*8, features*8, down=True, act='leaky', use_dropout=False) # Ukuran output: 8\n        self.down5 = Block(features*8, features*8, down=True, act='leaky', use_dropout=False) # Ukuran output: 4\n        self.down6 = Block(features*8, features*8, down=True, act='leaky', use_dropout=False) # Ukuran output: 2\n        \n        # Lapisan 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            ),  # Lapisan konvolusi bottleneck\n            nn.ReLU(),  # Aktivasi ReLU\n        )  # Ukuran output: 1x1\n        \n        # Menentukan Block upsampling dengan koneksi skip\n        self.up1 = Block(features*8, features*8, down=False, act='relu', use_dropout=True)  # Ukuran output: 2\n        self.up2 = Block(features*8*2, features*8, down=False, act='relu', use_dropout=True)  # Ukuran output: 4\n        self.up3 = Block(features*8*2, features*8, down=False, act='relu', use_dropout=True)  # Ukuran output: 8\n        self.up4 = Block(features*8*2, features*8, down=False, act='relu', use_dropout=False) # Ukuran output: 16\n        self.up5 = Block(features*8*2, features*4, down=False, act='relu', use_dropout=False) # Ukuran output: 32\n        self.up6 = Block(features*4*2, features*2, down=False, act='relu', use_dropout=False) # Ukuran output: 64\n        self.up7 = Block(features*2*2, features, down=False, act='relu', use_dropout=False)   # Ukuran output: 128\n        \n        # Lapisan upsampling akhir untuk merekonstruksi gambar\n        self.finil_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            ),  # Transposed convolution untuk upsampling ke ukuran gambar asli\n            nn.Tanh(),  # Aktivasi Tanh untuk menskalakan output antara -1 dan 1\n        )\n        \n    def forward(self, x):\n        \"\"\"\n        Proses maju melalui Generator, yang menerapkan arsitektur U-Net dengan koneksi skip.\n        \n        Args:\n            x (torch.Tensor): Tensor input (misalnya gambar).\n        \n        Returns:\n            torch.Tensor: Tensor output yang mewakili gambar yang dihasilkan.\n        \"\"\"\n        d1 = self.initial_down(x)  # Terapkan downsampling awal\n        d2 = self.down1(d1)  # Terapkan Block downsampling pertama\n        d3 = self.down2(d2)  # Terapkan Block downsampling kedua\n        d4 = self.down3(d3)  # Terapkan Block downsampling ketiga\n        d5 = self.down4(d4)  # Terapkan Block downsampling keempat\n        d6 = self.down5(d5)  # Terapkan Block downsampling kelima\n        d7 = self.down6(d6)  # Terapkan Block downsampling keenam\n        \n        bottleneck = self.bottleneck(d7)  # Terapkan lapisan bottleneck\n        \n        up1 = self.up1(bottleneck)  # Terapkan Block upsampling pertama\n        up2 = self.up2(torch.cat([up1, d7], 1))  # Gabungkan dengan peta fitur yang sesuai dan terapkan Block upsampling kedua\n        up3 = self.up3(torch.cat([up2, d6], 1))  # Gabungkan dengan peta fitur yang sesuai dan terapkan Block upsampling ketiga\n        up4 = self.up4(torch.cat([up3, d5], 1))  # Gabungkan dengan peta fitur yang sesuai dan terapkan Block upsampling keempat\n        up5 = self.up5(torch.cat([up4, d4], 1))  # Gabungkan dengan peta fitur yang sesuai dan terapkan Block upsampling kelima\n        up6 = self.up6(torch.cat([up5, d3], 1))  # Gabungkan dengan peta fitur yang sesuai dan terapkan Block upsampling keenam\n        up7 = self.up7(torch.cat([up6, d2], 1))  # Gabungkan dengan peta fitur yang sesuai dan terapkan Block upsampling ketujuh\n\n        # Gabungkan dengan peta fitur downsampling awal dan terapkan upsampling akhir untuk menghasilkan gambar output\n        return self.finil_up(torch.cat([up7, d1], 1))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-14T01:55:18.772848Z","iopub.execute_input":"2024-12-14T01:55:18.773250Z","iopub.status.idle":"2024-12-14T01:55:18.798025Z","shell.execute_reply.started":"2024-12-14T01:55:18.773223Z","shell.execute_reply":"2024-12-14T01:55:18.797018Z"}},"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        Inisialisasi Trainer dengan data loader, model, parameter pelatihan, dan konfigurasi perangkat.\n        \n        Args:\n            train_data (DataLoader): DataLoader untuk data pelatihan.\n            val_data (DataLoader): DataLoader untuk data validasi.\n            generator (torch.nn.Module): Model generator.\n            discriminator (torch.nn.Module): Model discriminator.\n            nb_epochs (int, opsional): Jumlah epoch pelatihan. Defaultnya 5.\n            device (str, opsional): Perangkat tempat pelatihan dijalankan ('cuda' atau 'cpu'). Defaultnya \"cuda\".\n            save_path (str, opsional): Jalur untuk menyimpan model yang telah dilatih. Defaultnya None.\n        \"\"\"\n        self.train_data = train_data        # Menetapkan data loader untuk pelatihan\n        self.val_data = val_data            # Menetapkan data loader untuk validasi\n        self.generator = generator          # Menetapkan model generator\n        self.discriminator = discriminator  # Menetapkan model discriminator\n        self.nb_epochs = nb_epochs          # Menetapkan jumlah epoch\n        self.device = device                # Menetapkan perangkat untuk komputasi\n        self.save_path = Path(save_path) if save_path else save_path  # Menetapkan jalur penyimpanan sebagai objek Path jika disediakan\n\n        # Inisialisasi sampel validasi tetap untuk memantau kemajuan generator\n        self.z = next(iter(self.val_data))[0][:32].to(self.device)\n        \n        # Inisialisasi kamus 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        Inisialisasi optimizers untuk generator dan discriminator.\n        \n        Args:\n            lr (float, opsional): Learning rate untuk optimizers. Defaultnya 3e-4.\n            betas (tuple, opsional): 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 discriminator\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: Kamus yang berisi log pelatihan.\n        \"\"\"\n        # Memastikan bahwa kedua optimizer sudah diinisialisasi sebelum pelatihan dimulai\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 model yang sudah disimpan ada di jalur penyimpanan, muat bobot model\n        if self.save_path and self.save_path.exists():\n            self.load_model()\n            \n        best_score = torch.inf  # Inisialisasi nilai skor validasi terbaik sebagai tak terhingga\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()      # Mengatur generator ke mode pelatihan\n            self.discriminator.train()  # Mengatur discriminator ke mode pelatihan\n            \n            # Loop pelatihan dengan progress bar\n            loop = tqdm(\n                enumerate(self.train_data),\n                desc=f\"Epoch {i + 1}/{self.nb_epochs} pelatihan\",\n                leave=False,\n                total=len(self.train_data),\n            )\n            for step, (x, y) in loop:\n                x = x.to(self.device)  # Memindahkan data input ke perangkat yang ditentukan\n                y = y.to(self.device)  # Memindahkan data target ke perangkat yang ditentukan\n\n                # Melakukan langkah pelatihan dan mendapatkan loss untuk generator dan discriminator\n                g_loss, d_loss = self.train_step(x, y)\n\n                # Akumulasi 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()  # Mengatur generator ke mode evaluasi\n            self.discriminator.eval()  # Mengatur discriminator ke mode evaluasi\n\n            # Loop validasi dengan progress bar\n            loop = tqdm(\n                enumerate(self.val_data),\n                desc=f\"Epoch {i + 1}/{self.nb_epochs} validasi\",\n                leave=True,\n                total=len(self.val_data),\n            )\n            for step, (x, y) in loop:\n                x = x.to(self.device)  # Memindahkan data input ke perangkat yang ditentukan\n                y = y.to(self.device)  # Memindahkan data target ke perangkat yang ditentukan\n\n                # Melakukan langkah validasi dan mendapatkan loss untuk generator dan discriminator\n                g_loss, d_loss = self.val_step(x, y)\n\n                # Akumulasi 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            # Memeriksa apakah loss validasi saat ini adalah yang terbaik sejauh ini\n            if self.save_path and best_score > val_g_loss:\n                best_score = val_g_loss  # Memperbarui skor terbaik\n                self.save_model()  # Menyimpan model saat ini sebagai 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 gambar sampel 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(\"Foto\")\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 discriminator.\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 discriminator.\n        \"\"\"\n        self.g_optimizer.zero_grad(set_to_none=True)  # Mengatur ulang gradien generator\n        self.d_optimizer.zero_grad(set_to_none=True)  # Mengatur ulang gradien discriminator\n        \n        # Menghasilkan gambar palsu dan mengklasifikasikannya dengan discriminator\n        fake_ = self.discriminator(\n            self.generator(x)\n        )\n        # Mengklasifikasikan gambar asli dengan discriminator\n        real = self.discriminator(y)\n        # Menghitung loss discriminator: 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()  # Melakukan backpropagation untuk loss discriminator\n        self.d_optimizer.step()  # Memperbarui bobot discriminator\n        \n        # Menghasilkan kembali gambar palsu 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()  # Melakukan backpropagation untuk loss generator\n        self.g_optimizer.step()  # Memperbarui bobot generator\n\n        return g_loss.item(), d_loss.item()  # Mengembalikan loss sebagai nilai skalar\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 discriminator.\n        \"\"\"\n        # Menghasilkan gambar palsu dan mengklasifikasikannya dengan discriminator\n        fake = self.discriminator(\n            self.generator(x)\n        )\n        # Mengklasifikasikan gambar asli dengan discriminator\n        real = self.discriminator(y)\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        # Menghitung loss discriminator: gambar asli 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        return g_loss.item(), d_loss.item()  # Mengembalikan loss sebagai nilai skalar\n\n    @torch.no_grad()\n    def log_metrics(\n        self,\n        step: int,\n        train_g_loss: torch.Tensor,\n        train_d_loss: torch.Tensor,\n        val_g_loss: torch.Tensor,\n        val_d_loss: torch.Tensor,\n    ):\n        \"\"\"\n        Mencatat metrik pelatihan dan validasi untuk epoch ini.\n        \n        Args:\n            step (int): Nomor epoch saat ini.\n            train_g_loss (torch.Tensor): Akumulasi loss generator selama pelatihan.\n            train_d_loss (torch.Tensor): Akumulasi loss discriminator selama pelatihan.\n            val_g_loss (torch.Tensor): Akumulasi loss generator selama validasi.\n            val_d_loss (torch.Tensor): Akumulasi loss discriminator selama validasi.\n        \"\"\"\n        self.logs[\"Step\"].append(step)  # Mencatat epoch saat ini\n        self.logs[\"Train_g_loss\"].append(train_g_loss / len(self.train_data))  # Mencatat rata-rata loss generator saat pelatihan\n        self.logs[\"Train_d_loss\"].append(train_d_loss / len(self.train_data))  # Mencatat rata-rata loss discriminator saat pelatihan\n        self.logs[\"Val_g_loss\"].append(val_g_loss / len(self.val_data))  # Mencatat rata-rata loss generator saat validasi\n        self.logs[\"Val_d_loss\"].append(val_d_loss / len(self.val_data))  # Mencatat rata-rata loss discriminator saat validasi\n        self.logs[\"Samples\"].append(make_grid(self.generator(self.z).cpu() * 0.5 + 0.5, normalize=True))  # Mencatat gambar sampel yang dihasilkan\n\n    def save_model(self, full: bool = False):\n        \"\"\"\n        Menyimpan model generator dan discriminator.\n        \n        Args:\n            full (bool, opsional): Apakah ingin menyimpan seluruh model atau hanya state dict. Defaultnya False.\n        \"\"\"\n        if full:\n            # Menyimpan seluruh model generator dan discriminator\n            torch.save(self.generator, Path(self.save_path) / \"generator.pth\")\n            torch.save(self.discriminator, Path(self.save_path) / \"discriminator.pth\")\n        else:\n            # Menyimpan hanya state dict (berat) model\n            torch.save(\n                self.generator.state_dict(),\n                Path(self.save_path) / \"generator_weights.pth\",\n            )\n            torch.save(\n                self.discriminator.state_dict(),\n                Path(self.save_path) / \"discriminator_weights.pth\",\n            )\n\n    def load_model(self, full: bool = False):\n        \"\"\"\n        Memuat model generator dan discriminator.\n        \n        Args:\n            full (bool, opsional): Apakah ingin memuat seluruh model atau hanya state dict. Defaultnya False.\n        \"\"\"\n        if (\n            full\n            and (self.save_path / \"generator.pth\").is_file()\n            and (self.save_path / \"discriminator.pth\").is_file()\n        ):\n            # Memuat seluruh model generator dan discriminator\n            self.generator = torch.load(self.save_path / \"generator.pth\")\n            self.discriminator = torch.load(self.save_path / \"discriminator.pth\")\n        elif (\n            (self.save_path / \"generator_weights.pth\").is_file() and\n            (self.save_path / \"discriminator_weights.pth\").is_file()\n        ):\n            # Memuat hanya state dict (berat) model\n            self.generator.load_state_dict(torch.load(self.save_path / \"generator_weights.pth\"))\n            self.discriminator.load_state_dict(torch.load(self.save_path / \"discriminator_weights.pth\"))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-14T01:55:18.799450Z","iopub.execute_input":"2024-12-14T01:55:18.799963Z","iopub.status.idle":"2024-12-14T01:55:18.826773Z","shell.execute_reply.started":"2024-12-14T01:55:18.799910Z","shell.execute_reply":"2024-12-14T01:55:18.825735Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!mkdir models","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-14T01:55:18.827966Z","iopub.execute_input":"2024-12-14T01:55:18.828508Z","iopub.status.idle":"2024-12-14T01:55:19.870523Z","shell.execute_reply.started":"2024-12-14T01:55:18.828478Z","shell.execute_reply":"2024-12-14T01:55:19.869283Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Tentukan perangkat untuk digunakan (GPU jika tersedia, jika tidak gunakan CPU)\ndevice = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\n# Inisialisasi Trainer dengan komponen dan parameter yang diperlukan\ntrainer = Trainer(\n    train_data=train_dataloader,    # DataLoader untuk dataset pelatihan\n    val_data=test_dataloader,       # DataLoader untuk dataset validasi\n    generator=Generator(in_channels=3, features=64),  # Inisialisasi model Generator dengan 3 channel input dan 64 feature maps\n    discriminator=Discriminator(),  # Inisialisasi model Discriminator dengan parameter default\n    nb_epochs=20,                   # Tentukan jumlah epoch pelatihan menjadi 20\n    device=device,                  # Tentukan perangkat ('cuda' atau 'cpu') untuk pelatihan\n    save_path='./models/'           # Path direktori untuk menyimpan model yang sudah dilatih\n)\n\ntrainer.load_model()       # Muat bobot model yang sudah dilatih sebelumnya, jika tersedia di path yang ditentukan\ntrainer.init_optimizers()  # Inisialisasi optimizer untuk model generator dan discriminator\nlogs = trainer.train()     # Mulai proses pelatihan dan simpan log pelatihan untuk analisis lebih lanjut","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-14T01:55:19.872096Z","iopub.execute_input":"2024-12-14T01:55:19.872402Z","iopub.status.idle":"2024-12-14T03:02:18.277922Z","shell.execute_reply.started":"2024-12-14T01:55:19.872379Z","shell.execute_reply":"2024-12-14T03:02:18.277045Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Membuat figure dengan 2 subplot untuk menampilkan grafik loss generator dan discriminator\nfig, ax = plt.subplots(1, 2, figsize=(20, 4))\n\n# Plotkan loss generator pada data pelatihan (Train_g_loss) di subplot pertama\nax[0].plot(logs[\"Step\"], logs[\"Train_g_loss\"], label=\"Train_g_loss\")\n\n# Plotkan loss discriminator pada data pelatihan (Train_d_loss) di subplot kedua\nax[1].plot(logs[\"Step\"], logs[\"Train_d_loss\"], label=\"Train_d_loss\")\n\n# Plotkan loss generator pada data validasi (Val_g_loss) di subplot pertama\nax[0].plot(logs[\"Step\"], logs[\"Val_g_loss\"], label=\"Val_g_loss\")\n\n# Plotkan loss discriminator pada data validasi (Val_d_loss) di subplot kedua\nax[1].plot(logs[\"Step\"], logs[\"Val_d_loss\"], label=\"Val_d_loss\")\n\n# Set judul untuk grafik loss generator\nax[0].set_title(\"Generator loss\")\n\n# Set judul untuk grafik loss discriminator\nax[1].set_title(\"Discriminator loss\")\n\n# Menampilkan legend untuk setiap grafik di subplot pertama dan kedua\nax[0].legend()\nax[1].legend()\n\n# Menampilkan plot yang sudah dibuat\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-14T03:02:18.279356Z","iopub.execute_input":"2024-12-14T03:02:18.279649Z","iopub.status.idle":"2024-12-14T03:02:18.719832Z","shell.execute_reply.started":"2024-12-14T03:02:18.279623Z","shell.execute_reply":"2024-12-14T03:02:18.719016Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.imshow(photos[1].permute(1, 2, 0).numpy() * 0.5 + 0.5)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-14T03:02:18.721102Z","iopub.execute_input":"2024-12-14T03:02:18.721771Z","iopub.status.idle":"2024-12-14T03:02:19.041355Z","shell.execute_reply.started":"2024-12-14T03:02:18.721739Z","shell.execute_reply":"2024-12-14T03:02:19.040528Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"fake_img = trainer.generator((photos[1].unsqueeze(0)).to('cuda'))\nfake_img = fake_img[0].cpu().detach()\nplt.imshow(fake_img.permute(1, 2, 0).numpy() * 0.5 + 0.5  )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-14T03:02:19.042656Z","iopub.execute_input":"2024-12-14T03:02:19.043309Z","iopub.status.idle":"2024-12-14T03:02:19.352613Z","shell.execute_reply.started":"2024-12-14T03:02:19.043276Z","shell.execute_reply":"2024-12-14T03:02:19.351822Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!mkdir ../images","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-14T03:02:19.353856Z","iopub.execute_input":"2024-12-14T03:02:19.354195Z","iopub.status.idle":"2024-12-14T03:02:20.432174Z","shell.execute_reply.started":"2024-12-14T03:02:19.354169Z","shell.execute_reply":"2024-12-14T03:02:20.430888Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"with torch.no_grad():\n    for i, (_,img) in enumerate(DataLoader(dataset, batch_size=1, shuffle=False)):\n        prediction = trainer.generator(img.to(device))[0].cpu().permute(1, 2, 0).numpy()* 0.5 + 0.5\n        im = Image.fromarray((prediction * 255).astype(np.uint8))\n        im.save(\"../images/\" + str(i) + \".jpg\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-14T03:02:20.433821Z","iopub.execute_input":"2024-12-14T03:02:20.434208Z","iopub.status.idle":"2024-12-14T03:03:56.564219Z","shell.execute_reply.started":"2024-12-14T03:02:20.434176Z","shell.execute_reply":"2024-12-14T03:03:56.563431Z"}},"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-14T03:03:56.568029Z","iopub.execute_input":"2024-12-14T03:03:56.568278Z","iopub.status.idle":"2024-12-14T03:04:01.581523Z","shell.execute_reply.started":"2024-12-14T03:03:56.568259Z","shell.execute_reply":"2024-12-14T03:04:01.580662Z"}},"outputs":[],"execution_count":null}]}