带噪音的手写数字识别

Oh,好久不更新,接下来说一说我最近的在做的其中的一个事情吧~

问题描述: 给我2000张带噪音的手写数字图片,如下图所示。要求我训练一个模型,使其能在测试集10000张带噪音的手写数字图片,取得较好的效果。

噪声图片

一、图片预处理

通过分析可以知道这是高斯噪音,高斯噪音的形成为:一个正太分布的随机数与原图相加,即可使图片拥有高斯噪音。

添加高斯噪音的Python代码为:

def add_gaussian_noise(image_in, noise_sigma):
    temp_image = np.float64(np.copy(image_in))
 
    h, w = temp_image.shape
    noise = np.random.randn(h, w) * noise_sigma
    noisy_image = np.zeros(temp_image.shape, np.float64)
    noisy_image = temp_image + noise
    return noisy_image

添加效果如下所示:

左边原图,右边加噪音图

消除高斯噪音,我有两种思路。

  1. 直接对图片二值化(大于0.4的像素点为1,小于0.4的像素点为0),下面是效果图。

左边噪音图,右边二值化

  1. 使用高斯滤波之后,做一个简单的去背景噪声,使用高斯滤波器核心代码cv2.GaussianBlur(noise,(3,3),0),下面是效果。

左边噪音图,右边效果图

二、数据增强

数据增强有一个非常好使的库,名字为imgaug,接下来我说一下基本用法吧。

from imgaug import augmenters as iaa # 导入库
iaa.Affine(rotate=(-15, 15)),        # 定义增强方式
img_aug = aug.augment_images(img)    # 进行数据增强

只需三行,方可入手。超级简单有么有~。如果需要查看更多增强方式,请看这个博客,讲的非常详细。

三、搭建模型

对于自己瞎玩可以随便搭建模型,但是这是一个比赛,可需要认真一点了。Kaggle上曾有人证明最优28×28的网络的形状,详细过程可以参考这篇博客,这里我直接说结论了,下图是Tensorflow代码,易读性很器的。

model = Sequential([
    Conv2D(32, kernel_size = 3, activation='relu', input_shape = (28, 28, 1)),
    BatchNormalization(),
    Conv2D(32, kernel_size = 3, activation='relu'),
    BatchNormalization(),
    Conv2D(32, kernel_size = 5, strides=2, padding='same', activation='relu'),
    BatchNormalization(),
    Dropout(0.4),
    Conv2D(64, kernel_size = 3, activation='relu'),
    BatchNormalization(),
    Conv2D(64, kernel_size = 3, activation='relu'),
    BatchNormalization(),
    Conv2D(64, kernel_size = 5, strides=2, padding='same', activation='relu'),
    BatchNormalization(),
    Dropout(0.4),
    Conv2D(128, kernel_size = 4, activation='relu'),
    BatchNormalization(),
    Flatten(),
    Dropout(0.4),
    Dense(10, activation='softmax'),
])

该模型三个特点:

  1. 连续使用2个3×3卷积层,增加了网络的判别能力。
  2. 使用卷积核为5,步长为2的卷积层代替池化层,增加的网络的性能。
  3. 使用批归一化层和Dropout层,防止模型过拟合的同时,增加了网络性能。

四、模型集成

老师说:团结就是力量,模型当然也是,一个CNN的力量有限的,这篇文章使用Bagging的训练的15个CNN进行集成,达到了前所未有的信高度,思路如下图所示:

CNN集成

对于该问题来说,同样如此,所以我训练了10个CNN进行集成。部分代码如下所示:

nets = 10
model = [0] * nets
for i in range(nets):
    model[i] = Sequential([...])
    model[i].compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

Tip

全部代码公布在Kaggle上了,点击出发

参考资料

https://www.kaggle.com/c/digit-recognizer/discussion/61480