如何使用Keras在CNN中处理可变大小的图像?

时间:2017-03-10 01:46:20

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

我目前正在CNN上使用keras在图像上进行特征提取。所有图像均为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 个答案:

答案 0 :(得分:2)

您使用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。有关Keras中不同后端的更多信息,请阅读this

要删除密集层错误,我认为您需要在添加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. 有多个卷积层。
  3. 在下文中,我将假设图像高度为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 > 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合并广告,即楼层(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

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