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

    万次阅读 多人点赞 2018-05-26 09:49:50
    二、CNN卷积神经网络 1、CNN的主要概述 2、CNN的一般结构 三、CNN卷积神经网络的应用 四、常见的CNN卷积神经网络 一、BP神经网络回顾 人工全连接神经网络 (1)每相邻两层之间的每个神经元之间都是有边...

    目录

    一、BP神经网络回顾

    二、CNN卷积神经网络

    1、CNN的主要概述

    2、CNN的一般结构

    三、CNN卷积神经网络的应用

    四、常见的CNN卷积神经网络


    一、BP神经网络回顾

     

    人工全连接神经网络

    (1)每相邻两层之间的每个神经元之间都是有边相连的

    (2)当输入层的特征维度变得很高时,这时全连接网络需要训练的参数就会增大很多,计算速度就会变得很慢

    传统神经网络存在的问题:

    (1)权值太多,计算量太大

    (2)权值太多,需要大量样本进行训练

    二、CNN卷积神经网络

    1、CNN的主要概述

    卷积层的神经元只与前一层的部分神经元节点相连,即它的神经元间的连接是非全连接的,且同一层中某些神经元之间的连接的权重w偏置b是共享的(即相同的)大量地减少了需要训练参数的数量

    CNN主要特点:减少权值,局部连接,权值共享

    CNN通过感受野权值共享减少了神经网络需要训练的参数的个数。

    2、CNN的一般结构

    • 输入层:用于数据的输入
    • 卷积层:使用卷积核进行特征提取特征映射
    • 激励层:由于卷积也是一种线性运算,因此需要增加非线性映射
    • 池化层:压缩数据和参数的量,减小过拟合。
    • 全连接层:通常在CNN的尾部进行重新拟合,减少特征信息的损失
    • 输出层:用于输出结果

    (1)输入层

    在CNN的输入层中,(图片)数据输入的格式与全连接神经网络的输入格式(一维向量)不太一样。CNN的输入层的输入格式保留了图片本身的结构

    对于黑白的 28×28 的图片,CNN的输入是一个28×28 的的二维神经元

    对于RGB格式的28×28图片,CNN的输入则是一个 3×28×28 的三维神经元(RGB中的每一个颜色通道都有一个 28×28 的矩阵),如下图所示:

    (2)卷积层

    需要明确的几个概念:

    感受视野( local receptive fields)

        即感受上一层的部分特征。在卷积神经网络中,隐藏层中的神经元的感受视野比较小,只能看到上一次的部分特征,上一层的其他特征可以通过平移感受视野来得到同一层的其他神经元。

    卷积核

        感受视野中的权重矩阵

    共享权值(shared weights)

    步长(stride)

        感受视野对输入的扫描间隔称为步长(stride

    边界扩充(pad)

         当步长比较大时(stride>1),为了扫描到边缘的一些特征,感受视野可能会“出界”,这时需要对边界扩充(pad)

    特征映射图(feature map)

         通过一个带有卷积核感受视野 扫描生成的下一层神经元矩阵 称为一个特征映射图(feature map

    通过以下图理解以上概念及卷积计算

    (3)激励层

    激励层主要对卷积层的输出进行一个非线性映射,因为卷积层的计算还是一种线性计算。使用的激励函数一般为ReLu函数

    卷积层和激励层通常合并在一起称为“卷积层”。

    (4)池化层

    当输入经过卷积层时,若感受视野比较小,布长stride比较小,得到的feature map(特征映射图)还是比较大,可以通过池化层来对每一个feature map进行降维操作,输出的深度还是不变的,依然为feature map的个数。

    池化层也有一个“池化视野(filter)”来对feature map矩阵进行扫描,对“池化视野”中的矩阵值进行计算,一般有两种计算方式:

       (1Max pooling:取“池化视野”矩阵中的最大值

       (2Average pooling:取“池化视野”矩阵中的平均值

    (5)全连接层和输出层

    全连接层主要对特征进行重新拟合,减少特征信息的丢失。

    输出层主要准备做好最后目标结果的输出。

    (6)中间还可以使用其他的功能层

    归一化层Batch Normalization):在CNN中对特征的归一化

    切分层:对某些(图片)数据的进行分区域的单独学习

    融合层:对独立进行特征学习的分支进行融合

    三、CNN卷积神经网络的应用

    1、目标分类 

    2、目标检测(目标定位、人脸检测、人形检测、车辆检测) 

        (R-CNN、Fast R-CNN、Faster R-CNN、YOLO、SSD、YOLO V2、YOLO V3)

    3、目标识别(人脸识别、人形识别)  人脸识别算法演化史    (DeepFace、FaceNet、DeepID)

    4、目标跟踪

    5、目标分割

    6、关键点检测     关键点检测

    CNN卷积神经网络实现Mnist数据集

    四、常见的CNN卷积神经网络

    • 卷积神经⽹络(LeNet)
    • 深度卷积神经⽹络(AlexNet
    • 使⽤重复元素的⽹络(VGG
    • ⽹络中的⽹络(NiN
    • 含并⾏连结的⽹络(GoogLeNet
    • 残差⽹络(ResNet
    • 稠密连接⽹络(DenseNet

     

     

     

     

     

     

     

     

     

     

    参考博客资料:

    1、深度学习之卷积神经网络CNN及tensorflow代码实现示例

    2、卷积神经网络CNN(基本理论)

    3、卷积神经网络CNN总结

     

     

     

    展开全文
  • 返回主目录 返回 CNN 卷积神经网络目录 ...深度篇—— CNN 卷积神经网络(一)细说 cnn 卷积神经网络 深度篇—— CNN 卷积神经网络(二)细说 池化(pooling) 与 反池化(unpooling) 深度篇—— CNN 卷积神经网络(三)关...

    返回主目录

    返回 CNN 卷积神经网络目录

    下一章:深度篇—— CNN 卷积神经网络(二)  细说 池化(pooling) 与 反池化(unpooling)

     

    目录内容

    深度篇—— CNN 卷积神经网络(一) 细说 cnn 卷积神经网络

    深度篇—— CNN 卷积神经网络(二) 细说 池化(pooling) 与 反池化(unpooling)

    深度篇—— CNN 卷积神经网络(三) 关于 ROI pooling 和 ROI Align 与 插值

    深度篇—— CNN 卷积神经网络(四) 使用 tf cnn 进行 mnist 手写数字 代码演示项目

     

    本小节,细说 CNN 卷积神经网络,下一小节细说 池化 与 反池化

     

    一. CNN 卷积神经网络

     

    1. CNN 卷积神经的理解

    CNN 卷积神经网络 (Convolution Neural Network, CNN) 是一种前馈神经网络,它的人工神经元可以响应一部分覆盖范围内的周围单元 ,对于大型图像处理有出色表现。卷积神经网络由一个或多个卷积层和顶端的全连接层组成,同时也包括关联权重和池化层。

    卷积神经,是依靠感受野(也就是滑动窗口)去提取特征进行学习 的。

    滑动窗口,就好像上图的圆圈一样,从 1 的位置,滑动到 2 的位置,再滑动到 3 的位置。滑动完一行之后,再移动都下一行去 滑动,然后,又滑动到 m 位置,再滑动到 n 的位置。这滑动窗口,就好像人的扫视一样。滑动窗口的大小,称为感受野,也就是一次能感受到多少信息量。

             

     

    2. CNN 的层级结构

    (1). CNN 依旧是层级网络

    (2). CNN层级的形式(运算) 与功能做了变化

    (3). 主要是以下层级

       ①. 数据输入层   (input layer)

       ②. 卷积计算层   (convolution layer)

       ③. 激励函数层   (activation layer)

       ④. 池化层   (pooling layer)

       ⑤. 全连接层   (full connected layer)

       ⑥. BN 层   (Batch Normalization layer)

       ⑦. 输出层   (output layer)

    (4). 层级一般依次:

        [(Conv \rightarrow act) \times m \rightarrow pooling(?)] \times n \rightarrow (fc \rightarrow act) \times k \rightarrow output

           

    3. CNN 卷积层 (convolution layer)

    (1). 卷积层是由多个特征面 (feature map) 组成的,每个特征面由多个神经元组成,它的每一个神经元通过卷积核(filter) 与上一层特征面的局部区域相连。

    (2). 卷积核 (filter) 是一个权值矩阵。(如对于二维而言,可为 \large 3 \times 3 或 \large 5 \times 5 的矩阵)

          在二维卷积函数 tf.nn.conv2d() 中会有参数 filter。

                filter = [filter_height, filter_width, in_channels, out_channels]

                filter_height:为卷积核的高

                filter_width:为卷积核的宽

                in_channels:为输入的通道数

                out_channels:为输出的通道数,即为当前卷积层的卷积核个数。

    (3). 卷积核滑动的步幅(stride,也称步长) 即卷积核每一次平移的距离;和 填充 (padding) 模式 即使用卷积时图像某部分做运算时,可能会有部分没有覆盖到(如边界),需要决定是否填充以及使用什么填充方式,是卷积层的两个重要参数。

       ①. 在二维卷积函数 tf.nn.conv2d(),最大池化函数 tf.nn.max_pool(),平均池化函数 tf.nn.avg_pool() 中会有参数 strides 和 padding。

       ②. strides = [b, h, w, c]

                b:为样本上的步长,默认为 1,也就是每个样本都会进行运算。

                h:为 filter 在高度上移动的步长,默认为 1,这个值可以自己设定,根据网络的结构合理调节。

                w:为 filter 在宽度上移动的步长,默认为 1,这个值可以自己设定,根据网络的结构合理调节。

                c:为在通道上移动的步长,默认值为 1,这个表示每一个通道都会进行移动和运算。

       ③. padding 模式

          a. padding = "SAME" 

              设置为 SAME 时,则卷积层在必要的时候使用零填充(默认为使用 零 填充,也有其他值填充方式)。在这种情况下,输出神经元数量等于输入神经元的数量除以步幅,结果向上取整 (如 13 / 5 = 3)。然后,在输入周围尽可能均匀地填充 零。(zero-padding:零 填充)

          b. padding = "VALID"

              设置为 VALID,卷积层就不使用填充,根据步幅可能会忽略输入图片中位于底部和右侧的一些 的数据。

    (4). 卷积的计算方式为内积和,即对应格子的数先相乘,再相加。

         zero-padding 并不是说,一定只填充 一圈 0,而是,根据卷积核的大小,和步幅 相关 来决定的。填充两圈、三圈 0 都是有可能的,看具体情况再确定。

        同样的,除了 零填充,还有 镜像填充、常数填充、重复填充 等等。

             滑动窗口,都是从左上角开始,然后,向右 和 向下 进行操作的。

    (5). 卷积核的理解

       ①. 卷积运算后,输出图像尺寸缩小(\large 1 \times 1 卷积核除外)

       ②. 越是边缘的像素点,对输出的影响越小,因为卷积运算在移动到边缘的时候就结束了。中间的像素有可能会参与多次运算,但是,边缘像素点可能只参与一次计算。所以,结果可能会丢失边缘信息。

       ③. 卷积层通过卷积操作提取输入的不同特征,第一层卷积提取低级特征(如 边缘、线条、角落等),更高层的卷积层提取更高级的特征。即:CNN 饿前几层是做边缘检测效果,后面的层有可能检测到物体的部分区域,更靠后的一些层可能检测 到完整的物体。

       ④. padding 的用途

          a. padding = "SAME" 可保持边界信息。否则的话,输入图像最边缘的像素点信息只会被卷积核操作一次,但是图像中间的像素点会被扫描到很多次,那就会在一定程度上降低了边界信息的参考程度。但是在加入了 padding = "SAME" 之后,在实际处理过程中就会从新的边界 进行操作,从而在一定程度上解决了这个问题。

          b. 可以 利用 padding = "SAME" 对输入尺寸有差异图像进行补齐,使得输入图像尺寸一致。

          c. padding = "SAME" 可以使得卷积层的输入维度和输出维度一致。

       ⑤. 卷积核权重参数共享。

     

    4. 反卷积

    (1). 反卷积又称转置卷积 (Transposed Convolution)

         可以看成是卷积层的前向传播过程就是反卷积的反向传播过程,卷积层的反向传播过程就是反卷积的前向传播过程。因为卷积层的前向、反向计算分别为 乘 \large w 和 \large w^{T} (\large y = wx),而反卷积的前向、反向计算分别为 乘 \large w^{T} 和 \large w,所以它们的前向传播和反向传播刚好交换过来。

    (2). 反卷积过程

       ①. 根据卷积核对 feature map 进行卷积,得到一个 new feature map。

       ②. 对 new feature map 进行非线性变化,得到所有整理后的 feature map。

       ③. 对整理后的 feature map 进行反卷积操作(如果卷积后有池化,就先反池化,再反卷积。池化和反池化,下一小节会说到)。

          如果 padding = "SAME" 的话,反卷积后的 feature map 大小,和刚开始 的 feature map 是一样大小的。但是,值会变大,图像的轮廓会更清晰。

    (3). 反卷积可视化各层得到的特征图作为输入,进行反卷积,得到反卷积结果,用以验证显示各层提取到的 feature map

     

    5. CNN 卷积神经的优缺点

    (1). 优点:

       ①. 共享卷积核,参数共享,使用稀疏连接。卷积神经网络通过这机制来减少参数,简化计算,节约内存资源,从而对高维数据处理无压力,即便使用更小的训练集来训练也能预防过拟合。

       ②. 无需手动选取特征,训练好权重,即得特征

       ③. 深层次的网络抽取图像信息丰富,表达效果好。

    (2). 缺点:

       ①. 需要调参,需要大量样本集,训练最好使用 GPU

       ②. 物理含义不好理解,虽然我们了解一些核函数,比如说高斯核,图像经过高斯核处理后,就会得到高斯图像。但是,有很多卷积核 我们并不了解。所以,很多时候,我们并无法解释。

     

                      

     

    返回主目录

    返回 CNN 卷积神经网络目录

    下一章:深度篇—— CNN 卷积神经网络(二)  细说 池化 与 反池化

    展开全文
  • CNN卷积神经网络的理论教程参考 http://blog.csdn.net/luanpeng825485697/article/details/79009241 加载样本数据集 首先我们要有手写体的数据集文件 t10k-images.idx3-ubyte t10k-labels.idx1-ubyte train-...

    分享一个朋友的人工智能教程。零基础!通俗易懂!风趣幽默!还带黄段子!大家可以看看是否对自己有帮助:点击打开

    ad1.jpg

    全栈工程师开发手册 (作者:栾鹏)
    python教程全解

    CNN卷积神经网络的理论教程参考
    http://blog.csdn.net/luanpeng825485697/article/details/79009241

    加载样本数据集

    首先我们要有手写体的数据集文件

    t10k-images.idx3-ubyte

    t10k-labels.idx1-ubyte

    train-images.idx3-ubyte

    train-labels.idx1-ubyte

    我们实现一个MNIST.py文件,专门用来读取手写体文件中的数据。

    # -*- coding: UTF-8 -*-
    
    # 获取手写数据。
    # 28*28的图片对象。每个图片对象根据需求是否转化为长度为784的横向量
    # 每个对象的标签为0-9的数字,one-hot编码成10维的向量
    import numpy as np
    
    # 数据加载器基类。派生出图片加载器和标签加载器
    class Loader(object):
        # 初始化加载器。path: 数据文件路径。count: 文件中的样本个数
        def __init__(self, path, count):
            self.path = path
            self.count = count
    
        # 读取文件内容
        def get_file_content(self):
            print(self.path)
            f = open(self.path, 'rb')
            content = f.read()  # 读取字节流
            f.close()
            return content  # 字节数组
    
        # 将unsigned byte字符转换为整数。python3中bytes的每个分量读取就会变成int
        # def to_int(self, byte):
        #     return struct.unpack('B', byte)[0]
    
    # 图像数据加载器
    class ImageLoader(Loader):
        # 内部函数,从文件字节数组中获取第index个图像数据。文件中包含所有样本图片的数据。
        def get_picture(self, content, index):
            start = index * 28 * 28 + 16  # 文件头16字节,后面每28*28个字节为一个图片数据
            picture = []
            for i in range(28):
                picture.append([])  # 图片添加一行像素
                for j in range(28):
                    byte1 = content[start + i * 28 + j]
                    picture[i].append(byte1)  # python3中本来就是int
                    # picture[i].append(self.to_int(byte1))  # 添加一行的每一个像素
            return picture   # 图片为[[x,x,x..][x,x,x...][x,x,x...][x,x,x...]]的列表
    
        # 将图像数据转化为784的行向量形式
        def get_one_sample(self, picture):
            sample = []
            for i in range(28):
                for j in range(28):
                    sample.append(picture[i][j])
            return sample
    
        # 加载数据文件,获得全部样本的输入向量。onerow表示是否将每张图片转化为行向量,to2表示是否转化为0,1矩阵
        def load(self,onerow=False):
            content = self.get_file_content()  # 获取文件字节数组
            data_set = []
            for index in range(self.count):  #遍历每一个样本
                onepic =self.get_picture(content, index)  # 从样本数据集中获取第index个样本的图片数据,返回的是二维数组
                if onerow: onepic = self.get_one_sample(onepic)  # 将图像转化为一维向量形式
                data_set.append(onepic)
            return data_set
    
    # 标签数据加载器
    class LabelLoader(Loader):
        # 加载数据文件,获得全部样本的标签向量
        def load(self):
            content = self.get_file_content()   # 获取文件字节数组
            labels = []
            for index in range(self.count):  #遍历每一个样本
                onelabel = content[index + 8]   # 文件头有8个字节
                onelabelvec = self.norm(onelabel) #one-hot编码
                labels.append(onelabelvec)
            return labels
    
        # 内部函数,one-hot编码。将一个值转换为10维标签向量
        def norm(self, label):
            label_vec = []
            # label_value = self.to_int(label)
            label_value = label  # python3中直接就是int
            for i in range(10):
                if i == label_value:
                    label_vec.append(1)
                else:
                    label_vec.append(0)
            return label_vec
    
    # 获得训练数据集。onerow表示是否将每张图片转化为行向量
    def get_training_data_set(num,onerow=False):
        image_loader = ImageLoader('train-images.idx3-ubyte', num)  # 参数为文件路径和加载的样本数量
        label_loader = LabelLoader('train-labels.idx1-ubyte', num)  # 参数为文件路径和加载的样本数量
        return image_loader.load(onerow), label_loader.load()
    
    # 获得测试数据集。onerow表示是否将每张图片转化为行向量
    def get_test_data_set(num,onerow=False):
        image_loader = ImageLoader('t10k-images.idx3-ubyte', num)  # 参数为文件路径和加载的样本数量
        label_loader = LabelLoader('t10k-labels.idx1-ubyte', num)  # 参数为文件路径和加载的样本数量
        return image_loader.load(onerow), label_loader.load()
    
    
    # 将一行784的行向量,打印成图形的样式
    def printimg(onepic):
        onepic=onepic.reshape(28,28)
        for i in range(28):
            for j in range(28):
                if onepic[i,j]==0: print('  ',end='')
                else: print('* ',end='')
            print('')
    
    
    if __name__=="__main__":
        train_data_set, train_labels = get_training_data_set(100)  # 加载训练样本数据集,和one-hot编码后的样本标签数据集
        train_data_set = np.array(train_data_set)       #.astype(bool).astype(int)    #可以将图片简化为黑白图片
        train_labels = np.array(train_labels)
        onepic = train_data_set[12]  # 取一个样本
        printimg(onepic)  # 打印出来这一行所显示的图片
        print(train_labels[12].argmax())  # 打印样本标签
    
    
    

    我们尝试运行一下。读取第13个样本的内容。

    可以看到打印输出样式如下。

    这里写图片描述

    激活器模块

    CNN卷积神经网络并不是只有卷积层,还有采样层和全连接层。在卷积层和全连接层都有激活函数。并且在前向预测和后项传播中需要计算激活函数的前向预测影响,以及误差后项传播影响。所以我们将所有的激活函数形成了一个独立的模块来实现。下面的代码存储为Activators.py

    # 1. 当为array的时候,默认d*f就是对应元素的乘积,multiply也是对应元素的乘积,dot(d,f)会转化为矩阵的乘积, dot点乘意味着相加,而multiply只是对应元素相乘,不相加
    # 2. 当为mat的时候,默认d*f就是矩阵的乘积,multiply转化为对应元素的乘积,dot(d,f)为矩阵的乘积
    
    import numpy as np
    
    # rule激活器
    class ReluActivator(object):
        def forward(self, weighted_input):    # 前向计算,计算输出
            return max(0, weighted_input)
    
        def backward(self, output):  # 后向计算,计算导数
            return 1 if output > 0 else 0
    
    # IdentityActivator激活器.f(x)=x
    class IdentityActivator(object):
        def forward(self, weighted_input):   # 前向计算,计算输出
            return weighted_input
    
        def backward(self, output):   # 后向计算,计算导数
            return 1
    
    #Sigmoid激活器
    class SigmoidActivator(object):
        def forward(self, weighted_input):
            return 1.0 / (1.0 + np.exp(-weighted_input))
    
        def backward(self, output):
            # return output * (1 - output)
            return np.multiply(output, (1 - output))  # 对应元素相乘
    
    # tanh激活器
    class TanhActivator(object):
        def forward(self, weighted_input):
            return 2.0 / (1.0 + np.exp(-2 * weighted_input)) - 1.0
    
        def backward(self, output):
            return 1 - output * output
    
    # # softmax激活器
    # class SoftmaxActivator(object):
    #     def forward(self, weighted_input):  # 前向计算,计算输出
    #         return max(0, weighted_input)
    #
    #     def backward(self, output):  # 后向计算,计算导数
    #         return 1 if output > 0 else 0
    
    

    DNN全连接网络层的实现

    将下面的代码存储为DNN.py

    # 实现神经网络反向传播算法,以此来训练网络。全连接神经网络可以包含多层,但是只有最后一层输出前有激活函数。
    # 所谓向量化编程,就是使用矩阵运算。
    
    import random
    import math
    import numpy as np
    import datetime
    import Activators  # 引入激活器模块
    
    # 1. 当为array的时候,默认d*f就是对应元素的乘积,multiply也是对应元素的乘积,dot(d,f)会转化为矩阵的乘积, dot点乘意味着相加,而multiply只是对应元素相乘,不相加
    # 2. 当为mat的时候,默认d*f就是矩阵的乘积,multiply转化为对应元素的乘积,dot(d,f)为矩阵的乘积
    
    
    
    # 全连接每层的实现类。输入对象x、神经层输出a、输出y均为列向量
    class FullConnectedLayer(object):
        # 全连接层构造函数。input_size: 本层输入列向量的维度。output_size: 本层输出列向量的维度。activator: 激活函数
        def __init__(self, input_size, output_size,activator,learning_rate):
            self.input_size = input_size
            self.output_size = output_size
            self.activator = activator
            # 权重数组W。初始化权重为(rand(output_size, input_size) - 0.5) * 2 * sqrt(6 / (output_size + input_size))
            wimin = (output_size - 0.5) * 2 * math.sqrt(6 / (output_size + input_size))
            wimax = (input_size-0.5)*2*math.sqrt(6/(output_size + input_size))
            # self.W = np.random.uniform(wimin,wimax,(output_size, input_size))  #初始化为-0.1~0.1之间的数。权重的大小。行数=输出个数,列数=输入个数。a=w*x,a和x都是列向量
            self.W = np.random.uniform(-0.1, 0.1,(output_size, input_size))  # 初始化为-0.1~0.1之间的数。权重的大小。行数=输出个数,列数=输入个数。a=w*x,a和x都是列向量
            # 偏置项b
            self.b = np.zeros((output_size, 1))  # 全0列向量偏重项
            # 学习速率
            self.learning_rate = learning_rate
            # 输出向量
            self.output = np.zeros((output_size, 1)) #初始化为全0列向量
    
        # 前向计算,预测输出。input_array: 输入列向量,维度必须等于input_size
        def forward(self, input_array):   # 式2
            self.input = input_array
            self.output = self.activator.forward(np.dot(self.W, input_array) + self.b)
    
        # 反向计算W和b的梯度。delta_array: 从上一层传递过来的误差项。列向量
        def backward(self, delta_array):
            # 式8
            self.delta = np.multiply(self.activator.backward(self.input),np.dot(self.W.T, delta_array))   #计算当前层的误差,以备上一层使用
            self.W_grad = np.dot(delta_array, self.input.T)   # 计算w的梯度。梯度=误差.*输入
            self.b_grad = delta_array  #计算b的梯度
    
        # 使用梯度下降算法更新权重
        def update(self):
            self.W += self.learning_rate * self.W_grad
            self.b += self.learning_rate * self.b_grad
    
    

    CNN卷积网络层和Pool降采样层的实现

    将下面的代码存储为CNN.py

    import numpy as np
    import Activators   # 引入自定义的激活器模块
    import math
    
    # 获取卷积区域。input_array为单通道或多通道的矩阵顺。i为横向偏移,j为纵向偏移,stride为步幅,filter_width为过滤器宽度,filter_height为过滤器的高度
    def get_patch(input_array, i, j, filter_width,filter_height, stride):
        '''
        从输入数组中获取本次卷积的区域,
        自动适配输入为2D和3D的情况
        '''
        start_i = i * stride
        start_j = j * stride
        if input_array.ndim == 2:  #如果只有一个通道
            return input_array[start_i:start_i + filter_height,start_j: start_j + filter_width]
        elif input_array.ndim == 3:  #如果有多个通道,也就是深度上全选
            return input_array[:,start_i: start_i + filter_height,start_j: start_j + filter_width]
    
    
    # 获取一个2D区域的最大值所在的索引
    def get_max_index(array):
        location = np.where(array == np.max(array))
        return location[0], location[1]
    
    
    # 计算一个过滤器的卷积运算,输出一个二维数据。每个通道的输入是图片,但是可能不是一个通道,所以这里自动适配输入为2D和3D的情况。
    def conv(input_array,kernel_array,output_array,stride, bias):
        output_width = output_array.shape[1]   # 获取输出的宽度。一个过滤器产生的输出一定是一个通道
        output_height = output_array.shape[0] # 获取输出的高度
        kernel_width = kernel_array.shape[-1]  # 过滤器的宽度。有可能有多个通道。多通道时shape=[深度、高度、宽度],单通道时shape=[高度、宽度]
        kernel_height = kernel_array.shape[-2] # 过滤器的高度。有可能有多个通道。多通道时shape=[深度、高度、宽度],单通道时shape=[高度、宽度]
        for i in range(output_height):
            for j in range(output_width):
                juanjiqu = get_patch(input_array, i, j, kernel_width,kernel_height, stride)   # 获取输入的卷积区。(单通道或多通道)
                # 这里是对每个通道的两个矩阵对应元素相乘求和,再将每个通道的和值求和
                kernel_values= (np.multiply(juanjiqu,kernel_array)).sum() # 卷积区与过滤器卷积运算。1,一个通道内,卷积区矩阵与过滤器矩阵对应点相乘后,求和值。2、将每个通道的和值再求和。
                output_array[i][j] = kernel_values + bias  #将卷积结果加上偏量
    
    
    
    # 为数组增加Zero padding。zp步长,自动适配输入为2D和3D的情况
    def padding(input_array, zp):
        if zp == 0: # 如果不补0
            return input_array
        else:
            if input_array.ndim == 3:   # 如果输入有多个通道
                input_width = input_array.shape[2]  # 获取输入的宽度
                input_height = input_array.shape[1]  # 获取输入的宽度
                input_depth = input_array.shape[0]  # 获取输入的深度
                padded_array = np.zeros((input_depth,input_height + 2 * zp,input_width + 2 * zp))  # 先定义一个补0后大小的全0矩阵
                padded_array[:,zp: zp + input_height,zp: zp + input_width] = input_array # 每个通道上,将中间部分替换成输入,这样就变成了原矩阵周围补0 的形式
                return padded_array
            elif input_array.ndim == 2:  # 如果输入只有一个通道
                input_width = input_array.shape[1] # 获取输入的宽度
                input_height = input_array.shape[0] # 虎丘输入的高度
                padded_array = np.zeros((input_height + 2 * zp,input_width + 2 * zp))  # 先定义一个补0后大小的全0矩阵
                padded_array[zp: zp + input_height,zp: zp + input_width] = input_array  # 将中间部分替换成输入,这样就变成了原矩阵周围补0 的形式
                return padded_array
    
    
    # 对numpy数组进行逐个元素的操作。op为函数。element_wise_op函数实现了对numpy数组进行按元素操作,并将返回值写回到数组中
    def element_wise_op(array, op):
        for i in np.nditer(array,op_flags=['readwrite']):
            i[...] = op(i)   # 将元素i传入op函数,返回值,再修改i
    
    # Filter类保存了卷积层的参数以及梯度,并且实现了用梯度下降算法来更新参数。
    class Filter(object):
        def __init__(self, width, height, depth,filter_num):
            # 卷积核每个元素初始化为[-sqrt(6 / (fan_in + fan_out)), sqrt(6 / (fan_in + fan_out))]。
            # 其中fan_in为输入通道数与滤波器宽高的乘机,即width*height*depth
            # 其中fan_out为输出通道数与滤波器宽高的乘机,即width*height*filter_num
            wimin = -math.sqrt(6 / (width*height*depth + width*height*filter_num))
            wimax = -wimin
            self.weights = np.random.uniform(wimin, wimax, (depth, height, width))  # 随机初始化卷基层权重一个很小的值,
            # self.weights = np.random.uniform(-1e-2, 1e-2,(depth, height, width))  # 随机初始化卷基层权重一个很小的值,
            self.bias = 0  # 初始化偏量为0
            self.weights_grad = np.zeros(self.weights.shape)   # 初始化权重梯度
            self.bias_grad = 0  # 初始化偏量梯度
    
        def __repr__(self):
            return 'filter weights:\n%s\nbias:\n%s' % (repr(self.weights), repr(self.bias))
    
        # 读取权重
        def get_weights(self):
            return self.weights
    
        # 读取偏量
        def get_bias(self):
            return self.bias
    
        # 更新权重和偏量
        def update(self, learning_rate):
            self.weights -= learning_rate * self.weights_grad
            self.bias -= learning_rate * self.bias_grad
    
    # 用ConvLayer类来实现一个卷积层。下面的代码是初始化一个卷积层,可以在构造函数中设置卷积层的超参数
    class ConvLayer(object):
        #初始化构造卷积层:输入宽度、输入高度、通道数、滤波器宽度、滤波器高度、滤波器数目、补零数目、步长、激活器、学习速率
        def __init__(self, input_width, input_height,channel_number, filter_width,filter_height, filter_number,
                     zero_padding, stride, activator,learning_rate):
            self.input_width = input_width   #  输入宽度
            self.input_height = input_height  # 输入高度
            self.channel_number = channel_number  # 通道数=输入的深度=过滤器的深度
            self.filter_width = filter_width  # 过滤器的宽度
            self.filter_height = filter_height  # 过滤器的高度
            self.filter_number = filter_number  # 过滤器的数量。
            self.zero_padding = zero_padding  # 补0圈数
            self.stride = stride # 步幅
            self.output_width = int(ConvLayer.calculate_output_size(self.input_width, filter_width, zero_padding,stride))  # 计算输出宽度
            self.output_height = int(ConvLayer.calculate_output_size(self.input_height, filter_height, zero_padding,stride))  # 计算输出高度
            self.output_array = np.zeros((self.filter_number,self.output_height, self.output_width)) # 创建输出三维数组。每个过滤器都产生一个二维数组的输出
            self.filters = []   # 卷积层的每个过滤器
            for i in range(filter_number):
                self.filters.append(Filter(filter_width,filter_height, self.channel_number,filter_number))
            self.activator = activator   # 使用rule激活器
            self.learning_rate = learning_rate  # 学习速率
    
        # 计算卷积层的输出。输出结果保存在self.output_array
        def forward(self, input_array):
            self.input_array = input_array  # 多个通道的图片,每个通道为一个二维图片
            self.padded_input_array = padding(input_array,self.zero_padding)  # 先将输入补足0
            for i in range(self.filter_number):  #每个过滤器都产生一个二维数组的输出
                filter = self.filters[i]
                conv(self.padded_input_array,filter.get_weights(), self.output_array[i],self.stride, filter.get_bias())
            # element_wise_op函数实现了对numpy数组进行按元素操作,并将返回值写回到数组中
            element_wise_op(self.output_array,self.activator.forward)
    
        # 后向传播。input_array为该层的输入,sensitivity_array为当前层的输出误差(和输出的维度相同),activator为激活函数
        def backward(self, input_array, sensitivity_array,activator):
    
            '''
            计算传递给前一层的误差项,以及计算每个权重的梯度
            前一层的误差项保存在self.delta_array
            梯度保存在Filter对象的weights_grad
            '''
    
            self.forward(input_array)   # 先根据输入计算经过该卷积层后的输出。(卷积层有几个过滤器,输出层的深度就是多少。输出每一层为一个二维数组)
            self.bp_sensitivity_map(sensitivity_array, activator)   # 将误差传递到前一层,self.delta_array存储上一次层的误差
            self.bp_gradient(sensitivity_array)   # 计算每个过滤器的w和b梯度
    
        # 按照梯度下降,更新权重
        def update(self):
            for filter in self.filters:
                filter.update(self.learning_rate)   # 每个过滤器
    
    
        # 将误差项传递到上一层。sensitivity_array: 本层的误差。activator: 上一层的激活函数
        def bp_sensitivity_map(self, sensitivity_array,activator):   # 公式9
            # 根据卷积步长,对原始sensitivity map进行补0扩展,扩展成如果步长为1的输出误差形状。再用公式8求解
            expanded_error_array = self.expand_sensitivity_map(sensitivity_array)
            # print(sensitivity_array)
            # full卷积,对sensitivitiy map进行zero padding
            # 虽然原始输入的zero padding单元也会获得残差,但这个残差不需要继续向上传递,因此就不计算了
            expanded_width = expanded_error_array.shape[2]   # 误差的宽度
            zp = int((self.input_width + self.filter_width - 1 - expanded_width) / 2)   # 计算步长
            padded_array = padding(expanded_error_array, zp)  #补0操作
            # 初始化delta_array,用于保存传递到上一层的sensitivity map
            self.delta_array = self.create_delta_array()
            # 对于具有多个filter的卷积层来说,最终传递到上一层的sensitivity map相当于所有的filter的sensitivity map之和
            for i in range(self.filter_number):   # 遍历每一个过滤器。每个过滤器都产生多通道的误差,多个多通道的误差叠加
                filter = self.filters[i]
                # 将滤波器每个通道的权重权重翻转180度。
                flipped_weights=[]
                for oneweight in filter.get_weights():  # 这一个滤波器下的每个通道都进行180翻转
                    flipped_weights.append(np.rot90(oneweight, 2))
                flipped_weights = np.array(flipped_weights)
                # 计算与一个filter对应的delta_array
                delta_array = self.create_delta_array()
                for d in range(delta_array.shape[0]):   # 计算每个通道上的误差,存储在delta_array的对应通道上
                    # print('大小:\n',flipped_weights[d])
                    conv(padded_array[i], flipped_weights[d],delta_array[d], 1, 0)
                self.delta_array += delta_array   # 将每个滤波器每个通道产生的误差叠加
    
            # 将计算结果与激活函数的偏导数做element-wise乘法操作
            derivative_array = np.array(self.input_array)  # 复制一个矩阵,因为下面的会改变元素的值,所以深复制了一个矩阵
            element_wise_op(derivative_array,activator.backward)  # 逐个元素求偏导数。
            self.delta_array *= derivative_array  # 误差乘以偏导数。得到上一层的误差
    
        # 计算梯度。根据误差值,计算本层每个过滤器的w和b的梯度
        def bp_gradient(self, sensitivity_array):
            # 处理卷积步长,对原始sensitivity map进行扩展,扩展成如果步长为1的输出误差形状。再用公式8求解
            expanded_error_array = self.expand_sensitivity_map(sensitivity_array)
            for i in range(self.filter_number):  # 每个过滤器产生一个输出
                # 计算每个权重的梯度
                filter = self.filters[i]
                for d in range(filter.weights.shape[0]):   # 过滤器的每个通道都要计算梯度
                    conv(self.padded_input_array[d],expanded_error_array[i],filter.weights_grad[d], 1, 0)   #  公式(31、32中间)
    
                # 计算偏置项的梯度
                filter.bias_grad = expanded_error_array[i].sum()   # 公式(34)
    
        # 对步长为S的sensitivitymap相应的位置进行补0,将其『还原』成步长为1时的sensitivitymap,再用式8进行求解
        def expand_sensitivity_map(self, sensitivity_array):
            depth = sensitivity_array.shape[0]   # 获取误差项的深度
            # 确定扩展后sensitivity map的大小,即计算stride为1时sensitivity map的大小
            expanded_width = (self.input_width - self.filter_width + 2 * self.zero_padding + 1)
            expanded_height = (self.input_height - self.filter_height + 2 * self.zero_padding + 1)
            # 构建新的sensitivity_map
            expand_array = np.zeros((depth, expanded_height, expanded_width))
            # 从原始sensitivity map拷贝误差值,每有拷贝的位置,就是要填充的0
            for i in range(self.output_height):
                for j in range(self.output_width):
                    i_pos = i * self.stride
                    j_pos = j * self.stride
                    expand_array[:, i_pos, j_pos] = sensitivity_array[:, i, j]
            return expand_array
    
        # 创建用来保存传递到上一层的sensitivity map的数组。(上一层的输出也就是这一层的输入。所以上一层的误差项的维度和这一层的输入的维度相同)
        def create_delta_array(self):
            return np.zeros((self.channel_number,self.input_height, self.input_width))
    
    
        # 确定卷积层输出的大小
        @staticmethod
        def calculate_output_size(input_size,filter_size, zero_padding, stride):
            return (input_size - filter_size + 2 * zero_padding) / stride + 1
    
    
    
    # Max Pooling层的实现。就是一个卷积区域取最大值,形成输出。除了Max Pooing之外,常用的还有Mean Pooling——取各样本的平均值。
    # 采样层并不改变输入的通道数,也不补零,只是通过某种卷积方式实现降采样
    class MaxPoolingLayer(object):
        # 构造降采样层,参数为输入宽度、高度、通道数、滤波器宽度、滤波器高度、步长
        def __init__(self, input_width, input_height,channel_number, filter_width,filter_height, stride):
            self.input_width = input_width
            self.input_height = input_height
            self.channel_number = channel_number
            self.filter_width = filter_width
            self.filter_height = filter_height
            self.stride = stride
            self.output_width = int((input_width -filter_width) / self.stride + 1)
            self.output_height = int((input_height -filter_height) / self.stride + 1)
            self.output_array = np.zeros((self.channel_number,self.output_height, self.output_width))
    
        # 前向计算。
        def forward(self, input_array):
            for d in range(self.channel_number):
                for i in range(self.output_height):
                    for j in range(self.output_width):
                        self.output_array[d, i, j] = (get_patch(input_array[d], i, j,self.filter_width,self.filter_height,self.stride).max())   # 获取卷积区后去最大值
    
        # 后向传播误差
        def backward(self, input_array, sensitivity_array):
            self.delta_array = np.zeros(input_array.shape)
            for d in range(self.channel_number):
                for i in range(self.output_height):
                    for j in range(self.output_width):
                        patch_array = get_patch(input_array[d], i, j,self.filter_width,self.filter_height,self.stride)  # 获取卷积区
                        k, l = get_max_index(patch_array)  # 获取最大值的位置
                        self.delta_array[d,i * self.stride + k,j * self.stride + l] = sensitivity_array[d, i, j]   # 更新误差
    
    
    
    
    

    CNN识别MNIST数据

    有了上面的MNIST数据采集模块,DNN模块,CNN模块,就可以来构建一个包含多层的卷积神经网络实现手写体识别了。

    数据集:MNIST数据集,60000张训练图像,10000张测试图像,每张图像size为28*28。每张像素值包含灰度值,即一个0-255之间的整数,而没有rgb三种颜色的值。其实我们可以进一步将像素灰度值,改为黑白像素。每个像素的值为0或1。这样更加方便。

    网络层级结构示意图如下:

    这里写图片描述

    网络层级结构概述:5层神经网络

    输入层: 输入数据为原始训练图像(我们在实现中将2828的灰度图片,改成了2828的黑白图片)

    第一卷积层:6个55的过滤器(卷积核),步长Stride为1,不补0,激活函数为sigmoid。在这一层,输入为2828,深度为1的图片数据,输出为24*24的,深度为6的图片数据。

    第一采样层:过滤器(卷积核)为22,步长Stride为2。在这一层,输入为2424的,深度为6的图片数据,输出为12*12的,深度为6的图片数据。

    第二卷积层:12个55的过滤器(卷积核),步长Stride为1,不补0,激活函数为sigmoid。在这一层,输入为1212,深度为6的图片数据,输出为8*8的,深度为12的图片数据。

    第二采样层:过滤器(卷积核)为22,步长Stride为2。在这一层,输入为88的,深度为12的图片数据,输出为4*4的,深度为12的图片数据。

    输出层:线性函数输入为宽高为(4412,1)的列向量,输出为10维列向量,激活函数为sigmoid。

    代码流程概述:

    (1)获取训练数据和测试数据;

    (2)定义网络层级结构;

    (3)初始设置网络参数(权重W,偏向b)

    (4)训练网络的超参数的定义(学习率,每次迭代中训练的样本数目,迭代次数)

    (5)网络训练——前向运算

    (6)网络训练——反向传播

    (7)网络训练——参数更新

    (8)重复(5)(6)(7),直至满足迭代次数

    (9)网络模型测试

    # 使用全连接神经网络类,和手写数据加载器,实现验证码识别。
    
    import datetime
    import numpy as np
    import Activators  # 引入激活器模块
    import CNN   # 引入卷积神经网络
    import MNIST  # 引入手写数据加载器
    import DNN  # 引入全连接神经网络
    
    # 网络模型类
    class MNISTNetwork():
        # =============================构造网络结构=============================
        def __init__(self):
            # 初始化构造卷积层:输入宽度、输入高度、通道数、滤波器宽度、滤波器高度、滤波器数目、补零数目、步长、激活器、学习速率
            self.cl1 = CNN.ConvLayer(28, 28, 1, 5, 5, 6, 0, 1, Activators.SigmoidActivator(),0.02)  # 输入28*28 一通道,滤波器5*5的6个,步长为1,不补零,所以输出为24*24深度6
            # 构造降采样层,参数为输入宽度、高度、通道数、滤波器宽度、滤波器高度、步长
            self.pl1 = CNN.MaxPoolingLayer(24, 24, 6, 2, 2, 2)  # 输入24*24,6通道,滤波器2*2,步长为2,所以输出为12*12,深度保持不变为6
            # 初始化构造卷积层:输入宽度、输入高度、通道数、滤波器宽度、滤波器高度、滤波器数目、补零数目、步长、激活器、学习速率
            self.cl2 = CNN.ConvLayer(12, 12, 6, 5, 5, 12, 0, 1, Activators.SigmoidActivator(),0.02)  # 输入12*12,6通道,滤波器5*5的12个,步长为1,不补零,所以输出为8*8深度12
            # 构造降采样层,参数为输入宽度、高度、通道数、滤波器宽度、滤波器高度、步长
            self.pl2 = CNN.MaxPoolingLayer(8, 8, 12, 2, 2, 2)  # 输入8*8,12通道,滤波器2*2,步长为2,所以输出为4*4,深度保持不变为12。共192个像素
            # 全连接层构造函数。input_size: 本层输入向量的维度。output_size: 本层输出向量的维度。activator: 激活函数
            self.fl1 = DNN.FullConnectedLayer(192, 10, Activators.SigmoidActivator(),0.02)  # 输入192个像素,输出为10种分类概率,学习速率为0.05
    
        # 根据输入计算一次输出。因为卷积层要求的数据要求有通道数,所以onepic是一个包含深度,高度,宽度的多维矩阵
        def forward(self,onepic):
            # print('图片:',onepic.shape)
            self.cl1.forward(onepic)
            # print('第一层卷积结果:',self.cl1.output_array.shape)
            self.pl1.forward(self.cl1.output_array)
            # print('第一层采样结果:',self.pl1.output_array.shape)
            self.cl2.forward(self.pl1.output_array)
            # print('第二层卷积结果:',self.cl2.output_array.shape)
            self.pl2.forward(self.cl2.output_array)
            # print('第二层采样结果:',self.pl2.output_array.shape)
            flinput = self.pl2.output_array.flatten().reshape(-1, 1)  # 转化为列向量
            self.fl1.forward(flinput)
            # print('全连接层结果:',self.fl1.output.shape)
            return  self.fl1.output
    
        def backward(self,onepic,labels):
            # 计算误差
            delta = np.multiply(self.fl1.activator.backward(self.fl1.output), (labels - self.fl1.output))  # 计算输出层激活函数前的误差
            # print('输出误差:',delta.shape)
    
            # 反向传播
            self.fl1.backward(delta)  # 计算了全连接层输入前的误差,以及全连接的w和b的梯度
            self.fl1.update()  # 更新权重w和偏量b
            # print('全连接层输入误差:', self.fl1.delta.shape)
            sensitivity_array = self.fl1.delta.reshape(self.pl2.output_array.shape)  # 将误差转化为同等形状
            self.pl2.backward(self.cl2.output_array, sensitivity_array)  # 计算第二采样层的输入误差。参数为第二采样层的 1、输入,2、输出误差
            # print('第二采样层的输入误差:', self.pl2.delta_array.shape)
            self.cl2.backward(self.pl1.output_array, self.pl2.delta_array,Activators.SigmoidActivator())  # 计算第二卷积层的输入误差。参数为第二卷积层的 1、输入,2、输出误差,3、激活函数
            self.cl2.update()  # 更新权重w和偏量b
            # print('第二卷积层的输入误差:', self.cl2.delta_array.shape)
            self.pl1.backward(self.cl1.output_array, self.cl2.delta_array)  # 计算第一采样层的输入误差。参数为第一采样层的 1、输入,2、输出误差
            # print('第一采样层的输入误差:', self.pl1.delta_array.shape)
            self.cl1.backward(onepic, self.pl1.delta_array,Activators.SigmoidActivator())  # 计算第一卷积层的输入误差。参数为第一卷积层的 1、输入,2、输出误差,3、激活函数
            self.cl1.update()  # 更新权重w和偏量b
            # print('第一卷积层的输入误差:', self.cl1.delta_array.shape)
    
    
    
    # 由于使用了逻辑回归函数,所以只能进行分类识别。识别ont-hot编码的结果
    if __name__ == '__main__':
    
        # =============================加载数据集=============================
        train_data_set, train_labels = MNIST.get_training_data_set(600, False)  # 加载训练样本数据集,和one-hot编码后的样本标签数据集。样本数量越大,训练时间越久,也越准确
        test_data_set, test_labels = MNIST.get_test_data_set(100, False)  # 加载测试特征数据集,和one-hot编码后的测试标签数据集。训练时间越久,也越准确
        train_data_set = np.array(train_data_set).astype(bool).astype(int)    #可以将图片简化为黑白图片
        train_labels = np.array(train_labels)
        test_data_set = np.array(test_data_set).astype(bool).astype(int)    #可以将图片简化为黑白图片
        test_labels = np.array(test_labels)
        print('样本数据集的个数:%d' % len(train_data_set))
        print('测试数据集的个数:%d' % len(test_data_set))
    
    
        # =============================构造网络结构=============================
        mynetwork =MNISTNetwork()
    
        # 打印输出每层网络
        # print('第一卷积层:\n',mynetwork.cl1.filters)
        # print('第二卷积层:\n', mynetwork.cl2.filters)
        # print('全连接层w:\n', mynetwork.fl1.W)
        # print('全连接层b:\n', mynetwork.fl1.b)
    
        # =============================迭代训练=============================
        for i in range(10):  #迭代训练10次。每个迭代内,对所有训练数据进行训练,更新(训练图像个数/batchsize)次网络参数
            print('迭代:',i)
            for k in range(train_data_set.shape[0]):  #使用每一个样本进行训练
                # 正向计算
                onepic =train_data_set[k]
                onepic = np.array([onepic])  # 卷积神经网络要求的输入必须包含深度、高度、宽度三个维度。
                result = mynetwork.forward(onepic)   # 前向计算一次
                # print(result.flatten())
                labels = train_labels[k].reshape(-1, 1)  # 获取样本输出,转化为列向量
                mynetwork.backward(onepic,labels)
    
    
    
        # 打印输出每层网络
        # print('第一卷积层:\n',mynetwork.cl1.filters)
        # print('第二卷积层:\n', mynetwork.cl2.filters)
        # print('全连接层w:\n', mynetwork.fl1.W)
        # print('全连接层b:\n', mynetwork.fl1.b)
    
        # =============================评估结果=============================
       
        right = 0
        for k in range(test_data_set.shape[0]):  # 使用每一个样本进行训练
            # 正向计算
            onepic = test_data_set[k]
            onepic = np.array([onepic])  # 卷积神经网络要求的输入必须包含深度、高度、宽度三个维度。
            result = mynetwork.forward(onepic)  # 前向计算一次
            labels = test_labels[k].reshape(-1, 1)  # 获取样本输出,转化为列向量
            # print(result)
            pred_type = result.argmax()
            real_type = labels.argmax()
    
            # print(pred_type,real_type)
            if pred_type==real_type:
                right+=1
    
    
        print('%s after right ratio is %f' % (datetime.datetime.now(), right/test_data_set.shape[0]))  # 打印输出正确率
    
    

    注意由于使用的样本和迭代次数少,所以训练模型在训练集上表现不错,但是在测试集上表现不好。

    由于读者训练时间太久,所以本文已经将训练集数量改成了600,测试集数量改成了100。这样大概在半个小时内就可以完成训练。但是测试集的效果不好,读者可以自行增加样本数量,和迭代次数、或者在训练集上进行评估(虽然这只是自欺欺人)。

    可以参考keras实现图像识别:https://blog.csdn.net/luanpeng825485697/article/details/79144616

    展开全文
  • 深度学习 CNN卷积神经网络 LeNet-5详解

    万次阅读 多人点赞 2017-10-18 16:04:35
    卷积神经网络( Convolutional Neural Network, CNN): 是一种常见的深度学习架构,受生物自然视觉认知机制(动物视觉皮层细胞负责检测光学信号)启发而来,是一种特殊的多层前馈神经网络。它的人工神经元可以响应...

    文章首发于公众号【编程求职指南】
    卷积神经网络( Convolutional Neural Network, CNN):
    是一种常见的深度学习架构,受生物自然视觉认知机制(动物视觉皮层细胞负责检测光学信号)启发而来,是一种特殊的多层前馈神经网络。它的人工神经元可以响应一部分覆盖范围内的周围单元,对于大型图像处理有出色表现。
    一般神经网络VS卷积神经网络:
    相同点:卷积神经网络也使用一种反向传播算法(BP)来进行训练
    不同点:网络结构不同。卷积神经网络的网络连接具有局部连接、参数共享的特点。
    局部连接:是相对于普通神经网络的全连接而言的,是指这一层的某个节点只与上一层的部分节点相连。
    参数共享:是指一层中多个节点的连接共享相同的一组参数。
    这里写图片描述
    全连接:连接个数nm 局部连接:连接个数im
    参数不共享:参数个数n*m+m 参数共享:参数个数i+1

    卷积神经网络的主要组成:
    卷积层(Convolutional layer),卷积运算的目的是提取输入的不同特征,第一层卷积层可能只能提取一些低级的特征如边缘、线条和角等层级,更多层的网络能从低级特征中迭代提取更复杂的特征。

    池化层(Pooling),它实际上一种形式的向下采样。有多种不同形式的非线性池化函数,而其中最大池化(Max pooling)和平均采样是最为常见的

    全连接层(Full connection), 与普通神经网络一样的连接方式,一般都在最后几层

    pooling层的作用:
    Pooling层相当于把一张分辨率较高的图片转化为分辨率较低的图片;
    pooling层可进一步缩小最后全连接层中节点的个数,从而达到减少整个神经网络中参数的目的。

    LeNet-5卷积神经网络模型
    LeNet-5:是Yann LeCun在1998年设计的用于手写数字识别的卷积神经网络,当年美国大多数银行就是用它来识别支票上面的手写数字的,它是早期卷积神经网络中最有代表性的实验系统之一。

    LenNet-5共有7层(不包括输入层),每层都包含不同数量的训练参数,如下图所示。
    这里写图片描述
    LeNet-5中主要有2个卷积层、2个下抽样层(池化层)、3个全连接层3种连接方式

    卷积层
    卷积层采用的都是5x5大小的卷积核/过滤器(kernel/filter),且卷积核每次滑动一个像素(stride=1),一个特征图谱使用同一个卷积核.
    每个上层节点的值乘以连接上的参数,把这些乘积及一个偏置参数相加得到一个和,把该和输入激活函数,激活函数的输出即是下一层节点的值
    这里写图片描述

    LeNet-5的下采样层(pooling层)
    下抽样层采用的是2x2的输入域,即上一层的4个节点作为下一层1个节点的输入,且输入域不重叠,即每次滑动2个像素,下抽样节点的结构如下:
    这里写图片描述
    每个下抽样节点的4个输入节点求和后取平均(平均池化),均值乘以一个参数加上一个偏置参数作为激活函数的输入,激活函数的输出即是下一层节点的值。

    卷积后输出层矩阵宽度的计算:
    Outlength=
    (inlength-fileterlength+2*padding)/stridelength+1

    Outlength:输出层矩阵的宽度
    Inlength:输入层矩阵的宽度
    Padding:补0的圈数(非必要)
    Stridelength:步长,即过滤器每隔几步计算一次结果

    LeNet-5第一层:卷积层C1
    C1层是卷积层,形成6个特征图谱。卷积的输入区域大小是5x5,每个特征图谱内参数共享,即每个特征图谱内只使用一个共同卷积核,卷积核有5x5个连接参数加上1个偏置共26个参数。卷积区域每次滑动一个像素,这样卷积层形成的每个特征图谱大小是(32-5)/1+1=28x28。C1层共有26x6=156个训练参数,有(5x5+1)x28x28x6=122304个连接。C1层的连接结构如下所示。
    这里写图片描述

    LeNet-5第二层:池化层S2
    S2层是一个下采样层(为什么是下采样?利用图像局部相关性的原理,对图像进行子抽样,可以减少数据处理量同时保留有用信息)。C1层的6个28x28的特征图谱分别进行以2x2为单位的下抽样得到6个14x14((28-2)/2+1)的图。每个特征图谱使用一个下抽样核。5x14x14x6=5880个连接。S2层的网络连接结构如下右图
    这里写图片描述

    LeNet-5第三层:卷积层C3
    C3层是一个卷积层,卷积和和C1相同,不同的是C3的每个节点与S2中的多个图相连。C3层有16个10x10(14-5+1)的图,每个图与S2层的连接的方式如下表 所示。C3与S2中前3个图相连的卷积结构见下图.这种不对称的组合连接的方式有利于提取多种组合特征。该层有(5x5x3+1)x6 + (5x5x4 + 1) x 3 + (5x5x4 +1)x6 + (5x5x6+1)x1 = 1516个训练参数,共有1516x10x10=151600个连接。
    这里写图片描述

    LeNet-5第四层:池化层S4
    S4是一个下采样层。C3层的16个10x10的图分别进行以2x2为单位的下抽样得到16个5x5的图。5x5x5x16=2000个连接。连接的方式与S2层类似,如下所示。
    这里写图片描述

    LeNet-5第五层:全连接层C5
    C5层是一个全连接层。由于S4层的16个图的大小为5x5,与卷积核的大小相同,所以卷积后形成的图的大小为1x1。这里形成120个卷积结果。每个都与上一层的16个图相连。所以共有(5x5x16+1)x120 = 48120个参数,同样有48120个连接。C5层的网络结构如下所示。
    这里写图片描述

    LeNet-5第六层:全连接层F6
    F6层是全连接层。F6层有84个节点,对应于一个7x12的比特图,该层的训练参数和连接数都是(120 + 1)x84=10164.
    这里写图片描述

    LeNet-5第七层:全连接层Output
    Output层也是全连接层,共有10个节点,分别代表数字0到9,如果节点i的输出值为0,则网络识别的结果是数字i。采用的是径向基函数(RBF)的网络连接方式。假设x是上一层的输入,y是RBF的输出,则RBF输出的计算方式是:
    这里写图片描述
    yi的值由i的比特图编码(即参数Wij)确定。yi越接近于0,则标明输入越接近于i的比特图编码,表示当前网络输入的识别结果是字符i。该层有84x10=840个设定的参数和连接。连接的方式如上图.

    以上是LeNet-5的卷积神经网络的完整结构,共约有60,840个训练参数,340,908个连接。一个数字识别的效果如图所示

    这里写图片描述

    LeNet-5的训练算法
    训练算法与传统的BP算法差不多。主要包括4步,这4步被分为两个阶段:
    第一阶段,向前传播阶段:
    a)从样本集中取一个样本(X,Yp),将X输入网络;
    b)计算相应的实际输出Op。
    在此阶段,信息从输入层经过逐级的变换,传送到输出 层。这个过程也是网络在完成训练后正常运行时执行的过程。在此过程中,网络执行的是计算(实际上就是输入与每层的权值矩阵相点乘,得到最后的输出结果):
    Op=Fn(…(F2(F1(XpW(1))W(2))…)W(n))
    第二阶段,向后传播阶段
    a)算实际输出Op与相应的理想输出Yp的差;
    b)按极小化误差的方法反向传播调整权矩阵。

    卷积神经网络的优点
    卷积网络较一般神经网络在图像处理方面有 如下优点
    a)输入图像和网络的拓扑结构能很好的吻
    合;
    b)特征提取和模式分类同时进行,并同时在
    训练中产生;
    c)权重共享可以减少网络的训练参数,使神
    经网络结构变得更简单,适应性更强。
    总结
    卷积网络在本质上是一种输入到输出的映射,它能够学习大量的输入与输出之间的映射关系,而不需要任何输入和输出之间的精确的数学表达式。

    通过对LeNet-5的网络结构的分析,可以直观地了解一个卷积神经网络的构建方法,可以为分析、构建更复杂、更多层的卷积神经网络做准备。

    LaNet-5的局限性
    CNN能够得出原始图像的有效表征,这使得CNN能够直接从原始像素中,经过极少的预处理,识别视觉上面的规律。然而,由于当时缺乏大规模训练数据,计算机的计算能力也跟不上,LeNet-5 对于复杂问题的处理结果并不理想。

    2006年起,人们设计了很多方法,想要克服难以训练深度CNN的困难。其中,最著名的是 Krizhevsky et al.提出了一个经典的CNN 结构,并在图像识别任务上取得了重大突破。其方法的整体框架叫做 AlexNet,与 LeNet-5 类似,但要更加深一些。

    展开全文
  • CNN卷积神经网络原理详解(上)

    万次阅读 多人点赞 2019-10-18 23:59:17
    CNN卷积神经网络原理详解(上)前言卷积神经网络的生物背景我们要让计算机做什么?卷积网络第一层全连接层训练 前言 卷积网络(convolutional network),也叫作卷积神经网络(convolutional neural network,CNN),是...
  • CNN卷积神经网络实现,Matlab仿真,识别手写数字集。
  • 详解CNN卷积神经网络

    万次阅读 多人点赞 2018-01-11 09:55:14
    详解卷积神经网络(CNN) 详解卷积神经网络CNN概揽Layers used to build ConvNets 卷积层Convolutional layer池化层Pooling Layer全连接层Fully-connected layer 卷积神经网络架构 Layer PatternsLayer ...
  • CNN卷积神经网络原理详解(中)

    万次阅读 2019-10-19 12:43:44
    CNN卷积神经网络原理详解(中)卷积神经网络与全连接神经网络的比较卷积运算的数学解释卷积计算的工作模式 卷积神经网络与全连接神经网络的比较 卷积神经网络(Convolutional Neural Network,CNN)是一种前馈...
  • Pytorch实现CNN卷积神经网络

    万次阅读 2017-08-02 15:08:38
    Pytorch实现CNN卷积神经网络
  • TensorFlow搭建CNN卷积神经网络

    千次阅读 多人点赞 2017-08-19 00:40:20
    TensorFlow搭建CNN卷积神经网络 该教程采用TernsorFlow搭建CNN卷积神经网络,并利用MNIST数据集进行数字的手写识别
  • CNN卷积神经网络的MATLAB程序,及注解
  • CNN卷积神经网络概述什么是神经网络神经网络的基本单位前馈神经网络卷积神经网络为什么要卷积神经网络什么是卷积一维卷积二维卷积卷积神经网络结构卷积层汇聚层全连接层 什么是神经网络 人工神经网络实质上是数学...
  • CNN卷积神经网络原理详解(下)

    万次阅读 多人点赞 2019-10-20 10:56:56
    CNN卷积神经网络原理详解(下)反向传播 反向传播 前面讲解了卷积神经网络的网络基本架构。我们在实际运算的时候会发现,随着计算次数的增加,我们的输出结果与我们的预期结果会不断的逼近。这是因为网络中的权重...
  • 如何简单理解CNN卷积神经网络

    千次阅读 多人点赞 2019-05-08 14:57:01
    CNN卷积神经网络常用于图像识别分类的训练,对图像分类具有很好的适应性。 一张图片,通常是由成千上万个像素点组成的,如下: 卷积神经网络,需要理解以下三点: 什么是卷积运算 什么是reLU运算 什么是pooling ...
  • CNN卷积神经网络及图像识别

    千次阅读 2021-02-22 21:13:34
    CNN卷积神经网络及图像识别 前言 神经网络(neual networks)是人工智能研究领域的一部分,当前最流行的神经网络是深度卷积神经网络(deep convolutional neural networks, CNNs),虽然卷积网络也存在浅层结构,但是...
  • pytorch 实现的cnn卷积神经网络

    千次阅读 2019-03-02 13:33:17
    pytorch 实现的cnn卷积神经网络简介网络结构网络实现训练步骤训练数据测试数据运行截图 简介 卷积神经网络(Convolutional Neural Networks, CNN)是一类包含卷积计算且具有深度结构的前馈神经网络(Feedforward ...
  • 原标题:车牌识别算法之CNN卷积神经网络随着我国经济的发展,汽车,特别是小轿车的数量越来越多,智能交通管理系统应运而生。车牌智能自动识别作为智能交通管理系统中的重要组成部分,在智能交通管理中发挥着越来越...
  • 原创 荣君宇 CNN,作为一个经典的神经网络模型,我们并不陌生。CNN包含若干个卷积层和池化层。池化层相对简单,对于减少神经元数量,功不可没。而卷积层,才是我们理解的一个难点。 我们知道卷积层可以提取不同的...
  • Deep Learning论文笔记之(五)CNN卷积神经网络代码理解zouxy09@qq.comhttp://blog.csdn.net/zouxy09 自己平时看了一些论文,但老感觉看完过后就会慢慢的淡忘,某一天重新拾起来的时候又好像没有看过一样。...
  • 戳上方蓝字【阿力阿哩哩的炼丹日常】关注我~4.3CNN卷积神经网络我们在4.2节讲到了神经网络已经逐步成为人类智能生活的璀璨明珠,并介绍了全连接神经网络的整个训练过程(深度学习开端-全连...
  • CNN卷积神经网络实例(基于pytorch)

    千次阅读 2020-06-06 20:56:45
    CNN卷积神经网络1.关于卷积神经网络2.卷积神经网络实例(手写字母识别)2.1 代码示例2.2 运行过程及结果2.3 测试结果3.参考与致谢 1.关于卷积神经网络 卷积神经网络(Convolutional Neural Network,CNN) 是一种具有...
  • 《DeepLearning tutorial(5)CNN卷积神经网络应用于人脸识别(详细流程+代码实现)》这篇文章的代码,将CNN用于人脸识别,整个实现流程请见:http://blog.csdn.net/u012162613/article/details/43277187
  • 目的:使用CNN卷积神经网络实现语音识别 步骤:(1)预处理。 首尾端的静音切除,降低对后续步骤造成的干扰,然后进行声音分帧,把声音切开成帧,,各帧之间一般是有交叠。 (2)特征提取。运用的算法为倒谱系数...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 15,132
精华内容 6,052
关键字:

cnn卷积神经网络