神经网络:卷积神经网络神经

介绍
  • 卷积神经网络常用于图片识别,尤其是在大型图像处理上尤为出色。
  • 卷积神经网络和全连接神经网络的区别(参考图片,后者为卷积):
    卷积神经网络
  • 相关专业名词
    • 零填充(Zero-padding):有时,在输入矩阵的边缘使用零值进行填充,这样我们就可以对输入图像矩阵的边缘进行滤波。零填充的一大好处是可以让我们控制特征图的大小。使用零填充的也叫做泛卷积,不适用零填充的叫做严格卷积 。
    • 卷积:看到一个很复杂的信号,我们往往是无从下手的,这个时候若能把复杂的信号进行分解,分解成我们常见的信号,复杂信号的分析过程自然就演变成了对简单信号的分析 。也就是说我们只需要掌握一些比较典型的简单信号的特性,而刚好一些复杂的信号都可以表示成这些简单信号的组合,那么我们就可以实现复杂信号的分析,卷积积分的思想就源自于此(来自百度百科)。
      卷积通俗理解:将复杂的问题的特性抽取出来,使复杂的问题转换为简单的问题。这个过程就是“卷积”。
    • 权值共享:一张图片,用一个过滤器去扫这张图,过滤器中的权重会在这张图的每个位置扫的,其权重是一样的,即为权值共享。
正文
  • 卷积神经网络的构造
    三层结构:输入层,隐藏层,全连接层。但是其又把隐藏层分为:卷积层和激活函数和池化层(又叫采样层)
    • 卷积层:通过在原始图像上平移(扫描)原始特征,提取得到特征映射。
      • 1、定义过滤器(观察窗口)大小(一般是奇数,常为:11、33或者5*5),观察窗口中是会带有权重值的。
      • 2、通过类似线性回归的矩阵运算(矩阵乘法),得出一个结论值。
      • 3、通过一定的步长(过滤器每次移动像素的数量)在原始图像上平移“扫描”来提取结论值,每一个结论值结合起来就是一张“表(又称特征映射)”(前三步请参考下图(一个过滤器、一个色彩通道)理解)
        卷积层
      • 4、定义多个“大小”一样,但所带权重值不一样的过滤器(又叫卷积核或滤波器)进行扫描,提出的结论值是不一样的,进而得出“表”(特征映射)也是不一样的。
      • 5、有时候过滤器在“扫描”的时候,会出现步长和图片像素不匹配的状况(比如上图,定义步长为2是合适的,步长为3存在越界),那么怎么办?
      • 6、过滤器(卷积核)在提取特征映射时的动作称之为padding(零填充),由于移动步长不一定能整出整张图的像素宽度。其中有两种方式,SAME和VALID。
          a)、VALLD:不越过边缘取样:规定不越界,超出部分舍弃,取样的面积小于输入人的图像的像素宽度。(缺点是,图片还没观察完就得出了结论)
          b)、SAME:越过边缘取样:越过就越过,取样的面积和输入图像的像素宽度一致
      • 7、再说一下,3通道的彩色图片的过滤器结论提取过程:两个3 * 3 * 3大小,步长为1的过滤器去“扫描(矩阵相乘)”这张彩色图片,再将“结果们”和偏置相加起来得到“绿色部分”的输出(“表”:特征映射)。(具体参考下图)
        卷积神经网络
    • 8、卷积计算公式
      • 输入体积大小(图片大小) :
        H1 * W1 * D1
        例如:28 * 28 * 1
      • 卷积层:
        Filter数量K、Filter大小F、步长S、零填充大小P(这四个值也是tensorflow API的超参数
        例如:100个filter、大小5*5、步长1、padding = 1
      • 输出体积大小:
        H2 * W2 *D2、其中:H2 = (H1 - F + 2P) / S + 1、W2 = (W1 - F + 2P) / S + 1、D2 = K
        例如:H2 = (28 - 5 + 2 * 1) / 1 + 1 = 26、进而得出:输出体积大小:26 * 26 *100
      • ps:
        1、如果零填充模式指定了SAME模式,那么输出大小一定等于输入大小(上一步的26则一定为28,零填充大小P是需要根据公式反向计算的)。
        2、例子的图片体积为单通道黑白图片,即使是三通道彩色图片,也不影响输入体积,因为在计算“表”(特征映射)的时候,对于黑白或者彩色图片,都是一个过滤器对应一个“表”(特征映射)的,只是其中的计算过程有所不同罢了。
    • 9、tensorflow卷积相关API介绍
      • API: tf.nn.conv2d(input, filter, strides=, padding=, name=None)
      • 参数:
        1、input:给定的输入张量,具有[batch,heigth,width,channel],类型为float32,64
        2、filter:指定过滤器的大小,[filter_height, filter_width, in_channels, out_channels], 其中:in_channels:如果是彩色图片那么就是3,如果是黑白图片就是1(这只是参考理解);out_channels:指的是过滤器的数量。(这两个参数可以参考特征映射提取过程卷积公式计算理解)
        3、strides:strides = [1, stride, stride, 1],步长
        4、padding:“SAME”, “VALID”,使用的填充算法的类型,一般使用“SAME”。其中”VALID”表示滑动超出部分舍弃,“SAME”表示填充,使得变化后height,width一样大。
  • 激活函数 - - relu(关于下面的链接,也是可以反着读的)
    • relu激活函数的缺点产生缘故:relu激活函数的缺点产生缘故
    • 激活函数的介绍:激活函数理解
    • 激活函数的作用的简单了解:激活函数通俗了解
    • 如何理解更好的理解激活函数,这是相关补充:梯度爆炸与消失
    • 梯度下降的学习率的作用:通俗理解梯度下降的学习率
    • 如果不知道梯度下降是个啥,或者说不明白它是为了干啥,可以看这篇文章:梯度下降是个啥
    • 尽量通俗的总结一下
      1、激活函数:就是为了增加线性函数的非线性分隔能力(要看第3个链接);
      2、梯度下降:就是为了求出激活函数和线性函数配合后的模型(非线性函数)的损失函数的损失值最小时候,所对应的模型的权值是什么,当求出了这些个权值的时候,该模型就是最优的时候(要看第6个链接)。
      3、梯度消失:就是在求损失函数最值点的时候,是需要不断的调整斜率的,这与激活函数是有关的,因为你需要将模型的值代入到损失函数中,而这模型的值又是通过激活函数得到的,所以就成了:损失函数 = f (激活函数(线性函数)(激活函数(线性函数2 )(…(激活函数(线性函数n)))),进而就会转化成:对 f 求导绝对值最小的最值位置 。假设此时导数值只和权重与激活函数的偏导数有关,那么如果初始的权重 w太小(小于1) 而这个激活函数的偏导数也很小(也小于1),那么最终 f 的求导会无限接近于0,就会误以为已经找到了最合适的权重w,而损失函数的值也确实没有在改变了,但实际上这不是最优权重w。具体就会表现在损失值基本不变,分类问题准确率低。(假设下图是损失函数 f 的图像,那么A点最优点吗?,很明显不是,但是它确实是 f 求导为0的点)。
      梯度下降
      4、梯度爆炸:就是初始化的权重w太大,激活函数的偏导数也很大,就导致f的求导无限大,不存在求导最小的最值得位置。具体就会表现:损失值变化显著,且在不断增大,最后直接表现为nan,更不用说准确率了。
      5、梯度下降的学习率:其实就是在不断的改变损失函数的导数大小,而不此过程就是在不断的改变权重值的大小的过程,所以,如果学习率设置太小,导数就改变幅度就会过小,求解会很慢;如果改变过大,可能会越过最优值,使之导数在最优解附近来回跳动,更甚之会出现梯度爆炸的现象。
      6、relu激活函数致使神经元死亡的缘故是:可能初始化的权重值为负,则输入到rule中的值可能为负,如果为负,在rule的偏导数求导时该项会为0,进而损失函数的求导可能为0,进而该项权重可能不再更新,进而该层神经元可能死亡。
      7、激活函数API:tf.nn.relu(features,name) 参数:features:卷积后加上偏置的结果。返回值:最终结果。
  • 池化层:通过特征化后稀疏参数来减少学习的参数,降低网络的复杂度,(最大池化平均池化两种方式)(就是去掉卷积后一些不重要的特征的过程)。
    • 最大池化(常用):使用2 * 2 的“池化窗口” ,2步长,取最大值来“池化”(降采样)特征。通常表现为下图模样:
      池化层
      • 最大池化API
        • API:tf.nn.max_pool(value, ksize=, strides=, padding=,name=None)
        • 参数
          1、value:4-D Tensor形状[batch, height, width, channels]
          2、 ksize:池化窗口大小,[1, ksize, ksize, 1]
          3、strides:步长大小,[1,strides,strides,1]
          4、padding:“SAME”, “VALID”,使用的填充算法的类型,常使用“SAME”
          ps: ksize和strides的4个值,第一个和最后一个的batch_count和channel_count都是1。(脑补一下为什么就可以了)
    • 平均池化:参考上图,不再取最大值,而是平均值了。
      • 平均池化API:tf.nn.avg_pool(value,ksize,strides,padding,data_format=‘NHWC’,name=None)
        1、value:四维张量 [batch, height, width, channels],数值类型为:float32,float64, qint8, quint8,qint32
        2、ksize:四个整型值 int 的列表或元组,输入张量 value 的每个维度上的滑动窗口的尺寸大小
        3、strides:四个整型值 int 的列表或元组,输入张量 value 的每个维度上的滑动窗口的步幅长度
        4、padding:字符型参数,“VALID” 或 “SAME”,填充算法,参照 tf.nn.conv2d() 或 tf.nn.convolution() (c++底层实现卷积)
        5、data_format:字符型参数,“NHWC” 或 “NCHW”,分别代表 [batch, height, width, channels] 和 [batch, height, channels , width]
        6、name:可选参数,操作的名称。
        ps:
        (1)、 ksize和strides的4个值,第一个和最后一个的batch_count和channel_count都是1。(脑补一下为什么就可以了)。
        (2)、这里的SAME不同于池化的SAME,不代表输入和输出大小保持一致,而是代表如果扫描时外围不够的话“零填充”。
    • 为什么最大池化比平均池化常用:因为图像识别的过程通常是靠边缘才能识别图像和物体,max pooling通常能够尽量保存边缘,而mean pooling用到了窗口内的所有像素,相当于是进行了模糊处理。 也可以参考这个链接:最大池化和平均池化的应用场景
案例
  • 需求:手写数字识别
  • 数据集:tensorflow自带数据集(下载四个.gz并解压)
  • 流程:1、准备数据;2、数据占位符;3、卷积处理数据;4、建立模型;5、计算平均损失值(交叉熵损失函数:参考理解链接);6、梯度下降优化。
  • code:
    #!/usr/local/bin/python3
    # -*- coding: utf-8 -*-
    # Author  : rusi_
    import os
    import tensorflow as tf
    
    from tensorflow.examples.tutorials.mnist import input_data
    
    os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
    
    
    def w_var(shape):
        """
        初始化权重的函数
        :param shape:
        :return: 权重
        """
        return tf.Variable(tf.random_normal(shape=shape, mean=0.0, stddev=1.0))
    
    
    def b_var(shape):
        """
        初始化偏置
        :return: 偏置
        """
        return tf.Variable(tf.constant(0.0, shape=shape))
    
    
    def init_conv(w_shape, b_shape, input_data, conv_strides, relu_ksize, relu_strides, padding="SAME"):
        """
        卷积池化的过程。
        因为阅读的注释体验,本项目未用该函数。
        :return: 结果
        """
        w_conv = w_var(w_shape)
        b_conv = b_var(b_shape)
        x_relu = tf.nn.relu(
            tf.nn.conv2d(input=input_data, filter=w_conv, strides=conv_strides, padding=padding)) + b_conv
        x_pool = tf.nn.max_pool(value=x_relu, ksize=relu_ksize, strides=relu_strides, padding=padding)
        return x_pool
    
    
    def model():
        """
        卷积模型
        :return:
        """
        # 准备数据,及其占位符
        with tf.variable_scope("data"):
            x = tf.placeholder(tf.float32, [None, 784])
            y_true = tf.placeholder(tf.float32, [None, 10])
            # 重整数据形状 [None,784]----->[None,28,28,1]  # -1 代表None
            x_reshape = tf.reshape(x, [-1, 28, 28, 1])
    
        # 一层神经元
        with tf.variable_scope("conv1"):
    
            # x_pool1 = init_conv([5, 5, 1, 32], [32], x_reshape, [1, 1, 1, 1], [1, 2, 2, 1], [1, 2, 2, 1])
    
            # 初始化权重和偏执
            w_conv1 = w_var([5, 5, 1, 32])
            b_conv1 = b_var([32])
            # 激活 和 卷积 [None,28,28,1]  -----> [None,28,28,32]
            x_relu1 = tf.nn.relu(
                tf.nn.conv2d(input=x_reshape, filter=w_conv1, strides=[1, 1, 1, 1], padding="SAME")) + b_conv1
            # 池化 [None,28,28,32] ----->  [None,14,14,32]
            x_pool1 = tf.nn.max_pool(value=x_relu1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding="SAME")
    
        # 二层神经元
        with tf.variable_scope("conv2"):
            w_conv2 = w_var([5, 5, 32, 64])
            b_conv2 = b_var([64])
            # [None,14,14,32]----> [None,14,14,64]
            x_relu2 = tf.nn.relu(
                tf.nn.conv2d(input=x_pool1, filter=w_conv2, strides=[1, 1, 1, 1], padding="SAME")) + b_conv2
            # [None,14,14,64] ----->  [None,7,7,64]
            x_pool2 = tf.nn.max_pool(value=x_relu2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding="SAME")
    
        # 全连接层
        with tf.variable_scope("fc"):
            # 权重和偏执
            w_fc = w_var([7 * 7 * 64, 10])
            b_fc = b_var([10])
            # 矩阵运算 :y_predict: [None,10]
            x_fc_reshape = tf.reshape(x_pool2, [-1, 7 * 7 * 64])
            y_predict = tf.matmul(x_fc_reshape, w_fc) + b_fc
        return x, y_true, y_predict
    
    
    def conv():
        """
        卷积神经网络预测手写数字
        :return:
        """
        mnist = input_data.read_data_sets(r"E:\mac_obj_file\mnist", one_hot=True)
        # 拿到模型输出
        x, y_true, y_predict = model()
        print(y_predict)
        # 进行交叉熵损失计算
        with tf.variable_scope("soft_cross"):
            # 求平均交叉熵损失
            loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_true, logits=y_predict))
        # 梯度下降求出损失
        with tf.variable_scope("optimizer"):
            # fixme:此处的学习率如果指定很高的话,就会出现准确率来回波动现象
            train_op = tf.train.GradientDescentOptimizer(0.0001).minimize(loss)
        # 计算准确率
        with tf.variable_scope("acc"):
            equal_list = tf.equal(tf.argmax(y_true, 1), tf.argmax(y_predict, 1))
            # equal_list  None个样本,每个样本为:[1, 0, 1, 0, 1, 1,..........]
            accuracy = tf.reduce_mean(tf.cast(equal_list, tf.float32))
        # 初始化变量的op
        init_op = tf.global_variables_initializer()
        # 开启回话
        with tf.Session() as sess:
            sess.run(init_op)
            # 训练
            for i in range(4000):
                # 取出真实的特征值和目标值
                mnist_x, mnist_y = mnist.train.next_batch(50)
                sess.run(train_op, feed_dict={x: mnist_x, y_true: mnist_y})
                print(f"训练第:{i}步,准确率:{sess.run(accuracy, feed_dict={x: mnist_x, y_true: mnist_y})}")
        return None
    
    
    if __name__ == '__main__':
        conv()
    
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值