GAN 딥러닝 코드 - ChatGPT code

@esthrelar·2023년 8월 22일
1

GAN(Generative Adversarial Network)을 활용하여 이미지 증폭을 시키는 모델을 구현하려면 다음과 같이 코드를 작성할 수 있습니다. 아래 코드는 간단한 GAN을 사용하여 MNIST 숫자 데이터를 생성하는 예시입니다. 이 코드를 참고하여 이미지 데이터를 증폭시키는 GAN 모델을 구현하실 수 있습니다.

import torch
import torch.nn as nn
import torchvision
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt

# Device configuration
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

# Hyperparameters
latent_size = 64
hidden_size = 256
image_size = 784
num_epochs = 100
batch_size = 100
learning_rate = 0.0002

# MNIST dataset
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize(mean=(0.5,), std=(0.5,))
])

mnist_dataset = torchvision.datasets.MNIST(root='./data', train=True, transform=transform, download=True)
data_loader = DataLoader(dataset=mnist_dataset, batch_size=batch_size, shuffle=True)

# Generator model
class Generator(nn.Module):
    def __init__(self):
        super(Generator, self).__init__()
        self.model = nn.Sequential(
            nn.Linear(latent_size, hidden_size),
            nn.ReLU(),
            nn.Linear(hidden_size, image_size),
            nn.Tanh()
        )

    def forward(self, x):
        return self.model(x)

# Discriminator model
class Discriminator(nn.Module):
    def __init__(self):
        super(Discriminator, self).__init__()
        self.model = nn.Sequential(
            nn.Linear(image_size, hidden_size),
            nn.ReLU(),
            nn.Linear(hidden_size, 1),
            nn.Sigmoid()
        )

    def forward(self, x):
        return self.model(x)

# Initialize models and optimizers
generator = Generator().to(device)
discriminator = Discriminator().to(device)
criterion = nn.BCELoss()
optimizer_g = torch.optim.Adam(generator.parameters(), lr=learning_rate)
optimizer_d = torch.optim.Adam(discriminator.parameters(), lr=learning_rate)

# Training
total_step = len(data_loader)
for epoch in range(num_epochs):
    for i, (images, _) in enumerate(data_loader):
        images = images.reshape(-1, image_size).to(device)
        real_labels = torch.ones(batch_size, 1).to(device)
        fake_labels = torch.zeros(batch_size, 1).to(device)

        # Train discriminator
        outputs = discriminator(images)
        d_loss_real = criterion(outputs, real_labels)
        real_score = outputs

        z = torch.randn(batch_size, latent_size).to(device)
        fake_images = generator(z)
        outputs = discriminator(fake_images.detach())
        d_loss_fake = criterion(outputs, fake_labels)
        fake_score = outputs

        d_loss = d_loss_real + d_loss_fake
        optimizer_d.zero_grad()
        d_loss.backward()
        optimizer_d.step()

        # Train generator
        z = torch.randn(batch_size, latent_size).to(device)
        fake_images = generator(z)
        outputs = discriminator(fake_images)
        g_loss = criterion(outputs, real_labels)

        optimizer_g.zero_grad()
        g_loss.backward()
        optimizer_g.step()

        if (i+1) % 200 == 0:
            print(f'Epoch [{epoch+1}/{num_epochs}], Step [{i+1}/{total_step}], '
                  f'D_loss: {d_loss.item():.4f}, G_loss: {g_loss.item():.4f}, '
                  f'D(x): {real_score.mean().item():.2f}, D(G(z)): {fake_score.mean().item():.2f}')

    # Generate sample images
    with torch.no_grad():
        z = torch.randn(batch_size, latent_size).to(device)
        fake_images = generator(z)
        fake_images = fake_images.reshape(-1, 1, 28, 28)
        torchvision.utils.save_image(fake_images, f'gan_samples_{epoch+1}.png', normalize=True)

# Save models
torch.save(generator.state_dict(), 'generator.ckpt')
torch.save(discriminator.state_dict(), 'discriminator.ckpt')

Hyperparameters

# Hyperparameters
latent_size = 64 # 잠재 벡터의 크기
hidden_size = 256 # 은닉층의 크기
image_size = 784 # 이미지 크기
num_epochs = 100 # 훈련 에포크 수
batch_size = 100 # 배치 크기
learning_rate = 0.0002 # 학습률

답변 )
하이퍼파라미터(Hyperparameter)는 모델을 훈련할 때 사용되는 설정 값으로, 모델의 구조나 학습 프로세스를 제어하고 조정하는 데 사용됩니다. 하이퍼파라미터는 사람이 직접 지정해야 하며, 모델 자체의 파라미터(가중치 및 편향)와는 달리 자동으로 학습되지 않습니다. 이러한 하이퍼파라미터의 조정은 모델의 성능과 수렴 속도에 큰 영향을 미칩니다.

아래에서 코드에서 사용된 각 하이퍼파라미터에 대해 설명해보겠습니다:

  1. latent_size (잠재 벡터의 크기):

    • GAN의 생성자(Generator)에서 사용되는 잠재 벡터의 차원 수입니다.
    • 이 벡터는 무작위 노이즈로부터 생성되며, 생성자가 이 벡터를 통해 이미지를 생성합니다.
    • 크기가 크면 더 다양한 이미지를 생성할 수 있으나, 과도하게 크면 학습이 어려울 수 있습니다.
  2. hidden_size (은닉층의 크기):

    • 생성자와 판별자의 은닉층에 사용되는 뉴런의 수입니다.
    • 네트워크의 복잡성과 표현 능력을 나타내며, 적절한 크기 선택이 중요합니다.
    • 크기가 작으면 모델의 표현 능력이 제한되지만, 크기가 크면 과적합 등의 문제가 발생할 수 있습니다.
  3. image_size (이미지 크기):

    • 입력 이미지의 크기입니다. 이 코드의 경우 MNIST 데이터셋을 사용하므로 28x28 크기의 이미지입니다.
    • 네트워크 구조와 입력 데이터 크기에 따라 조정됩니다.
  4. num_epochs (훈련 에포크 수):

    • 전체 데이터셋을 몇 번 반복해서 모델을 훈련할지를 결정하는 값입니다.
    • 훈련 에포크 수가 크면 더 많은 반복 학습이 가능하지만, 너무 크면 학습 시간이 길어지고 과적합 가능성이 증가합니다.
  5. batch_size (배치 크기):

    • 한 번의 훈련 스텝에서 사용되는 데이터의 개수입니다.
    • 큰 배치 크기는 메모리 사용량이 증가하고 학습이 불안정해질 수 있지만, 작은 배치 크기는 속도가 느릴 수 있습니다.
  6. learning_rate (학습률):

    • 경사 하강법에서 한 번의 스텝에서 얼마나 많은 업데이트를 수행할지를 결정하는 값입니다.
    • 너무 작으면 학습이 느리게 진행될 수 있고, 너무 크면 발산할 수 있습니다.
    • 적절한 학습률 선택은 학습의 수렴을 도와줍니다.

이러한 하이퍼파라미터들은 모델의 성능과 학습의 안정성에 영향을 미치기 때문에 조심스럽게 조정하는 것이 중요합니다. 실험을 통해 최적의 값을 찾는 것이 일반적인 접근 방법입니다.

MNIST Dataset

MNIST는 손글씨 숫자 이미지 데이터셋으로, 0부터 9까지의 숫자가 각각의 클래스로 지정되어 있는 데이터셋입니다. torchvision 라이브러리는 여러 데이터셋과 트랜스폼(transform) 기능을 제공하여 데이터를 효율적으로 불러오고 전처리할 수 있습니다.

아래는 주어진 코드에서 사용된 transform에 대한 설명입니다:

transform = transforms.Compose([
    transforms.ToTensor(),  # 이미지를 PyTorch 텐서로 변환
    transforms.Normalize(mean=(0.5,), std=(0.5,))  # 이미지의 픽셀 값을 정규화
])
  1. transforms.ToTensor():

    • 이 트랜스폼은 이미지를 PyTorch 텐서로 변환합니다.
    • 기존에는 0에서 255 사이의 픽셀 값이었던 이미지가 0에서 1 사이의 값으로 정규화된 텐서로 변환됩니다.
    • 딥러닝 모델에 입력하기 위해 데이터를 텐서 형식으로 변환하는 과정입니다.
  2. transforms.Normalize(mean=(0.5,), std=(0.5,)):

    • 이 트랜스폼은 이미지의 픽셀 값을 정규화하는 역할을 합니다.
    • 정규화는 평균과 표준 편차를 사용하여 데이터를 일정 범위로 조정하는 작업입니다.
    • 일반적으로 데이터를 정규화하면 모델의 학습이 안정적으로 이루어지며 수렴 속도도 개선될 수 있습니다.
    • 이 트랜스폼에서 사용된 평균과 표준 편차 값은 -1에서 1 사이로 데이터를 조정하고자 하기 때문에 0.5로 설정되었습니다.
    • 평균(mean)을 0.5로 설정하여 데이터를 중심에서 조정하고, 표준 편차(std)를 0.5로 설정하여 데이터의 범위를 조정합니다.
    • 따라서 이 설정으로 인해 이미지의 픽셀 값들은 -1에서 1 사이의 범위로 조정됩니다.

이렇게 transform을 정의하여 데이터셋을 전처리하면, 데이터를 로드할 때마다 이미지가 텐서로 변환되고 정규화되어 모델 학습에 적합한 형태로 준비됩니다.


torchvision.datasets.MNISTDataLoader를 사용하여 MNIST 데이터셋을 로드하고 데이터를 미니배치로 나누는 과정.

mnist_dataset = torchvision.datasets.MNIST(root='./data', train=True, transform=transform, download=True)
data_loader = DataLoader(dataset=mnist_dataset, batch_size=batch_size, shuffle=True)
  1. torchvision.datasets.MNIST:

    • torchvision.datasets 모듈은 다양한 이미지 데이터셋을 제공합니다.
    • MNIST 클래스는 MNIST 데이터셋을 로드하기 위한 클래스입니다.
    • root: 데이터셋이 저장될 디렉토리 경로를 지정합니다. 여기에서는 ./data 디렉토리에 저장됩니다.
    • train: 데이터셋의 훈련용 데이터를 사용할지 여부를 지정합니다.
    • transform: 이미지 데이터에 적용할 전처리 트랜스폼을 지정합니다. 위에서 설명한 대로 ToTensorNormalize 트랜스폼이 적용됩니다.
    • download: 데이터셋이 로컬에 없는 경우, 인터넷에서 데이터셋을 다운로드할지 여부를 지정합니다.
  2. DataLoader:

    • DataLoader는 데이터셋을 미니배치로 나누어주는 유틸리티 클래스입니다.
    • dataset: 데이터셋 객체를 지정합니다. 위에서 로드한 mnist_dataset을 사용합니다.
    • batch_size: 한 번에 로드되는 미니배치의 크기를 지정합니다.
    • shuffle: 데이터를 섞을지 여부를 지정합니다. 훈련 과정에서 데이터를 무작위로 섞는 것이 도움이 됩니다.

이렇게 작성된 코드는 mnist_datasetdata_loader로 미니배치 단위로 분할하여 사용할 수 있게 준비합니다. data_loader는 반복 가능한 객체이며, 반복문을 사용하여 각 스텝마다 미니배치를 얻을 수 있습니다. 데이터셋을 미니배치로 나누어 효율적으로 모델을 학습시키거나 평가하는 데 사용됩니다.

Generator model

# Generator model
# 생성자 신경망은 Generator라는 클래스로 정의되며, nn.Module을 상속받습니다.
# 이 생성자는 두 개의 선형 레이어, ReLU 활성화 함수, 그리고 최종적으로 Tanh 활성화 함수로 구성되어 있습니다. 
# Tanh 함수를 사용하여 픽셀 값이 -1에서 1 사이에 있도록 합니다.
class Generator(nn.Module):
    def __init__(self):
        super(Generator, self).__init__()
        self.model = nn.Sequential(
            nn.Linear(latent_size, hidden_size),
            nn.ReLU(),
            nn.Linear(hidden_size, image_size),
            nn.Tanh()
        )

    def forward(self, x):
        return self.model(x)
  1. 클래스 정의 (class Generator(nn.Module):):

    • 생성자 신경망은 nn.Module 클래스를 상속받아 정의됩니다.
    • nn.Module을 상속받는 것으로 PyTorch에서 모델을 정의하고 학습하기 위한 기본적인 틀을 생성합니다.
  2. __init__ 메서드:

    • 생성자 메서드로, 클래스 객체를 초기화할 때 호출됩니다.
    • super(Generator, self).__init__()은 부모 클래스의 초기화 메서드를 호출하는 코드로, 상속받은 클래스의 초기화를 수행합니다.
  3. 신경망 구성 (self.model = nn.Sequential(...)):

    • 생성자 내부에는 신경망 모델을 정의하는 nn.Sequential 객체가 있습니다.
    • nn.Sequential은 여러 개의 레이어를 순차적으로 쌓아서 신경망을 구성할 수 있게 해주는 컨테이너입니다.
  4. nn.Linear(latent_size, hidden_size):

    • 첫 번째 레이어는 입력인 잠재 벡터(latent_size)와 은닉층 크기(hidden_size)를 연결하는 선형 레이어입니다.
    • 선형 변환을 통해 잠재 벡터를 은닉층의 특성 공간으로 매핑합니다.
  5. nn.ReLU():

    • 활성화 함수인 ReLU(Rectified Linear Unit)을 적용합니다.
    • 음수 값은 0으로, 양수 값은 그대로 유지합니다.
  6. nn.Linear(hidden_size, image_size):

    • 두 번째 레이어는 은닉층 출력을 이미지 크기(image_size)로 변환하는 선형 레이어입니다.
    • 은닉층의 특성을 이미지의 픽셀 값으로 매핑합니다.
  7. nn.Tanh():

    • Tanh 활성화 함수를 적용합니다.
    • Tanh 함수는 출력을 -1에서 1 사이의 범위로 조정합니다.
    • 생성자의 출력은 이미지의 픽셀 값으로서, -1에서 1 사이의 값으로 조정되어 생성됩니다.
  8. forward 메서드:

    • 정의한 모델을 사용하여 입력 데이터를 처리하고 결과를 반환하는 메서드입니다.
    • 입력으로 받은 잠재 벡터 xself.model에 전달하여 생성된 이미지를 반환합니다.

이렇게 생성자 신경망은 잠재 벡터를 입력으로 받아 이미지를 생성하는 역할을 수행합니다. GAN의 생성자는 무작위 잠재 벡터를 입력으로 받아 현실적인 이미지처럼 보이는 가짜 이미지를 생성하는 능력을 가집니다.

생성자 신경망의 구조를 그림으로 표현하면 아래와 같습니다. 이 구조는 코드에서 정의한 대로, 두 개의 선형 레이어와 활성화 함수로 구성되어 있습니다.

Generator (
  (model): Sequential (
    (0): Linear(in_features=64, out_features=256, bias=True)
    (1): ReLU()
    (2): Linear(in_features=256, out_features=784, bias=True)
    (3): Tanh()
  )
)

위의 그림에서 in_features는 입력 특성의 수를 나타내며, out_features는 출력 특성의 수를 나타냅니다. 생성자의 구조는 다음과 같이 설명할 수 있습니다:

  1. 첫 번째 선형 레이어 (Linear(in_features=64, out_features=256, bias=True)):

    • 입력 특성의 수(in_features)는 잠재 벡터의 크기인 64입니다.
    • 출력 특성의 수(out_features)는 은닉층의 크기인 256입니다.
    • 이 레이어는 입력 잠재 벡터를 은닉층의 특성으로 매핑하는 선형 변환을 수행합니다.
  2. ReLU 활성화 함수:

    • ReLU 활성화 함수는 음수 값을 제거하고 양수 값을 그대로 통과시킵니다.
    • 이 활성화 함수는 비선형성을 도입하여 네트워크가 더 복잡한 함수를 학습할 수 있도록 돕습니다.
  3. 두 번째 선형 레이어 (Linear(in_features=256, out_features=784, bias=True)):

    • 입력 특성의 수(in_features)는 은닉층의 크기인 256입니다.
    • 출력 특성의 수(out_features)는 이미지의 크기인 784입니다.
    • 이 레이어는 은닉층의 특성을 이미지 픽셀 값으로 매핑하는 선형 변환을 수행합니다.
  4. Tanh 활성화 함수:

    • Tanh 활성화 함수는 출력 값을 -1에서 1 사이로 조정합니다.
    • 생성자의 출력은 이미지의 픽셀 값으로서, -1에서 1 사이의 값으로 조정되어 생성됩니다.

이 구조를 이해하면, 생성자가 어떻게 잠재 벡터를 입력으로 받아 실제 이미지와 유사한 이미지를 생성하는지 이해할 수 있을 것입니다.

Discriminator model

이 코드는 생성자와 마찬가지로 nn.Module을 상속받아 정의되며, 두 개의 선형 레이어와 활성화 함수로 구성되어 있습니다.

class Discriminator(nn.Module):
    def __init__(self):
        super(Discriminator, self).__init__()
        self.model = nn.Sequential(
            nn.Linear(image_size, hidden_size),
            nn.ReLU(),
            nn.Linear(hidden_size, 1),
            nn.Sigmoid()
        )

    def forward(self, x):
        return self.model(x)
  1. 클래스 정의 (class Discriminator(nn.Module):):

    • 식별자 신경망은 nn.Module 클래스를 상속받아 정의됩니다.
    • nn.Module을 상속받는 것으로 PyTorch에서 모델을 정의하고 학습하기 위한 기본적인 틀을 생성합니다.
  2. __init__ 메서드:

    • 생성자 메서드로, 클래스 객체를 초기화할 때 호출됩니다.
    • super(Discriminator, self).__init__()은 부모 클래스의 초기화 메서드를 호출하는 코드로, 상속받은 클래스의 초기화를 수행합니다.
  3. 신경망 구성 (self.model = nn.Sequential(...)):

    • 식별자 내부에는 신경망 모델을 정의하는 nn.Sequential 객체가 있습니다.
    • nn.Sequential은 여러 개의 레이어를 순차적으로 쌓아서 신경망을 구성할 수 있게 해주는 컨테이너입니다.
  4. nn.Linear(image_size, hidden_size):

    • 첫 번째 레이어는 입력 특성의 수(image_size)와 은닉층 크기(hidden_size)를 연결하는 선형 레이어입니다.
    • 선형 변환을 통해 이미지의 픽셀 값을 은닉층의 특성으로 매핑합니다.
  5. nn.ReLU():

    • 활성화 함수인 ReLU(Rectified Linear Unit)을 적용합니다.
    • 음수 값은 0으로, 양수 값은 그대로 유지합니다.
  6. nn.Linear(hidden_size, 1):

    • 두 번째 레이어는 은닉층 출력을 하나의 출력 뉴런으로 매핑하는 선형 레이어입니다.
    • 이 레이어의 출력은 입력된 데이터가 진짜 데이터일 확률을 나타내는 값입니다.
  7. nn.Sigmoid():

    • Sigmoid 활성화 함수를 적용합니다.
    • 이 함수는 출력 값을 0에서 1 사이의 확률 값으로 조정합니다.
  8. forward 메서드:

    • 정의한 모델을 사용하여 입력 데이터를 처리하고 결과를 반환하는 메서드입니다.
    • 입력으로 받은 데이터 xself.model에 전달하여 식별자의 출력을 반환합니다.

이렇게 식별자 신경망은 입력된 데이터가 진짜 데이터일 확률을 추정하는 역할을 수행합니다. GAN에서 식별자는 생성된 이미지와 실제 이미지를 구분하는 역할을 담당하며, 이를 통해 생성자를 더 나은 이미지를 생성하도록 학습시키는 경쟁 관계가 형성됩니다.

생성자 신경망과 마찬가지로 식별자 신경망의 구조를 그림으로 표현하면 아래와 같습니다. 이 구조는 코드에서 정의한 대로, 두 개의 선형 레이어와 활성화 함수로 구성되어 있습니다.

Discriminator (
  (model): Sequential (
    (0): Linear(in_features=784, out_features=256, bias=True)
    (1): ReLU()
    (2): Linear(in_features=256, out_features=1, bias=True)
    (3): Sigmoid()
  )
)

위의 그림에서 in_features는 입력 특성의 수를 나타내며, out_features는 출력 특성의 수를 나타냅니다. 식별자의 구조는 다음과 같이 설명할 수 있습니다:

  1. 첫 번째 선형 레이어 (Linear(in_features=784, out_features=256, bias=True)):

    • 입력 특성의 수(in_features)는 이미지의 크기인 784입니다.
    • 출력 특성의 수(out_features)는 은닉층의 크기인 256입니다.
    • 이 레이어는 입력 이미지의 픽셀 값을 은닉층의 특성으로 매핑하는 선형 변환을 수행합니다.
  2. ReLU 활성화 함수:

    • ReLU 활성화 함수는 음수 값을 제거하고 양수 값을 그대로 통과시킵니다.
    • 이 활성화 함수는 비선형성을 도입하여 네트워크가 더 복잡한 함수를 학습할 수 있도록 돕습니다.
  3. 두 번째 선형 레이어 (Linear(in_features=256, out_features=1, bias=True)):

    • 입력 특성의 수(in_features)는 은닉층의 크기인 256입니다.
    • 출력 특성의 수(out_features)는 1로서, 하나의 출력 뉴런을 가진 선형 레이어입니다.
    • 이 레이어의 출력은 입력된 데이터가 진짜 데이터일 확률을 나타내는 값입니다.
  4. Sigmoid 활성화 함수:

    • Sigmoid 활성화 함수를 적용합니다.
    • 이 함수는 출력 값을 0에서 1 사이의 확률 값으로 조정합니다.
    • 출력 값은 입력된 데이터가 진짜 데이터일 확률을 나타내는 점수로 해석됩니다.

이 구조를 이해하면, 식별자가 어떻게 입력된 이미지를 진짜 데이터와 가짜 데이터로 구분하려고 하는지 이해할 수 있을 것입니다. GAN의 식별자는 이진 분류 문제로써, 입력 데이터가 진짜 데이터인지 가짜 데이터인지를 확률로 예측하는 역할을 합니다.

Initialize models and optimizers

# Initialize models and optimizers
# 생성자와 식별자의 인스턴스를 생성하고, 선택한 장치(CPU 또는 GPU)로 이동시킵니다. 
# 손실 함수(nn.BCELoss)와 옵티마이저(Adam)를 생성자와 식별자 각각에 대해 정의합니다.
generator = Generator().to(device)
discriminator = Discriminator().to(device)
criterion = nn.BCELoss()
optimizer_g = torch.optim.Adam(generator.parameters(), lr=learning_rate)
optimizer_d = torch.optim.Adam(discriminator.parameters(), lr=learning_rate)
  1. 생성자와 식별자 인스턴스 생성:

    • Generator()Discriminator() 클래스를 호출하여 생성자와 식별자의 인스턴스를 생성합니다.
    • 이렇게 생성한 인스턴스는 실제 모델을 나타냅니다.
  2. 장치 이동 (to(device)):

    • 생성자와 식별자의 모델 파라미터와 연산은 지정한 장치(CPU 또는 GPU)에서 이루어져야 합니다.
    • .to(device)를 사용하여 모델과 관련된 모든 요소를 지정한 장치로 이동시킵니다.
  3. 손실 함수 (nn.BCELoss()):

    • 이진 분류 문제를 위한 Binary Cross Entropy Loss(BCE Loss) 함수를 생성합니다.
    • BCE Loss는 생성자와 식별자 간의 학습에서 사용됩니다.
    • 생성자는 실제 데이터에 가까운 가짜 데이터를 생성하도록 학습하고, 식별자는 진짜 데이터와 가짜 데이터를 구분하도록 학습합니다.
  4. 옵티마이저 설정 (torch.optim.Adam):

    • 생성자와 식별자 각각에 대해 Adam 옵티마이저를 설정합니다.
    • 옵티마이저는 모델 파라미터를 업데이트하기 위한 최적화 알고리즘입니다.
    • generator.parameters()discriminator.parameters()는 각 모델의 학습 가능한 파라미터들을 가져옵니다.
    • lr=learning_rate는 학습률(learning rate)로, 옵티마이저가 모델 파라미터를 업데이트할 때 사용되는 스텝 크기를 결정합니다.

이렇게 설정된 생성자와 식별자의 인스턴스, 손실 함수, 그리고 옵티마이저는 GAN 학습 과정에서 사용되며, 생성자와 식별자 모두가 경쟁 관계를 통해 서로를 학습하게 됩니다.

Training

전체 코드 :

# Training
# 지정된 에포크 수만큼 훈련 루프가 실행됩니다. 각 에포크마다 루프는 데이터셋 배치를 반복하게 됩니다.
total_step = len(data_loader)
for epoch in range(num_epochs):
    for i, (images, _) in enumerate(data_loader):
        images = images.reshape(-1, image_size).to(device)
        real_labels = torch.ones(batch_size, 1).to(device)
        fake_labels = torch.zeros(batch_size, 1).to(device)

        # Train discriminator
        # 식별자는 실제 이미지와 생성된 이미지를 구별하도록 훈련됩니다. 
        # 실제 이미지는 식별자를 통과하고 실제 레이블(1)에 대한 손실이 계산됩니다. 
        # 생성자에 의해 생성된 가짜 이미지는 생성자를 통과한 후 그래디언트를 분리하여 가짜 레이블(0)에 대한 손실이 계산됩니다. 
        # 전체 식별자 손실은 이 두 손실의 합입니다.
        outputs = discriminator(images)
        d_loss_real = criterion(outputs, real_labels)
        real_score = outputs

        z = torch.randn(batch_size, latent_size).to(device)
        fake_images = generator(z)
        outputs = discriminator(fake_images.detach())
        d_loss_fake = criterion(outputs, fake_labels)
        fake_score = outputs

        d_loss = d_loss_real + d_loss_fake
        optimizer_d.zero_grad()
        d_loss.backward()
        optimizer_d.step()

        # Train generator
        # 생성자는 식별자를 속이는 이미지를 생성하기 위해 훈련됩니다. 
        # 무작위 잠재 벡터가 생성되고 생성자의 입력으로 사용됩니다. 
        # 생성된 이미지는 식별자를 통과하며, 생성자 손실은 실제 레이블(1)에 대한 손실로 계산됩니다.
        z = torch.randn(batch_size, latent_size).to(device)
        fake_images = generator(z)
        outputs = discriminator(fake_images)
        g_loss = criterion(outputs, real_labels)

        optimizer_g.zero_grad()
        g_loss.backward()
        optimizer_g.step()
        # 그래디언트가 계산되고 그것들을 사용하여 식별자와 생성자 매개변수를 업데이트합니다. 이를 위해 각각의 옵티마이저를 사용합니다.

        # 진행 상황 출력: 코드는 훈련 진행 상황을 지정된 간격마다 출력합니다.
        if (i+1) % 200 == 0:
            print(f'Epoch [{epoch+1}/{num_epochs}], Step [{i+1}/{total_step}], '
                  f'D_loss: {d_loss.item():.4f}, G_loss: {g_loss.item():.4f}, '
                  f'D(x): {real_score.mean().item():.2f}, D(G(z)): {fake_score.mean().item():.2f}')

    # Generate sample images
    # 각 에포크 후에 생성자는 일괄적인 생성된 이미지를 만듭니다. 이 이미지는 모양을 조정하고 PNG 이미지로 저장됩니다.
    with torch.no_grad():
        z = torch.randn(batch_size, latent_size).to(device)
        fake_images = generator(z)
        fake_images = fake_images.reshape(-1, 1, 28, 28)
        torchvision.utils.save_image(fake_images, f'gan_samples_{epoch+1}.png', normalize=True)

1) 첫 부분:

total_step = len(data_loader)
for epoch in range(num_epochs):
    for i, (images, _) in enumerate(data_loader):
        images = images.reshape(-1, image_size).to(device)
        real_labels = torch.ones(batch_size, 1).to(device)
        fake_labels = torch.zeros(batch_size, 1).to(device)
  1. total_step 설정:

    • total_step은 전체 데이터셋을 몇 번의 배치로 나누어 훈련할 것인지를 나타냅니다.
    • len(data_loader)를 사용하여 데이터로더에서 제공되는 총 배치 개수를 계산하여 설정합니다.
  2. 에포크 루프 (for epoch in range(num_epochs):):

    • 지정된 에포크 수(num_epochs)만큼 에포크 루프가 실행됩니다.
    • 각 에포크는 전체 데이터셋을 한 번씩 훑는 과정입니다.
  3. 배치 루프 (for i, (images, _) in enumerate(data_loader):):

    • data_loader에서 데이터 배치를 순회하며 반복합니다.
    • enumerate(data_loader)는 데이터로더에서 배치와 그에 해당하는 인덱스를 함께 제공합니다.
  4. 이미지 변환과 장치 이동:

    • 각 배치의 이미지 데이터를 가져와서 -1, image_size 모양으로 변환합니다.
    • -1 차원은 이미지 개수를 유지하고, image_size 차원은 이미지 데이터의 차원을 펼쳐주는 역할을 합니다.
    • .to(device)를 사용하여 이미지 데이터를 지정한 장치(CPU 또는 GPU)로 이동시킵니다.
  5. 레이블 생성과 장치 이동:

    • 진짜 이미지에 대한 실제 레이블과 가짜 이미지에 대한 가짜 레이블을 생성하고 장치로 이동시킵니다.
    • torch.ones(batch_size, 1)batch_size 개수의 1로 이루어진 실제 레이블을 생성합니다.
    • torch.zeros(batch_size, 1)batch_size 개수의 0으로 이루어진 가짜 레이블을 생성합니다.
    • 이 레이블들은 손실 계산 및 옵티마이저 업데이트에서 사용됩니다.

이렇게 위의 코드 블록은 에포크와 배치 루프를 반복하면서 이미지 데이터를 변환하고 장치로 이동시키며 실제 레이블과 가짜 레이블을 생성하는 과정을 수행합니다. 이는 GAN의 훈련 과정에서 식별자와 생성자를 학습시키기 위해 필요한 준비 작업입니다.

2) Train Discriminator

# Train discriminator
        # 식별자는 실제 이미지와 생성된 이미지를 구별하도록 훈련됩니다. 
        # 실제 이미지는 식별자를 통과하고 실제 레이블(1)에 대한 손실이 계산됩니다. 
        # 생성자에 의해 생성된 가짜 이미지는 생성자를 통과한 후 그래디언트를 분리하여 가짜 레이블(0)에 대한 손실이 계산됩니다. 
        # 전체 식별자 손실은 이 두 손실의 합입니다.
        outputs = discriminator(images)
        d_loss_real = criterion(outputs, real_labels)
        real_score = outputs

        z = torch.randn(batch_size, latent_size).to(device)
        fake_images = generator(z)
        outputs = discriminator(fake_images.detach())
        d_loss_fake = criterion(outputs, fake_labels)
        fake_score = outputs

        d_loss = d_loss_real + d_loss_fake
        optimizer_d.zero_grad()
        d_loss.backward()
        optimizer_d.step()

위 코드 블록은 GAN의 학습 중에서 식별자(discriminator)를 훈련하는 부분을 나타냅니다. 식별자는 진짜 이미지와 가짜 이미지를 구별하도록 학습됩니다. 이때 식별자의 목표는 진짜 이미지에는 높은 확률을, 가짜 이미지에는 낮은 확률을 할당하여 구별하는 것입니다.

  1. outputs = discriminator(images):

    • 실제 이미지 데이터 images를 식별자에 입력하고, 식별자의 출력을 계산합니다.
    • 이 출력은 실제 이미지에 대한 식별자의 예측 확률을 나타냅니다.
  2. d_loss_real = criterion(outputs, real_labels):

    • 실제 이미지에 대한 실제 레이블 real_labels와 식별자의 출력 outputs 사이의 손실을 계산합니다.
    • 이 손실은 실제 이미지가 실제인지를 판단하는데 사용됩니다.
  3. real_score = outputs:

    • 실제 이미지에 대한 식별자의 예측 확률을 real_score에 저장합니다.
  4. z = torch.randn(batch_size, latent_size).to(device):

    • 무작위 잠재 벡터 z를 생성합니다. 이 벡터는 생성자에 입력으로 사용됩니다.
    • latent_size는 잠재 벡터의 차원을 나타냅니다.
  5. fake_images = generator(z):

    • 생성자에 무작위 잠재 벡터 z를 입력으로 주어 가짜 이미지 fake_images를 생성합니다.
  6. outputs = discriminator(fake_images.detach()):

    • 생성된 가짜 이미지 fake_images를 식별자에 입력하고, 식별자의 출력을 계산합니다.
    • .detach()를 사용하여 생성자의 그래디언트를 분리합니다. 이는 생성자의 업데이트를 방지하고, 식별자만을 업데이트하는 데 사용됩니다.
  7. d_loss_fake = criterion(outputs, fake_labels):

    • 가짜 이미지에 대한 가짜 레이블 fake_labels와 식별자의 출력 outputs 사이의 손실을 계산합니다.
    • 이 손실은 가짜 이미지가 가짜인지를 판단하는데 사용됩니다.
  8. fake_score = outputs:

    • 가짜 이미지에 대한 식별자의 예측 확률을 fake_score에 저장합니다.
  9. d_loss = d_loss_real + d_loss_fake:

    • 식별자의 전체 손실은 실제 이미지 손실과 가짜 이미지 손실의 합입니다.
  10. optimizer_d.zero_grad(), d_loss.backward(), optimizer_d.step():

  • 식별자의 그래디언트를 초기화하고, 전체 손실 d_loss에 대한 역전파를 수행합니다.
  • 그 다음 옵티마이저를 사용하여 식별자의 매개변수를 업데이트합니다.

이렇게 실제 이미지와 가짜 이미지를 사용하여 식별자를 훈련하게 되면, 식별자는 실제 이미지와 가짜 이미지를 잘 구별할 수 있는 능력을 향상시키게 됩니다.

3) Train Generator

# Train generator
        # 생성자는 식별자를 속이는 이미지를 생성하기 위해 훈련됩니다. 
        # 무작위 잠재 벡터가 생성되고 생성자의 입력으로 사용됩니다. 
        # 생성된 이미지는 식별자를 통과하며, 생성자 손실은 실제 레이블(1)에 대한 손실로 계산됩니다.
        z = torch.randn(batch_size, latent_size).to(device)
        fake_images = generator(z)
        outputs = discriminator(fake_images)
        g_loss = criterion(outputs, real_labels)

        optimizer_g.zero_grad()
        g_loss.backward()
        optimizer_g.step()

위 코드 블록은 GAN의 학습 중에서 생성자(generator)를 훈련하는 부분을 나타냅니다. 생성자의 목표는 실제 데이터처럼 보이는 가짜 이미지를 생성하여 식별자를 속이는 것입니다.

  1. z = torch.randn(batch_size, latent_size).to(device):

    • 무작위 잠재 벡터 z를 생성합니다. 이 벡터는 생성자의 입력으로 사용됩니다.
    • latent_size는 잠재 벡터의 차원을 나타냅니다.
  2. fake_images = generator(z):

    • 생성자에 무작위 잠재 벡터 z를 입력으로 주어 가짜 이미지 fake_images를 생성합니다.
  3. outputs = discriminator(fake_images):

    • 생성된 가짜 이미지 fake_images를 식별자에 입력하고, 식별자의 출력을 계산합니다.
    • 이 출력은 생성된 이미지가 실제 이미지처럼 보이는 정도를 나타내는 값으로 해석될 수 있습니다.
  4. g_loss = criterion(outputs, real_labels):

    • 생성자의 목표는 식별자를 속이기 위해 가짜 이미지를 생성하는 것이므로, 생성된 이미지가 실제 이미지처럼 보이도록 식별자의 출력을 "진짜" 레이블과 비교합니다.
    • 이를 위해 식별자의 출력 outputs와 실제 레이블 real_labels 사이의 손실을 계산합니다.
  5. optimizer_g.zero_grad(), g_loss.backward(), optimizer_g.step():

    • 생성자의 그래디언트를 초기화하고, 생성자 손실 g_loss에 대한 역전파를 수행합니다.
    • 그 다음 옵티마이저를 사용하여 생성자의 매개변수를 업데이트합니다.

생성자는 실제 이미지를 보다 잘 모방할 수 있도록 학습됩니다. 이렇게 생성자와 식별자가 서로를 경쟁적으로 학습하면서, 생성자는 더 실제와 유사한 이미지를 생성하고, 식별자는 그 구별 능력을 향상시키는 방향으로 학습이 진행됩니다. 이런 학습의 상호 작용을 통해 GAN은 점차적으로 실제와 유사한 이미지를 생성하도록 생성자를 조정하게 됩니다.

4) 진행 상황 출력

# 진행 상황 출력: 코드는 훈련 진행 상황을 지정된 간격마다 출력합니다.
        if (i+1) % 200 == 0:
            print(f'Epoch [{epoch+1}/{num_epochs}], Step [{i+1}/{total_step}], '
                  f'D_loss: {d_loss.item():.4f}, G_loss: {g_loss.item():.4f}, '
                  f'D(x): {real_score.mean().item():.2f}, D(G(z)): {fake_score.mean().item():.2f}')

위 코드 블록은 학습 중에 생성자와 식별자의 손실 및 예측 확률 등 학습 진행 상황을 주기적으로 출력하는 부분입니다. 이는 학습 과정을 모니터링하고 학습 진행 상황을 파악하는데 도움을 줍니다.

  • (i+1) % 200 == 0:

    • 현재 배치 인덱스 i에 대해 200 배치마다 아래의 출력을 실행합니다.
    • 이는 학습 진행 상황을 주기적으로 모니터링하기 위해 사용됩니다.
  • f'Epoch [{epoch+1}/{num_epochs}], Step [{i+1}/{total_step}], ':

    • 현재 에포크 번호와 전체 에포크 수, 현재 배치 번호와 전체 배치 수를 출력합니다.
  • f'D_loss: {d_loss.item():.4f}, G_loss: {g_loss.item():.4f}, ':

    • 식별자 손실(D_loss)과 생성자 손실(G_loss)을 출력합니다. 손실 값은 소수점 4자리까지 출력됩니다.
  • f'D(x): {real_score.mean().item():.2f}, D(G(z)): {fake_score.mean().item():.2f}':

    • D(x)는 실제 이미지에 대한 식별자의 평균 예측 확률을 출력합니다. 이 값은 실제 이미지가 얼마나 실제로 인식되는지를 나타냅니다.
    • D(G(z))는 생성된 이미지에 대한 식별자의 평균 예측 확률을 출력합니다. 이 값은 생성자가 얼마나 실제 이미지와 유사한 이미지를 생성하는지를 나타냅니다.

이러한 출력을 통해 학습이 진행되는 동안 식별자와 생성자의 손실 및 예측 확률 등의 변화를 실시간으로 모니터링할 수 있습니다. 학습이 진행됨에 따라 손실이 감소하고, 생성된 이미지가 실제와 유사해지는지 확인할 수 있습니다.

5) Generate Sample Images

# Generate sample images
    # 각 에포크 후에 생성자는 일괄적인 생성된 이미지를 만듭니다. 이 이미지는 모양을 조정하고 PNG 이미지로 저장됩니다.
    with torch.no_grad():
        z = torch.randn(batch_size, latent_size).to(device)
        fake_images = generator(z)
        fake_images = fake_images.reshape(-1, 1, 28, 28)
        torchvision.utils.save_image(fake_images, f'gan_samples_{epoch+1}.png', normalize=True)

위 코드 블록은 각 에포크가 끝난 후 생성자를 사용하여 가짜 이미지를 생성하고, 이를 이미지 파일로 저장하는 부분을 나타냅니다.

  • with torch.no_grad()::

    • 이 부분은 그래디언트 계산을 비활성화하여 메모리 사용량을 줄이는 용도로 사용됩니다. 이미지 생성 과정에서 그래디언트는 필요하지 않기 때문에 비활성화합니다.
  • z = torch.randn(batch_size, latent_size).to(device):

    • 무작위 잠재 벡터 z를 생성합니다. 이 벡터는 생성자에 입력으로 주어 가짜 이미지를 생성합니다.
    • latent_size는 잠재 벡터의 차원을 나타냅니다.
  • fake_images = generator(z):

    • 생성자에 무작위 잠재 벡터 z를 입력으로 주어 가짜 이미지 fake_images를 생성합니다.
  • fake_images = fake_images.reshape(-1, 1, 28, 28):

    • 생성된 가짜 이미지 fake_images의 모양을 변경합니다. 일반적으로 생성된 이미지는 일렬로 나열되어 있으므로 이를 28x28 크기의 이미지로 변환합니다.
    • -1은 나머지 차원을 자동으로 계산하라는 의미입니다. 따라서 배치 크기와 이미지 차원에 따라 알맞게 모양이 조정됩니다.
  • torchvision.utils.save_image(fake_images, f'gan_samples_{epoch+1}.png', normalize=True):

    • 변형된 가짜 이미지 fake_images를 PNG 이미지 파일로 저장합니다.
    • f'gan_samples_{epoch+1}.png'은 파일 이름으로, 에포크 번호를 포함하여 저장됩니다.
    • normalize=True는 이미지를 정규화하여 [0, 1] 범위로 변환하여 저장하도록 지정합니다.

이렇게 생성자가 생성한 가짜 이미지를 각 에포크가 끝난 후 저장함으로써, 학습 중에 생성자가 어떤 이미지를 생성하고 있는지를 확인할 수 있습니다. 생성된 이미지들이 에포크마다 어떻게 변화하는지를 시각적으로 관찰하여 모델의 진행 상황을 평가하거나 문제를 해결할 수 있습니다.

Save models

# Save models
# 훈련 후에 생성자와 식별자 모델의 매개변수 상태를 torch.save를 사용하여 체크포인트 파일로 저장합니다.
torch.save(generator.state_dict(), 'generator.ckpt')
torch.save(discriminator.state_dict(), 'discriminator.ckpt')

위 코드 블록은 학습이 완료된 후 생성자와 식별자 모델의 매개변수 상태를 저장하는 부분입니다. 이렇게 저장된 매개변수 상태를 나중에 로드하여 모델을 재사용하거나 이어서 학습하는데 사용할 수 있습니다.

  • torch.save(generator.state_dict(), 'generator.ckpt'):

    • 생성자 모델의 매개변수 상태를 'generator.ckpt'라는 파일로 저장합니다.
    • generator.state_dict()는 생성자 모델의 매개변수들을 딕셔너리 형태로 가져옵니다.
    • 이 딕셔너리는 모델의 각 매개변수 이름과 해당 매개변수의 값으로 구성되어 있습니다.
  • torch.save(discriminator.state_dict(), 'discriminator.ckpt'):

    • 식별자 모델의 매개변수 상태를 'discriminator.ckpt'라는 파일로 저장합니다.
    • discriminator.state_dict()는 식별자 모델의 매개변수들을 딕셔너리 형태로 가져옵니다.

이렇게 매개변수 상태를 파일로 저장하면, 이후에 모델을 재구성하거나 다른 프로젝트에서 모델을 사용할 때 유용합니다. 저장된 체크포인트 파일을 로드하여 모델의 매개변수를 설정하면, 해당 모델을 저장한 시점의 상태로 모델을 복구할 수 있습니다.

profile
moved to tistory. ( linked w/ the home btn below. )

1개의 댓글

comment-user-thumbnail
2023년 8월 23일

딥러닝에 대해 열심히 공부하셨군요👍

답글 달기