图像识别与卷积神经网络

图像识别与卷积神经网络卷积神经网络是除了全连接神经网络以外另一个常用的网络结果,其在图像识别方面表现十分突出。本文结合Tensorflow:实战Google深度学习框架,讲述卷积神经网络常用数据集,介绍卷积网络的结构思想,以及通过TensorFlow实现其设计。1图像识别数据集MNIST手写体识别数据集解决是一个相对简单的问题,而对于更加复杂的类别,可以用到CIFAR数据集。比如CIFAR10数据集收集了来自10…

大家好,又见面了,我是你们的朋友全栈君。

卷积神经网络是除了全连接神经网络以外另一个常用的网络结果,其在图像识别方面表现十分突出。本文结合Tensorflow:实战Google深度学习框架,讲述卷积神经网络常用数据集,介绍卷积网络的结构思想,以及通过TensorFlow实现其设计。

1 图像识别数据集

MNIST手写体识别数据集解决是一个相对简单的问题,而对于更加复杂的类别,可以用到CIFAR数据集。比如CIFAR10数据集收集了来自10个不同种类的6万张图片,每张图片像素为32×32,如下图在这里插入图片描述

CIFAR10数据集与MNIST相似之处在于,其每张图片大小都是一定的,每张图片中都只包含一个类别。区别在于CIFAR10中的图片都是彩色的,而且分类难度也比MNIST高,人工标注的正确率约为94%。

在现实生活中,图片的格式不一定都是恒定的,类别也远超出10种,每张图片也会包含多个元素,因此需要有更强大的数据集。
由斯坦福大学的李飞飞开发的ImageNet,有近1500万张图片,关联了大概20000个类别。

ImageNet每年都会举办图像识别竞赛ILSVRC(现已停办),每年的比赛都提供不同的数据集。下图给出不同算法在ImageNet图像分类上的top-5正确率,ton-N表示算法给出的前N个答案中有一个是正确的,2013年之后基本上所有的研究都集中在卷积神经网络上。在这里插入图片描述

2 卷积神经网络简介

深度神经网络有多种,主要有全连接层神经网络,卷积神经网络和循环神经网络。其中全连接层神经网络之前已有介绍,其相邻层的节点之间都会相连。循环神经网络将会在后续的章节介绍,以下简单说一下卷积神经网络。在这里插入图片描述

在这里插入图片描述

上图是卷积神经网络的架构图,输入的图片一般会经过多个卷积层和池化层后,再接上数个全连接层后,通过softmax输出结果。下边着重介绍输入图片格式、卷积层和池化层的概念。

输入图片

输入的图片一般为其像素矩阵,如果图片为黑白,则其只有1个通道,其像素矩阵为长X宽。如果图片为彩色,则一般表示为RGB格式,因此有3个通道,其像素矩阵是一个三维矩阵,大小为长X宽X3。

卷积层

在这里插入图片描述
在上图中,输入层是一个3X3矩阵。中间的过滤器是卷积过程中最重要的部分。在构建卷积神经网络时,过滤器的尺寸大小需要手动指定,而过滤器中的值则是需要优化的部分。此处我们指定了过滤器大小为2X2,其中的4个值分别为1,1,-1,-1。

接下来我们来看看卷积如何进行的,首先将过滤器放置在输入层的左上角,也就是红色的2X2方框中。之后将输入层的2X2共四个数字与过滤器中的四个数字分别对应相乘,然后相加,得到的输出层的第一个输出结果:1×10+0x(-1)+1×10+0x(-1)=20。之后将过滤器向右平移一个元素,再次计算得到第二个结果:0x10+10x(-1)+0x10+10x(-1)=-20。
然后分别将过滤器分别放在输入层的左下角和右下角,得到剩下的两个结果。
以上就是卷积神经网络网络的核心过程。但是依然有几个地方需要说明。

过滤器的尺寸一般为3×3或5×5,这个是人工指定的。对于RGB图像,因为其像素矩阵是三维的,因此过滤器也是三维的,也就是3x3x3或5x5x3。每个过滤器还可以添加一个偏置项,因此对于RGB图像,每个过滤器的参数为3x3x3+1=28个或5x5x3+1=76个。

除了过滤器的长宽,需要手动指定的另一个参数是输出节点的深度。如上图所示每一个过滤器都只会输出一层二维的节点。因此指定过滤器的层数就是指定了输出的层数。
在这里插入图片描述
另一个可以手动设置的参数是过滤器的步长stride。在最开始的图中,过滤器设定的步长是1。而在上图中,设定的步长是2。

由以上分析可以看到,输出层的尺寸是比输入层要少的。当卷积的层数越来越多,输出的尺寸就会越来越少,这样对我们的结果是不利的。我们希望的是输出的尺寸不会随着卷积的层数的增加而变化。因此我们可以想法增大输入层的尺寸,其中一种方法就是用0来填充输入层的边界。
在这里插入图片描述

如上图,当3×3的输入层添加了一层0(zero padding)后,再用过滤器卷积后,输出层和输入层已经保持一致。

下边的公式给出了输出层的尺寸和输入层尺寸、过滤器尺寸、步长的关系:
o u t p u t = [ ( i n p u t − f i l t e r + 1 ) / s t r i d e ] output = [(input – filter+1)/stride] output=[(inputfilter+1)/stride]

过滤器的作用

不难发现,图像识别的第一步就是识别边缘。所谓边缘就是图片的像素值发生变化的地方,如果整张图片的像素值没什么变化,那就只不过是一张纯色图片而已。
在这里插入图片描述


图片来自https://mooc.study.163.com/learn/2001281004?tid=2001392030&_trace_c_p_k2_=96de2913028e4fceb899982a5d8212c4#/learn/content?type=detail&id=2001729323&cid=2001725119

上图显示了一个过滤器如何在扫描图片之后获得图片的边缘。根据图片的像素值可以看到中央有一条垂直的边界。当用对应的过滤器对图片进行卷积操作后,输出层中央对应位置的矩阵数值会增大,数值大的矩阵值在经过激活函数后将更大可能得到保存,因此过滤器能够识别图像的特征。

池化层

除了卷积层,另外一个在卷积神经网络里常用的是池化层。池化层的作用主要是缩小矩阵尺寸,以及有效防止网络过拟合。
池化层的计算比卷积要简单,主要是获得某几个节点的最大值(最大池化层 max pooling)或平均值(平均池化层 average pooling)。

池化层的一些参数,如尺寸,步长,以及是否使用全0填充等于卷积层是一致的。不同之处在于,其一般是二维矩阵,除了在长宽两个节点中移动外,池化过滤器还需要在深度这个维度上移动。以下是池化的计算例子。在这里插入图片描述

以下是一个利用tensorflow使用卷积层和池化层的简单例子:

import tensorflow as tf
import numpy as np

M = np.array([
        [[1],[-1],[0]],
        [[-1],[2],[1]],
        [[0],[2],[-2]]
    ])
    

M = np.asarray(M,dtype='float32')
M = M.reshape(1,3,3,1)

x = tf.placeholder('float32',[1,None,None,1])
filter_weight = tf.get_variable('weights',[2,2,1,1],initializer=tf.constant_initializer([[1,-1],[0,2]]))
biases = tf.get_variable('bias',[1],initializer=tf.constant_initializer(1))
#当padding设置为VALID时,输入矩阵是保持原样的,因此输出矩阵尺寸比输入矩阵小
#当padding设置为SAME时,输入矩阵会进行0填充,使输出矩阵尺寸与输入矩阵保持一致。
conv = tf.nn.conv2d(x,filter_weight,strides=[1,1,1,1,],padding="SAME")
bias = tf.nn.bias_add(conv,biases)
pool = tf.nn.max_pool(bias,ksize=[1,2,2,1],strides=[1,1,1,1],padding="SAME")

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    conv_result = sess.run(conv,feed_dict={ 
   x:M})
    pool_result = sess.run(pool,feed_dict={ 
   x:M})
    print(conv_result.reshape(3,3))
    print(pool_result.reshape(3,3))


经典卷积神经网络模型:LeNet-5

LeNet-5是第一个成功应用于数字识别的神经网络,在MNIST数据集上,LeNet-5可以达到99.2%的正确率。模型共有7层,下边介绍一下每层结构。

1.卷积层
第一层的输入就是图像的像素矩阵,为32x32x1。第一层过滤器尺寸为5×5,深度为6,不使用0填充,步长为1。因此输出的长宽为32-5+1=28。输出的尺寸为28x28x6。每一个过滤器有5×5+1个参数,其中1为偏置项,因此此层参数共有(5×5+1)x6=156个参数。

2.池化层
这一层的输入为上一层的输出:28x28x6。过滤器大小为2×2,长宽步长均为2,所以输出矩阵为14x14x6。

3.卷积层
本层输入矩阵为14x14x6,过滤器大小为5×5,深度为16。不使用全0填充,步长为1。因此输出为10x10x16。参数个数为(5x5x6+1)x16=2416个。

4.池化层
本层输入矩阵为10x10x16。过滤器大小为2×2,步长为2。输出为5x5x16。

5.全连接层
本层输入矩阵大小为5x5x16,在输入时会被组合成1列向量,输出节点为120个。因此参数个数为5x5x16x120+120=48120个参数。

6.全连接层
本层输入节点个数120,输出节点84个,参数总数为120×84+84=10164

7.全连接层
本层输入节点个数为84,输出节点为10个,参数为84×10+10=850个。

以下是Le-Net5的实现代码:

import tensorflow as tf
import numpy as np


INPUT_NODE = 784
OUTPUT_NODE = 10

IMAGE_SIZE = 28
NUM_CHANNELS = 1
NUM_LABELS = 10

CONV1_DEEP = 32
CONV1_SIZE = 5

CONV2_DEEP = 64
CONV2_SIZE = 5

FC_SIZE = 512
REGULARIZATION_RATE = 0.0001

BATCH_SIZE = 100
LEARNING_RATE_BASE = 0.01
LEARNING_RATE_DECAY = 0.99
REGULARIZATION_RATE = 0.0001
TRAINING_STEPS = 6000

def inference(input_tensor):#Le-Net5的结构
    with tf.variable_scope("layer1-conv"):
        conv1_weight = tf.get_variable('weight',[CONV1_SIZE,CONV1_SIZE,NUM_CHANNELS,CONV1_DEEP],
                                      initializer = tf.truncated_normal_initializer(stddev=0.1))
        conv1_biases = tf.get_variable("bias",CONV1_DEEP,initializer=tf.constant_initializer(0.0))
        conv1 = tf.nn.conv2d(input_tensor,conv1_weight,strides=[1,1,1,1],padding="SAME")
        relu1 = tf.nn.relu(tf.nn.bias_add(conv1,conv1_biases))
   
    with tf.variable_scope("layer2-pool"):
        pool1 = tf.nn.max_pool(relu1,ksize=[1,2,2,1],strides=[1,2,2,1],padding="SAME")
    
    with tf.variable_scope("layer3-conv"):
        conv2_weight = tf.get_variable('weight',[CONV2_SIZE,CONV2_SIZE,CONV1_DEEP,CONV2_DEEP],
                                      initializer=tf.truncated_normal_initializer(stddev=0.1))
        conv2_biases = tf.get_variable('bias',CONV2_DEEP,initializer=tf.constant_initializer(0))
        conv2 = tf.nn.conv2d(pool1,conv2_weight,strides=[1,1,1,1],padding="SAME")
        relu2 = tf.nn.relu(tf.nn.bias_add(conv2,conv2_biases))
        
    with tf.variable_scope("layer4-pool"):
        pool2 = tf.nn.max_pool(relu2,ksize=[1,2,2,1],strides=[1,2,2,1],padding="SAME")
        pool2_shape = pool2.get_shape().as_list()
        node = pool2_shape[1] * pool2_shape[2] * pool2_shape[3]
        reshape = tf.reshape(pool2,[pool2_shape[0],node])
   
    with tf.variable_scope("layer5-fc1"):
        fc1_weights = tf.get_variable("weight",[node,FC_SIZE],initializer=tf.truncated_normal_initializer(stddev=0.1))
        tf.add_to_collection('losses',tf.contrib.layers.l2_regularizer(REGULARIZATION_RATE)(fc1_weights))
        fc1_biases = tf.get_variable("bias",[FC_SIZE],initializer=tf.constant_initializer(0.1))
        fc1 = tf.nn.relu(tf.matmul(reshape,fc1_weights) + fc1_biases)
        fc1 = tf.nn.dropout(fc1,0.5)
        
    with tf.variable_scope("layer6-fc2"):
        fc2_weights = tf.get_variable("weight",[FC_SIZE,NUM_LABELS],initializer=tf.truncated_normal_initializer(stddev=0.1))
        tf.add_to_collection("losses",tf.contrib.layers.l2_regularizer(REGULARIZATION_RATE)(fc2_weights))
        fc2_biases = tf.get_variable("bias",[NUM_LABELS],initializer=tf.constant_initializer(0.1))
        logit = tf.nn.relu(tf.matmul(fc1,fc2_weights) + fc2_biases)
    
    return logit

def train(mnist):
    x = tf.placeholder(tf.float32,[BATCH_SIZE,IMAGE_SIZE,IMAGE_SIZE,NUM_CHANNELS],name='x-input')
    y_= tf.placeholder(tf.float32,[None,OUTPUT_NODE],name='y-input')
    y = inference(x)
    
    global_step = tf.Variable(0,trainable=False)
    cross_entropy_mean = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y,labels=tf.argmax(y_,1)))
    loss = cross_entropy_mean + tf.add_n(tf.get_collection("losses"))
    learning_rate = tf.train.exponential_decay(LEARNING_RATE_BASE,global_step,mnist.train.num_examples/BATCH_SIZE,
                                              LEARNING_RATE_DECAY,staircase = False)
    
    train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss,global_step=global_step)
    
    correct_prediction = tf.equal(tf.argmax(y,1),tf.argmax(y_,1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32))
    
    
    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
        xs,ys = mnist.train.next_batch(BATCH_SIZE)
        reshape = np.reshape(xs,[BATCH_SIZE,IMAGE_SIZE,IMAGE_SIZE,NUM_CHANNELS])
        test_reshape = np.reshape(mnist.test.images,[10000,IMAGE_SIZE,IMAGE_SIZE,NUM_CHANNELS])
        test_feed = { 
   x:test_reshape,y_:mnist.test.labels}
        for i in range(TRAINING_STEPS):
            losses = sess.run(loss,feed_dict={ 
   x:reshape,y_:ys})
            if i%1000==0:
                print("accruacy after %d step is %f"%(i,losses))
    

总结

本文主要介绍了卷积神经网络的基本知识,包括其结构,在tensorflow中的用法,还展示了一个用tensorflow搭建的基本模型Le-Net5。
近几年卷积神经网络在图片识别中大展拳脚,然而这些网络与Le-Net5相比层数更多,参数更多,需要更多的图片来训练,训练的时长也需要数天至数周不等。因此如果要利用这些模型来解决自己的问题,就要利用迁移学习,迁移学习的用法将会在以后介绍。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/138881.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • C++ 23种设计模式(6)-适配器模式

    C++ 23种设计模式(6)-适配器模式适配器模式将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。它包括类适配器和对象适配器,本文针对的是对象适配器。举个例子,在STL中就用到了适配器模式。STL实现了一种数据结构,称为双端队列(deque),支持前后两段的插入与删除。STL实现栈和队列时,没有从头开始定义它们,而是直接使用双端队列实现的。这里双端队列就扮演了适配器的角色。队列用到了它的后端插入,前端删除。而栈用到了它的后端插入,后端删除。假设栈和队列都是一种顺序容器,有两种操作:压入和弹出。

    2022年7月25日
    8
  • FCoin币改试验区(主版C)筹备公告(5号)「建议收藏」

    7月9日,币改试验区筹备组已经完成了一轮投票,通过了16位新成员,当前群成员总数26人。今日启动第二轮投票的前期准备工作。我们欢迎热心通证经济实践的社会各界有识之士继续报…

    2022年4月16日
    41
  • 汇编指令大全

    汇编指令大全80×86指令系统,指令按功能可分为以下七个部分。(1)数据传送指令。(2)算术运算指令。(3)逻辑运算指令。(4)串操作指令。(5)控制转移指令。(6)处理器控制指令。(7

    2022年7月2日
    19
  • 两位数乘法速算(无意中发现)

    两位数乘法速算(无意中发现)比如目前计算12*34=?现在拿ab*cd=?举例子步骤:就是b*d的个位数放在?的个位上。。。。。。。。。。。。。。。。。①然后如果bd有十位那么先记下来(心里默记)。。。。。。。。。。。②然后计算bc+a*d+②结果得到的个位数写在①前面。。。。。。。。③然后把上一步剩下的结果除了个位数以后的保留下来。。。。。。。。。④然后…

    2022年6月7日
    32
  • 中文分词技术是什么_中文分词技术

    中文分词技术是什么_中文分词技术分词技术就是搜索引擎针对用户提交查询的关键词串进行的查询处理后根据用户的关键词串用各种匹配方法进行的一种技术。当然,我们在进行数据挖掘、精准推荐和自然语言处理工作中也会经常用到中文分词技术。一、为什么

    2022年8月4日
    5
  • js传参数到另一个页面_jquery json字符串转换json对象

    js传参数到另一个页面_jquery json字符串转换json对象我是MVC3和Razor的新手,一旦从AJAX发布返回数据,就需要绑定/加载WebGrid的帮助。任何帮助将不胜感激(项目截止日期很快临近);)我的情况是这样的:我有两个级联的下拉列表。第一个列表包含数据库中的区域。选择区域后,它将在第二个下拉列表中填充一系列设施。选择设施后,我需要使用建筑物列表填充WebGrid。我的级联下拉菜单正常工作Index.cshtml:@usingThisContr…

    2022年10月6日
    0

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号