趋近智
生成对抗网络(GAN)涉及设置两个相互竞争的神经网络 (neural network)——生成器和判别器,并以对抗方式同时训练它们。一个基本的GAN将使用TensorFlow和Keras进行构建,目标是生成与MNIST数据集相似的图像。
生成器的任务是创建模仿真实数据分布的合成数据。它接收一个随机噪声向量 (vector)(通常取自高斯分布或均匀分布)作为输入,并将其转换为与真实数据具有相同结构(例如,MNIST的28x28灰度图像)的输出。
一个简单的生成器可以使用tf.keras.Sequential构建。我们将从一个Dense层开始,将输入噪声投影到更高维的空间,然后进行重塑,如果构建的是卷积GAN(DCGAN),则可能使用转置卷积(Conv2DTranspose)。为简单起见,这里我们使用Dense层进行演示,它适用于生成扁平化的MNIST图像或适应简单的图像结构。
import tensorflow as tf
def build_generator(latent_dim, output_shape):
model = tf.keras.Sequential(name='Generator')
model.add(tf.keras.layers.Input(shape=(latent_dim,)))
# 使用Dense层的示例 - 根据具体任务调整架构
model.add(tf.keras.layers.Dense(128, activation='relu'))
model.add(tf.keras.layers.Dense(256, activation='relu'))
model.add(tf.keras.layers.Dense(output_shape, activation='tanh')) # 对于缩放到[-1, 1]的输出使用tanh激活函数
return model
# 扁平化MNIST图像(28*28 = 784)的示例用法
latent_dim = 100
output_dim = 784
generator = build_generator(latent_dim, output_dim)
generator.summary() # 显示模型结构
latent_dim参数 (parameter)定义了输入噪声向量的大小。最终的激活函数 (activation function)(例如,tanh或sigmoid)应与真实数据的预期范围相匹配。对于归一化 (normalization)到[-1, 1]的MNIST图像,tanh是合适的。
判别器充当二分类器。它的输入可以是真实数据样本,也可以是生成器产生的虚假样本。它的目标是输出一个概率,表示输入是真实的(概率接近1)还是虚假的(概率接近0)。
与生成器类似,一个简单的判别器可以是tf.keras.Sequential模型。它通常由Dense层(或图像数据的卷积层)组成,然后是一个带有单个输出单元和sigmoid激活函数 (activation function)的最终Dense层,以产生概率分数。
def build_discriminator(input_shape):
model = tf.keras.Sequential(name='Discriminator')
model.add(tf.keras.layers.Input(shape=(input_shape,)))
# 使用Dense层的示例
model.add(tf.keras.layers.Dense(256, activation='relu'))
model.add(tf.keras.layers.Dense(128, activation='relu'))
model.add(tf.keras.layers.Dropout(0.3)) # 正则化会有帮助
model.add(tf.keras.layers.Dense(1, activation='sigmoid')) # 输出概率
return model
# 扁平化MNIST图像(784)的示例用法
discriminator = build_discriminator(output_dim) # 输入与生成器输出/真实数据匹配
discriminator.summary()
对抗训练需要为生成器和判别器设置不同的损失函数。我们通常使用二元交叉熵损失(tf.keras.losses.BinaryCrossentropy),因为判别器执行的是二分类(真实 vs. 虚假)。
判别器损失 (): 此损失旨在促使判别器对真实图像输出1,对虚假图像输出0。它由两部分组成:真实图像上的损失和虚假图像上的损失。
其中是判别器对真实数据的输出,是生成器对噪声的输出,是批量大小。
生成器损失 (): 此损失旨在促使生成器产生判别器将其分类为真实(输出1)的输出。
我们可以使用tf.keras.losses.BinaryCrossentropy来实现这些。请注意,对于生成器损失,我们将判别器对虚假图像的输出与标签1(真实)进行比较。
# 如果判别器的最终层没有sigmoid激活函数,则使用from_logits=True
cross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits=False)
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
def generator_loss(fake_output):
# 生成器希望判别器认为虚假图像是真实的(标签1)
return cross_entropy(tf.ones_like(fake_output), fake_output)
由于生成器和判别器有不同的目标并单独更新,我们需要为它们各自设置独立的优化器。Adam优化器是常用的一种。
generator_optimizer = tf.keras.optimizers.Adam(learning_rate=1e-4)
discriminator_optimizer = tf.keras.optimizers.Adam(learning_rate=1e-4)
学习率可能需要调整;有时生成器和判别器会使用不同的学习率。
GAN训练需要自定义训练循环,因为生成器和判别器的更新必须精心协调。标准的model.fit()不直接适用。我们将使用tf.GradientTape来计算每个网络的梯度。
以下是单个训练步骤的结构,通常用tf.function封装以优化性能:
# 假设 'real_images' 是数据集(例如 MNIST)中的一个批次
# 假设 'latent_dim' 已定义
@tf.function
def train_step(real_images, generator, discriminator, gen_optimizer, disc_optimizer, batch_size, latent_dim):
# 1. 生成噪声
noise = tf.random.normal([batch_size, latent_dim])
# 使用 GradientTape 进行自动微分
with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:
# 2. 生成虚假图像
generated_images = generator(noise, training=True)
# 3. 获取判别器对真实和虚假图像的预测
real_output = discriminator(real_images, training=True)
fake_output = discriminator(generated_images, training=True)
# 4. 计算损失
gen_loss = generator_loss(fake_output)
disc_loss = discriminator_loss(real_output, fake_output)
# 5. 计算梯度
gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables)
gradients_of_discriminator = disc_tape.gradient(disc_loss, discriminator.trainable_variables)
# 6. 应用梯度来更新权重
gen_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables))
disc_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.trainable_variables))
return gen_loss, disc_loss
这个train_step函数封装了对抗训练过程的一次迭代:生成虚假数据、评估两个网络、计算损失、计算梯度并更新权重 (weight)。
完整的训练过程包括在多个周期和真实数据集的多个批次上迭代执行train_step。
# 训练循环(需要加载数据集、周期循环等)
# epochs = ...
# batch_size = ...
# dataset = load_and_prepare_mnist_dataset(...) # 归一化到 [-1, 1]
# for epoch in range(epochs):
# print(f"周期 {epoch+1}/{epochs}")
# epoch_gen_loss_avg = tf.keras.metrics.Mean()
# epoch_disc_loss_avg = tf.keras.metrics.Mean()
# for image_batch in dataset: # 假设数据集产生真实图像的批次
# gen_loss, disc_loss = train_step(
# image_batch,
# generator,
# discriminator,
# generator_optimizer,
# discriminator_optimizer,
# batch_size,
# latent_dim
# )
# epoch_gen_loss_avg.update_state(gen_loss)
# epoch_disc_loss_avg.update_state(disc_loss)
# print(f"生成器损失: {epoch_gen_loss_avg.result():.4f}, 判别器损失: {epoch_disc_loss_avg.result():.4f}")
# # 在此处添加代码以定期保存检查点并生成样本图像
# # 在每个周期结束时重置指标
# epoch_gen_loss_avg.reset_states()
# epoch_disc_loss_avg.reset_states()
这种结构为编写一个简单的GAN提供了基础。具体内容包括定义独立的生成器和判别器网络,根据二元交叉熵设置各自的损失函数 (loss function),使用独立的优化器,并使用tf.GradientTape在自定义训练循环中协调它们的更新。监测损失值和定期可视化生成器的输出是评估训练进展的重要步骤。请记住,GAN训练可能不稳定;通常需要仔细调整超参数 (parameter) (hyperparameter)(学习率、网络架构)。
这部分内容有帮助吗?
tf.GradientTape。© 2026 ApX Machine Learning用心打造