My blogs

The greatest test of courage on earth is to bear defeat without losing heart.

0%

DCGAN_Mnist

介紹

DCGAN Minst 實作,筆記。


介紹 DCGAN 與 Mnist dataset

Mnist 介紹

手寫數字,請參閱至本篇按此點選

DCGAN 介紹

請參閱至本篇論文按此點選

DCGAN(Deep Convolutional Generative Adversarial Network),與一般GAN最主要差別是加入捲積引入生成器鑑別器網絡。

CNN 卷積的概念。通過強制一層的神經元共享權重,前向傳遞(通過網絡提供數據)相當於在圖像上卷積一個濾波器以產生新圖像。然後 CNN 的訓練變成了學習過濾器的任務(決定你應該在數據中尋找哪些特徵。)


特點:

  1. 取消所有 pooling 層。G 網路中使用轉置卷積(transposed convolutional layer)進行向上取樣,D 網路中用加入 stride 的卷積代替 pooling
  2. DG 中均使用 batch normalization
  3. 去掉 FC 層(全連接層) ,使網路變為 全卷積網路 (Conv2D、Conv2DTranspose)
  4. G 網路中使用 ReLU 作為啟用函式,最後一層使用 tanh
  5. D 網路中使用 LeakyReLU 作為啟用函式

ReLU 是所有正值的線性(身份),所有負值為零Leaky ReLU負值的斜率很小,而不是將值推到零。例如,當 x < 0 時,leaky ReLU 可能有 y = 0.01x。


生成器在生成逼真圖像方面逐漸變強,而判別器在辨別這些圖像的能力上逐漸變強。當判別器不再能夠區分真實圖片和偽造圖片時,訓練過程達到平衡

ex

上方圖片來源 tensorglow 範例圖。

ex

上方 GAN 模型結構事意圖。


Import libraries

Colab 運行按此點選

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import tensorflow as tf
# 查看版本
tf.__version__
# To generate GIFs
!pip install imageio
!pip install git+https://github.com/tensorflow/docs
import glob
import imageio
import matplotlib.pyplot as plt
import numpy as np
import os
import PIL
from tensorflow.keras import layers
import time

from IPython import display

Preprocessing && load_data

使用Tensorflow API 載入 mnist 資料級。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 只放入訓練資料
(train_images, train_labels), (_, _) = tf.keras.datasets.mnist.load_data()
# data_size 6000*28*28*1
# 60000張 長 * 寬 28 / 28 彩度為 1

train_images = train_images.reshape(train_images.shape[0], 28, 28, 1).astype('float32')
train_images = (train_images - 127.5) / 127.5 # Normalize the images to [-1, 1]
# 載入訓練照片為 60000張 批量進行數量256
BUFFER_SIZE = 60000
BATCH_SIZE = 256

'''
張量沿它們的第一維進行切片。此操作保留輸入張量的結構,刪除每個張量的第一個維度並將其用作數據集維
度。所有輸入張量的第一維必須具有相同的大小。
'''
# Batch and shuffle the data
train_dataset = tf.data.Dataset.from_tensor_slices(train_images).shuffle(BUFFER_SIZE).batch(BATCH_SIZE)

Create model

The Generator

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
def make_generator_model():
model = tf.keras.Sequential()
# 7 * 7 * filter = 256
# this dense 12544
model.add(layers.Dense(7*7*256, use_bias=False, input_shape=(100,)))
# 维持激活项平均值接近 0,标准差接近 1 的转换。
model.add(layers.BatchNormalization())
# 当神经元未激活时,它仍允许赋予一个很小的梯度: f(x) = alpha * x for x < 0, f(x) = x for x >= 0.
model.add(layers.LeakyReLU())

model.add(layers.Reshape((7, 7, 256)))
assert model.output_shape == (None, 7, 7, 256) # Note: None is the batch size
# “assert”命令來確定一個語句是否與我們期望的完全相同 執行確認鎘是正確

model.add(layers.Conv2DTranspose(128, (5, 5), strides=(1, 1), padding='same', use_bias=False))
# ConvTranspose2d 逆捲積 簡單來說就是把特徵還原成圖片的概念 strides=(1,1) (7*1,7*1,filter = 128) filter = 128 , kernel_size(5,5)
assert model.output_shape == (None, 7, 7, 128)
model.add(layers.BatchNormalization())
model.add(layers.LeakyReLU())

# filter = 64 , kernel=(5,5) (7*2,7*2,filter=64)
model.add(layers.Conv2DTranspose(64, (5, 5), strides=(2, 2), padding='same', use_bias=False))
assert model.output_shape == (None, 14, 14, 64)
model.add(layers.BatchNormalization())
model.add(layers.LeakyReLU())
# last layer (14*2,14*2,filter=1)

# 双曲正切激活函数。
# tanh 激活的範圍內,[-1, 1]
model.add(layers.Conv2DTranspose(1, (5, 5), strides=(2, 2), padding='same', use_bias=False, activation='tanh'))
assert model.output_shape == (None, 28, 28, 1)

return model

ex

上方 generator 模型結構圖。


Test generator
1
2
3
4
5
6
generator = make_generator_model()

noise = tf.random.normal([1, 100])
generated_image = generator(noise, training=False)

plt.imshow(generated_image[0, :, :, 0], cmap='gray')

The discriminator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def make_discriminator_model():
model = tf.keras.Sequential()
# filter =64 kerneal=(5,5) 14*14
model.add(layers.Conv2D(64, (5, 5), strides=(2, 2), padding='same',
input_shape=[28, 28, 1]))
model.add(layers.LeakyReLU())
model.add(layers.Dropout(0.3))
# filter =128 kerneal=(5,5) 7*7
model.add(layers.Conv2D(128, (5, 5), strides=(2, 2), padding='same'))
model.add(layers.LeakyReLU())
# 捨去部分特徵
model.add(layers.Dropout(0.3))

model.add(layers.Flatten())
model.add(layers.Dense(1))

return model

ex

上方 discriminator 模型結構圖。


Test discriminator

使用(尚未訓練的)判別器對所生成的圖像進行真偽分類。模型將被訓練為對真實圖像輸出正值,對偽造圖像輸出負值。

1
2
3
4
5
# 測試未訓練前
discriminator = make_discriminator_model()
# 當圖像放入判別器時判別值出來
decision = discriminator(generated_image)
print (decision)

Define the loss and optimizers

兩個模型定義損失函數和優化器。

1
2
3
# 該方法返回一個輔助函數來計算交叉熵損失
# 交叉熵損失用於二元(0 或 1)分類應用程序
cross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits=True)

loss func

discriminator_loss。

1
2
3
4
5
6
7
8
9
10
11
12


# 該方法量化判別器從判斷真偽圖片的能力。它將判別器對真實圖片的預測值與值全為 1 的數組進行對比,將判別器對偽造(生成的)圖片的預測值與值全為 0 的數組進行對比。
def discriminator_loss(real_output, fake_output):
real_loss = cross_entropy(tf.ones_like(real_output), real_output)
fake_loss = cross_entropy(tf.zeros_like(fake_output), fake_output)
total_loss = real_loss + fake_loss #預測的實數與一個比較那些張量,並用假貨零張量。然後將它們組合在一起
return total_loss
# 一批真實數據被饋送到鑑別器。
# 一批生成的數據被饋送到鑑別器。
# 生成器的性能有多差(即它的損失)是通過查看鑑別器識別假樣本的能力來衡量的。
# 鑑別器的性能有多差(即它的損失)是通過查看真實樣本和假樣本的分類誤差來衡量的。

generator_loss。

1
2
3
def generator_loss(fake_output):
return cross_entropy(tf.ones_like(fake_output), fake_output)
# 生成器的損失可量化其欺騙判別器的能力。直觀地說,如果生成器表現良好,判別器會將偽造圖像分類為真實圖像(或1)。在此,需要將判別器對生成圖像的決策與值全為1 的數組進行對比。

判別器和生成器優化器不同。

1
2
3
4
5
6
#learning rate
# 1e-4=1x10^(-4)=1/(10^4)=0.0001
generator_optimizer = tf.keras.optimizers.Adam(1e-4)
discriminator_optimizer = tf.keras.optimizers.Adam(1e-4)
# Adam 優化算法是隨機梯度下降的擴展。隨機梯度下降為所有權重更新保持一個單一的學習率(稱為 alpha)
# ,並且學習率在訓練期間不會改變。每個網絡權重(參數)都有一個學習率,並且隨著學習的展開而分別進行調整。

Ex

ex


Save checkpoints

1
2
3
4
5
checkpoint_dir = './training_checkpoints'
checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt")
checkpoint = tf.train.Checkpoint(generator_optimizer=generator_optimizer,
discriminator_optimizer=discriminator_optimizer,
generator=generator,

Define the training

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
EPOCHS = 50
noise_dim = 100
num_examples_to_generate = 16

# You will reuse this seed overtime (so it's easier)
# to visualize progress in the animated GIF)
# 為輸出生成隨機噪聲
seed = tf.random.normal([num_examples_to_generate, noise_dim])

# Notice the use of `tf.function`
# This annotation causes the function to be "compiled".
@tf.function
def train_step(images):
noise = tf.random.normal([BATCH_SIZE, noise_dim])
# GradientTape 記錄自動微分操作
# 反向模式微分计算“记录的”计算的梯度
# 來查找兩個模型的各自變量
with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:
generated_images = generator(noise, training=True)
# 將噪聲放入生成器並創建假圖像

real_output = discriminator(images, training=True)
# 將真實圖像和假圖像放入判別器並返回判別器值
fake_output = discriminator(generated_images, training=True)

gen_loss = generator_loss(fake_output)
#更新假圖像,使鑑別器學習到
# 返回總損失,即真實圖像損失和假圖像損失的總和
disc_loss = discriminator_loss(real_output, fake_output)
# GradientTape.gradient(target, sources)
gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables)
gradients_of_discriminator = disc_tape.gradient(disc_loss, discriminator.trainable_variables)

generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables))
discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.trainable_variables))


training

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
def train(dataset, epochs):
for epoch in range(epochs):
start = time.time()

for image_batch in dataset:
# 插入圖片並運行學習。
train_step(image_batch)

# Produce images for the GIF as you go
display.clear_output(wait=True)
generate_and_save_images(generator,
epoch + 1,
seed)

# Save the model every 15 epochs
if (epoch + 1) % 15 == 0:
checkpoint.save(file_prefix = checkpoint_prefix)

print ('Time for epoch {} is {} sec'.format(epoch + 1, time.time()-start))

# Generate after the final epoch
display.clear_output(wait=True)
generate_and_save_images(generator,
epochs,
seed)


save img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def generate_and_save_images(model, epoch, test_input):
# Notice `training` is set to False.
# This is so all layers run in inference mode (batchnorm).
predictions = model(test_input, training=False)

fig = plt.figure(figsize=(4, 4))

for i in range(predictions.shape[0]):
plt.subplot(4, 4, i+1)
plt.imshow(predictions[i, :, :, 0] * 127.5 + 127.5, cmap='gray')
plt.axis('off')

plt.savefig('image_at_epoch_{:04d}.png'.format(epoch))
plt.show()


Train the model && checkpoint

1
2
3
train(train_dataset, EPOCHS)
checkpoint.restore(tf.train.latest_checkpoint(checkpoint_dir))


Create a GIF

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Display a single image using the epoch number
def display_image(epoch_no):
return PIL.Image.open('image_at_epoch_{:04d}.png'.format(epoch_no))
display_image(EPOCHS)

anim_file = 'dcgan.gif'

with imageio.get_writer(anim_file, mode='I') as writer:
filenames = glob.glob('image*.png')
filenames = sorted(filenames)
for filename in filenames:
image = imageio.imread(filename)
writer.append_data(image)
image = imageio.imread(filename)
writer.append_data(image)
import tensorflow_docs.vis.embed as embed
embed.embed_file(anim_file)

EX:

ex


Save model && load model

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def save_model():
generator.save("trained_generator1.h5")
save_model()


def load_model(path_of_saved_h5_file):

generator = tf.keras.models.load_model(path_of_saved_h5_file)


noise = tf.random.normal([1, 100])
generated_image = generator(noise, training=False)

plt.imshow(generated_image[0, :, :, 0], cmap='gray')
plt.show()





load_model("trained_generator1.h5")

Download

EX:

ex


參考網站


如果您喜歡我的文章,請幫我按五下 ,感謝大家。