簡(jiǎn) 介
一、relu層
1、前向傳播
2、反向傳播
二、dropout層
1、dropout工作原理
2、在哪里使用dropout
3、dropout的實(shí)現(xiàn)
4、dropout的功效
5、python實(shí)現(xiàn)dropout的前向傳播
6、python實(shí)現(xiàn)dropout的反向傳播
三、卷積層
1 Im2col
2前向傳播
3反向
4小案例
四、池化層
1、python實(shí)現(xiàn)池化層的前向傳播
2、python實(shí)現(xiàn)池化層的反向傳播
relu層
如何在Python中實(shí)現(xiàn)ReLU層?
簡(jiǎn)而言之,relu層就是輸入張量通過一個(gè)非線性的relu函數(shù),得到輸出,而不改變其空間或者深度信息
image
從上圖可以看出,所有大于0的保持不變,而小于零的變?yōu)榱?。此外,空間信息和深度也是相同的
relu函數(shù)作為激活函數(shù),具有以下功能:
易于計(jì)算(前向/反向傳播),采用sigmoid函數(shù)作為激活函數(shù)時(shí)候(指數(shù)運(yùn)算),計(jì)算量大,反向傳播求誤差梯度時(shí),求導(dǎo)涉及除法,計(jì)算量相當(dāng)大,而采用Relu激活函數(shù),整個(gè)過程的計(jì)算量節(jié)省很多。
深度模型中受消失梯度的影響要小得多,對(duì)于深層網(wǎng)絡(luò),sigmoid函數(shù)反向傳播時(shí),很容易就出現(xiàn)梯度消失的情況(在sigmoid函數(shù)接近飽和區(qū)時(shí),變換太緩慢,導(dǎo)數(shù)趨于0,這種情況會(huì)造成信息丟失),從而無(wú)法完成深層網(wǎng)絡(luò)的訓(xùn)練。
如果你使用大的學(xué)習(xí)率,他們可能會(huì)不可逆轉(zhuǎn)地死去,因?yàn)楫?dāng)一個(gè)非常大的梯度流過一個(gè) ReLU 神經(jīng)元,更新過參數(shù)之后,這個(gè)神經(jīng)元再也不會(huì)對(duì)任何數(shù)據(jù)有激活現(xiàn)象了。這個(gè)神經(jīng)元的梯度將一直都是0了。
1、前向傳播
將所有小于0的數(shù)變成0,大于0的數(shù)保持不變,空間和深度信息保持不變。
python實(shí)現(xiàn)relu的前向傳播:
2、反向傳播
在前向傳播的時(shí)候,我們對(duì)每個(gè)輸入X=[x1,x2,x3]應(yīng)用了max(0,x)函數(shù),所以在反向傳播的時(shí)候,小于0的元素,梯度dx等于0:
python實(shí)現(xiàn)relu 反向傳播:
02
Dropout層
Dropout是一種用于防止神經(jīng)網(wǎng)絡(luò)過度擬合的技術(shù),你還可以使用L2正則化防止過擬合。
image
下面是分類的錯(cuò)誤率,可以發(fā)現(xiàn)使用了dropout之后錯(cuò)誤率更低:
image
和其他正則化技術(shù)一樣,使用dropout會(huì)使得訓(xùn)練損失稍稍惡化,但是模型的泛化能力卻更好,因?yàn)槿绻覀兊哪P瓦^于復(fù)雜(更多層或者更多神經(jīng)元),模型就很可能過擬合,下面是訓(xùn)練和驗(yàn)證集上的損失情況,以及他們中有無(wú)dropout情況。
image
1、dropout工作原理
在訓(xùn)練期間,隨機(jī)的選擇一定比例的神經(jīng)元,讓它停止工作,如下圖所示,這樣泛化能力更好,因?yàn)槟愕木W(wǎng)絡(luò)層的不同的神經(jīng)元會(huì)學(xué)習(xí)相同的“概念”。在測(cè)試階段,不需要使用dropout.
2、在哪里使用dropout
通常會(huì)在全連接層使用dropout,但也可以在最大池化后使用dropout,從而產(chǎn)生某種圖像噪聲增強(qiáng)。
3、dropout的實(shí)現(xiàn)
為了實(shí)現(xiàn)某個(gè)神經(jīng)元的失活,我們?cè)谇跋騻鞑ミ^程中創(chuàng)建一個(gè)掩碼(0和1),此掩碼應(yīng)用于訓(xùn)練期間的層的輸出,并緩存以供以后在反向傳播中使用。如前所述,這個(gè)dropout掩碼只在訓(xùn)練中使用。
在反向傳播中,我們對(duì)被激活的神經(jīng)元感興趣(我們需要將掩碼保存為前向傳播),這些被選中的神經(jīng)元中,使用反向傳播,失活的神經(jīng)元沒有可學(xué)習(xí)的參數(shù),僅僅是輸入x,反向傳播返回dx。
4、dropout的功效
Dropout背后理念和集成模型很相似。在Drpout層,不同的神經(jīng)元組合被關(guān)閉,這代表了一種不同的結(jié)構(gòu),所有這些不同的結(jié)構(gòu)使用一個(gè)的子數(shù)據(jù)集并行地帶權(quán)重訓(xùn)練,而權(quán)重總和為1。
如果Dropout層有 n 個(gè)神經(jīng)元,那么會(huì)形成2^n個(gè)不同的子結(jié)構(gòu)。在預(yù)測(cè)時(shí),相當(dāng)于集成這些模型并取均值。這種結(jié)構(gòu)化的模型正則化技術(shù)有利于避免過擬合。
Dropout有效的另外一個(gè)視點(diǎn)是:由于神經(jīng)元是隨機(jī)選擇的,所以可以減少神經(jīng)元之間的相互依賴,從而確保提取出相互獨(dú)立的重要特征。
5、python實(shí)現(xiàn)dropout的前向傳播
6、python實(shí)現(xiàn)dropout的反向傳播
image
03
卷積層
簡(jiǎn)單的說(shuō),卷積層所做的工作就是對(duì)輸入的特征圖應(yīng)用卷積算子,卷積核的個(gè)數(shù)是輸出特征圖的深度。下面我們介紹一下相關(guān)的參數(shù):
N:批處理大小(4d張量上的圖像數(shù))
F:卷積層上的濾波器個(gè)數(shù)
kW/kH:內(nèi)核寬度/高度(通常我們使用方形卷積核,kW=kH)
H/W:圖像高度/寬度(通常H=W)
H'/W':卷積圖像高度/寬度(如果使用適當(dāng)?shù)奶畛?,則與輸入相同)
Stride:卷積滑動(dòng)窗口將要移動(dòng)的像素?cái)?shù)。
Padding:將0添加到圖像的邊框,以保持輸入和輸出大小相同。
Depth:輸入特征圖的深度(如輸入為RGB圖像則深度為3)
Output depth:輸出的特征圖的深度(與F相同)
1、前向傳播
在前向傳播過程中,我們用不同的過濾器“卷積”輸入,每個(gè)過濾器將在圖像上尋找不同的特征。
在這里觀察到所有來(lái)自第一層的神經(jīng)元共享相同的權(quán)重集,不同的過濾器得到不同的特征。
2、python實(shí)現(xiàn)卷積層的前向傳播
3、反向傳播
為了更好的理解,這里使用1維卷積來(lái)理解卷積層的反向傳播,2維的也類似。
輸入信號(hào)為X=[x0,x1,x2,x3,x4],參數(shù)為W=[w0,w1,w2],不使用padding,卷積之后的結(jié)果是:Y=[y0,y1,y2],這里Y = X * flip(W),flip可以看作是180度的旋轉(zhuǎn)。
現(xiàn)在我們使用計(jì)算圖來(lái)表示,并且加上一個(gè)偏差,通過觀察可以發(fā)現(xiàn)這個(gè)過程跟全連接層類似,不同之處在于卷積核可以使得權(quán)重共享。
現(xiàn)在來(lái)看反向傳播
向后追蹤計(jì)算圖,反向傳播可以表示為以下的公式
意味著損失值隨著輸入進(jìn)行變化,由上圖可以看出。
image
注意:
dX跟X大小相同,所以我們需要進(jìn)行填充
dout跟Y大小相同,在本例中為3(漸變輸入)
為了節(jié)省編程工作量,我們將梯度的計(jì)算采用卷積的形式
在dX梯度上,所有元素都乘以W,所以我們可能會(huì)對(duì)W和dout進(jìn)行卷積操作
1d卷積的輸出尺寸計(jì)算公式:outputSize=(InputSize-KernelSize+2P)+1,
我們期望的尺寸是3,由于原始輸入尺寸是3,并且我們將與也有3個(gè)元素的W矩陣進(jìn)行卷積。所以我們需要用2個(gè)零填充輸入,之后再進(jìn)行卷積,就可以得到尺寸為3的輸出。
就卷積而言:
根據(jù)鏈?zhǔn)椒▌t,求損失函數(shù)對(duì)各個(gè)參數(shù)的偏導(dǎo):
再次查看從圖表中得到的表達(dá)式,可以將它們表示為dout和X之間的卷積。同樣,由于輸出將是3個(gè)元素,因此不需要進(jìn)行填充。
就卷積的計(jì)算而言,

如果將X看成是卷積核,而dout看做輸入信號(hào),則:
對(duì)于偏差,計(jì)算將類似于全連接層。 基本上我們每個(gè)過濾器有一個(gè)偏差,計(jì)算如下:
4、python實(shí)現(xiàn)卷積的反向傳播
5、卷積運(yùn)算轉(zhuǎn)換為矩陣運(yùn)算
使用矩陣運(yùn)算,能夠使得運(yùn)算速度更快,但也會(huì)消耗更多的內(nèi)存。
5.1 Im2col
前面的代碼,使用的是for循環(huán)來(lái)實(shí)現(xiàn)卷積,運(yùn)算速度不夠快,在本節(jié)中,我們將學(xué)習(xí)如何使用矩陣運(yùn)算來(lái)實(shí)現(xiàn)卷積,首先,卷積是內(nèi)核過濾器和它移動(dòng)之后在圖像上選擇的區(qū)域之間的點(diǎn)積,如果我們?cè)趦?nèi)存上擴(kuò)展所有可能的窗口并將點(diǎn)積作為矩陣運(yùn)算,運(yùn)算速度將更快,但內(nèi)存的消耗也會(huì)更大。
例如,輸入圖片為2272273,卷積核為11113,步長(zhǎng)為4,padding為0,進(jìn)行卷積運(yùn)算的時(shí)候,我們可以將卷積核在輸入圖片上采樣的11113大小的像素塊(感受野)拉伸為大小為11113=363的列向量,2272273大小的圖片,又有步長(zhǎng)為4,padding為0,卷積之后的寬高計(jì)算方式為(227-11)/4)+1=55,所以采樣之后得到5555個(gè)11113大小的像素塊(感受野),最終可以得到尺寸為3633025的輸出矩陣X_col,(3025由55*55得到,表示有3025個(gè)感受野)
總結(jié)一下,如何計(jì)算im2col輸出的大?。?/p>
[img_height, img_width, img_channels] = size(img);
newImgHeight = floor(((img_height + 2P - ksize) / S)+1);
newImgWidth = floor(((img_width + 2
P - ksize) / S)+1);
cols = single(zeros((img_channelsksizeksize),(newImgHeight * newImgWidth)));
卷積核也進(jìn)行類似的伸展,假設(shè)有96個(gè)大小為11113的卷積核,通過im2col函數(shù)之后,得到96*363的矩陣W_col.
將圖像和卷積核轉(zhuǎn)換之后,卷積操作就變成了簡(jiǎn)單的矩陣乘法運(yùn)算,這個(gè)例子中,W_col(96363)c乘以X_col(3633025)得到的矩陣是963025,最后可以重塑為5555*96,重塑可以定義一個(gè)col2im的函數(shù)來(lái)實(shí)現(xiàn)。
5.2前向傳播計(jì)算圖
下圖是前向傳播中使用im2col之后的計(jì)算圖,輸入為443,步長(zhǎng)為1,padding為0,卷積核大小為2*2,卷積核個(gè)數(shù)為1:
前向傳播代碼如下:
defconv_forward_naive(x, w, b, conv_param):"""
? A naive implementation of the forward pass for a convolutional layer.
? The input consists of N data points, each with C channels, height H and width
? W. We convolve each input with F different filters, where each filter spans
? all C channels and has height HH and width HH.
? Input:
? - x: Input data of shape (N, C, H, W)
? - w: Filter weights of shape (F, C, HH, WW)
? - b: Biases, of shape (F,)
? - conv_param: A dictionary with the following keys:
? ? - 'stride': The number of pixels between adjacent receptive fields in the
? ? ? horizontal and vertical directions.
? ? - 'pad': The number of pixels that will be used to zero-pad the input.
? Returns a tuple of:
? - out: Output data, of shape (N, F, H', W') where H' and W' are given by
? ? H' = 1 + (H + 2 * pad - HH) / stride
? ? W' = 1 + (W + 2 * pad - WW) / stride
? - cache: (x, w, b, conv_param)
? """out =Nonepad_num = conv_param['pad']? stride = conv_param['stride']? N,C,H,W = x.shape? F,C,HH,WW = w.shape? H_prime = (H+2*pad_num-HH) // stride +1W_prime = (W+2*pad_num-WW) // stride +1out = np.zeros([N,F,H_prime,W_prime])#im2colforim_numinrange(N):? ? ? im = x[im_num,:,:,:]? ? ? im_pad = np.pad(im,((0,0),(pad_num,pad_num),(pad_num,pad_num)),'constant')? ? ? im_col = im2col(im_pad,HH,WW,stride)? ? ? filter_col = np.reshape(w,(F,-1))? ? ? mul = im_col.dot(filter_col.T) + b? ? ? out[im_num,:,:,:] = col2im(mul,H_prime,W_prime,1)? cache = (x, w, b, conv_param)returnout, cache
im2col函數(shù):
defim2col(x,hh,ww,stride):"""
? ? Args:
? ? ? x: image matrix to be translated into columns, (C,H,W)
? ? ? hh: filter height
? ? ? ww: filter width
? ? ? stride: stride
? ? Returns:
? ? ? col: (new_h*new_w,hh*ww*C) matrix, each column is a cube that will convolve with a filter
? ? ? ? ? ? new_h = (H-hh) // stride + 1, new_w = (W-ww) // stride + 1
? ? """c,h,w = x.shape? ? new_h = (h-hh) // stride +1new_w = (w-ww) // stride +1col = np.zeros([new_h*new_w,c*hh*ww])foriinrange(new_h):forjinrange(new_w):? ? ? ? ? patch = x[...,i*stride:i*stride+hh,j*stride:j*stride+ww]? ? ? ? ? col[i*new_w+j,:] = np.reshape(patch,-1)returncol
5.3反向傳播圖
使用im2col,計(jì)算圖類似于具有相同格式的FC層

,不同之處在于有一堆重塑,轉(zhuǎn)置和im2col塊。
關(guān)于在反向傳播期間的重塑和轉(zhuǎn)置,只需要再次使用另一個(gè)重塑或轉(zhuǎn)置來(lái)反轉(zhuǎn)它們的操作,需要注意的是,如果在向前傳播期間使用行優(yōu)先進(jìn)行重塑,反向傳播中也要使用行優(yōu)先。
im2col反向傳播操作時(shí)。無(wú)法實(shí)現(xiàn)簡(jiǎn)單的重塑。這是因?yàn)楦惺芤皩?shí)際上是重合的(取決于步長(zhǎng)),所以需要將感受野相交的地方的梯度相加。
反向傳播代碼:
defconv_backward_naive(dout, cache):"""
? A naive implementation of the backward pass for a convolutional layer.
? Inputs:
? - dout: Upstream derivatives.
? - cache: A tuple of (x, w, b, conv_param) as in conv_forward_naive
? Returns a tuple of:
? - dx: Gradient with respect to x
? - dw: Gradient with respect to w
? - db: Gradient with respect to b
? """dx, dw, db =None,None,Nonex, w, b, conv_param = cache? pad_num = conv_param['pad']? stride = conv_param['stride']? N,C,H,W = x.shape? F,C,HH,WW = w.shape? H_prime = (H+2*pad_num-HH) // stride +1W_prime = (W+2*pad_num-WW) // stride +1dw = np.zeros(w.shape)? dx = np.zeros(x.shape)? db = np.zeros(b.shape)# We could calculate the bias by just summing over the right dimensions# Bias gradient (Sum on dout dimensions (batch, rows, cols)#db = np.sum(dout, axis=(0, 2, 3))foriinrange(N):? ? ? im = x[i,:,:,:]? ? ? im_pad = np.pad(im,((0,0),(pad_num,pad_num),(pad_num,pad_num)),'constant')? ? ? im_col = im2col(im_pad,HH,WW,stride)? ? ? filter_col = np.reshape(w,(F,-1)).T? ? ? dout_i = dout[i,:,:,:]? ? ? dbias_sum = np.reshape(dout_i,(F,-1))? ? ? dbias_sum = dbias_sum.T#bias_sum = mul + bdb += np.sum(dbias_sum,axis=0)? ? ? dmul = dbias_sum#mul = im_col * filter_coldfilter_col = (im_col.T).dot(dmul)? ? ? dim_col = dmul.dot(filter_col.T)? ? ? dx_padded = col2im_back(dim_col,H_prime,W_prime,stride,HH,WW,C)? ? ? dx[i,:,:,:] = dx_padded[:,pad_num:H+pad_num,pad_num:W+pad_num]? ? ? dw += np.reshape(dfilter_col.T,(F,C,HH,WW))returndx, dw, db
col2im函數(shù):
defcol2im(mul,h_prime,w_prime,C):"""
? ? ? Args:
? ? ? mul: (h_prime*w_prime*w,F) matrix, each col should be reshaped to C*h_prime*w_prime when C>0, or h_prime*w_prime when C = 0
? ? ? h_prime: reshaped filter height
? ? ? w_prime: reshaped filter width
? ? ? C: reshaped filter channel, if 0, reshape the filter to 2D, Otherwise reshape it to 3D
? ? Returns:
? ? ? if C == 0: (F,h_prime,w_prime) matrix
? ? ? Otherwise: (F,C,h_prime,w_prime) matrix
? ? """F = mul.shape[1]if(C ==1):? ? ? ? out = np.zeros([F,h_prime,w_prime])foriinrange(F):? ? ? ? ? ? col = mul[:,i]? ? ? ? ? ? out[i,:,:] = np.reshape(col,(h_prime,w_prime))else:? ? ? ? out = np.zeros([F,C,h_prime,w_prime])foriinrange(F):? ? ? ? ? ? col = mul[:,i]? ? ? ? ? ? out[i,:,:] = np.reshape(col,(C,h_prime,w_prime))returnout
col2im_back函數(shù):
defcol2im_back(dim_col,h_prime,w_prime,stride,hh,ww,c):"""
? ? Args:
? ? ? dim_col: gradients for im_col,(h_prime*w_prime,hh*ww*c)
? ? ? h_prime,w_prime: height and width for the feature map
? ? ? strid: stride
? ? ? hh,ww,c: size of the filters
? ? Returns:
? ? ? dx: Gradients for x, (C,H,W)
? ? """H = (h_prime -1) * stride + hh? ? W = (w_prime -1) * stride + ww? ? dx = np.zeros([c,H,W])foriinrange(h_prime*w_prime):? ? ? ? row = dim_col[i,:]? ? ? ? h_start = (i / w_prime) * stride? ? ? ? w_start = (i % w_prime) * stride? ? ? ? dx[:,h_start:h_start+hh,w_start:w_start+ww] += np.reshape(row,(c,hh,ww))returndx
5.4小案例
這里使用X[3x3]與W [2x2]進(jìn)行卷積的簡(jiǎn)單示例,來(lái)幫助大家的理解。

04
池化層
池化層用于減少特征空間的維度,但是不會(huì)改變特征圖的深度,它的左右有如下的幾點(diǎn):
減少了特征空間信息,內(nèi)存的使用更少,計(jì)算速度也將更
防止過擬合
引入了位移不變性,更關(guān)注是否存在某些特征而不是特征具體的位置。比如最常見的max pooling,因?yàn)槿∫黄瑓^(qū)域的最大值,所以這個(gè)最大值在該區(qū)域內(nèi)無(wú)論在哪,max-pooling之后都是它,相當(dāng)于對(duì)微小位移的不變性。
使用的最多的是最大池化,如下圖所示,最大池化像卷積核一樣滑動(dòng)窗,并在窗口上獲得最大值作為輸出。
參數(shù)有:
輸入:H1 x W1 x Depth_In x N.
步長(zhǎng):控制窗口滑動(dòng)的像素?cái)?shù)量的標(biāo)量。
K:內(nèi)核大小
輸出:H2 x W2 x Depth_Out x N:
由于池化層上沒有可學(xué)習(xí)的參數(shù),所以它的反向傳播更簡(jiǎn)單。
最大池在其計(jì)算圖上使用一系列最大節(jié)點(diǎn)。因此,最大池化層的反向傳播包含在前向傳播期間選擇的所有元素和dout的掩碼之間的乘積。
換句話說(shuō),最大池層的輸入的梯度是由前向傳播選擇的元素的梯度和0組成的張量。
1、python實(shí)現(xiàn)池化層的前向傳播
池化層上的窗口移動(dòng)機(jī)制與卷積核相同,不同之處在于池化層的窗口是選擇最大值。
2、python實(shí)現(xiàn)池化層的反向傳播
參考文獻(xiàn):
https://blog.csdn.net/byplane/article/details/52422997
https://mp.weixin.qq.com/s/oFWqM9HPhstk7H-GQY0O3g
作者:機(jī)器學(xué)習(xí)算法工程師
鏈接:http://www.itdecent.cn/p/efa807c256ae
來(lái)源:簡(jiǎn)書
簡(jiǎn)書著作權(quán)歸作者所有,任何形式的轉(zhuǎn)載都請(qǐng)聯(lián)系作者獲得授權(quán)并注明出處。