天天看点

10 卷积神经网络练习 - 卷积神经网络 [Deep Learning Specialization系列]

本文是Deep Learning Specialization系列课程的第4课《Convolutional Neural Networks》中Convolutional Neural Networks练习部分的学习笔记。

本文主要是通过NumPy来一步步实现卷积运算(Convolutional layer)和池化运算(Pooling layer)部分。具体包括:

  1. 填充
  2. 一个窗口的卷积运算
  3. 完整的卷积运算
  4. 池化运算
10 卷积神经网络练习 - 卷积神经网络 [Deep Learning Specialization系列]

1. 卷积运算

1.1 填充

填充有两个好处:

  1. 能保证卷积神经网络的大小不变,以应用很深的网络
  2. 在运算中保留更多的处于边缘的信息

填充的原理如图所示:

10 卷积神经网络练习 - 卷积神经网络 [Deep Learning Specialization系列]

在填充的运算中,我们可以直接使用NumPy提供的 np.pad 函数。比如,要对形状为(5, 5, 5, 5, 5)的

a

数组,将其第二维填充

pad=1

,第四维填充

pad=3

,其他维度不做填充(

pad=0

),其表达式为:

a_pad = np.pad(a, ((0, 0), (1, 1), (0, 0), (3, 3), (0, 0)), mode = 'constant', constant_values = (0, 0))

在我们的例子中,输入数据X的维度为

(m, n_h, n_w, n_c)

,我们只对图片的高、宽进行填充,最终填充的结果维度为

(m, n_h + 2*pad, n_w + 2*pad, n_c)

。其具体实现为:

def zero_pad(X, pad):
    X_pad = np.pad(X, ((0, 0), (pad, pad), (pad, pad), (0, 0)), mode='constant', constant_values = (0, 0))
    return X_pad
           

1.2 一个窗口的卷积运算

在卷积运算中,我们是需要将滤波器与输入数据进行叠加运算,并根据步长来移动窗口,最后得到卷积运算后的结果,具体如下图所示:

10 卷积神经网络练习 - 卷积神经网络 [Deep Learning Specialization系列]

我们这里先来实现一个窗口的卷积运算,即将滤波器叠加到输入图像,相乘后求和得到的结果,这里没有滤波器窗口的移动。这里

a_slice_prev

W

的尺寸相同,都为

(f, f, n_c_prev)

def conv_single_step(a_slice_prev, W, b):
    s = np.multiply(a_slice_prev, W)
    Z = np.sum(s) 
    Z = Z + b
    return Z
           

1.3 卷积运算

下面,就要将滤波器窗口移动起来了,这里就需要用到我们前面学到的关于第

l

层各参数的知识了:

  • 权重: ( f [ l ] , f [ l ] , n c [ l − 1 ] ) ∗ n c [ l ] (f^{[l]}, f^{[l]}, n_c^{[l-1]}) * n_c^{[l]} (f[l],f[l],nc[l−1]​)∗nc[l]​
  • 偏置: n c [ l ] n_c^{[l]} nc[l]​
  • 输入数据(第

    l-1

    层): ( n h [ l − 1 ] , n w [ l − 1 ] , n c [ l − 1 ] ) (n_h^{[l-1]}, n_w^{[l-1]}, n_c^{[l-1]}) (nh[l−1]​,nw[l−1]​,nc[l−1]​)
  • l

    层维度的计算公式: n h [ l ] = ( n h [ l − 1 ] + 2 p [ l ] − f [ l ] ) / s [ l ] + 1 n_h^{[l]} = (n_h^{[l-1]} +2p^{[l]} -f^{[l]})/s^{[l]} + 1 nh[l]​=(nh[l−1]​+2p[l]−f[l])/s[l]+1

于是有下面的维度信息:

  • A_prev: (m, n_h_prev, n_w_prev, n_c_prev)
  • W: (f, f, n_c_prev, n_c)
  • b: (1, 1, 1, n_c)
  • Z: (m, n_h, n_w, n_c)

在移动窗口计算中,需要定义移动窗口在输入层中的位置,这里需要用到for循环。下图是定义该窗口的一个很好的图示:

10 卷积神经网络练习 - 卷积神经网络 [Deep Learning Specialization系列]

在实现代码中,需要注意的是权重的维度以及for循环中各个参数的维度:

def conv_forward(A_prev, W, b, h_parameters):
    stride = h_parameters['stride']
    pad = h_parameters['pad']

    (m, n_h_prev, n_w_prev, n_c_prev) = A_prev.shape
    (f, f, n_c_prev, n_c) = W.shape

    n_h = int((n_h_prev + 2 * pad - f) / stride + 1)
    n_w = int((n_w_prev + 2 * pad - f) / stride + 1)

    Z = np.zeros((m, n_h, n_w, n_c))

    A_prev_pad = zero_pad(A_prev, pad)

    for i in range(m):
        a_prev_pad = A_prev_pad[i]

        for h in range(n_h):
            vert_start = h * stride
            vert_end = vert_start + f

            for w in range(n_w):
                hori_start = w * stride
                hori_end = hori_start + f

                for c in range(n_c):
                    a_slice_prev = a_prev_pad[vert_start:vert_end, hori_start:hori_end, :]

                    weights = W[:, :, :, c]
                    bias = b[:, :, :, c]
                    Z[i, h, w, c] = conv_single_step(a_slice_prev, weights, bias)

    assert(Z.shape == (m, n_h, n_w, n_c))

    cache = (A_prev, W, b, h_parameters)

    return Z, cache
           

2. 池化运算

池化层是为了减少输入层的尺寸,在保证特征不变的情况下来减少运算,一般是包含最大值和平均值两种方法。

10 卷积神经网络练习 - 卷积神经网络 [Deep Learning Specialization系列]

在池化层的运算中,其大部分与卷积层运算类似,只是有以下不同点:

  • 没有填充
  • 没有权重、偏置参数的计算
  • 输出的通道与输入相同

具体代码实现如下:

def pool_forward(A_prev, h_parameters, mode = 'max'):
    f = h_parameters['f']
    stride = h_parameters['stride']

    (m, n_h_prev, n_w_prev, n_c_prev) = A_prev.shape

    n_h = int((n_h_prev - f) / stride + 1)
    n_w = int((n_w_prev - f) / stride + 1)
    n_c = n_c_prev

    A = np.zeros((m, n_h, n_w, n_c))

    for i in range(m):
        a_prev = A_prev[i]
        for h in range(n_h):
            vert_start = h * stride
            vert_end = vert_start + f
            for w in range(n_w):
                hori_start = w * stride
                hori_end = hori_start + f
                for c in range(n_c):
                    a_slice_prev = a_prev[vert_start:vert_end, hori_start:hori_end, c]

                    if mode == 'max':
                        A[i, h, w, c] = np.max(a_slice_prev)
                    if mode == 'average':
                        A[i, h, w, c] = np.average(a_slice_prev)

    cache = (A_prev, h_parameters)

    assert(A.shape == (m, n_h, n_w, n_c))

    return A, cache
           

继续阅读