GAN生成48×48人脸图异常:滤波器尺寸影响及选型咨询
GAN生成48×48人脸图像的架构问题
我正在开发一个用于生成48×48人脸图像的GAN,但无论训练多久,生成器始终生成怪异图像,且判别器始终判定其为伪造图像,我认为这是架构问题。
生成图像示例
- 未训练输出:

- 训练25轮后:

- 使用5×5核尺寸时的生成图像:

生成图像呈现方块图案,而非未训练GAN应有的随机像素。该问题似乎与生成器反卷积层的滤波器尺寸有关,但我不确定具体关联机制。
我的问题:
- 为什么会出现这种情况?滤波器尺寸对图像产生了何种影响,从而导致此类图案?
- 如何根据图像尺寸或其他参数确定应使用的滤波器尺寸?
模型代码
generator = keras.Sequential([ keras.layers.Dense(4*4*32, input_shape=(100,), use_bias=False), keras.layers.LeakyReLU(), keras.layers.Reshape((4, 4, 32)), SpectralNormalization(keras.layers.Conv2DTranspose(64, (2, 2), strides=(2, 2), padding="same", use_bias=False)), keras.layers.LeakyReLU(), SpectralNormalization(keras.layers.Conv2DTranspose(32, (2, 2), strides=(2, 2), padding="same", use_bias=False)), keras.layers.LeakyReLU(), SpectralNormalization(keras.layers.Conv2DTranspose(3, (2, 2), strides=(3, 3), padding="same", use_bias=False)), keras.layers.LeakyReLU(), SpectralNormalization(keras.layers.Conv2DTranspose(3, (2, 2), strides=(2, 2), padding="same", use_bias=False)), ]) discriminator = keras.Sequential([ keras.layers.Conv2D(128, (2, 2), strides=(2, 2), input_shape=(96, 96, 3), padding="same"), keras.layers.LeakyReLU(), SpectralNormalization(keras.layers.Conv2D(64, (2, 2), strides=(2, 2), padding="same")), keras.layers.LeakyReLU(), SpectralNormalization(keras.layers.Conv2D(32, (2, 2), strides=(2, 2), padding="same")), keras.layers.LeakyReLU(), SpectralNormalization(keras.layers.Conv2D(16, (2, 2), strides=(2, 2), padding="same")), keras.layers.LeakyReLU(), SpectralNormalization(keras.layers.Conv2D(8, (2, 2), strides=(2, 2), padding="same")), keras.layers.LeakyReLU(), keras.layers.Flatten(), keras.layers.Dense(1, activation="sigmoid") ])
训练循环
cross_entropy = tf.keras.losses.BinaryCrossentropy() 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): return cross_entropy(tf.ones_like(fake_output), fake_output) 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([32, 100]) 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) 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))
问题解答
1. 方块图案的成因与滤波器尺寸的影响
你看到的方块图案是转置卷积的棋盘格伪影,和2×2小核+同padding+大步长的组合直接相关:
- 当
strides > 1且kernel_size无法被strides整除时,转置卷积会让相邻输出像素由输入的独立区域生成,导致像素间出现不连续的方块边界。 - 2×2核的感受野极小,生成器无法学习全局人脸特征,只能生成局部重复的方块结构;再加上某一层用了
strides=(3,3)这种非2倍数的步长,进一步加剧了像素对齐混乱,伪影更明显。 - 换成5×5核后,更大的感受野能捕捉更连贯的特征,同时5无法被2整除,反而缓解了棋盘格的对齐问题,图像因此更平滑。
2. 滤波器尺寸的选择原则
确定滤波器尺寸需要结合图像尺寸、步长和特征连贯性,核心原则如下:
- 避免棋盘格伪影:使用转置卷积且
strides = s时,优先选kernel_size = 2s或kernel_size > s的尺寸,让输出像素的生成区域有重叠。比如strides=(2,2)时,选4×4或5×5核,不要用2×2核。 - 匹配图像缩放倍数:你的目标是生成48×48图像,但当前生成器最终输出是96×96,和训练数据尺寸错配,先修正生成器的输出维度,确保和判别器输入、训练数据一致。
- 感受野与特征层级:浅层(靠近噪声输入)用3×3、4×4小核捕捉局部细节,深层(靠近输出图像)用5×5稍大核整合全局特征;优先用奇数核,方便padding后特征对齐。
- 参考成熟架构:标准DCGAN的转置卷积层通常用4×4核、2×2步长、same padding,这种组合能有效避免伪影,稳定生成图像。
额外架构修正建议
除滤波器尺寸外,你的模型还有几个关键问题:
- 生成器输出尺寸错误:目标是48×48,但当前生成器最后一层输出96×96,和训练数据尺寸不匹配,导致训练失效。
- 通道数设计不合理:从噪声到图像的过程中,通道数应先增后减,你中间有通道数下降后又上升的情况,不利于特征传递。
- 生成器缺少最终激活:人脸图像像素值通常在[-1,1]或[0,1]之间,最后一层需加
tanh()或sigmoid()激活,否则输出值范围不受控,判别器极易识别。
修正后的生成器示例(目标48×48):
generator = keras.Sequential([ keras.layers.Dense(6*6*128, input_shape=(100,), use_bias=False), keras.layers.BatchNormalization(), keras.layers.LeakyReLU(), keras.layers.Reshape((6, 6, 128)), # 6×6 → 12×12 SpectralNormalization(keras.layers.Conv2DTranspose(64, (4, 4), strides=(2, 2), padding="same", use_bias=False)), keras.layers.BatchNormalization(), keras.layers.LeakyReLU(), # 12×12 → 24×24 SpectralNormalization(keras.layers.Conv2DTranspose(32, (4, 4), strides=(2, 2), padding="same", use_bias=False)), keras.layers.BatchNormalization(), keras.layers.LeakyReLU(), # 24×24 → 48×48 SpectralNormalization(keras.layers.Conv2DTranspose(3, (4, 4), strides=(2, 2), padding="same", use_bias=False)), keras.layers.Activation('tanh') # 添加最终激活函数 ])
内容的提问来源于stack exchange,提问作者A random coder
相关产品推荐
相关产品推荐

