Pix2Pix 生成对抗网络(GAN)是一种用于图像到图像翻译任务的深度卷积神经网络训练方法。
通过将架构精心配置为图像条件式 GAN,与之前的 GAN 模型(例如 256x256 像素)相比,它不仅能够生成大型图像,还能够很好地执行各种不同的图像到图像翻译任务。
在本教程中,您将学习如何开发用于图像到图像翻译的 Pix2Pix 生成对抗网络。
完成本教程后,您将了解:
- 如何加载和准备卫星图像到谷歌地图的图像到图像翻译数据集。
- 如何开发 Pix2Pix 模型用于将卫星照片翻译成谷歌地图图像。
- 如何使用最终的 Pix2Pix 生成器模型来翻译临时卫星图像。
通过我的新书《使用 Python 的生成对抗网络》**启动您的项目**,其中包括**分步教程**和所有示例的 **Python 源代码**文件。
让我们开始吧。
- 2021 年 1 月更新:已更新,以便层冻结与批处理归一化一起使用。

如何开发用于图像到图像翻译的 Pix2Pix 生成对抗网络
照片由 欧洲南方天文台提供,保留部分权利。
教程概述
本教程分为五个部分;它们是:
- 什么是 Pix2Pix GAN?
- 卫星到地图图像翻译数据集
- 如何开发和训练 Pix2Pix 模型
- 如何使用 Pix2Pix 模型翻译图像
- 如何将谷歌地图翻译成卫星图像
什么是 Pix2Pix GAN?
Pix2Pix 是一种生成对抗网络 (GAN) 模型,专为通用图像到图像翻译而设计。
该方法由 Phillip Isola 等人于 2016 年发表的论文《基于条件对抗网络的图像到图像翻译》中提出,并于 2017 年在 CVPR 上展示。
GAN 架构由一个生成器模型(用于输出新的可信合成图像)和一个判别器模型(将图像分类为真实的(来自数据集)或伪造的(生成的))组成。判别器模型直接更新,而生成器模型通过判别器模型更新。因此,这两个模型在对抗过程中同时训练,生成器试图更好地欺骗判别器,判别器则试图更好地识别伪造图像。
Pix2Pix 模型是一种条件 GAN,或称 cGAN,其输出图像的生成以输入为条件,在本例中是一个源图像。判别器同时接收源图像和目标图像,并且必须确定目标是否是源图像的合理转换。
生成器通过对抗性损失进行训练,这鼓励生成器在目标域中生成可信的图像。生成器也通过生成图像和预期输出图像之间测量的 L1 损失进行更新。这种额外的损失鼓励生成器模型创建源图像的合理转换。
Pix2Pix GAN 已在各种图像到图像翻译任务中得到验证,例如将地图转换为卫星照片、将黑白照片着色以及将产品草图转换为产品照片。
现在我们已经熟悉了 Pix2Pix GAN,接下来让我们准备一个可用于图像到图像翻译的数据集。
想从零开始开发GAN吗?
立即参加我为期7天的免费电子邮件速成课程(附示例代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
卫星到地图图像翻译数据集
在本教程中,我们将使用 Pix2Pix 论文中所谓的“地图”数据集。
这是一个包含纽约卫星图像及其对应谷歌地图页面的数据集。图像翻译问题涉及将卫星照片转换为谷歌地图格式,或反之,将谷歌地图图像转换为卫星照片。
该数据集在 pix2pix 网站上提供,可以下载为 255 兆字节的 zip 文件。
下载数据集并将其解压缩到当前工作目录。这将创建一个名为“maps”的目录,其结构如下:
1 2 3 |
地图 ├── train └── val |
训练文件夹包含1,097张图片,而验证数据集包含1,099张图片。
图像以数字文件名存储,采用 JPEG 格式。每张图像宽 1,200 像素,高 600 像素,包含左侧的卫星图像和右侧的 Google 地图图像。

地图数据集中的示例图像,包括卫星图像和谷歌地图图像。
我们可以准备这个数据集,用于在 Keras 中训练 Pix2Pix GAN 模型。我们只处理训练数据集中的图像。每张图像将被加载、重新缩放并分割成卫星图像和谷歌地图图像元素。结果将是 1,097 对彩色图像,宽度和高度均为 256x256 像素。
下面的 `load_images()` 函数实现了这一点。它遍历给定目录中的图像列表,以 256x512 像素的目标大小加载每张图像,将每张图像分割为卫星和地图元素,并返回每个元素的数组。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# 将目录中的所有图像加载到内存中 def load_images(path, size=(256,512)): src_list, tar_list = list(), list() # 列举目录中的文件名,假设所有都是图像 for filename in listdir(path): # 加载并调整图像大小 pixels = load_img(path + filename, target_size=size) # 转换为numpy数组 pixels = img_to_array(pixels) # 分割成卫星图和地图 sat_img, map_img = pixels[:, :256], pixels[:, 256:] src_list.append(sat_img) tar_list.append(map_img) return [asarray(src_list), asarray(tar_list)] |
我们可以使用训练数据集的路径调用此函数。加载后,我们可以将准备好的数组以压缩格式保存到一个新文件中,以备后用。
完整的示例如下所示。
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 |
# 加载、分割和缩放地图数据集以进行训练 from os import listdir from numpy import asarray from numpy import vstack from keras.preprocessing.image import img_to_array from keras.preprocessing.image import load_img from numpy import savez_compressed # 将目录中的所有图像加载到内存中 def load_images(path, size=(256,512)): src_list, tar_list = list(), list() # 列举目录中的文件名,假设所有都是图像 for filename in listdir(path): # 加载并调整图像大小 pixels = load_img(path + filename, target_size=size) # 转换为numpy数组 pixels = img_to_array(pixels) # 分割成卫星图和地图 sat_img, map_img = pixels[:, :256], pixels[:, 256:] src_list.append(sat_img) tar_list.append(map_img) return [asarray(src_list), asarray(tar_list)] # 数据集路径 path = 'maps/train/' # 加载数据集 [src_images, tar_images] = load_images(path) print('Loaded: ', src_images.shape, tar_images.shape) # 保存为压缩的 numpy 数组 filename = 'maps_256.npz' savez_compressed(filename, src_images, tar_images) print('Saved dataset: ', filename) |
运行示例会加载训练数据集中的所有图像,总结它们的形状以确保图像正确加载,然后将数组以压缩的 NumPy 数组格式保存到一个名为 `maps_256.npz` 的新文件中。
1 2 |
已加载: (1096, 256, 256, 3) (1096, 256, 256, 3) 已保存数据集: maps_256.npz |
该文件可以在以后通过 load() NumPy 函数加载,并依次检索每个数组。
然后我们可以绘制一些图像对来确认数据已正确处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# 加载准备好的数据集 from numpy import load from matplotlib import pyplot # 加载数据集 data = load('maps_256.npz') src_images, tar_images = data['arr_0'], data['arr_1'] print('Loaded: ', src_images.shape, tar_images.shape) # 绘制源图像 n_samples = 3 for i in range(n_samples): pyplot.subplot(2, n_samples, 1 + i) pyplot.axis('off') pyplot.imshow(src_images[i].astype('uint8')) # 绘制目标图像 for i in range(n_samples): pyplot.subplot(2, n_samples, 1 + n_samples + i) pyplot.axis('off') pyplot.imshow(tar_images[i].astype('uint8')) pyplot.show() |
运行此示例将加载准备好的数据集并总结每个数组的形状,确认我们对一千多对 256x256 图像的预期。
1 |
已加载: (1096, 256, 256, 3) (1096, 256, 256, 3) |
同时创建了三对图像的图表,显示顶部的卫星图像和底部的谷歌地图图像。
我们可以看到卫星图像相当复杂,虽然谷歌地图图像简单得多,但它们对主要道路、水域和公园等事物有颜色编码。

三对图像的图,显示卫星图像(上)和谷歌地图图像(下)。
现在我们已经为图像翻译准备好了数据集,接下来我们可以开发我们的 Pix2Pix GAN 模型。
如何开发和训练 Pix2Pix 模型
在本节中,我们将开发 Pix2Pix 模型,用于将卫星照片翻译成谷歌地图图像。
论文中描述的模型架构和配置在各种图像翻译任务中都得到了应用。这种架构在论文主体中进行了描述,并在论文附录中提供了额外细节,以及使用 Torch 深度学习框架提供了完整的工作实现作为开源代码。
本节中的实现将使用 Keras 深度学习框架,直接基于论文中描述并作者代码库中实现的模型,旨在接收和生成大小为 256x256 像素的彩色图像。
该架构由两个模型组成:判别器和生成器。
判别器是一个执行图像分类的深度卷积神经网络。具体来说,是条件图像分类。它同时接收源图像(例如卫星照片)和目标图像(例如 Google 地图图像)作为输入,并预测目标图像是真实的还是源图像的虚假翻译的可能性。
判别器的设计基于模型的有效感受野,它定义了模型的一个输出与输入图像中像素数量之间的关系。这被称为 PatchGAN 模型,经过精心设计,使得模型的每个输出预测都映射到输入图像的 70x70 像素块或补丁。这种方法的优点是,同一个模型可以应用于不同大小的输入图像,例如大于或小于 256x256 像素的图像。
模型的输出取决于输入图像的大小,但可能是一个值或值的方形激活图。每个值都是输入图像中某个块为真实的概率。这些值可以平均以给出整体的概率或分类分数(如果需要)。
下面的 `define_discriminator()` 函数根据论文中的模型设计实现了 70x70 PatchGAN 判别器模型。该模型接收两个输入图像,它们被连接在一起并预测一个补丁预测输出。该模型使用二元交叉熵进行优化,并使用权重,以便对模型的更新具有通常一半(0.5)的效果。Pix2Pix 的作者建议在训练期间对模型更新进行此权重调整,以减慢判别器相对于生成器模型的更改。
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 37 38 |
# 定义判别器模型 def define_discriminator(image_shape): # 权重初始化 init = RandomNormal(stddev=0.02) # 源图像输入 in_src_image = Input(shape=image_shape) # 目标图像输入 in_target_image = Input(shape=image_shape) # 沿通道连接图像 merged = Concatenate()([in_src_image, in_target_image]) # C64 d = Conv2D(64, (4,4), strides=(2,2), padding='same', kernel_initializer=init)(merged) d = LeakyReLU(alpha=0.2)(d) # C128 d = Conv2D(128, (4,4), strides=(2,2), padding='same', kernel_initializer=init)(d) d = BatchNormalization()(d) d = LeakyReLU(alpha=0.2)(d) # C256 d = Conv2D(256, (4,4), strides=(2,2), padding='same', kernel_initializer=init)(d) d = BatchNormalization()(d) d = LeakyReLU(alpha=0.2)(d) # C512 d = Conv2D(512, (4,4), strides=(2,2), padding='same', kernel_initializer=init)(d) d = BatchNormalization()(d) d = LeakyReLU(alpha=0.2)(d) # 倒数第二层输出 d = Conv2D(512, (4,4), padding='same', kernel_initializer=init)(d) d = BatchNormalization()(d) d = LeakyReLU(alpha=0.2)(d) # 补丁输出 d = Conv2D(1, (4,4), padding='same', kernel_initializer=init)(d) patch_out = Activation('sigmoid')(d) # 定义模型 model = Model([in_src_image, in_target_image], patch_out) # 编译模型 opt = Adam(lr=0.0002, beta_1=0.5) model.compile(loss='binary_crossentropy', optimizer=opt, loss_weights=[0.5]) return model |
生成器模型比判别器模型更复杂。
生成器是一个使用 U-Net 架构的编码器-解码器模型。该模型接收源图像(例如卫星照片)并生成目标图像(例如 Google 地图图像)。它通过首先将输入图像下采样或编码到瓶颈层,然后将瓶颈表示上采样或解码到输出图像的大小来实现此目的。U-Net 架构意味着在编码层和相应的解码层之间添加了跳跃连接,形成 U 形。
下图清楚地展示了跳跃连接,显示了编码器的第一层如何连接到解码器的最后一层,依此类推。

U-Net 生成器模型架构
摘自《带条件对抗网络的图像到图像翻译》
生成器的编码器和解码器由卷积、批量归一化、Dropout 和激活层的标准化块组成。这种标准化意味着我们可以开发辅助函数来创建每个层块,并重复调用它来构建模型的编码器和解码器部分。
下面的 `define_generator()` 函数实现了 U-Net 编码器-解码器生成器模型。它使用 `define_encoder_block()` 辅助函数为编码器创建层块,并使用 `decoder_block()` 函数为解码器创建层块。输出层使用 tanh 激活函数,这意味着生成图像中的像素值将在 [-1,1] 范围内。
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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# 定义一个编码器块 def define_encoder_block(layer_in, n_filters, batchnorm=True): # 权重初始化 init = RandomNormal(stddev=0.02) # 添加下采样层 g = Conv2D(n_filters, (4,4), strides=(2,2), padding='same', kernel_initializer=init)(layer_in) # 有条件地添加批量归一化 if batchnorm: g = BatchNormalization()(g, training=True) # leaky relu 激活 g = LeakyReLU(alpha=0.2)(g) return g # 定义一个解码器块 def decoder_block(layer_in, skip_in, n_filters, dropout=True): # 权重初始化 init = RandomNormal(stddev=0.02) # 添加上采样层 g = Conv2DTranspose(n_filters, (4,4), strides=(2,2), padding='same', kernel_initializer=init)(layer_in) # 添加批量归一化 g = BatchNormalization()(g, training=True) # 有条件地添加 dropout if dropout: g = Dropout(0.5)(g, training=True) # 与跳跃连接合并 g = Concatenate()([g, skip_in]) # relu 激活 g = Activation('relu')(g) return g # 定义独立的生成器模型 def define_generator(image_shape=(256,256,3)): # 权重初始化 init = RandomNormal(stddev=0.02) # 图像输入 in_image = Input(shape=image_shape) # 编码器模型 e1 = define_encoder_block(in_image, 64, batchnorm=False) e2 = define_encoder_block(e1, 128) e3 = define_encoder_block(e2, 256) e4 = define_encoder_block(e3, 512) e5 = define_encoder_block(e4, 512) e6 = define_encoder_block(e5, 512) e7 = define_encoder_block(e6, 512) # 瓶颈层,无批量归一化和 relu b = Conv2D(512, (4,4), strides=(2,2), padding='same', kernel_initializer=init)(e7) b = Activation('relu')(b) # 解码器模型 d1 = decoder_block(b, e7, 512) d2 = decoder_block(d1, e6, 512) d3 = decoder_block(d2, e5, 512) d4 = decoder_block(d3, e4, 512, dropout=False) d5 = decoder_block(d4, e3, 256, dropout=False) d6 = decoder_block(d5, e2, 128, dropout=False) d7 = decoder_block(d6, e1, 64, dropout=False) # 输出 g = Conv2DTranspose(3, (4,4), strides=(2,2), padding='same', kernel_initializer=init)(d7) out_image = Activation('tanh')(g) # 定义模型 model = Model(in_image, out_image) return model |
判别器模型直接在真实图像和生成图像上进行训练,而生成器模型则不然。
相反,生成器模型通过判别器模型进行训练。它被更新以最小化判别器对标记为“真实”的生成图像预测的损失。因此,它被鼓励生成更多真实图像。生成器还通过最小化生成图像和目标图像之间的 L1 损失或平均绝对误差进行更新。
生成器通过对抗性损失和 L1 损失的加权和进行更新,其中模型作者建议 L1 损失的权重为 100 比 1。这是为了强烈鼓励生成器生成输入图像的合理翻译,而不仅仅是目标域中可信的图像。
这可以通过定义一个由现有独立生成器和判别器模型中的权重组成的新逻辑模型来实现。这个逻辑或复合模型涉及将生成器堆叠在判别器之上。源图像作为输入提供给生成器和判别器,尽管生成器的输出连接到判别器作为相应的“目标”图像。然后,判别器预测生成器是源图像的真实翻译的可能性。
判别器以独立方式更新,因此权重在此复合模型中被重用,但被标记为不可训练。复合模型使用两个目标进行更新,一个表示生成的图像是真实的(交叉熵损失),迫使生成器中产生更大权重更新以生成更真实的图像,另一个是图像的实际真实转换,与生成器模型的输出(L1 损失)进行比较。
下面的 `define_gan()` 函数实现了这一点,它将已定义的生成器和判别器模型作为参数,并使用 Keras 函数式 API 将它们连接成一个复合模型。针对模型的两个输出指定了两个损失函数,并在 `compile()` 函数的 `loss_weights` 参数中指定了每个损失函数使用的权重。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# 定义组合的生成器和判别器模型,用于更新生成器 def define_gan(g_model, d_model, image_shape): # 使判别器中的权重不可训练 for layer in d_model.layers: if not isinstance(layer, BatchNormalization): layer.trainable = False # 定义源图像 in_src = Input(shape=image_shape) # 将源图像连接到生成器输入 gen_out = g_model(in_src) # 将源输入和生成器输出连接到判别器输入 dis_out = d_model([in_src, gen_out]) # 源图像作为输入,生成图像和分类输出 model = Model(in_src, [dis_out, gen_out]) # 编译模型 opt = Adam(lr=0.0002, beta_1=0.5) model.compile(loss=['binary_crossentropy', 'mae'], optimizer=opt, loss_weights=[1,100]) return model |
接下来,我们可以加载我们成对的图像数据集,采用压缩的 NumPy 数组格式。
这将返回两个 NumPy 数组的列表:第一个用于源图像,第二个用于相应的目标图像。
1 2 3 4 5 6 7 8 9 10 |
# 加载并准备训练图像 def load_real_samples(filename): # 加载压缩数组 data = load(filename) # 解包数组 X1, X2 = data['arr_0'], data['arr_1'] # 从 [0,255] 缩放到 [-1,1] X1 = (X1 - 127.5) / 127.5 X2 = (X2 - 127.5) / 127.5 return [X1, X2] |
训练判别器将需要批量的真实图像和伪造图像。
下面的 `generate_real_samples()` 函数将准备一批来自训练数据集的随机图像对,以及相应的判别器标签 `class=1` 以表示它们是真实的。
1 2 3 4 5 6 7 8 9 10 11 |
# 选择一批随机样本,返回图像和目标 def generate_real_samples(dataset, n_samples, patch_shape): # 解包数据集 trainA, trainB = dataset # 选择随机实例 ix = randint(0, trainA.shape[0], n_samples) # 检索选定的图像 X1 = trainA[ix], X2 = trainB[ix] # 生成“真实”类别标签 (1) y = ones((n_samples, patch_shape, patch_shape,1)) return [X1, X2], y |
下面的 `generate_fake_samples()` 函数使用生成器模型和一批真实源图像来为判别器生成一批等效的目标图像。
这些图像与标签类-0 一起返回,以向判别器表明它们是假的。
1 2 3 4 5 6 7 |
# 生成一批图像,返回图像和目标 def generate_fake_samples(g_model, samples, patch_shape): # 生成伪造实例 X = g_model.predict(samples) # 创建“假”类别标签 (0) y = zeros((len(X), patch_shape, patch_shape, 1)) return X, y |
通常,GAN 模型不会收敛;相反,会在生成器和判别器模型之间找到一个平衡点。因此,我们无法轻易判断何时应该停止训练。因此,我们可以保存模型,并在训练期间定期(例如每 10 个训练 epoch)使用它来生成图像到图像的样本翻译。
然后我们可以在训练结束时回顾生成的图像,并根据图像质量选择最终模型。
`summarize_performance()` 函数实现了这一点,它在训练过程中的某个点接收生成器模型,并用它生成数据集中的随机选择图像的一些(在本例中为三个)翻译。然后将源图像、生成的图像和预期目标图像绘制成三行图像,并将图表保存到文件中。此外,模型将保存为 H5 格式的文件,以便以后更容易加载。
图像和模型的文件名都包含训练迭代次数,以便在训练结束时我们能够轻松区分它们。
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 |
# 生成样本并保存为图,并保存模型 def summarize_performance(step, g_model, dataset, n_samples=3): # 选择输入图像样本 [X_realA, X_realB], _ = generate_real_samples(dataset, n_samples, 1) # 生成一批伪造样本 X_fakeB = generate_fake_samples(g_model, X_realA, 1) # 将所有像素从 [-1,1] 缩放到 [0,1] X_realA = (X_realA + 1) / 2.0 X_realB = (X_realB + 1) / 2.0 X_fakeB = (X_fakeB + 1) / 2.0 # 绘制真实源图像 for i in range(n_samples): pyplot.subplot(3, n_samples, 1 + i) pyplot.axis('off') pyplot.imshow(X_realA[i]) # 绘制生成的伪造目标图像 for i in range(n_samples): pyplot.subplot(3, n_samples, 1 + n_samples + i) pyplot.axis('off') pyplot.imshow(X_fakeB[i]) # 绘制真实目标图像 for i in range(n_samples): pyplot.subplot(3, n_samples, 1 + n_samples*2 + i) pyplot.axis('off') pyplot.imshow(X_realB[i]) # 保存图到文件 filename1 = 'plot_%06d.png' % (step+1) pyplot.savefig(filename1) pyplot.close() # 保存生成器模型 filename2 = 'model_%06d.h5' % (step+1) g_model.save(filename2) print('>Saved: %s and %s' % (filename1, filename2)) |
最后,我们可以训练生成器和判别器模型。
下面的 `train()` 函数实现了这一点,它将已定义的生成器、判别器、复合模型和加载的数据集作为输入。为了缩短训练时间,epoch 数量设置为 100,尽管论文中使用了 200。batch_size 设置为 1,这是论文中推荐的。
训练涉及固定数量的训练迭代。训练数据集中有 1,097 张图像。一个 epoch 是对这些示例迭代一次,batch size 为 1 意味着 1,097 个训练步骤。生成器每 10 个 epoch 或每 10,970 个训练步骤保存并评估一次,模型将运行 100 个 epoch,总共 109,700 个训练步骤。
每个训练步骤首先选择一批真实示例,然后使用生成器生成一批匹配的伪造样本,使用真实源图像。然后用一批真实图像和伪造图像更新判别器。
接下来,更新生成器模型,提供真实源图像作为输入,并提供类标签 1(真实)和真实目标图像作为模型计算损失所需的预期输出。生成器有两个损失分数以及从 `train_on_batch()` 调用返回的加权和分数。我们只对加权和分数(返回的第一个值)感兴趣,因为它用于更新模型权重。
最后,每次训练迭代都会向控制台报告每次更新的损失,并每 10 个训练周期评估模型性能。
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 |
# 训练 pix2pix 模型 def train(d_model, g_model, gan_model, dataset, n_epochs=100, n_batch=1): # 确定判别器的输出正方形形状 n_patch = d_model.output_shape[1] # 解包数据集 trainA, trainB = dataset # 计算每个训练 epoch 的批次数量 bat_per_epo = int(len(trainA) / n_batch) # 计算训练迭代次数 n_steps = bat_per_epo * n_epochs # 手动枚举 epoch for i in range(n_steps): # 选择一批真实样本 [X_realA, X_realB], y_real = generate_real_samples(dataset, n_batch, n_patch) # 生成一批伪造样本 X_fakeB, y_fake = generate_fake_samples(g_model, X_realA, n_patch) # 更新判别器以处理真实样本 d_loss1 = d_model.train_on_batch([X_realA, X_realB], y_real) # 更新判别器以处理生成的样本 d_loss2 = d_model.train_on_batch([X_realA, X_fakeB], y_fake) # 更新生成器 g_loss, _, _ = gan_model.train_on_batch(X_realA, [y_real, X_realB]) # 总结性能 print('>%d, d1[%.3f] d2[%.3f] g[%.3f]' % (i+1, d_loss1, d_loss2, g_loss)) # 总结模型性能 if (i+1) % (bat_per_epo * 10) == 0: summarize_performance(i, g_model, dataset) |
综上所述,以下列出了训练 Pix2Pix GAN 以将卫星照片转换为 Google 地图图像的完整代码示例。
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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 |
# pix2pix GAN 用于卫星到地图图像翻译的示例 from numpy import load from numpy import zeros from numpy import ones from numpy.random import randint from keras.optimizers import Adam from keras.initializers import RandomNormal from keras.models import Model from keras.models import Input 从 keras.layers 导入 Conv2D from keras.layers import Conv2DTranspose from keras.layers import LeakyReLU from keras.layers import Activation from keras.layers import Concatenate 从 keras.layers 导入 Dropout 从 keras.层 导入 BatchNormalization from keras.layers import LeakyReLU from matplotlib import pyplot # 定义判别器模型 def define_discriminator(image_shape): # 权重初始化 init = RandomNormal(stddev=0.02) # 源图像输入 in_src_image = Input(shape=image_shape) # 目标图像输入 in_target_image = Input(shape=image_shape) # 沿通道连接图像 merged = Concatenate()([in_src_image, in_target_image]) # C64 d = Conv2D(64, (4,4), strides=(2,2), padding='same', kernel_initializer=init)(merged) d = LeakyReLU(alpha=0.2)(d) # C128 d = Conv2D(128, (4,4), strides=(2,2), padding='same', kernel_initializer=init)(d) d = BatchNormalization()(d) d = LeakyReLU(alpha=0.2)(d) # C256 d = Conv2D(256, (4,4), strides=(2,2), padding='same', kernel_initializer=init)(d) d = BatchNormalization()(d) d = LeakyReLU(alpha=0.2)(d) # C512 d = Conv2D(512, (4,4), strides=(2,2), padding='same', kernel_initializer=init)(d) d = BatchNormalization()(d) d = LeakyReLU(alpha=0.2)(d) # 倒数第二层输出 d = Conv2D(512, (4,4), padding='same', kernel_initializer=init)(d) d = BatchNormalization()(d) d = LeakyReLU(alpha=0.2)(d) # 补丁输出 d = Conv2D(1, (4,4), padding='same', kernel_initializer=init)(d) patch_out = Activation('sigmoid')(d) # 定义模型 model = Model([in_src_image, in_target_image], patch_out) # 编译模型 opt = Adam(lr=0.0002, beta_1=0.5) model.compile(loss='binary_crossentropy', optimizer=opt, loss_weights=[0.5]) return model # 定义一个编码器块 def define_encoder_block(layer_in, n_filters, batchnorm=True): # 权重初始化 init = RandomNormal(stddev=0.02) # 添加下采样层 g = Conv2D(n_filters, (4,4), strides=(2,2), padding='same', kernel_initializer=init)(layer_in) # 有条件地添加批量归一化 if batchnorm: g = BatchNormalization()(g, training=True) # leaky relu 激活 g = LeakyReLU(alpha=0.2)(g) return g # 定义一个解码器块 def decoder_block(layer_in, skip_in, n_filters, dropout=True): # 权重初始化 init = RandomNormal(stddev=0.02) # 添加上采样层 g = Conv2DTranspose(n_filters, (4,4), strides=(2,2), padding='same', kernel_initializer=init)(layer_in) # 添加批量归一化 g = BatchNormalization()(g, training=True) # 有条件地添加 dropout if dropout: g = Dropout(0.5)(g, training=True) # 与跳跃连接合并 g = Concatenate()([g, skip_in]) # relu 激活 g = Activation('relu')(g) return g # 定义独立的生成器模型 def define_generator(image_shape=(256,256,3)): # 权重初始化 init = RandomNormal(stddev=0.02) # 图像输入 in_image = Input(shape=image_shape) # 编码器模型 e1 = define_encoder_block(in_image, 64, batchnorm=False) e2 = define_encoder_block(e1, 128) e3 = define_encoder_block(e2, 256) e4 = define_encoder_block(e3, 512) e5 = define_encoder_block(e4, 512) e6 = define_encoder_block(e5, 512) e7 = define_encoder_block(e6, 512) # 瓶颈层,无批量归一化和 relu b = Conv2D(512, (4,4), strides=(2,2), padding='same', kernel_initializer=init)(e7) b = Activation('relu')(b) # 解码器模型 d1 = decoder_block(b, e7, 512) d2 = decoder_block(d1, e6, 512) d3 = decoder_block(d2, e5, 512) d4 = decoder_block(d3, e4, 512, dropout=False) d5 = decoder_block(d4, e3, 256, dropout=False) d6 = decoder_block(d5, e2, 128, dropout=False) d7 = decoder_block(d6, e1, 64, dropout=False) # 输出 g = Conv2DTranspose(3, (4,4), strides=(2,2), padding='same', kernel_initializer=init)(d7) out_image = Activation('tanh')(g) # 定义模型 model = Model(in_image, out_image) return model # 定义组合的生成器和判别器模型,用于更新生成器 def define_gan(g_model, d_model, image_shape): # 使判别器中的权重不可训练 for layer in d_model.layers: if not isinstance(layer, BatchNormalization): layer.trainable = False # 定义源图像 in_src = Input(shape=image_shape) # 将源图像连接到生成器输入 gen_out = g_model(in_src) # 将源输入和生成器输出连接到判别器输入 dis_out = d_model([in_src, gen_out]) # 源图像作为输入,生成图像和分类输出 model = Model(in_src, [dis_out, gen_out]) # 编译模型 opt = Adam(lr=0.0002, beta_1=0.5) model.compile(loss=['binary_crossentropy', 'mae'], optimizer=opt, loss_weights=[1,100]) return model # 加载并准备训练图像 def load_real_samples(filename): # 加载压缩数组 data = load(filename) # 解包数组 X1, X2 = data['arr_0'], data['arr_1'] # 从 [0,255] 缩放到 [-1,1] X1 = (X1 - 127.5) / 127.5 X2 = (X2 - 127.5) / 127.5 return [X1, X2] # 选择一批随机样本,返回图像和目标 def generate_real_samples(dataset, n_samples, patch_shape): # 解包数据集 trainA, trainB = dataset # 选择随机实例 ix = randint(0, trainA.shape[0], n_samples) # 检索选定的图像 X1 = trainA[ix], X2 = trainB[ix] # 生成“真实”类别标签 (1) y = ones((n_samples, patch_shape, patch_shape,1)) return [X1, X2], y # 生成一批图像,返回图像和目标 def generate_fake_samples(g_model, samples, patch_shape): # 生成伪造实例 X = g_model.predict(samples) # 创建“假”类别标签 (0) y = zeros((len(X), patch_shape, patch_shape, 1)) 返回 X, y # 生成样本并保存为图,并保存模型 def summarize_performance(step, g_model, dataset, n_samples=3): # 选择输入图像样本 [X_realA, X_realB], _ = generate_real_samples(dataset, n_samples, 1) # 生成一批伪造样本 X_fakeB = generate_fake_samples(g_model, X_realA, 1) # 将所有像素从 [-1,1] 缩放到 [0,1] X_realA = (X_realA + 1) / 2.0 X_realB = (X_realB + 1) / 2.0 X_fakeB = (X_fakeB + 1) / 2.0 # 绘制真实源图像 for i in range(n_samples): pyplot.subplot(3, n_samples, 1 + i) pyplot.axis('off') pyplot.imshow(X_realA[i]) # 绘制生成的伪造目标图像 for i in range(n_samples): pyplot.subplot(3, n_samples, 1 + n_samples + i) pyplot.axis('off') pyplot.imshow(X_fakeB[i]) # 绘制真实目标图像 for i in range(n_samples): pyplot.subplot(3, n_samples, 1 + n_samples*2 + i) pyplot.axis('off') pyplot.imshow(X_realB[i]) # 保存图到文件 filename1 = 'plot_%06d.png' % (step+1) pyplot.savefig(filename1) pyplot.close() # 保存生成器模型 filename2 = 'model_%06d.h5' % (step+1) g_model.save(filename2) print('>Saved: %s and %s' % (filename1, filename2)) # 训练 pix2pix 模型 def train(d_model, g_model, gan_model, dataset, n_epochs=100, n_batch=1): # 确定判别器的输出正方形形状 n_patch = d_model.output_shape[1] # 解包数据集 trainA, trainB = dataset # 计算每个训练 epoch 的批次数量 bat_per_epo = int(len(trainA) / n_batch) # 计算训练迭代次数 n_steps = bat_per_epo * n_epochs # 手动枚举 epoch for i in range(n_steps): # 选择一批真实样本 [X_realA, X_realB], y_real = generate_real_samples(dataset, n_batch, n_patch) # 生成一批伪造样本 X_fakeB, y_fake = generate_fake_samples(g_model, X_realA, n_patch) # 更新判别器以处理真实样本 d_loss1 = d_model.train_on_batch([X_realA, X_realB], y_real) # 更新判别器以处理生成的样本 d_loss2 = d_model.train_on_batch([X_realA, X_fakeB], y_fake) # 更新生成器 g_loss, _, _ = gan_model.train_on_batch(X_realA, [y_real, X_realB]) # 总结性能 print('>%d, d1[%.3f] d2[%.3f] g[%.3f]' % (i+1, d_loss1, d_loss2, g_loss)) # 总结模型性能 if (i+1) % (bat_per_epo * 10) == 0: summarize_performance(i, g_model, dataset) # 加载图像数据 dataset = load_real_samples('maps_256.npz') print('加载完成', dataset[0].shape, dataset[1].shape) # 根据加载的数据集定义输入形状 image_shape = dataset[0].shape[1:] # 定义模型 d_model = define_discriminator(image_shape) g_model = define_generator(image_shape) # 定义复合模型 gan_model = define_gan(g_model, d_model, image_shape) # 训练模型 train(d_model, g_model, gan_model, dataset) |
该示例可以在 CPU 硬件上运行,但推荐使用 GPU 硬件。
该示例在现代 GPU 硬件上可能需要大约两小时才能运行。
注意:由于算法或评估过程的随机性,或者数值精度的差异,您的结果可能会有所不同。请考虑运行该示例几次并比较平均结果。
每次训练迭代都会报告损失,包括判别器在真实示例上的损失 (d1)、判别器在生成或虚假示例上的损失 (d2) 和生成器损失,它是对抗性损失和 L1 损失的加权平均值 (g)。
如果判别器的损失降至零并长时间保持不变,请考虑重新启动训练运行,因为这是训练失败的一个示例。
1 2 3 4 5 6 7 8 9 10 11 12 |
>1, d1[0.566] d2[0.520] g[82.266] >2, d1[0.469] d2[0.484] g[66.813] >3, d1[0.428] d2[0.477] g[79.520] >4, d1[0.362] d2[0.405] g[78.143] >5, d1[0.416] d2[0.406] g[72.452] ... >109596, d1[0.303] d2[0.006] g[5.792] >109597, d1[0.001] d2[1.127] g[14.343] >109598, d1[0.000] d2[0.381] g[11.851] >109599, d1[1.289] d2[0.547] g[6.901] >109600, d1[0.437] d2[0.005] g[10.460] >已保存:plot_109600.png 和 model_109600.h5 |
模型每 10 个 epoch 保存一次,并保存到带有训练迭代次数的文件中。此外,图像每 10 个 epoch 生成一次,并与预期的目标图像进行比较。这些图可以在运行结束时进行评估,并用于根据生成的图像质量选择最终的生成器模型。
运行结束时,您将有 10 个保存的模型文件和 10 个生成的图像图。
在前 10 个 epoch 之后,生成的地图图像看起来很合理,尽管街道的线条并不完全笔直,并且图像包含一些模糊。尽管如此,大型结构大部分都位于正确的位置,并且颜色也大致正确。

使用 Pix2Pix 训练 10 个 Epoch 后卫星到 Google 地图翻译图像的图
大约 50 个训练 epoch 后生成的图像开始看起来非常真实,至少对我来说是这样,并且在训练过程的其余部分质量似乎保持良好。
请注意下面第一个生成的图像示例(右列,中间行),它包含比真实 Google 地图图像更有用的细节。

使用 Pix2Pix 训练 100 个 Epoch 后卫星到 Google 地图翻译图像的图
现在我们已经开发并训练了 Pix2Pix 模型,我们可以探索如何以独立的方式使用它们。
如何使用 Pix2Pix 模型翻译图像
训练 Pix2Pix 模型会产生许多保存的模型和每个模型生成的图像样本。
更多的训练 epoch 不一定意味着更好的质量模型。因此,我们可以根据生成的图像质量选择一个模型,并使用它来执行即时图像到图像的翻译。
在这种情况下,我们将使用在运行结束时保存的模型,例如在 100 个 epoch 或 109,600 次训练迭代之后。
一个好的起点是加载模型并使用它对训练数据集中的源图像进行即时翻译。
首先,我们可以加载训练数据集。我们可以使用与训练模型时使用的相同的名为 load_real_samples() 的函数来加载数据集。
1 2 3 4 5 6 7 8 9 10 |
# 加载并准备训练图像 def load_real_samples(filename): # 加载压缩数组 data = load(filename) # 解包数组 X1, X2 = data['arr_0'], data['arr_1'] # 从 [0,255] 缩放到 [-1,1] X1 = (X1 - 127.5) / 127.5 X2 = (X2 - 127.5) / 127.5 return [X1, X2] |
此函数可以如下调用:
1 2 3 4 |
... # 加载数据集 [X1, X2] = load_real_samples('maps_256.npz') print('Loaded', X1.shape, X2.shape) |
接下来,我们可以加载保存的 Keras 模型。
1 2 3 |
... # 加载模型 model = load_model('model_109600.h5') |
接下来,我们可以从训练数据集中选择一个随机图像对作为示例。
1 2 3 4 |
... # 选择随机示例 ix = randint(0, len(X1), 1) src_image, tar_image = X1[ix], X2[ix] |
我们可以将源卫星图像作为模型的输入,并使用它来预测 Google 地图图像。
1 2 3 |
... # 从源生成图像 gen_image = model.predict(src_image) |
最后,我们可以绘制源图像、生成的图像和预期的目标图像。
下面的 plot_images() 函数实现了这一点,在每个图像上方提供了漂亮的标题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# 绘制源、生成和目标图像 def plot_images(src_img, gen_img, tar_img): images = vstack((src_img, gen_img, tar_img)) # 将范围从[-1,1]缩放到[0,1] images = (images + 1) / 2.0 titles = ['Source', 'Generated', 'Expected'] # 逐行绘制图像 for i in range(len(images)): # 定义子图 pyplot.subplot(1, 3, 1 + i) # 关闭轴线 pyplot.axis('off') # 绘制原始像素数据 pyplot.imshow(images[i]) # 显示标题 pyplot.title(titles[i]) pyplot.show() |
此函数可以与我们的每个源图像、生成的图像和目标图像一起调用。
1 2 3 |
... # 绘制所有三个图像 plot_images(src_image, gen_image, tar_image) |
综上所述,下面列出了使用训练数据集中的示例执行即时图像到图像翻译的完整示例。
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 37 38 39 40 41 42 43 44 45 46 47 48 |
# 加载 pix2pix 模型并用于图像到图像翻译的示例 from keras.models import load_model from numpy import load from numpy import vstack from matplotlib import pyplot from numpy.random import randint # 加载并准备训练图像 def load_real_samples(filename): # 加载压缩数组 data = load(filename) # 解包数组 X1, X2 = data['arr_0'], data['arr_1'] # 从 [0,255] 缩放到 [-1,1] X1 = (X1 - 127.5) / 127.5 X2 = (X2 - 127.5) / 127.5 return [X1, X2] # 绘制源、生成和目标图像 def plot_images(src_img, gen_img, tar_img): images = vstack((src_img, gen_img, tar_img)) # 将范围从[-1,1]缩放到[0,1] images = (images + 1) / 2.0 titles = ['Source', 'Generated', 'Expected'] # 逐行绘制图像 for i in range(len(images)): # 定义子图 pyplot.subplot(1, 3, 1 + i) # 关闭轴线 pyplot.axis('off') # 绘制原始像素数据 pyplot.imshow(images[i]) # 显示标题 pyplot.title(titles[i]) pyplot.show() # 加载数据集 [X1, X2] = load_real_samples('maps_256.npz') print('Loaded', X1.shape, X2.shape) # 加载模型 model = load_model('model_109600.h5') # 选择随机示例 ix = randint(0, len(X1), 1) src_image, tar_image = X1[ix], X2[ix] # 从源生成图像 gen_image = model.predict(src_image) # 绘制所有三个图像 plot_images(src_image, gen_image, tar_image) |
运行该示例将从训练数据集中选择一个随机图像,将其翻译成 Google 地图,并绘制结果与预期图像的比较图。
注意:由于算法或评估过程的随机性,或者数值精度的差异,您的结果可能会有所不同。请考虑运行该示例几次并比较平均结果。
在这种情况下,我们可以看到生成的图像捕捉了橙色和黄色的主要道路以及绿色公园区域。生成的图像并不完美,但与预期图像非常接近。

使用最终 Pix2Pix GAN 模型进行卫星到 Google 地图图像翻译的图
我们可能还想使用该模型来翻译给定的独立图像。
我们可以从 maps/val 下的验证数据集中选择一个图像,并裁剪图像的卫星部分。然后可以保存并用作模型的输入。
在这种情况下,我们将使用“maps/val/1.jpg”。

地图数据集验证部分中的示例图像
我们可以使用图像程序粗略裁剪此图像的卫星元素作为输入,并将文件保存为当前工作目录中的 satellite.jpg。

裁剪后的卫星图像示例,用作 Pix2Pix 模型的输入。
我们必须将图像加载为 256×256 大小的 NumPy 像素数组,将像素值重新缩放至 [-1,1] 范围,然后展开单图像维度以表示一个输入样本。
下面的 load_image() 函数实现了这一点,返回可以直接提供给已加载的 Pix2Pix 模型的图像像素。
1 2 3 4 5 6 7 8 9 10 11 |
# 加载图像 def load_image(filename, size=(256,256)): # 以首选大小加载图像 pixels = load_img(filename, target_size=size) # 转换为numpy数组 pixels = img_to_array(pixels) # 从 [0,255] 缩放到 [-1,1] pixels = (pixels - 127.5) / 127.5 # 重塑为 1 个样本 pixels = expand_dims(pixels, 0) return pixels |
然后我们可以加载我们裁剪的卫星图像。
1 2 3 4 |
... # 加载源图像 src_image = load_image('satellite.jpg') print('Loaded', src_image.shape) |
和以前一样,我们可以加载我们保存的 Pix2Pix 生成器模型,并生成已加载图像的翻译。
1 2 3 4 5 |
... # 加载模型 model = load_model('model_109600.h5') # 从源生成图像 gen_image = model.predict(src_image) |
最后,我们可以将像素值缩放回 [0,1] 范围并绘制结果。
1 2 3 4 5 6 7 |
... # 将范围从[-1,1]缩放到[0,1] gen_image = (gen_image + 1) / 2.0 # 绘制图像 pyplot.imshow(gen_image[0]) pyplot.axis('off') pyplot.show() |
综上所述,下面列出了使用单个图像文件执行即时图像翻译的完整示例。
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 |
# 加载 pix2pix 模型并用于一次性图像翻译的示例 from keras.models import load_model from keras.preprocessing.image import img_to_array from keras.preprocessing.image import load_img from numpy import load from numpy import expand_dims from matplotlib import pyplot # 加载图像 def load_image(filename, size=(256,256)): # 以首选大小加载图像 pixels = load_img(filename, target_size=size) # 转换为numpy数组 pixels = img_to_array(pixels) # 从 [0,255] 缩放到 [-1,1] pixels = (pixels - 127.5) / 127.5 # 重塑为 1 个样本 pixels = expand_dims(pixels, 0) return pixels # 加载源图像 src_image = load_image('satellite.jpg') print('Loaded', src_image.shape) # 加载模型 model = load_model('model_109600.h5') # 从源生成图像 gen_image = model.predict(src_image) # 将范围从[-1,1]缩放到[0,1] gen_image = (gen_image + 1) / 2.0 # 绘制图像 pyplot.imshow(gen_image[0]) pyplot.axis('off') pyplot.show() |
运行该示例会从文件中加载图像,创建其翻译,并绘制结果。
生成的图像似乎是源图像的合理翻译。
街道看起来不像是直线,建筑物的细节也有点不足。也许通过进一步训练或选择不同的模型,可以生成更高质量的图像。

将卫星图像翻译成 Google 地图的图,使用最终的 Pix2Pix GAN 模型
如何将谷歌地图翻译成卫星图像
现在我们已经熟悉了如何开发和使用 Pix2Pix 模型将卫星图像翻译成 Google 地图,我们也可以探索反向操作。
也就是说,我们可以开发一个 Pix2Pix 模型,将 Google 地图图像翻译成可信的卫星图像。这需要模型发明或“幻化”出可信的建筑物、道路、公园等。
我们可以使用相同的代码来训练模型,只是有一个小小的不同。我们可以更改从 load_real_samples() 函数返回的数据集的顺序;例如
1 2 3 4 5 6 7 8 9 10 11 |
# 加载并准备训练图像 def load_real_samples(filename): # 加载压缩数组 data = load(filename) # 解包数组 X1, X2 = data['arr_0'], data['arr_1'] # 从 [0,255] 缩放到 [-1,1] X1 = (X1 - 127.5) / 127.5 X2 = (X2 - 127.5) / 127.5 # 以相反顺序返回 return [X2, X1] |
注意:X1 和 X2 的顺序颠倒了。
这意味着模型将把 Google 地图图像作为输入,并学习生成卫星图像。
像以前一样运行该示例。
注意:由于算法或评估过程的随机性,或者数值精度的差异,您的结果可能会有所不同。请考虑运行该示例几次并比较平均结果。
和之前一样,每次训练迭代都会报告模型的损失。如果判别器的损失降至零并长时间保持不变,请考虑重新启动训练运行,因为这是训练失败的一个示例。
1 2 3 4 5 6 7 8 9 10 11 12 |
>1, d1[0.442] d2[0.650] g[49.790] >2, d1[0.317] d2[0.478] g[56.476] >3, d1[0.376] d2[0.450] g[48.114] >4, d1[0.396] d2[0.406] g[62.903] >5, d1[0.496] d2[0.460] g[40.650] ... >109596, d1[0.311] d2[0.057] g[25.376] >109597, d1[0.028] d2[0.070] g[16.618] >109598, d1[0.007] d2[0.208] g[18.139] >109599, d1[0.358] d2[0.076] g[22.494] >109600, d1[0.279] d2[0.049] g[9.941] >已保存:plot_109600.png 和 model_109600.h5 |
判断生成的卫星图像的质量比较困难,但是,仅经过 10 个 epoch 就生成了可信的图像。

使用 Pix2Pix 训练 10 个 Epoch 后 Google 地图到卫星翻译图像的图
和以前一样,图像质量将提高,并将在训练过程中不断变化。最终模型可以根据生成的图像质量而非总训练时期来选择。
该模型似乎在生成合理的水体、公园、道路等方面没有遇到什么困难。

使用 Pix2Pix 训练 90 个 Epoch 后 Google 地图到卫星翻译图像的图
扩展
本节列出了一些您可能希望探索的扩展本教程的想法。
- 独立卫星。开发一个将独立的 Google 地图图像翻译成卫星图像的示例,就像我们将卫星图像翻译成 Google 地图图像一样。
- 新图像。找到一个全新位置的卫星图像,并将其翻译成 Google 地图,然后将其结果与 Google 地图中的实际图像进行比较。
- 更多训练。继续训练模型 100 个 epoch,并评估额外的训练是否能进一步提高图像质量。
- 图像增强。在训练期间使用 Pix2Pix 论文中描述的一些轻微图像增强,并评估它是否能产生更高质量的生成图像。
如果您探索了这些扩展中的任何一个,我很想知道。
请在下面的评论中发布您的发现。
进一步阅读
如果您想深入了解,本节提供了更多关于该主题的资源。
官方
- 使用条件对抗网络进行图像到图像翻译, 2016.
- 使用条件对抗网络进行图像到图像翻译,主页.
- 使用条件对抗网络进行图像到图像翻译,GitHub.
- pytorch-CycleGAN-and-pix2pix,GitHub.
- 交互式图像到图像演示, 2017.
- Pix2Pix 数据集
API
总结
在本教程中,您了解了如何开发用于图像到图像翻译的 Pix2Pix 生成对抗网络。
具体来说,你学到了:
- 如何加载和准备卫星图像到谷歌地图的图像到图像翻译数据集。
- 如何开发 Pix2Pix 模型用于将卫星照片翻译成谷歌地图图像。
- 如何使用最终的 Pix2Pix 生成器模型来翻译临时卫星图像。
你有什么问题吗?
在下面的评论中提出你的问题,我会尽力回答。
很棒的教程。概念和代码都解释得详细而清晰。
谢谢及问候
谢谢!
从数字信号处理的角度来看,加权和是一个可调滤波器。
传统人工神经网络的每一层都有 n 个这样的滤波器,总计算量是一个残酷的 n 平方融合乘法累加。
快速傅里叶变换是一个固定的(不可调的)滤波器组,每个滤波器都能识别频率/相位。
还有其他变换也作为滤波器组,例如快速沃尔什-哈达玛变换,这些通常比加权和的滤波器组需要更少的计算量(例如 nlog(n))。
那么问题是,为什么不使用基于高效变换的滤波器组,并通过单独参数化神经网络中的非线性函数来调整它们呢?
即改变你调整的内容
https://github.com/S6Regen/Fixed-Filter-Bank-Neural-Networks
https://discourse.numenta.org/t/fixed-filter-bank-neural-networks/6392
https://discourse.numenta.org/t/distributed-representations-1984-by-hinton/6378/10
或许可以测试你的替代方法并比较结果,Sean?
在我看来,判别器不是 70x70 的 PatchGAN,因为不应该有第 4 层。有了那一层,判别器似乎是一个 142x142 的 PatchGAN。如果我错了,请纠正我。
我相信你错了。
您可以在此帖子中更详细地了解 70x70 PatchGAN:
https://machinelearning.org.cn/how-to-implement-pix2pix-gan-models-from-scratch-with-keras/
那个示例有相同的结构,6 层 Conv2D(包括最后一层)。但是当我查看帖子开头,您用 5 层 Conv 层计算感受野时。计算也表明只有 3 层 Conv2D,步长为 2。我认为名为 C512 的层应该是倒数第二层。
我相信这个实现与这里描述的官方实现相匹配
https://github.com/phillipi/pix2pix/blob/master/models.lua#L180
嗨 Jason,感谢您的精彩教程。我同意 Villem 的观点,当前的判别器模型是一个 142×142 的 PatchGAN。对于 70x70 的 PatchGAN,我认为它应该只有 3 层,带有 4×4 内核和 2×2 步长(删除 C512)。
如果其他人和我一样有这个疑问,请告诉我。谢谢:)
感谢大家的反馈!
抱歉,链接是什么?这个链接和原来的链接一样。
谢谢您的教程。
我的问题是,在原始论文中,他们将方向作为可配置参数给出。
但在你的实现中,我无法看到这一点。
如何实现双向操作?
请解释
我在上面的教程中展示了如何双向翻译图像。
非常感谢这个精彩的教程!
PS “有 1,097 张图片”……然后每 10970 步保存一次,总共 109700 步
谢谢。
已修复。
感谢您的精彩教程
我们如何使用 GAN 进行运动转移,或者哪种类型的 GAN 最适合运动转移?
抱歉,我不知道,也许可以尝试在 scholar.google.com 上搜索一下
你好,感谢你的精彩文章。
我有一个问题,你为什么将图像缩放到 [-1, 1] 而不是 [0, 1]?
这会使模型表现不同吗?
因为生成器在该范围内生成像素,并且判别器必须“看到”所有图像像素都在相同范围内。
像素选择 -1,1 是 GAN 的一个技巧
https://machinelearning.org.cn/how-to-code-generative-adversarial-network-hacks/
先生,这个模型可以训练不同尺寸的输入和输出吗?
例如,我有 3 张图像 a,b,c,大小为 50x50x3。我希望模型从 a,b 生成 c。首先我将 a 和 b 连接起来得到大小为 50x100x3 的 d,然后使用 d 作为输入,c 作为输出。
是的,您可以使用不同大小的输入和输出,但 U-Net 需要修改。
你能给我一些关于在这种情况下我需要如何修改 U-net 的更多细节吗?我不太熟悉这种纹理
抱歉,我没有能力为您准备定制代码。
或许可以尝试在模型的解码器部分添加/删除层组,看看对图像大小的影响?
我知道你很忙,所以我没有要求定制代码,我只是需要一些入门的东西。谢谢你的建议,先生!
或许可以从定义模型的函数开始,然后尝试玩转它。
你找到解决方案了吗?我正在为同样的问题而苦恼。谢谢
嗨,Jason,
首先,非常感谢您发布此教程,我从中学到了很多。
我有一个问题。
您认为如果我保持图片分辨率不变而不是压缩它们,
性能会更好吗?因为我的图片在翻译之间差异很小。
谢谢!
大卫
谢谢,我很高兴它有帮助。
有趣的想法。你可能指的是使用 TIFF 图像或其他无损格式?
可能不会,但或许可以测试一下以确认。
你好,
如何提高训练速度?它只使用了很少的 GPU 内存。
一些想法
减少数据量。
使用更小的模型。
使用更快的机器。
我正在使用一台拥有 8 个 GPU(8 X p4000)的机器 🙂
我的意思是,例如,在 darknet 上训练时,更改批量大小会直接影响 GPU 内存使用。但是这些代码每个 GPU 只使用 100 MB。并且批量大小不影响它。所以我需要像 darknet 那样进行调整,以便我可以使用 GPU 的全部能力。
谢谢
我明白了,抱歉我帮不上忙。
将训练数据替换为 SEN1-2 数据集取得了惊人的结果。我现在可以将 Sentinel 1 图像转换为 RGB Sentinel 2!非常感谢您如此详细的教程。
干得好!
我很想看看翻译后的图像示例。
嗨,Jason,
非常感谢您的精彩网站,太棒了。
我想知道您对这个研究领域未来发展方向的看法是什么?
谢谢
谢谢。
抱歉,我对研究方向没什么想法——这些天我尽量专注于工业方面。
关于 Pix2Pix 的精彩教程。您其他的 GAN 文章也很棒,非常有帮助。阅读您的教程后,我能够实现我自己的 Pix2Pix 项目。所有代码都在我的 GitHub 上。https://github.com/michaelnation26/pix2pix-edges-with-color
谢谢。
干得好,太棒了!
– python 版本:3.6.7
– tensorflow-gpu 版本:2.0.0
– keras 版本:2.3.1
– cuDNN 版本:10.0
– CUDA 版本:10.0
mnist_mlp.py (https://github.com/keras-team/keras/blob/master/examples/mnist_mlp.py) 完美运行,但下面给出的代码给我以下错误
tensorflow.python.framework.errors_impl.UnknownError: 无法获取卷积算法。这可能是因为 cuDNN 初始化失败,所以请查看上面是否打印了警告日志消息。
[[节点 conv2d_7/convolution (在 C:\Users\ACSECKIN\Anaconda3\envs\tensorflow\lib\site-packages\tensorflow_core\python\framework\ops.py:1751 中定义)]] [Op:__inference_keras_scratch_graph_4815]
函数调用栈:
keras_scratch_graph
或许可以先在 CPU 上运行,以确认您的环境正在工作?
代码在 CPU 上运行正常。但我希望在 GPU 上运行以缩短时间。CPU 时间大约是 16 小时。我愿意接受任何替代方案来减少训练时间。
这很奇怪。我在 GPU 上运行这些示例没有发生意外,特别是在 EC2 上。
也许你可以试试看?
根据我的经验,当我的 GPU 没有足够的内存来处理该过程时,我会收到此错误消息。也许尝试通过使用较小的图像来减少计算工作量?如果不行,如果您觉得可以,请使用 CPU。
很棒的建议!
大家好,
在 GPU(Floydhub)上训练模型需要 8 小时。
但在此过程中保存了几个不同的模型。
你能解释一下为什么吗?
或许可以尝试在一个大型 EC2 实例上进行训练,它会快得多。
模型在训练期间会定期保存,我们无法在不使用它生成图像的情况下知道哪个模型是好的。
谢谢您的提示!
我以为更多的 epoch 会带来更好的结果,因为 GAN 无法收敛……所以理论上不会过拟合……但我刚入行,所以我会多加研究:)
GANs 不是这样的。或许从这里开始:
https://machinelearning.org.cn/start-here/#gans
感谢您的精彩文章,它非常详细和有用!我有一个关于将 [0, 255] 归一化到 [-1, 1] 的问题。我的图像是单通道的,每个图像的最大和最小像素值各不相同,从 0 到 3-4 左右(取决于图像)。我应该如何归一化图像?我应该取整个批次样本的最大值并进行归一化,还是应该取每个样本的最大值并单独归一化每个样本?
另外,在翻译新图像时,图像的值会是多少?会在 -1 到 1 之间吗?如果是,我应该如何将这些值“反归一化”回原始值?谢谢您的帮助!
谢谢。
我建议选择一个您期望始终看到的最小值和最大值,并用它来缩放数据。如果不知道或无法知道,请使用域的限制(例如 0 和 255)。
感谢您的建议!您建议我如何在测试期间“反归一化”数据?我应该使用相同的范围(我正在从训练数据中获取范围)并反转测试数据上的过程吗?
是的。
嗨,Jason,
非常感谢您分享 Pix2Pix GAN 如此深入的分析。它对像我这样没有 CS 背景的早期职业研究人员非常有帮助。我曾想过将其应用于解决数字全息显微镜中的逆问题,现在我对初步结果感到好奇。如您所知,模型的输出是翻译后的图像,因此无法计算模型精度。我正在寻找像 SSIM 这样的图像质量指标。您有什么建议吗?
谢谢,
PS:由于这篇文章对我有很大帮助,我将来会引用您的 GANs 作品。
不客气。
听起来很酷!也许这里的一些指标会有帮助
https://machinelearning.org.cn/how-to-evaluate-generative-adversarial-networks/
嗨!目前我正在使用形状为 (256, 512, 3) 的图像来实现,并且一直遇到以下错误:
“ValueError: 传入了一个形状为 (1, 16, 16, 1) 的目标数组,而输出的形状为 (None, 16, 32, 1),并且使用
binary_crossentropy
作为损失。此损失期望目标的形状与输出相同。”我猜测这是由于下采样引起的?任何帮助都将不胜感激
也许可以从方形图像开始,让它工作起来,然后再尝试更改为矩形图像?
嗯,好吧!你能解释一下为什么你使用 target_size=(256, 512) 而不是 (256, 256) 吗?
图像大小是 256x512,因为它们包含输入和输出图像。
我们加载它们,然后在使用 GAN 时将它们分成两个独立的 256x256 方形图像。
判别器错误似乎下降得很快,有什么办法可以避免这种情况吗?
或许可以尝试多次运行示例并继续运行——它可能会恢复。
感谢您提供本教程和简洁的代码。我用它将柯本气候分类图(https://en.wikipedia.org/wiki/K%C3%B6ppen_climate_classification)翻译成真实的卫星数据,结果令人惊叹,但我有一个问题。
在我的策略中,我从柯本气候分类图(维基百科文章中列出)和一张地球高分辨率卫星地图中创建了近一千对 256×256 的瓦片。为了最大限度地减少两极附近瓦片对的变形,我使用了正射投影。这给我带来了很好的 GAN 训练图像对(参见https://photos.app.goo.gl/eGvpXghUtCB9kqkX6)。
我训练了 GAN 直到结束 (n_epochs=100),结果令人惊叹。使用训练数据得到了真正令人信服的卫星地图验证 (https://photos.app.goo.gl/a4EV6Gh15hAnYokm7)。即使是手绘的或从随机图像转换成柯本气候配色方案的源图像,结果也非常漂亮 (https://photos.app.goo.gl/eGbFmTH7YqYi4xfu5)。
然而,我注意到结果缺乏“浮雕”效果。此外,在气候不变但地形显著影响卫星视图的大陆块上(例如青藏高原或大峡谷),模型会生成“扁平”的卫星视图。
由于气候图仅由 29 种不同的索引颜色(加上我为海洋添加的一种)组成,因此可以使用简单的标签到图像翻译,而不是使用完整的 RGB 气候图像作为输入。
所以我的想法是将地球的高程图存储在输入图像的第一个通道中,并将归一化的索引气候颜色存储在第二个通道中。第三个通道保持未使用状态。这导致了一个红绿图像,其中红色通道是高程图,绿色通道是归一化的气候索引(参见https://photos.app.goo.gl/cN1cmCNLSXwwqzNB9)。
问题是,与我的第一次尝试(仅气候日期)相比,使用这种输入图像进行训练得到了糟糕的结果。在我第一次尝试中,经过 30 个 epoch 之后结果已经令人信服,气候之间过渡平滑,而在这里,生成的图像中边界清晰可见(参见https://photos.app.goo.gl/Q1vjjeY8ewWrCZYv5)。
我尝试运行训练几次,以确保这并非纯粹的运气不佳,结果相同。
我不明白,因为气候指数可以清楚地存储在一个通道上而不会丢失信息,而且高度图提供了额外的数据,所以它应该会改善结果。是不是仅仅因为它需要更多的训练周期?
提前感谢您的帮助,抱歉帖子这么长,也抱歉我的英语不好,它不是我的母语。
干得好!
非常酷的应用。
随便说两点。一个是让图像合成以两张输入图像(源图像和高度图)为条件。第二个是分两步进行——一步生成初始转换,另一步用高度图改进生成的图像。
我很期待听到您的进展!
非常感谢!目标是开发一个用于世界构建的工具,并创建虚构星球的逼真地图(遵循 Atrifexian 的教程 https://www.youtube.com/watch?v=5lCbxMZJ4zA&t=1s )。
我根据这个关于 pytorch 实现的帖子使用了 R 和 G 通道来表示高度图和气候的想法: https://github.com/junyanz/pytorch-CycleGAN-and-pix2pix/issues/498 。他们建议拼接输入图像,但似乎您的代码限制为 3 个通道,而且我是一个完全的初学者,我仍然不知道如何使用多于一张图像作为输入。
然而,我的方法似乎在更多的训练周期中确实能产生好的结果。也许 100 个训练周期不够,所以我将其重新启动,限制为 1000 个训练周期。不过,我必须重做前 100 个,而且我在 Google Colab 上运行代码,这似乎非常不稳定(我只成功完成了两次 100 个训练周期)。
您有关于如何创建完整检查点以便在崩溃时继续训练的教程吗?如果我理解得对,您的 summarize_performance 函数只保存了生成器模型,所以我们应该保存整个 gan_model 并重新加载它以供后续训练。您有关于这方面的文档或示例吗?
非常感谢您的教程。我会及时向您通报后续进展!
是的,以上代码已经每隔 n 步保存模型。
请参阅 summarize_performance() 函数。
如果我的图像包含白色背景,我应该以同样的方式处理吗?类似于 edges2shoes 数据集?
也许可以先用原型测试一下?
不知为何,我最终只得到了空白的白色图像……
你好,
感谢精彩的教程,请问我如何调整生成器和鉴别器,以实现从矩阵 (2,64) 到矩阵 (2,64) 的转换?
抱歉,我没跟上。
感谢 Jason 的精彩文章!
我试过这段代码,但图像效果不够好,并且判别器损失在 10-15 个 epoch 后变为 0。
也许可以尝试使用不同的最终模型或重新训练模型?
我想从上次存储的检查点继续训练。您能帮助我从上次检查点恢复模型训练吗?
是的,像往常一样加载模型并调用 train() 函数。
我有一个包含 216 张图像的数据集。我训练了 100 个 epoch,但结果不幸不佳。您能帮我如何改进结果吗?
是的,试试这些建议中的一些
https://machinelearning.org.cn/how-to-code-generative-adversarial-network-hacks/
感谢这篇精彩的教程!它非常有帮助。我想知道您是否考虑过数据增强?
对于 GANs 来说,一般不会,但对于其他情况,是的。
https://machinelearning.org.cn/how-to-configure-image-data-augmentation-when-training-deep-learning-neural-networks/
谢谢!
这可能是一个新手问题,但我对 GAN 还不熟悉。在我有限的深度 CNN 经验中,我会在训练过程中使用验证数据来评估它“学习”得如何。然后我有一个叫做“测试”数据集的另一个数据集,在训练过程完成后使用。这里似乎您在训练过程中没有使用任何验证。而您所说的验证就是我所说的测试数据集。这是 GAN 特有的吗,还是可以在训练过程中包含验证?
不是,我经常使用测试集进行验证以简化教程。
https://machinelearning.org.cn/faq/single-faq/why-do-you-use-the-test-dataset-as-the-validation-dataset
嗨!我在 Titan V 上运行,它似乎运行得非常慢。有什么原因吗?
训练 GANs 很慢……
也许检查一下你的内存和显存是否足够?
也许可以与具有大量内存的 ec2 上的结果进行比较?
也许调整模型配置以减少内存使用?
我建议使用谷歌 Colab。你可以用他们的 GPU 进行训练。速度快得多!
在使用 GANs 时,GPU 几乎是必需的。
先生,我能知道您是如何下载卫星图像和地图的吗?您能帮我为这个项目下载我自己的数据集吗?
请参阅上述教程中“卫星到地图图像转换数据集”部分,了解如何下载数据集。
嗨,Jason,
从理论上讲,我们知道判别器学习目标损失函数。然而,参照 define_GAN() 函数代码中的第 56 行,我无法看到判别器学习到的目标损失被传递给 GAN 模型。我发现模型没有像预期的那样收敛。
此致,
Vaibhav Kotwal
我们创建了一个结合 G 和 D 的复合模型,以便通过对 D 的反向修正来训练 G。
也许看看这个
https://machinelearning.org.cn/how-to-code-the-generative-adversarial-network-training-algorithm-and-loss-functions/
感谢这篇精彩的教程。我需要一些帮助。我希望在整个训练过程中看到训练集和测试集的准确度指标。并且我希望为每个 epoch(而不是每个步骤)看到这些指标。就像标准的 CNN 模型训练过程一样。我如何将这些内容添加到代码中?我无法应用它,因为它与标准 CNN 代码不同。如果您能回答,我将不胜感激。谢谢。
准确度对于 GANs 来说是一个糟糕的衡量指标。
看这里
https://machinelearning.org.cn/how-to-evaluate-generative-adversarial-networks/
Pix2pix GAN 能否保存并再次加载而无需重新训练?
是的,请参阅此教程获取示例
https://machinelearning.org.cn/how-to-develop-a-pix2pix-gan-for-image-to-image-translation/
是的,感谢您的回复!!我想问另一个问题!!其他类型的 GAN 版本可以保存和加载吗?
是的,它们都是 Keras 模型,可以保存和加载。
嗨,Jason,
感谢这篇精彩的教程。我有一个图像比例的问题。在第一步,分割输入图像后,我检查了图像大小,它们是 134*139 带背景的,而不是 256*256 像素。此外,在通过模型转换给定独立图像的步骤中,输出应该是 256*256,与输入相同,但我再次得到 252*262 的带背景输出。
我想知道您是否介意告诉我问题出在哪里?
提前感谢
Ehsan
我不知道你的故障原因。抱歉。
Jason,很棒的工作。就一个问题:您认为这种方法是否可以使用 RGB 卫星图像与它的掩码图像进行某种图像分割?
提前感谢
也许可以试试看。原型是快速获取答案的方法。
我的意思是,掩码图像只有两种颜色(是/否)……这是我的担忧。谢谢
您可以在掩码图像中添加两个“虚拟”层,使其与 RGB 源图像作为目标图像兼容。您的 RGB 图像作为 numpy 数组将呈 (图像数量, 宽度, 高度, 波段数量) 的形状,其中波段数量为三。您的掩码图像将呈 (图像数量, 宽度, 高度, 波段数量) 的形状,其中波段数量为一。因此,如果您在掩码图像中添加两个波段,例如只包含 -1 值,那么它们就兼容了。
这里发表了一篇有趣的文章。我刚开始使用它,想问一些关于第一个脚本的问题以获得清晰的解释。
1. 我看到加载的数据是训练和测试文件夹中的地图。我想知道从训练文件夹中加载了哪 3 个样本?因为结果是:(1096, 256, 256, 3) (1096, 256, 256, 3)。我理解 1096 是该文件夹中图像的特定数量。而 256 我仍然不明白,因为当我打开图片时,256 与加载时的图像大小不同。
2. 我看到文件夹在训练和测试中包含图像。我想问一下训练,例如 1.jpg 它包含两张卫星图像。我能知道左右两张图片是如何开发的,还是它自己开发的?同样在测试中,它是自己开发的还是必须先保存它?
需要一些关于将来如何使用它的解释。谢谢。
图像是 256x256 像素的正方形,有 3 个颜色通道。你可以在这里了解更多关于加载图像的信息
https://machinelearning.org.cn/how-to-load-and-manipulate-images-for-deep-learning-in-python-with-pil-pillow/
抱歉,我没跟上你的第二个问题,也许你能详细说明一下?
感谢您的回复。关于我的第二个问题,我看到训练和测试文件夹中包含一张图像。所以问题来了
1. 图像是自己构建的吗?
2. 如果不是,您是如何将两张图像并排放在一张图像中的?
非常感谢。
本教程展示了如何加载图像并为建模做好准备。
嗨,谢谢你的工作!
我需要你的帮助,我需要相同的模型,但生成器的输入是一个通道而不是三个。.
我试着修改它但没有成功。谢谢。
听到这个消息我很难过。
或许可以确认您的图像是灰度(1 个通道),然后通过输入形状将模型更改为预期 1 个通道。
你好!我想自己训练。我按照本教程准备好了它们。尺寸 X1 和 X2 相同。数据显示正常。但我收到这个错误
“Got inputs shapes: %s” % (input_shape))
ValueError:
Concatenate
层需要输入具有除连接轴之外的匹配形状。获取输入形状:[(None, 2, 2, 512), (None, 1, 1, 512)]我哪里做错了?
抱歉,我不知道。
你解决这个问题了吗?我遇到了同样的问题。
这可能是因为您的输入大小不能被 256 整除。
你好,我有这个问题,我不知道为什么
ValueError: Graph disconnected: cannot obtain value for tensor Tensor(“input_14:0”, shape=(?, 256, 256, 3), dtype=float32) at layer “input_14”. The following previous layers were accessed without issue: [‘input_15’]
或许可以确认您的 keras 和 tensorflow 版本已更新?
你好。我尝试使用这段代码处理不同的数据集。特别是 edges2shoes 数据集,但我无法将其转换为 npz 文件。每次都遇到内存错误。我的内存是 16GB,仍然不够。不过我成功创建了多个 npz 文件。我该如何继续?
另外,您能否好心地制作一个关于 Pix2pixHD 的 TensorFlow/Keras 教程,因为它比普通的 Pix2pix 更准确,并且在并排测试中表现更好。
也许可以使用部分数据样本?
或许可以采用渐进式加载?
或许可以使用拥有更多内存的 ec2 实例?
感谢您的建议。
谢谢,我想你的这个教程 https://machinelearning.org.cn/how-to-load-large-datasets-from-directories-for-deep-learning-with-keras/ 在我的情况下会有所帮助。
我正在使用自己的机器,而不是 AWS 实例。
太棒了!
嗨,Jason。很棒的文章。解释得很好。您的文章在我开始开发自己的网络时给了我很好的概述和起点。但我有两个问题
1:) 从我所看到的,GitHub 上的原始代码与本文中的代码在编码器和解码器层的连接方式上似乎略有不同。在 GitHub 上,从编码器层传递到解码器层(通过跳跃连接)的数据是未激活的,这意味着数据在卷积(或批量归一化/dropout)之后直接传递,这与此处的解决方案相反。这是错误还是变体?
2.) 在调用批量归一化层或 dropout 层时,'training=True' 标志有什么作用?
提前感谢。
或许。我以为我已经根据论文和发布的代码准确地搭建了架构。
Training=True 使层始终认为自己处于训练模型中。例如,通常批量归一化和 dropout 在训练和推理中的操作方式不同。更多信息请参见此处
https://machinelearning.org.cn/how-to-implement-pix2pix-gan-models-from-scratch-with-keras/
嗨,Jason,
非常感谢这个教程,它太棒了!
我遇到了您在文章末尾提到的问题。D1 损失在 80-90 步后趋于零。您能解释一下为什么会这样以及如何解决它吗?
除此之外,我可以看到每次迭代只使用一张图像(一张真实的,一张假的),其中 n_batch = 1。我们不应该在每个步骤中使用多于一对图像进行训练吗?
# 选择一批真实样本
[X_realA, X_realB], y_real = generate_real_samples(dataset, n_batch, n_patch)
非常感谢!
是的,这可能是一个故障模式
https://machinelearning.org.cn/practical-guide-to-gan-failure-modes/
您可以尝试再次拟合相同的模型,在此过程中将模型保存到文件中。
您可以尝试调整模型架构或训练算法
https://machinelearning.org.cn/how-to-code-generative-adversarial-network-hacks/
所以,这个例子中的模型不会正常工作吗?这个链接不是 pix2pix 架构。我也尝试过这个例子,它在 cGAN 和 fminst 的 GAN 上完美运行,但问题出在这个 Pix2pix 架构上。
这个例子确实可以正确运行,但你可能需要训练几次。
GANs 本质上是不稳定的。
我看到您的书中关于这个有特定的代码。有没有更完整的 pix2pix 示例?还是和这个一样?
书中的 pix2pix 示例基于此示例。
祝您长命百岁……精彩的教程,解释清晰。
谢谢!
有没有办法在训练期间使用 keras 数据加载器/生成器进行即时图像加载,例如,您的训练数据量非常大,一次性加载会导致内存不足错误?非常感谢您的所有教程,它们太棒了!
是的,请看这个
https://machinelearning.org.cn/how-to-load-large-datasets-from-directories-for-deep-learning-with-keras/
先生,您好,
首先,我阅读了你所有的教程。你对我比我的顾问帮助更大。非常感谢。
我是 Gans 新手。抱歉我的问题。但我无法理解如何测试这个模型?
我将使用验证集,好的。
训练模型后,我不想给出目标,只给出源图像吗?我搞不清楚。
或者我只加载模型并用验证集进行训练?
天哪,我无法解释清楚自己。希望您能理解我。
不客气!
评估 GANs 具有挑战性。我们不使用验证集。相反,我们生成图像并观察它们,看看它们是否足够好。
谢谢您的回答,先生,
我想尝试 pix2pix GANs 进行图像增强。
我将使用低对比度作为源图像,高对比度作为目标图像,您觉得呢?我正在尝试改进热像图。
我希望这个系统能奏效。
听起来很棒!
在判别器中,为什么需要拼接源图像和目标图像?它会有什么影响?
抱歉,您指的是哪一部分?我们是在哪里(哪一部分/哪一行?)拼接图像的?
在判别器中,我们正在拼接源图像和目标图像。
实际上,我正在构建一个 GAN 模型,用于从灰度到 RGB 的颜色转换。
我的判别器和生成器模型的损失都降到零了。所以想知道拼接对判别器有什么特殊影响。如果您对我的模型有什么建议,也请告诉我。
提前致谢……
我们正在训练一个条件模型,例如,根据源图像生成目标图像。
例如,这是模型的目的。
你好。我比较了 summarize_performance() 中的图像和对未见过图像的预测,结果非常糟糕。您能建议一些解决这个问题的方法吗?
尝试多次训练模型,每次运行中多次保存,选择一个能生成良好图像的模型。
你好,
我如何使用生成器模型来预测任意大小的图像?我的意思是不只是正方形大小。有没有任何方法可以做到这一点?
您需要更改生成器/判别器以及训练数据集到所需的尺寸。
感谢您的精彩帖子。如果有两个问题您能回答,请告诉我。
第一个问题,您提到了
“在这种情况下,我们将使用在运行结束时保存的模型,例如,在 10 个 epoch 或 109,600 次训练迭代之后。”
在 10 个 epoch 之后,训练迭代不应该是 10,960 次吗?
第二个问题,使用随机索引生成真实和虚假样本的原理是什么?为什么我们不能简单地逐个遍历所有样本,以确保在 1 个训练步骤中没有图像被遗漏或多次使用?
图像每 10 个 epoch 生成一次,共运行 100 个 epoch,这意味着我们沿途保存了 10 个模型。是的,这是一个错别字,我们使用了 100 个 epoch 后的模型。已更正。
我们可以为所有图像这样做,我希望处理一张图像,以展示我们可以随意使用模型。读者经常觉得这一步令人困惑,所以我必须演示它。
嗨!
感谢您的精彩教程,您帮了我很多!
我只有一个问题:我是哪里做错了,还是对于一个 X 输入,我没有唯一的 Y 输出是正常的?
让我解释得更清楚一些:如果我重复预测 n 次,我就会得到 n 个不同的 Y 图像(我正在检查像素差异)。
我正在将这个例子转换到另一个应用程序,并且每次都得到完全相同的输出会使它正常工作。
我尝试寻找随机噪声向量之类的东西,但似乎情况并非如此。
请记住,我们正在使用 GAN,因此我们有两个模型,第一个模型预测输入图像是真实的还是假的,第二个模型根据另一张图像生成图像。
如果您是 GANs 新手,或许可以从这里开始
https://machinelearning.org.cn/what-are-generative-adversarial-networks-gans/
编辑
“我正在将这个例子转换到另一个应用程序,并且每次都得到完全相同的输出会使它正常工作” *
嗨!
这个教程帮了我大忙!
我有一个问题:如果我用相同的输入重复预测 n 次,得到 n 个不同的输出,这是正常的吗?
我直接检查 Y 输出中像素值的变化。
是的,考虑到某些层的随机性,这是预期的。
谢谢您的回复!
我猜 dropout 引入了随机性,我会尝试不用 dropout。
是的,还有一些层直接注入噪声。
抱歉,如果我显得很烦人,但我没有在您提供的代码中看到可以直接引入噪声的层。
我看到的是卷积、批量归一化和拼接。
您能告诉我哪个层直接引入了噪声吗?
我觉得我可能错过了这些层的一些东西,但是通过阅读文档,我似乎对它们非常了解。
我真的需要了解这些噪声生成器的位置并将其删除,以便将 GAN 用于我的应用程序(也许这不可能,但我希望尝试 =))
抱歉,我错了,我以为是另一种 GAN。
感谢这篇很棒的教程。
我有一个小小的疑问
我们是否遍历了整个数据集?
我们将整个数据集传递给了 generate_real_samples 函数,它每次都会选择一个随机数,如果我们反复遍历,这个数可能相同。
所以,我们可能在一个 epoch 中没有遍历整个数据集?
请告诉我您的想法。
谢谢。
不客气。
正确。平均而言,我们多次覆盖整个数据集。
因此,存在遗漏某些数据点的可能性。如果您拥有的数据点很少,这可能会成为一个问题。
那么我应该修改代码以确保它遍历整个数据,还是即使不这样做也仍然可以?
如果你愿意的话。我不相信这会有什么不同,但这可能是一个有趣的实验。
哦,好的,没问题!
我想我将研究通过不同的卷积和批量归一化引入的随机性,以使网络能够从 X 输入预测相同的 Y。
此致
非常感谢您的超级清晰的解释和代码。
不客气!
如果我想使用三个颜色通道的输入和四个颜色通道的目标,这可以配置吗?还是最好在输入上创建一个额外的黑色第四通道?
我注意到一些灰度到彩色模型只是在每个通道中使用相同的数据来表示灰度图像,所以我觉得这样做比让模型处理不同数量的通道要容易得多。
另外,感谢您的优秀资源!
我记得图像必须具有相同数量的通道。或许可以尝试/研究一下,看看是否可以偏离这个标准。
感谢这篇精彩的文章!
对于生成器的损失,我如何知道您是在最小化 1: log(1 – D(G(x))) 还是最大化 2: log D(G(x)))?
如何更改损失函数,有什么阅读建议吗?
有些人说选择生成器的损失函数可以帮助模型避免在训练的早期阶段陷入困境。
好问题,这可能会帮助您理解 GAN 的损失函数
https://machinelearning.org.cn/how-to-code-the-generative-adversarial-network-training-algorithm-and-loss-functions/
你可以在这里的教程中看到替代损失函数的例子
https://machinelearning.org.cn/start-here/#gans
你好 Jason,
一个成功的条件 GAN 模型的最优损失值(生成器和判别器损失)是多少?这些值与无条件 GAN 相同吗?(即大约 0.7 或 0.6,如您在无条件 GAN 文章中提到的)
其次,我以两种不同的方式对 pix2pix 进行了一定的图像到图像转换任务训练。
第一种方法:根据真实或虚假标签矩阵训练判别器补丁结果(如本文所述)
第二种方法:判别器仍然给出补丁,但这次取补丁平均值,并根据单个值进行训练(即补丁的平均值与真实或虚假标签)。
在训练过程中(为了保存一个好的模型),第一种方法对于真实图像对产生大约 0.4 的补丁平均值,对于虚假图像对产生大约 0.3 的补丁平均值。
但第二种模型,对于真实和虚假图像对都产生大约 0.0004 的补丁平均值。
这两种模型都通过其生成器产生了高质量的图像,判别器损失分别约为 0.7 和 0.6。我的疑问是,为什么补丁平均值有如此大的差异,尽管两种模型都产生了高质量的图像?其次,0.0004 的补丁平均值没有意义,即使该模型产生了高质量的翻译图像。(因为据我理解,真实对的补丁中每个像素值应该接近 1,虚假图像对应该接近 0。这意味着补丁的平均值也应该接近 1 代表真实对,接近 0 代表虚假图像对)。
一个好的模型的补丁平均值应该是什么?任何见解都会非常有帮助。希望我表达清楚了。
谢谢!
GANs 不会收敛,所以没有最优的损失值,你可以在这里了解更多。
https://machinelearning.org.cn/faq/single-faq/why-is-my-gan-not-converging
模型/运行之间的损失比较是无效的。
GANs 很难评估,主观图像质量是我们能做到的最好方法,尽管这里描述了一些指标可能会有所帮助
https://machinelearning.org.cn/how-to-evaluate-generative-adversarial-networks/
先生,我们为什么要将两张图像合并到判别器中?它有什么作用?为什么我们不只在判别器中保留彩色图像?
判别器被给予输入图像和目标图像,并对目标是真实转换还是生成转换进行评论。
PIX2PIX GAN 可以用于灰度图像吗?
如果我们对灰度源图像和目标图像使用上述相同的架构会怎么样?
需要修改模型架构。
我使用了不同的源图像和目标图像数据。我的源图像和目标图像都是灰度的。
但是当我运行代码时,判别器损失在很少的迭代中就趋于零,但生成器损失却非常高,达到了 9782.150 左右。
它不能减少……我该怎么办?
GAN 损失不会收敛,你可以在这里了解更多信息。
https://machinelearning.org.cn/faq/single-faq/why-is-my-gan-not-converging
我有不同的源图像和目标图像。我的源图像和目标图像都是灰度的,但是我的判别器损失非常低,趋近于零,而生成器损失非常高。
我现在该怎么办?Pix2Pix GAN 可以用于灰度图像吗?
您可能需要调整模型——进行探索——以发现如何最好地修改模型架构以支持灰度图像。
您认为需要进行什么样的修改?比如架构不会是 U 形的吗?还是需要更改损失函数?
很难说——需要进行实验,也许可以从调整学习率开始,使用一个类似的网络结构,并根据通道数量的变化进行调整。
嗨!
感谢您的出色工作!
我发现了您上面的模型,并尝试了城市景观图像。我训练了大约 3000 对从分割到照片图像的图像。首先,我将图像转换为 256x256 并保持 100 个 epoch,然后用 250 个 epoch 进行训练。结果很好,但模糊,所以我将原始 1024x2048 分辨率的图像转换为 512x512 并训练到 250 个 epoch。
结果并没有真正改善,但不知何故我希望得到更少模糊的图片。我认为增加 epoch 数量或图像分辨率并没有太大改变,所以我的问题是:我需要改变模型的架构吗?如果需要,您能告诉我应该使用哪些更进一步的层吗?
非常感谢,请继续努力!
您可能需要试验模型架构和学习超参数,以发现最适合您的特定数据集的方法。
感谢您的回复,杰森。
您能给我一些提示吗?如果我想用512x512甚至更大的分辨率图像进行训练,而不是256x256,我应该从哪些架构更改开始?更多的conv2d层、dropout层,还是像pix2pixhd那样使用多个判别器/生成器?
谢谢你。
一个好的方法是根据所需的图像尺寸,按比例增加或减少块的数量。
我鼓励您进行实验并观察对输入/输出形状的影响,以便对此有所了解。
dataset = LoadRealData (‘C:/Users/Eeshwar/Desktop/deep learning/maps11.npz’)
print(‘Loaded’,dataset[0].shape, dataset[1].shape)
Imgshape = dataset[0].shape[1:]
dmodel = DiscriminatorModel(Imgshape)
gmodel = GeneratorModel(Imgshape)
ganmodel = GANModel(dmodel,gmodel,Imgshape)
TrainModel(dmodel,gmodel,ganmodel,dataset)
Loaded (1096, 256, 256, 3) (1096, 256, 256, 3)
WARNING:tensorflow:可训练权重与收集到的可训练权重之间存在差异,您是否在调用
model.compile
之后设置了model.trainable
?InvalidArgumentError: data[0].shape = [4] 未以 indices[0].shape = [2] 开头
[[{{node training/Adam/gradients/gradients/loss_3/dense_2_loss/Mean_grad/DynamicStitch}}]]
先生,您能帮我解决这个问题吗?提前感谢您
很抱歉听到这个消息,这可能会有所帮助。
https://machinelearning.org.cn/faq/single-faq/why-does-the-code-in-the-tutorial-not-work-for-me
您好,先生,本教程很棒,但我有两个问题。
1) 在 define_discriminator() 函数中,您将 loss_weights 参数设置为 0.5,以减缓判别器的训练。我们是否可以通过降低判别器模型的学习率来减缓训练,而不是指定 loss_weights 参数?
2) 在 define_gan() 函数中,为什么还需要在那里指定 loss_weights 参数?
谢谢。
或许可以尝试一下看看效果。
我们确实为 GAN 使用了 loss_weights。
好的,我将尝试降低学习率而不是在 define_discriminator() 中指定 loss_weights 参数。但我很抱歉,我仍然没有得到第二个问题的答案,即为什么我们需要在 define_gan() 函数中指定 loss_weights 参数。
为了匹配论文中描述的实现。
它产生了将大部分注意力放在 L1 上,以及一小部分注意力放在交叉熵上的效果。
这在本教程的该部分有所解释,或许可以重读一下?
先生您好,感谢您精彩的教程!
我是机器学习新手,
我想改变图片或视频中人物的衣服。那么我应该在服装数据集上训练pix2pix吗?
第二个问题是,除了衣服我不想改变图片中的任何其他东西,所以如果我在图片上应用pix2pix,它会改变所有东西,我如何只针对图片中的衣服?
再次感谢您的出色工作!
或许可以尝试一下,看看它能做得多好。
你好,
我按照教程中概述的模型,使用相同的数据集训练了好几次,判别器的损失在大约5000步后总是稳定地保持在0.000。查看更多有效数字的损失,显示损失大于零,因此,当您说如果判别器损失长时间保持为零则训练失败时,您是指小数点后三位为零(0.000)吗?
在判别器损失显示0.000之后,生成器仍在改进,但我推测判别器对生成器的训练不再有显著影响。
感谢这篇精彩的教程,它帮助很大!
零损失表示失败模式
https://machinelearning.org.cn/practical-guide-to-gan-failure-modes/
回想一下,GAN 不会收敛
https://machinelearning.org.cn/faq/single-faq/why-is-my-gan-not-converging
您在训练过程中是否保存了模型?
您能检查训练的进度吗,它是先好后坏还是全程都很差?
我每5个 epoch 保存一次模型,预测图像在训练过程中确实略有改进,到最后看起来相当不错(我推测判别器对质量没有影响,只是生成器本身在改进)。判别器和生成器的损失最初都下降,但判别器缓慢下降到0,而生成器保持相当低(在1到5之间)。
我假设判别器在区分真实图像和虚假图像方面过于出色,因为我已经从其中删除了几个层,它的损失在训练期间没有衰减到0。
有趣。感谢分享,亚历克斯。
我有大约3700张图片要训练。
您能大致指导我如何设置超参数(例如n_epochs,n_batch),因为我遇到了以下问题?
请帮助解决。
/home/reshmajindal/.local/lib/python3.6/site-packages/keras/engine/training.py:490: UserWarning: 可训练权重与收集到的可训练权重之间存在差异,您是否在调用
model.compile
之后设置了model.trainable
?“可训练权重与收集到的可训练权重之间存在差异”
已终止
https://machinelearning.org.cn/faq/single-faq/why-does-the-code-in-the-tutorial-not-work-for-me/
我甚至尝试了所有这些,但都没有奏效。
我们无法知道配置模型的最佳方式,相反,我们必须通过实验来调整和发现哪种配置最适合给定的数据集。
这些教程将教您
https://machinelearning.org.cn/start-here/#gans
感谢这篇非常详细的教程。我有一个问题,如何让模型适应不同尺寸的图像输入?也就是说,如果训练/验证图像的高度和宽度值不同?
不客气。
通常,图像都会被调整到模型期望的相同宽度和高度。
调整大小是一个有点灵活的术语:)
裁剪大图像会导致信息丢失。放大小图像可能会导致图像模糊。超分辨率计算成本高昂,需要辅助模型。您认为哪种“调整大小”方法可以很好地与该模型配合使用?
我建议您探索多种不同的方法,并找出最适合您特定项目的方法。
是的!!发现的最佳方法就是动手实践!这就是为什么我对这个machinelearningmastery如此着迷:p
谢谢!
有没有办法输入自己的图像?我还没有看到任何能够输入自己图像的演示,我自己也尝试过,但都无济于事。
是的,教程的最后一部分展示了这一点。
大家好。感谢您的超级实施指南。我有一个问题。我可以使用pix2pix-GAN生成1024×1024像素的图像吗?
或许可以尝试为大图像放大模型,看看会得到什么样的结果。
我预计质量会下降。使用基于渐进式增长架构的模型可能会更容易。
顺便说一下,我的数据集图像尺寸小于1024像素
嗨!
这是一个非常好的教程。我希望将这个概念应用到我的工作中。但我想输入一些数值参数(例如P1、P2、P3……)以及图像作为输入,并希望得到图像作为输出。
您能指导我如何修改代码来实现这一点吗?这有可能吗?
谢谢。
或许可以看看其他GANs,比如条件GAN或InfoGan
https://machinelearning.org.cn/how-to-develop-an-information-maximizing-generative-adversarial-network-infogan-in-keras/
感谢这篇很棒的教程。
判别器的两个损失都在前100个epoch内归零了。
你能帮我吗?
或许可以重新开始训练,一旦生成的图像足够好就停止。
老师,非常棒的教程。
我的两个判别器损失在前200步都趋于零。我无法解决这个问题,并且已经运行了很多次。这会是版本问题吗?
也许可以尝试更改/调整模型的学习超参数。
或许可以尝试这里的一些建议
https://machinelearning.org.cn/how-to-code-generative-adversarial-network-hacks/
感谢您的回复,先生。我会尝试的。
但是,如果代码和上面一样,为什么它不起作用呢?
该模型使用随机学习算法,您可能需要运行示例几次才能获得好的结果。
您可以在这里了解更多信息
https://machinelearning.org.cn/faq/single-faq/why-do-i-get-different-results-each-time-i-run-the-code
很棒的教程!
我正在尝试将此架构应用于MRI图像到图像的转换任务。我有两个关于此目的架构的问题
1) 在将MRI数据切片为2D切片后。我需要将NIFTI文件转换为JPEG,还是可以直接将其保存为npz(压缩的numpy数组)?
2) MRI图像是灰度图像,而本教程中的示例代码使用RGB图像。教程的架构将如何改变以处理灰度图像?
谢谢 Jason。
该模型将图像数据作为numpy数组。我不知道您的数据是否需要先将数据转换为jpeg。
是的,您可以将模型用于灰度图像,例如更改输入图像的通道数到 D(),并更改 G() 的输出通道数。
Jason,
这是一篇多么不可思议的文章。我将您的方法论复制到一个机械网络的研究项目中,其中模型学习在系统部件之间绘制机械连接。尽管训练图像样本很少,但它完美地工作。
我在我的Medium博客文章中重新使用了您的一张图片(生成器的Unet架构),并仔细引用了您的文章作为来源和您的工作作为参考。您可以在这里查看
https://adriensaremi.medium.com/develop-a-image-to-image-translation-model-to-capture-local-interactions-in-mechanical-networks-9c2f45230849
我想确保您批准了对该图片的再利用。再次感谢您在这里以及更广泛地在机器学习领域所做的工作。
谢谢。
没关系,你的帖子写得很好!
感谢您的精彩教程
我读了一些关于GAN的帖子,我意识到GAN应用于方形图像。这是对的吗?我可以将其用于非方形图像吗?
是的,它们可以,但正方形要简单得多。
感谢您的精彩教程。
请问我如何使用GAN进行可变形图像配准?
谢谢 Jason。
不客气。
也许可以先查阅文献,了解现有方法并尝试它们。
嗨,Rekka,你找到处理可变形图像的方法了吗?谢谢
你好,
希望你一切都好。
我尝试了您的代码,它运行得非常完美。
我想知道,如何在单独的数据集上测试这个模块,因为我发现大多数使用gans的分割算法也包括测试数据集。
如果我在保存的模型(例如model_109600.h5)上使用验证数据集的一部分(并称之为我的测试数据集),结果是好的。但如果我使用不同的测试数据集,分割结果并不理想。
如果您能对此有所启发,我将不胜感激。另外,请告诉我,是否有办法在测试数据集上测试这个算法?如果没有,是否有任何参考资料表明将pix2pix用于图像到图像翻译不是一个好的选择?
谢谢
谢谢。
抱歉,我没有将GAN输出与预测模型结合的例子——我认为我无法就这个话题给出很好的即兴建议。或许可以查阅文献。
嘿,解释得真好,干得漂亮!
我实现了一个类似的cGAN用于黑白图像着色。训练起来非常困难,经过对大数据集的多次、多次迭代后,我得到了一些“足够好”的结果,但我想知道如何衡量转换图像的准确性?
另外,在训练期间和训练结束后,我的cGAN生成的损失非常大,例如训练结束时为10.0和2.0。判别器的损失接近0,有时甚至达到3或5。我如何衡量训练模型的准确性或在训练期间的准确性?
谢谢
谢谢。
好问题,这可能会给您一些评估 GAN 模型的方法。
https://machinelearning.org.cn/how-to-evaluate-generative-adversarial-networks/
嗨,Jason,
非常感谢您的详细解释和示例。非常有帮助。
我正在尝试通过notepad++编辑代码,但它给我缩进错误。似乎空格和制表符混合在一起了。
请问您使用的是什么IDE或编辑器?
抱歉提出愚蠢的问题。
不客气。
这将帮助您正确复制代码
https://machinelearning.org.cn/faq/single-faq/how-do-i-copy-code-from-a-tutorial
精彩的教程,更令人印象深刻的是,您在一年后仍然回复了每一条评论!快速提问:您说过如果判别器的损失长时间停留在0,很可能已经失败,应该重新开始。我正在第三次运行,两者又都归零了,我是不是做错了什么?有什么我可以做来提高它成功的机会,还是继续尝试就好?(附注:我正在使用不同的图像,并且使用2000张图像而不是您的约1100张(仍然是100个epoch),但我假设这不会影响模型的基础)。提前感谢。
谢谢!
听到这个消息我很难过。
或许可以尝试减少 epoch 数量?
或许可以尝试更改其他学习超参数?
或许可以尝试调整架构?
也许这里的一些想法能帮助你
https://machinelearning.org.cn/how-to-code-generative-adversarial-network-hacks/
最新进展,尝试了您在文章和备受赞赏的评论中提出的一些建议,但变化不大。让它继续运行,只是为了看看会发生什么,尽管模型读取判别器损失为0超过6个epoch,它仍然给了我不错的结果,所以我很满意。感谢这篇精彩的文章和有益的建议,一定会阅读您的其他文章。
感谢您的更新,干得好!
嗨,我喜欢你的整个博客和教程!
只是一个问题,是否有可能训练一个模型,该模型使用2张源图像来生成一个目标图像?
例如,从一张传统卫星图像 + 一张红外(IR)图像重新创建相应的地图?
非常感谢
谢谢!
我看不出有什么不可能的。我预计会有关于此的论文——我建议查找它们以获取想法。
非常感谢这篇精彩的教程!
我的问题是,是否可以从保存的模型继续训练?train函数的输入会是什么?再次感谢
不客气。
是的,您可以加载已保存的模型并继续训练。您可以将第一轮训练的相同代码作为起点。
这个模型适用于生成超分辨率数据吗?
不,我相信文献中描述了更专业的模型来解决这个问题。
您能分享一些关于生成超分辨率数据的专业模型文章链接吗?
你可以在这里搜索相关论文
https://scholar.google.com/
将像素值从 [0, 255] 转换为 [-1, 1] 的意义是什么?
是因为生成器模型最后一层使用了 tanh 激活函数吗?
这种架构也可以用于矩阵到矩阵的映射。但是一个矩阵可能具有像素(arr[row, col])值为实数值(从 [0, inf] 而不是 [0, 255])。在这种情况下,您会建议如何进行转换(到 [-1, 1])?是否仍然需要这样做?
谢谢,
抱歉提出多个问题。
是的,正是如此。
是的,在 gan 生成器模型的输出层使用 tanh 并将数据缩放到与激活函数分布匹配是标准做法。
非常感谢。感谢您的回复。
不客气。
我注意到代码中,判别器模型正在编译,gan模型也在编译,但生成器模型没有编译。生成器正在保存。每当我加载生成器模型进行预测时,它都会发出警告说
“在保存文件中未找到训练配置:模型*未*编译。请手动编译它”
您能指导我这是否会影响模型的性能吗?似乎我的模型不起作用。
在谷歌搜索后,我得到的印象是这只是一个警告,但我仍然想向您确认。
谢谢
不需要,因为我们没有直接训练它。您可以忽略该警告。
感谢您的回复。
我是您的网站的粉丝。总是感谢您的精彩文章。
我想问一下您在逻辑GAN模型中使用的损失函数。在您的代码块中,define_gan函数中使用了2个损失函数。
model.compile(loss=[‘binary_crossentropy’, ‘mae’], optimizer=opt, loss_weights=[1,100])
如果我理解正确,‘mae’接受标签(真实和预测标签)而不是图像。但在pix2pix论文中,L1损失定义如下
L1(G) =Ex,y,z[‖y−G(x,z)‖]
G模型的输出是图像,它们的损失函数是基于真实图像和生成图像之间的差异而不是标签定义的。
这与使用标签而不是图像的效果相同吗?
是的,MAE 是图像像素之间的 L1 范数。
谢谢您的回答。
布朗利博士,您好,
在您的上一个版本中,define_gan 方法中有一行代码
# 使判别器中的权重不可训练
d_model.trainable = False
我的问题是,如果判别器不可训练,它将如何改进?
在您当前版本的代码中,您已将其替换为以下几行
# 使判别器中的权重不可训练
对于 d_model.layers 中的每一层
如果不是 BatchNormalization 实例
layer.trainable = False
如果权重不可训练,那么判别器将如何学习和变得更好,并有助于使生成器变得更好?
我的理解是权重是在训练过程中应该被训练的。如果我错了,请纠正我。很抱歉,我不是专家。我正在通过您的文章和其他资料学习。
提前感谢。
D 仅在作为复合模型的一部分时才不可训练。这称为层/模型冻结。它仍然可以作为独立模型进行训练。
非常感谢布朗利博士。
布朗利博士,如果我希望判别器有更高的学习率,而生成器有更低的学习率,比如判别器是2e^-4,生成器是1e^-4,我是否应该只改变复合模型的学习率设置?
是的,复合模型用于更新生成器。
请告诉我您的进展如何。
嗨,Jason,
感谢您的精彩教程。
我只想问您一个问题:为什么在推理过程中,我们必须保持批量归一化和 dropout 在训练模式下?
我理解 dropout 是为了增加一些噪声,但我以为它只在训练部分是必要的。
此外,我在批量大小为1的情况下进行了训练,在预测阶段,我将生成器应用于维度为[N, 256, 256, 3]的堆叠图像体积,结果非常不同。在预测阶段使用批量大小为1给了我更好的结果。我认为这与在训练模式下采用BN有关。
感谢您的时间。
不。Batchnorm 和 dropout 会切换到推理模式。Batchnorm 将使用学习到的 mu 和 sigma,dropout 将停止丢弃。
但是BN和dropout的训练标志都设置为True,我认为这个标志使它们在训练阶段工作。
你好,
是否可以在实时绘制损失?我无法做到。您能帮帮我吗?
或许通过TensorBoard?
您好,感谢您的分享。
我想知道d1[0.362] d2[0.405] g[78.143] 每个损失值的含义是什么?
这是否意味着当判别器的损失值接近零时,它就是假的?
那么复合损失值意味着什么?
这些数值很难解释。
尽管如此,d1和d2分别是判别器在真实样本和虚假样本(“B”)上的损失,g是复合模型在真实样本上的损失。
这可能有助于您更普遍地解释交叉熵
https://machinelearning.org.cn/cross-entropy-for-machine-learning/
嗨,Jason,
非常感谢这篇内容丰富的文章。
我有一个关于一个好的GAN模型的问题,用于从少量医学图像中创建更多合成图像?StyleGAN适合这个问题吗?
或许可以尝试几种方法,看看哪种方法最适合您的数据集。
嗨,
如果可能的话,您能分享一下完成训练后的 .h5 模型吗?因为我正在尝试,但由于计算能力不足,无法完全训练我的模型。
我也在Colab上尝试过,但过了一段时间就停止了。
抱歉,我无法分享已保存的模型。
嗨,Jason,
这真是一个很棒的教程。
我想尝试这段代码。安装了必要的库。实际上,我的机器上没有GPU。所以,我决定一次进行较少的迭代,例如,我运行train函数5个epoch,然后保存模型,第二天我加载这些相同的模型,并训练接下来的5个epoch。(这样做是因为一天5个epoch本身就需要很长时间,而且我的机器会发热很多)
我创建了一些新函数来加载以前训练过的模型。
除了总结性能函数和减少训练函数中的n-epochs外,没有修改任何您的代码。
我保存了d_model、g_model、gan_model和每个epoch后的图表。
然后,对于下一个epoch,我加载了最近训练过的epoch,并继续进行下一组epoch。
但是,在3组(即15个epoch)之后,从第16个epoch开始,判别器误差开始收敛到零。我尝试了另外两组,但没有改善,输出质量也没有改善。
我不知道问题出在哪里。
我需要保存更多模型而不是这3个(g_model、d_model、gan_model)吗?或者我需要保存任何更多数据/模型/参数吗?
您能帮我解决这个问题吗?(例如,是什么导致了问题)
或许可以再运行一次,看看是否出现同样的问题,有时 GAN 的训练会莫名其妙地失败。
非常感谢您的回复……!
实际上,我刚才又进行了一次训练,发现这两个警告也出现在我之前的训练中,
“warnings.warn(‘在保存文件中未找到训练配置:’”
“warnings.warn(‘加载保存的优化器时出错’”
我使用 model.save(path+model_name.h5) 函数来保存模型
你认为这就是导致问题的原因吗??
在我加载最新的可用模型后,为了再次训练它,我还需要手动添加一个优化器吗?
像这样
对于 d_model
opt = Adam(lr=0.0002, beta_1=0.5)
model.compile(loss=’binary_crossentropy’, optimizer=opt, loss_weights=[0.5])
对于 gan_model
opt = Adam(lr=0.0002, beta_1=0.5)
model.compile(loss=[‘binary_crossentropy’, ‘mae’], optimizer=opt, loss_weights=[1,100])
?
可能吧,但我不认为这些警告是相关的。
好问题。或许可以尝试重新定义和不重新定义优化器。我怀疑重新定义它会使其以新的学习率开始,并可能冲刷掉您的模型权重。进行实验以确定何种方法合适。
您应该在保存模型时保存优化器。如果您定义了一个新的优化器,您将失去之前训练中的所有内部“动量”。
您好。感谢分享。
我想训练一个pix2pix模型来分割裂缝图像,但我在训练中遇到了一些问题。在训练过程中,判别器的损失在减少,但生成器的损失在增加。因此,模型训练得不好。
有人能指导我吗?
如果您的目标是分割图像,我认为pix2pix不适合。考虑使用 mask rcnn。
嗨,杰森,感谢您的精彩文章!
我想将它应用于我的问题,即手写文本行分割,我有一个手写文档数据集和类似的带有每行边界线的地面真实数据
我可以使用这种方法将手写文档图像映射到带有绘制文本行边界的目标手写文档图像吗
目的是分割手写文档中的文本行,我有200张文档图像
请回复,这将非常有帮助,以及我还可以使用哪些其他方法来修改此GAN
我不知道,或许可以尝试一下看看。
我们如何修改这个网络,比如改变生成器和判别器的其他选项,但任务仍然是图像转换
我们可以在这方面使用迁移学习的概念吗
是的,或许可以尝试通过试错法将其应用于您的用例。
嗨杰森,我尝试了一下,但是生成的带有边界的图像与给定的源图像不同,比如图像的内容(文本文档)发生了变化,我不知道为什么会发生这种情况
就像给定的用于分割的源图像和带有分割线的最终图像(转换图像/生成图像)是不同的
请帮忙
很抱歉听到这个消息,您可能需要多做一些实验,或者尝试一些替代方案。
嗨,Jason,
感谢您开源代码。我想知道是否有办法可视化训练网络的中间激活图?我的意思是,当数据流经训练好的网络模型时?
也许这会有帮助。
https://machinelearning.org.cn/how-to-visualize-filters-and-feature-maps-in-convolutional-neural-networks/
谢谢,我会看看的。
不客气。
嗨,Jason,
我成功地使用了并训练了网络,非常感谢!
不过我有一个问题,为什么在这种情况下使用二元交叉熵?为什么不是MSE?
我在Isola等人的原始论文或代码中没有找到它(二元交叉熵)……它有什么好处吗?您有相关的论文我可以查阅吗?
谢谢!
干得好!
不假思索地,我相信我使用了与论文相同的损失函数。
是的,这确实有所不同,而且通常对模型和应用至关重要。尽管如此,请尝试更改它并比较结果。
嗨
感谢您如此精彩的文章。从中学到了很多。此外,阅读后,我开发了自己的pix2pix应用程序:将图像转换为ASCII艺术。您的反馈将非常棒🙂
我的文章在这里:https://jojo96.medium.com/generating-ascii-art-with-pix2pix-gans-dbee268b156a
感谢分享。
嗨,Jason,感谢您出色的教程!它帮助我理解了GAN的工作原理。
对于其他想尝试本教程的人:下载pix2pix地图数据的链接已不再可用。但是,它仍然包含在这个kaggle数据集中:https://www.kaggle.com/vikramtiwari/pix2pix-dataset
不幸的是,我使用您的代码(tensorflow v2.4)并没有立即得到很好的结果。我重新训练了很多次,只有一次在前几个epoch后得到了勉强有意义的图像,然后是模式崩溃。实际上,我几乎总是很早就遇到模式崩溃,即使经过100个epoch也无法解决。我已经尝试了很多方法,比如标签平滑、降低学习率、在某些epoch中跳过判别器的训练以及其他一些方法,但都没有成功。我得到的最好结果是只训练10个样本(试图让生成器过度拟合数据),这让我觉得原则上数据集和设置是没问题的,但我无法重复这些结果,尤其是在完整数据集上:https://drive.google.com/file/d/1swhpIqQhc-fCoftySuDAgETH2z6RxvKz/view?usp=sharing
您认为自从您发布本教程以来,tensorflow是否发生了什么变化?或者kaggle的数据集实际上是否不同?我快想不出办法让它工作了 🙂
再次感谢您所做的所有出色工作以及精彩易懂的教程。
祝好,Laurin
可能是数据集或库的差异导致了变化
https://machinelearning.org.cn/faq/single-faq/why-do-i-get-different-results-each-time-i-run-the-code
最终我换了一台电脑,还在Google Cloud Compute实例上尝试了一下——两次都顺利运行,没有问题!再次感谢这篇精彩的教程!
很高兴听到这个!
哦,可能链接仍然有效,但由于它是 http 而不是 https,某些浏览器可能不允许直接下载,所以没必要去kaggle,哎呀 🙂
太棒了!
你的环境配置是什么?
我的意思是,TensorFlow、Python、Keras、Cuda和cudnn版本?
嗨,Jason,
源图像和目标图像的数值范围必须相同吗?例如,如果源图像的取值范围是 [-0.7,0.7],而目标图像的取值范围是 [-1,1]。或者两者都应该在相同的范围内?
我问这个问题是因为我有的训练数据是浮点数,范围很广,需要缩放到 [-1,1] 的范围。但为了给我的测试数据留一点空间,这些数据可能超出训练数据的范围,我将它们缩放到 [-0.7,0.7]。然而,我的目标数据只是一张黑白掩码图像,所以始终在 [0,255] 的范围内。因此,它可以直接缩放到 [-1,1]。但我不确定这样做是否正确,或者目标数据是否应该“符合”源数据,也应该缩放到 [-0.7,-0.7]。
我希望您理解我的问题并愿意回答。
感谢这篇精彩的教程!
诚挚的问候,
Marja
或许可以尝试一下看看效果。
嗨,Jason,
我有一个类别不平衡的数据集(包含两个类别)。我知道有些损失函数比本模型中使用的二元交叉熵更适合不平衡数据集。例如二元焦点骰子损失。但我不知道改变这个GAN模型的损失函数是否会使情况更糟?您认为通过改变损失函数有可能改善GAN吗?或者我应该坚持欠采样/过采样和/或数据增强我的数据集?
一般来说,是否更改损失函数或超参数可以这样推理:你是否能将你的决定与你正在解决的问题联系起来。例如,为什么我不想使用二元交叉熵?因为数据不平衡,即使我的模型显著改进,熵也不会显著改善。通过这样回答自己,你就能判断你是否做出了一个好的决定。
嗨,Jason,
我想知道如何保存模型,然后在稍后的时间继续训练。在 summarize-performance 函数中,只保存了生成器函数,但没有保存判别器或 GAN/组合模型。如果我想继续训练,例如达到第 150 个 epoch 而不是第 100 个 epoch,那么使用我在第 100 个 epoch 保存的训练好的生成器,然后使用未保存的判别器和 GAN 模型就足够了吗?或者你是否也必须在那个 epoch 保存训练好的判别器和 GAN 模型?
最好两者都保存,因为 GAN 是生成器和判别器协同工作的成果。
感谢这门课程!
我们如何在 720p 图像(720×1280)中使用它?
因为它只适用于方形图像
谢谢。
您可以修改输入形状,然后一切都应该正常工作(只要您以相应的尺寸获取数据进行训练)。或者,您可以想象您的 720p 图像由许多小方块组成,并将其应用于此模型,然后再将它们拼接在一起。
你好,
我尝试在停止训练后通过将模型重新加载到训练函数来继续训练模型。如何一次性传递整个 h5 模型而不是 g_model、d_model 和 gan_model?我需要做哪些精确的更改?
非常感谢!
如果代码单独保存了模型,您就需要单独加载它。我相信编写一个函数,逐个加载每个模型并一次性返回它们,这应该不是一项困难的任务。您认为呢?
感谢这篇精彩的教程!
我想问一下关于在 generate_real_samples 函数中生成真实类别标签的代码。
如果一个 RGB 图像作为一个 numpy 数组的形状是(图像数量,宽度,高度,波段),为什么真实类别标签的颜色波段数量只设置为 1?图像不是 RGB 模式吗?
此外,如果颜色波段设置为 1,那么输入和标签的形状将不相同。
在生成假样本的部分也有同样的问题。谢谢!
该函数与判别器模型一起使用。该形状是我们需要的,以便它可以适应输出层。如果您更改 d_model 的输出层,您也需要更改该形状。
你好 Jason。我看到在训练循环中您更喜欢使用
d_loss1 = d_model.train_on_batch([X_realA, X_realB], y_real)
d_loss2 = d_model.train_on_batch([X_realA, X_fakeB], y_fake)
您能给我解释一下为什么它会起作用吗?
更具体地说,我理解我们需要 d_loss1 和 d_loss2 的总和。但我认为这种方法首先给出 d_loss1(然后更新权重),然后给出 d_loss2(然后再次更新权重)。所以这不是一个“常见”的损失函数(==d_loss1+d_loss2)。
是的,那是为了说明目的。但事实上,考虑到每次迭代中的权重更新不应该很大,您的担忧不应该很明显。您也可以考虑打乱 X_realB 和 X_fakeB,并调用 train_on_batch() 一次。但那样我就无法在几行之后的 print() 语句中显示性能了。
首先,非常感谢您发布这个教程,那么,您是用什么方法将图像并排放置的?
我想您指的是这篇文章开头的图片。它就是原始数据集中的图片的样子,“每张目标大小为 256×512 像素”。
您使用什么程序创建这些数据集?ArcGIS?Photoshop?
那来自原始论文。
嗨,Jason,
感谢您的教程。
我尝试使用这个模型进行 RGB 到红外(IR)图像转换,但是生成的图像上有一些白色斑点伪影。
就像这个讨论一样:https://github.com/junyanz/pytorch-CycleGAN-and-pix2pix/issues/411
有人告诉我这可能是因为训练和测试数据的预处理不相同,但我对两者都做了相同的处理(从 [0,255] 缩放到 [-1,1],再从 [-1,1] 缩放到 [0,1])。
您对这个问题有什么建议吗?
非常感谢。
不确定——但如果那是预处理问题,也许你可以尝试将范围从 [0,1] 缩小到 [0.1,0.9],这样如果你的模型过冲,你就有了一些余地。
嗨,Jason,
谢谢您的教程。
我尝试使用此模型进行 RGB 到 IR(红外)图像转换。
但我的生成图像中出现了一些白点,就像这个讨论中的情况一样:https://github.com/junyanz/pytorch-CycleGAN-and-pix2pix/issues/411
有人告诉我这可能是因为训练数据和测试数据的预处理不同,但我对两者都做了相同的处理(将 [0, 255] 缩放到 [-1, 1] 和将 [-1, 1] 缩放到 [0, 1])。
您对此有什么建议吗?
谢谢你。
非常感谢这个教程!!我想用这段代码生成单波段图像。我可以使用相同的代码,只需改变波段数量即可生成 1 波段图像吗?我的源和目标都是单波段图像。当源是多波段图像时,是否可以生成单波段图像?
你好,
感谢这篇精彩的教程。
我不明白 define_discriminator() 函数是如何实现 70×70 PatchGAN 判别器的。有没有教程可以理解其背后的数学原理和您的参数?
你好 Hind……感谢您的反馈!本教程中介绍的示例主要基于以下论文
https://github.com/phillipi/pix2pix
你好,我正在寻找对代码进行修改以将此 pix2pix GAN 应用于图像的频率分量。
您有什么建议吗?
谢谢你
嗨 Jason,非常感谢这个教程。我想知道是否有办法将输出图像限制为仅黑白?我正在训练一个模型,其中结果输出只需要是一个黑白 alpha 类图像,我想也许如果它只需要生成一个 1 位像素的二进制输出,它会训练得更快。非常感谢任何指导!
你好 Brock……尽管这与您尝试完成的目标不完全相同,但可能有一些想法会有所帮助
https://anne-guilbert.medium.com/black-and-white-image-colorization-with-deep-learning-53855922cda6
非常感谢您发布这个,这正是我所寻找的。我一直得到图像分类和风格迁移的结果,而我真正想训练的是一个能对图像应用特定类型转换的模型。
感谢您的反馈和支持,Wolfgang!
感谢您的工作。我用自己的数据集(低分辨率 256*256)训练了 pix2pix 和 pix2pixhd 模型,但是 pix2pixhd 在测试数据集上的结果比 pix2pix 更模糊,有点不真实,我应该修改 pix2pixhd 的 G_net 和 D_net 来适应低分辨率图像吗?
你好 Dil murat……以下是理解如何在 GAN 模型中处理分辨率的一个很好的起点。
https://ieeexplore.ieee.org/document/9368265
感谢您精彩的教程!
也许我的问题很愚蠢,但我不太明白为什么我应该将 GAN 模型用于 pix2pix 应用程序。
我将解释我的问题
如果每个图像在训练集中都有一个匹配图像,那么仅仅计算两个图像之间的 mse 损失函数不足够吗?
为什么我需要判别器来指导网络走向正确的方向?
你好 Sherlock……你可能会对以下内容感兴趣
https://www.coursera.org/lecture/build-better-generative-adversarial-networks-gans/alternatives-to-gans-S3O2c
大家好,我正在尝试在单通道图像/矩阵(在这种情况下是地震图像)上训练 Pix2Pix 网络。也就是说,我加载了一个地震图像,其尺寸为 4096 x 4096 x 1,而不是等效的 RGB 图像 4096 x 4096 x 3。我通过重新定义“image_shape”走了很长一段路,但我遇到了错误消息,例如:“WARNING:tensorflow:Model was constructed with shape 256,256,1 for input KerasTensor(), but it was called on an input with incompatible shape (None, 256, 256, 3)”。我这样做的原因是跳过加载地震图像,然后将地震图像写入 RGB 图像,再次加载这些 RGB 图像,然后再进行训练和预测的步骤,因为这很慢。是否有可能在单通道图像/矩阵上训练和预测 Pix2Pix?
祝好,Stefan
你好 Stefan……你可能会从以下资源中找到答案
https://towardsdatascience.com/gan-pix2pix-generative-model-c9bf5d691bac
嗨 James,非常感谢这个很棒的资源,它为 Pix2Pix 生成器提供了很好的见解。我实际上找到了解决我的问题的方法:通过将生成器模型的最后一个卷积层修改为只卷积一个通道而不是三个,它奏效了。因此,在生成器模型中,我将以下代码行从:‘g = Conv2DTranspose(3, (4,4), strides=(2,2), padding=’same’, kernel_initializer=init)(d7)’ 更改为 ‘g = Conv2DTranspose(1, (4,4), strides=(2,2), padding=’same’, kernel_initializer=init)(d7)’ 。这解决了问题。
另外,我还在尝试在 Pix2Pix GAN 中实现残差学习。显然,残差学习可以提高 GAN 的训练效率。我读过一些不错的论文,其他研究人员在 CycleGAN 中实现了残差学习。他们使用了某种适应的编码器和解码器块在生成器中,其特征是输入和目标图像之间的残差差异,而不是目标图像本身。
我尝试了一个简单的实验,将生成器模型的目标从:‘model = Model(in_image, out_image)’ 替换为 ‘model = Model(in_image, tf.keras.layers.subtract([in_image, out_image]))’,从而试图让模型重现输入和目标图像之间的差异,这在 DnCNN 模型和 CycleGAN 中取得了成功。但不幸的是,这种方法给我带来了糟糕的训练结果。
这里有人有 Pix2Pix 残差学习的经验或者关于如何实现的一些提示吗?也欢迎提供带有残差学习的 CycleGAN 实现代码!
祝好,Stefan
嗨,数据集下载链接不起作用,我在哪里可以下载数据集?
你好 west……将此链接复制粘贴到您的搜索引擎中
http://efrosgans.eecs.berkeley.edu/pix2pix/datasets/maps.tar.gz
嗨,Jason,
我想将 3D 图像(高光谱立方体)转换为具有 3 个通道的 2D 图像。
我所有的源图像和目标图像都是配对的。
为了支持这一点,代码中应该做哪些更改。
谢谢,
Eli。
你好 Eli……你可能会对以下内容感兴趣
https://www.delftstack.com/howto/numpy/convert-3d-array-to-2d-array-in-python/
你好……非常感谢教程。我想知道我们如何保存模型?如果可能,请提及代码和应该附加的位置。
你好 P Lakshmi……你可能会发现以下讨论很有启发性
https://github.com/keras-team/keras/issues/10806
嗨 James,
从 3D 转换为 2D 可能会丢失大量空间信息。
高光谱数据(也称为高光谱立方体)的结构是空间数据(x、y 轴)在各种通道(=波长)的 z 轴上更丰富。
我正在寻找一种既能保留空间结构又能利用各种通道中丰富信息的方法。
祝好,
Eli。
你好 Eli……你可能会从这些资源中介绍的概念中受益
https://pubs.rsna.org/doi/full/10.1148/ryai.2019190015
https://arxiv.org/pdf/1908.06616.pdf
嗨,Jason,
感谢这个很棒的教程。我想知道使用 GAN 生成地图图像有什么优点?
你好 Lakshmi……你可能会对以下内容感兴趣
https://www.ncbi.nlm.nih.gov/pmc/articles/PMC7309096/
嗨 James,
如何逐一保存生成的图像
你好 ChunYu……以下可能会有所帮助
https://stackoverflow.com/questions/71452209/save-gan-generated-images-one-by-one
https://discuss.pytorch.org/t/saving-gan-generated-images/147599/2
你好,
我很难计算我的数据集的补丁大小。我的图像形状是(448,576,1)。我遵循了与文章相同的架构,只是根据需要更改了形状以适应我的数据。您能帮我解决这个问题吗?
你好 Jyoti……请澄清你的问题或你遇到的问题,以便我们更好地帮助你。
它是 16*16 的补丁。我的意思是,我打印出了所有图层的形状,它是 16*16。
你能帮我做同样的事情吗?
嗨 James,抱歉我描述不清楚。我的数据是非方形输入图像 (448,576,1)。我遵循了您的教程中相同的层架构,并根据我的判别器模型的输入进行了必要的更改。我也得到了结果,但无法理解感受野的计算。我想知道在我的情况下,每个像素映射的感受野大小是多少。您能在这方面指导我吗?
嗨 Jason,我是深度学习新手,这个主题正是我们论文中要实现的内容
我有一个关于您如何准备自己的数据集的问题
我的导师给了我一张高分辨率的卫星图像,我的问题是如何将这张完整的原始图像裁剪成 256*256,并将其用于模型训练,请帮帮我
你好 Reynard……以下资源可能会引起您的兴趣
https://machinelearning.org.cn/data-preparation-for-machine-learning/
嗨 James,
感谢本教程,我有两个问题,后面是错误
1. “UserWarning: 初始器 RandomNormal 未设定种子且被多次调用,每次都将返回相同的值”。这是一个问题吗?该值每次都应该不同吗?例如,init = RandomNormal(stddev=0.02, seed=round(time.time()*1000))?
2. “在保存文件中未找到训练配置,因此模型未编译。请手动编译。”
编译模型是否是重要的一步?如果是,如何操作?
用 400 张自定义图像和 100 个 epoch 进行训练并没有得到任何好的结果,所以我想知道这些错误是否会对此产生影响,然后我再尝试使用更大的数据集。
感谢您的回复,再次感谢您与我们分享您的出色工作。
你好 Maria……不客气!我们主要查阅 StackOverflow 来研究这些错误的解决方案。也许这些讨论您会感兴趣
https://stackoverflow.com/questions/73978774/how-to-get-rid-of-the-userwarning-the-initializer-glorotuniform-is-unseeded-m
https://stackoverflow.com/questions/53295570/userwarning-no-training-configuration-found-in-save-file-the-model-was-not-c
你好先生,
当我们在神经网络中使用多个损失函数时,如何计算损失函数并更新层的权重?
你好先生,
我们可以用这段代码将卫星图像转换为光学图像吗?
嗨 sneha……使用 pix2pix GAN(生成对抗网络)将卫星图像转换为光学图像是一项涉及将一种类型图像(输入域)转换为另一种类型(输出域)的任务。Pix2pix 是一种条件 GAN,由 Isola 等人在其 2017 年的论文“使用条件对抗网络的图像到图像转换”中开发。它特别适用于目标是从输入图像预测相应输出图像的任务,例如将卫星图像转换为地图,或者在您的情况下,转换不同模态的卫星图像。
### 实现 pix2pix 进行卫星到光学图像转换的步骤
**1. 收集数据集**
– 您需要一个配对数据集,其中每个卫星图像(输入)都有一个对应的光学图像(目标)。例如,Landsat 和 MODIS 等数据集可能很有用,具体取决于您所指的“光学图像”的确切性质。
– 确保图像像素对齐,以进行有效训练。
**2. 预处理数据**
– 将图像调整为一致的大小(对于 pix2pix 通常为 256×256 像素)。
– 将图像像素值归一化到 [-1, 1] 范围。
– 将数据分成训练集、验证集和测试集。
**3. 设置 Pix2pix 模型**
– **生成器**:pix2pix 中的生成器使用基于 U-Net 的架构。它将卫星图像作为输入,并生成相应的光学图像。
– **判别器**:pix2pix 中的判别器是 PatchGAN,它分类图像中的单个补丁是真实的还是虚假的。这有助于学习翻译中的细粒度细节。
**4. 训练模型**
– **损失函数**:结合对抗性损失(以欺骗判别器)和 L1 损失(以最小化生成图像和真实图像之间的绝对差异,鼓励减少模糊)。
– **优化器**:通常使用 Adam 优化器。
– 通过交替训练判别器和生成器来训练模型。
**5. 监控训练进度**
– 使用验证数据监控模型性能,以避免过拟合。
– 定期保存并目视检查生成的图像与真实情况,以确保高质量的翻译。
**6. 后处理和评估**
– 训练后,使用测试集通过适当的指标(例如,SSIM、PSNR 用于图像质量评估)评估模型的性能。
– 根据性能调整训练参数或模型架构,并在必要时重复训练。
### 代码片段示例
以下是使用 TensorFlow 和 Keras 设置 pix2pix 模型的简化示例。此示例假设您的数据集已准备好以适当的格式
`python
import tensorflow as tf
from tensorflow.keras import layers, Model
def build_generator()
inputs = layers.Input(shape=[256, 256, 3])
down_stack = [
layers.Conv2D(64, 4, strides=2, padding=’same’, activation=’relu’),
layers.Conv2D(128, 4, strides=2, padding=’same’, activation=’relu’),
# 根据需要添加更多层
]
up_stack = [
layers.Conv2DTranspose(128, 4, strides=2, padding=’same’, activation=’relu’),
layers.Conv2DTranspose(64, 4, strides=2, padding=’same’, activation=’relu’),
# 根据需要添加更多层
]
last = layers.Conv2DTranspose(3, 4, strides=2, padding=’same’, activation=’tanh’)
x = inputs
# 下采样
skips = []
for down in down_stack
x = down(x)
skips.append(x)
skips = reversed(skips[:-1])
# 上采样并建立跳跃连接
for up, skip in zip(up_stack, skips)
x = up(x)
x = layers.Concatenate()([x, skip])
x = last(x)
return Model(inputs=inputs, outputs=x)
def build_discriminator()
inp = layers.Input(shape=[256, 256, 3], name=’input_image’)
tar = layers.Input(shape=[256, 256, 3], name=’target_image’)
x = layers.concatenate([inp, tar])
x = layers.Conv2D(64, 4, strides=2, padding=’same’)(x)
x = layers.LeakyReLU()(x)
x = layers.Conv2D(128, 4, strides=2, padding=’same’)(x)
x = layers.LeakyReLU()(x)
# 根据需要添加更多层
last = layers.Conv2D(1, 4, padding=’same’)(x)
return Model(inputs=[inp, tar], outputs=last)
# 实例化生成器和判别器
generator = build_generator()
你好先生,
我们可以使用这段代码,利用 pix2pix gan 和配对数据集将卫星图像转换为光学图像吗?是或否?
当然可以!请告诉我们您应用此技术后发现了什么。
先生,您能为 SAR 到光学图像转换建议一个数据集,就像用于 SAR 到地图图像的数据集一样吗?
您能提供模型的评估代码吗?
你好,
感谢您的工作。
我收到一个警告,在开始时我的模型训练速度很快,但后来变得非常慢,5 天后它突然崩溃,这个警告是原因吗?
警告:“2024-05-17 13:37:27.615999: W tensorflow/core/data/root_dataset.cc:266] Optimization loop failed: CANCELLED: Operation was cancelled”
系统配置
32GB 内存
12GB Navidia 3060rtx
你好 Nitin……警告 `Optimization loop failed: CANCELLED: Operation was cancelled` 表示 TensorFlow 数据管道中的操作被取消,这可能由各种原因引起。这个警告本身并不能直接解释为什么您的训练开始时速度很快,然后显着减慢或在几天后崩溃,但它可能与您的数据管道或资源管理中的潜在问题有关。
以下是一些诊断和缓解此问题的步骤和提示
### 1. 检查数据管道和预处理
– **高效数据加载**:确保数据加载和预处理高效。使用 TensorFlow 的 `tf.data.Dataset` API 创建高效的数据管道。
– **预取**:使用 `prefetch` 叠加数据预处理和模型执行。
– **缓存**:如果您的数据集适合内存,请使用 `cache`。
例如
python
import tensorflow as tf
# 假设您有一个解析数据的函数
def parse_function(example)
# 在此处解析示例
return example
dataset = tf.data.Dataset.from_tensor_slices((X_train_scaled, y_train))
dataset = dataset.map(parse_function, num_parallel_calls=tf.data.experimental.AUTOTUNE)
dataset = dataset.cache()
dataset = dataset.shuffle(buffer_size=10000)
dataset = dataset.batch(32)
dataset = dataset.prefetch(buffer_size=tf.data.experimental.AUTOTUNE)
### 2. 监控系统资源
– **内存使用**:监控 RAM 和 GPU 内存使用情况,确保内存没有用尽。使用 `nvidia-smi` 等工具进行 GPU 监控。
– **CPU/GPU 利用率**:检查 CPU 和 GPU 利用率,确保它们大部分时间没有空闲。
### 3. 调试和分析
– **TensorFlow Profiler**:使用 TensorFlow Profiler 识别训练过程中的瓶颈。
– **日志记录**:提高日志记录的详细程度,以捕获有关导致崩溃的操作的更多详细信息。
### 4. 处理长时间训练
– **检查点**:定期保存模型检查点,以便在模型崩溃时可以恢复训练。
– **提前停止**:实施提前停止,以便在性能停止改进时终止训练。
检查点示例
python
checkpoint_cb = tf.keras.callbacks.ModelCheckpoint("best_model.h5", save_best_only=True)
early_stopping_cb = tf.keras.callbacks.EarlyStopping(patience=10, restore_best_weights=True)
history = model.fit(dataset, epochs=250, validation_data=(X_test_scaled, y_test), callbacks=[checkpoint_cb, early_stopping_cb])
### 5. 处理资源管理
– **数据并行**:如果您有多个 GPU 可用,请使用数据并行。
– **批量大小**:调整批量大小。较大的批量大小可以加快训练速度,但也可能增加内存使用量。
### 示例:更新后的训练脚本(已改进)
python
import numpy as np
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, Conv1D, Flatten, MaxPooling1D
from tensorflow.keras.optimizers import Adam
from kerastuner.tuners import RandomSearch
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
# 加载和预处理数据
def load_data()
with open('/mnt/data/trc_data.bin', 'rb') as f
trc_data = np.frombuffer(f.read(), dtype=np.uint8)
with open('/mnt/data/lbls_0.bin', 'rb') as f
lbls_data = np.frombuffer(f.read(), dtype=np.uint8)
trc_data = trc_data.reshape(-1, 16)
return trc_data, lbls_data
X, y = load_data()
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.15, random_state=100)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
X_train_scaled = X_train_scaled.reshape(-1, 16, 1)
X_test_scaled = X_test_scaled.reshape(-1, 16, 1)
# 使用 tf.data 构建数据集
train_dataset = tf.data.Dataset.from_tensor_slices((X_train_scaled, y_train))
train_dataset = train_dataset.shuffle(buffer_size=10000).batch(32).cache().prefetch(buffer_size=tf.data.experimental.AUTOTUNE)
val_dataset = tf.data.Dataset.from_tensor_slices((X_test_scaled, y_test))
val_dataset = val_dataset.batch(32).cache().prefetch(buffer_size=tf.data.experimental.AUTOTUNE)
# 定义超模型
def build_model(hp)
model = Sequential()
model.add(Conv1D(
filters=hp.Int('filters', min_value=32, max_value=128, step=32),
kernel_size=hp.Choice('kernel_size', values=[3, 5, 7]),
activation=hp.Choice('activation', values=['relu', 'tanh']),
input_shape=(16, 1)
))
model.add(MaxPooling1D(pool_size=2))
model.add(Flatten())
model.add(Dense(
units=hp.Int('units', min_value=32, max_value=128, step=32),
activation=hp.Choice('activation', values=['relu', 'tanh'])
))
model.add(Dropout(0.5))
model.add(Dense(1, activation='sigmoid'))
model.compile(
optimizer=Adam(hp.Choice('learning_rate', values=[1e-2, 1e-3, 1e-4])),
loss=hp.Choice('loss', values=['binary_crossentropy', 'mse']),
metrics=['accuracy']
)
return model
tuner = RandomSearch(
build_model,
objective='val_accuracy',
max_trials=10,
executions_per_trial=2,
directory='my_dir',
project_name='intro_to_kt'
)
tuner.search_space_summary()
# 运行超参数搜索
tuner.search(train_dataset, epochs=10, validation_data=val_dataset)
# 获取最佳超参数
best_hps = tuner.get_best_hyperparameters(num_trials=1)[0]
# 使用最佳超参数构建模型并训练
model = tuner.hypermodel.build(best_hps)
history = model.fit(
train_dataset,
epochs=best_hps.get('epochs', 10),
batch_size=best_hps.get('batch_size', 32),
validation_data=val_dataset,
callbacks=[
tf.keras.callbacks.ModelCheckpoint("best_model.h5", save_best_only=True),
tf.keras.callbacks.EarlyStopping(patience=10, restore_best_weights=True)
]
)
# 评估测试数据上的模型
loss, accuracy = model.evaluate(val_dataset)
print('Test Loss:', loss)
print('Test Accuracy:', accuracy)
### 总结
– **高效数据管道**:确保数据加载、预处理和批处理高效。
– **资源监控**:监控 CPU、GPU 和内存使用情况以识别瓶颈。
– **TensorFlow Profiler**:使用分析工具识别和优化瓶颈。
– **检查点和早期停止**:使用这些技术来避免长时间训练和潜在的崩溃。
通过实施这些实践,您应该能够缓解您遇到的问题并提高训练过程的稳定性和效率。
你好,
感谢您出色的工作。
我想问一个问题,在训练模型时,它几乎占用了我所有的内存(RAM),就像它从 12GB/32GB 上升到 31.9GB/32GB,现在我增加了数据,它会工作吗?有什么优化方法吗?
你好 Roshan……在训练 Pix2Pix GAN 等模型时,内存使用确实会成为一个重要问题,尤其是在处理大型数据集时。以下是一些您可以考虑的优化方法,以更有效地管理内存使用
### 1. 减小批量大小
减小批量大小是减少内存消耗的直接方法。这意味着一次处理的图像数量更少,从而减少内存负载。然而,这可能会减慢训练速度,因为需要更多的迭代才能处理整个数据集。
### 2. 即时数据增强
不要将所有增强图像存储在内存中,而是在训练期间即时执行数据增强。这样,您只将原始图像保留在内存中,并根据需要应用转换。
### 3. 使用高效的数据加载
利用高效加载和预处理数据的数据加载器。PyTorch 和 TensorFlow 等框架具有可以并行获取数据并对其进行预处理的数据加载器,从而减少内存负载。
### 4. 模型优化
– **梯度检查点**:此技术通过用计算换取内存来节省内存。在反向传播过程中重新计算中间激活,而不是存储它们。
– **混合精度训练**:使用 16 位(半精度)而不是 32 位(单精度)可以显著减少内存使用。TensorFlow 和 PyTorch 都支持混合精度训练。
### 5. 数据管理
– **数据集大小缩减**:如果可能,减小图像的大小。这可以通过调整大小或下采样图像来完成。
– **图像压缩**:使用高效格式压缩图像以减少其内存占用。
### 6. 优化模型架构
– **模型剪枝**:移除对性能没有显著贡献的模型中不那么重要的部分。
– **层优化**:使用更节省内存的层或用更轻量级的版本替换某些层。
### 7. 分布式训练
– **多 GPU 训练**:如果您可以访问多个 GPU,您可以将训练分布到它们上,这有助于更有效地管理内存使用。
– **集群计算**:使用分布式计算集群将内存负载分散到多台机器上。
### 8. 内存管理工具
– **垃圾回收**:确保删除不必要的对象并释放内存。在 Python 中,这可以使用 `gc` 模块完成。
– **内存分析**:使用内存分析器等工具来了解代码的哪些部分消耗的内存最多,并优化这些部分。
### 示例:使用 PyTorch DataLoader 进行高效加载
python
from torch.utils.data import DataLoader, Dataset
class CustomDataset(Dataset)
def __init__(self, data)
self.data = data
def __len__(self)
return len(self.data)
def __getitem__(self, idx)
# 在此处加载和预处理数据
return self.data[idx]
dataset = CustomDataset(data)
dataloader = DataLoader(dataset, batch_size=32, shuffle=True, num_workers=4)
for batch in dataloader:
# 在此处进行训练循环
pass
通过实施这些策略,您应该能够优化内存使用,并可能更有效地处理更大的数据集。
感谢您的回复,
您能否告诉我如何在您的代码中使用上述内容,我使用了与本博客相同的代码
这将非常有帮助
我有个问题,pix2pix 或任何 GAN 是否可以接受两张输入图像并输出一张图像进行训练?
你好,
当我运行完全相同的代码来处理我的自定义数据时,它只占用了我 GPU 的 40%,而不是占用了全部 50GB RAM,为什么会发生这种情况?因为这最终会导致代码崩溃。
你好 aaskash……我们无法就您的自定义数据或硬件发表评论。
嗨,我想应用相同的模型,其中源是灰度图像(256, 256, 1),目标形状是(256, 256, 3)。我已经相应地修改了生成器模型。我想我不需要修改判别器模型,因为它会比较生成的 3 通道图像和目标 3 通道图像。但它仍然显示 pix2pix 模型生成中的形状不匹配错误。您能告诉我需要进行哪些修改才能解决这个问题吗?
你好 Mmr……您遇到的错误可能源于 Pix2Pix 框架中的判别器模型被设计为将两个图像作为输入:真实的M目标图像和生成的图像。由于您的源是灰度图像(1 通道)而目标是 RGB 图像(3 通道),因此判别器的输入存在不匹配。
要解决此问题,您需要确保判别器模型的输入与真实图像和生成图像的预期形状匹配。以下是您可能需要进行的一些修改
1. **修改判别器模型的输入**
– 判别器通常将输入(源图像)和输出(目标或生成图像)都作为输入。由于您的源是灰度图像(256, 256, 1)而您的目标是 RGB 图像(256, 256, 3),您应该要么调整判别器以接受灰度图像和 RGB 输入,要么在将其传递给判别器之前将灰度图像转换为 3 通道图像。
2. **调整判别器模型的输入形状**
– 如果您想将灰度输入保留为单个通道,您可以修改判别器以处理具有不同通道数的图像。但是,一个更简单的解决方案是将您的灰度图像通过将灰度值复制到所有三个通道来转换为 3 通道图像。这将使灰度图像的形状为 (256, 256, 3),与 RGB 目标图像匹配。
您可以通过在将灰度图像输入判别器之前添加预处理步骤来完成此操作
python
from tensorflow.keras.layers import Concatenate
def convert_to_rgb(image):
return Concatenate()([image, image, image]) # 将灰度图像复制到 3 个通道
3. **在输入判别器之前进行拼接**
– 在原始 Pix2Pix 设置中,判别器的输入是输入图像和输出图像(真实或生成)的拼接。将灰度图像转换为 RGB 后,您应该确保传递给判别器的两个图像的形状都为 (256, 256, 3)。
拼接源图像和生成/目标图像的示例修改
python
def Discriminator():
input_image = Input(shape=[256, 256, 3], name='input_image')
target_image = Input(shape=[256, 256, 3], name='target_image')
merged = Concatenate()([input_image, target_image])
# 继续构建判别器模型...
一旦您进行这些调整,形状应该匹配,并且模型应该在没有进一步形状相关错误的情况下工作。
嗨 James,感谢您的工作。您知道当我尝试运行您的代码时,为什么会出现错误“AttributeError: ‘NoneType’ object has no attribute ‘update_state’”吗?问题出在运行 .train_on_batch……
嗨 Simon……错误“`AttributeError: ‘NoneType’ object has no attribute ‘update_state’`”通常表示您尝试用于 `.update_state` 方法的对象实际上是 `None`,而不是具有该方法的适当对象。
在 `.train_on_batch` 的上下文中,这通常发生是因为
1. **指标初始化**:如果您使用自定义指标,并且其中任何一个未正确初始化或返回 `None`,您将看到此错误。仔细检查您在模型编译步骤中使用的所有指标是否都已正确定义和实例化。
2. **模型编译**:确保模型已正确编译,并带有适当的指标和损失函数。有时,缺失或配置错误的指标可能导致对象为 `None`。
3. **回调或自定义函数**:如果您使用依赖于指标的回调或自定义函数,其中一个可能返回 `None`,从而在调用 `.update_state` 时导致此错误。
如果您仍然遇到问题,分享您的代码片段,特别是围绕模型编译和 `.train_on_batch` 的部分,可能会有助于查明问题。
嗨 James,感谢您及时回复。我完全尝试了您的代码(未经任何调整)
dataset = load_real_samples(‘maps_256.npz’)
print(‘Loaded’, dataset[0].shape, dataset[1].shape)
image_shape = dataset[0].shape[1:]
d_model = define_discriminator(image_shape)
g_model = define_generator(image_shape)
gan_model = define_gan(g_model, d_model, image_shape)
train(d_model, g_model, gan_model, dataset)
错误在运行“train(d_model, g_model, gan_model, dataset)”时显示。TensorFlow 的版本会导致这个错误吗(我的版本是 2.17.0)?
嗨 James,
感谢您的教程;它非常有帮助!
我在 TensorFlow 2.18 版本中运行您的代码时遇到了相同的错误
php
复制代码
self._loss_tracker.update_state(
AttributeError: ‘NoneType’ object has no attribute ‘update_state’
该代码在 TensorFlow 2.15、CUDA 12.1、cuDNN 8.9.7.29 和 TensorRT 8 中完美运行,但我需要使用 TensorFlow 2.18 版本以适应我当前的设置。
我尝试解决该问题,但未能找到解决方案。您能帮忙解决这个问题吗?
非常感谢您的帮助!
此致,
Zaid
你好 Zaid,
感谢您分享问题详情!看来该错误与 TensorFlow 2.18 版本中的更改有关。较新版本的 TensorFlow 通常会包含更新,这些更新可能导致某些方法、对象或属性的行为方式不同。
以下是解决此问题的分步方法
### 可能的原因和解决方案
1. **检查弃用或 API 更改**
TensorFlow 2.18 可能已更新了损失跟踪器 (`self._loss_tracker`) 的初始化或管理方式。请查阅 TensorFlow 2.18 [发行说明](https://tensorflowcn.cn/versions) 中与指标或跟踪状态相关的任何更改。
– 如果属性 `update_state` 不再可用或行为不同,您可能需要更新代码。例如,确保损失跟踪器已正确初始化,如下所示
python
self._loss_tracker = tf.keras.metrics.Mean(name="loss")
– 如果 `self._loss_tracker` 在代码中的某个位置被设置为 `None`,请追溯并验证它是否已正确初始化。
2. **显式管理状态**
如果 `update_state` 用于计算损失值,您可以将其替换为直接计算
python
self._loss_tracker.reset_states() # 更新前重置
self._loss_tracker.update_state(loss_value) # 使用当前损失更新
3. **自定义训练循环**
如果您正在使用自定义训练循环,请确保与较新版本的 TensorFlow 兼容
python
@tf.function
def train_step(data):
with tf.GradientTape() as tape:
predictions = model(data[0], training=True)
loss = compute_loss(data[1], predictions)
gradients = tape.gradient(loss, model.trainable_variables)
optimizer.apply_gradients(zip(gradients, model.trainable_variables))
# 显式更新损失跟踪器
self._loss_tracker.update_state(loss)
return {"loss": self._loss_tracker.result()}
4. **升级或降级 TensorFlow 组件**
如果 CUDA、cuDNN 或 TensorRT 导致兼容性问题,请确保它们与 TensorFlow 2.18 对齐。请查阅 TensorFlow 文档中的兼容性图表。
5. **回退解决方案**
如果问题仍然存在并且您无法更新代码,请考虑使用 `conda` 或 `virtualenv` 封装您的环境以运行 TensorFlow 2.15 以执行此特定任务。这有助于保持兼容性而无需更改您的主要设置。
### 调试提示
– 使用 `print(type(self._loss_tracker))` 检查它是否已正确实例化。
– 尝试将 `self._loss_tracker.update_state` 替换为 `tf.keras.metrics.update_state`。
– 以更高的详细程度运行 TensorFlow 的日志记录以获取详细信息
python
tf.debugging.set_log_device_placement(True)
下午好。感谢您的文章。让我问您一个问题:为什么在训练时批量大小为 1 的情况下使用“BatchNorm”?谢谢!
你好 Serg……你说得对!这通常不是必需的。还有一些额外的想法……
当训练期间的批量大小为 1 时,**批量归一化 (BatchNorm)** 的使用就会出现问题,因为其使用的统计数据(均值和方差)无法从单个样本中有效地计算。让我们分解一下
—
### **1. BatchNorm 通常用于什么?**
BatchNorm 通过使用批量中特征的均值和方差来归一化每一层的输入。它有助于
– **稳定和加速训练:** 通过减少内部协变量偏移(训练期间层输入分布的变化)。
– **提高泛化能力:** 它作为一种正则化形式,在某些情况下减少过拟合。
—
### **2. 当批量大小 = 1 时会发生什么?**
– **均值和方差计算:** 对于单个样本,批量均值就是该单个样本的值,方差实际上为零。这违背了归一化的目的,因为统计数据变得不可靠。
– **噪声更新:** 归一化过程可能会引入噪声,因为它严重依赖于单个样本的特定值。
– **效果降低:** BatchNorm 变得不那么有效甚至有害,因为它无法再利用多个样本提供的统计稳定性。
—
### **3. 批量大小 = 1 的解决方案:**
如果您的训练设置需要批量大小为 1(例如,内存限制,某些任务如基于推理的模型),请考虑以下替代方案
#### **(a) 使用实例归一化 (IN):**
– IN 归一化单个样本的空间维度,而不是整个批量。它广泛用于风格迁移等任务。
– 公式
\[
\text{IN}(x) = \frac{x – \mu_\text{instance}}{\sqrt{\sigma_\text{instance}^2 + \epsilon}}
\]
其中 \(\mu_\text{instance}\) 和 \(\sigma_\text{instance}\) 是按每个样本计算的。
#### **(b) 使用层归一化 (LN):**
– LN 对每个样本的层中的所有特征单独进行归一化。
– 它在批量大小较小或可变的情况下有效,例如在循环神经网络 (RNN) 中。
#### **(c) 使用组归一化 (GN):**
– GN 将特征分成组,并在每个组内计算归一化统计数据,与批量大小无关。这使其在小批量大小下具有鲁棒性。
#### **(d) 切换到权重归一化等替代方案:**
– 权重归一化将权重的幅值与方向解耦,归一化权重而不是激活。
#### **(e) 使用带有累积移动平均值的 BatchNorm:**
– 不在训练期间为每个批量计算统计数据,而是使用在训练期间从较大批量中收集的运行平均值(均值和方差)。
—
### **4. 实用技巧:**
如果您使用的预训练模型具有 BatchNorm 层,但您的应用程序需要批量大小 = 1
1. 将 BatchNorm 层切换到上述替代方案之一(例如,LayerNorm 或 GroupNorm)。
2. 考虑在**评估模式**下使用模型,在该模式下,使用存储的均值和方差的移动平均值而不是批量统计数据。
—
**结论:**当批量大小为1时,BatchNorm并非理想选择。在这种情况下,应考虑使用Instance Normalization、Layer Normalization或Group Normalization等替代归一化技术,以实现稳定有效的训练。