【问题标题】:How to work with variable-sized image in CNNs using Keras?如何使用 Keras 在 CNN 中处理可变大小的图像?
【发布时间】:2017-03-10 01:46:20
【问题描述】:

我目前正在对使用 keras 进行特征提取的图像进行 CNN 研究。所有图像为 276 行、x 列和 3 个颜色维度 (RGB)。 列数等于它应该生成的输出特征向量的长度。

输入数据表示 - 编辑:

图像的输入数据由图像的列切片组成。这意味着图像的实际输入是 (276,3) 并且列数等于它应该生成的特征长度。

我的初始模型是这样的:

    print "Model Definition"
    model = Sequential()

    model.add(Convolution2D(64,row,1,input_shape=(row,None,3)))
    print model.output_shape
    model.add(MaxPooling2D(pool_size=(1,64)))
    print model.output_shape
    model.add(Dense(1,activation='relu'))

我在中间打印了output.shape,我似乎对输出有点困惑。

Model Definition
(None, 1, None, 64)
(None, 1, None, 64)

3D数据怎么变成4d了?并在 maxpoolling2d 层之后保持不变?

我的密集层/全连接层给我的尺寸带来了一些问题:

Traceback (most recent call last):
  File "keras_convolutional_feature_extraction.py", line 466, in <module>
    model(0,train_input_data,output_data_train,test_input_data,output_data_test)
  File "keras_convolutional_feature_extraction.py", line 440, in model
    model.add(Dense(1,activation='relu'))
  File "/usr/local/lib/python2.7/dist-packages/keras/models.py", line 324, in add
    output_tensor = layer(self.outputs[0])
  File "/usr/local/lib/python2.7/dist-packages/keras/engine/topology.py", line 474, in __call__
    self.assert_input_compatibility(x)
  File "/usr/local/lib/python2.7/dist-packages/keras/engine/topology.py", line 415, in assert_input_compatibility
    str(K.ndim(x)))
Exception: Input 0 is incompatible with layer dense_1: expected ndim=2, found ndim=4

那么为什么我不能从 3D 图像中将数据降低到 1 个单一值。 ?

【问题讨论】:

  • 查看我的编辑。我还更改了您的问题标题以更好地反映问题。如果您不同意,请随时更改。

标签: python-2.7 keras conv-neural-network dimension


【解决方案1】:

您正在使用 64 个卷积滤波器对 276 x None x 3 图像进行操作,每个滤波器的大小为 276 x 1(假设为 rows = 276)。一个卷积滤波器将输出大小为1 x None 的矩阵。如果您不知道卷积滤波器的工作原理,请详细阅读this。所以对于 64 个过滤器,(在 Theano 后端)你会得到一个大小为64 x 1 x None 的矩阵。在 Tensorflow 后端,我认为是1 x None x 64。现在,Keras-Theano 的第一个维度始终是样本。因此,您的最终输出形状将是None x 64 x 1 x None。对于 TensorFlow,它将是 None x 1 x None x 64。阅读this,了解有关 Keras 中不同后端的更多信息。

要消除密集层错误,我认为您需要在添加Dense 层之前通过引入以下行来展平输出。

model.add(Flatten())

但是,我不是很了解这里密集层的使用。您必须知道,密集层仅接受固定大小的输入并提供固定大小的输出。因此,如果您希望网络运行而不会引发错误,那么您的 None 维度基本上将被限制为单个值。如果您想要输出形状为1 x None,则不应包含密集层并在最后使用average 池化以折叠对1 x 1 x None 输出的响应。

编辑:如果您有一个大小为276 x n x 3 的图像,它的列数是可变的,并且如果您想要一个大小为1 x n 的输出,那么您可以执行以下操作:

model = Sequential()
model.add(Convolution2D(64,row,1,input_shape=(row,None,3)))
model.add(Convolution2D(1,1,1))
print model.output_shape  # this should print `None x 1 x None x 1`
model.add(flatten())

现在,我怀疑这个网络的性能会不会很好,因为它只有一层 64 个过滤器。感受野也太大(例如 276 - 图像的高度)。你可以做两件事:

  1. 减少感受野,即一次只能卷积一列的 3 个像素,而不是一次卷积图像的整个列。
  2. 有多个卷积层。

下面我假设图片高度为50,那么你可以写一个网络如下:

model = Sequential()
model.add(Convolution2D(32,3,1,activation='relu',
          init='he_normal',input_shape=(row,None,3)))  # row = 50
model.add(Convolution2D(32,3,1,activation='relu',init='he_normal'))
model.add(MaxPooling2D(pool_size=(2,1), strides=(2,1), name='pool1'))
model.add(Convolution2D(64,3,1,activation='relu',init='he_normal'))
model.add(Convolution2D(64,3,1,activation='relu',init='he_normal'))
model.add(MaxPooling2D(pool_size=(2,1), strides=(2,1), name='pool2'))
model.add(Convolution2D(128,3,1,activation='relu',init='he_normal'))
model.add(Convolution2D(128,3,1,activation='relu',init='he_normal'))
model.add(Convolution2D(128,3,1,activation='relu',init='he_normal'))
model.add(MaxPooling2D(pool_size=(2,1), strides=(2,1), name='pool3'))
model.add(Convolution2D(1,1,1), name='squash_channels')
print model.output_shape  # this should print `None x 1 x None x 1`
model.add(flatten(), name='flatten_input')

您应该验证所有这些卷积层和最大池化层在最后一次最大池化之后将输入高度从 50 降低到 1。

如何处理可变尺寸的图片

一种方法是首先确定数据集的通用大小,例如224.然后为224 x n图像构建网络如上图(可能更深一点)。现在让我们假设您获得了不同大小的图像,例如p x n',其中p &gt; 224n' != n。您可以对大小为224 x n' 的图像进行中心裁剪并将其传递给图像。你有你的特征向量。

如果你认为大部分信息没有集中在中心附近,那么你可以采取多次裁剪,然后对得到的多个特征向量进行平均(或最大池化)。使用这些方法,我认为您应该能够处理可变大小的输入。

编辑:

查看我使用3 x 3 卷积定义的 CNN。假设输入的大小为50 x n x 3。假设我们将大小为p x q x r 的输入传递给具有f 过滤器的卷积层,每个过滤器的大小为3 x 3,步幅为1。输入没有填充。那么卷积层的输出大小将是(p-2) x (q-2) x f,即输出的高度和宽度将比输入的小两个。我们的池化层大小为(2,1),步幅为(2,1)。它们会将 y 方向的输入减半(或图像高度减半)。牢记这一点,以下内容很容易推导出来(请注意我在 CNN 中给出的层名称,它们在下面被引用)。

CNN 输入:None x 50 x n x 3

pool1 层的输入:None x 46 x n x 32
pool1层的输出:None x 23 x n x 32

pool2 层的输入:None x 19 x n x 64
pool2 层的输出:None x 9 x n x 64 (我认为 Keras 池占用了地板,即 floor(19/2) = 9)

pool3 层的输入:None x 3 x n x 128
pool3层的输出:None x 1 x n x 128

squash_channels 层的输入:None x 1 x n x 128
squash_channels层的输出:None x 1 x n x 1

flatten_input 层的输入:None x 1 x n x 1
flatten_input层的输出:None x n

我想这就是你想要的。我希望它现在清楚了。

【讨论】:

  • 我给了输入形状276 x none x 3,因为图像有不同的列数,这是我唯一能想到的,代表列数
  • 我从未尝试过给None,但我认为应该没问题。主要问题是Dense 层。当您有 Dense 层时,您不能提供可变大小的输入。顺便说一下,密集层是全连接层(即它们是同义词)。所以我没有得到你的第一条评论。
  • 哦..我认为密集层的使用不清楚,这就是我澄清它的原因。
  • 哦...对不起..我给你的​​信息不正确..我会改变帖子。
  • 输入给 CNN 的数据实际上是按列进行的图像切片,认为是整个图像......所以输入形状是 (276,3)。在上面的帖子中也进行了更改。
猜你喜欢
  • 2018-02-22
  • 1970-01-01
  • 2021-04-26
  • 2019-08-26
  • 1970-01-01
  • 2019-06-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多