我正在尝试对MNIST数据库进行常规分类,但使用的是随机裁剪的数字。图像的裁剪方式如下:随机移除首行/末行和/或首列/末列。
我想使用Keras(以及Tensorflow后端)来构建卷积神经网络进行卷积操作,然后进行常规分类。
输入的大小是可变的,我无法使其正常工作。
以下是我裁剪数字的方式
import numpy as npfrom keras.utils import to_categoricalfrom sklearn.datasets import load_digitsdigits = load_digits()X = digits.imagesX = 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_splitX_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, Dropoutfrom keras.layers.convolutional import Conv2Dfrom keras.models import Sequentialmodel = 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))
-
有人知道如何在我的神经网络中处理可变大小输入吗?
-
以及如何进行分类?
回答:
TL/DR – 请直接跳到第4点
在我们进入正题之前,先让我们解决你网络中的一些问题:
-
你的网络因为激活函数而无法工作:使用
categorical_crossentropy
时,你需要使用softmax
激活函数:model.add(Dense(10, activation='softmax'))
-
空间张量向量化:正如Daniel所说,你需要在某个阶段将你的向量从空间(图像)转换为向量化(向量)。当前,直接将
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
多次是个坏主意,因为这样会重置内部模型状态。所以需要做的是:a. 编写一个函数,用于裁剪单个批次 – 例如,一个
get_cropped_batches_generator
函数,该函数给定一个矩阵,从中裁剪出一个批次并随机裁剪它。b. 使用
train_on_batch
方法。以下是一个示例代码:from six import nextbatches_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在不同的批次中可能具有不同的形状(一般情况下) 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
之间的兼容性。