1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | import numpy as np import pandas as pd import matplotlib.pyplot as plt import keras from keras.layers import Dense, Dropout, Input from keras.models import Model,Sequential from keras.datasets import mnist from tqdm import tqdm from keras.layers.advanced_activations import LeakyReLU from keras.optimizers import Adam 1. 데이터 로드 def load_data(): (x_train, y_train), (x_test, y_test) = mnist.load_data() x_train = (x_train.astype(np.float32) - 127.5)/127.5 # convert shape of x_train from (60000, 28, 28) to (60000, 784) # 784 columns per row x_train = x_train.reshape(60000, 784) return (x_train, y_train, x_test, y_test) 2. 모델 평가 함수 #학습하기 위해서는 모델을 평가할 수 있어야 한다. 모델의 평가 지표가 좋아지는 방향으로 #매개 변수를 업데이트할 것이기 때문이다. #구분자의 출력값은 이미지가 진짜일 확률이고, 이 확률이 얼마나 정답과 가까운지를 측정하기 위해 #바이너리 크로스 엔트로피(Binary cross entropy) 손실 함수(loss function)를 사용한다. #이 함수는 구분자가 출력한 확률값이 정답에 가까우면 낮아지고 정답에서 멀면 높아진다. #이 손실 함수의 값을 낮추는 것이 모델 학습의 목표가 된다. def adam_optimizer(): return Adam(lr=0.0002, beta_1=0.5) #이제 생성자와 구분자의 매개 변수를 업데이트하는 최적화 함수가 필요하다. #최적화 기법에는 여러 종류가 있지만 여기서는 가장 널리 쓰이는 기법인 아담(Adam)을 사용했다. #아담은 매개 변수마다 업데이트 속도를 최적으로 조절하는 효율적인 최적화 기법이다. 3. GAN의 생성자(Generator) #생성자는 랜덤 벡터 ‘z’를 입력으로 받아 가짜 이미지를 출력하는 함수다. #여기서 ‘z’는 단순하게 균등 분포(Uniform Distribution)나 정규 분포(Normal Distribution)에서 무작위로 추출된 값이다. #생성자는 이렇게 단순한 분포를 사람 얼굴 이미지와 같은 복잡한 분포로 매핑(Mapping)하는 함수라고 볼 수 있다. #생성자 모델에 충분한 수의 매개 변수가 있다면 어떤 복잡한 분포도 근사할 수 있다는 것이 알려져 있다. def create_generator(): generator=Sequential() generator.add(Dense(units=256,input_dim=100)) generator.add(LeakyReLU(0.2)) generator.add(Dense(units=512)) generator.add(LeakyReLU(0.2)) generator.add(Dense(units=1024)) generator.add(LeakyReLU(0.2)) generator.add(Dense(units=784, activation='tanh')) generator.compile(loss='binary_crossentropy', optimizer=adam_optimizer()) return generator #왜 noise vector크기를 100으로 하는건가? ㅜㅜ #‘z’ 벡터가 존재하는 공간을 잠재 공간(Latent Space) #여기서는 잠재 공간의 크기를 임의로 100차원으로 뒀다. #재 공간의 크기에는 제한이 없으나 나타내려고 하는 대상의 정보를 충분히 담을 수 있을 만큼은 커야 한다. #GAN은 우리가 이해할 수는 없는 방식이지만 ‘z’ 벡터의 값을 이미지의 속성에 매핑시키기 때문이다. #(출처 : https://dreamgonfly.github.io/2018/03/17/gan-explained.html) #생성자는 랜덤 벡터 ‘z’를 입력으로 받아 가짜 이미지를 출력하는 함수다. #여기서 ‘z’는 단순하게 균등 분포(Uniform Distribution)나 정규 분포(Normal Distribution)에서 무작위로 추출된 값이다. #생성자는 이렇게 단순한 분포를 사람 얼굴 이미지와 같은 복잡한 분포로 매핑(Mapping)하는 함수라고 볼 수 있다. #생성자 모델에 충분한 수의 매개 변수가 있다면 어떤 복잡한 분포도 근사할 수 있다는 것이 알려져 있다. #이 구현에서는 4개의 선형 레이어(Linear Layer, Fully Connected Layer, Linear Transformation)를 쌓아서 생성자를 만들었다. #선형 레이어는 속해있는 모든 뉴런이 이전 레이어의 모든 뉴런과 연결되는 가장 단순한 구조의 레이어다. #이 모델에서는 100차원의 랜덤 벡터를 받아 이를 256개의 뉴런을 가진 레이어로 보내고, # 다시 레이어의 크기를 512, 1024로 점점 증가시켰다. #마지막에는 출력을 MNIST 이미지의 크기로 맞추기 위해 레이어 크기를 28x28로 줄였다. #각 레이어마다 활성 함수로는 LeakyReLU를 이용했다. #생성자의 마지막 레이어에서는 출력값을 픽셀값의 범위인 -1과 1 사이로 만들어주기 위해 Tanh를 사용했다. def create_discriminator(): discriminator=Sequential() discriminator.add(Dense(units=1024,input_dim=784)) discriminator.add(LeakyReLU(0.2)) discriminator.add(Dropout(0.3)) discriminator.add(Dense(units=512)) discriminator.add(LeakyReLU(0.2)) discriminator.add(Dropout(0.3)) discriminator.add(Dense(units=256)) discriminator.add(LeakyReLU(0.2)) discriminator.add(Dense(units=1, activation='sigmoid')) discriminator.compile(loss='binary_crossentropy', optimizer=adam_optimizer()) return discriminator #구분자는 이미지를 입력으로 받고 그 이미지가 진짜일 확률을 0과 1 사이의 숫자 하나로 출력하는 함수다. #구분자의 구현은 생성자와 마찬가지로 4개의 선형 레이어를 쌓았으며 레이어마다 활성 함수로 LeakyReLU를 넣어줬다. #입력값으로 이미지 크기인 28x28개의 변수를 받은 뒤 레이어의 크기가 #28x28에서 1024로, 512로, 256으로 점차 줄어들다. #마지막에는 확률값을 나타내는 숫자 하나를 출력한다. #레이어마다 들어간 드롭아웃(Dropout)은 학습 시에 무작위로 절반의 뉴런을 사용하지 않도록 한다. #이를 통해 모델이 과적합(Overfitting, 오버피팅)되는 것을 방지할 수 있고, #또한 구분자가 생성자보다 지나치게 빨리 학습되는 것도 막을 수 있다. #구분자의 마지막 레이어에서는 출력값을 0과 1 사이로 만들기 위해 활성 함수로 Sigmoid를 넣었다. def create_gan(discriminator, generator): discriminator.trainable=False gan_input = Input(shape=(100,)) x = generator(gan_input) gan_output= discriminator(x) gan= Model(inputs=gan_input, outputs=gan_output) gan.compile(loss='binary_crossentropy', optimizer='adam') return gan #학습하기 위해서는 모델을 평가할 수 있어야 한다. #모델의 평가 지표가 좋아지는 방향으로 매개 변수를 업데이트할 것이기 때문이다. #구분자의 출력값은 이미지가 진짜일 확률이고, 이 확률이 얼마나 정답과 가까운지를 측정하기 위해 #바이너리 크로스 엔트로피(Binary cross entropy) 손실 함수(loss function)를 사용한다. #이 함수는 구분자가 출력한 확률값이 정답에 가까우면 낮아지고 정답에서 멀면 높아진다. #이 손실 함수의 값을 낮추는 것이 모델 학습의 목표가 된다. def plot_generated_images(epoch, generator, examples=100, dim=(10,10), figsize=(10,10)): noise= np.random.normal(loc=0, scale=1, size=[examples, 100]) generated_images = generator.predict(noise) generated_images = generated_images.reshape(100,28,28) plt.figure(figsize=figsize) for i in range(generated_images.shape[0]): plt.subplot(dim[0], dim[1], i+1) plt.imshow(generated_images[i], interpolation='nearest') plt.axis('off') plt.tight_layout() plt.savefig('gan_generated_image %d.png' %epoch) #! pip install pydot #! pip install pydot>=1.2.4 #! pip install graphviz import pydot from IPython.display import SVG from keras.utils.vis_utils import model_to_dot from keras.utils.vis_utils import plot_model def training(epochs=1, batch_size=128): #Loading the data (X_train, y_train, X_test, y_test) = load_data() batch_count = X_train.shape[0] / batch_size # Creating GAN generator = create_generator() discriminator = create_discriminator() gan = create_gan(discriminator, generator) ## structure of model from keras.utils import plot_model plot_model(generator, show_shapes=True, to_file='generator.png') plot_model(discriminator, show_shapes=True, to_file='discriminator.png') plot_model(gan, show_shapes=True, to_file='gan.png') for e in range(1,epochs+1): print("Epoch %d" %e) for _ in tqdm(range(batch_size)): # Generate random noise as an input to initialize the generator noise= np.random.normal(0,1, [batch_size, 100]) # Generate fake MNIST images from noised input generated_images = generator.predict(noise) # Get a random set of real images image_batch =X_train[np.random.randint(low=0,high=X_train.shape[0],size=batch_size)] # Construct different batches of real and fake data X= np.concatenate([image_batch, generated_images]) # Labels for generated and real data y_dis=np.zeros(2*batch_size) y_dis[:batch_size]=0.9 # Pretrain discriminator on fake and real data before starting the gan. discriminator.trainable=True discriminator.train_on_batch(X, y_dis) # Tricking the noised input of the Generator as real data y_gen = np.ones(batch_size) # During the training of gan, the weights of discriminator should be fixed. # We can enforce that by setting the trainable flag discriminator.trainable=False # Training the GAN by alternating the training of the Discriminator and training the chained GAN model with Discriminator's weights freezed. gan.train_on_batch(noise, y_gen) if e == 1 or e % 20 == 0: plot_generated_images(e, generator) training(400,128) #구분자는 진짜 이미지를 입력하면 1에 가까운 확률값을 출력하고, #가짜 데이터를 입력하면 0에 가까운 확률값을 출력해야 한다. #따라서 구분자의 손실 함수는 두 가지의 합으로 이루어진다. #진짜 이미지를 입력했을 때의 출력값과 1과의 차이, 그리고 가짜 이미지를 입력했을 때의 출력값과 0과의 차이, #두 경우의 합이 구분자의 손실 함수다. 이 손실 함수의 값을 최소화하는 방향으로 구분자의 매개 변수가 업데이트된다. #생성자의 목적은 구분자를 속이는 것이다. 다시 말해 생성자가 만들어낸 가짜 이미지를 구분자에 넣었을 때 출력값이 1에 가깝게 나오도록 해야 한다. #이 값이 1에서 떨어진 정도가 생성자의 손실 함수가 되고, 이를 최소화 시키도록 생성자를 학습시키게 된다. #참조 사이트 #코드 관련 해설 및 이해 (출처: https://dreamgonfly.github.io/2018/03/17/gan-explained.html) #GAN 이해 (출처: https://skymind.ai/kr/wiki/generative-adversarial-network-gan) gan_mnist keras 구현 (출처: https://m.blog.naver.com/PostView.nhn?blogId=qbxlvnf11&logNo=221524732924&categoryNo=87&proxyReferer=https%3A%2F%2Fwww.google.com%2F) | cs |
Designed by sketchbooks.co.kr / sketchbook5 board skin
Sketchbook5, 스케치북5
Sketchbook5, 스케치북5
Sketchbook5, 스케치북5
Sketchbook5, 스케치북5