DNN模塊的前向傳播與反向傳播

簡(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)并注明出處。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容