全连接层存在的问题

之前介绍的全连接的神经网络中使用了全连接层(Affine层)。在全连接层中,相邻层的神经元全部连接在一起,输出的数量可以任意决定。

全连接层存在什么问题呢?那就是数据的形状被“忽视”了。比如,输入数据是图像时,图像通常是高、长、通道方向上的3维形状。但是,向全连接层输入时,需要将3维数据拉平为1维数据。实际上,前面提到的使用了MNIST数据集的例子中,输入图像就是1通道、高28像素、长28像素的(1, 28, 28)形状,但却被排成1列,以784个数据的形式输入到最开始的Affine层。

图像是3维形状,这个形状中应该含有重要的空间信息。比如,空间上邻近的像素为相似的值、RBG的各个通道之间分别有密切的关联性、相距较远的像素之间没有什么关联等,3维形状中可能隐藏有值得提取的本质模式。但是,因为全连接层会忽视形状,将全部的输入数据作为相同的神经元(同一维度的神经元)处理,所以无法利用与形状相关的信息。而卷积层可以保持形状不变。当输入数据是图像时,卷积层会以3维数据的形式接收输入数据,并同样以3维数据的形式输出至下一层。因此,在CNN中,可以(有可能)正确理解图像等具有形状的数据。

另外,CNN 中,有时将卷积层的输入输出数据称为特征图(feature map)。其中,卷积层的输入数据称为输入特征图(input feature map),输出数据称为输出特征图(output feature map)。本书中将“输入输出数据”和“特征图”作为含义相同的词使用。

卷积运算:

卷积层进行的处理就是卷积运算。卷积运算相当于图像处理中的“滤波器运算”。滤波器有的文献也叫卷积核。在传统图像处理一般叫滤波器,而在深度学习中叫卷积核。

下图中展示了卷积运算的计算顺序。对于输入数据,卷积运算以一定间隔滑动滤波器的窗口并应用。这里所说的窗口是指图中灰色的3 × 3的部分。如图所示,将各个位置上滤波器的元素和输入的对应元素相乘,然后再求和(有时将这个计算称为乘积累加运算)。然后,将这个结果保存到输出的对应位置。将这个过程在所有位置都进行一遍,就可以得到卷积运算的输出。

在全连接的神经网络中,除了权重参数,还存在偏置。CNN中,滤波器的参数就对应之前的权重。并且,CNN中也存在偏置。包含偏置的卷积运算的处理流如下图所示,向应用了滤波器的数据加上了偏置。偏置通常只有1个,这个值会被加到应用了滤波器的所有元素上:

填充

在进行卷积层的处理之前,有时要向输入数据的周围填入固定的数据(比如0等),这称为填充(padding),是卷积运算中经常会用到的处理。

如图所示,通过填充,大小为(4, 4)的输入数据变成了(6, 6)的形状。然后,应用大小为(3, 3)的滤波器,生成了大小为(4, 4)的输出数据。这个例子中将填充设成了1,不过填充的值也可以设置成2、3等任意的整数。在上图的例子中,如果将填充设为2,则输入数据的大小变为(8, 8);如果将填充设为3,则大小变为(10, 10)。

使用填充主要是为了调整输出的大小。比如,对大小为(4, 4)的输入数据应用(3, 3)的滤波器时,输出大小变为(2, 2),相当于输出大小比输入大小缩小了 2个元素。这在反复进行多次卷积运算的深度网络中会成为问题。为什么呢?因为如果每次进行卷积运算都会缩小空间,那么在某个时刻输出大小就有可能变为 1,导致无法再应用卷积运算。为了避免出现这样的情况,就要使用填充。在刚才的例子中,将填充的幅度设为 1,那么相对于输入大小(4, 4),输出大小也保持为原来的(4, 4)。因此,卷积运算就可以在保持空间大小不变的情况下将数据传给下一层。

步幅

应用滤波器的位置间隔称为步幅(stride)。之前的例子中步幅都是1,如果将步幅设为2,则如下图所示,应用滤波器的窗口的间隔变为2个元素:

在上图的例子中,对输入大小为(7, 7)的数据,以步幅2应用了滤波器。通过将步幅设为2,输出大小变为(3, 3)。像这样,步幅可以指定应用滤波器的间隔。

综上,增大步幅后,输出大小会变小。而增大填充后,输出大小会变大。这里,假设输入大小为(H, W),滤波器大小为(FH, FW),输出大小为(OH, OW),填充为P,步幅为S。此时,输出大小可通过下式进行计算:

3维数据的卷积运算

之前的卷积运算的例子都是以有高、长方向的2维形状为对象的。但是,图像是3维数据,除了高、长方向之外,还需要处理通道方向。这里,我们按照与之前相同的顺序,看一下对加上了通道方向的3维数据进行卷积运算的例子。这里以3通道的数据为例,展示了卷积运算的结果。和2维数据时相比,可以发现纵深方向(通道方向)上特征图增加了。通道方向上有多个特征图时,会按通道进行输入数据和滤波器的卷积运算,并将结果相加,从而得到输出:

需要注意的是,在3维数据的卷积运算中,输入数据和滤波器的通道数要设为相同的值。在这个例子中,输入数据和滤波器的通道数一致,均为3。滤波器大小可以设定为任意值(不过,每个通道的滤波器大小要全部相同)。这个例子中滤波器大小为(3, 3),但也可以设定为(2, 2)、(1, 1)、(5, 5)等任意值。再强调一下,通道数只能设定为和输入数据的通道数相同的值。

结合方块思考

将数据和滤波器结合长方体的方块来考虑,3维数据的卷积运算会很容易理解。把3维数据表示为多维数组时,书写顺序为(channel, height, width)。比如,通道数为C、高度为H、长度为W的数据的形状可以写成(C, H, W)。滤波器也一样,要按(channel, height, width)的顺序书写。比如,通道数为C、滤波器高度为FH(Filter Height)、长度为FW(Filter Width)时,可以写成(C, FH, FW)。

在这个例子中,数据输出是1张特征图。所谓1张特征图,换句话说,就是通道数为1的特征图。那么,如果要在通道方向上也拥有多个卷积运算的输出,该怎么做呢?为此,就需要用到多个滤波器(权重)。如下图所示:

上图中,通过应用FN个滤波器,输出特征图也生成了FN个。如果将这FN个特征图汇集在一起,就得到了形状为(FN, OH, OW)的方块。将这个方块传给下一层,就是CNN的处理流。关于卷积运算的滤波器,也必须考虑滤波器的数量。因此,作为4维数据,滤波器的权重数据要按(output_channel, input_channel, height, width)的顺序书写。比如,通道数为3、大小为5 × 5的滤波器有20个时,可以写成(20, 3, 5, 5)。卷积运算中(和全连接层一样)存在偏置。如果进一步追加偏置的加法运算处理,则结果如下图所示,每个通道只有一个偏置。这里,偏置的形状是(FN, 1, 1),滤波器的输出结果的形状是(FN, OH, OW)。这两个方块相加时,要对滤波器的输出结果(FN, OH, OW)按通道加上相同的偏置值。另外,不同形状的方块相加时,可以基于NumPy的广播功能轻松实现:

批处理

神经网络的处理中进行了将输入数据打包的批处理。之前的全连接神经网络的实现也对应了批处理,通过批处理,能够实现处理的高效化和学习时对mini-batch的对应。我们希望卷积运算也同样对应批处理。为此,需要将在各层间传递的数据保存为4维数据。具体地讲,就是按(batch_num, channel, height, width)的顺序保存数据。如下图所示:

批处理版的数据流中,在各个数据的开头添加了批用的维度,数据作为4维的形状在各层间传递。这里需要注意的是,网络间传递的是4维数据,对这N个数据进行了卷积运算。也就是说,批处理将N次的处理汇总成了1次进行。

基于 im2col的展开

如前所述,CNN中各层间传递的数据是4维数据。所谓4维数据,比如数据的形状是(10, 1, 28, 28),则它对应10个高为28、长为28、通道为1的数据。如果老老实实地实现卷积运算,估计要重复好几层的for语句。这样的实现有点麻烦,而且,NumPy中存在使用for语句后处理变慢的缺点(NumPy中,访问元素时最好不要用for语句)。这里,我们不使用for语句,而是使用im2col这个便利的函数进行简单的实现。

im2col是一个函数,将输入数据展开以适合滤波器(权重)。如下图所示,对3维的输入数据应用im2col后,数据转换为2维矩阵(正确地讲,是把包含批数量的4维数据转换成了2维数据)。

im2col会把输入数据展开以适合滤波器(权重)。具体地说,如下图所示,对于输入数据,将应用滤波器的区域(3维方块)横向展开为1列。im2col会在所有应用滤波器的地方进行这个展开处理:

上图中,为了便于观察,将步幅设置得很大,以使滤波器的应用区域不重叠。而在实际的卷积运算中,滤波器的应用区域几乎都是重叠的。在滤波器的应用区域重叠的情况下,使用im2col展开后,展开后的元素个数会多于原方块的元素个数。因此,使用im2col的实现存在比普通的实现消耗更多内存的缺点。但是,汇总成一个大的矩阵进行计算,对计算机的计算颇有益处。比如,在矩阵计算的库(线性代数库)等中,矩阵计算的实现已被高度最优化,可以高速地进行大矩阵的乘法运算。因此,通过归结到矩阵计算上,可以有效地利用线性代数库。

使用im2col展开输入数据后,之后就只需将卷积层的滤波器(权重)纵向展开为1列,并计算2个矩阵的乘积即可。这和全连接层的Affine层进行的处理基本相同。如下图所示,基于im2col方式的输出结果是2维矩阵。因为CNN中数据会保存为4维数组,所以要将2维输出数据转换为合适的形状。这就是卷积层的实现流程:

卷积层的实现

im2col (input_data, filter_h, filter_w, stride=1, pad=0),介绍一下im2col函数的参数。input_data―由(数据量,通道,高,长)的4维数组构成的输入数据,filter_h为滤波器的高,filter_w为滤波器的长stride为步幅,pad为填充。现在使用im2col来实现卷积层。这里我们将卷积层实现为名为Convolution的类:

class Convolution:def __init__(self, W, b, stride=1, pad=0):self.W = Wself.b = bself.stride = strideself.pad = pad# 中间数据(backward时使用)self.x = Noneself.col = Noneself.col_W = None# 权重和偏置参数的梯度self.dW = Noneself.db = Nonedef forward(self, x):FN, C, FH, FW = self.W.shapeN, C, H, W = x.shapeout_h = 1 + int((H + 2 * self.pad - FH) / self.stride)out_w = 1 + int((W + 2 * self.pad - FW) / self.stride)col = im2col(x, FH, FW, self.stride, self.pad)col_W = self.W.reshape(FN, -1).Tout = np.dot(col, col_W) + self.bout = out.reshape(N, out_h, out_w, -1).transpose(0, 3, 1, 2)self.x = xself.col = colself.col_W = col_Wreturn outdef backward(self, dout):FN, C, FH, FW = self.W.shapedout = dout.transpose(0, 2, 3, 1).reshape(-1, FN)self.db = np.sum(dout, axis=0)self.dW = np.dot(self.col.T, dout)self.dW = self.dW.transpose(1, 0).reshape(FN, C, FH, FW)dcol = np.dot(dout, self.col_W.T)dx = col2im(dcol, self.x.shape, FH, FW, self.stride, self.pad)return dx

卷积层的初始化方法将滤波器(权重)、偏置、步幅、填充作为参数接收。滤波器是 (FN, C, FH, FW)的 4 维形状。另外,FN、C、FH、FW分别是 Filter Number(滤波器数量)、Channel、Filter Height、Filter Width的缩写。

用im2col展开输入数据,并用reshape将滤波器展开为2维数组。然后,计算展开后的矩阵的乘积。将各个滤波器的方块纵向展开为1列。这里通过reshape(FN,-1)将参数指定为-1,这是reshape的一个便利的功能。通过在reshape时指定为-1,reshape函数会自动计算-1维度上的元素个数,以使多维数组的元素个数前后一致。比如,(10, 3, 5, 5)形状的数组的元素个数共有750个,指定reshape(10,-1)后,就会转换成(10, 75)形状的数组。forward的实现中,最后会将输出大小转换为合适的形状。转换时使用了NumPy的transpose函数。transpose会更改多维数组的轴的顺序。如下图所示,通过指定从0开始的索引(编号)序列,就可以更改轴的顺序。

以上就是卷积层的forward处理的实现。通过使用im2col进行展开,基本上可以像实现全连接层的Affine层一样来实现。接下来是卷积层的反向传播的实现,在进行卷积层的反向传播时,必须进行im2col的逆处理。这可以使用col2im函数来进行。除了使用col2im这一点,卷积层的反向传播和Affine层的实现方式都一样。最后附上im2col函数和col2im函数。

def im2col(input_data, filter_h, filter_w, stride=1, pad=0):"""Parameters----------input_data : 由(数据量, 通道, 高, 长)的4维数组构成的输入数据filter_h : 滤波器的高filter_w : 滤波器的长stride : 步幅pad : 填充Returns-------col : 2维数组"""N, C, H, W = input_data.shapeout_h = (H + 2*pad - filter_h)//stride + 1out_w = (W + 2*pad - filter_w)//stride + 1img = np.pad(input_data, [(0,0), (0,0), (pad, pad), (pad, pad)], 'constant')col = np.zeros((N, C, filter_h, filter_w, out_h, out_w))for y in range(filter_h):y_max = y + stride*out_hfor x in range(filter_w):x_max = x + stride*out_wcol[:, :, y, x, :, :] = img[:, :, y:y_max:stride, x:x_max:stride]col = col.transpose(0, 4, 5, 1, 2, 3).reshape(N*out_h*out_w, -1)return coldef col2im(col, input_shape, filter_h, filter_w, stride=1, pad=0):"""Parameters----------col :input_shape : 输入数据的形状(例:(10, 1, 28, 28))filter_h :filter_wstridepadReturns-------"""N, C, H, W = input_shapeout_h = (H + 2*pad - filter_h)//stride + 1out_w = (W + 2*pad - filter_w)//stride + 1col = col.reshape(N, out_h, out_w, C, filter_h, filter_w).transpose(0, 3, 4, 5, 1, 2)img = np.zeros((N, C, H + 2*pad + stride - 1, W + 2*pad + stride - 1))for y in range(filter_h):y_max = y + stride*out_hfor x in range(filter_w):x_max = x + stride*out_wimg[:, :, y:y_max:stride, x:x_max:stride] += col[:, :, y, x, :, :]return img[:, :, pad:H + pad, pad:W + pad]

卷积层运算详解与im2col实现相关推荐

  1. python中如何反解函数_PyTorch中反卷积的用法详解

    pytorch中的 2D 卷积层 和 2D 反卷积层 函数分别如下: class torch.nn.Conv2d(in_channels, out_channels, kernel_size, str ...

  2. FCN(全卷积神经网络)详解

    文章目录 1. 综述 简介 核心思想 2. FCN网络 2.1 网络结构 2.2 上采样 Upsampling 2.3 跳级结构 3 FCN训练 4. 其它 4.1 FCN与CNN 4.2 FCN的不 ...

  3. (转)C语言位运算详解

    地址:http://www.cnblogs.com/911/archive/2008/05/20/1203477.html C语言位运算详解 作者:911 说明:本文参考了http://www2.ts ...

  4. 位运算详解+竞赛常见用法总结

    目录 一.位运算详解 二.位运算应用 1.快速幂 2.给定一个数组A, 长度为n,求下面这段程序的值 3.数数字 4.数数字 2 5.nim博弈问题: 6.树状数组 7.判断一个数x是不是2的某次方 ...

  5. python开方运算符_Pytorch Tensor基本数学运算详解

    1. 加法运算 示例代码: import torch # 这两个Tensor加减乘除会对b自动进行Broadcasting a = torch.rand(3, 4) b = torch.rand(4) ...

  6. 大数取余运算(详解)

    大数取余运算(详解) //(19^3 mod 23)^2 mod 31=25 //a=19 b=3 c=23 d=2 e=31 #include<stdio.h> long long po ...

  7. pytorch---之BN层参数详解及应用(1,2,3)(1,2)?

    BN层参数详解(1,2) 一般来说pytorch中的模型都是继承nn.Module类的,都有一个属性trainning指定是否是训练状态,训练状态与否将会影响到某些层的参数是否是固定的,比如BN层(对 ...

  8. 取模(余)%运算详解

    取模(余)%运算详解 1.JAVA中 对于整型数a,b来说,取模运算是:               1.求 整数商: c = a/b;               2.计算模: a%b = a - ...

  9. OSI七层模型详解物理层、数据链路层、网络层、传输层.....应用层协议

    OSI七层模型详解(物理层.数据链路层.网络层.传输层.....应用层协议与硬件) OSI 七层模型通过七个层次化的结构模型使不同的系统不同的网络之间实现可靠的通讯,因此其最主要的功能就是帮助不同类型 ...

最新文章

  1. 总结和展望:情感分析研究的新视野
  2. IDEA好用的Servlet模板
  3. IntelliJ IDEA 源值1.5已过时,将在未来所有版本中删除
  4. springboot 扫描文件夹_Spring Boot学习07_自定义starter
  5. HDU 5009 Paint Pearls
  6. paip.svn服务服务器安装配置
  7. 相信我,我们离成为废物阶级不远了!
  8. c语言---16 关于goto语句
  9. 千元平板乐Pad A1完整拆解视频
  10. ipa文件反编译_iOS class-dump 反编译ipa包
  11. TCP/UDP常见端口
  12. js中数组的高逼格操作(filter、sort、map、reduce)
  13. Linux 限制IP访问与白名单
  14. Java程序员:真是会服了面试官,不就要个40k嘛硬是把Spring问烂
  15. 4G LTE 频率表
  16. 解决composer 安装某些扩展出现composer:Could not find package
  17. 童鞋们有福了,U880 GPS使用指南终于找到了,不看后悔
  18. matlab arctan unwrap,atan和unwrap解卷绕
  19. 数字图像处理:关于BMP格式图像的理解和读写(c++)
  20. 【证明】线性变换在两个基下的矩阵相似

热门文章

  1. IntelliJ IDEA之内存不够提示os::commit_memory failed
  2. 推箱子游戏java毕业答辩ppt_基于Java推箱子游戏的设计与实现
  3. 张俊林:对比学习研究进展精要
  4. 众筹大家的年度阅读与分享计划啦
  5. Android Studio之GsonFormat用法
  6. JAVA几种缓存技术介绍说明
  7. MySQL查询优化和参数优化
  8. WORKLIST服务
  9. 腾讯高工保姆级“Java成长手册”,层层递进,全是精华
  10. 大数据信息资料采集:刘润公众号历史商业评论文章采集评论搜集