사용 데이터 : fashion MNIST dataset
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
from tensorflow.keras import layers
tf.__version__
(X_train, y_train), (_, _) = tf.keras.datasets.fashion_mnist.load_data()
# 숫자 mnist 데이터 코드
# (X_train, y_train), (_, _) = tf.keras.datasets.mnist.load_data()
X_train.shape
# (60000, 28, 28) : 이미지 개수, 가로, 세로 픽셀값
28*28
# 784 (한 이미지에 있는 총 픽셀수)
y_train.shape
# (60000,) : 개수
i = np.random.randint(0, 60000)
#print(i)
print(y_train[i]) # 랜덤 인덱스 출력
plt.imshow(X_train[i], cmap = 'gray'); #끝에 ; 붙이면 출력값에 이미지만 깔끔하게뜸
X_train = X_train.reshape(X_train.shape[0], 28, 28, 1).astype('float32')
# 이미지 개수, 가로, 세로, 채널값(흑백:1, 컬러:3)
X_train.shape
# (60000, 28, 28, 1)
X_train[0].min(), X_train[0].max() # 첫번째 이미지 선택
# (0.0, 255.0)
# 정규화(normalize)
X_train = (X_train - 127.5) / 127.5
X_train[0].min(), X_train[0].max()
# (-1.0, 1.0) : DCGAN 사용시에는 -1~1로 정규화해야됨.
# 다른 거 사용할 때는 0~1로 정규화하기도 함.
# 미니배치 경사하강법(mini batch gradient descent algorithm)
buffer_size = 60000 # 이미지 총 개수
batch_size = 256 # 데이터셋 나눌 개수
buffer_size / batch_size # 234.375
# 배치 개수 234개, 배치 한번당 데이터 256개
# 데이터 타입 확인 및 변환
type(X_train)
# numpy.ndarray
# 데이터타입=어래이 -> 신경망에 집어넣기 위해 텐서로 변환
X_train = tf.data.Dataset.from_tensor_slices(X_train).shuffle(buffer_size).batch(batch_size)
type(X_train)
# tensorflow.python.data.ops.batch_op._BatchDataset
X_train
# <_BatchDataset element_spec=TensorSpec(shape=(None, 28, 28, 1), dtype=tf.float32, name=None)>
def build_generator():
network = tf.keras.Sequential() #텐서플로우 사용 인공신경망 기본값
network.add(layers.Dense(units = 7*7*256, use_bias = False, input_shape=(100,))) # units:첫번째 레이어에서 사용할 총 뉴런 개수, generator에서는 분류 문제를 푸는게 아니라서 bias 필요없음, input_shape:인풋레이어의 뉴런 개수(default:100)
# 256은 데이터 개수인데, 왜 7*7로 설정했는지를 모르겠음.???
network.add(layers.BatchNormalization()) # 정규화 레이어
network.add(layers.LeakyReLU()) #activation layer
network.add(layers.Reshape((7,7,256)))
# 7x7x128
network.add(layers.Conv2DTranspose(filters = 128, kernel_size = (5,5), padding='same', use_bias=False)) # filters: filter detector 수, kernel_size: detector 사이즈, padding:'same'은 남는 컬럼 뒤에 0추가해서 사이즈 맞추는거/'valid'는 남는 컬럼 사용안하는거
# Conv2DTranspose : 차원값 늘리려고 사용.(7->14->28)
network.add(layers.BatchNormalization())
network.add(layers.LeakyReLU())
# 14x14x64
network.add(layers.Conv2DTranspose(filters = 64, kernel_size = (5,5), padding='same', use_bias=False, strides=(2,2)))
network.add(layers.BatchNormalization())
network.add(layers.LeakyReLU())
# 28x28x1 (원래 이미지 사이즈 가로, 세로, 채널값)
network.add(layers.Conv2DTranspose(filters = 1, kernel_size=(5,5), padding='same',use_bias=False, strides=(2,2), activation = 'tanh')) #'tanh'으로 하면 -1~1로 결과 나옴. 처음 데이터 정규화한거랑 같은 출력값이 나오게 해야함. 0~1이었으면 sigmoid 사용.
network.summary()
return network
# 생성
generator = build_generator()
generator.input
# <KerasTensor: shape=(None, 100) dtype=float32 (created by layer 'dense_input')>
# 인풋 레이어에 들어갈 노이즈값 랜덤 설정
noise = tf.random.normal([1, 100])
noise
# generator 작동하는지 테스트
generated_image = generator(noise, training = False) #지금은 학습안되게 설정
generated_image.shape
# TensorShape([1, 28, 28, 1]) : 마지막 출력되는 결과값의 shape
plt.imshow(generated_image[0, :, :, 0], cmap='gray');
# 이미지 하나만 그릴거라서 0, 차원 정보는 다 필요해서 :, 채널값도 한개라서 0 넣어줌.
# -> ?? 잘 이해안됨. 아시는 분 댓글 달아주세요..
def build_discriminator():
network = tf.keras.Sequential()
# 14x14x64
network.add(layers.Conv2D(filters = 64, strides=(2,2), kernel_size = (5,5), padding = 'same', input_shape = [28,28,1]))
# Conv2D 사용해서 사이즈 줄임 (28->14->7), strides: moving window(커널이 움직이는 정도같음)
network.add(layers.LeakyReLU())
network.add(layers.Dropout(0.3)) # Dropout: 과적합 방지용으로 0.2~0.5 정도로 사용
# 7x7x128
network.add(layers.Conv2D(filters = 128, strides=(2,2), kernel_size = (5,5), padding = 'same'))
network.add(layers.LeakyReLU())
network.add(layers.Dropout(0.3))
network.add(layers.Flatten()) # 벡터화
network.add(layers.Dense(1)) # real or fake (1 or 0) 값 하나로 출력.
# 마지막에 activation function 사용 안함.
network.summary()
return network
# 생성
discriminator = build_discriminator()
discriminator.input
# discriminator.input
<KerasTensor: shape=(None, 28, 28, 1) dtype=float32 (created by layer 'conv2d_input')>
# discriminator 작동하는지 테스트
discriminator(generated_image, training = False)
# <tf.Tensor: shape=(1, 1), dtype=float32, numpy=array([[0.00055447]], dtype=float32)>
# 마지막에 활성함수 지정안해서 출력값이 logit값으로 나옴(0.00055447)
tf.sigmoid(0.00356018)
# <tf.Tensor: shape=(), dtype=float32, numpy=0.5008901>
# 확률값으로 보기 위해 시그모이드 적용하면 0.5(진짠지 가짠지 반반이라는 소리)
cross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits = True)
# 진짠지 가짠지 이진분류라서 binaryCrossentropy 사용, D 아웃풋이 logit값이어서 True
generator_optimizer = tf.keras.optimizers.Adam(learning_rate=0.00001)
discriminator_optimizer = tf.keras.optimizers.Adam(learning_rate=0.00001)
# best weights를 찾기 위한 옵션
def discriminator_loss(expected_output, fake_output):
real_loss = cross_entropy(tf.ones_like(expected_output), expected_output)
fake_loss = cross_entropy(tf.zeros_like(fake_output), fake_output)
total_loss = real_loss + fake_loss
return total_loss
def generator_loss(fake_output):
return cross_entropy(tf.ones_like(fake_output), fake_output)
discriminator는 진짜 이미지와 가짜 이미지를 비교하는 작업을 위해,
generator는 가짜 이미지를 진짜로 판별되도록 만들기위해,
- D => V(D,G)가 최대가 되도록 진화
가짜 D(G(x))는 0, 진짜 D(x)는 1로 판별하는 Discriminator.
D(G(x))=0, log(1-D(G(x)))=0
D(x)=1, logD(x)=0
이때 D(x)는 0 아니면 1이어서, logD(x) + log(1-D(G(x))) 최대값이 0이 되는 것.- G => V(D,G)가 최소가 되도록 진화
가짜 D(G(x))가 진짜라고 판별되도록 진화하는 generator.
D(G(x))=1이 되는게 목표.
위에서 했던 코드들 정리해서 한번에 쓰는 느낌
epochs = 100 # 학습횟수
noise_dim = 100 # noise dimension: 인풋값에 넣을 랜덤값 수
num_images_to_generate = 16 # 만들 이미지 수
@tf.function
def train_steps(images):
noise = tf.random.normal([batch_size, noise_dim])
with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:
generated_images = generator(noise, training = True)
# 가짜 이미지 = generator(노이즈 넣음, 학습 ㅇ)
expected_output = discriminator(images, training = True)
# 실제 출력값 = discriminator(실제 데이터 입력, 학습 ㅇ)
fake_output = discriminator(generated_images, training = True)
# 가짜 출력값 = discriminator(가짜 이미지 입력, 학습 ㅇ)
gen_loss = generator_loss(fake_output)
disc_loss = discriminator_loss(expected_output, fake_output)
# weight 수정 방향 정하기
gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables)
gradients_of_discriminator = disc_tape.gradient(disc_loss, discriminator.trainable_variables)
# optimizer 수정 방향 정하기
generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables))
discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.trainable_variables))
def train(dataset, epochs, test_images):
for epoch in range(epochs):
for image_batch in dataset: # 미니 배치
#print(image_batch.shape)
train_steps(image_batch)
print('Epoch: ', epoch + 1)
generated_images = generator(test_images, training=False)
fig = plt.figure(figsize = (6,6))
for i in range(generated_images.shape[0]): # 생성된 이미지 수
plt.subplot(4,4,i+1)
plt.imshow(generated_images[i, :, :, 0] * 127.5 + 127.5, cmap = 'gray')
plt.axis('off')
plt.show()
test_images = tf.random.normal([num_images_to_generate, noise_dim])
# 랜덤 이미지 생성
test_images.shape
# TensorShape([16, 100])
train(X_train, epochs, test_images)