【问题标题】:How to handle variable sized input in CNN with Keras?如何使用 Keras 在 CNN 中处理可变大小的输入?
【发布时间】:2018-02-22 13:55:10
【问题描述】:

我正在尝试对 MNIST 数据库执行常规分类,但使用随机裁剪的数字。 图像按以下方式裁剪:随机删除第一个/最后一个和/或行/列。

我想使用使用 Keras(和 Tensorflow 后端)的卷积神经网络来执行卷积,然后执行通常的分类。

输入的大小可变,我无法让它工作。

这是我如何裁剪数字

import numpy as np
from keras.utils import to_categorical
from sklearn.datasets import load_digits

digits = load_digits()

X = digits.images
X = np.expand_dims(X, axis=3)

X_crop = list()
for index in range(len(X)):
    X_crop.append(X[index, np.random.randint(0,2):np.random.randint(7,9), np.random.randint(0,2):np.random.randint(7,9), :])
X_crop = np.array(X_crop)

y = to_categorical(digits.target)

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X_crop, y, train_size=0.8, test_size=0.2)

这是我要使用的模型的架构

from keras.layers import Dense, Dropout
from keras.layers.convolutional import Conv2D
from keras.models import Sequential

model = Sequential()

model.add(Conv2D(filters=10, 
                 kernel_size=(3,3), 
                 input_shape=(None, None, 1), 
                 data_format='channels_last'))

model.add(Dense(128, activation='relu'))
model.add(Dropout(0.2))

model.add(Dense(10, activation='softmax'))


model.compile(loss='categorical_crossentropy', optimizer='sgd', metrics=['accuracy'])

model.summary()

model.fit(X_train, y_train, epochs=100, batch_size=16, validation_data=(X_test, y_test))
  1. 有人知道如何在我的神经网络中处理可变大小的输入吗?

  2. 以及如何进行分类?

【问题讨论】:

    标签: python machine-learning neural-network deep-learning keras


    【解决方案1】:

    通常,包含Dense 层的模型不能有可变大小的输入,除非输出也是可变的。但是使用GlobalMaxPooling2D 查看解决方法和其他答案 - 解决方法等同于GlobalAveragePooling2D。这些层可以消除密集层之前的可变大小并抑制空间维度。

    对于图像分类案例,您可能需要调整模型外的图像大小。

    当我的图片是 numpy 格式时,我会像这样调整它们的大小:

    from PIL import Image
    im = Image.fromarray(imgNumpy)
    im = im.resize(newSize,Image.LANCZOS) #you can use options other than LANCZOS as well
    imgNumpy = np.asarray(im)
    

    为什么?

    卷积层的权重作为过滤器。有一个静态过滤器大小,并且一遍又一遍地将相同的过滤器应用于图像。

    但密集层的权重基于输入。如果有 1 个输入,则有一组权重。如果有 2 个输入,则权重是原来的两倍。但是权重必须经过训练,改变权重的数量肯定会改变模型的结果。

    正如@Marcin 评论的那样,当您的密集层输入形状具有两个维度时,我所说的是正确的:(batchSize,inputFeatures)

    但实际上 keras 密集层可以接受更多维度的输入。这些额外的维度(来自卷积层)的大小可能会有所不同。但这会使这些密集层的输出大小也发生变化。

    不过,最后您需要一个固定的分类大小:10 个类别,仅此而已。为了减少维度,人们经常使用Flatten层,这里会出现错误。


    一种可能的解决方法(未经测试):

    在模型的卷积部分结束时,使用 lambda 层将所有值压缩在一个固定大小的张量中,可能取侧面维度的平均值并保留通道(通道不可变)

    假设最后一个卷积层是:

    model.add(Conv2D(filters,kernel_size,...))
    #so its output shape is (None,None,None,filters) = (batchSize,side1,side2,filters)
    

    让我们添加一个 lambda 层来压缩空间维度并只保留过滤器维度:

    import keras.backend as K
    
    def collapseSides(x):
    
        axis=1 #if you're using the channels_last format (default)   
        axis=-1 #if you're using the channels_first format
    
        #x has shape (batchSize, side1, side2, filters)
        step1 = K.mean(x,axis=axis) #mean of side1
        return K.mean(step1,axis=axis) #mean of side2
    
        #this will result in a tensor shape of (batchSize,filters)
    

    由于过滤器的数量是固定的(您已经排除了None 维度),密集层应该可以工作:

    model.add(Lambda(collapseSides,output_shape=(filters,)))
    model.add(Dense.......)
    .....
    

    为了使这可能起作用,我建议最后一个卷积层中的过滤器数量至少为 10。

    有了这个,你可以制作input_shape=(None,None,1)

    如果您这样做,请记住,您只能通过每批固定大小的输入数据。因此,您必须将整个数据分成较小的批次,每个批次的图像大小都相同。见这里:Keras misinterprets training data shape

    【讨论】:

    • 这不是真的。 Dense 的当前实现使其完全卷积。 Flatten 可能会导致错误。
    • 另一件事是,您实际上可以在不同大小的图像上训练模型,而无需使用train_on_batch 方法调整大小。
    • 如果模型与之兼容,那是我回答的结尾。
    • For an image classification case, you will probably have to resize the images outside the model.
    • 此外-您的可疑解决方法等效于GlobalAveragePooling2D。你的第一句话是错误的 - 你仍然可以在完全卷积模型中使用 Dense - 如果它位于 GlobalPooling 层之后。
    【解决方案2】:

    TL/DR - 转到第 4 点

    所以 - 在我们进入正题之前 - 让我们解决您的网络的一些问题:

    1. 由于激活,您的网络将无法工作:使用categorical_crossentropy,您需要有softmax 激活:

      model.add(Dense(10, activation='softmax'))
      
    2. 向量化空间张量:正如 Daniel 提到的 - 在某个阶段,您需要将向量从空间(图像)切换到向量化(向量)。目前 - 将Dense 应用于Conv2D 的输出相当于(1, 1) 卷积。所以基本上 - 你的网络的输出是空间的 - 没有矢量化导致维度不匹配的原因(你可以通过运行你的网络或检查model.summary()来检查。为了改变你需要使用GlobalMaxPooling2DGlobalAveragePooling2D。例如:

      model.add(Conv2D(filters=10, 
                   kernel_size=(3, 3), 
                   input_shape=(None, None, 1),
                   padding="same",
                   data_format='channels_last'))
      model.add(GlobalMaxPooling2D())
      model.add(Dense(128, activation='relu'))
      model.add(Dropout(0.2))
      
      model.add(Dense(10, activation='softmax'))
      
    3. 连接的numpy 数组需要具有相同的形状:如果您检查X_crop 的形状,您会发现它不是空间矩阵。这是因为你连接了不同形状的矩阵。可悲的是 - 因为numpy.array 需要有一个固定的形状,所以无法克服这个问题。

    4. 如何让您的网络在不同形状的示例上进行训练:这样做最重要的是要了解两件事。首先 - 在一个批次中,每个图像都应该具有相同的大小。其次 - 多次调用 fit 是个坏主意 - 当您重置内部模型状态时。所以这里是需要做的:

      一个。编写一个函数来裁剪 单个批次 - 例如一个get_cropped_batches_generator,它给定一个矩阵,从中切出一批并随机裁剪。

      b.使用train_on_batch 方法。这是一个示例代码:

      from six import next
      
      batches_generator = get_cropped_batches_generator(X, batch_size=16)
      losses = list()
      for epoch_nb in range(nb_of_epochs):
          epoch_losses = list()
          for batch_nb in range(nb_of_batches):
              # cropped_x has a different shape for different batches (in general)
              cropped_x, cropped_y = next(batches_generator) 
              current_loss = model.train_on_batch(cropped_x, cropped_y)
              epoch_losses.append(current_loss)
          losses.append(epoch_losses.sum() / (1.0 * len(epoch_losses))
      final_loss = losses.sum() / (1.0 * len(losses))
      

    所以 - 上面有几个 cmets 代码:首先,train_on_batch 不使用漂亮的 keras 进度条。它返回一个损失值(对于给定的批次)——这就是我添加逻辑来计算损失的原因。你也可以使用Progbar 回调。其次 - 你需要实现get_cropped_batches_generator - 我还没有编写代码来让我的答案更清楚一点。你可以问另一个关于如何实现它的问题。最后一件事 - 我使用six 来保持Python 2Python 3 之间的兼容性。

    【讨论】:

    • @Marcin Możejko 你能解释一下 get_cropped_batches_generator 的实现吗?
    • 能否详细说明model.fit和train_on_batch的区别?模型的哪些内部状态正在重置?
    • 能不能得到之前层的神经元个数?那么,当第一层有动态输入形状时,下一层有一半?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-12-30
    • 1970-01-01
    • 2019-10-12
    • 2017-02-10
    • 2021-05-20
    • 2018-05-27
    • 2018-05-11
    相关资源
    最近更新 更多