精华内容
下载资源
问答
  • 卷积神经网络python

    2018-05-24 10:34:21
    深度学习卷积神经网络,简单的实现卷积神经网络的架构 。简单通俗
  • 卷积神经网络 python代码 代码代码 代码。。。。。
  • 卷积神经网络Python

    2021-09-14 18:06:26
    卷积神经网络的应用
  • 里面包含基于TensorFlow的mnist数据集卷积神经网络代码,从数据提取,到精度测试都有,适合初学者观看。
  • 多层前向卷积神经网络Python代码。Theano的CNN代码
  • CNN实现MNIST分类,在测试集上实现准确率0.99,TensorFlow实现,容易上手
  • 卷积神经网络python实现。 卷积神经网络(Convolutional Neural Networks, CNN)是一类包含卷积或相关计算且具有深度结构的前馈神经网络(Feedforward Neural Networks),是深度学习(deep learning)的代表算法之...
  • CNN卷积神经网络PYTHON

    2018-10-26 19:02:39
    CNN卷积神经网络,包含数据,代码有标注,可以用来参考学习
  • 卷积神经网络python实现

    万次阅读 多人点赞 2018-12-18 12:02:10
    1、网络结构 2、各层详解 1)卷积层(conv) A、概念解释 B、实现过程 C、多维输入数据计算 D、卷积层代码实现 2)池化层(pooling) A、实现过程 B、池化层代码实现 3)激活层 A、sigmoid函数 B、阶跃...

    目录

    1、网络结构

    2、各层详解

    1)卷积层(conv)

    A、概念解释

    B、实现过程

    C、多维输入数据计算

    D、卷积层代码实现

    2)池化层(pooling)

    A、实现过程

    B、池化层代码实现

    3)激活层

    A、sigmoid函数

    B、阶跃函数

    C、relu函数

    4)affine层

    5)dropout层

    6)softmax-with-loss层

    A、损失函数介绍

    B、softmax-with-loss层结构介绍

    C、代码实现


    1、网络结构

    11层卷积网络结构如下:

    conv - relu - conv - relu - conv - relu - pool -

    conv - relu - conv - relu - conv - relu - pool -

    conv - relu - conv - relu - conv - relu - pool -

    affine - relu - dropout - affine - dropout – softmaxwithloss

    conv:卷积层

    relu:激活层

    pool:池化层

    affine:全连接层,实现输入数据和节点参数矩阵的相乘

    dropout:按照一定概率抛弃某些节点,防止过拟合

    softmax-with-loss:将输入数据正规化后输出,并输出交叉熵误差

    2、各层详解

    1)卷积层(conv)

    A、概念解释

    填充(pad):卷积运算前,想输入数据周围填入固定的数据(比如0),用P表示。

    步幅(stride):应用滤波器的窗口间隔大小。

    B、实现过程

    如图1

                                               图1

    假设待处理矩阵大小为 ( W , H ) ,卷积滤波器大小为 ( FW , FH ) ,填充为P,步幅为S,则输出矩阵大小为:

                                          

    C、多维输入数据计算

    实际应用中,输入多为三通道彩色图像,此时滤波器相应设为三通道,即:

     

    通过im2col函数将四维矩阵展开为二维矩阵进行运算,函数形式如下:

                             col = im2col(x, FH, FW, self.stride, self.pad)

    x为输入矩阵,输出矩阵的大小为

                                 ( OH * OW * N ,  FH * FW * C )

    D、卷积层代码实现

    2)池化层(pooling)

    A、实现过程

                                                                           图2

    池化层滤波器大小一般为(2 , 2),随着滤波器的移动,依次选择窗口内的最大值作为输出。

    B、池化层代码实现

    处理多维数组时,依然用im2col函数改变维度以方便计算。

    3)激活层

    激活函数一般用非线性函数,本节介绍常用的几种激活函数。

    A、sigmoid函数

    函数形式如下:

    Python代码实现:

    B、阶跃函数

    Python代码实现:

    C、relu函数

    函数形式如下:

    Python代码实现:

    本文所构建网络选用relu函数作为激活函数。

    4)affine层

    前文提到,affine层主要作用时实现矩阵的乘法运算。

                                                                  图3

    图3为affine层的计算图,X , W , B 分别为输入数据,节点参数,偏置。

    Affine层代码实现:

    5)dropout层

    Dropout是一种在学习的过程中随机删除神经元的方法。训练时,随机选出隐藏层的神经元,然后将其删除。被删除的神经元不再进行信号的传递,如图4所示。训练时,每传递一次数据,就会随机选择要删除的神经元。然后,测试时,虽然会传递所有的神经元信号,但是对于各个神经元的输出,要乘上训练时的删除比例后再输出。

                                                                     图4

    Dropout层python代码实现:

    6)softmax-with-loss层

    A、损失函数介绍

    常用的损失函数包括均方误差和交叉熵误差。均方误差的计算公式如下:

    这里,yk 是表示神经网络的输出,tk 表示监督数据,k表示数据的维数。E值越小,代表输出与监督数据越吻合。

    cross-entropy-error层的输出为交叉熵误差,计算公式如下:

    这里,log表示以e为底数的自然对数(log e )。yk 是神经网络的输出,tk 是正确解标签。并且,tk 中只有正确解标签的索引为1,其他均为0 。因此,上式实际上只计算对应正确解标签的输出的自然对数。比如,假设正确解标签的索引是“2”,与之对应的神经网络的输出是0.6,则交叉熵误差是−log0.6 = 0.51;若“2”对应的输出是0.1,则交叉熵误差为−log0.1 = 2.30。也就是说,交叉熵误差的值是由正确解标签所对应的输出结果决定的。

    B、softmax-with-loss层结构介绍

    该层的计算图如图5所示。softmax-with-loss层包括正向传播和反向传播,本节先介绍正向传播。

                                                                                  图5

    softmax-with-loss层由softmax、cross-entropy-error两个函数构成。Softmax层的作用为将输入正规化输出,计算公式如下:

    如图6,Softmax层将输入值正规化(将输出值的和调整为1)之后再输出。另外,因为手写数字识别要进行10类分类,所以向Softmax层的输入也有10个。

                                           图6

    Softmax层代码实现:

    C、代码实现

    展开全文
  • resources for graph convolutional networks (图卷积神经网络相关资源)
  • 基于Python卷积神经网络的图像分类,很适合初学者的学习使用
  • 对于卷积神经网络(CNN)而言,相信很多读者并不陌生,该网络近年来在大多数领域都表现优异,尤其是在计算机视觉领域中。但是很多工作人员可能直接调用相关的深度学习工具箱搭建卷积神经网络模型,并不清楚其中具体...
  • 一、CIFAR-10简介 CIFAR-10数据集包含10个类别,共计60000张 32×32 3通道彩色图像。...第一次卷积卷积核大小为3*3,输出32*32 32通道 第一次池化:最大值池化,输出为16*16 32通道 第二次卷积:卷
  • import numpy as np ... # 循环遍历图像以应用卷积运算 for r in np.uint16(np.arange(filter_size/2.0, img.shape[0]-filter_size/2.0+1)): for c in np.uint16(np.arange(filter_size/2.0, img.shape[1]-filter_s
  • 基于卷积神经网络CNN的人脸识别项目,主要代码包括对人脸数据的获取,对图像集的预处理,将图像加载到内存,构建并训练模型和识别人脸五个模块组成。
  • 教你如何搭建一个卷积神经网络,是开展图像处理的重要一步
  • 人工神经网络 基于CNN卷积神经网络 基于Python 实现图片验证码的识别
  • 越来越卷,教你使用Python实现卷积神经网络(CNN)

    千次阅读 多人点赞 2021-05-31 07:51:16
    Yann LeCun 和Yoshua Bengio在1995年引入了卷积神经网络,也称为卷积网络或CNN。CNN是一种特殊的多层神经网络,用于处理具有明显网格状拓扑的数据。其网络的基础基于称为卷积的数学运算。

    @Author:Runsen

    卷积神经网络

    Yann LeCun 和Yoshua Bengio在1995年引入了卷积神经网络,也称为卷积网络或CNN。CNN是一种特殊的多层神经网络,用于处理具有明显网格状拓扑的数据。其网络的基础基于称为卷积的数学运算。

    卷积神经网络(CNN)的类型
    以下是一些不同类型的CNN:

    • 1D CNN:1D CNN 的输入和输出数据是二维的。一维CNN大多用于时间序列。

    • 2D CNNN:2D CNN的输入和输出数据是三维的。我们通常将其用于图像数据问题。

    • 3D CNNN:3D CNN的输入和输出数据是四维的。一般在3D图像上使用3D CNN,例如MRI(磁共振成像),CT扫描(甲CT扫描或计算机断层扫描(以前称为计算机轴向断层或CAT扫描)是一种医学成像 技术中使用的放射学获得用于非侵入性详述的身体

    展开全文
  • 设计神经网络 进行训练测试 1. 数据处理 将图片数据处理为 tf 能够识别的数据格式,并将数据设计批次。 第一步get_files() 方法读取图片,然后根据图片名,添加猫狗 label,然后再将 image和label 放到 数组中,...
  • 卷积神经网络图像识别python代码
  • 卷积神经网络基于 Tensorflow 实现的中文文本分类
  • 在上一篇神经网络的Python实现(二)全连接网络...这篇博文将要详细介绍卷积神经网络的概念,并且进行前馈和反向传播的公式推导及Numpy实现。 卷积神经网络 卷积神经网络(Convolutional Neural Network)非常擅于...

    推荐在我的博客中给我留言,这样我会随时收到你的评论,并作出回复。


    在上一篇神经网络的Python实现(二)全连接网络中,已经介绍了神经网络的部分激活函数,损失函数和全连接网络的前馈和反向传播公式及Numpy实现。这篇博文将要详细介绍卷积神经网络的概念,并且进行前馈和反向传播的公式推导及Numpy实现。

    卷积神经网络

    卷积神经网络(Convolutional Neural Network)非常擅于处理图像任务,它的灵感来自于视觉神经中的感受野这一概念,卷积神经网络的卷积核(Convolution Kernel) 好似感受野一样去扫描数据。一个卷积神经网络基本包括卷积层池化层输出层

    接下来介绍什么是卷积核、卷积神经网络中的卷积是怎么运算的。

    卷积和卷积核

    卷积神经网络中的卷积操作与数学中的类似。就是输入数据中不同数据窗口的数据和卷积核(一个权值矩阵)作内积的操作。其中卷积核是卷积神经网络中卷积层的最重要的部分。卷积核相当于信息处理中的滤波器,可以提取输入数据的当前特征。卷积核的实质是一个权值矩阵,在下图中的卷积核便是一个权值如下的矩阵(图中黄色色块中的红色数字)

    [ 1 0 1 0 1 0 1 0 1 ] \left[ \begin{matrix} 1 & 0 & 1\\ 0 & 1 & 0\\ 1 & 0 & 1 \end{matrix} \right] 101010101

    卷积

    如果并不理解卷积,那么我们来看图中输出的第一行第一列的4是怎么得到的。

    原输入数据大小为 5 × 5 5\times 5 5×5,我们要使用 3 × 3 3\times3 3×3的卷积核来进行卷积,我们使用 ∗ \ast 表示卷积操作。那么图中第一个4的运算过程就可以表达为:

    [ 1 1 1 0 1 1 0 0 1 ] ∗ [ 1 0 1 0 1 0 1 0 1 ] = 1 × 1 + 1 × 0 + 1 × 1 + 0 × 0 + 1 × 1 + 1 × 0 + 0 × 1 + 0 × 0 + 1 × 1 = 4 \left[ \begin{matrix} \color{red}{1} & 1 & 1\\ 0 & 1 & 1\\ 0 & 0 & 1 \end{matrix} \right] * \left[ \begin{matrix} \color{blue}{1} & 0 & 1\\ 0 & 1 & 0\\ 1 & 0 & 1 \end{matrix} \right]= \color{red}{1}\times\color{blue}{1} + 1\times0+1 \times 1+ 0\times0+1\times1+1\times0+ 0\times1+0\times0 +1\times1=4 100110111101010101=1×1+1×0+1×1+0×0+1×1+1×0+0×1+0×0+1×1=4

    剩下位置的输出就是卷积核在输入矩阵上从左到右从上到下移动一格做如上卷积操作过程的结果。

    步长(strides)和填充(padding)

    步长

    上面例子说到的一格表示的就是步长(strides),步长分为横向步长和纵向步长,步长是多少就表示一次卷积操作之后卷积核移动的距离。知道步长的概念了,我们就可以去计算一下根据输入大小,卷积核大小,我们得到的输出的大小。假设用 C C C表示边长,那么:

    C o u t p u t = C i n p u t − C k e r n e l s t r i d e s + 1 C_{output} = \frac{C_{input}-C_{kernel}}{strides}+1 Coutput=stridesCinputCkernel+1

    根据公式当步长为1或是输入大小能够被步长整除时很好处理,无法整除时也就是卷积核移动到最后,输入数据的剩下的部分不足卷积核大小,这时我们会想到要么将输入变大点让它能够整除要么是干脆边界直接丢弃让它能够整除。这两种处理办法对应于填充(padding) 的两种方式,‘SAME’’VALID’

    VALID

    O w = c e i l ( I w − k w + 1 s w ) O h = c e i l ( I h − k h + 1 s h ) O_w = ceil\left(\frac{I_w-k_w+1}{s_w}\right)\\ O_h = ceil\left(\frac{I_h-k_h+1}{s_h}\right)\\ Ow=ceil(swIwkw+1)Oh=ceil(shIhkh+1)

    其中 c e i l ceil ceil向上取整, w w w是宽方向, h h h是长方向, I , O , k , s I,O,k,s I,O,k,s分别代表输入、输出、卷积核和步长。

    超过 O w , O h O_w,O_h Ow,Oh部分就舍弃不要了。

    真正输入大小 I w = s w ( O w − 1 ) + k w I h = s h ( O h − 1 ) + k h I_w = s_w(O_w-1)+k_w\\I_h = s_h(O_h-1)+k_h Iw=sw(Ow1)+kwIh=sh(Oh1)+kh

    SAME

    same

    SAME就是在输入周围补0,我们先计算补0后的输出大小:

    O w = c e i l ( I w s w ) O h = c e i l ( I h s h ) O_w = ceil\left(\frac{I_w}{s_w}\right)\\ O_h = ceil\left(\frac{I_h}{s_h}\right) Ow=ceil(swIw)Oh=ceil(shIh)

    接下来便根据应得到输出的大小去padding。

    P h = max ⁡ ( ( O h − 1 ) × s h + k h − I h    ,   0 ) P w = max ⁡ ( ( O w − 1 ) × s w + k w − I w    ,   0 ) P t o p = f l o o r ( P h 2 )          P b o t t o m = P h − P t o p P l e f t = f l o o r ( P w 2 )          P r i g h t = P w − P l e f t \begin{aligned} P_h &= \max \left((O_h-1)\times s_h + k_h-I_h\ \ ,\ 0\right)\\ P_w &= \max \left((O_w-1)\times s_w + k_w-I_w\ \ ,\ 0\right)\\ P_{top} &= floor\left(\frac{P_h}{2}\right) \ \ \ \ \ \ \ \ P_{bottom} = P_h - P_{top} \\ P_{left} &= floor\left(\frac{P_w}{2}\right) \ \ \ \ \ \ \ \ P_{right} = P_w - P_{left} \end{aligned} PhPwPtopPleft=max((Oh1)×sh+khIh  , 0)=max((Ow1)×sw+kwIw  , 0)=floor(2Ph)        Pbottom=PhPtop=floor(2Pw)        Pright=PwPleft

    其中 f l o o r floor floor向下取整

    这样0就几乎对称地分布在输入四周。

    多通道的卷积

    一般卷积神经网络处理的都是3通道或是多通道的图像数据,那么对于多通道如何卷积呢?对于多通道,卷积公式并不变,只是要求卷积核通道与输入通道数一致,不同通道分别做内积,然后不同通道得到的值相加起来作为最后的输出。如图。

    多通道

    对于计算,我们使用 2 × 2 × 2 2\times2\times2 2×2×2的输入和 2 × 2 × 2 2\times2\times2 2×2×2的卷积核举个例子:

         [ [ 1 2 3 4 ] , [ 5 6 7 8 ] ] ∗ [ [ 1 2 3 4 ] , [ 5 6 7 8 ] ] = [ 1 × 1 + 5 × 5 2 × 2 + 6 × 6 3 × 3 + 7 × 7 4 × 4 + 8 × 8 ] = [ 26 40 58 80 ] \begin{aligned} &\ \ \ \ \left[ \begin{matrix} \left[ \begin{matrix} 1 & 2\\ 3 & 4 \end{matrix} \right], \left[ \begin{matrix} 5 & 6\\ 7 & 8 \end{matrix} \right] \end{matrix} \right] *\left[ \begin{matrix} \left[ \begin{matrix} 1 & 2\\ 3 & 4 \end{matrix} \right], \left[ \begin{matrix} 5 & 6\\ 7 & 8 \end{matrix} \right] \end{matrix} \right] \\ \\ &=\left[ \begin{matrix} 1\times1+5\times5 & 2 \times 2+6 \times 6 \\ 3\times3+7\times7 & 4 \times4+8\times8 \end{matrix} \right]\\ \\ &=\left[ \begin{matrix} 26 & 40\\ 58 & 80 \end{matrix} \right] \end{aligned}     [[1324],[5768]][[1324],[5768]]=[1×1+5×53×3+7×72×2+6×64×4+8×8]=[26584080]

    可以看到,不论输入通道数是多少最后的输出仍是一个矩阵。在卷积层如果有多个卷积核,每个卷积核会提取一种特征,输出一个二维矩阵。最终的结果就是把这些卷积核的输出看作不同通道。如下图。

    多卷积核

    下面以图像处理为例,来看一下卷积神经网络的前馈和反向传播。

    前向传播

    卷积层的前向传播方式与全连接层类似,我们回顾一下全连接层的前向传播:

    KaTeX parse error: No such environment: align at position 8: \begin{̲a̲l̲i̲g̲n̲}̲ a^{(l)} &= \si…

    卷积层只不过把全连接层的矩阵乘法运算换成了卷积运算。详细的步骤如下
    知道前一层的输出之后:

    1. 定义好卷积核数目,卷积核大小,步长和填充方式。根据输入大小,计算输出大小并进行相应的padding,得到了卷积层的输入 a l − 1 a^{l-1} al1
    2. 初始化所有卷积和的权重 W W W 和偏置 b b b
    3. 根据前向传播的公式(M个通道):
      a l = σ ( z l ) = σ ( ∑ k = 1 M z k l ) = σ ( ∑ k = 1 M a k l − 1 ∗ W k l + b l ) a^l= \sigma(z^l) = \sigma(\sum\limits_{k=1}^{M}z_k^l) = \sigma(\sum\limits_{k=1}^{M}a_k^{l-1}*W_k^l +b^l) al=σ(zl)=σ(k=1Mzkl)=σ(k=1Makl1Wkl+bl)
      a l = σ ( z l ) = σ ( W l a l − 1 + b l ) a^l= \sigma(z^l) = \sigma(W^la^{l-1} +b^l) al=σ(zl)=σ(Wlal1+bl)
      计算出卷积层输出,其中 ∗ * 是卷积运算、 σ \sigma σ是激活函数。

    反向传播

    现在已知卷积层的 δ l \delta^l δl ,我们通过反向传播算法来计算上一层的 δ l − 1 \delta^{l-1} δl1
    我们也先回顾一下反向传播公式,根据链式法则:

    δ l − 1 = ∂ J ( W , b ) ∂ z l − 1 = ∂ J ( W , b ) ∂ z l ∂ z l ∂ z l − 1 = δ l ∂ z l ∂ z l − 1 \delta^{l-1} = \frac{\partial J(W,b)}{\partial z^{l-1}} = \frac{\partial J(W,b)}{\partial z^{l}}\frac{\partial z^{l}}{\partial z^{l-1}} = \delta^{l}\frac{\partial z^{l}}{\partial z^{l-1}} δl1=zl1J(W,b)=zlJ(W,b)zl1zl=δlzl1zl

    要计算 δ l \delta^l δl的值,必须知道 ∂ z l ∂ z l − 1 \frac{\partial z^{l}}{\partial z^{l-1}} zl1zl的值,所以根据前向传播公式:

    a l = σ ( z l ) = σ ( a l − 1 ∗ W l + b l ) a^l= \sigma(z^l) = \sigma(a^{l-1}*W^l +b^l) al=σ(zl)=σ(al1Wl+bl)

    这里我们将 z l z^{l} zl z l − 1 z^{l-1} zl1 拿出来看:

    z l = σ ( z l − 1 ) ∗ W l + b l z^l = \sigma(z^{l-1})*W^l+b^l zl=σ(zl1)Wl+bl

    现在就差卷积运算的偏导该如何求,我们先把正确公式写出来,之后再解释:

    δ l − 1 = δ l ∂ z l ∂ z l − 1 = δ l ∗ r o t 180 ( W l ) ⊙ σ ′ ( z l − 1 ) \delta^{l-1} = \delta^{l}\frac{\partial z^{l}}{\partial z^{l-1}} = \delta^{l}*rot180(W^{l}) \odot \sigma^{'}(z^{l-1}) δl1=δlzl1zl=δlrot180(Wl)σ(zl1)

    这里的 r o t 180 ( W l ) rot180(W^l) rot180(Wl) 表示将卷积核旋转180°,即卷积核左右翻转之后再上下翻转。可以拿张正反内容不一样的纸转一转。然后我们解释为什么卷积的求导就是将卷积核旋转180°再做卷积的结果。

    我们拿 3 × 3 3\times 3 3×3 大小矩阵作为例子,卷积核大小为 2 × 2 2\times 2 2×2,步长为1(步长不是1时后面会提到):

    [ a 11 a 12 a 13 a 21 a 22 a 23 a 31 a 32 a 33 ] ∗ [ w 11 w 12 w 21 w 22 ] = [ z 11 z 12 z 21 z 22 ] \left[ \begin{array}{ccc} a_{11}&a_{12}&a_{13} \\ a_{21}&a_{22}&a_{23}\\ a_{31}&a_{32}&a_{33} \end{array} \right] * \left[ \begin{array}{ccc} w_{11}&w_{12}\\ w_{21}&w_{22} \end{array} \right] = \left[ \begin{array}{ccc} z_{11}&z_{12}\\ z_{21}&z_{22} \end{array} \right] a11a21a31a12a22a32a13a23a33[w11w21w12w22]=[z11z21z12z22]

    上面是前向的卷积运算,我们把它展开来:

    z 11 = a 11 w 11 + a 12 w 12 + a 21 w 21 + a 22 w 22 z 12 = a 12 w 11 + a 13 w 12 + a 22 w 21 + a 23 w 22 z 21 = a 21 w 11 + a 22 w 12 + a 31 w 21 + a 32 w 22 z 22 = a 22 w 11 + a 23 w 12 + a 32 w 21 + a 33 w 22 \begin{aligned} z_{11} &= a_{11}w_{11} + a_{12}w_{12} + a_{21}w_{21} + \color{red}{a_{22}}w_{22}\\ z_{12} &= a_{12}w_{11} + a_{13}w_{12} + \color{red}{a_{22}}w_{21} + a_{23}w_{22}\\ z_{21} &= a_{21}w_{11} + \color{red}{a_{22}}w_{12} + a_{31}w_{21} + a_{32}w_{22}\\ z_{22} &= \color{red}{a_{22}}w_{11} + a_{23}w_{12} + a_{32}w_{21} + a_{33}w_{22} \end{aligned} z11z12z21z22=a11w11+a12w12+a21w21+a22w22=a12w11+a13w12+a22w21+a23w22=a21w11+a22w12+a31w21+a32w22=a22w11+a23w12+a32w21+a33w22

    这样就变成了简单的运算,根据反向传播公式:

    ∇ a l − 1 = ∂ J ( W , b ) ∂ a l − 1 = ∂ J ( W , b ) ∂ z l ∂ z l ∂ a l − 1 = δ l ∂ z l ∂ a l − 1 \nabla a^{l-1} = \frac{\partial J(W,b)}{\partial a^{l-1}} = \frac{\partial J(W,b)}{\partial z^{l}} \frac{\partial z^{l}}{\partial a^{l-1}} = \delta^{l} \frac{\partial z^{l}}{\partial a^{l-1}} al1=al1J(W,b)=zlJ(W,b)al1zl=δlal1zl

    我们就要对每个 a a a 求其梯度:

    比如 a 11 a_{11} a11 只和 z 11 z_{11} z11 有关,所以:

    ∇ a 11 = δ 11 w 11 \nabla a_{11} = \delta_{11}w_{11} a11=δ11w11

    复杂点的对于为了明显标红的 a 22 a_{22} a22 ,它与 z 11 , z 12 , z 21 , z 22 z_{11},z_{12},z_{21},z_{22} z11,z12,z21,z22 都有关,所以:

    ∇ a 22 = δ 11 w 22 + δ 12 w 21 + δ 21 w 12 + δ 22 w 11 \nabla a_{22} = \delta_{11}w_{22} + \delta_{12}w_{21} + \delta_{21}w_{12} + \delta_{22}w_{11} a22=δ11w22+δ12w21+δ21w12+δ22w11

    类似地我们把所有的 ∇ a \nabla a a 都求出来:

    ∇ a 11 = δ 11 w 11 ∇ a 12 = δ 11 w 12 + δ 12 w 11 ∇ a 13 = δ 12 w 12 ∇ a 21 = δ 11 w 21 + δ 21 w 11 ∇ a 22 = δ 11 w 22 + δ 12 w 21 + δ 21 w 12 + δ 22 w 11 ∇ a 23 = δ 12 w 22 + δ 22 w 12 ∇ a 31 = δ 21 w 21 ∇ a 32 = δ 21 w 22 + δ 22 w 21 ∇ a 33 = δ 22 w 22 \begin{aligned} \nabla a_{11} &= \delta_{11}w_{11}\\ \nabla a_{12} &= \delta_{11}w_{12} + \delta_{12}w_{11}\\ \nabla a_{13} &= \delta_{12}w_{12}\\ \nabla a_{21} &= \delta_{11}w_{21} + \delta_{21}w_{11}\\ \nabla a_{22} &= \delta_{11}w_{22} + \delta_{12}w_{21} + \delta_{21}w_{12} + \delta_{22}w_{11}\\ \nabla a_{23} &= \delta_{12}w_{22} + \delta_{22}w_{12}\\ \nabla a_{31} &= \delta_{21}w_{21}\\ \nabla a_{32} &= \delta_{21}w_{22} + \delta_{22}w_{21}\\ \nabla a_{33} &= \delta_{22}w_{22} \end{aligned} a11a12a13a21a22a23a31a32a33=δ11w11=δ11w12+δ12w11=δ12w12=δ11w21+δ21w11=δ11w22+δ12w21+δ21w12+δ22w11=δ12w22+δ22w12=δ21w21=δ21w22+δ22w21=δ22w22

    如果你尝试过padding过的卷积运算,你会发现上面的式子就是下面列出的卷积(步长为1):

    [ 0 0 0 0 0 δ 11 δ 12 0 0 δ 21 δ 22 0 0 0 0 0 ] ∗ [ w 22 w 21 w 12 w 11 ] = [ ∇ a 11 ∇ a 12 ∇ a 13 ∇ a 21 ∇ a 22 ∇ a 23 ∇ a 31 ∇ a 32 ∇ a 33 ] \left[ \begin{array}{ccc} 0&0&0&0 \\ 0&\delta_{11}& \delta_{12}&0 \\ 0&\delta_{21}&\delta_{22}&0 \\ 0&0&0&0 \end{array} \right] * \left[ \begin{array}{ccc} w_{22}&w_{21}\\ w_{12}&w_{11} \end{array} \right] = \left[ \begin{array}{ccc} \nabla a_{11}&\nabla a_{12}&\nabla a_{13} \\ \nabla a_{21}&\nabla a_{22}&\nabla a_{23}\\ \nabla a_{31}&\nabla a_{32}&\nabla a_{33} \end{array} \right] 00000δ11δ2100δ12δ2200000[w22w12w21w11]=a11a21a31a12a22a32a13a23a33

    这里最好动手写一下就可以发现这种运算关系。(这里的周围的0填充宽度是卷积核边长-1)

    以上是步长为1时的求解过程,当步长大于1时,我们就需要在 δ \delta δ 矩阵的值之间填充0来实现步长。

    先说结论,每两个 δ \delta δ 之间需要填充步长-1个0(对应方向上的)。也举个例子来看看是不是这样,步长为2。

    [ a 11 a 12 a 13 a 14 a 21 a 22 a 23 a 24 a 31 a 32 a 33 a 34 a 41 a 42 a 43 a 44 ] ∗ [ w 11 w 12 w 21 w 22 ] = [ z 11 z 12 z 21 z 22 ] \left[ \begin{matrix} a_{11} & a_{12} & a_{13} & a_{14}\\ a_{21} & a_{22} & a_{23} & a_{24}\\ a_{31} & a_{32} & a_{33} & a_{34}\\ a_{41} & a_{42} & a_{43} & a_{44} \end{matrix} \right] * \left[ \begin{matrix} w_{11} & w_{12} \\ w_{21} & w_{22} \end{matrix} \right] = \left[ \begin{matrix} z_{11} & z_{12} \\ z_{21} & z_{22} \end{matrix} \right] a11a21a31a41a12a22a32a42a13a23a33a43a14a24a34a44[w11w21w12w22]=[z11z21z12z22]

    展开来:

    z 11 = a 11 w 11 + a 12 w 12 + a 21 w 21 + a 22 w 22 z 12 = a 13 w 11 + a 14 w 12 + a 23 w 21 + a 24 w 22 z 21 = a 31 w 11 + a 32 w 12 + a 41 w 21 + a 42 w 22 z 22 = a 33 w 11 + a 34 w 12 + a 43 w 21 + a 44 w 22 \begin{aligned} z_{11} &= a_{11}w_{11} + a_{12}w_{12} + a_{21}w_{21} + a_{22}w_{22}\\ z_{12} &= a_{13}w_{11} + a_{14}w_{12} + a_{23}w_{21} + a_{24}w_{22}\\ z_{21} &= a_{31}w_{11} + a_{32}w_{12} + a_{41}w_{21} + a_{42}w_{22}\\ z_{22} &= a_{33}w_{11} + a_{34}w_{12} + a_{43}w_{21} + a_{44}w_{22} \end{aligned} z11z12z21z22=a11w11+a12w12+a21w21+a22w22=a13w11+a14w12+a23w21+a24w22=a31w11+a32w12+a41w21+a42w22=a33w11+a34w12+a43w21+a44w22

    计算梯度:

    ∇ a 11 = δ 11 w 11     ∇ a 12 = δ 11 w 12     ∇ a 13 = δ 12 w 11     ∇ a 14 = δ 12 w 12 ∇ a 21 = δ 11 w 21     ∇ a 22 = δ 11 w 22     ∇ a 23 = δ 12 w 21     ∇ a 24 = δ 12 w 22 ∇ a 31 = δ 21 w 11     ∇ a 32 = δ 21 w 12     ∇ a 33 = δ 22 w 11     ∇ a 34 = δ 22 w 12 ∇ a 41 = δ 21 w 21     ∇ a 42 = δ 21 w 22     ∇ a 43 = δ 22 w 21     ∇ a 44 = δ 22 w 22 \begin{aligned} \nabla a_{11} &= \delta_{11}w_{11}\ \ \ \nabla a_{12} = \delta_{11}w_{12}\ \ \ \nabla a_{13} = \delta_{12}w_{11}\ \ \ \nabla a_{14} = \delta_{12}w_{12}\\ \nabla a_{21} &= \delta_{11}w_{21}\ \ \ \nabla a_{22} = \delta_{11}w_{22}\ \ \ \nabla a_{23} = \delta_{12}w_{21}\ \ \ \nabla a_{24} = \delta_{12}w_{22}\\ \nabla a_{31} &= \delta_{21}w_{11}\ \ \ \nabla a_{32} = \delta_{21}w_{12}\ \ \ \nabla a_{33} = \delta_{22}w_{11}\ \ \ \nabla a_{34} = \delta_{22}w_{12}\\ \nabla a_{41} &= \delta_{21}w_{21}\ \ \ \nabla a_{42} = \delta_{21}w_{22}\ \ \ \nabla a_{43} = \delta_{22}w_{21}\ \ \ \nabla a_{44} = \delta_{22}w_{22} \end{aligned} a11a21a31a41=δ11w11   a12=δ11w12   a13=δ12w11   a14=δ12w12=δ11w21   a22=δ11w22   a23=δ12w21   a24=δ12w22=δ21w11   a32=δ21w12   a33=δ22w11   a34=δ22w12=δ21w21   a42=δ21w22   a43=δ22w21   a44=δ22w22

    即:

    [ 0 0 0 0 0 0 δ 11 0 δ 12 0 0 0 0 0 0 0 δ 21 0 δ 22 0 0 0 0 0 0 ] ∗ [ w 22 w 21 w 12 w 11 ] = [ ∇ a 11 ∇ a 12 ∇ a 13 ∇ a 14 ∇ a 21 ∇ a 22 ∇ a 23 ∇ a 24 ∇ a 31 ∇ a 32 ∇ a 33 ∇ a 34 ∇ a 41 ∇ a 42 ∇ a 43 ∇ a 44 ] \left[ \begin{matrix} 0 & 0 & 0 & 0 & 0 \\ 0 & \delta_{11} & 0 & \delta_{12} & 0 \\ 0 & 0 & 0 & 0 & 0 \\ 0 &\delta_{21} & 0 & \delta_{22} & 0\\ 0 & 0 & 0 & 0 & 0 \end{matrix} \right] * \left[ \begin{matrix} w_{22} & w_{21} \\ w_{12} & w_{11} \end{matrix} \right] = \left[ \begin{matrix} \nabla a_{11} & \nabla a_{12} & \nabla a_{13} & \nabla a_{14}\\ \nabla a_{21} & \nabla a_{22} & \nabla a_{23} & \nabla a_{24}\\ \nabla a_{31} & \nabla a_{32} & \nabla a_{33} & \nabla a_{34}\\ \nabla a_{41} & \nabla a_{42} & \nabla a_{43} & \nabla a_{44} \end{matrix} \right] 000000δ110δ210000000δ120δ22000000[w22w12w21w11]=a11a21a31a41a12a22a32a42a13a23a33a43a14a24a34a44

    :无论前向步长为多少,旋转后的卷积步长一直是1。

    其余计算过程类比全连接层是一样的。至此,卷积层的反向传播就结束了。

    下面我们使用Numpy来实现卷积层的前向和反向传播。

    CODE

    代码是在上一篇全连接网络基础上增加的,继承自Layer类,使得不同类型的层可以叠加成网络。

    卷积层

    首先我们定义一个卷积核类,用来实现每个卷积核的卷积计算和前向传播反向传播。

    class ConvKernel(Layer):
        """
        这里不需要继承自Layer,但是把激活函数求导过程放在了这里,没改所以还是继承了。
        """
        def __init__(self, kernel_size, input_shape, strides):
            """
            :param kernel_size: 卷积核大小
            :param input_shape: 输入大小
            :param strides:     步长大小
            """
            super().__init__()
            self.__kh = kernel_size[0]
            self.__kw = kernel_size[1]
            self.__input_shape = input_shape
            self.__channel = input_shape[2]
            self.__strides = strides
            # self.__padding = padding
            self.__w = np.random.randn(kernel_size[0], kernel_size[1],
                                       input_shape[2])  # np.array([[1,0,1],[0,1,0],[1,0,1]])
            self.__output_shape = (int((input_shape[0] - kernel_size[0]) / strides[0]) + 1,
                                   int((input_shape[1] - kernel_size[1]) / strides[1]) + 1)
            self.__input = None
            self.__output = None
            self.__b = np.random.randn(self.__output_shape[0], self.__output_shape[1])
    
        def __flip_w(self):
            """
            :return: w after flip 180
            """
            return np.fliplr(np.flipud(self.__w))
    
        def __updata_params(self, w_delta, b_delta, lr):
            self.__w -= w_delta * lr
            self.__b -= b_delta * lr
    
        def __conv(self, _input, weights, strides, _axis=None):
            """
            卷积运算
            :param _input:      输入
            :param weights:     权重
            :param strides:     步长
            :param _axis:       维度
            :return: 
            """
            if _axis is None:  # 矩阵情况
                result = np.zeros((int((_input.shape[0] - weights.shape[0]) / strides[0]) + 1,
                                   int((_input.shape[1] - weights.shape[1]) / strides[1]) + 1))
                for h in range(result.shape[0]):
                    for w in range(result.shape[1]):
                        result[h, w] = np.sum(_input[h * strides[0]:h * strides[0] + weights.shape[0],
                                              w * strides[1]:w * strides[1] + weights.shape[1]] * weights)
            else:
                result = np.zeros((int((_input.shape[0] - weights.shape[0]) / strides[0]) + 1,
                                   int((_input.shape[1] - weights.shape[1]) / strides[1]) + 1,
                                   self.__input_shape[2]))
                for h in range(result.shape[0]):
                    for w in range(result.shape[1]):
                        result[h, w, :] = np.sum(_input[h * strides[0]:h * strides[0] + weights.shape[0],
                                                 w * strides[1]:w * strides[1] + weights.shape[1]] * weights,
                                                 axis=_axis)
    
            return result
    
        def forward_pass(self, X):
            self.__input = X
            self.__output = self.__conv(X, self.__w, self.__strides) + self.__b
            return self.__output
    
        def back_pass(self, error, lr, activation_name='none'):
            o_delta = np.zeros((self.__output_shape[0], self.__output_shape[1], self.__channel))
            # 将delta扩展至通道数
            for i in range(self.__channel):
                o_delta[:, :, i] = error
            # 根据输入、步长、卷积核大小计算步长
            X = np.zeros(
                shape=(self.__input_shape[0] + self.__kh - 1, self.__input_shape[1] + self.__kw - 1, self.__channel))
    
            o_delta_ex = np.zeros(
                (self.__output_shape[0], self.__output_shape[1],
                 self.__channel))
            
            #  根据步长填充0
            for i in range(o_delta.shape[0]):
                for j in range(o_delta.shape[1]):
                    X[self.__kh - 1 + i * self.__strides[0],
                    self.__kw - 1 + j * self.__strides[1], :] = o_delta[i, j, :]
                    # print(o_delta_ex.shape,o_delta.shape)
                    o_delta_ex[i, j, :] = o_delta[i, j, :]
    
            flip_conv_w = self.__conv(X, self.__flip_w(), (1, 1), _axis=(0, 1))
            delta = flip_conv_w * np.reshape(
                self._activation_prime(activation_name, self.__input),
                flip_conv_w.shape)
    
            w_delta = np.zeros(self.__w.shape)
            for h in range(w_delta.shape[0]):
                for w in range(w_delta.shape[1]):
                    if self.__channel == 1:
                        w_delta[h, w, :] = np.sum(self.__input[h:h + o_delta_ex.shape[0],
                                                  w:w + o_delta_ex.shape[1]] * o_delta_ex)
                    else:
                        w_delta[h, w, :] = np.sum(self.__input[h:h + o_delta_ex.shape[0],
                                                  w:w + o_delta_ex.shape[1]] * o_delta_ex, axis=(0, 1))
            self.__updata_params(w_delta, error, lr)
            return delta
    

    之后再定义卷积层

    class ConvLayer(Layer):
        def __init__(self, filters, kernel_size, input_shape, strides, padding, activation, name="conv"):
            """
            :param filters:         卷积核个数
            :param kernel_size:     卷积核大小
            :param input_shape:     输入shape
            :param strides:         步长
            :param padding:         填充方式
            :param activation:      激活函数名
            :param name:            层名称
            """
            super().__init__()
            self.__filters = filters
            self.__kernel_size = kernel_size
            self.__strides = strides
            self.__padding = padding
            self.activation_name = activation
            self.__input_shape = input_shape  # eg 64*64*3
            self.__input_padding_shape = input_shape
            self.__input = np.zeros(self.__input_shape)
            self.name = name
            self.flag = False
    
        def _padding_X(self, X):
            """
            对输入进行padding
            :param X:  输入
            :return:   输入padding后的值
            """
            if self.__padding == 'SAME':
                o_w = int(np.ceil(X.shape[0] / self.__strides[0]))
                o_h = int(np.ceil(X.shape[1] / self.__strides[1]))
                self.__output_size = (o_w, o_h, self.__filters)
                p_w = np.max((o_w - 1) * self.__strides[0] + self.__kernel_size[0] - X.shape[0], 0)
                p_h = np.max((o_h - 1) * self.__strides[1] + self.__kernel_size[1] - X.shape[1], 0)
                self.p_l = int(np.floor(p_w / 2))
                self.p_t = int(np.floor(p_h / 2))
                res = np.zeros((X.shape[0] + p_w, X.shape[1] + p_h, X.shape[2]))
                res[self.p_t:self.p_t + X.shape[0], self.p_l:self.p_l + X.shape[1], :] = X
                return res
            elif self.__padding == 'VALID':
                o_w = int(np.ceil((X.shape[0] - self.__kernel_size[0] + 1) / self.__strides[0]))
                o_h = int(np.ceil((X.shape[1] - self.__kernel_size[1] + 1) / self.__strides[1]))
                self.__output_size = (o_w, o_h, self.__filters)
                return X[:self.__strides[0] * (o_w - 1) + self.__kernel_size[0],
                       :self.__strides[1] * (o_h - 1) + self.__kernel_size[1], :]
            else:
                raise ValueError("padding name is wrong")
    
        def forward_propagation(self, _input):
            """
            前向传播,在前向传播过程中得到输入值,并计算输出shape
            :param _input:  输入值
            :return:        输出值
            """
            self.__input = self._padding_X(_input)
            self.__input_padding_shape = self.__input.shape
            self.__output = np.zeros(self.__output_size)
            if not self.flag: # 初始化
                self.__kernels = [ConvKernel(self.__kernel_size, self.__input_padding_shape, self.__strides) for _ in
                                  range(self.__filters)]  # 由于随机函数,所以不能使用[]*n来创建多个(数值相同)。
                self.flag = True
            for i, kernel in enumerate(self.__kernels):
                self.__output[:, :, i] = kernel.forward_pass(self.__input)
            return self._activation(self.activation_name, self.__output)
    
        def back_propagation(self, error, lr):
            """
            反向传播过程,对于误差也需要根据padding进行截取或补0
            :param error:   误差
            :param lr:      学习率
            :return:        上一层误差(所有卷积核的误差求平均)
            """
            delta = np.zeros(self.__input_shape)
            for i in range(len(self.__kernels)):
                index = len(self.__kernels) - i - 1
                tmp = self.__kernels[index].back_pass(error[:, :, index], lr, self.activation_name)
                if self.__padding == 'VALID':
                    bd = np.ones(self.__input_shape)
                    bd[:self.__input_padding_shape[0], :self.__input_padding_shape[1]] = tmp
                elif self.__padding == 'SAME':
                    bd = tmp[self.p_t:self.p_t + self.__input_shape[0], self.p_l:self.p_l + self.__input_shape[1]]
                else:
                    raise ValueError("padding name is wrong")
                delta += bd
    
            return delta / len(self.__kernels)
    

    以上是卷积层的前向和反向传播实现。需要自己定义好输入维度,不正确会报错。卷积神经网络大多用作图像的分类任务,所以我们还要实现分类任务需要的softmax激活函数和交叉熵(cross entropy) 损失函数。

    softmax激活函数和交叉熵(cross entropy)损失函数详细的推导将会放在下一篇中讲解,这里先给出代码实现。

    softmax 和 cross entropy

    softmax

    def _activation(self, name, x):
        #···
        #···其他激活函数(详细见上篇)
        #···
        elif name == 'softmax':
            x = x - np.max(x)  # 防止过大
            exp_x = np.exp(x)
            return exp_x / np.sum(exp_x)
            
    def _activation_prime(self, name, x): 
        elif name == 'softmax':
          x = np.squeeze(x)
          #print(x)
          length = len(x)
          res = np.zeros((length,length))
          # print("length", length)
          for i in range(length):
              for j in range(length):
                  res[i,j] = self.__softmax(i, j, x)
    
        return res
    
    def __softmax(self, i, j, a):
        if i == j:
            return a[i] * (1 - a[i])
        else:
            return -a[i] * a[j]
    

    cross entropy

        def __cross_entropy(self, output, y, loss):
            output[output == 0] = 1e-12
            if loss:
                return -y * np.log(output)
            else:
                return - y / output
    

    经过卷积层得到的输出一般是多通道的,我们想要接全连接层去进行分类还需要将多通道数据展成一维向量,就需要Flatten层。只是数据位置的变换,看代码就好。

    import numpy as np
    from Layer import Layer
    
    class FlattenLayer(Layer):
        def __init__(self):
            super().__init__()
            self.__input_shape = None
            self.activation_name = 'none'
    
        def forward_propagation(self, _input):
            self.__input_shape = _input.shape
            return _input.flatten()
    
        def back_propagation(self, error, lr=1):
            return np.resize(error, self.__input_shape)
    
    

    结果

    我使用了200张MNIST手写数据,以0.03的学习率训练了100轮之后,对测试集的40张进行了预测,(随便瞎写的模型)结果如下:

    epochs 1 / 100 loss : 1.6076663151543635
    epochs 2 / 100 loss : 1.51308051414868
    epochs 3 / 100 loss : 1.4435877198762985
    epochs 4 / 100 loss : 1.4170579907154772
    epochs 5 / 100 loss : 1.2782959961456577
    epochs 6 / 100 loss : 0.9999002367380303
    ···

    TODO

    卷积层常常需要搭配池化层进行数据的降维,所以下一篇会继续实现池化层和讲解Softmax与cross entropy。

    参考内容

    感谢以下博主的文章,感谢YJango的过程可视化图片。

    [1] 能否对卷积神经网络工作原理做一个直观的解释?-知乎
    [2] 【TensorFlow】一文弄懂CNN中的padding参数
    [3] 卷积神经网络(CNN)反向传播算法

    展开全文
  • Python实现卷积神经网络

    万次阅读 2018-01-16 22:16:04
    目前尚未完全写好,支持随机梯度下降和批量梯度下降,激活函数仅支持sigmoid,输出层为softmax,池化核仅支持average,权值初始化采用Xavier方法。今后会逐渐进行补充和.../usr/bin/env python3 # -*- coding: utf-8

    代码见https://github.com/rbtbecontinued/cnn

    目前尚未完全写好,支持随机梯度下降和批量梯度下降,激活函数仅支持sigmoid,输出层为softmax,池化核仅支持average,权值初始化采用Xavier方法。今后会逐渐进行补充和完善。

    下面提供一个简单的测试用例。

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    """
    Created on Mon Jan 22 20:37:37 2018
    
    @author: 行客Thinker
    """
    
    import numpy as np
    import matplotlib.pyplot as plt
    from convolutional_neural_network import cnn
    
    
    def config_net():
        """
        配置网络。
        """
        
        # 输入层
        size_input = np.array([8, 8])
        args_input = ("input", (size_input,))
        
        # C1卷积层
        connecting_matrix_C1 = np.ones([1, 2])
        size_conv_kernel_C1 = np.array([5, 5])
        stride_conv_kernel_C1 = 1
        padding_conv_C1 = 0
        type_activation_C1 = "sigmoid"
        args_C1 = ("convoluting", (connecting_matrix_C1, size_conv_kernel_C1, 
                                   stride_conv_kernel_C1, padding_conv_C1, 
                                   type_activation_C1))
        
        # S2池化层
        type_pooling_S2 = "average"
        size_pool_kernel_S2 = np.array([2, 2])
        stride_pool_kernel_S2 = 2
        padding_pool_S2 = 0
        type_activation_S2 = "sigmoid"
        args_S2 = ("pooling", (type_pooling_S2, size_pool_kernel_S2, 
                               stride_pool_kernel_S2, padding_pool_S2, 
                               type_activation_S2))
        
        # C3卷积层
        connecting_matrix_C3 = np.ones([2, 2])
        size_conv_kernel_C3 = np.array([2, 2])
        stride_conv_kernel_C3 = 1
        padding_conv_C3 = 0
        type_activation_C3 = "sigmoid"
        args_C3 = ("convoluting", (connecting_matrix_C3, size_conv_kernel_C3, 
                                   stride_conv_kernel_C3, padding_conv_C3, 
                                   type_activation_C3))
        
        # 输出层
        n_nodes_output = 2
        type_output = "softmax"
        args_output = ("output", (n_nodes_output, type_output))
        
        args = (args_input,
                args_C1,
                args_S2,
                args_C3,
                args_output)
        cnn_net = cnn()
        cnn_net.config(args)
        
        return cnn_net
    
    
    n_train = 10000
    X_train = 0.2 * np.random.randn(8, 8, n_train)
    Y_train = np.random.randint(2, size=n_train)
    for i in range(Y_train.shape[0]):
        if Y_train[i] == 0:
            X_train[1, :, i] += np.ones(8)
        elif Y_train[i] == 1:
            X_train[:, 1, i] += np.ones(8)
    
    size_batch = 50
    n_epochs = 500
    cnn_net = config_net()
    cnn_net.fit(X_train, Y_train, size_batch=size_batch, n_epochs=n_epochs)
    
    n_test = 1000
    X_test = 0.2 * np.random.randn(8, 8, n_test)
    Y_test = np.random.randint(2, size=n_test)
    for i in range(Y_test.shape[0]):
        if Y_test[i] == 0:
            X_test[1, :, i] += np.ones(8)
        elif Y_test[i] == 1:
            X_test[:, 1, i] += np.ones(8)
            
    correct_rate = cnn_net.test(X_test, Y_test)
    
    plt.figure()
    for i in range(cnn_net.layers[1].n_nodes):
        plt.subplot(1, 2, i + 1)
        plt.imshow(cnn_net.layers[1].nodes[i].conv_kernels[0], cmap="gray")
    plt.show()
    
    
    运行结果如下图所示:


    横轴为训练样本的批数,纵轴为网络对每批训练样本的损失。

    展开全文
  • python环境下tensorflow卷积神经网络cnn示例,手写数字,mnist数据集自动分类。
  • 深度学习-卷积神经网络(python3代码实现)

    万次阅读 多人点赞 2018-03-26 16:50:58
    卷积神经网络(上) 作者:Bossof537 写这个也不容易,小哥哥小姐姐转载请注明出处吧,感谢! 1、简介 卷积神经网络与常规的神经网络十分相似,它们都由可以对权重和偏置进行学习的神经元构成。每个神经元接收...
  • 请问哪里有3D卷积神经网络开源python代码吗?小白求学习?

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 49,408
精华内容 19,763
关键字:

卷积神经网络python

python 订阅