1. 程式人生 > >吳恩達深度學習4-Week1課後作業1-卷積模型Step by Step

吳恩達深度學習4-Week1課後作業1-卷積模型Step by Step

一、deeplearning-assignment

在本次任務中,我們將學習用numpy實現卷積(CONV)層和池化(POOL)層,由於大多數深度學習工程師不需要關注反向傳遞的細節,而且卷積網路的反向傳遞很複雜,所以在本次作業中只討論關於前向傳播的處理細節。

用 python 來實現每個函式,在下次任務用 TensorFlow 中等價的函式構造如下模型:

對於每個前向傳播,都有相應的反向傳播。因此,每一步前向傳播,都會儲存一些引數在快取中。這些引數用於計算反向傳播中的梯度。

卷積神經網路

儘管程式設計框架使得卷積操作易於實現,但它仍然是深度學習中最難理解的概念之一。卷積層將輸入轉換為不同尺寸的輸出:

在實現卷積神經網路之前,首先要實現兩個輔助函式:一個用於零填充,另一個用於卷積計算。

零填充

指的是在影象的邊緣填充一系列的零點。

上圖中,將一個影象(三個通道對應三個RGB值)的每一層RGB矩陣進行padding為2的零填充。

零填充的主要好處如下:

  • 它允許你在不縮小寬度和高度的同時使用CONV層. 這對構建更深層次的網路非常重要, 否則網路越深,寬度/高度越小. 一個重要的特殊案例是 "same" convolution, 它在經歷一層卷積後寬度/高度維持不變.

  • 它會在影象的邊界保留更多的資訊. 如果沒有填充,下一層的極少數值會受到邊緣畫素的影響。

卷積計算

卷積計算

將過濾器對應於Image中對應的位置,進行乘法運算後將累加和輸出到新矩陣對應的位置,然後通過步長平移相應的位置重複之前的步驟,直到輸出矩陣計算完畢。

卷積神經網路的前向傳播

在正向傳遞中,將對輸入採用多種過濾器進行卷積。每個“卷積”輸出一個二維矩陣。最後疊加二維矩陣獲得3D volume。

池化層

池化層會降低輸入的高度和寬度。它有助於減少計算量,主要有兩種型別的池化,如下圖所示:

池化層中相關引數的計算:

二、相關演算法程式碼

import numpy as np
import h5py
import matplotlib.pyplot as plt

plt.rcParams['figure.figsize'] = (5.0, 4.0)  # set default size of plots
plt.rcParams['image.interpolation'] = 'nearest'
plt.rcParams['image.cmap'] = 'gray'

np.random.seed(1)


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


# np.random.seed(1)
# x = np.random.randn(4, 3, 3, 2)
# x_pad = zero_pad(x, 2)
# print("x.shape =", x.shape)
# print("x_pad.shape =", x_pad.shape)
# print("x[1,1] =", x[1, 1])
# print("x_pad[1,1] =", x_pad[1, 1])
# fig, axarr = plt.subplots(1, 2)
# axarr[0].set_title('x')
# axarr[0].imshow(x[0, :, :, 0])
# axarr[1].set_title('x_pad')
# axarr[1].imshow(x_pad[0, :, :, 0])
# plt.show()


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


# np.random.seed(1)
# a_slice_prev = np.random.randn(4, 4, 3)
# W = np.random.randn(4, 4, 3)
# b = np.random.randn(1, 1, 1)
# Z = conv_single_step(a_slice_prev, W, b)
# print("Z =", Z)


def conv_forward(A_prev, W, b, hparameters):
    (m, n_H_prev, n_W_prev, n_C_prev) = A_prev.shape
    (f, f, n_C_prev, n_C) = W.shape

    stride = hparameters['stride']
    pad = hparameters['pad']

    n_H = int((n_H_prev - f + 2 * pad) / stride + 1)
    n_W = int((n_W_prev - f + 2 * pad) / 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):
            for w in range(n_W):
                for c in range(n_C):
                    vert_start = h * stride
                    vert_end = vert_start + f
                    horiz_start = w * stride
                    horiz_end = horiz_start + f

                    a_slice_prev = a_prev_pad[vert_start: vert_end, horiz_start: horiz_end, :]

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

    assert (Z.shape == (m, n_H, n_W, n_C))

    cache = (A_prev, W, b, hparameters)

    return Z, cache


# np.random.seed(1)
# A_prev = np.random.randn(10, 4, 4, 3)
# W = np.random.randn(2, 2, 3, 8)
# b = np.random.randn(1, 1, 1, 8)
# hparameters = {"pad": 2,
#                "stride": 2}
# Z, cache_conv = conv_forward(A_prev, W, b, hparameters)
# print("Z.shape = ", Z.shape)
# print("Z's mean = ", np.mean(Z))
# print("Z[3,2,1] = ", Z[3, 2, 1])
# print("cache_conv[0][1][2][3] =", cache_conv[0][1][2][3])


def pool_forward(A_prev, hparameters, mode='max'):
    (m, n_H_prev, n_W_prev, n_C_prev) = A_prev.shape
    f = hparameters['f']
    stride = hparameters['stride']

    n_H = int(1 + (n_H_prev - f) / stride)
    n_W = int(1 + (n_W_prev - f) / stride)
    n_C = n_C_prev

    A = np.zeros((m, n_H, n_W, n_C))

    for i in range(m):
        for h in range(n_H):
            for w in range(n_W):
                for c in range(n_C):
                    vert_start = h * stride
                    vert_end = vert_start + stride
                    horiz_start = w * stride
                    horiz_end = horiz_start + stride

                    a_prev_slice = A_prev[i, vert_start:vert_end, horiz_start:horiz_end, c]

                    if mode == "max":
                        A[i, h, w, c] = np.max(a_prev_slice)
                    elif mode == "average":
                        A[i, h, w, c] = np.mean(a_prev_slice)

    cache = (A_prev, hparameters)
    assert (A.shape == (m, n_H, n_W, n_C))

    return A, cache


np.random.seed(1)
A_prev = np.random.randn(2, 4, 4, 3)
hparameters = {"stride": 2, "f": 3}
A, cache = pool_forward(A_prev, hparameters)
print("mode = max")
print("A =", A)
print()
A, cache = pool_forward(A_prev, hparameters, mode="average")
print("mode = average")
print("A =", A)

三、總結

在前面的學習中,我們知道了怎樣通過numpy建立輔助函式來理解卷積神經網路背後的機制,包括零填充、卷積計算以及池化層等相關的操作。不過今天大多數實際應用的深度學習都會使用程式設計框架,有很多內建函式可以簡單地呼叫,在下次的work中,我們將學習運用tensorflow框架來實現卷積神經網路。