天天看点

[Python|神经网络学习笔记] Tensorflow学习与神经网络应用前言1. 安装Tensorflow-gpu2. 数据基础—张量3. 数学基础—梯度下降4. Tensorflow基本操作5. Tensorflow—手写数字分类

文章目录

  • 前言
  • 1. 安装Tensorflow-gpu
    • 安装版本
    • 系统变量
    • pip install tensorflow-gpu==版本号
    • 安装测试
  • 2. 数据基础—张量
    • 张量的轴(axis)
    • 张量降维函数reduce_sum/max/mean/...
    • (未完待续)
  • 3. 数学基础—梯度下降
    • SGD
  • 4. Tensorflow基本操作
    • 数据流图
    • 基本操作
  • 5. Tensorflow—手写数字分类
    • 神经网络基本概念
    • 全连接网络
    • 卷积神经网络

前言

学习资料来源Tensorflow中文社区

1. 安装Tensorflow-gpu

Tensorflow有CPU版本和GPU加速两个版本,GPU加速版本在处理大型图像识别任务的时候速度会更快一些,但要求安装Cuda和cudnn,这对显卡有要求(本机显卡Nvidia GTX1060)。

安装版本

  • Python的IDE为Pycharm,Tensorflow各个版本需要匹配python和Cuda以及cudnn,在安装前首先看看自己显卡支持的Cuda最高到版本(如下图在“帮助”—>“系统信息”里面看到,已经支持Cuda10了),现在最高版本的tensorflow只需要Cuda9,所以安装Cuda9,推荐版本:Python/Cuda/cudnn匹配版本
    [Python|神经网络学习笔记] Tensorflow学习与神经网络应用前言1. 安装Tensorflow-gpu2. 数据基础—张量3. 数学基础—梯度下降4. Tensorflow基本操作5. Tensorflow—手写数字分类
    [Python|神经网络学习笔记] Tensorflow学习与神经网络应用前言1. 安装Tensorflow-gpu2. 数据基础—张量3. 数学基础—梯度下降4. Tensorflow基本操作5. Tensorflow—手写数字分类

系统变量

  • 安装Cuda,之前会检查兼容性,不兼容的话可以不用管,也能安装使用,然后一路默认安装即可。完成后将cudnn解压后的所有文件复制粘贴到Cuda对应的文件夹下C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0。
  • 用户变量Path下添加:
    • C:\ProgramData\NVIDIA GPU Computing Toolkit\v9.2;
  • 在系统变量中添加如下几个变量:
    • CUDA_SDK_PATH = C:\ProgramData\NVIDIACorporation\CUDA Samples\v9.2
    • CUDA_LIB_PATH = %CUDA_PATH%\lib\x64
    • CUDA_BIN_PATH = %CUDA_PATH%\bin
    • CUDA_SDK_BIN_PATH = %CUDA_SDK_PATH%\bin\win64
    • CUDA_SDK_LIB_PATH = %CUDA_SDK_PATH%\common\lib\x64

pip install tensorflow-gpu==版本号

  • tensorflow-gpu版本号

安装测试

import tensorflow as tf 
hello =tf.constant("Hello!TensorFlow")
sess = tf.Session()
print(sess.run(hello))
           
[Python|神经网络学习笔记] Tensorflow学习与神经网络应用前言1. 安装Tensorflow-gpu2. 数据基础—张量3. 数学基础—梯度下降4. Tensorflow基本操作5. Tensorflow—手写数字分类

2. 数据基础—张量

张量可以简单理解为矩阵,只是这个矩阵可以有很多维度,不仅是平时的一维向量或者二维矩阵。

张量的轴(axis)

为了帮助理解轴的概念,可以先简单定义3阶张量的构成(3阶以上张量在欧式空间没法理解):

  • 1、首先每一个单独的元素按照行构成向量,如:[1,2,3],假设沿着1,2,3的方向叫做x向,即向量元素延伸的方向;
  • 2、然后不同的向量沿垂直x的方向顺序放在一起,这个方向叫y向,即向量延伸的方向(注意区分向量元素延伸的方向),进而得到了矩阵,如:

    [[1,2,3],

    [4,5,6],

    [7,8,9]]

    (写成:[[1, 2, 3],[4, 5, 6],[7, 8, 9]])

  • 3、最后,将矩阵沿着垂直于xy平面的方向顺序放在一起,这个方向叫z向,即矩阵延伸的方向,就得到3阶张量(长方体)。

    [[[1, 2, 3],[4, 5, 6],[7, 8, 9]],

    [[4, 5, 6],[7, 8, 6],[1, 3.3, 2]]]

因此,在3阶张量里面就有三个轴,x,y和z,其中z轴是表示矩阵延伸的方向,这是生成3阶张量的最后一步,把这个轴的索引设为0(理解为索引越小,步骤越靠后);y轴是表示向量延伸的方向,这是倒数第二步,这个轴索引设为1;x轴是表示向量元素 延伸的方向,这是倒数第3步,这个轴索引设为2。对于2阶或者1阶张量,只有沿x方向的1轴和沿y方向的0轴。

总结一下:沿着哪一个轴延伸的内容级别越高(假设级别高低为:矩阵>向量>元素),则哪一个轴的索引就越低。此外,在索引张量中的元素时候,比如索引3阶张量中的元素,通常用(a0,a1,a2)表示:其中,a0表示被索引元素在0轴(z)上的第a0个矩阵里面;a1表示被索引元素在a0这个矩阵的1轴(y)上的第a1个向量里面;a2表示被索引元素即为a1这个向量里面的2轴(x)上的第a2个元素。例如:上述3阶张量的“3.3”这个元素的索引为(2,3,2)。

张量降维函数reduce_sum/max/mean/…

理解了张量的轴,就可以使用reduce_sum/max/mean…这个函数了。之所以讲这个函数,就是因为在神经网络设计损失函数—loss函数时,需要用到reduce。reduce可以理解为降维,将高阶张量降维为低阶张量,后面的sum/max/mean这些可以理解为降维的方法。以sum为例,还是如下矩阵:

[[1,2,3],

[4,5,6],

[7,8,9]]

首先进行如下运算:

import tensorflow as tf
A = tf.constant([[1, 2, 3],[4, 5, 6],[7, 8, 9]], dtype= tf.float32)
B1 = tf.reduce_sum(A,reduction_indices=0)
sess = tf.Session()
print(sess.run(B1))
           

结果输出:[12. 15. 18.]

再进行以下运算:

import tensorflow as tf
A = tf.constant([[1, 2, 3],[4, 5, 6],[7, 8, 9]], dtype= tf.float32)
B2 = tf.reduce_sum(A,reduction_indices=1)
sess = tf.Session()
print(sess.run(B2))
           

结果输出:[ 6. 15. 24.]

可以发现,reduce_sum(A,reduction_indices=)中仅reduction_indices不一样,这个参数就是降维时候的参考轴。对于这个张量,索引轴为0,1。因此,reduction_indices=0时,意味将张量A沿着上述的“y”方向压缩,将这个矩阵上下挤扁,即[1+4+7, 2+5+8, 3+6+9] = [12. 15. 18.];reduction_indices=1时,意味将张量A沿着上述的“x”方向压缩,将这个矩阵左右挤扁,即[1+2+3, 4+5+6, 7+8+9] = [ 6. 15. 24.]。但需要注意,不管是上下挤扁还是左右挤扁,最后输出都会转换为行向量输出。

(未完待续)

3. 数学基础—梯度下降

神经网络在对权值更新时,绝大多数采用误差反向传播与梯度下降的方法。tensorflow已经处理好了方向传播求微分的事情,同时也提供了多种更新梯度的方法,随机梯度下降(Stochastic Gradiant Descent, SGD)、Adagrad、Momentum、Adam等算法。目前没有绝对优的算法,这个在后面会进行对比。

SGD

随机梯度下降是最原始一种权值更新方法,表达式为:

[外链图片转存失败(img-H5myZK15-1562574427149)(https://latex.codecogs.com/png.download?W=W-\eta\frac{\partial L}{\partial W})]

n为学习率,是一个固定值,取值太小,学习效率低下且容易陷入局部最优,取值太大也有可能跳过最优值,来回跳动误差也很大。此外,上述公式中的梯度(偏导部分)反映的是当前点的坡度下降最大的方向而不一定指向全局最优点。例如,在一个三维曲面优化z,曲面沿着x方向的梯度变化很小,沿着y方向梯度变化很大。此时,如果把学习率取值过小,则沿着x方向的优化过程会很慢,如果学习率取值过大,则沿着y向的误差跳动就很大。

4. Tensorflow基本操作

数据流图

[外链图片转存失败(img-Cph2kumI-1562574427150)(http://www.tensorfly.cn/images/tensors_flowing.gif)]

上图表示了Tensorflow的计算模式——数据流图(后面一一介绍):

  • 使用图 (graph) 来表示计算任务(可以对比Matlab/Simulink的计算模式).
  • 在被称之为会话 (Session) 的上下文 (context) 中执行图.
  • 使用张量(tensor)表示数据.
  • 通过变量 (Variable) 维护状态.
  • 使用 feed 和 fetch 可以为任意的操作(arbitrary operation) 赋值或者从其中获取数据

数据流图(Data Flow Graph)?(引用自Tensorflow中文社区)

数据流图用“结点”(nodes)和“线”(edges)的有向图来描述数学计算。“节点” 一般用来表示施加的数学操作,但也可以表示数据输入(feed in)的起点/输出(push out)的终点,或者是读取/写入持久变量(persistent variable)的终点。“线”表示“节点”之间的输入/输出关系。这些数据“线”可以输运“size可动态调整”的多维数据数组,即“张量”(tensor)。张量从图中流过的直观图像是这个工具取名为“Tensorflow”的原因。一旦输入端的所有张量准备好,节点将被分配到各种计算设备完成异步并行地执行运算。

基本操作

  • 构建数据流图:构建图的第一步, 是创建源 op (source op),把op当作是上面数据流图中的节点,一个节点的输出可以作为其他节点的输入。把一个个节点创建好,定义好输入输出就形成了数据流图;例如:
    import tensorflow as tf
    # 创建一个常量 op, 产生一个 1x2 矩阵. 这个 op 被作为一个节点
    # 加到默认图中.
    # 构造器的返回值代表该常量 op 的返回值.
    matrix1 = tf.constant([[3., 3.]])
    # 创建另外一个常量 op, 产生一个 2x1 矩阵.
    matrix2 = tf.constant([[2.],[2.]])
    # 创建一个矩阵乘法 matmul op , 把 'matrix1' 和 'matrix2' 作为输入.
    # 返回值 'product' 代表矩阵乘法的结果.
    product = tf.matmul(matrix1, matrix2)
               
    则上述图有三个节点, 两个 constant() op, 和一个matmul() op.
  • 启动图:Tensorflow里面创建了数据流图后,如何运行程序不会有数据输出。上面这个例子,print(produce)的话,输出下面结果:
    [Python|神经网络学习笔记] Tensorflow学习与神经网络应用前言1. 安装Tensorflow-gpu2. 数据基础—张量3. 数学基础—梯度下降4. Tensorflow基本操作5. Tensorflow—手写数字分类
    构造完数据流图后,要启动这个图才可以得到数据输出。启动图的第一步是创建一个 Session 对象:
    # 启动默认图.
    sess = tf.Session()
    # 调用 sess 的 'run()' 方法来执行矩阵乘法 op, 传入 'product' 作为该方法的参数. 
    # 上面提到, 'product' 代表了矩阵乘法 op 的输出, 传入它是向方法表明, 我们希望取回
    # 矩阵乘法 op 的输出.
    # 整个执行过程是自动化的, 会话负责传递 op 所需的全部输入. op 通常是并发执行的.
    # 函数调用 'run(product)' 触发了图中三个 op (两个常量 op 和一个矩阵乘法 op) 的执行.
    # 返回值 'result' 是一个 numpy `ndarray` 对象.
    result = sess.run(product)
    print(result)
    # 任务完成, 关闭会话.
    sess.close()
               
    Session 对象在使用完后需要关闭以释放资源. 除了显式调用 close 外, 也可以使用 “with” 代码块 来自动完成关闭动作:
    with tf.Session() as sess:
        result = sess.run([product])
        print(result)
               
    另外,采用这种方式可以指定CPU或者GPU
    with tf.Session() as sess:
       with tf.device("/gpu:1"):
       ...
               
    设备用字符串进行标识. 目前支持的设备包括:
    • “/cpu:0”: 机器的 CPU.
    • “/gpu:0”: 机器的第一个 GPU, 如果有的话.
    • “/gpu:1”: 机器的第二个 GPU, 以此类推
  • 定义与初始化变量:采用以下方式来定义一个变量:
    # 创建一个变量, 初始化为标量 0.
    state = tf.Variable(0, name="counter")
               
    所有变量定义以后都要进行初始化然后才能参与图计算:
    init = tf.global_variables_initializer() #使用变量,就要对其初始化(以前版本是tf.initialize_all_variables())
    sess = tf.Session() #定义Session,并使用Session来初始化步骤
    sess.run(init)
    sess.close()
               
  • 查看数据流图中的数据:在sess.run()中传入需要的节点。
    input1 = tf.constant(3.0)
    input2 = tf.constant(2.0)
    input3 = tf.constant(5.0)
    intermed = tf.add(input2, input3)
    mul = tf.matmul(input1, intermed)
    
    with tf.Session() as sess:
        result = sess.run([mul, intermed])
        print(result)
               
  • Tensor赋值:在数据流图中,有的tensor需要外接传入参数,比如神经网络的输入参数和标签等。TensorFlow 提供了 feed 机制, 该机制可以临时替代图中的任意操作中的 tensor 可以对图中任何操作提交补丁, 直接插入一个 tensor。

    通常,占位符placeholder与feed配合使用。可以把placeholder看作是定义的一个数据输入接口,数据从placeholder这里传输进数据流图中。用例如下:

    input1 = tf.placeholder(tf.float32)
    input2 = tf.placeholder(tf.float32)
    output = tf.matmul(input1, input2)
    
    with tf.Session() as sess:
        print(sess.run([output], feed_dict={input1:[7.], input2:[2.]}))#feed_dict采用字典的形势“喂”数据
               

5. Tensorflow—手写数字分类

神经网络基本概念

神经网络的基本概念可以参见神经网络浅讲:从神经元到深度学习和零基础入门深度学习,很详细。这里介绍利用python/Tensorflow实现手写阿拉伯数字的识别和分类。具体来说,采用全连接网络和卷积神经网络分别实现。样本集采用MNIST数据,里面包含60000组训练数据和10000组测试数据。每组数据都对应着一幅图片和该图片的标签。采用如下代码下载MNIST数据:

from tensorflow.examples.tutorials.mnist import input_data #导入mnist库

mnist = input_data.read_data_sets('./MNIST_data', one_hot=True) #如果没有mnist数据就进行下载;使用one_hot编码
print(mnist.test.images.shape, mnist.test.labels.shape)
           

这里运行可能会提示下载不了数据,拒绝访问。一个解决办法就是自己去网站下载数据,这四个数据包下载以后,不需要自己解压,在主程序目录下新建一个文件夹“MNIST_data”,将数据放在文件夹里面即可。上面程序运行后就会自动在这个文件夹里面解压数据并导入。

[Python|神经网络学习笔记] Tensorflow学习与神经网络应用前言1. 安装Tensorflow-gpu2. 数据基础—张量3. 数学基础—梯度下降4. Tensorflow基本操作5. Tensorflow—手写数字分类

之后运行上述程序就会输出以下结果。可以看到,mnist的测试集mnist.test包含图像和标签,图像是一个784个数据的行向量,标签是10个数据的行向量。图片包含灰度图和彩色图等。灰度图的通道为1,也就是用一个2维矩阵就可以表示,每一个像素点的值在0-255之间。RGB彩色图的通道为3,每一个像素点的颜色是由3个值决定的,所以需要用3个二维矩阵表示,也就是三维矩阵。这里的MNIST是灰度图,大小为28x28=784,并且灰度图进行了归一化,在0-1之间。这里将这个28x28的矩阵按一行一行首位相连,排成了1x784的行向量。注意,这就是全连接网络的要求,全连接网络只能处理一维数据,而卷积神经网络可以处理三维数据。数据的标签是一个包含10个元素的向量,比如标签[0,0,0,0,0,1,0,0,0,0],表示这张图片的数字是“5”。

...
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
(10000, 784) (10000, 10)
           

全连接网络

所谓全连接网络,就是指上一层所有的神经元都与下一层的所有神经元连接。比如,输入层有3个神经元,隐藏层有4个神经元,那从输入到隐藏层就有3x4=12个权值。

[Python|神经网络学习笔记] Tensorflow学习与神经网络应用前言1. 安装Tensorflow-gpu2. 数据基础—张量3. 数学基础—梯度下降4. Tensorflow基本操作5. Tensorflow—手写数字分类

首先,编写一个用于定义神经网络层的文件,命名为Add_Layer.py:

import tensorflow as tf

#添加一个全链接神经网络层
def add_affine_layer(inputs, w_shape, b_shape, activation_function = None):
    r'''
    :param inputs:上一层所有神经元的输出数据,shape=[batch, -1]
    :param w_shape:权值矩阵, shape=[n_size, out_size], in_size: 上一层神经元输出数据的列数, out_size: 本层神经元个数
    :param b_shape:偏置矩阵,shape=[out_size]
    :param activation_function: 激活函数
    :return: 本层神经元输出,权值,偏置
    '''
    weights = tf.Variable(tf.random_normal(w_shape, stddev=0.1))#定义权值矩阵.,方差选择很重要
    biases = tf.Variable(tf.constant(0.1, shape=b_shape))#定义偏置
    activation_input = tf.matmul(inputs, weights) + biases#诱导局部阈
    if activation_function is None:
        outputs = activation_input
    else:
        outputs = activation_function(activation_input)
    return outputs, weights, biases

#添加卷积层
def add_conv_layer(input, w_shape, b_shape, strides=[1, 1, 1, 1], padding='SAME', activation_function=None):
    r'''
    :param input:上层神经元输入,shape=[batch, height, width, channel]
    :param w_shape:卷积核,shape=[height, width, channel, num]
    :param b_shape:偏置,shape=[num]
    :param strides:卷积核滑动步长,shape[1, row_step, col_step, 1]
    :param padding:填充,"SAME"或""VALID",填充算法,参见help(tf.nn.convolution)
    :param activation_function:激活函数
    :return:本层神经元输出,权值,偏置
    '''
    weight = tf.Variable(tf.random_normal(w_shape, stddev=0.1))#定义权值矩阵.,方差选择很重要
    biass = tf.Variable(tf.constant(0.1, shape=b_shape))
    activation_input = tf.nn.conv2d(input, weight, strides=strides, padding=padding) + biass
    if activation_function is None:
        output = activation_input
    else:
        output = activation_function(activation_input)
    return output, weight, biass

#添加池化层
def add_pool_layer(input, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME', pool_function=tf.nn.max_pool):
    r'''
    :param input:卷积结果
    :param ksize:池化窗大小,shape=[1, height, width, 1]
    :param strides:滑动步长
    :param padding:填充,"SAME"或""VALID",填充算法,help(tf.nn.convolution)
    :param pool_function:池化函数
    :return:池化结果
    '''
    return pool_function(input, ksize=ksize, strides=strides, padding=padding)
           

然后,构建全连接神经网络Example_Affine.py:

from Add_Layer import *
from tensorflow.examples.tutorials.mnist import input_data

mnist = input_data.read_data_sets('./MNIST_data', one_hot=True)# 导入数据

xs = tf.placeholder(tf.float32, shape=[None, 784])#定义输入接口,输入图像数据
ys = tf.placeholder(tf.float32, shape=[None, 10])#定义输入接口,输入图像标签

l1, w1, b1 = add_affine_layer(xs, [784, 10], [1, 10], activation_function=tf.nn.softmax)#定义一层全连接网络,输入直接到输出

Loss = tf.reduce_mean(-tf.reduce_sum(ys * tf.log(l1), 1))#定义代价函数,交叉熵函数
train = tf.train.AdagradOptimizer(0.5).minimize(Loss)
accuracy = tf.reduce_mean(tf.cast(tf.equal(tf.argmax(l1, 1), tf.argmax(ys, 1)), tf.float32))#计算神经网络的精度

'''初始化变量,必须有'''
sess = tf.InteractiveSession()
ini = tf.global_variables_initializer()
sess.run(ini)

for i in range(10000):
    x, y = mnist.train.next_batch(100)
    sess.run(train, feed_dict={xs: x, ys: y})
    if i%100 == 0:#每隔100个对比一下测试精度和训练精度
        train_accuracy = accuracy.eval(feed_dict={xs: x, ys: y})
        test_accuracy = accuracy.eval(feed_dict={xs: mnist.test.images, ys: mnist.test.labels})
        print(train_accuracy, test_accuracy)
           

这个测试精度在91%左右。

卷积神经网络

[Python|神经网络学习笔记] Tensorflow学习与神经网络应用前言1. 安装Tensorflow-gpu2. 数据基础—张量3. 数学基础—梯度下降4. Tensorflow基本操作5. Tensorflow—手写数字分类

同样,用上面的神经网络层定义卷积神经网络。这里的卷积网络为:“输入-卷积层-Relu-池化-卷积-Relu-池化-全连接-Relu-全连接-Softmax-输出”

from Add_Layer import *
from tensorflow.examples.tutorials.mnist import input_data

mnist = input_data.read_data_sets('./MNIST_data', one_hot=True)

xs = tf.placeholder("float")
ys = tf.placeholder("float", [None, 10])
xs_in = tf.reshape(xs, [-1, 28, 28, 1])

l1, w1, b1 = add_conv_layer(xs_in, [5, 5, 1, 32], [32], activation_function=tf.nn.relu)#定义卷积层
p1 = add_pool_layer(l1)#定义池化层

l2, w2 , b2 = add_conv_layer(p1, [5, 5, 32, 64], [64], activation_function=tf.nn.relu)
p2 = add_pool_layer(l2)

l3_in = tf.reshape(p2, [-1, 64*49])#因为全连接层只支持一维数据,所以要将64个特征图全部展开成一行
l3, w3, b3 = add_affine_layer(l3_in, [64*49, 1024], [1024], activation_function=tf.nn.relu)#定义全连接层

l4, w4, b4 = add_affine_layer(l3, [1024, 10], [10], activation_function=tf.nn.softmax)

Loss = tf.reduce_mean(-tf.reduce_sum(ys*tf.log(l4), 1))
train = tf.train.AdamOptimizer(1e-4).minimize(Loss)
accuracy = tf.reduce_mean(tf.cast(tf.equal(tf.argmax(l4, 1), tf.argmax(ys, 1)), "float"))

sess = tf.InteractiveSession()
ini = tf.global_variables_initializer()
sess.run(ini)

for i in range(1000):
    x_train, y_train = mnist.train.next_batch(50)
    sess.run(train, feed_dict={xs: x_train, ys: y_train})
    if i % 100 == 0:
        train_accuracy = accuracy.eval(feed_dict={xs: x_train, ys: y_train})
        test_accuracy = accuracy.eval(feed_dict={xs:mnist.test.images, ys: mnist.test.labels})
        print(train_accuracy, test_accuracy)
           

这个测试精度在99%