美文网首页机器学习小组个人专题Python建模与NLP
TensorFlow_Python搭建神经网络 (译)

TensorFlow_Python搭建神经网络 (译)

作者: dalalaa | 来源:发表于2017-12-16 15:16 被阅读153次

    原文地址:http://adventuresinmachinelearning.com/python-tensorflow-tutorial/


    正文:

    谷歌的TensorFlow框架是近期深度学习领域的热门话题。这个基于高性能数据流计算图的开源项目十分适合于深度学习。TensorFlow支持单个或多个的CPU并行计算(并且支持GPU加速),为深度学习任务提供了一个很好的方案。
    最新的1.0版添加了对移动端的支持,这篇TensorFlow教程将会对TensorFlow(Python版)的一些基本概念进行概述性地讲解,为了能够帮助读者们搭建更加复杂的神经网络模型,比如说卷积神经网络,自然语言模型和复发神经网络等。我们会搭建一个简单的三层神经网络用于识别MNIST手写数字集(这个数据可以很容易地在网上找到)。

    注意:在阅读此教程前请先自行了解神经网络基本知识

    1.0 TensorFlow计算图

    计算图是一种TensorFlow中的表现计算概念的方式,比如说一个表达式a = (b + c) * (c + 2)可以分解为如下三个式子

    d = b + c
    e = c + 2 
    a = d * e
    

    绘制成计算图结果如下


    简单的计算图

    这种分解方式看起来很诡异,为什么要做这样的处理呢?因为分解之后的式子可以做并行预算(比如说d = b + c 放在cpu1里面运算,而e = c + 2 放在cpu2(或者gpu)里面运算),然后再通过a = d * e合并起来,能够大大缩短运算时间。在大数据和深度学习中效果尤为明显。通过这个方法能够在并行运算中获得显著的效率提升。
    我们可以从下图中看到一个三层神经网络的运行方式(读者们可以参看文章原地址,原图是gif动图):


    TesnsorFlow数据流图
    在结点之间传输的数据流成为张量(tensors),是一种多维数组。这个输入如张量的尺寸是5000×64×1,代表这是一个64节点的输入层,其中包含了5000个训练样本。在输入层之后是一个隐藏层,使用relu函数作为激活函数。最后是输出层(在上面的图中这个也叫逻辑层)使用交叉熵作为损失函数。在每个点中都可以看到每个张量都流入Gradients模块,最后通过梯度下降优化器对模型进行反馈调整。
    从图中可以看到计算图是如何表述神经网络中的运算的。下面介绍如何用TensorFlow表示一些基本的数学运算。

    2.0 一个简单的TensorFlow的例子

    让我们从这个上面那个简单的例子开始吧。
    首先介绍一下TensorFlow的变量和常量,代码如下:

    import tensorflow as tf
    #创建常量
    const = tf.constant(2.0,name="const")
    #创建变量
    b = tf.Variable(2.0,name="b")
    c = tf.Variable(1.0,name="c")
    

    如上所述,TensorFlow中的常量需要使用tf.constant()函数声明,变量使用tf.Variable()函数声明。两个函数中第一个参数是在初始化过程中会赋给常量/变量的值,第二个参数是可选参数,用于标记常量/变量(如果你想进行可视化能够用到这个功能,将在以后的文章中介绍)。就像在Python中一样,TensorFlow能根据初始变量推测变量的数据类型,不过也可以自己指定数据类型,TensorFlow中包含了多种专属数据类型,如tf.float32,tf.int32等等

    值得注意的是,与Python中常用的变量声明不同,我们上面的代码中的变量和常量(以及后面的运算、计算图等)并没有真正地初始化,而是会在将来的初始化过程中进行初始化。

    接下来我们将创建一些运算操作:

    #创建运算
    d = tf.add(b,c,name="d")
    e = tf.add(c,const,name="e")
    a = tf.multiply(d,e,name="a")
    

    TensorFlow中包含了很多种类的运算,我们将在后面介绍一部分。上述代码是将add和multiply运算进行了实例化。

    下一步是对代码中的所有变量和计算图结构进行初始化(当然也可以分别初始化):

    #初始化
    init_op = tf.global_variables_initializer()
    

    接下来,为了进行变量常量之间的运算,我们需要建立一个TensorFlow会话——tf.session,所有的运算都会在tf.session中运行,为了免去关闭会话的麻烦,我们可以在with语句中进行:

    #创建会话
    with tf.Session() as sess:
      #初始化
      sess.run(init_op)
      #计算
      a_out = sess.run(a)
      print("Variable a is {}".format(a_out))
    

    注意,上述代码中的a是什么?
    a = tf.multiply(d,e,name='a')
    a是一个运算,不是一个变量,所以可以“run”。我们使用sess.run()命令对其进行运算,然后将结果复制给a_out,并打印出来。
    注意,虽然我们在a之前定义了运算d和e,但是并不需要对d和e进行运算,TensorFlow会自动计算a所依赖的运算,可以使用TensorBoard功能查看这段代码中产生的计算图:


    简单的计算图

    2.1 TensorFlow的占位符

    如果我们在声明数组b的时候不知道数组b的值,可以使用占位符对数组b进行声明:

    #创建TensorFlow变量
    b = tf.placeholder(tf.float32,[None,1],name='b')
    

    在这个声明中,我们没有给数组b赋值,不过我们需要告诉TensorFlow数组b中包含的数据类型,这个例子中b包含的数据类型为tf.float32。第二个参数是将要传入这个变量的数据的维度(shape),这里只指定了第二个维度,第一个维度可以为任意值,可以向b中传入任意数量的一维数据。
    在使用占位符之后,调用sess.run()的代码有所变化:

    a_out = sess.run(a,feed_dict={b:np.arrange(0,10)[:,np.newaxis]})
    print("Variable a is {}".format(a_out))
    

    这里我们传入了一个名为feed_dict的Python数组,数组的键是占位符的名称,值为占位符的值。

    运行之后得到结果如下:

    Variable a is [[  3.]
     [  6.]
     [  9.]
     [ 12.]
     [ 15.]
     [ 18.]
     [ 21.]
     [ 24.]
     [ 27.]
     [ 30.]]
    

    下文中将利用上述知识搭建一个简单的神经网络来识别MNIST手写数字。

    3.0 搭建简单的神经网络模型

    接下来本文将介绍如何使用TensorFlow创建一个简单的三层神经网络,在以后的文章中我们将演示如何搭建卷积神经网络等结构更加复杂的神经网络模型。如果你对神经网络还不够了解,请看作者关于神经网络的教程[http://adventuresinmachinelearning.com/neural-networks-tutorial/]
    在本例中,我们将使用TensorFlow库中自带的MNIST数据集,MNIST是一系列的28×28像素的灰阶手写数字图像,其中包含了55000条训练数据、10000条测试数据以及5000条验证数据。
    首先、导入数据:

    from tensorflow.examples.tutorials.mnist import input_data
    mnist = input_data.read_data_sets("./MNIST_data",one_hot=True)
    

    one_hot参数为True代表数据集中数字的label并不是使用阿拉伯数字12345……而是使用一个向量,比如使用[0,0,0,0,1,0,0,0,0,0]表示数字4,这样能更好地用于神经网络的output层。

    注意,以国内的网络是很难自动下载MNIST数据集的,所以建议自行下载MNIST数据集,放在与代码同目录的MNIST文件夹中,程序会自动搜索。附下载地址:http://wiki.jikexueyuan.com/project/tensorflow-zh/tutorials/mnist_download.html

    3.1 初始化

    使用占位符先声明训练数据集

    learning_rate = 0.5
    epochs = 10
    batch_size = 100
    x = tf.placeholder(tf.float32,[None,784])
    y = tf.placeholder(tf.float32,[None,10])
    

    注意输入数据x的784个节点代表了28×28(=784)像素,而输出数据y的10个节点代表了十个数字。

    接下来设置权重w和偏置项b:

    #输入层到隐藏层
    W1 = tf.Variable(tf.random_normal([784, 300], stddev=0.03), name='W1')
    b1 = tf.Variable(tf.random_normal([300]), name='b1')
    #隐藏层到输出层
    W2 = tf.Variable(tf.random_normal([300, 10], stddev=0.03), name='W2')
    b2 = tf.Variable(tf.random_normal([10]), name='b2')
    

    上述代码中,我们声明了隐藏层和输出层的权重,隐藏层中将会有300个节点,所以W1的大小为[784,300]。我们使用均值为0,方差为0.03的正态分布的随机数来初始化权重,b1、W2、b2也是以同样的方式进行了初始化。

    注意初始值设定可能会影响最终的结果

    然后根据前面声明的变量以及我们即将使用的激活函数(relu)来计算神经网络的输出:

    hidden_out = tf.add(tf.matmul(x, W1), b1)
    hidden_out = tf.nn.relu(hidden_out)
    

    上述代码中,我们首先使用tf.matmul()函数计算了矩阵x和W1的乘积,然后再加上偏置项b1,然后将计算结果使用激活函数进行非线性转换,得到隐藏层的节点值。
    接下来处理输出层y:

    y_ = tf.nn.softmax(tf.add(tf.matmul(hidden_out, W2), b2))
    

    与之前的隐藏层处理不同的是,这里对输出层数据进行了SoftMax函数处理,这个函数能够将预测结果转化为概率分布,方便后面进行交叉熵的计算。

    接下来我们将设定用于优化器的损失函数,在这个实例中我们将选择交叉熵作为损失函数,公式如下:

    交叉熵函数
    在公式中,yj(i)是第j个输出层节点的第i个训练标签,而yj-(i)是第j个输出层节点的第i个预测标签,m是每次提取的样本数量,n是输出的节点数,实现代码如下:
    y_clipped = tf.clip_by_value(y_, 1e-10, 0.9999999)
    cross_entropy = -tf.reduce_mean(tf.reduce_sum(y * tf.log(y_clipped)
                             + (1 - y) * tf.log(1 - y_clipped), axis=1))
    

    上述代码中首先对y_进行了处理,将y_值限制在1e-10到0.99999之间,避免出现log(0)。
    为了计算交叉熵,首先使用了TensorFlow中的tf.reduce_sum()函数计算张量的和,这里的张量指的是一个样本中的一个节点的交叉熵:
    y(i)j*log(yj_(i))+(1–y(i)j)log(1–yj_(i))
    上面的y和y_clipped都是(m×10)维张量,我们首先要计算各个节点的交叉熵和,即对axis=1方向求和,得到(m×1)维张量,然后利用tf.reduce_mean()函数对这些张量取均值,

    接下来需要设置优化器:

    optimiser = tf.train.GradientDescentOptimizer(learning_rate=learning_rate).minimize(cross_entropy)
    
    

    这里直接使用TensorFlow提供的梯度下降优化器,优化器中需要设置学习率(可以理解为优化过程中的一次调整的步长)和损失函数(这里设置为交叉熵,即每次调整都向能获取更小的交叉熵的方向进行调整)。TensorFlow中有众多的可选优化器,参见(https://www.tensorflow.org/api_guides/python/train

    在正式开始训练之前,还需要设置初始化函数和准确度函数:

    init_op = tf.global_variables_initializer()
    correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
    

    上述的correct_prediction中,使用tf.equal()True对y和y_进行了对比,返回了True或者False。tf.argmax()返回的是张量中最大数字的位置(在本例中其实就是1的位置,即标签y和y_对应的数字)。这样correct_prediction是一个(m×1)维布尔类型张量。
    可以借助此张量计算平均准确率,首先使用tf.cast()函数将布尔值转换为tf.float32变量,然后进行平均,得到准确率。

    3.2 开始训练

    如今万事俱备,可以开始训练了,训练代码如下:

    with tf.Session() as sess:
       sess.run(init_op)
       total_batch = int(len(mnist.train.labels) / batch_size)
       for epoch in range(epochs):
            avg_cost = 0
            for i in range(total_batch):
                batch_x, batch_y = mnist.train.next_batch(batch_size=batch_size)
                 _, c = sess.run([optimiser, cross_entropy], 
                             feed_dict={x: batch_x, y: batch_y})
                avg_cost += c / total_batch
            print("Epoch:", (epoch + 1), "cost =", "{:.3f}".format(avg_cost))
       print(sess.run(accuracy, feed_dict={x: mnist.test.images, y: mnist.test.labels}))
    

    上述代码的实现步骤如下:

    1. 初始化各运算及变量
    2. 将数据随机分为多个小样本(next_batch函数用于提取样本)
    3. 对小样本进行训练,训练过程中输出每一步训练得到的交叉熵和最终的准确率。
      训练中,我们运行了两个操作,第一个是[optimiser,cross_entropy],列表中每个操作都会运行,得到两个操作结果,但是我们并不关心优化器返回什么结果,只想知道cross_entropy(变量c)的变化。

    最后训练结束之后,打印出最终的准确率。
    完整代码如下,有兴趣的读者可以运行一下(记住要自己下载MNIST数据集):

    from tensorflow.examples.tutorials.mnist import input_data
    import tensorflow as tf
    
    #导入数据
    mnist = input_data.read_data_sets("./MNIST_data",one_hot=True)
    
    #设置参数
    learning_rate = 0.5#学习率
    epochs = 10#训练次数
    batch_size = 100#每次取的样本数量
    
    #使用占位符声明x和y
    x = tf.placeholder(tf.float32,[None,784])
    y = tf.placeholder(tf.float32,[None,10])
    
    #声明权重和偏置项
    
    W1 = tf.Variable(tf.random_normal([784,300],stddev = 0.03),name="W1")
    b1 = tf.Variable(tf.random_normal([300]),name='b1')
    W2 = tf.Variable(tf.random_normal([300,10],stddev=0.03),name="W2")
    b2 = tf.Variable(tf.random_normal([10]),name="b2")
    
    #计算隐藏层
    hidden_out = tf.add(tf.matmul(x,W1),b1)
    hidden_out = tf.nn.relu(hidden_out)
    
    #计算输出值
    y_ = tf.nn.softmax(tf.add(tf.matmul(hidden_out,W2),b2))
    
    #计算交叉熵
    y_clipped = tf.clip_by_value(y_,1e-10,0.9999999)
    cross_entropy = -tf.reduce_mean(tf.reduce_sum(y*tf.log(y_clipped) + (1 - y)*tf.log(1-y_clipped),axis=1))
    
    #定义优化器
    optimiser = tf.train.GradientDescentOptimizer(learning_rate=learning_rate).minimize(cross_entropy)
    
    #设置初始化函数
    init_op = tf.global_variables_initializer()
    
    #设置准确率函数
    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(init_op)
        total_batch = int(len(mnist.train.labels)/batch_size)
        for epoch in range(epochs):#epochs=10,训练10次
            avg_cost = 0
            for i in range(total_batch):
                batch_x ,batch_y = mnist.train.next_batch(batch_size=batch_size)
                _,c = sess.run([optimiser,cross_entropy],feed_dict={x:batch_x,
                                                                    y:batch_y})
                avg_cost += c/total_batch
            print("Epoch:",(epoch+1),"cost=","{:.3f}".format(avg_cost))
        print(sess.run(accuracy,feed_dict={x:mnist.test.images,
                                           y:mnist.test.labels}))
    #训练过程中,优化器会对权重和偏置项进行调整,计算accuracy时,TensorFlow会根据最新的w和b对x进行预测,得到的预测值y_再与y进行对比
    
    

    运行结果:

    H:\ProgramData\Anaconda3\python.exe H:/kaggle/houseprice/analysis/DeepCNN.py
    Extracting ./MNIST_data\train-images-idx3-ubyte.gz
    Extracting ./MNIST_data\train-labels-idx1-ubyte.gz
    Extracting ./MNIST_data\t10k-images-idx3-ubyte.gz
    Extracting ./MNIST_data\t10k-labels-idx1-ubyte.gz
    2017-12-16 14:37:58.355471: W c:\l\tensorflow_1501918863922\work\tensorflow-1.2.1\tensorflow\core\platform\cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use SSE instructions, but these are available on your machine and could speed up CPU computations.
    2017-12-16 14:37:58.355471: W c:\l\tensorflow_1501918863922\work\tensorflow-1.2.1\tensorflow\core\platform\cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use SSE2 instructions, but these are available on your machine and could speed up CPU computations.
    2017-12-16 14:37:58.355471: W c:\l\tensorflow_1501918863922\work\tensorflow-1.2.1\tensorflow\core\platform\cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use SSE3 instructions, but these are available on your machine and could speed up CPU computations.
    2017-12-16 14:37:58.355471: W c:\l\tensorflow_1501918863922\work\tensorflow-1.2.1\tensorflow\core\platform\cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use SSE4.1 instructions, but these are available on your machine and could speed up CPU computations.
    2017-12-16 14:37:58.355471: W c:\l\tensorflow_1501918863922\work\tensorflow-1.2.1\tensorflow\core\platform\cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use SSE4.2 instructions, but these are available on your machine and could speed up CPU computations.
    2017-12-16 14:37:58.356471: W c:\l\tensorflow_1501918863922\work\tensorflow-1.2.1\tensorflow\core\platform\cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use AVX instructions, but these are available on your machine and could speed up CPU computations.
    Epoch: 1 cost= 0.554
    Epoch: 2 cost= 0.207
    Epoch: 3 cost= 0.143
    Epoch: 4 cost= 0.114
    Epoch: 5 cost= 0.091
    Epoch: 6 cost= 0.073
    Epoch: 7 cost= 0.058
    Epoch: 8 cost= 0.047
    Epoch: 9 cost= 0.038
    Epoch: 10 cost= 0.029
    0.9776
    
    Process finished with exit code 0
    

    可以利用TensorBoard可视化工具查看到准确率的变化:


    准确率提升过程

    tensorboard内容将在以后的文章中介绍。

    文章中有些网址是国外网址,不一定打得开~~~~~~~

    相关文章

      网友评论

        本文标题:TensorFlow_Python搭建神经网络 (译)

        本文链接:https://www.haomeiwen.com/subject/dnyiixtx.html