做网站敲代码的图片,做网站需要哪些费用,深圳网站建设哪家比较好,秦皇岛在哪里CNN简介与实现 导语整体结构卷积层卷积填充步幅三维卷积立体化批处理 实现 池化层特点实现 CNN实现可视化总结参考文献 导语 
CNN全称卷积神经网络#xff0c;可谓声名远扬#xff0c;被用于生活中的各个领域#xff0c;也是最好理解的神经网络结构之一。 
整体结构 
相较于… CNN简介与实现 导语整体结构卷积层卷积填充步幅三维卷积立体化批处理 实现 池化层特点实现 CNN实现可视化总结参考文献 导语 
CNN全称卷积神经网络可谓声名远扬被用于生活中的各个领域也是最好理解的神经网络结构之一。 
整体结构 
相较于先前的神经网络CNN出现了卷积层和池化层的概念基本的组成模块是“卷积-ReLU-池化”并且在靠近输出或最后输出时时仍会采用“Affine-ReLU”、Affine-ReLU的组合书上给出的示例图如下 卷积层 
在思考为什么要用卷积层之前我们可以先来看看卷积层之前的全连接层有什么局限性全连接层通常要求输入是一个一维的数组即使原始数据是更高维的数据如高、长、通道的三维图像这个时候使用全连接层原始数据中的几何信息、点之间的相对位置等空间信息就都被清除了这些信息其实很重要因为点与点之间在高维空间的关联性是比一维更强的。 
相比之下卷积层就考虑到了这些空间信息当输入为图像时卷积层会以三维数据的形式接受输入数据并且输出也是三维数据。 
CNN中卷积层的输入输出数据被称作特征图输入叫输入特征图输出叫输出特征图。 
卷积 
卷积是卷积层的运算类似与图像中的滤波器处理具体做法如图图源自网络侵删 此图省略了卷积核只给出了输入和结果以该图为例输入是一个4×4的矩阵在矩阵上存在一个3×3的滑动窗口窗口每次移动一个单位每次对窗口内的矩阵A进行一次权重累和具体的权重为同等大小的卷积核矩阵具体的例子如下 36  1 × 1  2 × 1  0 × 3  4 × 0  5 × 2  6 × 0  7 × 1  8 × 2  1 × 1 361×12×10×34×05×26×07×18×21×1 361×12×10×34×05×26×07×18×21×1。 与全连接层一样CNN中也存在偏置对于算出的结果矩阵对矩阵中的所有元素可以加上一个相同的偏置值。 
填充 
在进行卷积前有时候要把数据拓宽例如把4×4拓成6×6如何拓宽呢很简单把不够的部分都设置为同一个值就可以一般是0或者1具体操作如图图源网络侵删 这种做法就叫做填充使用填充主要是为了调整输出大小在使用卷积核运算的时候如果不进行填充卷积的结果势必会在整体上变小如4×4变成2×2多次使用后最后的结果就可能只有一个1因此使用填充来避免这种情况的发生。 
步幅 
步幅很容易理解就是滑动窗口的每次的移动距离像下面这张图就是步幅为2时候的卷积图源网络侵删 可以看到增大步幅会使得输出变小加上填充会变大这个时候就可以根据两者关系列出卷积输出结果的公式了。 
书上的描述如下值除不尽四舍五入 三维卷积 
在现实使用中CNN的输入并不是一个单纯的二维矩阵输入的图像时一个带有高、宽、通道的具体的特征图以RGB为例RGB图像是三通道如果对RGB图像进行卷积那么就要对图像上的每一个通道都使用一个卷积核通道方向有多个特征图时需要按照通道方向进行输入数据和滤波器的卷积运算并将结果累和生成一个新的二维矩阵。 
立体化 
当我们把输入和输出推向更一般的适用情况多通道输入数据使用对应的多通道核最后输出一张单个图书上的例子如下其中C为通道数、H为高度、W为长度。 如果要再通道方向上也拥有多个卷积运算的输出就需要使用多个滤波器权重书上的图如下 如果再考虑上偏置书上给出的图如下 批处理 
通常为了加快效率神经网络会将输入数据进行一批批的打包一次性处理一堆数据为了处理一批数据需要在上一张图的基础上加上批次书上给出的图如下 数据作为4维数据在各层之间传递批处理将N次处理汇总成了1次进行。 
实现 
如果直接实现卷积运算利用for循环效率其实是不高的况且python给出了更好的选择im2col函数。 
im2col将输入数据展开来适合卷积核的计算书上给出的图如下 这里更详细的解释一下输入的是一个三维的数据把每一面二维从左到右从上到下拉成一个一维的数组然后把每个通道的一维数组拼起来形成一个二维的矩阵如果是多批次就把这些矩阵首尾相连形成一个更大的二维矩阵即可。 
实际的卷积运算中卷积核的应用区域几乎彼此重叠因此在使用im2col之后展开的元素个数会多于原来的输入元素个数所以会消耗更多的内存。 
书上给出了用im2col进行卷积的流程  还需要明晰的一点是im2col的使用并不会损失原数据在空间上的信息它只是为了方便进行矩阵对数据进行了一些处理并且在最后恢复了原来的数据模式。 
书上给出了im2col和基于im2col实现的卷积层代码如下 
def im2col(input_data, filter_h, filter_w, stride1, pad0):#输入高长步幅填充N, C, H, W  input_data.shapeout_h  (H  2*pad - filter_h)//stride  1#根据步长和高度计算输出的长高out_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 colClass Convolution:def __init__(self,W,b,stride1,pad0):#初始化赋值self.WWself.bbself.stridestrideself.padpaddef forward(self,x):FN,C,FH,FWself.W.shapeN,C,H,Wx.shapeout_hint(1(H2*self.pad-FH)/self.stride)#获得填充和卷积之后的规模out_wint(1(W2*self.pad-FW)/self.stride)colim2col(x,FH,FW,self.stride,self.pad)#拉伸#卷积层反向传播的时候需要进行im2col的逆处理col_Wself.W.reshape(FN,-1).T#把卷积核展开outnp.dot(col,col_W)self.boutout.reshape(N,out_h,out_w,-1).transpose(0,3,1,2)#更改轴的顺序NHWC变成NCHWreturn 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, axis0)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池化层 
简单来说卷积是使用卷积核计算对应区域的乘积和池化层是选取对应区域的最大值也有其他的池化比如平均值池化指的是取对应区域的平均值作为输出书上给出的例子如下 特点 
池化层的操作很简单不需要像卷积层那样学习卷积核的参数只需要提取最值或平均即可其次池化层的计算是按照通道独立进行的输入和输出的通道数不会变化最后池化层对输入数据的微小偏差具有鲁棒性例如目标区域的非最大值有变化并不会影响池化层最后的输出。 
实现 
池化层也是用im2col展开但展开时在通道方向上是独立的书上给的图示如下 书上的实现代码如下 
class Pooling:def __init__(self, pool_h, pool_w, stride1, pad0):#初始化self.pool_h  pool_hself.pool_w  pool_wself.stride  strideself.pad  padself.x  Noneself.arg_max  Nonedef forward(self, x):#推理函数N, C, H, W  x.shapeout_h  int(1  (H - self.pool_h) / self.stride)#拿到输出大小out_w  int(1  (W - self.pool_w) / self.stride)col  im2col(x, self.pool_h, self.pool_w, self.stride, self.pad)#拉伸col  col.reshape(-1, self.pool_h*self.pool_w)#变成二维矩阵arg_max  np.argmax(col, axis1)out  np.max(col, axis1)#取最值out  out.reshape(N, out_h, out_w, C).transpose(0, 3, 1, 2)#还原成数据self.x  xself.arg_max  arg_maxreturn outdef backward(self, dout):#反向传播dout  dout.transpose(0, 2, 3, 1)pool_size  self.pool_h * self.pool_wdmax  np.zeros((dout.size, pool_size))dmax[np.arange(self.arg_max.size), self.arg_max.flatten()]  dout.flatten()dmax  dmax.reshape(dout.shape  (pool_size,)) dcol  dmax.reshape(dmax.shape[0] * dmax.shape[1] * dmax.shape[2], -1)dx  col2im(dcol, self.x.shape, self.pool_h, self.pool_w, self.stride, self.pad)return dxCNN实现 
将已经实现的各个层进行组合就可以实现一个简单的CNN书上给出了一个简单CNN的具体代码实现具体图如下  
书上加上注释的代码如下 
class SimpleConvNet:def __init__(self, input_dim(1, 28, 28), conv_param{filter_num:30, filter_size:5, pad:0, stride:1},hidden_size100, output_size10, weight_init_std0.01):#输入大小卷积核数量卷积核大小填充步幅隐藏层神经元数量输出大小初始权重标准差filter_num  conv_param[filter_num]filter_size  conv_param[filter_size]filter_pad  conv_param[pad]filter_stride  conv_param[stride]input_size  input_dim[1]conv_output_size  (input_size - filter_size  2*filter_pad) / filter_stride  1pool_output_size  int(filter_num * (conv_output_size/2) * (conv_output_size/2))# 初始化权重self.params  {}self.params[W1]  weight_init_std * \np.random.randn(filter_num, input_dim[0], filter_size, filter_size)self.params[b1]  np.zeros(filter_num)self.params[W2]  weight_init_std * \np.random.randn(pool_output_size, hidden_size)self.params[b2]  np.zeros(hidden_size)self.params[W3]  weight_init_std * \np.random.randn(hidden_size, output_size)self.params[b3]  np.zeros(output_size)# 生成层self.layers  OrderedDict()self.layers[Conv1]  Convolution(self.params[W1], self.params[b1],conv_param[stride], conv_param[pad])self.layers[Relu1]  Relu()self.layers[Pool1]  Pooling(pool_h2, pool_w2, stride2)self.layers[Affine1]  Affine(self.params[W2], self.params[b2])self.layers[Relu2]  Relu()self.layers[Affine2]  Affine(self.params[W3], self.params[b3])self.last_layer  SoftmaxWithLoss()#损失函数def predict(self, x):#预测值for layer in self.layers.values():x  layer.forward(x)return xdef loss(self, x, t):#计算损失y  self.predict(x)return self.last_layer.forward(y, t)def accuracy(self, x, t, batch_size100):#计算准确度if t.ndim ! 1 : t  np.argmax(t, axis1)acc  0.0for i in range(int(x.shape[0] / batch_size)):tx  x[i*batch_size:(i1)*batch_size]tt  t[i*batch_size:(i1)*batch_size]y  self.predict(tx)y  np.argmax(y, axis1)acc  np.sum(y  tt) return acc / x.shape[0]def numerical_gradient(self, x, t):#求梯度用数值微分方法loss_w  lambda w: self.loss(x, t)grads  {}for idx in (1, 2, 3):grads[W  str(idx)]  numerical_gradient(loss_w, self.params[W  str(idx)])grads[b  str(idx)]  numerical_gradient(loss_w, self.params[b  str(idx)])return gradsdef gradient(self, x, t):#误差反向传播求梯度# forwardself.loss(x, t)# backwarddout  1dout  self.last_layer.backward(dout)layers  list(self.layers.values())layers.reverse()for layer in layers:dout  layer.backward(dout)# 设定grads  {}grads[W1], grads[b1]  self.layers[Conv1].dW, self.layers[Conv1].dbgrads[W2], grads[b2]  self.layers[Affine1].dW, self.layers[Affine1].dbgrads[W3], grads[b3]  self.layers[Affine2].dW, self.layers[Affine2].dbreturn grads 
训练所需要的时间相较于先前的方法比较久但是得到的结果识别率更高具体训练结果如下 可视化 
“卷积”是一种数学运算逻辑上其实很难理解到它的用处因此书上给出了对卷积作用更加直接的展现方式以上一部分学习前和学习后的卷积核为例各个卷积核的权重图如下 
学习前 学习后 可以明显的看到学习前杂乱无章的权重矩阵在学习后变得有迹可循明显有些区域的权重更深一些那么这些权重更大的部分对应的目标究竟是什么呢 
书上给出了答案这些卷积核在学习边缘颜色变化的分界线和斑块局部的块状区域例如黑白分界线可以根据手写数字识别的例子想象手写的数字是黑色背景是白色那么卷积核的目标就是使得模型对黑色的部分更加敏感权重更大。 
上述的结果是只进行了一次卷积得到的随着层次的加深提取的信息也会越来越抽象在深度学习中最开始层会对简单的边缘有响应接下来是对纹理在接下来是对更复杂的性质随着层次递增模型的目标会从简单的形状进化到更高级的信息。 
总结 
本章详细介绍了CNN的构造对卷积层、池化层进行了从零开始的实现但是对反向传播的部分只给出了代码实现。最重要的还是对im2col的理解明白了im2col的原理卷积层、池化层乃至反向传播的实现这些问题就迎刃而解了。 
基于最基本的CNN后续还有更多功能强大网络结构更深的CNN网络如LeNet激活函数为sigmod使用子采样缩小中间数据大小而不是卷积、池化还有AlexNet多个卷积层和池化层激活函数为sigmod使用进行局部正规化的LRN层使用Dropout等。 
参考文献 
【Pytorch实现】——深入理解im2col详细图解12张动图帮你看懂卷积神经网络到底是什么《深度学习入门——基于Python的理论与实现》