生成对抗网络 (GAN) 是一种神经网络,可以根据训练数据集中的模式生成新数据。GAN 由两个神经网络组成:生成器和鉴别器。生成器接受一个随机输入并产生一个新的输出,该输出试图与训练数据相似,而鉴别器则试图区分真实数据和生成数据。这两个网络在称为对抗训练的过程中一起训练,生成器试图愚弄鉴别器,而鉴别器则试图正确识别生成的数据。
在本文中,我们将介绍使用 TensorFlow 和 Keras 生成手写数字的基本 GAN。我们将首先解释生成器和鉴别器网络的架构,然后展示如何使用手写数字的 MNIST 数据集训练 GAN。
生成器网络的架构
生成器网络采用长度为 100 的随机输入向量,并生成类似于手写数字的大小为 28x28 的图像。生成器网络的架构如下:
- 具有 7x7x256 个单元且无偏差的密集层。
- 批量归一化层。
- Leaky ReLU 激活函数。
- 重塑层以将输出转换为 7x7x256 张量。
- Conv2DTranspose 层具有 128 个过滤器,内核大小为 5x5,步幅为 1,填充相同。
- 批量归一化层。
- Leaky ReLU 激活函数。
- Conv2DTranspose 层具有 64 个过滤器,内核大小为 5x5,步幅为 2,填充相同。
- 批量归一化层。
- Leaky ReLU 激活函数。
- Conv2DTranspose 层具有 1 个过滤器、内核大小为 5x5、步幅为 2、相同的填充和 tanh 激活函数。
生成器网络采用长度为 100 的随机输入向量并生成大小为 28x28 的输出图像。该网络的架构旨在通过应用转置卷积层来逐渐将输入向量放大为 28x28 图像,这些卷积层会增加特征图的宽度和高度,同时降低其深度。
鉴别器网络的架构
鉴别器网络获取大小为 28x28 的图像并输出一个二进制值,指示图像是真实的还是假的(由生成器网络生成)。判别器网络的架构如下:
- Conv2D 层具有 64 个过滤器,内核大小为 5x5,步幅为 2,填充相同,输入形状为 [28,28,1]。
- Leaky ReLU 激活函数。
- dropout 层的比率为 0.3。
- Conv2D 层具有 128 个过滤器,内核大小为 5x5,步幅为 2,填充相同。
- Leaky ReLU 激活函数。
- dropout 层的比率为 0.3。
- 展平层以将输出转换为一维张量。
- 具有 1 个单元和 S 形激活函数的密集层。
该网络的架构旨在通过应用卷积层来逐渐将输入图像缩小为一维张量,这些卷积层会减少特征图的宽度和高度,同时增加它们的深度。
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
# 生成器网络
generator = keras.Sequential(
[
layers.Dense( 7 * 7 * 256 , input_shape=( 100 ,), use_bias= False ),
layers.BatchNormalization( ),
layers.LeakyReLU(),
layers.Reshape(( 7 , 7 , 256 )),
layers.Conv2DTranspose( 128 , ( 5, 5 ), strides=( 1 , 1 ), padding= 'same' , use_bias= False ),
layers.BatchNormalization(),
layers.LeakyReLU(),
layers.Conv2DTranspose( 64 , ( 5 , 5 ), strides=( 2 , 2 ), padding= 'same' , use_bias= False ),
layers.BatchNormalization(),
layers.LeakyReLU(),
layers.Conv2DTranspose( 1 , ( 5 , 5 ), strides=( 2 , 2), padding= 'same' , use_bias= False , activation= 'tanh' )
]
)
# 鉴别器网络
discriminator = keras.Sequential(
[
layers.Conv2D( 64 , ( 5 , 5 ), strides=( 2 , 2 ), padding= 'same' , input_shape=[ 28 , 28 , 1 ]),
layers.LeakyReLU(),
layers.Dropout( 0.3 ),
layers.Conv2D( 128 , ( 5 , 5 ), strides=(2 , 2 ), padding= 'same' ),
layers.LeakyReLU(),
layers.Dropout( 0.3 ),
layers.Flatten(),
layers.Dense( 1 , activation= 'sigmoid' )
]
)
# 合并两个网络into a GAN
gan = keras.Sequential([generator, discriminator])
# 编译判别器 discriminator
. compile (loss= 'binary_crossentropy' , optimizer=keras.optimizers.Adam(learning_rate= 0.0002 , beta_1= 0.5 ), metrics=[ 'accuracy' ])
# 编译 GAN
甘 compile (loss= 'binary_crossentropy' , optimizer=keras.optimizers.Adam(learning_rate= 0.0002 , beta_1= 0.5 ))
# 加载 MNIST 数据集
(train_images, train_labels), (_, _) = keras.datasets.mnist.load_data( )
# 标准化图像并为 GAN 重塑
图像 train_images = train_images.reshape(train_images.shape[ 0 ], 28 , 28 , 1 ).astype( 'float32' )
train_images = (train_images - 127.5 ) / 127.5
# 定义训练循环
时期 = 100
batch_size = 256
steps_per_epoch = train_images.shape[ 0 ] // batch_size
for epoch in range (epochs):
for step in range (steps_per_epoch):
# 生成随机噪声作为生成器的输入
noise = tf.random.normal([batch_size, 100 ])
# 使用生成器生成假图像
fake_images = generator(noise)
# 将真实图像和假图像连接成一个数组
combined_images = tf.concat([fake_images, train_images[step*batch_size:(step+ 1 )*batch_size]], axis= 0 )
# 真实和虚假图像的标签
real_labels = tf.ones((batch_size, 1 ))
fake_labels = tf.zeros((batch_size, 1 ))
# 给标签加噪声进行正则化
real_labels += 0.05 * tf.random.uniform(real_labels.shape)
fake_labels += 0.05 * tf.random.uniform(fake_labels.shape)
# 训练判别器
discriminator_loss_real = discriminator.train_on_batch(train_images[step*batch_size:(step+ 1 )*batch_size], real_labels)
discriminator_loss_fake = discriminator.train_on_batch(fake_images, fake_labels)
discriminator_loss = 0.5 * (discriminator_loss_real[ 0] +
训练 GAN
为了训练 GAN,我们首先加载手写数字的 MNIST 数据集并将图像归一化为像素值介于 -1 和 1 之间。然后我们定义训练循环以迭代数据集并交替训练生成器和鉴别器网络。在每次迭代中,我们生成一批随机噪声向量,并使用生成器网络生成一批生成图像。然后,我们将生成的图像与数据集中的一批真实图像结合起来,并使用它们来训练鉴别器网络。最后,我们更新生成器网络的权重以最小化组合 GAN 网络的损失。
这是训练循环的代码:
# 加载 MNIST 数据集
(x_train, _), (_, _) = mnist.load_data()
# 规范化图像
x_train = (x_train.astype( 'float32' ) - 127.5 ) / 127.5
# 定义 GAN 网络
generator = make_generator_model()
discriminator = make_discriminator_model()
# 定义损失函数和优化器
cross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits= True )
generator_optimizer = tf.keras.optimizers.Adam( 1e-4 )
discriminator_optimizer = tf.keras.optimizers.Adam( 1e- 4 )
# 定义训练循环
@tf.function
def train_step ( images):
# 生成噪声
noise = tf.random.normal([BATCH_SIZE, 100 ])
# 使用生成器生成图像
generated_images = generator(noise)
# 合并生成的图像和真实图像
combined_images = tf.concat([generated_images, images], axis= 0 )
# 为鉴别器生成标签
real_labels = tf.ones([BATCH_SIZE, 1 ])
fake_labels = tf.zeros([BATCH_SIZE, 1 ])
labels = tf.concat([fake_labels, real_labels], axis= 0 )
# 添加噪声to labels
labels += 0.05 * tf.random.uniform(tf.shape(labels))
# 训练鉴别器
以tf.GradientTape()作为磁带:
predictions = discriminator(combined_images)
discriminator_loss = cross_entropy(labels, predictions)
gradients = tape.gradient(discriminator_loss, discriminator.trainable_variables)
discriminator_optimizer.apply_gradients( zip (gradients, discriminator.trainable_variables))
# Generate生成器的新噪声
noise = tf.random.normal([BATCH_SIZE, 100 ]) #使用tf.GradientTape()作为磁带
训练生成器: generated_images = generator(noise) predictions = discriminator(generated_images)
generator_loss = cross_entropy(tf.ones_like(predictions), predictions)
gradients = tape.gradient(generator_loss, generator.trainable_variables)
generator_optimizer.apply_gradients( zip (gradients, generator.trainable_variables))
return discriminator_loss, generator_loss #在范围内
训练 GAN时期( EPOCHS): for i in range ( len (x_train) // BATCH_SIZE): # 选择一批图像 images = x_train[i * BATCH_SIZE:(i + 1 ) * BATCH_SIZE] # Train step discriminator_loss, generator_loss = train_step(images)
# 打印进度
if i % 100 == 0 :
print ( f'Epoch {epoch+ 1 } , Batch {i+ 1 } , Discriminator Loss: {discriminator_loss: .4 f} , Generator Loss: {generator_loss: .4 f} ' )
在训练循环中,我们首先定义 GAN 网络、损失函数和优化器。然后我们定义train_step函数,该函数获取一批图像并使用对抗训练过程训练鉴别器和生成器网络。最后,我们迭代固定数量的 epoch 的数据集,并使用该train_step函数训练 GAN。
结果
在 GAN 训练 50 个 epoch 后,我们可以通过将随机噪声向量输入生成器网络来生成新的手写数字。下面是生成 25 个新数字的代码:
# 生成新图像
noise = tf.random.normal([ 25 , 100 ])
generated_images = generator(noise)
重新缩放图像
生成图像 = 0.5 * 生成图像 + 0.5
绘制图像
fig, axs = plt.subplots( 5 , 5 )
fig.subplots_adjust(hspace= 0.3 , wspace= 0.3 )
idx = 0
for i in range ( 5 ):
for j in range ( 5 ):
axs[i, j]. imshow(generated_images[idx], cmap= 'gray' )
axs[i, j].axis( 'off' )
idx += 1
plt.show() 显示
结论
在本文中,我们探索了生成对抗网络 (GAN) 的概念,并演示了如何使用 TensorFlow 库实现一个简单的 GAN 来生成手写数字。GAN 已成为生成逼真图像的强大工具,并在艺术、设计和计算机视觉等领域拥有众多应用。随着渐进式 GAN 和基于样式的 GAN 等更先进的技术的出现,GAN 可能会继续突破图像生成和操作的可能性边界。但是,GAN 仍然是一个相对较新且复杂的主题,存在许多挑战和挑战有待解决的局限性。主要挑战之一是训练稳定性,因为众所周知 GAN 难以训练并且可能会出现模式崩溃,其中生成器产生一组有限的输出,无法捕获训练数据的全部多样性。其他挑战包括选择适当的损失函数和超参数,以及围绕使用 GAN 生成可用于传播错误信息或实施欺诈的虚假图像和视频的伦理问题。
尽管存在这些挑战,GAN 仍有可能彻底改变人工智能领域,并且已经在生成逼真的图像和视频方面取得了令人瞩目的成果。随着 GAN 不断进步和发展,它们可能会在广泛的应用中发挥越来越重要的作用,从生成虚拟现实环境到改进医学成像和诊断。