《GANs实战》学习笔记(三)第三章 你的第一个GAN模型:生成手写数字

GAN(生成对抗网络)出版了一本实战书,了解下?

第3章 你的第一个GAN模型:生成手写数字

一、GAN的基础:对抗训练

生成器和鉴别器由可微函数表示如神经网络,它们都有自己的代价函数。这两个网络是利用鉴别器的损失进行反向传播训练

鉴别器努力使真实样本输入和伪样本输入带来的损失最小化,而生成器努力使它生成的伪样本造成的鉴别器损失最大化。

生成器的目标是生成符合训练数据集数据分布的样本。训练数据集决定了生成器要学习模拟的样本类型,例如:目标是生成猫的逼真图像,我们就会给GAN一组猫的图像。

生成器和鉴别器只能调整自己的参数而不能相互调整对方的参数,所以GAN的训练过程可以用一个博弈过程来更好地描述,而非优化。

二、生成器和鉴别器

现在通过引入更多的表示概括所学的内容。生成器(G)接收随机噪声向量z并生成一个伪样本x^{x}。数学上来说,G(z)=x^{x}。鉴别器(D)的输入要么是真实样本x,要么是伪样本x^{x};对于每个输入,它输出一个介于0和1之间的值,表示输入是真实样本的概率。

生成器网络G将随机向量z转换为伪样本x^{x}:G(z) = x^{x}

鉴别器网络D对输入样本是否是真实进行分类并输出。

  • 对于真实样本x,鉴别器力求输出尽可能接近1的值;
  • 对于伪样本 x^{x},鉴别器力求输出尽可能接近0的值。

相反:生成器希望D(x^{x})尽可能接近1,这表明鉴别器被欺骗,将伪样本分类为真实样本。

1、对抗的目标 -鉴真与造假

鉴真:鉴别器的目标是尽可能精确。对于真是样本x,D(x)力求尽可能接近1(正的标签);对于伪样本x^{x},D(x^{x})力求尽可能接近0(负的标签)。

造假:生成器的目标正好相反,它试图通过生成与训练数据集中的真实数据别无二致的伪样本x^{x}来欺骗鉴别器。从数学角度讲,即生成器试图生成假样本x^{x},使得D(x^{x})尽可能接近1。

2、混淆矩阵

鉴别器的分类可以用混淆矩阵来表示,混淆矩阵是二元分类中所有可能结果的表格表示。鉴别器的分类结果:真阳性(True Positive)、假阴性(False Negative)、真阴性(True Negative)、假阴性(False Negative)。

三、GAN训练算法

GAN训练算法:对每次训练迭代,执行:

(1)训练鉴别器。

a.取随机的小批量的真实样本x。

b.去随机的小批量的随机噪声z,并生成一小批量伪样本:G(z)=x^{x}

c.计算D(x)和D(x^{x})的分类损失,并反向传播总误差以更新D模型参数来最小化分类损失。

(2)训练生成器。

a.取随机的小批量的随机噪声z生成一小批量伪样本:G(z)=x^{x}

b.用鉴别器网络对x^{x}进行分类。

c.计算D(x^{x})的分类损失,并反向传播总误差以更新生成器的模型参数来最大化分类损失。

注意:在步骤1中训练鉴别器时,生成器的参数保持不变;同样,在步骤2中,在训练生成器时保持鉴别器的参数不变。

之所以只允许更新被训练网络的权重和偏置,是因为要将所有更改隔离到仅受到该网络控制的参数中。这可以确保每个网络都能获得如何进行更新的相关信号,而不受其他网络更新的干扰。

四、、教程:生成手写数字

1、导入依赖包输入超参数

%matplotlib inline

import matplotlib.pyplot as plt
import numpy as np

from keras.datasets import mnist
from keras.layers import Dense, Flatten, Reshape
from keras.layers.advanced_activations import LeakyReLU
from keras.models import Sequential
from keras.optimizers import Adam
img_rows = 28
img_cols = 28
channels = 1

# Input image dimensions
img_shape = (img_rows, img_cols, channels)

# Size of the noise vector, used as input to the Generator
z_dim = 100

2、构造生成器

生成器是一个只有一格隐藏层的神经网络。

生成器以z为输入,生成28*28*1的图像。在隐藏层中使用LeakyReLU激活函数。与将任何负输入映射到0的常规ReLU函数不同,LeakyRelu函数允许存在一个小的正梯度,这样可以防止梯度在训练过程中消失,从而产生更好的训练效果。

在输出层使用tanh激活函数,它将输出值缩放到范围[-1,1]。之所以使用tanh(与sigmoid不同,sigmoid会输出更为典型的0到1范围内的值),是因为它有助于生成更清晰的图像。

使用贯序模式,无分支。

def build_generator(img_shape, z_dim):

    model = Sequential()

    # Fully connected layer
    model.add(Dense(128, input_dim=z_dim))

    # Leaky ReLU activation
    model.add(LeakyReLU(alpha=0.01))

    # Output layer with tanh activation
    model.add(Dense(28 * 28 * 1, activation='tanh'))

    # Reshape the Generator output to image dimensions
    model.add(Reshape(img_shape))

    return model

3、构造鉴别器

鉴别器接受28*28*1的图像,并输出表示输入是否被视为真而不是假的概率。(为真概率)

鉴别器由一个两层神经网络表示,器隐藏层由128个隐藏单元及激活函数为LeakyReLU

鉴别器的输出层应用了sigmoid激活函数。这确保了输出值将介于0和1之间,可以将其解释为生成器将输入认定为真的概率

def build_discriminator(img_shape):

    model = Sequential()

    # Flatten the input image
    model.add(Flatten(input_shape=img_shape))

    # Fully connected layer
    model.add(Dense(128))

    # Leaky ReLU activation
    model.add(LeakyReLU(alpha=0.01))

    # Output layer with sigmoid activation
    model.add(Dense(1, activation='sigmoid'))

    return model

4、搭建整个模型

def build_gan(generator, discriminator):

    model = Sequential()

    # Combined Generator -> Discriminator model
    model.add(generator)
    model.add(discriminator)

    return model

组合模型(其中鉴别器设置为不可训练)仅用于训练生成器鉴别器将用单独编译的模型训练。 

二元交叉熵用于度量二分类预测计算的概率和实际概率之间的差异;交叉熵损失越大,预测离真值就越远。

# Build and compile the Discriminator
discriminator = build_discriminator(img_shape)
discriminator.compile(loss='binary_crossentropy',
                      optimizer=Adam(),
                      metrics=['accuracy'])

# Build the Generator
generator = build_generator(img_shape, z_dim)

# Keep Discriminator’s parameters constant for Generator training
discriminator.trainable = False

# Build and compile GAN model with fixed Discriminator to train the Generator
gan = build_gan(generator, discriminator)
gan.compile(loss='binary_crossentropy', optimizer=Adam())

优化每个网络使用的是Adam优化算法。 

5、训练

首先,取随机小批量 的MNIST图像为真实样本,从随机噪声向量z中生成小批量伪样本,然后在保持生成器参数不变的情况下,利用这些伪样本训练鉴别器网络。 

losses = []
accuracies = []
iteration_checkpoints = []


def train(iterations, batch_size, sample_interval):

    # Load the MNIST dataset
    (X_train, _), (_, _) = mnist.load_data()

    # Rescale [0, 255] grayscale pixel values to [-1, 1]
    X_train = X_train / 127.5 - 1.0
    X_train = np.expand_dims(X_train, axis=3)

    # Labels for real images: all ones
    real = np.ones((batch_size, 1))

    # Labels for fake images: all zeros
    fake = np.zeros((batch_size, 1))

    for iteration in range(iterations):

        # -------------------------
        #  Train the Discriminator
        # -------------------------

        # Get a random batch of real images
        idx = np.random.randint(0, X_train.shape[0], batch_size)
        imgs = X_train[idx]

        # Generate a batch of fake images
        z = np.random.normal(0, 1, (batch_size, 100))
        gen_imgs = generator.predict(z)

        # Train Discriminator
        d_loss_real = discriminator.train_on_batch(imgs, real)
        d_loss_fake = discriminator.train_on_batch(gen_imgs, fake)
        d_loss, accuracy = 0.5 * np.add(d_loss_real, d_loss_fake)

        # ---------------------
        #  Train the Generator
        # ---------------------

        # Generate a batch of fake images
        z = np.random.normal(0, 1, (batch_size, 100))
        gen_imgs = generator.predict(z)

        # Train Generator
        g_loss = gan.train_on_batch(z, real)

        if (iteration + 1) % sample_interval == 0:

            # Save losses and accuracies so they can be plotted after training
            losses.append((d_loss, g_loss))
            accuracies.append(100.0 * accuracy)
            iteration_checkpoints.append(iteration + 1)

            # Output training progress
            print("%d [D loss: %f, acc.: %.2f%%] [G loss: %f]" %
                  (iteration + 1, d_loss, 100.0 * accuracy, g_loss))

            # Output a sample of generated image
            sample_images(generator)

 训练K次判别器、再训练一次生成器。交替轮循进行。当前代码中k=1。

6、输出样本图像

def sample_images(generator, image_grid_rows=4, image_grid_columns=4):

    # Sample random noise
    z = np.random.normal(0, 1, (image_grid_rows * image_grid_columns, z_dim))

    # Generate images from random noise
    gen_imgs = generator.predict(z)

    # Rescale image pixel values to [0, 1]
    gen_imgs = 0.5 * gen_imgs + 0.5

    # Set image grid
    fig, axs = plt.subplots(image_grid_rows,
                            image_grid_columns,
                            figsize=(4, 4),
                            sharey=True,
                            sharex=True)

    cnt = 0
    for i in range(image_grid_rows):
        for j in range(image_grid_columns):
            # Output a grid of images
            axs[i, j].imshow(gen_imgs[cnt, :, :, 0], cmap='gray')
            axs[i, j].axis('off')
            cnt += 1

7、运行模型

设置训练超参数——迭代次数和批量大小,训练模型。

# Set hyperparameters
iterations = 20000
batch_size = 128
sample_interval = 1000

# Train the GAN for the specified number of iterations
train(iterations, batch_size, sample_interval)
1000 [D loss: 0.000703, acc.: 100.00%] [G loss: 6.611647]
2000 [D loss: 0.000129, acc.: 100.00%] [G loss: 8.342966]
3000 [D loss: 0.048652, acc.: 99.61%] [G loss: 5.102392]
4000 [D loss: 0.290520, acc.: 87.11%] [G loss: 5.049376]
5000 [D loss: 0.272809, acc.: 92.19%] [G loss: 5.135170]
6000 [D loss: 0.241676, acc.: 91.41%] [G loss: 4.293948]
7000 [D loss: 0.578245, acc.: 73.83%] [G loss: 2.877602]
8000 [D loss: 0.253521, acc.: 89.45%] [G loss: 4.035530]
9000 [D loss: 0.260670, acc.: 89.06%] [G loss: 3.997754]
10000 [D loss: 0.366024, acc.: 81.64%] [G loss: 3.163330]
11000 [D loss: 0.224645, acc.: 91.02%] [G loss: 4.301305]
12000 [D loss: 0.609185, acc.: 76.56%] [G loss: 2.731848]
13000 [D loss: 0.374400, acc.: 83.98%] [G loss: 2.878426]
14000 [D loss: 0.447661, acc.: 82.42%] [G loss: 2.822231]
15000 [D loss: 0.130454, acc.: 95.31%] [G loss: 4.019130]
16000 [D loss: 0.395809, acc.: 81.25%] [G loss: 3.178937]
17000 [D loss: 0.318862, acc.: 86.33%] [G loss: 3.722922]
18000 [D loss: 0.326413, acc.: 86.72%] [G loss: 2.938485]
19000 [D loss: 0.475047, acc.: 75.78%] [G loss: 2.513085]
20000 [D loss: 0.450170, acc.: 78.91%] [G loss: 2.463450]

 

8、Loss与Acc

losses = np.array(losses)

# Plot training losses for Discriminator and Generator
plt.figure(figsize=(15, 5))
plt.plot(iteration_checkpoints, losses.T[0], label="Discriminator loss")
plt.plot(iteration_checkpoints, losses.T[1], label="Generator loss")

plt.xticks(iteration_checkpoints, rotation=90)

plt.title("Training Loss")
plt.xlabel("Iteration")
plt.ylabel("Loss")
plt.legend()

accuracies = np.array(accuracies)

# Plot Discriminator accuracy
plt.figure(figsize=(15, 5))
plt.plot(iteration_checkpoints, accuracies, label="Discriminator accuracy")

plt.xticks(iteration_checkpoints, rotation=90)
plt.yticks(range(0, 100, 5))

plt.title("Discriminator Accuracy")
plt.xlabel("Iteration")
plt.ylabel("Accuracy (%)")
plt.legend()

 

五、结论

考虑到生成器和鉴别器只用了简单的双层网络结构,后续将使用更加复杂和强大的神经网络结构——卷积神经网络,以提高生成图像的质量。

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值