<jupyter_start><jupyter_text>LSGANGAN 이 훈련시키가 어려운 이유는 대체적으로 손실함수를 최적화 시킬때 발생한다. Jensen-Shannon 발산을 최적화 하는 것이 GAN 의 당면 과제이며, 두 분포함수가 중첩되는 부분이 거의 없을 경우에는 이를 최적화하기에는 어렵다. WGAN 의 경우, 두 분포사이에 중첩되는 영역이 거의 없을 때도 매끄러운 미분 가능함수를 갖도록 EMD 나 Wasserstein 손실을 사용함으로써 이문제를 해결한다.하지만 WGAN 은 생성 이미지의 품질에는 신경쓰지 않는 경향이 있고 이와 같은 부분에서 개선되어야 하는 점이 있다. LSGAN은 최소제곱 손실로서 위의 문제를 해결하는 방법이다. GAN 에서 Sigmoid Activation 과 Cross Entropy 손실함수를 사용했을때 생성된 데이터 품질이 나쁜 이유는 , 이상적으로, 가짜 샘플의 분포는 진짜 샘플의 분포와 가능한 가까워야 한다. 하지만, GAN 에서 가짜 샘플이 이미 결정경계에서 진짜로 분류가 되기 시작했을 경우, 경사가 소실된다.이는 생성기가 생성된 가짜 데이터의 품질을 개선하려고 더 노력할 필요가 없게 만들며, 결정 경계로 부터 멀리 떨어져 있는 가짜 샘플은 더 이상 진짜 샘플 분포에 가까워 지려고 시도하지 않는다.이 때, 최소제곱 손실을 사용한다면, 생성기는 가짜 샘플이 이미 결정 경계의 진짜 영역에 속해 있더라도, 실제 밀도 분포의 추정을 개선하려고 학습한다.  GPU 할당 Colab 이 아닌 환경 (GPU 의 메모리가 부족할 경우)에서는 아래의 코드를 통해 우선적으로 gpu를 할당해준다<jupyter_code>import tensorflow as tf 
physical_devices =tf.config.experimental.list_physical_devices('GPU')
tf.config.experimental.set_memory_growth(physical_devices[0],True)
from tensorflow.keras.layers import Activation, Dense, Input
from tensorflow.keras.layers import Conv2D, Flatten
from tensorflow.keras.layers import Reshape, Conv2DTranspose
from tensorflow.keras.layers import LeakyReLU
from tensorflow.keras.layers import BatchNormalization
from tensorflow.keras.layers import concatenate
from tensorflow.keras.models import Model
from tensorflow.keras.optimizers import RMSprop

from tensorflow.keras.datasets import mnist
from tensorflow.keras.models import load_model
from tensorflow.keras import backend as K

import numpy as np
import math
import matplotlib.pyplot as plt<jupyter_output><empty_output><jupyter_text>생성기와 판별기 등 함수 구성 생성기와 판별기의 함수는 DCGAN의 구성을 그대로 사용한다.<jupyter_code>def build_generator(inputs,
              image_size,
              activation='sigmoid'):

    image_resize = image_size // 4
    kernel_size = 5
    layer_filters = [128, 64, 32, 1]
    
    x = inputs
    x = Dense(image_resize * image_resize * layer_filters[0])(x)
    x = Reshape((image_resize, image_resize, layer_filters[0]))(x)

    for filters in layer_filters:
        if filters > layer_filters[-2]:
            strides = 2
        else:
            strides = 1
        x = BatchNormalization()(x)
        x = Activation('relu')(x)
        x = Conv2DTranspose(filters=filters,
                            kernel_size=kernel_size,
                            strides=strides,
                            padding='same')(x)

    if activation is not None:
        x = Activation(activation)(x)

    return Model(inputs, x, name='generator')


def build_discriminator(inputs,
                  activation='sigmoid'):

    kernel_size = 5
    layer_filters = [32, 64, 128, 256]

    x = inputs
    for filters in layer_filters:
        if filters == layer_filters[-1]:
            strides = 1
        else:
            strides = 2
        x = LeakyReLU(alpha=0.2)(x)
        x = Conv2D(filters=filters,
                   kernel_size=kernel_size,
                   strides=strides,
                   padding='same')(x)

    x = Flatten()(x)
  
    outputs = Dense(1)(x)
    if activation is not None:
        print(activation)
        outputs = Activation(activation)(outputs)

    return Model(inputs, outputs, name='discriminator')


def plot_images(generator,
                noise_input,
                noise_label=None,
                noise_codes=None,
                show=False,
                step=0,
                model_name="gan"):
  
    os.makedirs(model_name, exist_ok=True)
    filename = os.path.join(model_name, "%05d.png" % step)
    rows = int(math.sqrt(noise_input.shape[0]))
    if noise_label is not None:
        noise_input = [noise_input, noise_label]
        if noise_codes is not None:
            noise_input += noise_codes

    images = generator.predict(noise_input)
    plt.figure(figsize=(2.2, 2.2))
    num_images = images.shape[0]
    image_size = images.shape[1]
    for i in range(num_images):
        plt.subplot(rows, rows, i + 1)
        image = np.reshape(images[i], [image_size, image_size])
        plt.imshow(image, cmap='gray')
        plt.axis('off')
    plt.savefig(filename)
    if show:
        plt.show()
    else:
        plt.close('all')


def test_generator(generator):
    noise_input = np.random.uniform(-1.0, 1.0, size=[16, 100])
    plot_images(generator,
                noise_input=noise_input,
                show=True,
                model_name="test_outputs")<jupyter_output><empty_output><jupyter_text>LSGAN 구현 DCGAN 과 거의 동일한 구조이며, 아래와 같이 손실함수를 모두 mse 로 대체하고, Activation 층을 제거 해주면 된다. LSGAN 네트워크 모델은 선형출력 혹은 화성화 함수가 없다는 점에서 다른 성능을 나타낸다.<jupyter_code>#MNIST 데이터 세트 로딩
(x_train,_),(_,_) = mnist.load_data()

# 데이터 형상 변환 및 정규화
image_size = x_train.shape[1]
x_train = np.reshape(x_train, [-1, image_size, image_size, 1])
x_train = x_train.astype('float32')/255
  
model_name = "lsgan_mnist"

#네트워크 매개 변수 지정 
latent_size = 100
batch_size = 64
lr = 2e-4
decay = 6e-8
train_steps = 40000
input_shape = (image_size, image_size, 1)
#판별기 모델 구성
inputs = Input(shape=input_shape, name = 'discriminator_input')
 
discriminator = build_discriminator(inputs, activation=None)
optimizer = RMSprop(lr=lr, decay=decay)

#LSGAN 은 mse loss 를 사용한다.
discriminator.compile(loss='mse',optimizer=optimizer,metrics=['accuracy'])

discriminator.summary()
#생성기 모델 구성
input_shape = (latent_size,)
inputs = Input(shape=input_shape, name = 'z_input')
generator = build_generator(inputs, image_size)
generator.summary()
#적대적 모델 구성
optimizer = RMSprop(lr=lr*0.5, decay=decay*0.5)

#적대적 네트워크를 훈련하는 동안 판별기의 가중치는 고정
discriminator.trainable = False
adversarial = Model(inputs, discriminator(generator(inputs)),name = model_name)

adversarial.compile(loss='mse', optimizer=optimizer,metrics=['accuracy'])
adversarial.summary()
models = (generator, discriminator, adversarial)
params = (batch_size, latent_size, train_steps, model_name)<jupyter_output><empty_output><jupyter_text>LSGAN 훈련<jupyter_code>def train(models, x_train, params):
  # 함수의 인수로는 앞선 셀에서 리스트로 지정된 models 와 params, 그리고 훈련 이미지인 x_train 이 있다.
  
  """
  판별기와 적대적 네트워크를 훈련한 후, 배치 단위로 판별기와 적대적 네트워크를 교대로 훈련한다. 
  우선 판별기는 제대로 레이블이 붙은 진짜와 가짜 이미지를 가지고 훈련 시킨 후, 
  다음으로 적대적 네트워크를 진짜인척 하는 가짜 이미지를 사용하여 훈련시킨다.
  """

  # GAN 모델 불러오기
  generator, discriminator, adversarial = models

  # 네트워크 매개변수
  batch_size, latent_size, train_steps, model_name = params

  # 500 단계 마다 생성기 이미지가 저장되도록 설정
  save_interval = 500

  #훈련 기간 동안 생성기 출력 이미지가 어떻게 변화하는지 보여주기 위한 노이즈 벡터
  noise_input = np.random.uniform(-1.0, 1.0, size = [16, latent_size])
  
  # 훈련 이미지의 개수
  train_size = x_train.shape[0]

  for i in range(train_steps):
    # 1 배치에 대해 판별기 훈련
    # 데이터 셋에서 임의로 진짜 이미지를 선택한다
    rand_indices = np.random.randint(0, train_size, size = batch_size)
    real_images = x_train[rand_indices]

    #생성기를 사용해 노이즈로 부터 가짜 이미지를 생성한다.
    
    # 노이즈 분포 사용해 노이즈 생성
    noise = np.random.uniform(-1.0, 1.0, size=[batch_size, latent_size])

    # 가짜 이미지 생성
    fake_images = generator.predict(noise)
    
    # 진짜 이미지와 가짜이미지의 훈련데이터의 배치 
    x = np.concatenate((real_images, fake_images))
    # 레이블을 붙임
    y = np.ones([2*batch_size, 1])
    y[batch_size:, :] = 0.0

    #판별기 훈련 및 손실과 정확도 기록
    loss, acc = discriminator.train_on_batch(x, y)
    log = "%d:[discriminator loss = %f, acc: %f]" %(i, loss, acc)

    # 1 배치에 대한 적대적 네트워크 훈련 
    # label = 1.0 인 가짜 이미지로 구성된 배치 

    #판별기의 가중치가 고정되므로 생성기만 훈련된다.
    noise = np.random.uniform(-1.0, 1.0, size=[batch_size, latent_size])
    
    # 가짜 이미지에 진짜 혹은 1.0 으로 레이블
    y = np.ones([batch_size, 1])

    #판별기를 훈련시키는 것과 달리 변수에 가짜 이미지를 저장하지 않는다.
    #가짜 이미지는 분류를 위해 적대적 네트워크의 판별기 입력으로 전달됨

    loss, acc = adversarial.train_on_batch(noise, y)
    log = "%s:[adversarial loss = %f, acc: %f]" %(log, loss, acc)
    print(log)

    if (i+1) % save_interval == 0:
      if (i+1) == train_steps:
        show = True
      else:
        show = False

      plot_images(generator, noise_input=noise_input, show=show, step=(i+1), model_name=model_name)
    
  genrator.save(model_name+".h5")
train(models, x_train, params)<jupyter_output><empty_output>
StarcoderdataJupyterStructuredCleanDedup