CNN


Convolutional Neural Networks
Zero padding
ダウンジャケットを追加する理由は、合成乗算中に入力値の大きさが減少することを防止するためです.これは深さニューラルネットワークを構築する上で非常に重要な部分であり,主に合成乗子を構築した後の結果値の体積が変わらない同治合成乗子(同じボリューム)に用いられる.
また、合成乗算を行うことは、画像自体のエッジ特徴の抽出に困難をもたらす.このため、ダウンジャケットを追加することで、すべての画素を同等にフィルタリングすることができる.入力値の次元が(m, n_H, n_W, n_C)である場合、np.padを使用してダウンジャケットを追加することができる.
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
Convolution
プロセス中に現れる各要素を乗じた階層を合成するには、以前の記事を参照してください.まず、入力値においてフィルタの合成に乗算するために、この部分がa_slice_prevに抽出されたときに、単一の合成に乗算された関数を仮定する.
def conv_single_step(a_slice_prev, W, b):
    """
    Arguments:
    a_slice_prev -- slice of input data of shape (f, f, n_C_prev)
    W -- Weight parameters contained in a window - matrix of shape (f, f, n_C_prev)
    b -- Bias parameters contained in a window - matrix of shape (1, 1, 1)
    """
    # Element-wise product between a_slice_prev and W. Do not add the bias yet.
    s = W * a_slice_prev
    # Sum over all entries of the volume s.
    Z = np.sum(s)
    # Add bias b to Z. Cast b to a float() so that Z results in a scalar value.
    Z = Z + np.float64(b)

    return 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(shape = (m, n_H, n_W, n_C))
    
    A_prev_pad = zero_pad(A_prev, pad)

    for i in range(m):
    
        a_prev_pad = pad               
        for h in range(n_H):          
            vert_start = h * stride
            vert_end = vert_start + f
            
            for w in range(n_W):
                horiz_start = w * stride
                horiz_end = horiz_start + f
                
                for c in range(n_C):
                    a_slice_prev = A_prev_pad[i, vert_start : vert_end, horiz_start : horiz_end, :]
                    
                    weights = W[:, :, :, c]
                    biases = b[:, :, :, c]
                    Z[i, h, w, c] = conv_single_step(a_slice_prev, weights, biases)
                    
    # Save information in "cache" for the backprop
    cache = (A_prev, W, b, hparameters)
    
    return Z, cache
Pooling Layer
「ポーリング」を適用すると、入力値の高さと幅が減少し、フィーチャーを簡単に検索するだけでなく、計算が簡単であるなどの利点があります.展開中は、通常、ダウンジャケットを追加しない場合は、ステップサイズとフィルタサイズのみを使用して計算されます.
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):
            vert_start = h * stride
            vert_end = vert_start + f
            
            for w in range(n_W):  
                horiz_start = w * stride
                horiz_end = horiz_start + f
                
                for c in range (n_C):       
                    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.average(a_prev_slice)
                        
    cache = (A_prev, hparameters)
    
    assert(A.shape == (m, n_H, n_W, n_C))
    
    return A, cache
Backpropagation in CNN
Convolution Layer
基本的には,順方向伝搬と損失関数のみを計算し,ライブラリやフレームワークにおける逆伝搬過程を容易に計算することもできる.従って,効率を考慮すれば逆伝搬に気を配る必要はないが,全体構造の独特な合成乗ニューラルネットワークにおいて,逆伝搬がどのように発生するかを見てみよう.
Computing dA
AAA値が合成乗算とスラリー処理で得られた結果値で変化量を測定するためには、合成乗算中に発生した変化量dZdZDZで測定する必要がある.アクティブ値dAddAは、フィルタWcWcWcによって計算されます.
 dA+=∑h=0nH∑w=0nW(Wc×dZhw)\dA\mathrel{+}=\sum_{h=0}^{nH}\sum_{w=0}^{nW}(W_c\times dZ_{hw}) dA+=h=0∑nH​w=0∑nW​(Wc​×dZhw​)
Computing dW
上記の手順と同様に、フィルタの変化量を調べるためにはdzdzdzを逆合成して乗算する必要がありますが、ターゲットは固定アクティビティ値の一部である必要があります.
 dWc+=∑h=0nH∑w=0nW(aslice×dZhw)\dW_c\mathrel{+}=\sum_{h=0}^{nH}\sum_{w=0}^{nW}(a_{slice}\times dZ_{hw}) dWc​+=h=0∑nH​w=0∑nW​(aslice​×dZhw​)
aslicea{slice}asliceとは、火星化値から一部を抽出して合成乗を計算する値である.
Computing db
偏差値の変化量は合成乗の後に現れるので、微分時にdzdzdzに乗算する要因は不要であり、逆に合成乗過程を行えばよい.
 db+=∑h=0nH∑w=0nWdZhw\db\mathrel{+}=\sum_{h=0}^{nH}\sum_{w=0}^{nW}dZ_{hw} db+=h=0∑nH​w=0∑nW​dZhw​
conv_backward
def conv_backward(dZ, cache):
    (A_prev, W, b, hparameters) = cache
    (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"]
    
    (m, n_H, n_W, n_C) = dZ.shape
    
    # Initialize dA_prev, dW, db with the correct shapes
    dA_prev = np.zeros(shape = A_prev.shape)                          
    dW = np.zeros(shape = W.shape)
    db = np.zeros(shape = b.shape)
    
    # Pad A_prev and dA_prev
    A_prev_pad = zero_pad(A_prev, pad)
    dA_prev_pad = zero_pad(dA_prev, pad)
    
    for i in range(m):                       
        # select ith training example from A_prev_pad and dA_prev_pad
        a_prev_pad = A_prev_pad[i]
        da_prev_pad = dA_prev_pad[i]
        
        for h in range(n_H):
           for w in range(n_W):
               for c in range(n_C):
                    # Find the corners of the current "slice"
                    vert_start = h * stride
                    vert_end = vert_start + f
                    horiz_start = w * stride
                    horiz_end = horiz_start + f

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

                    da_prev_pad[vert_start:vert_end, horiz_start:horiz_end, :] += W[:,:,:,c] * dZ[i, h, w, c]
                    dW[:,:,:,c] += a_slice * dZ[i, h, w, c]
                    db[:,:,:,c] += dZ[i, h, w, c]
                    
        # Set the ith training example's dA_prev to the unpadded da_prev_pad 
        dA_prev[i, :, :, :] = da_prev_pad[pad:-pad, pad:-pad, :]
    
    # Making sure your output shape is correct
    assert(dA_prev.shape == (m, n_H_prev, n_W_prev, n_C_prev))
    
    return dA_prev, dW, db
Pooling Layer
解法中に学習が必要なパラメータは存在しないが,どのような解法が用いられているかによって,dAddAに渡す必要がある要素値が変化する.
Max Pooling
最大ポーリングでは、ポーリングを適用するウィンドウのどの要素がポーリングの結果であり、どの要素が採用されているかを示すパラメータが必要です.
def create_mask_from_window(x):
    mask = x == np.max(x)

    return mask
ウィンドウに表示マトリクスを作成することは、最大値のプール化レイヤを適用して後方伝播を適用すると、プール化オブジェクトとしての因子だけが損失関数の影響を受けることを意味します.そのためには、影響を受けた部分と影響を受けない部分を区別するためにcreate_mask_from_window()が必要である.
Average Pooling
平均放出過程では,最大放出過程とは異なり,すべての行列の要因が損失関数に影響を及ぼす.したがって,dzdzdzを各要素に分散して修正する必要がある.
def distribute_value(dz, shape):
    (n_H, n_W) = shape
    average = dz / (n_H * n_W)
    a = np.full(shape, average)
    return a
pool_backward
def pool_backward(dA, cache, mode = "max"):
    (A_prev, hparameters) = cache
    stride = hparameters["stride"]
    f = hparameters["f"]
    
    (m, n_H_prev, n_W_prev, n_C_prev) = A_prev.shape
    (m, n_H, n_W, n_C) = dA.shape
    
    dA_prev = np.zeros(shape = A_prev.shape)
    
    for i in range(m): # loop over the training examples
        a_prev = A_prev[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
                    
                    # Compute the backward propagation in both modes.
                    if mode == "max":
                        a_prev_slice = a_prev[vert_start: vert_end, horiz_start: horiz_end, c]
                        mask = create_mask_from_window(a_prev_slice)

                        dA_prev[i, vert_start: vert_end, horiz_start: horiz_end, c] += dA[i, h, w, c] * mask
                        
                    elif mode == "average":
                        da = dA[i, h, w, c]
                        shape = (f, f)

                        dA_prev[i, vert_start: vert_end, horiz_start: horiz_end, c] += distribute_value(da, shape)

    assert(dA_prev.shape == A_prev.shape)
    
    return dA_prev
Reference
  • deeplearning.ai