我正在尝试对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))
有人知道如何在我的神经网络中处理可变大小的输入吗?
如何进行分类?
答案 0 :(得分:18)
TL / DR - 转到第4点
所以 - 在我们谈到这一点之前 - 让我们解决你网络的一些问题:
您的网络因激活而无效:categorical_crossentropy
您需要softmax
激活:
model.add(Dense(10, activation='softmax'))
矢量化空间张量:,正如丹尼尔所说 - 你需要在某个阶段将矢量从空间(图像)切换到矢量化(矢量)。目前 - 将Dense
应用于Conv2D
的输出相当于(1, 1)
卷积。所以基本上 - 你的网络输出是空间的 - 没有矢量化导致维度不匹配的原因(你可以通过运行你的网络或检查model.summary()
来检查。为了改变你需要使用GlobalMaxPooling2D
或GlobalAveragePooling2D
。例如:
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'))
连续numpy
数组需要具有相同的形状:如果您检查X_crop
的形状,您会发现它不是空间矩阵。这是因为你连接了不同形状的矩阵。可悲的是 - 由于numpy.array
需要有固定的形状,所以无法克服这个问题。
如何使用不同形状的示例进行网络训练:这样做最重要的是要了解两件事。首先 - 在一个批次中,每个图像应该具有相同的大小。第二个 - 多次调用fit
是一个坏主意 - 当你重置内部模型状态时。所以这是需要做的事情:
一个。编写一个能够裁剪单批的函数 - 例如给定矩阵的get_cropped_batches_generator
从中切出一批并随机裁剪。
湾使用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))
所以 - 上面代码的一些注释:首先,train_on_batch
不使用好的keras
进度条。它返回一个损失值(对于给定的批处理) - 这就是我添加逻辑来计算损失的原因。您也可以使用Progbar
回调。第二 - 你需要实现get_cropped_batches_generator
- 我没有编写代码来更清楚地回答我的问题。你可以问另一个关于如何实现它的问题。最后一件事 - 我使用six
来保持Python 2
和Python 3
之间的兼容性。
答案 1 :(得分:3)
通常,包含Dense
层的模型不能具有可变大小的输入,除非输出也是可变的。但请参阅解决方法以及使用GlobalMaxPooling2D
的其他答案 - 解决方法等同于GlobalAveragePooling2D
。这些层可以在Dense图层之前消除可变大小并抑制空间维度。
对于图像分类案例,您可能需要调整模型外部的图像大小。
当我的图像处于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