如何在 keras 中提供可变大小的图像作为输入

Posted

技术标签:

【中文标题】如何在 keras 中提供可变大小的图像作为输入【英文标题】:How to give variable size images as input in keras 【发布时间】:2018-05-27 12:01:03 【问题描述】:

我正在使用带有 tensorflow 后端的 keras 为两个类编写图像分类代码。我的图像存储在计算机的文件夹中,我想将这些图像作为我的 keras 模型的输入。 load_img 只接受一个输入图像,所以我必须使用flow(x,y)flow_from_directory(directory),但在flow(x,y) 中,我们还需要提供长度任务的标签,所以我使用flow_from_directory(directory)。我的图像尺寸可变,例如 20*40、55*43 ..... 但是here 提到需要固定的 target_size。在this 解决方案中,我们可以使用input_shape=(1, None, None) 或 input_shape=(None,None,3) (通道最后和彩色图像)将可变大小的图像作为卷积层的输入,但 fchollet 提到它对展平层和我的模型包括卷积层和展平层。在那篇文章中,只有 moi90 建议尝试不同的批次,但每个批次应该有相同大小的图像,但我不可能对相同大小的图像进行分组,因为我的数据非常分散。所以我决定使用batch size=1 并编写以下代码:

from __future__ import print_function
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPooling2D
from keras import backend as K
import numpy as np
from keras.preprocessing.image import ImageDataGenerator

input_shape = (None,None,3)

model = Sequential()
model.add(Conv2D(8, kernel_size=(3, 3),
                 activation='relu',
                 input_shape=input_shape))
model.get_weights()
model.add(Conv2D(16, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(32, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(2, activation='softmax'))

model.compile(loss='binary_crossentropy',optimizer='rmsprop',metrics=['accuracy'])

train_datagen = ImageDataGenerator()
test_datagen = ImageDataGenerator()
train_generator = train_datagen.flow_from_directory('/data/train', target_size=input_shape, batch_size=1,class_mode='binary') 
validation_generator = test_datagen.flow_from_directory('/data/test',target_size=input_shape,batch_size=1,class_mode='binary')
model.fit_generator(train_generator,steps_per_epoch=1,epochs=2,validation_data=validation_generator,validation_steps=1)

现在我收到以下错误:

Traceback (most recent call last):

  File "<ipython-input-8-4e22d22e4bd7>", line 23, in <module>
    model.add(Flatten())

  File "/home/nd/anaconda3/lib/python3.6/site-packages/keras/models.py", line 489, in add
    output_tensor = layer(self.outputs[0])

  File "/home/nd/anaconda3/lib/python3.6/site-packages/keras/engine/topology.py", line 622, in __call__
    output_shape = self.compute_output_shape(input_shape)

  File "/home/nd/anaconda3/lib/python3.6/site-packages/keras/layers/core.py", line 478, in compute_output_shape
    '(got ' + str(input_shape[1:]) + '. '

ValueError: The shape of the input to "Flatten" is not fully defined (got (None, None, 16). Make sure to pass a complete "input_shape" or "batch_input_shape" argument to the first layer in your model.

我确定这不是因为 img_dim_orderingbackend 而是因为 this 我已经检查过两者都是 th 请帮助纠正他的代码或帮助我如何将可变大小的图像作为输入我的模型。

【问题讨论】:

等待更多正确答案 【参考方案1】:

只要不尝试将可变大小放入 numpy 数组中,您就可以训练可变大小。

但有些层不支持可变大小,Flatten 就是其中之一。训练包含可变大小的 Flatten 层的模型是不可能的。

不过,您可以尝试将 Flatten 层替换为 GlobalMaxPooling2DGlobalAveragePooling2D 层。但是这些层可能会将太多的信息压缩成一个小数据,因此可能需要在它们之前添加更多的卷积和更多的通道。

不过,您必须确保您的生成器能够生成包含相同大小图像的批次。尝试将两个或多个不同大小的图像放在同一个 numpy 数组中时,生成器将失败。

【讨论】:

谢谢丹尼尔的回复。我将制作一组相同大小的图像,但是如何进行呢?我必须在上面提到的代码中进行哪些更改。 当我将 model.add(Flatten()) 替换为 model.add(GlobalMaxPooling2D()) 时,出现以下错误:File "/home/nd/anaconda3/lib/python3.6/site-packages/keras/utils/data_utils.py", line 557, in get six.raise_from(StopIteration(e), e) File "&lt;string&gt;", line 2, in raise_from StopIteration: 'NoneType' object cannot be interpreted as an integer 我相信在此层之前的某个地方,您的网络正在输出(甚至作为输入)nan 值。 relu 激活可能通过意外使所有结果等于 0(因此没有梯度)而导致此问题。您可以尝试(如果您的图像仅由正值组成)使用 relu 将初始化程序传递给层,使所有权重开始为正值。 我所有的图片都是由正值组成的。我将kernel_initializer='glorot_uniform' 添加到上面存在的两个C​​onv2d 层到我的代码中的GlobalAveragePooling2D 层,但得到相同的错误。 在那种情况下,我不知道问题出在哪里......不确定这是否重要,但通常,'softmax' 是为 'categorical_crossentropy' 制作的,而 'sigmoid' 是为'binary_crossentropy'。 --- 如果你一直只有一个真正的类,可以使用'softmax'。如果你可能有两个类都为真,或者两个类都为假,那么它应该是“sigmoid”。【参考方案2】:

在https://github.com/keras-team/keras/issues/1920中查看答案 哟,您应该将输入更改为:

input = Input(shape=(None, None,3))

最后添加GlobalAveragePooling2D()

试试类似的东西...

input = Input(shape=(None, None,3))

model = Sequential()
model.add(Conv2D(8, kernel_size=(3, 3),
                 activation='relu',
                 input_shape=(None, None,3)))  #Look on the shape
model.add(Conv2D(16, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
# IMPORTANT !
model add(GlobalAveragePooling2D())
# IMPORTANT !
model.add(Flatten())
model.add(Dense(32, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(2, activation='softmax'))

model.compile(loss='binary_crossentropy',optimizer='rmsprop',metrics=['accuracy'])

【讨论】:

【参考方案3】:

不幸的是,您无法训练具有各种大小图像的神经网络。您必须将所有图像调整为给定大小。幸运的是,您不必在硬盘驱动器中执行此操作,keras 永久为您执行此操作。

在您的 flow_from_directory 中,您应该像这样定义一个 target_size:

train_generator = train_datagen.flow_from_directory(
    'data/train',
    target_size=(150, 150), #every image will be resized to (150,150) before fed to neural network
    batch_size=32,
    class_mode='binary')

另外,如果你这样做,你可以有任何你想要的批量大小。

【讨论】:

好的。如果我将相同尺寸的图像分组,那么有可能吗?我必须对我的代码进行哪些更改。 我相信 target_size 通过拉伸/收缩而不是裁剪来转换图像。 这不是真的

以上是关于如何在 keras 中提供可变大小的图像作为输入的主要内容,如果未能解决你的问题,请参考以下文章

如何将文件或图像作为 Keras 模型中的参数提供给 model.predict?

Tensorflow 可变图像输入大小(自动编码器,放大...)

如何在keras tensorflow中将图像作为输入并获取另一个图像作为输出

在 Keras Lambda 层中调整输入图像的大小

TensorFlow 2.0 Keras:如何为TensorBoard编写图像摘要

TensorFlow 2.0 Keras:如何为 TensorBoard 编写图像摘要