手写数字识别 Digit Recognizer(下)

简介: 手写数字识别 Digit Recognizer(下)

3. CNN 卷积神经网络


3.1 Define the model 定义模型


我使用了 Keras Sequential API,您只需从输入开始,一次添加一层。


第一个是卷积(Conv2D)层。它就像一组可学习的过滤器。我选择为前两个 conv2D 层设置 32 个过滤器,为最后两个层设置 64 个过滤器。每个过滤器使用内核过滤器转换图像的一部分(由内核大小定义)。内核滤波器矩阵应用于整个图像。过滤器可以看作是图像的一种变换。


CNN 可以从这些转换后的图像(特征图)中分离出处处有用的特征。


CNN 中的第二个重要层是池化 (MaxPool2D) 层。该层仅充当下采样滤波器。它查看 2 个相邻像素并选择最大值。这些用于降低计算成本,并在一定程度上减少过拟合。我们必须选择池化大小(即每次池化的区域大小),池化维度越高,下采样越重要。


结合卷积层和池化层,CNN 能够结合局部特征并学习图像的更多全局特征。


Dropout 是一种正则化方法,对于每个训练样本,层中一定比例的节点被随机忽略(将它们的权重设置为零)。这会随机丢弃网络的一部分,并强制网络以分布式方式学习特征。这种技术还提高了泛化能力并减少了过拟合。


‘relu’ 是激活函数 max(0,x)。激活函数用于为网络添加非线性。


Flatten 层用于将最终的特征图转换为一个单一的一维向量。 需要此展平步骤,以便您可以在一些卷积/最大池层之后使用完全连接的层。 它结合了先前卷积层的所有找到的局部特征。


最后,我使用了两个完全连接(密集)层中的特征,这只是人工神经网络(ANN)分类器。 在最后一层 (Dense(10,activation=“softmax”)) 中,每个类别的概率的净输出分布。


# Set the CNN model 
# my CNN architechture is In -> [[Conv2D->relu]*2 -> MaxPool2D -> Dropout]*2 -> Flatten -> Dense -> Dropout -> Out
model = Sequential()
model.add(Conv2D(filters = 32, kernel_size = (5,5),padding = 'Same', 
                 activation ='relu', input_shape = (28,28,1)))
model.add(Conv2D(filters = 32, kernel_size = (5,5),padding = 'Same', 
                 activation ='relu'))
model.add(MaxPool2D(pool_size=(2,2)))
model.add(Dropout(0.25))
model.add(Conv2D(filters = 64, kernel_size = (3,3),padding = 'Same', 
                 activation ='relu'))
model.add(Conv2D(filters = 64, kernel_size = (3,3),padding = 'Same', 
                 activation ='relu'))
model.add(MaxPool2D(pool_size=(2,2), strides=(2,2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(256, activation = "relu"))
model.add(Dropout(0.5))
model.add(Dense(10, activation = "softmax"))

3.2 Set the optimizer and annealer 设置优化器和退火器


一旦我们的层被添加到模型中,我们需要设置一个评分函数、一个损失函数和一个优化算法。


我们定义了损失函数来衡量我们的模型在具有已知标签的图像上的表现有多差。它是观察到的标签和预测的标签之间的错误率。我们使用称为“categorical_crossentropy”的分类分类(> 2 个类别)的特定形式。


最重要的功能是优化器。该函数将迭代地改进参数(过滤核值、神经元的权重和偏差…)以最小化损失。


我选择了 RMSprop(带有默认值),它是一个非常有效的优化器。 RMSProp 更新以非常简单的方式调整 Adagrad 方法,以尝试降低其激进的、单调递减的学习率。我们也可以使用随机梯度下降 (‘SGD’) 优化器,但它比 RMSprop 慢。


度量函数“准确度”用于评估我们模型的性能。该度量函数类似于损失函数,不同之处在于在训练模型时不使用度量评估的结果(仅用于评估)。

# Define the optimizer
optimizer = RMSprop(lr=0.001, rho=0.9, epsilon=1e-08, decay=0.0)
# Compile the model
model.compile(optimizer = optimizer , loss = "categorical_crossentropy", metrics=["accuracy"])

为了使优化器收敛得更快并且最接近损失函数的全局最小值,我使用了学习率(LR)的退火方法。


LR 是优化器遍历“损失情况”的步骤。 LR越高,步长越大,收敛越快。 然而,高 LR 的采样非常差,优化器可能会陷入局部最小值。


最好在训练期间降低学习率,以有效地达到损失函数的全局最小值。


为了保持具有高 LR 的快速计算时间的优势,我根据是否有必要(当精度未提高时)每 X 步(时期)动态地减少 LR。


使用 Keras.callbacks 的 ReduceLROnPlateau 函数,如果 3 个 epoch 后精度没有提高,我选择将 LR 减少一半。

# Set a learning rate annealer
learning_rate_reduction = ReduceLROnPlateau(monitor='val_acc', 
                                            patience=3, 
                                            verbose=1, 
                                            factor=0.5, 
                                            min_lr=0.00001)
epochs = 30
batch_size = 86

3.3 Data augmentation 数据增强


为了避免过拟合问题,我们需要人为地扩展我们的手写数字数据集。我们可以使您现有的数据集更大。这个想法是通过小的转换来改变训练数据,以重现有人写数字时发生的变化。


比如数字不居中 比例不一样(有的写大/小数字) 图像旋转…


以改变数组表示同时保持标签不变的方式改变训练数据的方法被称为数据增强技术。人们使用的一些流行的增强是灰度、水平翻转、垂直翻转、随机裁剪、颜色抖动、平移、旋转等等。


通过对我们的训练数据应用这些转换中的几个,我们可以轻松地将训练示例的数量增加一倍或三倍,并创建一个非常强大的模型。

# With data augmentation to prevent overfitting (accuracy 0.99286)
datagen = ImageDataGenerator(
        featurewise_center=False,  # set input mean to 0 over the dataset
        samplewise_center=False,  # set each sample mean to 0
        featurewise_std_normalization=False,  # divide inputs by std of the dataset
        samplewise_std_normalization=False,  # divide each input by its std
        zca_whitening=False,  # apply ZCA whitening
        rotation_range=10,  # randomly rotate images in the range (degrees, 0 to 180)
        zoom_range = 0.1, # Randomly zoom image 
        width_shift_range=0.1,  # randomly shift images horizontally (fraction of total width)
        height_shift_range=0.1,  # randomly shift images vertically (fraction of total height)
        horizontal_flip=False,  # randomly flip images
        vertical_flip=False)  # randomly flip images
datagen.fit(X_train)


对于数据增强,我选择:


将一些训练图像随机旋转 10 度

将一些训练图像随机放大 10%

将图像水平随机移动 10% 的宽度

将图像垂直随机移动 10% 的高度

我没有应用vertical_flip 或horizontal_flip,因为它可能导致错误分类对称数,例如6 和9。


一旦我们的模型准备就绪,我们就会拟合训练数据集。

# Fit the model
history = model.fit_generator(datagen.flow(X_train,Y_train, batch_size=batch_size),
                              epochs = epochs, validation_data = (X_val,Y_val),
                              verbose = 2, steps_per_epoch=X_train.shape[0] // batch_size
                              , callbacks=[learning_rate_reduction])


4. Evaluate the model 评估模型


4.1 Training and validation curves 训练和验证集的学习曲线

# Plot the loss and accuracy curves for training and validation 
fig, ax = plt.subplots(2,1)
ax[0].plot(history.history['loss'], color='b', label="Training loss")
ax[0].plot(history.history['val_loss'], color='r', label="validation loss",axes =ax[0])
legend = ax[0].legend(loc='best', shadow=True)
ax[1].plot(history.history['acc'], color='b', label="Training accuracy")
ax[1].plot(history.history['val_acc'], color='r',label="Validation accuracy")
legend = ax[1].legend(loc='best', shadow=True)

4.2 Confusion matrix 混淆矩阵


混淆矩阵对于查看我们的模型缺点非常有帮助。

我绘制了验证结果的混淆矩阵。

# Look at confusion matrix 
def plot_confusion_matrix(cm, classes,
                          normalize=False,
                          title='Confusion matrix',
                          cmap=plt.cm.Blues):
    """
    This function prints and plots the confusion matrix.
    Normalization can be applied by setting `normalize=True`.
    """
    plt.imshow(cm, interpolation='nearest', cmap=cmap)
    plt.title(title)
    plt.colorbar()
    tick_marks = np.arange(len(classes))
    plt.xticks(tick_marks, classes, rotation=45)
    plt.yticks(tick_marks, classes)
    if normalize:
        cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
    thresh = cm.max() / 2.
    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
        plt.text(j, i, cm[i, j],
                 horizontalalignment="center",
                 color="white" if cm[i, j] > thresh else "black")
        plt.tight_layout()
    plt.ylabel('True label')
    plt.xlabel('Predicted label')
# Predict the values from the validation dataset
Y_pred = model.predict(X_val)
# Convert predictions classes to one hot vectors 
Y_pred_classes = np.argmax(Y_pred,axis = 1) 
# Convert validation observations to one hot vectors
Y_true = np.argmax(Y_val,axis = 1) 
# compute the confusion matrix
confusion_mtx = confusion_matrix(Y_true, Y_pred_classes) 
# plot the confusion matrix
plot_confusion_matrix(confusion_mtx, classes = range(10)) 

20210605010309747.png


在这里我们可以看到,考虑到验证集的大小(4 200 张图像),我们的 CNN 在所有数字上都表现得非常好,几乎没有错误。

然而,我们的 CNN 似乎对 4 位数字有一些小问题,嘿嘿被误分类为 9。有时在曲线平滑时很难捕捉 4 和 9 之间的差异。

# Display some error results 
# Errors are difference between predicted labels and true labels
errors = (Y_pred_classes - Y_true != 0)
Y_pred_classes_errors = Y_pred_classes[errors]
Y_pred_errors = Y_pred[errors]
Y_true_errors = Y_true[errors]
X_val_errors = X_val[errors]
def display_errors(errors_index,img_errors,pred_errors, obs_errors):
    """ This function shows 6 images with their predicted and real labels"""
    n = 0
    nrows = 2
    ncols = 3
    fig, ax = plt.subplots(nrows,ncols,sharex=True,sharey=True)
    for row in range(nrows):
        for col in range(ncols):
            error = errors_index[n]
            ax[row,col].imshow((img_errors[error]).reshape((28,28)))
            ax[row,col].set_title("Predicted label :{}\nTrue label :{}".format(pred_errors[error],obs_errors[error]))
            n += 1
# Probabilities of the wrong predicted numbers
Y_pred_errors_prob = np.max(Y_pred_errors,axis = 1)
# Predicted probabilities of the true values in the error set
true_prob_errors = np.diagonal(np.take(Y_pred_errors, Y_true_errors, axis=1))
# Difference between the probability of the predicted label and the true label
delta_pred_true_errors = Y_pred_errors_prob - true_prob_errors
# Sorted list of the delta prob errors
sorted_dela_errors = np.argsort(delta_pred_true_errors)
# Top 6 errors 
most_important_errors = sorted_dela_errors[-6:]
# Show the top 6 errors
display_errors(most_important_errors, X_val_errors, Y_pred_classes_errors, Y_true_errors)

20210605010331420.png


最重要的错误也是最有趣的。

对于这六个案例,该模型并不可笑。 其中一些错误也可能是人类造成的,尤其是对于非常接近 4 的 9。最后一个 9 也非常具有误导性,对我来说似乎是 0


5. Prediction and submition 预测和提交

5.1 Predict and Submit results 预测并提交结果

# predict results
results = model.predict(test)
# select the indix with the maximum probability
results = np.argmax(results,axis = 1)
results = pd.Series(results,name="Label")


submission = pd.concat([pd.Series(range(1,28001),name = "ImageId"),results],axis = 1)
submission.to_csv("cnn_mnist_datagen.csv",index=False)

20210605012149589.png


相关文章
|
存储 数据库 索引
客户端存储 —— IndexedDB 实现分页查询(下)
客户端存储 —— IndexedDB 实现分页查询
636 0
解决使用-webkit-app-region后鼠标点击失败的问题
在开发中,如果我们想要实现窗口拖动的功能,可以通过给相应的元素设置 -webkit-app-region: drag 属性来实现。然而,这样做会引发一些问题,包括当前元素权级高于其他元素、无法触发鼠标相关事件以及双击全屏和还原窗口大小功能失效等。本文将介绍如何解决这些问题,实现既能拖动窗口又不影响窗口内部元素的需求。
2051 0
|
Android开发
【Android App】蓝牙的设备配对、音频传输、点对点通信的讲解及实战(附源码和演示 超详细)
【Android App】蓝牙的设备配对、音频传输、点对点通信的讲解及实战(附源码和演示 超详细)
2810 1
|
机器学习/深度学习 人工智能 TensorFlow
深度学习的魔法:用神经网络识别手写数字
【8月更文挑战第24天】在这个数字世界中,深度学习技术如同一把钥匙,开启了智能识别的大门。通过构建和训练神经网络,我们能够教会机器“看”懂人类的手迹。本文将带你走进深度学习的世界,一起探索如何利用Python和TensorFlow框架搭建一个简单的卷积神经网络(CNN),实现对MNIST数据集中的手写数字进行分类。
|
11月前
|
JSON 缓存 JavaScript
使用 jsDelivr 免费加速 GitHub Pages 博客的静态资源(二)
使用 jsDelivr 加速 GitHub Pages 的图片资源和动态编译的 JSON 资源。
202 2
|
虚拟化 数据安全/隐私保护 Windows
如何在 VM 虚拟机中安装 Win10 操作系统保姆级教程(附链接)
如何在 VM 虚拟机中安装 Win10 操作系统保姆级教程(附链接)
如何在 VM 虚拟机中安装 Win10 操作系统保姆级教程(附链接)
|
JavaScript 容器
技术经验解读:【详解】提示框(tooltip)的使用
技术经验解读:【详解】提示框(tooltip)的使用
|
持续交付 开发者 Python
vulture,一个有趣的 Python 死代码清除库!
vulture,一个有趣的 Python 死代码清除库!
209 3
|
应用服务中间件 Shell 网络安全
nginx安装提示 libssl.so.3: cannot open shared object file: No
【8月更文挑战第1天】### 原因 未将安装的ssl中的`libssl.so.3`链接到`/usr/lib`导致缺失。 ### 解决方案 1. 检查openssl是否已安装,若为低版本则需重装。 ```sh whereis openssl
5345 6
|
算法 图形学
计算机图形学 之 DDA直线算法(数值微分法)
计算机图形学 之 DDA直线算法(数值微分法)
664 0