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

Python 2.7 如何使用Keras在CNN中处理可变大小的图像?,python-2.7,keras,conv-neural-network,dimension,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_

我目前正在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'))
我在打印之间打印
输出。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
回溯(最近一次呼叫最后一次):
文件“keras_卷积特征提取.py”,第466行,中
模型(0,训练输入数据,输出数据训练,测试输入数据,输出数据测试)
文件“keras_卷积特征提取.py”,第440行,模型中
model.add(密集(1,activation='relu'))
文件“/usr/local/lib/python2.7/dist-packages/keras/models.py”,第324行,添加
输出张量=层(自输出[0])
文件“/usr/local/lib/python2.7/dist-packages/keras/engine/topology.py”,第474行,在调用中__
self.assert\u输入\u兼容性(x)
文件“/usr/local/lib/python2.7/dist packages/keras/engine/topology.py”,第415行,断言输入兼容性
str(K.ndim(x)))
异常:输入0与层1不兼容:预期ndim=2,发现ndim=4

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

您正在使用64个卷积滤波器对
276xnonex3
图像进行操作,每个卷积滤波器的大小
276x1
(假设
rows=276
)。一个卷积滤波器将输出一个大小为
1 x None
的矩阵。如果您不知道卷积滤波器是如何工作的,请详细阅读。因此,对于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中不同后端的更多信息,请阅读

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

model.add(Flatten())
然而,我并不真正理解致密层在这里的用途。您必须知道,密集层只接受固定的输入大小,并提供固定大小的输出。因此,如果您希望网络运行时不会抛出错误,那么
None
维度基本上只限于一个值。如果希望有形状
1 x None
的输出,则不应包括密集层,并在末尾使用
average
池来折叠对
1 x None
输出的响应

编辑:如果您有一个大小为276xNx3的图像,其中列数可变,并且如果您希望输出大小为1xN,则可以执行以下操作:

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())
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')
现在,我怀疑这个网络会表现得很好,因为它只有一层64个过滤器。感受野也太大(例如276-图像高度)。你可以做两件事:

  • 减小感受野,即不是一次卷积整个图像列,而是一次只能卷积一列的3个像素
  • 有多个卷积层
  • 在下面,我将假设图像高度为50。然后,您可以按如下方式编写网络:

    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())
    
    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
    图像构建网络,如上图所示(可能更深一点)。现在让我们假设你得到一个不同大小的图像,比如,
    pxn'
    where
    p>224
    n'!=n
    。您可以对大小为
    224 x n'
    的图像进行中心裁剪,然后将其穿过图像。你有你的特征向量

    如果您认为大多数信息不是集中在中心周围,那么您可以选择多个作物,然后平均(或最大池)获得的多个特征向量。使用这些方法,我认为您应该能够处理可变大小的输入

    编辑:

    请参阅我使用
    3x3
    卷积定义的CNN。假设输入的大小为
    50xnx3
    。假设我们通过一个卷积层传递一个大小为
    pxqxr
    的输入,该卷积层具有
    f
    滤波器,每个滤波器的大小为
    3x3
    ,步长为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