卷積層
原理:基於人腦的圖片識別過程,我們可以認為圖像的空間聯系也是局部的像素聯系比較緊密,而較遠的像素相關性比較弱,所以每個神經元沒有必要對全局圖像進行感知,只要對局部進行感知,而在更高層次對局部的信息進行綜合操作得出全局信息;即局部感知。
卷積分的知識
過程:
作用:
局部感知:在進行計算的時候,將圖片划分為一個個的區域進行計算/考慮; 參數共享機制:假設每個神經元連接數據窗的權重是固定的 滑動窗口重疊:降低窗口與窗口之間的邊緣不平滑的特性。
不同的過濾器產生不同的效果:
真實做了什么?
一步有一步的濃縮,產生更加靠譜更加准確的特征
一張圖片卷積后高和寬如何變化?
# 卷積函數
def conv_fun(cache):
x, w, b = cache["a"], cache["w"], cache["b"]
pad, stride = cache["pad"], cache["stride"]
N, C, H, W = x.shape
F, C, HH, WW = w.shape
# numpy提供的可以填充0的api,constant代表用一樣的值填充前兩維不填,后兩維各自填充pad行
x_padded = np.pad(x, ((0, 0), (0, 0), (pad, pad), (pad, pad)), mode='constant')
H_new = int((H + 2 * pad - HH) / stride) + 1
W_new = int((W + 2 * pad - WW) / stride) + 1
s = stride
out = np.zeros((N, F, H_new, W_new))
for i in range(N): # ith image
for f in range(F): # fth filter
for j in range(H_new):
for k in range(W_new):
out[i, f, j, k] = np.sum
(x_padded[i, :, j * s:(HH + j * s), k * s:(WW + k * s)] * w[f]) +b[f]
return out
池化層
池化層:通過特征后稀疏參數來減少學習的參數,降低網絡的復雜度,(最大池化和平均池化)
# 前向池化
def max_pool_forward(cache):
x, HH, WW, s = cache["net"], cache["HH"], cache["WW"], cache["s"]
N, C, H, W = x.shape
H_new = 1 + int((H - HH) / s)
W_new = 1 + int((W - WW) / s)
out = np.zeros((N, C, H_new, W_new))
for i in range(N):
for j in range(C):
for k in range(H_new):
for l in range(W_new):
# 定位到某個窗口
window = x[i, j, k * s:HH + k * s, l * s:WW + l * s]
# 找到該窗口的最大值,然后賦值
out[i, j, k, l] = np.max(window)
return out
ReLU層
http://playground.tensorflow.org/
作用:增加網絡非線性的分割能力
# Relu函數
def Relu(x):
return np.maximum(0, x)
全連接層
# 全連接
def fc(net, w, b):
N = net.shape[0]
# 把每個像素提取出來
x_row = net.reshape(N, -1)
out = np.dot(x_row, w) + b
return out
CNN反向傳播的不同之處
首先要注意的是,一般神經網絡中每一層輸入輸出a,z都只是一個向量,而CNN中的a,z是一個三維張量,即由若干個輸入的子矩陣組成。其次:
-
池化層沒有激活函數。這個問題倒比較好解決,我們可以令池化層的激活函數為σ(z)=z,即激活后就是自己本身。這樣池化層激活函數的導數為1。
-
池化層在前向傳播的時候,對輸入進行了壓縮,那么我們向前反向推導上一層的誤差時,需要做upsample處理。
-
卷積層是通過張量卷積,或者說若干個矩陣卷積求和而得到當前層的輸出,這和一般的網絡直接進行矩陣乘法得到當前層的輸出不同。這樣在卷積層反向傳播的時候,上一層誤差的遞推計算方法肯定有所不同。
-
對於卷積層,由於W使用的運算是卷積,那么由該層誤差推導出該層的所有卷積核的W,b的方式也不同。
池化層的反向傳播
這時候假如前向的時候是最大化池化:
這時候要用到前向傳播的時候最大值位置進行還原:
如果是平均:
卷積層的反向傳播
吳恩達筆記中的推導:
卷積神經網絡中的反向傳播(可選/非梯度)
在現代深度學習框架中,你只需要執行正向傳遞,這個框架負責向后傳遞,所以大多數深度學習工程師不會這樣做
需要麻煩的細節向后傳遞。卷積的向后傳遞網絡是復雜的。但是,如果您願意,您可以完成這個可選部分
來了解卷積網絡中的backprop是什么樣子的。
在之前的課程中,你實現了一個簡單的(完全連接的)神經網絡,使用反向傳播來計算關於更新成本的導數
參數。類似地,在卷積神經網絡中你可以計算為了更新參數對代價求導。backprop方程不是平常那樣的,我們在課堂上沒有推導出來,但是我們簡單地介紹了一下:
Convolutional layer backward pass
讓我們從實現CONV層的向后傳遞開始。
-
計算dA:
這是計算dA對於一定的過濾器Wc的代價的公式以及一個給定的訓練例子:
其中Wc是一個過濾器,dZhw是上一層傳遞過來的梯度,每次我們都將相同的過濾器Wc乘以不同的dZ更新的。我們這樣做主要是因為當計算正向傳播時,每個過濾器都是由不同的a_slice點乘求和。因此,當計算dA的反向傳播的時候,我們也是把所有a_slice的梯度相加。
在代碼中,在合適的for循環中,這個公式可以轉化為:
da_prev_pad[vert_start:vert_end, horiz_start:horiz_end, :]
+=,W[:,:,:,c] * dZ[i, h, w, c]
-
計算dW: 這是計算dWc (dWc是一個過濾器的導數)關於損失的公式:
其中aslice是原樣本的一個窗口。因此,這就得到了W關於這個窗口的梯度。這是相同的W,我們將所有這些梯度相加得到dW。
在代碼中,在合適的for循環中,這個公式可以轉化為:
dW[:,:,:,c] += a_slice * dZ[i, h, w, c]
-
計算db: 這是計算db對於一個過濾器Wc的代價的公式:
正如您之前在基本神經網絡中看到的,db是通過求和dZ來計算的。在本例中只需對conv輸出(Z)的所有梯度求和。
在代碼中,在合適的for循環中,這個公式可以轉化為:
db[:,:,:,c] += dZ[i, h, w, c]
真實代碼:
def conv_backward(dout, cache): x, w, b = cache["a"], cache["w"], cache["b"] pad, stride = cache["pad"], cache["stride"] F, C, HH, WW = w.shape N, C, H, W = x.shape H_new = 1 + int((H + 2 * pad - HH) / stride) W_new = 1 + int((W + 2 * pad - WW) / stride) dx = np.zeros_like(x) dw = np.zeros_like(w) db = np.zeros_like(b) s = stride x_padded = np.pad(x, ((0, 0), (0, 0), (pad, pad), (pad, pad)), 'constant') dx_padded = np.pad(dx, ((0, 0), (0, 0), (pad, pad), (pad, pad)), 'constant') for i in range(N): # ith image for f in range(F): # fth filter for j in range(H_new): for k in range(W_new): window = x_padded[i, :, j * s:HH + j * s, k * s:WW + k * s] # db = dout // dw=dout*x // dx = dout*w db[f] += dout[i, f, j, k] dw[f] += window * dout[i, f, j, k] dx_padded[i, :, j * s:HH + j * s, k * s:WW + k * s] += w[f] * dout[i, f, j, k]