使用Keras进行Mnist识别

12

如何训练模型以识别一张图片中的五个数字。

以下是代码:

from keras.layers import Conv2D
from keras.layers import MaxPooling2D
from keras.layers import Flatten
from keras.layers import Dropout, Dense, Input
from keras.models import Model, Sequential

model = Sequential()
model.add(Conv2D(32, kernel_size=(3, 3),
             activation='relu',
             input_shape=(28, 140, 1)))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dropout(0.5))

这里应该有一个循环来识别图片中的每个数字,但我不知道如何实现。

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

model.compile(loss=keras.losses.categorical_crossentropy,
          optimizer=keras.optimizers.Adadelta(),
          metrics=['accuracy'])

model.fit(X_train, y_train,
      batch_size=1000,
      epochs=8,
      verbose=1,
      validation_data=(X_valid, y_valid))

合并的MNIST数字图片如下所示:

合并的数字图片


总之,解决这个问题基本上有两种方法。使用类似于opencv的预处理图像来提取您想要识别的数字,然后运行标准的单个数字CNN;或者使用某种类型的CNN来完成整个过程,如下所述:暴力CNN(在多个数字上进行训练),RNN等。如果图像格式是可预测的,则opencv是一个不错的选择,目前我选择了这条路线! - PhoebeB
4个回答

2
我建议两种可能的方法:
情况1-图像结构良好。
在您提供的示例中,确实是这种情况,因此如果您的数据看起来像您提供的链接中的数据,我会建议使用这种方法。
在您提供的链接中,每个图像基本上由5个28×28像素的图像堆叠而成。在这种情况下,我建议剪切图像(即将每个图像分为5个部分),并像处理通常的MNIST数据一样训练模型(例如,使用您提供的代码)。然后,当您想要将模型应用于分类新数据时,只需将每个新图像也分成5个部分。使用您的模型对这5个部分进行分类,然后将这5个数字直接写在其他数字旁边作为输出。
所以关于这句话:
“这里应该有一个循环来识别图片中的每个数字,但我不知道如何实现”
你不需要一个for循环。只需切割你的数字。
情况2-图像结构不好。
在这种情况下,每个图像都带有5个数字标签。因此,y_trainy_valid中的每一行都将是具有55个条目的0,1向量。前11个条目是第一个数字的one-hot编码,第二个11个条目是第二个数字的one-hot编码,依此类推。因此,y_train中的每一行将有5个条目等于1,其余条目等于0。
此外,不要在输出层使用softmax激活和categorical_crossentropy损失,而是使用sigmoid激活函数和“binary_crossentropy”损失(有关原因的进一步讨论,请参见这里这里)。
总之,替换为:
model.add(Dense(11, activation='softmax'))

model.compile(loss=keras.losses.categorical_crossentropy,
          optimizer=keras.optimizers.Adadelta(),
          metrics=['accuracy'])

使用以下方法:

model.add(Dense(55, activation='sigmoid'))

model.compile(loss='binary_crossentropy',
          optimizer=keras.optimizers.Adadelta())

2

这个领域的经典作品是使用深度卷积神经网络在街景图像中识别多位数字,论文名为《Multi-digit Number Recognition from Street View Imagery using Deep Convolutional Neural Networks》

Keras模型(函数式,非顺序):

inputs = Input(shape=(28, 140, 1), name="input")
x = inputs
x = Conv2D(32, kernel_size=(3, 3), activation='relu', input_shape=(28, 140, 1))(x)
x = Conv2D(64, (3, 3), activation='relu')(x)
x = MaxPooling2D(pool_size=(2, 2))(x)
x = Dropout(0.25)(x)
x = Flatten()(x)
x = Dropout(0.5)(x)
digit1 = Dense(10, activation='softmax', name='digit1')(x)
digit2 = Dense(10, activation='softmax', name='digit2')(x)
digit3 = Dense(10, activation='softmax', name='digit3')(x)
digit4 = Dense(10, activation='softmax', name='digit4')(x)
digit5 = Dense(10, activation='softmax', name='digit5')(x)
predictions = [digit1,digit2,digit3,digit4,digit5]
model = Model(inputs=inputs, outputs=predictions)
model.compile(optimizer=Adam(), metrics=['accuracy'], oss='categorical_crossentropy')

PS 您可以使用11个类来表示10个数字和空格。


所有的回答都很有帮助,这个回答似乎最接近原问题,所以我在这里奖励了额外的分数。 - PhoebeB

1

既然您已经有一张行为良好的图像,您所要做的就是扩展模型中的类别数量。

您可以使用5倍11类别代替仅使用11类别。

前11个类别标识第一个数字,接下来的11个类别标识第二个数字,依此类推。共55个类别,每个图像位置对应11个类别。

简而言之:

  • X_training将是整个图像,如链接中所示,形状为(28,140)(140,28),具体取决于您使用的加载图像的方法。
  • Y_training将是一个55元素向量,形状为(55,),告诉每个象限中包含哪些数字。

例如:对于包含9、7、5、4、10的第一张图像,您将创建包含以下位置值为1的Y_training

  • Y_training[9] = 1
  • Y_training[18] = 1 #(18=7+11)
  • Y_training[27] = 1 #(27=5+22)
  • Y_training[37] = 1 #(37=4+33)
  • Y_training[54] = 1 #(54=10+44)

按照您的意愿创建模型层,与普通的MNIST模型相当类似,这意味着不需要尝试循环或类似的东西。

但是它可能需要比以前更大一点。

由于每个图像有5个正确的类别而不是只有1个,因此您将无法再使用categorical_crossentropy。如果在最后使用“sigmoid”激活,则binary_crossentropy应该是一个好的替代品。

确保您的最后一层适合55元素向量,例如Dense(55)


0

这个问题在90年代由Yann LeCun解决了。您可以在他的 网站 上找到演示和论文。

一个不太常规的解决方案是在单个数字MNIST上训练CNN,并使用该CNN对像您提供的图像一样的图像执行推断。通过在多位数字图像上滑动训练过的CNN并应用后处理来预测结果,并可能估计边界框。

一个非常通用的解决方案是构建一个能够预测数字边界框并对其进行分类的模型,以处理可变数量、不同比例和位置的数字。近年来,有这样的模型历史,如R-CNN、Fast-RCNN和Faster-RCNN

您可以在github上找到Faster-RCNN的Python实现。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接