生成对抗网络(GAN)是一种用于训练生成模型(例如用于生成图像的深度卷积神经网络)的架构。
GAN 的架构由生成器模型和判别器模型组成。生成器负责创建新的输出,例如可能来自原始数据集的图像。生成器模型通常使用深度卷积神经网络和专门的层来实现,这些层学习填充图像中的特征,而不是从输入图像中提取特征。
在生成器模型中可以使用的两种常见层是上采样层 (UpSampling2D),它简单地将输入的维度加倍,以及转置卷积层 (Conv2DTranspose),它执行逆卷积操作。
在本教程中,您将了解在生成图像时如何在生成对抗网络中使用 UpSampling2D 和 Conv2DTranspose 层。
完成本教程后,您将了解:
- GAN 架构中的生成模型需要对输入数据进行上采样才能生成输出图像。
- 上采样层是一个简单的层,没有权重,它会使输入的维度加倍,并且可以在生成模型中跟随传统卷积层使用。
- 转置卷积层是一个逆卷积层,它既可以对输入进行上采样,也可以在模型训练过程中学习如何填充细节。
立即开始您的项目,阅读我的新书《Python 生成对抗网络》,其中包含分步教程和所有示例的Python 源代码文件。
让我们开始吧。

生成对抗网络的上采样和转置卷积层入门介绍
照片作者:BLM Nevada,保留部分权利。
教程概述
本教程分为三个部分;它们是:
- GAN 中上采样的需求
- 如何使用上采样层
- 如何使用转置卷积层
生成对抗网络中上采样的需求
生成对抗网络是用于训练生成模型的神经网络架构。
该架构由生成器和判别器模型组成,两者均实现为深度卷积神经网络。判别器负责将图像分类为真实(来自域)或假(生成)。生成器负责生成来自问题域的新似真样本。
生成器通过将潜在空间中的一个随机点作为输入,以一次性方式输出完整的图像。
用于图像分类及相关任务的传统卷积神经网络会使用池化层对输入图像进行下采样。例如,平均池化或最大池化层会将卷积层的特征图在每个维度上减半,从而使输出的面积是输入面积的四分之一。
卷积层本身也执行一种下采样,方法是将每个滤波器应用于输入图像或特征图;由于边界效应,产生的激活是输出特征图,它更小。通常使用填充来抵消这种影响。
GAN 中的生成器模型需要与传统卷积层中的池化层相反的操作。它需要一个层来从粗略的显著特征转换到更密集、更详细的输出。
未池化或反池化层的一个简单版本称为上采样层。它通过重复输入的行和列来工作。
更精细的方法是执行反向卷积操作,最初称为反卷积,这是不正确的,但更常称为分数卷积层或转置卷积层。
这两种层都可以在 GAN 上使用,以执行所需上采样操作,将小输入转换为大图像输出。
在接下来的部分中,我们将仔细研究每个层,并建立对它们工作原理的直观理解,以便我们在 GAN 模型中有效地使用它们。
如何使用 UpSampling2D 层
上采样输入的可能最简单的方法是加倍每一行和每一列。
例如,一个 2×2 的输入图像将输出为 4×4。
1 2 3 4 5 6 7 |
1, 2 输入 = (3, 4) 1, 1, 2, 2 输出 = (1, 1, 2, 2) 3, 3, 4, 4 3, 3, 4, 4 |
使用 UpSampling2D 层的实际示例
Keras 深度学习库在名为 *UpSampling2D* 的层中提供了此功能。
它可以添加到卷积神经网络中,并重复输入到输出中的行和列。例如:
1 2 3 4 |
... # 定义模型 model = Sequential() model.add(UpSampling2D()) |
我们可以通过一个简单的示例来演示此层的行为。
首先,我们可以定义一个 2×2 像素的示例文本输入图像。我们可以为每个像素使用特定值,以便在上采样后,我们可以确切地看到操作对输入的影响。
1 2 3 4 5 6 |
... # 定义输入数据 X = asarray([[1, 2], [3, 4]]) # 为上下文显示输入数据 print(X) |
一旦定义了图像,我们必须添加一个通道维度(例如灰度)以及一个样本维度(例如我们有 1 个样本),以便将其作为输入传递给模型。
1 2 3 |
... # 将输入数据重塑为一个样本,一个带有通道的样本 X = X.reshape((1, 2, 2, 1)) |
现在我们可以定义我们的模型。
模型只包含 *UpSampling2D* 层,该层直接接收 2×2 灰度图像作为输入,并输出上采样操作的结果。
1 2 3 4 5 6 |
... # 定义模型 model = Sequential() model.add(UpSampling2D(input_shape=(2, 2, 1))) # 总结模型 model.summary() |
然后,我们可以使用该模型进行预测,即对提供的输入图像进行上采样。
1 2 3 |
... # 使用模型进行预测 yhat = model.predict(X) |
输出将具有与输入相同的四个维度,因此,我们可以将其转换回 2×2 数组,以便更轻松地查看结果。
1 2 3 4 5 |
... # 重塑输出以删除通道,以便更轻松地打印 yhat = yhat.reshape((4, 4)) # 总结输出 print(yhat) |
将所有这些结合起来,下面提供了在 Keras 中使用 *UpSampling2D* 层的完整示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# 使用上采样层的示例 from numpy import asarray from keras.models import Sequential from keras.layers import UpSampling2D # 定义输入数据 X = asarray([[1, 2], [3, 4]]) # 为上下文显示输入数据 print(X) # 将输入数据重塑为一个样本,一个带有通道的样本 X = X.reshape((1, 2, 2, 1)) # 定义模型 model = Sequential() model.add(UpSampling2D(input_shape=(2, 2, 1))) # 总结模型 model.summary() # 使用模型进行预测 yhat = model.predict(X) # 重塑输出以删除通道,以便更轻松地打印 yhat = yhat.reshape((4, 4)) # 总结输出 print(yhat) |
运行该示例,首先创建并总结我们的 2×2 输入数据。
接下来,总结模型。我们可以看到它将输出 4×4 的结果,正如我们所预期的,并且重要的是,该层没有参数或模型权重。这是因为它没有学习任何东西;它只是将输入加倍。
最后,模型用于对我们的输入进行上采样,从而使我们的输入数据的每一行和每一列都加倍,正如我们预期的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
[[1 2] [3 4]] _________________________________________________________________ 层(类型) 输出形状 参数数量 ================================================================= up_sampling2d_1 (UpSampling2 (None, 4, 4, 1) 0 ================================================================= 总参数:0 可训练参数:0 不可训练参数: 0 _________________________________________________________________ [[1. 1. 2. 2.] [1. 1. 2. 2.] [3. 3. 4. 4.] [3. 3. 4. 4.]] |
默认情况下,*UpSampling2D* 会将每个输入维度加倍。这由“*size*”参数定义,该参数设置为元组 (2,2)。
您可能希望在每个维度上使用不同的因子,例如将宽度加倍并将高度加三倍。这可以通过将“*size*”参数设置为 (2, 3) 来实现。将此操作应用于 2×2 图像的结果将是一个 4×6 输出图像(例如 2×2 和 2×3)。例如:
1 2 3 |
... # 使用每个维度的不同缩放因子的示例 model.add(UpSampling2D(size=(2, 3))) |
此外,默认情况下,*UpSampling2D* 层将使用最近邻算法来填充新行和列。这会简单地加倍行和列,如所述,并通过设置为“*nearest*”的“*interpolation*”参数指定。
或者,可以使用双线性插值方法,该方法基于多个周围点。这可以通过将“*interpolation*”参数设置为“*bilinear*”来指定。例如:
1 2 3 |
... # 使用双线性插值上采样时的示例 model.add(UpSampling2D(interpolation='bilinear')) |
想从零开始开发GAN吗?
立即参加我为期7天的免费电子邮件速成课程(附示例代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
使用 UpSampling2D 层的简单生成器模型
*UpSampling2D* 层简单有效,但它不执行任何学习。
它无法在上采样操作中填充有用的细节。要能在 GAN 中使用,每个 *UpSampling2D* 层后必须跟一个 Conv2D 层,该层将学习解释加倍的输入,并进行训练以将其转换为有意义的细节。
我们可以通过一个示例来演示这一点。
在这种情况下,我们的小型 GAN 生成器模型必须生成一个 10×10 的图像,并以 100 个元素的向量作为输入。
首先,可以使用一个 Dense 全连接层来解释输入向量并创建足够数量的激活(输出),这些激活可以重塑为输出图像的低分辨率版本,在本例中是 5×5 图像的 128 个版本。
1 2 3 4 5 6 7 |
... # 定义模型 model = Sequential() # 定义输入形状,输出足够的激活,以生成 128 个 5x5 图像 model.add(Dense(128 * 5 * 5, input_dim=100)) # 将激活向量重塑为 128 个 5x5 的特征图 model.add(Reshape((5, 5, 128))) |
接下来,可以将 5×5 的特征图上采样到 10×10 的特征图。
1 2 3 |
... # 将输入从 128 个 5x5 加倍到 1 个 10x10 的特征图 model.add(UpSampling2D()) |
最后,Conv2D 层可以解释上采样后的特征图并填充有用的细节。
Conv2D 只有一个输出特征图,以创建我们需要的单个图像。
1 2 3 |
... # 填充上采样特征图的细节 model.add(Conv2D(1, (3,3), padding='same')) |
将这些结合起来,完整的示例列在下面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# 在简单生成器模型中使用上采样的示例 from keras.models import Sequential from keras.layers import Dense from keras.layers import Reshape from keras.layers import UpSampling2D from keras.layers import Conv2D # 定义模型 model = Sequential() # 定义输入形状,输出足够的激活,以生成 128 个 5x5 图像 model.add(Dense(128 * 5 * 5, input_dim=100)) # 将激活向量重塑为 128 个 5x5 的特征图 model.add(Reshape((5, 5, 128))) # 将输入从 128 个 5x5 加倍到 1 个 10x10 的特征图 model.add(UpSampling2D()) # 填充上采样特征图的细节并输出单个图像 model.add(Conv2D(1, (3,3), padding='same')) # 总结模型 model.summary() |
运行示例会创建模型并总结每个层的输出形状。
我们可以看到 Dense 层输出 3200 个激活,然后这些激活被重塑为 128 个形状为 5×5 的特征图。
*UpSampling2D* 层将宽度和高度加倍到 10×10,从而产生具有四倍面积的特征图。
最后,Conv2D 处理这些特征图并添加细节,输出一个 10×10 的单个图像。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
_________________________________________________________________ 层(类型) 输出形状 参数数量 ================================================================= dense_1 (Dense) (None, 3200) 323200 _________________________________________________________________ reshape_1 (Reshape) (None, 5, 5, 128) 0 _________________________________________________________________ up_sampling2d_1 (UpSampling2 (None, 10, 10, 128) 0 _________________________________________________________________ conv2d_1 (Conv2D) (None, 10, 10, 1) 1153 ================================================================= 总参数:324,353 可训练参数:324,353 不可训练参数: 0 _________________________________________________________________ |
如何使用 Conv2DTranspose 层
Conv2DTranspose 或转置卷积层比简单的上采样层更复杂。
一种简单的理解方式是,它既执行上采样操作,又在进行上采样的同时解释粗略的输入数据以填充细节。这就像一个层将 *UpSampling2D* 和 Conv2D 层结合在一起。这是一个粗略的理解,但一个实用的起点。
转置卷积的需求通常源于希望使用与正常卷积相反方向的变换,即从具有某些卷积输出形状的事物转换为具有其输入形状的事物,同时保持与所述卷积兼容的连接模式。
— 深度学习卷积算术指南,2016。
事实上,转置卷积层执行的是逆卷积操作。
具体来说,卷积层的正向和反向传播被颠倒了。
一种说法是,核定义了卷积,但它是直接卷积还是转置卷积取决于正向和反向传播的计算方式。
— 深度学习卷积算术指南,2016。
它有时被称为反卷积或反卷积层,使用这些层的模型可以被称为反卷积网络或 deconvnets。
反卷积网络可以被认为是使用相同组件(滤波、池化)但顺序相反的 convnet 模型,因此它不是将像素映射到特征,而是做相反的事情。
— 可视化和理解卷积网络,2013。
将此操作称为反卷积在技术上是不正确的,因为反卷积是一个不被该层执行的特定数学操作。
事实上,传统 卷积层 在技术上并不执行卷积操作,它执行的是互相关。
人们通常提到的反卷积层,在 Zeiler 的论文中首次出现,作为反卷积网络的一部分,但没有特定名称……它也有许多名称,包括(但不限于)子像素或分数卷积层、转置卷积层、逆卷积、上采样或反向卷积层。
— 反卷积层与卷积层相同吗?,2016。
它是一个非常灵活的层,尽管我们将重点关注它在生成模型中用于对输入图像进行上采样。
转置卷积层非常像正常的卷积层。它要求您指定滤波器的数量以及每个滤波器的核大小。该层的关键是步幅。
通常,卷积层的步幅为 (1×1),即滤波器在每次从左到右读取时沿水平方向移动一个像素,然后向下移动一个像素进行下一行读取。正常卷积层的 2×2 步幅具有下采样输入的效果,很像池化层。事实上,在判别器模型中可以使用 2×2 步幅代替池化层。
转置卷积层就像一个逆卷积层。因此,您会直观地认为 2×2 步幅会向上采样输入而不是下采样,这正是发生的情况。
步幅或跨度是指在传统卷积层中,滤波器扫描输入的模式。而在转置卷积层中,步幅是指特征图中输出的布局方式。
这个效果可以用一个正常的卷积层通过插入分数输入步幅(f)来实现,例如使用 f=1/2 的步幅。当反转时,输出步幅设置为该分数的分子,例如 f=2。
从某种意义上说,用因子 f 进行上采样是具有分数输入步幅 1/f 的卷积。只要 f 是整数,因此上采样的自然方法就是反向卷积(有时称为反卷积),输出步幅为 f。
— 用于语义分割的全卷积网络,2014。
可以用正常卷积层实现这种效果的一种方法是在输入数据中插入新的行和列(值为 0.0)。
最后请注意,总是可以使用直接卷积来模拟转置卷积。缺点是它通常涉及在输入中添加许多列和行……
— 深度学习卷积算术指南,2016。
让我们用一个例子来具体说明。
考虑一个具有 2×2 大小的输入图像,如下所示:
1 2 |
1, 2 输入 = (3, 4) |
假设一个具有 1×1 核的单个滤波器和模型权重,这些权重在输出时不会改变输入(例如,模型权重为 1.0,偏置为 0.0),则输出步幅为 1×1 的转置卷积操作将原样复制输出。
1 2 |
1, 2 输出 = (3, 4) |
使用 (2,2) 的输出步幅时,1×1 卷积需要插入额外的行和列到输入图像中,以便可以执行操作的读取。因此,输入如下所示:
1 2 3 4 |
1, 0, 2, 0 输入 = (0, 0, 0, 0) 3, 0, 4, 0 0, 0, 0, 0 |
然后模型可以使用 2×2 的输出步幅读取此输入,并将输出一个 4×4 的图像,在本例中没有变化,因为我们的模型权重按设计没有影响。
1 2 3 4 |
1, 0, 2, 0 输出 = (0, 0, 0, 0) 3, 0, 4, 0 0, 0, 0, 0 |
使用 Conv2DTranspose 层的实际示例
Keras 通过 Conv2DTranspose 层提供了转置卷积功能。
它可以直接添加到您的模型中;例如:
1 2 3 4 |
... # 定义模型 model = Sequential() model.add(Conv2DTranspose(...)) |
我们可以通过一个简单的示例来演示此层的行为。
首先,我们可以定义一个 2×2 像素的示例文本输入图像,就像我们在上一节中所做的那样。我们可以为每个像素使用特定值,以便在转置卷积操作后,我们可以确切地看到操作对输入的影响。
1 2 3 4 5 6 |
... # 定义输入数据 X = asarray([[1, 2], [3, 4]]) # 为上下文显示输入数据 print(X) |
一旦定义了图像,我们必须添加一个通道维度(例如灰度)以及一个样本维度(例如我们有 1 个样本),以便将其作为输入传递给模型。
1 2 3 |
... # 将输入数据重塑为一个样本,一个带有通道的样本 X = X.reshape((1, 2, 2, 1)) |
现在我们可以定义我们的模型。
模型只包含 *Conv2DTranspose* 层,该层直接接收 2×2 灰度图像作为输入,并输出操作的结果。
Conv2DTranspose 既进行上采样又执行卷积。因此,我们必须指定滤波器的数量和滤波器的尺寸,就像我们为 Conv2D 层所做的那样。此外,我们必须指定一个 2×2 的步幅,因为上采样是通过卷积对输入实现的步幅行为。
指定 2×2 的步幅会间隔输入。具体来说,会插入 0.0 值的行和列以实现所需的步幅。
在此示例中,我们将使用一个滤波器,具有 1×1 的核和 2×2 的步幅,以便将 2×2 的输入图像上采样到 4×4。
1 2 3 4 5 6 |
... # 定义模型 model = Sequential() model.add(Conv2DTranspose(1, (1,1), strides=(2,2), input_shape=(2, 2, 1))) # 总结模型 model.summary() |
为了清楚地说明 *Conv2DTranspose* 层的作用,我们将单个滤波器中的单个权重固定为 1.0,并将偏置值设置为 0.0。
这些权重,以及 1×1 的核大小,意味着输入中的值将被乘以 1 并按原样输出,而通过 2×2 步幅添加的新行和列中的 0 值将被输出为 0(例如,在每种情况下为 1 * 0)。
1 2 3 4 5 |
... # 定义权重,使它们不起作用 weights = [asarray([[[[1]]]]), asarray([0])] # 将权重存储在模型中 model.set_weights(weights) |
然后,我们可以使用该模型进行预测,即对提供的输入图像进行上采样。
1 2 3 |
... # 使用模型进行预测 yhat = model.predict(X) |
输出将具有与输入相同的四个维度,因此,我们可以将其转换回 2×2 数组,以便更轻松地查看结果。
1 2 3 4 5 |
... # 重塑输出以删除通道,以便更轻松地打印 yhat = yhat.reshape((4, 4)) # 总结输出 print(yhat) |
将所有这些结合起来,下面提供了在 Keras 中使用 *Conv2DTranspose* 层的完整示例。
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 |
# 使用转置卷积层的示例 from numpy import asarray from keras.models import Sequential from keras.layers import Conv2DTranspose # 定义输入数据 X = asarray([[1, 2], [3, 4]]) # 为上下文显示输入数据 print(X) # 将输入数据重塑为一个样本,一个带有通道的样本 X = X.reshape((1, 2, 2, 1)) # 定义模型 model = Sequential() model.add(Conv2DTranspose(1, (1,1), strides=(2,2), input_shape=(2, 2, 1))) # 总结模型 model.summary() # 定义权重,使它们不起作用 weights = [asarray([[[[1]]]]), asarray([0])] # 将权重存储在模型中 model.set_weights(weights) # 使用模型进行预测 yhat = model.predict(X) # 重塑输出以删除通道,以便更轻松地打印 yhat = yhat.reshape((4, 4)) # 总结输出 print(yhat) |
运行该示例,首先创建并总结我们的 2×2 输入数据。
接下来,总结模型。我们可以看到它将输出 4×4 的结果,正如我们所预期的,并且重要的是,该层有两个参数或模型权重。一个用于单个 1×1 滤波器,一个用于偏置。与 *UpSampling2D* 层不同,*Conv2DTranspose* 将在训练期间学习,并将尝试在 upsampling 过程中填充细节。
最后,模型用于对我们的输入进行上采样。我们可以看到,涉及真实值作为输入的单元格计算会产生真实值作为输出(例如 1×1、1×2 等)。我们可以看到,在通过 2×2 步幅插入新行和列的地方,它们的 0.0 值乘以单个 1×1 滤波器中的 1.0 值,结果是输出中的 0 值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
[[1 2] [3 4]] _________________________________________________________________ 层(类型) 输出形状 参数数量 ================================================================= conv2d_transpose_1 (Conv2DTr (None, 4, 4, 1) 2 ================================================================= 总参数:2 可训练参数:2 不可训练参数: 0 _________________________________________________________________ [[1. 0. 2. 0.] [0. 0. 0. 0.] [3. 0. 4. 0.] [0. 0. 0. 0.]] |
请记住:这是一个示例文本案例,我们在其中人为地指定了模型权重,以便可以看到转置卷积操作的效果。
实际上,我们将使用大量滤波器(例如 64 或 128),更大的核(例如 3×3、5×5 等),并且该层将使用随机权重进行初始化,这些权重将在训练期间学习如何有效地填充细节。
事实上,您可以想象不同大小的核将产生不同大小的输出,输出的宽度和高度会增加一倍以上。在这种情况下,可以将层的“*padding*”参数设置为“*same*”以强制输出具有所需的(加倍的)输出形状;例如:
1 2 3 |
... # 使用填充确保输出仅加倍的示例 model.add(Conv2DTranspose(1, (3,3), strides=(2,2), padding='same', input_shape=(2, 2, 1))) |
使用 Conv2DTranspose 层的简单生成器模型
*Conv2DTranspose* 比 *UpSampling2D* 层更复杂,但在 GAN 模型,特别是生成器模型中使用时也很有效。
两种方法都可以使用,但 *Conv2DTranspose* 层可能更受青睐,或许是因为更简单的生成器模型和可能更好的结果,尽管 GAN 的性能和技巧度量起来非常困难。
我们可以通过另一个简单的示例来演示在生成器模型中使用 *Conv2DTranspose* 层。
在这种情况下,我们的小型 GAN 生成器模型必须生成一个 10×10 的图像,并以 100 个元素的向量作为输入,就像在之前的 *UpSampling2D* 示例中一样。
首先,可以使用一个 Dense 全连接层来解释输入向量并创建足够数量的激活(输出),这些激活可以重塑为输出图像的低分辨率版本,在本例中是 5×5 图像的 128 个版本。
1 2 3 4 5 6 7 |
... # 定义模型 model = Sequential() # 定义输入形状,输出足够的激活,以生成 128 个 5x5 图像 model.add(Dense(128 * 5 * 5, input_dim=100)) # 将激活向量重塑为 128 个 5x5 的特征图 model.add(Reshape((5, 5, 128))) |
接下来,可以将 5×5 的特征图上采样到 10×10 的特征图。
我们将为单个滤波器使用 3×3 的核大小,这将导致输出特征图的宽度和高度略大于两倍(11×11)。
因此,我们将“*padding*”设置为“*same*”以确保输出尺寸为所需的 10×10。
1 2 3 |
... # 将输入从 128 个 5x5 加倍到 1 个 10x10 的特征图 model.add(Conv2DTranspose(1, (3,3), strides=(2,2), padding='same')) |
将这些结合起来,完整的示例列在下面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# 在简单生成器模型中使用转置卷积的示例 from keras.models import Sequential from keras.layers import Dense from keras.layers import Reshape from keras.layers import Conv2DTranspose from keras.layers import Conv2D # 定义模型 model = Sequential() # 定义输入形状,输出足够的激活,以生成 128 个 5x5 图像 model.add(Dense(128 * 5 * 5, input_dim=100)) # 将激活向量重塑为 128 个 5x5 的特征图 model.add(Reshape((5, 5, 128))) # 将输入从 128 个 5x5 加倍到 1 个 10x10 的特征图 model.add(Conv2DTranspose(1, (3,3), strides=(2,2), padding='same')) # 总结模型 model.summary() |
运行示例会创建模型并总结每个层的输出形状。
我们可以看到 Dense 层输出 3200 个激活,然后这些激活被重塑为 128 个形状为 5×5 的特征图。
Conv2DTranspose 层将宽度和高度加倍到 10×10,从而产生一个四倍面积的单个特征图。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
_________________________________________________________________ 层(类型) 输出形状 参数数量 ================================================================= dense_1 (Dense) (None, 3200) 323200 _________________________________________________________________ reshape_1 (Reshape) (None, 5, 5, 128) 0 _________________________________________________________________ conv2d_transpose_1 (Conv2DTr (None, 10, 10, 1) 1153 ================================================================= 总参数:324,353 可训练参数:324,353 不可训练参数: 0 _________________________________________________________________ |
进一步阅读
如果您想深入了解,本节提供了更多关于该主题的资源。
论文
- 深度学习卷积算术指南, 2016.
- 反卷积网络, 2010.
- 反卷积层与卷积层相同吗?, 2016.
- 可视化和理解卷积网络, 2013.
- 用于语义分割的全卷积网络, 2014.
API
文章
总结
在本教程中,您了解了在生成图像时如何在生成对抗网络中使用 UpSampling2D 和 Conv2DTranspose 层。
具体来说,你学到了:
- GAN 架构中的生成模型需要对输入数据进行上采样才能生成输出图像。
- 上采样层是一个简单的层,没有权重,它会使输入的维度加倍,并且可以在生成模型中跟随传统卷积层使用。
- 转置卷积层是一个逆卷积层,它既可以对输入进行上采样,也可以在模型训练过程中学习如何填充细节。
你有什么问题吗?
在下面的评论中提出你的问题,我会尽力回答。
我相信转置卷积层由于伪影问题不再用于生成模型。上采样(即最近邻重采样)是标准。
这 realmente 取决于模型。
对于简单的 DCGAN,它们效果很好;对于更大的渐进式加载 GAN、StyleGAN 等,则使用上采样层。
我尝试了多次卷积,我的理解是,我们有128个5x5的图像,并伴有不同的卷积。当我们进行上采样时,我们得到128个10x10的图像。但是,当我们只有一个128个、1通道的滤波器时,它怎么会变成一个10x10x1的图像呢?
因为滤波器有128个,但每个滤波器只连接到一个通道。所以,我假设这是一个单通道多滤波器卷积。?剩余的卷积不应该是128个10x10的图像(尤其是在应用填充以确保尺寸保持不变之后)吗?
你可能需要调试你的例子,找出你误解的原因,我还不清楚。
这太棒了。这些博客文章非常有价值,因为它们解释了幕后发生的事情。
非常感谢Jason。我非常感激,并且很喜欢阅读您的这些博文。
工作很出色,请继续!
此致
谢谢Jack,很高兴能帮到你!
感谢这篇博文。关于转置卷积,填充的值是否仅限于“0”?
如果是这样,该值将始终等于0,并且仅取决于偏置项。
是的,会添加零值,因为它们对计算没有影响。
不确定这是否回答了你的问题?
很棒的教程!我喜欢在定义层、分配用户定义的权重和预测输出时采用“人为设计但刻意为之”的方法。
我确实有一个关于Conv2DTranspose示例的问题。
1) 假设 X = [[1,2],[3,4]],并且层是“model.add(Conv2DTranspose(1, (2,2), strides=(2,2), input_shape=(2,2,1)))”。那么上采样后的输入将是:[[1,0,2,0], [0,0,0,0], [3,0,4,0], [0,0,0,0]]。当我应用model.predict(X)时,输出看起来像一个步长为2的“平铺”,而不是步长为2的“2dconv”。换句话说,无论模型的权重是多少,即2x2的核[[a,b],[c,d]],它们都会与输入进行逐元素相乘:分别乘以1、2、3和4,然后连接在一起,例如:
[a, b, 2a, 2b],
[c, d, 2c, 2d],
[3a,3b,4a, 4b],
[3c,3d,4c, 4d]
]
这是一个具体的例子
/*
model = Sequential()
model.add(Conv2DTranspose(1,(2,2), strides=(2,2), padding=’valid’, input_shape=(2,2,1)))
model.weights
# output
[,
]
X = np.array([[1,2],[3,4]]).reshape((1,2,2,1))
model.predict(X).reshape((4,4))
#Output
array([[-0.28302956, 0.67811257, -0.5660591 , 1.3562251 ],
[ 0.19257468, -0.58342797, 0.38514936, -1.1668559 ],
[-0.84908867, 2.0343378 , -1.1321182 , 2.7124503 ],
[ 0.57772404, -1.750284 , 0.7702987 , -2.3337119 ]],
dtype=float32)
/*
从这个意义上说,我认为[Convolution Arithmetic Project, GitHub.]中显示的动画,特别是[No padding, strides, transposed]面板,有些误导。
谢谢!
很好。感谢分享。
我使用conv2dtranspose构建了一个模型,该模型可以将图像分辨率从14x14提高到28x28。有没有办法上传一张新的任意分辨率的图像并将其输入模型以获得两倍分辨率的图像?
干得不错。
我认为你指的是超分辨率。我没有这方面的例子,但也许可以试试谷歌搜索?
谢谢你
不客气。
嗨,Jason,
感谢这篇精彩的博文!
我正在尝试在keras中使用Conv3DTranspose,但它表现得像Conv3D而不是Conv3DTranspose,也就是说,尺寸是减半而不是加倍?我的tensorflow版本是1.4。
抱歉,我没有Conv3DTranspose的教程,无法就此给出好的建议。
你尝试用步长0.5而不是2了吗?
非常清楚。感谢您一如既往的高质量博文。
谢谢!
非常欢迎。
这是一个非常有用的教程。我一直在到处寻找,终于找到了这篇完美解释其根本的博文。做得好!!
不客气!
你好Jason博士。感谢您的教程。
我正在为Conv2DTranspose相关的代码块中的输出形状而苦恼。
stride = (2, 2), kernel_size = (1, 1)
您的输出形状和TensorFlow代码的输出形状匹配,但我无法理解为什么输出中会包含最后一行零,因为允许的步长数已用完,而核大小为(1x1)?
我阅读了arXiv上的PDF“A guide to convolution arithmetic for deep learning”,其中,在转置卷积的最后一节(第26页)提供了两个用于计算输出形状的公式。
公式1) out_shape = s(i − 1) + k − 2p
公式2) out_shape = s(i − 1) + a + k − 2p; 其中a = (n + 2p – k) % s
s=步长, i=输入尺寸, k=核尺寸, p=填充
在TensorFlow的文档中,输出形状的计算如下:
new_rows = ((rows – 1) * strides[0] + kernel_size[0] – 2 * padding[0] +
output_padding[0])
new_cols = ((cols – 1) * strides[1] + kernel_size[1] – 2 * padding[1] +
output_padding[1])
并且Pytorch文档也一样。它们都与公式1相同。
我深入研究了TensorFlow的这个函数实现,发现它们的计算方式是:
out_size = input * stride + max(kernel – stride, 0)
我继续进行并创建了一些案例来理解,现在我迷失了。
案例1) 输入:2,步长:1,核:1
公式1:输出形状:2
公式2:输出形状:2
TF/Pytorch文档:输出形状:2
TF代码输出:输出形状:2
案例2) 输入:2,步长:1,核:3
公式1:输出形状:4
公式2:输出形状:4
TF/Pytorch文档:输出形状:4
TF代码输出:输出形状:4
从这里开始变得复杂
案例3) 输入:2,步长:2,核:1
公式1:输出形状:3
公式2:输出形状:4(与本教程相同)
TF/Pytorch文档:输出形状:3
TF代码输出:输出形状:4
案例4) 输入:2,步长:2,核:3
公式1:输出形状:5
公式2:输出形状:6
TF/Pytorch文档:输出形状:5
TF代码输出:输出形状:5
这些都是在padding = “Valid”下的情况。
哪个是正确的?我迷茫了。
是否存在一个像普通卷积过程那样的单一公式来正确计算输出形状?
任何帮助都将不胜感激,我太困惑了。
我认为你在问1x1卷积。如果是这样,也许这会有帮助
https://machinelearning.org.cn/introduction-to-1×1-convolutions-to-reduce-the-complexity-of-convolutional-neural-networks/
感谢您精彩的解释!
下一步,我想了解如何设计卷积核大小、步长和填充大小。例如,如果输入形状是(1024, 4, 4),以下不同的转置卷积层设置会产生相同的输出尺寸,即(512, 16, 16)。
设置A:kernel=4, stride=2, padding=1
设置B:kernel=5, stride=1, padding=0
有什么设计它们的线索吗?
是的,通常放大或缩小涉及每个轴的加倍或减半。
也许可以看看一些已完成的例子,例如带有Unet的GAN(在博客上搜索)。
嗨,Jason,
如何获得这些机器学习项目的就业机会?
如果你是招聘人员,除了概念之外,你真正想看到哪些项目作为机器学习工程师?
我想为工作做好准备,创建机器学习的投资组合项目。
这取决于你的兴趣或你想追求的工作,公司做什么等等。
尊敬的Jason博士,
在一个例子中,你用了这个表达式
将1嵌套在四个方括号中的目的是什么?为什么0只在单个方括号中?
谢谢你
悉尼的Anthony
据我回忆,那是模型期望的结构,无疑是类似[model[layer[node[weight]]]]的东西。
尊敬的Jason博士,
谢谢你的回复。
我搜索了结构
和
但找不到任何笔记。
您能否向我指出关于模型期望的结构(包括model[layer[node[weight]]]]的顺序)的笔记?
谢谢你,
悉尼的Anthony
谢谢您,
悉尼的Anthony
我不记得有关于此主题的笔记,我认为我通过检查代码弄清楚了所需的结构——那已经是几年前的事了。
尊敬的Jason博士,
我尝试了https://keras.org.cn/api/layers/base_layer/ 上的文档,但无法将其与本教程中的set_weights关联起来,其中
文档中没有任何内容与本教程相关。
谢谢你,
悉尼的Anthony
您可以通过检查代码本身来了解模型权重的形状,或者检索模型的权重并查看它们的形状。我记得这相对简单。
尊敬的Jason博士,
我获得了模型的权重
我仍然无法将权重与给定的输入关联起来
为什么权重是一个列表,第一个参数是[[[[0]]]]——这是什么意思?
设置权重有什么作用?权重是多少?
谢谢你,
悉尼的Anthony
正如我之前解释的,这是一个具有给定维数数量的数组。
我们可以手动设置权重进行小型实验,例如将它们设置为1以查看不同操作的效果——就像我在卷积和池化操作介绍中所做的那样。在实践中,我们不会手动设置权重。
尊敬的Jason博士,
再次感谢您的回复。我明白权重不是手动设置的。权重是通过计算每个神经元的权重得出的。
背景——请跳过并转到问题
尽管如此,我仍然想理解权重的结构。我使用了您在https://machinelearning.org.cn/handwritten-digit-recognition-using-convolutional-neural-networks-python-keras/ 上的教程中的model.get_weights()
代码片段
请问
* 鉴于有两个层,为什么会有三个784(等于num_pixels)权重的层和另一个10(等于num_pixels)权重的层——请参考define_baseline()代码。
谢谢,
悉尼的Anthony
我相信有计算层权重数量的标准公式,抱歉,我一时记不起来了。
你好
我想实现一个GAN来减少压缩伪影,我不知道如何做,你能给我一些想法吗?
也许你可以将其建模为图像翻译任务,使用CycleGAN或Pix2Pix GAN。
你好,我可以知道在哪些应用中使用转置卷积与上采样版本1和卷积层的组合吗?
你好 Vijay…你可能会发现以下信息有帮助
https://dsp.stackexchange.com/questions/3228/practical-applications-of-upsampling-and-downsampling
你好 James,感谢您的回复。我只想知道在“Simple Generator Model With the UpSampling2D Layer”主题下,它显示了使用
model.add(UpSampling2D())
# 填充上采样特征图的细节并输出单个图像
model.add(Conv2D(1, (3,3), padding=’same’))
在深度学习模型中,我们是否有任何标准应用使用这种上采样层版本1和卷积层?我只看到了与上采样层版本2用于转置卷积层相关的应用。
你好 James,感谢您的回复。我只想知道在“Simple Generator Model With the UpSampling2D Layer”主题下,它显示了使用
model.add(UpSampling2D())
# 填充上采样特征图的细节并输出单个图像
model.add(Conv2D(1, (3,3), padding=’same’))
在深度学习模型中,我们是否有任何标准应用使用这种上采样层版本1和卷积层?我只看到了与上采样层版本2用于转置卷积层相关的应用。