在Keras中,“Flatten”的作用是什么?

187

我正在尝试理解Keras中Flatten函数的作用。以下是我的代码,这是一个简单的两层神经网络。它接收形状为(3,2)的二维数据,并输出形状为(1,4)的一维数据:

model = Sequential()
model.add(Dense(16, input_shape=(3, 2)))
model.add(Activation('relu'))
model.add(Flatten())
model.add(Dense(4))
model.compile(loss='mean_squared_error', optimizer='SGD')

x = np.array([[[1, 2], [3, 4], [5, 6]]])

y = model.predict(x)

print y.shape

这将打印出 y 的形状为 (1, 4)。然而,如果我移除 Flatten 行,那么它会打印出 y 的形状为 (1, 3, 4)。

我不理解这个问题。根据我对神经网络的理解,model.add(Dense(16, input_shape=(3, 2))) 函数创建了一个具有 16 个节点的隐藏全连接层。每个节点都与 3x2 个输入元素中的每个元素相连。因此,第一层的这 16 个节点已经是“扁平”的了。所以,第一层的输出形状应该是 (1, 16)。然后,第二层以此作为输入,输出形状为 (1, 4) 的数据。

因此,如果第一层的输出已经是“扁平”的且形状为 (1, 16),那么我为什么还需要进一步压缩它?


了解Flatten并将其与GlobalPooling进行比较可能会很有用 https://dev59.com/AVUM5IYBdhLWcg3wINak#63502664 - Marco Cerliani
10个回答

182
如果您阅读Keras文档中有关Dense的条目,您会看到这个调用:
Dense(16, input_shape=(5,3))

将会得到一个具有 3 个输入和 16 个输出的 Dense 神经网络,在每个时间步骤中都将独立应用。因此,如果 D(x) 可以将 3 维向量转化为 16 维向量,则图层的输出将是由向量序列组成:[D(x[0,:]), D(x[1,:]),..., D(x[4,:])] 其形状为 (5, 16)。为了获得所需的行为,您可能首先需要使用 Flatten 将输入展平为一个 15 维向量,然后再应用Dense

model = Sequential()
model.add(Flatten(input_shape=(3, 2)))
model.add(Dense(16))
model.add(Activation('relu'))
model.add(Dense(4))
model.compile(loss='mean_squared_error', optimizer='SGD')

编辑: 由于有些人难以理解,这里提供一张解释性的图片:

输入图像描述此处


1
你有一个Dense层,其中有3个神经元并输出16个神经元,应用于5组3个神经元的每个神经元。 - Marcin Możejko
1
啊,好的。我想做的是将一个包含5个颜色像素的列表作为输入,并希望它们通过一个全连接层。所以input_shape=(5,3)意味着有5个像素,每个像素有三个通道(R、G、B)。但根据你所说的,每个通道都会被单独处理,而我希望所有三个通道都由第一层中的所有神经元进行处理。那么,在开始时立即应用“Flatten”层是否能给我想要的结果? - Karnivaurus
14
一张带有和不带有Flatten的小图可以帮助理解。 - Xvolks
1
@Xvolks,你能分享一下图纸吗?我想要理解这个。 - Gásten
6
好的,我为大家提供了一张图片。现在你们可以取消踩我的操作了。 - Marcin Możejko
显示剩余10条评论

111

enter image description here 这是Flatten函数将矩阵转换为单一数组的工作原理。


44
是的,但为什么需要它,这才是我认为实际的问题。 - Helen
2
一张图片胜过千言万语。 - Hom Bahrani
根据我的理解,展平是用于减少输入层的维度。密集层期望一个行向量(在数学上仍然是一个多维对象),其中每列对应于密集层的特征输入,因此基本上是Numpy的reshape的方便等价物:)。实际上,展平是一件非常通用的事情。例如,在硬件上,您可能希望将“结构”展平为逻辑连续的位字符串,以通过网络传递它。 - Balázs Börcsök
@HomBahrani 不是 这张 图片,它将一个一维数组“压缩”成一个一维数组。 - endolith
@imatiasmb 但是前后两个都是一维的… - endolith
显示剩余2条评论

54

short read:

扁平化张量意味着除了一个维度外,移除所有维度。这正是Flatten层所做的。

long read:

如果考虑原始模型(带有Flatten层)的情况,我们可以得到以下模型摘要:

Layer (type)                 Output Shape              Param #   
=================================================================
D16 (Dense)                  (None, 3, 16)             48        
_________________________________________________________________
A (Activation)               (None, 3, 16)             0         
_________________________________________________________________
F (Flatten)                  (None, 48)                0         
_________________________________________________________________
D4 (Dense)                   (None, 4)                 196       
=================================================================
Total params: 244
Trainable params: 244
Non-trainable params: 0

为了更好地理解每个层的输入和输出大小,下面的图片将提供一些帮助。

如您所见,Flatten层的输出形状是(None, 48)。这里有一个提示:您应该将其解释为(1, 48)(2, 48)或...或(16, 48)... 或(32, 48), ...

实际上,该位置上的None表示任何批量大小。回想一下输入时,第一维表示批量大小,第二维表示输入特征数。

Keras中Flatten层的作用非常简单:

张量上的展平操作将张量重新塑造为其形状等于张量中不包括批处理尺寸的元素数量的形状。

enter image description here


注意:我使用model.summary()方法提供输出形状和参数详情。


2
你说 None 表示任意批次大小,但为什么 D16 的输出形状也有 None,这里不是批次大小为 3 吗? - Ray Jasson
1
不是的,据我理解,您可以选择任何批处理大小。您是如何得出批处理大小必须为3的结果的? - Balázs Börcsök

4
我最近看到了这个链接,它确实帮助我理解:https://www.cs.ryerson.ca/~aharley/vis/conv/ 因此,有一个输入、Conv2D、MaxPooling2D等等,Flatten层在最后展示了它们是如何形成和定义最终分类(0-9)的。

1
这很棒。还有更多内容在这里。http://www.cs.cmu.edu/~aharley/vis/ - Matt Allen
1
已经失效,但这是一个存档链接:https://web.archive.org/web/20201103090310/https://www.cs.ryerson.ca/~aharley/vis/conv/ - Joel Wigton

3
"Flattening"是将数据转换为一维数组,以便输入到下一层。我们将卷积层的输出展开成一个单独的长特征向量。在某些架构中,例如CNN,如果将图像处理成1D形式而不是2D形式,神经网络会更好地处理它。"

enter image description here


3

一般来说,您的网络中的第一层应该与您的数据形状相同。例如,我们的数据是28x28的图片,如果使用28层28个神经元就不现实了,因此将其“压平”成784x1则更为合理。为了避免自己编写处理代码,我们在开头添加了Flatten()层,在后面加载数组到模型时,它们会自动为我们压平。


1

Flatten会明确地展开多维张量(通常是输入张量)的序列化方式。这样可以建立起(展开后的)输入张量和第一个隐藏层之间的映射关系。如果第一个隐藏层是“密集”的,那么(序列化后的)输入张量的每个元素都将与隐藏数组的每个元素相连。

如果不使用Flatten,则输入张量被映射到第一个隐藏层的方式将是模糊的。


0
Keras的flatten类在处理多维输入(如图像数据集)时非常重要。Keras.layers.flatten函数将多维输入张量展平为单个维度,使您能够建模输入层并构建神经网络模型,然后有效地将这些数据传递到模型的每个神经元中。
通过时尚MNIST数据集,你可以很容易理解这一点。这个数据集中的图像是28*28像素。因此,如果你在Python中打印第一张图片,你会看到一个多维数组,我们实际上不能将其馈送到深度神经网络的输入层中。
print(train_images[0])

时尚MNIST的第一张图片

为了解决这个问题,我们可以在将图像数据馈送到神经网络时将其展平。我们可以通过将这个多维张量转换为一维数组来实现这一点。在这个展平的数组中,我们现在有784个元素(28 * 28)。然后,我们可以创建包含784个神经元的输入层,以处理传入数据的每个元素。

我们可以通过使用一行代码来完成所有这些,有点像......

keras.layers.flatten(input_shape=(28,28))

你的意思是这个层通常等同于对输入进行两行重塑的操作吗? xTrain = xTrain.reshape(xTrain.shape[0], -1) xTest = xTest.reshape(xTest.shape[0], -1) - Osama El-Ghonimy

0

正如其名称所示,它只是将输入张量展平。下面提供了一个非常好的视觉效果来理解这一点。 如果有任何疑问,请告诉我。 展平输入张量


0

在这里,我想介绍另一种Flatten函数的替代方法。这可能有助于理解内部发生了什么。这种替代方法添加了三行代码。

不使用

#==========================================Build a Model
model = tf.keras.models.Sequential()

model.add(keras.layers.Flatten(input_shape=(28, 28, 3)))#reshapes to (2352)=28x28x3
model.add(layers.experimental.preprocessing.Rescaling(1./255))#normalize
model.add(keras.layers.Dense(128,activation=tf.nn.relu))
model.add(keras.layers.Dense(2,activation=tf.nn.softmax))

model.build()
model.summary()# summary of the model

我们可以使用

    #==========================================Build a Model
    tensor = tf.keras.backend.placeholder(dtype=tf.float32, shape=(None, 28, 28, 3))
    
    model = tf.keras.models.Sequential()
    
    model.add(keras.layers.InputLayer(input_tensor=tensor))
    model.add(keras.layers.Reshape([2352]))
model.add(layers.experimental.preprocessing.Rescaling(1./255))#normalize
    model.add(keras.layers.Dense(128,activation=tf.nn.relu))
    model.add(keras.layers.Dense(2,activation=tf.nn.softmax))
    
    model.build()
    model.summary()# summary of the model

在第二种情况下,我们首先创建一个张量(使用占位符),然后创建一个输入层。之后,我们将张量重塑为平坦形式。因此,基本上,
Create tensor->Create InputLayer->Reshape == Flatten

Flatten是一个方便的函数,可以自动完成所有这些操作。当然,两种方法都有其特定的用例。Keras提供了足够的灵活性来操纵您想要创建模型的方式。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接