DNN模块的前向传播与反向传播

简 介

一、relu层

1、前向传播

2、反向传播

二、dropout层

1、dropout工作原理

2、在哪里使用dropout

3、dropout的实现

4、dropout的功效

5、python实现dropout的前向传播

6、python实现dropout的反向传播

三、卷积层

1 Im2col

2前向传播

3反向

4小案例

四、池化层

1、python实现池化层的前向传播

2、python实现池化层的反向传播

relu层

如何在Python中实现ReLU层?

简而言之,relu层就是输入张量通过一个非线性的relu函数,得到输出,而不改变其空间或者深度信息

image

从上图可以看出,所有大于0的保持不变,而小于零的变为零。此外,空间信息和深度也是相同的

relu函数作为激活函数,具有以下功能:

易于计算(前向/反向传播),采用sigmoid函数作为激活函数时候(指数运算),计算量大,反向传播求误差梯度时,求导涉及除法,计算量相当大,而采用Relu激活函数,整个过程的计算量节省很多。

深度模型中受消失梯度的影响要小得多,对于深层网络,sigmoid函数反向传播时,很容易就出现梯度消失的情况(在sigmoid函数接近饱和区时,变换太缓慢,导数趋于0,这种情况会造成信息丢失),从而无法完成深层网络的训练。

如果你使用大的学习率,他们可能会不可逆转地死去,因为当一个非常大的梯度流过一个 ReLU 神经元,更新过参数之后,这个神经元再也不会对任何数据有激活现象了。这个神经元的梯度将一直都是0了。

1、前向传播

将所有小于0的数变成0,大于0的数保持不变,空间和深度信息保持不变。

python实现relu的前向传播:

2、反向传播

在前向传播的时候,我们对每个输入X=[x1,x2,x3]应用了max(0,x)函数,所以在反向传播的时候,小于0的元素,梯度dx等于0:

python实现relu 反向传播:

02

Dropout层

Dropout是一种用于防止神经网络过度拟合的技术,你还可以使用L2正则化防止过拟合。

image

下面是分类的错误率,可以发现使用了dropout之后错误率更低:

image

和其他正则化技术一样,使用dropout会使得训练损失稍稍恶化,但是模型的泛化能力却更好,因为如果我们的模型过于复杂(更多层或者更多神经元),模型就很可能过拟合,下面是训练和验证集上的损失情况,以及他们中有无dropout情况。

image

1、dropout工作原理

在训练期间,随机的选择一定比例的神经元,让它停止工作,如下图所示,这样泛化能力更好,因为你的网络层的不同的神经元会学习相同的“概念”。在测试阶段,不需要使用dropout.

2、在哪里使用dropout

通常会在全连接层使用dropout,但也可以在最大池化后使用dropout,从而产生某种图像噪声增强。

3、dropout的实现

为了实现某个神经元的失活,我们在前向传播过程中创建一个掩码(0和1),此掩码应用于训练期间的层的输出,并缓存以供以后在反向传播中使用。如前所述,这个dropout掩码只在训练中使用。

在反向传播中,我们对被激活的神经元感兴趣(我们需要将掩码保存为前向传播),这些被选中的神经元中,使用反向传播,失活的神经元没有可学习的参数,仅仅是输入x,反向传播返回dx。

4、dropout的功效

Dropout背后理念和集成模型很相似。在Drpout层,不同的神经元组合被关闭,这代表了一种不同的结构,所有这些不同的结构使用一个的子数据集并行地带权重训练,而权重总和为1。

如果Dropout层有 n 个神经元,那么会形成2^n个不同的子结构。在预测时,相当于集成这些模型并取均值。这种结构化的模型正则化技术有利于避免过拟合。

Dropout有效的另外一个视点是:由于神经元是随机选择的,所以可以减少神经元之间的相互依赖,从而确保提取出相互独立的重要特征。

5、python实现dropout的前向传播

6、python实现dropout的反向传播

image

03

卷积层

简单的说,卷积层所做的工作就是对输入的特征图应用卷积算子,卷积核的个数是输出特征图的深度。下面我们介绍一下相关的参数:

N:批处理大小(4d张量上的图像数)

F:卷积层上的滤波器个数

kW/kH:内核宽度/高度(通常我们使用方形卷积核,kW=kH)

H/W:图像高度/宽度(通常H=W)

H'/W':卷积图像高度/宽度(如果使用适当的填充,则与输入相同)

Stride:卷积滑动窗口将要移动的像素数。

Padding:将0添加到图像的边框,以保持输入和输出大小相同。

Depth:输入特征图的深度(如输入为RGB图像则深度为3)

Output depth:输出的特征图的深度(与F相同)

1、前向传播

在前向传播过程中,我们用不同的过滤器“卷积”输入,每个过滤器将在图像上寻找不同的特征。

在这里观察到所有来自第一层的神经元共享相同的权重集,不同的过滤器得到不同的特征。

2、python实现卷积层的前向传播

3、反向传播

为了更好的理解,这里使用1维卷积来理解卷积层的反向传播,2维的也类似。

输入信号为X=[x0,x1,x2,x3,x4],参数为W=[w0,w1,w2],不使用padding,卷积之后的结果是:Y=[y0,y1,y2],这里Y = X * flip(W),flip可以看作是180度的旋转。

现在我们使用计算图来表示,并且加上一个偏差,通过观察可以发现这个过程跟全连接层类似,不同之处在于卷积核可以使得权重共享。

现在来看反向传播

向后追踪计算图,反向传播可以表示为以下的公式

意味着损失值随着输入进行变化,由上图可以看出。

image

注意:

dX跟X大小相同,所以我们需要进行填充

dout跟Y大小相同,在本例中为3(渐变输入)

为了节省编程工作量,我们将梯度的计算采用卷积的形式

在dX梯度上,所有元素都乘以W,所以我们可能会对W和dout进行卷积操作

1d卷积的输出尺寸计算公式:outputSize=(InputSize-KernelSize+2P)+1,

我们期望的尺寸是3,由于原始输入尺寸是3,并且我们将与也有3个元素的W矩阵进行卷积。所以我们需要用2个零填充输入,之后再进行卷积,就可以得到尺寸为3的输出。

就卷积而言:

根据链式法则,求损失函数对各个参数的偏导:

再次查看从图表中得到的表达式,可以将它们表示为dout和X之间的卷积。同样,由于输出将是3个元素,因此不需要进行填充。

就卷积的计算而言,

如果将X看成是卷积核,而dout看做输入信号,则:

对于偏差,计算将类似于全连接层。 基本上我们每个过滤器有一个偏差,计算如下:

4、python实现卷积的反向传播

5、卷积运算转换为矩阵运算

使用矩阵运算,能够使得运算速度更快,但也会消耗更多的内存。

5.1 Im2col

前面的代码,使用的是for循环来实现卷积,运算速度不够快,在本节中,我们将学习如何使用矩阵运算来实现卷积,首先,卷积是内核过滤器和它移动之后在图像上选择的区域之间的点积,如果我们在内存上扩展所有可能的窗口并将点积作为矩阵运算,运算速度将更快,但内存的消耗也会更大。

例如,输入图片为2272273,卷积核为11113,步长为4,padding为0,进行卷积运算的时候,我们可以将卷积核在输入图片上采样的11113大小的像素块(感受野)拉伸为大小为11113=363的列向量,2272273大小的图片,又有步长为4,padding为0,卷积之后的宽高计算方式为(227-11)/4)+1=55,所以采样之后得到5555个11113大小的像素块(感受野),最终可以得到尺寸为3633025的输出矩阵X_col,(3025由55*55得到,表示有3025个感受野)

总结一下,如何计算im2col输出的大小:

[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)));

卷积核也进行类似的伸展,假设有96个大小为11113的卷积核,通过im2col函数之后,得到96*363的矩阵W_col.

将图像和卷积核转换之后,卷积操作就变成了简单的矩阵乘法运算,这个例子中,W_col(96363)c乘以X_col(3633025)得到的矩阵是963025,最后可以重塑为5555*96,重塑可以定义一个col2im的函数来实现。

5.2前向传播计算图

下图是前向传播中使用im2col之后的计算图,输入为443,步长为1,padding为0,卷积核大小为2*2,卷积核个数为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函数:

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,计算图类似于具有相同格式的FC层

,不同之处在于有一堆重塑,转置和im2col块。

关于在反向传播期间的重塑和转置,只需要再次使用另一个重塑或转置来反转它们的操作,需要注意的是,如果在向前传播期间使用行优先进行重塑,反向传播中也要使用行优先。

im2col反向传播操作时。无法实现简单的重塑。这是因为感受野实际上是重合的(取决于步长),所以需要将感受野相交的地方的梯度相加。

反向传播代码:

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函数:

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函数:

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]进行卷积的简单示例,来帮助大家的理解。

04

池化层

池化层用于减少特征空间的维度,但是不会改变特征图的深度,它的左右有如下的几点:

减少了特征空间信息,内存的使用更少,计算速度也将更

防止过拟合

引入了位移不变性,更关注是否存在某些特征而不是特征具体的位置。比如最常见的max pooling,因为取一片区域的最大值,所以这个最大值在该区域内无论在哪,max-pooling之后都是它,相当于对微小位移的不变性。

使用的最多的是最大池化,如下图所示,最大池化像卷积核一样滑动窗,并在窗口上获得最大值作为输出。

参数有:

输入:H1 x W1 x Depth_In x N.

步长:控制窗口滑动的像素数量的标量。

K:内核大小

输出:H2 x W2 x Depth_Out x N:

由于池化层上没有可学习的参数,所以它的反向传播更简单。

最大池在其计算图上使用一系列最大节点。因此,最大池化层的反向传播包含在前向传播期间选择的所有元素和dout的掩码之间的乘积。

换句话说,最大池层的输入的梯度是由前向传播选择的元素的梯度和0组成的张量。

1、python实现池化层的前向传播

池化层上的窗口移动机制与卷积核相同,不同之处在于池化层的窗口是选择最大值。

2、python实现池化层的反向传播

参考文献:

https://blog.csdn.net/byplane/article/details/52422997

https://mp.weixin.qq.com/s/oFWqM9HPhstk7H-GQY0O3g

作者:机器学习算法工程师

链接:https://www.jianshu.com/p/efa807c256ae

來源:简书

简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容