深度学习——手写数字识别

发布于:2025-06-23 ⋅ 阅读:(17) ⋅ 点赞:(0)

深度学习——手写数字识别

图 1

学习深度学习的朋友应该对MNIST数据集不陌生吧,相信很多人在刚开始学习深度学习的时候都会用到MNIST数据集进行书写数字识别。本篇文章参考鱼书创建一个深度网络来进行书写数字识别的任务。

如上图所示,这里使用的卷积层全都是 3 × 3 3 \times 3 3×3 的小型滤波器,特点是随着层的加深,通道数变大(卷积层的通道数从前面的层开始按顺序以16、16、32、32、64、64的方式增加)。此外,如图所示,插入了池化层,以逐渐减小中间数据的空间大小;并且,后面的全连接层中还使用了Dropout层(为了防止过拟合)。

这个网络使用He初始值(何恺明大神的提出的)作为权重的初始值,使用Adam更新权重参数。把上述内容总结起来,这个网络有如下特点。

  • 基于 3 × 3 3 \times 3 3×3的小型滤波器的卷积层。
  • 激活函数是 ReLU。
  • 全连接层的后面使用 Dropout 层。
  • 基于 Adam 的最优化。
  • 使用 He 初始值作为权重初始值。

权重初始化对训练深度网络很重要,特别是ReLU激活函数流行之后。Xavier 初始化是针对Sigmoid和Tanh设计的,Sigmoid函数和Tanh函数左右对称,且中央附近可以视作线性函数,但ReLU的非线性特性导致前向传播时输出方差会变化。

而He初始化主要是为了解决ReLU激活函数在初始化时方差缩小的问题。He初始值一种专门为使用ReLu激活函数及其变体(如Leaky ReLU, PPeLU)的神经网络层设计的权重初始化方法。它是由何恺明(Kaiming He)等人在2015年的论文《Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification》中提出的。ReLU会把负数部分归零,所以输出的方差会比输入小一半。如果每一层都这样,深度网络的方差会越来越小,导致梯度消失。

He初始值使用标准差为 2 n \sqrt{\frac{2}{n}} n2 的高斯分布,n是输入神经元的数量。与 Xavier 初始化相比, He 的方差更大,以补偿ReLU造成的方差减半。

代码:https://github.com/Benxiaogu/mnist

鱼书全书代码
https://github.com/qiaohaoforever/DeepLearningFromScratch

训练结果:

函数im2col的作用是将数据展开以适合滤波器(权重)。如下图所示,对3维的输入数据应用此函数后,数据转换为2维矩阵(正确地讲,是把包含批数量的4维数据转换成了2维数据)。

im2col 是 "image to column"的缩写,翻译过来就是“从图像到矩阵”的意思。

使用im2col展开输入数据之后,之后就只需将卷积层的滤波器(权重)纵向展开为1列,并计算2个矩阵的乘积即可,如下图所示。这和全连接层的 Affine 层进行的处理基本相同。

如上图所示,基于 im2col 方式的输出结果是2为矩阵。因为 CNN 中数据会保存为4维数组,所以要将 2 维输出数据转换为合适的形状。

函数col2im是函数im2col的逆过程

def im2col(input_data, filter_h, filter_w, stride=1, pad=0):
    """
        Parameters:
            input_data:由(数据量, 通道, 高, 长)的4维数组构成的输入数据
            filter_h:滤波器的高
            filter_w:滤波器的长
            stride:步幅
            pad:填充

        Returns
            col:2维数组
    """
    N, C, H, W = input_data.shape
    out_h = 1 + (H + 2*pad - filter_h) // stride
    out_w = 1 + (W + 2*pad - filter_w) // stride
    
    img = np.pad(input_data, [(0,0), (0,0), (pad,pad), (pad,pad)], 'constant')
    col = np.zeros((N, C, filter_h, filter_w, out_h, out_w))

    for y in range(filter_h):
        y_max = y + stride*out_h
        for x in range(filter_w):
            x_max = x + stride*out_w
            col[:, :, y, x, :, :] = img[:, :, y:y_max:stride, x:x_max:stride]

    col = col.transpose(0, 4, 5, 1, 2, 3).reshape(N*out_h*out_w, -1)
    return col

def col2im(col, input_shape, filter_h, filter_w, stride=1, pad=0):
    """
        Parameters:
            col:2维数组
            input_data:由(数据量, 通道, 高, 长)的4维数组构成的输入数据
            filter_h:滤波器的高
            filter_w:滤波器的长
            stride:步幅
            pad:填充
        Returns:
            img:由(数据量, 通道, 高, 长)的4维数组构成的输出数据
    """
    N, C, H, W = input_shape
    out_h = (H + 2*pad - filter_h) // stride + 1
    out_w = (W + 2*pad - filter_w) // stride + 1
    col = col.reshape(N, out_h, out_w, C, filter_h, filter_w).transpose(0, 3, 4, 5, 1, 2) # reshape形状,transpose调整维度顺序
    img = np.zeros((N, C, H + 2*pad + filter_h - 1, W + 2*pad + filter_w - 1))
    for y in range(filter_h):
        y_max = y + stride*out_h
        for x in range(filter_w):
            x_max = x + stride*out_w
            img[:, :, y:y_max:stride, x:x_max:stride] += col[:, :, y, x, :, :]
    return img[:, :, pad:H + pad, pad:W + pad] # 去除填充部分

函数np.flatten()的作用是将多维数组扁平化为一维数组。np.flatten()默认按行优先进行降维,也就是将多维数组的第一行所有元素放到一位数组的前面,然后是第二行,依次类推。


网站公告

今日签到

点亮在社区的每一天
去签到