Tensorflow

Tensorflow简介

我们知道,在Python中利用NumPy编写神经网络代码是非常麻烦的:不如编写一个简单的一层前馈网络的代码尚且需要40行,增加层数则需要更多代码,且执行时间也会更长。

而Tensorflow则使这一切变得更加简单快捷,从而缩短了想法到部署之间的实现时间。该开源深度学习库允许将深度神经网络的计算部署到任意数量的CPU或者GPU设备上,且仅需要调用一个Tensorflow API。包括 TensorFlow 在内的大多数深度学习库能够自动求导、开源、支持多种 CPU/GPU、拥有预训练模型,并支持常用的NN架构,如递归神经网络(RNN)、卷积神经网络(CNN)和深度置信网络(DBN)。

1 第一个程序详解

import tensorflow as tf
message = tf.constant('Welcome to the the exciting world of Deep Neural Networks!')
with tf.Session() as sess:
    print(sess.run(message).decode())

简要叙述一下代码的表示:

1,导入tensorflow,这将导入TensorFlow库,从此可以使用Tensorflow的功能。

import tensorflow as tf

2,由于要打印的信息是一个常量字符串,所以使用tf.constant:

message = tf.constant('Welcome to the the exciting world of Deep Neural Networks!')

3,为了执行计算图,利用with语句定义Session,并使用run来运行:

with tf.Session() as sess:
    print(sess.run(message).decode())

4,输出中包含一系列警告消息(W),具体取决于所使用的计算机和操作系统,并声明如果针对所使用的计算机进行编译,代码运行速度可能会更快

5,最后在会话中打印信息:

Welcome to the the exciting world of Deep Neural Networks!

程序解读分析

前面的代码分为以下三个主要部分:

  • 第一部分 import 模块包含代码将使用的所有库,在目前的代码中只使用 TensorFlow,其中语句 import tensorflow as tf 则允许 Python 访问 TensorFlow 所有的类、方法和符号。
  • 第二个模块包含图形定义部分...创建想要的计算图。在本例中计算图只有一个节点,tensor 常量消息由字符串“Welcome to the exciting world of Deep Neural Networks”构成。
  • 第三个模块是通过会话执行计算图,这部分使用 with 关键字创建了会话,最后在会话中执行以上计算图。

如果不想得到计算机设备的提醒和警告信息,可以用以下两行代码实现:

import os
os.environ['TF_CPP_MIN_LOG_LEVEL']='2'

以上代码用于忽略级别 2 及以下的消息(级别 1 是提示,级别 2 是警告,级别 3 是错误)。

该程序打印计算图执行的结果,计算图的执行则使用 sess.run() 语句,sess.run 求取 message 中所定义的 tensor 值;计算图执行结果输入到 print 函数,并使用 decode 方法改进,print 函数向 stdout 输出结果:

b'Welcome to the exciting world of Deep Neural Networks!'

这里的输出结果是一个字节字符串。要删除字符串引号和“b”(表示字节,byte)只保留单引号内的内容,可以使用 decode() 方法。

2 TensorFlow程序结构

该编程语言与其他语言非常不同。

首先通过将程序分为两个独立的部分,构建任何拟创建神经网络的蓝图,包括计算图的定义及其执行。

计算图:是包含节点和边的网络。本节定义所有要使用的数据,也就是张量(tensor)对象(常量、变量和占位符),同时定义要执行的所有计算,即运算操作对象(Operation Object,简称 OP)。

每个节点可以有零个或多个输入,但只有一个输出。网络中的节点表示对象(张量和运算操作),边表示运算操作之间流动的张量。计算图定义神经网络的蓝图,但其中的张量还没有相关的数值。

为了构建计算图,需要定义所有要执行的常量、变量和运算操作。

这里通过一个简单的例子来描述程序结构——通过定义并且执行计算图来实现两个向量的相加。

计算图的执行:使用会话对象来实现计算图的执行。会话对象封装了评估张量和操作对象的环境。这里真正实现了运算操作并将信息从网络的一层传递到另外一层。不同张量对象的值仅在会话对象中被初始化、访问和保存。在此之前张量对象只被抽象定义,在会话中才被赋予实际的意义。

具体做法

通过以下步骤定义一个计算图:

1,在次以两个向量相加为例给出计算图。假设有两个向量$\vec{v_1}和\vec{v_2}$将作为输入提供给Add操作,建立计算图。

2,定义改图的相应代码如下所示:

v_1 = tf.constant([1,2,3,4])
v_2 = tf.constant([2,1,5,3])
v_add = tf.add(v_1,v_2)

3,然后在会话中执行该图:

with tf.Session() as sess:
    print(sess.run(v_add))

4,以上两行代码相当于下面的代码。上面的代码的优点是不必显式写出关闭会话的命令:

sess = tf.Session()
print(sess.run(v_add))
sess.close()

5,运行结果是显示两个向量的和:

[3 3 8 7]

请记住,每个会话都需要使用 close() 来明确关闭,而 with 格式可以在运行结束时隐式关闭会话。

解读分析

计算图的构建非常简单。添加变量和操作,并按照逐层建立神经网络的顺序传递它们(让张量流动)。TensorFlow 还允许使用 with tf.device() 命令来使用具有不同计算图形对象的特定设备(CPU/GPU)。在例子中,计算图由三个节点组成, v_1 和 v_2 表示这两个向量,Add 是要对它们执行的操作。

接下来,为了使这个图生效,首先需要使用 tf.Session() 定义一个会话对象 sess。然后使用 Session 类中定义的 run 方法运行它,如下所示:

run(fetches,feed_dict=None,options=None,run_metadata)

运算结果的值在 fetches 中提取;在示例中,提取的张量为 v_add。run 方法将导致在每次执行该计算图的时候,都将对与 v_add 相关的张量和操作进行赋值。

此外,一次可以提取一个或多个张量或操作对象,例如,如果结果抽取的是 [v_1...v_add],那么输出如下:

{array([1,2,3,4]),array([2,1,5,3]),array([3,3,8,7])}

在同一段代码中,可以有多个会话对象。

在python shell中建议使用tf.InteractiveSession ,可以直接使用 eval() 直接调用运行张量对象而不用显式调用会话。

In [8]: sess = tf.InteractiveSession()

In [9]: v_1 = tf.constant([1,2,3,4])

In [10]: v_2 = tf.constant([2,1,5,3])

In [11]: v_add = tf.add(v_1,v_2)

In [12]: print(v_add.eval())
[3 3 8 7]

In [13]: sess.close()

3 TensorFlow常量、变量和占位符详解

最基本的 TensorFlow 提供了一个库来定义和执行对张量的各种数学运算。张量,可理解为一个 n 维矩阵,所有类型的数据,包括标量、矢量和矩阵等都是特殊类型的张量。

数据类型张量形状
标量0-D张量[]
矩阵2-D张量[$D_0,D_1$]
向量1-D张量[$D_0 $]
张量N-D张量[$D_0,D_1,\cdots,D_{n-1}$]

ensorFlow 支持以下三种类型的张量:

  1. 常量:常量是其值不能改变的张量。
  2. 变量:当一个量在会话中的值需要更新时,使用变量来表示。例如,在神经网络中,权重需要在训练期间更新,可以通过将权重声明为变量来实现。变量在使用前需要被显示初始化。另外需要注意的是,常量存储在计算图的定义中,每次加载图时都会加载相关变量。换句话说,它们是占用内存的。另一方面,变量又是分开存储的。它们可以存储在参数服务器上。
  3. 占位符:用于将值输入 TensorFlow 图中。它们可以和 feed_dict 一起使用来输入数据。在训练神经网络时,它们通常用于提供新的训练样本。在会话中运行计算图时,可以为占位符赋值。这样在构建一个计算图时不需要真正地输入数据。需要注意的是,占位符不包含任何数据,因此不需要初始化它们。

TensorFlow常量

声明一个标量常量:

t_1 = tf.constant(4)

一个形如[1,3]的常量向量可以用一下代码声明:

t_2 = tf.constant([4,3,2])

要创建一个所有元素为零的张量,可以使用 tf.zeros() 函数。这个语句可以创建一个形如 [M,N] 的零元素矩阵,数据类型(dtype)可以是 int32、float32 等:

tf.zeros([M,N],tf.dtype)

例如:

zero_t = tf.zeros([2,3],tf.int32)
# Results in an 2x3 array of zeros:[[0 0 0],[0 0 0]]

还可以创建与现有Numpy数组或者张量常量具有相同形状的张量常量,如下所示:

tf,zeros_like(t_2)
#创造一个和t_2相同形状的零矩阵
tf.ones_like(t_2)
#创造一个和t_2相同形状的一矩阵

创建一个所有元素都设为 1 的张量。下面的语句即创建一个形如 [M,N]、元素均为 1 的矩阵:

tf.ones([M,N],tf,dtype)

例如:

ones_t = tf.ones([2,3],tf.int32)
# Results in an 2x3 array of ones:[[1 1 1],[1 1 1]]

更进一步,还有以下语句:

在一定范围内生成一个从初值到终值等差排布的序列:

tf.linspace(start,stop,num)

相应的值为(stop-start)/(num-1)。例如:

range_t = tf.linspace(2.0,5.0,5)
#We get:[2. 2.75 3.5 4.25 5.]

从开始(默认值=0)生成一个数字序列,增量为 delta(默认值=1),直到终值(但不包括终值):

tf.range(start,limit,delta)

实例:

range_t = tf.range(10)
#Result:[0 1 2 3 4 5 6 7 8 9]

TensorFlow 允许创建具有不同分布的随机张量:
1,使用以下语句创建一个具有一定均值(默认值=0.0)和标准差(默认值=1.0)、形状为 [M,N] 的正态分布随机数组:

t_random = tf.random_normal([2,3],mean=2.0,stddev=4,seed=12)
#Result:[[ 0.25347447  5.37991     1.9527606 ] [-1.5376031   1.2588985   2.8478067 ]]
#生成了2行3列的数据,均值为2且标准差为4,设定随机生成器12为种子

2,创建一个具有一定均值(默认值=0.0)和标准差(默认值=1.0)、形状为 [M,N] 的截尾正态分布随机数组:

t_random = tf.truncated_normal([1,5],stddev=2,seed=12)
# Result:[[-0.87326276  1.689955   -0.02361972 -1.7688016  -3.87749   ]]

3,要在种子的 [minval(default=0),maxval] 范围内创建形状为 [M,N] 的给定伽马分布随机数组,请执行如下语句:

t_random = tf.random_uniform([2,3],maxval=4,seed=12)
# Result:[[2.54461   3.6963658 2.7051091] [2.0085006 3.8445983 3.5426888]]

4,要将给定的张量随机裁剪为指定的大小,使用以下语句:

tf.random_crop(t_random,[2,5],seed=12)

这里,t_random 是一个已经定义好的张量。这将导致随机从张量 t_random 中裁剪出一个大小为 [2,5] 的张量。

很多时候需要以随机的顺序来呈现训练样本,可以使用 tf.random_shuffle() 来沿着它的第一维随机排列张量。如果 t_random 是想要重新排序的张量,使用下面的代码:

tf.random_shuffle(t_random)

5,随机生成的张量受初始种子值的影响。要在多次运行或会话中获得相同的随机数,应该将种子设置为一个常数值。当使用大量的随机张量时,可以使用 tf.set_random_seed() 来为所有随机产生的张量设置种子。以下命令将所有会话的随机张量的种子设置为 54:

tf.set_random_seed(54)

PS:种子只能有整数值

TensorFlow 变量

它们通过使用变量类来创建。变量的定义还包括应该初始化的常量/随机值。下面的代码中创建了两个不同的张量变量 t_a 和 t_b。两者将被初始化为形状为 [50,50] 的随机均匀分布,最小值=0,最大值=10:

rand_t = tf.random_uniform([50,50],0,10,seed=0)
t_a = tf.Variable(rand_t)
t_b = tf.Variable(rand_t)

变量通常在神经网络中代表权重和偏置。

下面的代码中定义了两个变量的权重和偏置。权重变量使用正态分布随机初始化,均值为 0,标准差为 2,权重大小为 100×100。偏置由 100 个元素组成,每个元素初始化为 0。在这里也使用了可选参数名以给计算图中定义的变量命名:

weights = tf.Variable(tf.random_normal([100,100],stddev=2))
bias = tf.Variable(tf.zeros(100),name='biases')

在前面的例子中,都是利用一些常量来初始化变量,也可以指定一个变量来初始化另一个变量。下面的语句将利用前面定义的权重来初始化weight2:

weight2 = tf.Variable(weights.initialized_value(),name='w2')

变量的定义将指定变量如何被初始化,但是必须显式初始化所有的声明变量。在计算图的定义中通过声明初始化操作对象来实现:

intial_op = tf.global_variables_initializer()

每个变量也可以在运行图中单独使用 tf.Variable.initializer 来初始化:

bias = tf.Variable(tf.zeros([100,100]))
with tf.Session() as sess:
    sess.run(bias.initializer)

保存变量:使用 Saver 类来保存变量,定义一个 Saver 操作对象:

saver = tf.train.Saver()

TensorFlow 占位符

介绍完常量和变量之后,我们来讲解最重要的元素——占位符,它们用于将数据提供给计算图。可以使用以下方法定义一个占位符:

tf.placeholder(dtype,shape=None,name=None)

dtype 定占位符的数据类型,并且必须在声明占位符时指定。在这里,为 x 定义一个占位符并计算 y=2*x,使用 feed_dict 输入一个随机的 4×5 矩阵:

x = tf.placeholder("float")
y = 2*x
data = tf.random_uniform([4,5],10)
with tf.Session() as sess:
    x_data = sess.run(data)
    print(sess.run(y,feed_dict={x:x_data}))

解读分析

需要注意的是,所有常量、变量和占位符将在代码的计算图部分中定义。如果在定义部分使用 print 语句,只会得到有关张量类型的信息,而不是它的值。

为了得到相关的值,需要创建会话图并对需要提取的张量显式使用运行命令,如下所示:

print(sess.run(t_1))
#Will print the value of t_1 defined in step 1

4 TensorFlow矩阵基本操作及其实现

矩阵运算,例如执行乘法、加法和减法,是任何神经网络中信号传播的重要操作。通常在计算中需要随机矩阵、零矩阵、一矩阵或者单位矩阵。

具体做法

开始一个交互式会话,以便得到计算结果:

import tensorflow as tf
#Start an Interactive Session
sess = tf.InteractiveSession()
#Define a 5*5 Identity matrix
I_matrix = tf.eye(5)
print(I_matrix.eval())
#This will print a 5*5 Identity matrix

#Define a Variable initalized to a 10*10 identity matrix
X = tf.Variable(tf.eye(10))
X.initializer.run()  #Initialize the Variable(初始化变量)
print(X.eval())
#Evaluate the Variable and print the result

#Create a random 5*10 matrix
A = tf.Variable(tf.random_normal([5,10]))
A.initializer.run()

#Multiply two matrices 矩阵相乘
product = tf.matmul(A,X)
print(product.eval())
#create a random matrix of 1s and 0s,size 5*10
b = tf.Variable(tf.random_uniform([5,10],0,2,dtype=tf.int32))
b.initializer.run()
print(b.eval())
b_new = tf.cast(b,dtype=tf.float32)
#Cast to float32 data type,强制转换为浮点数32位

#Add the two matrices
t_sum = tf.add(product,b_new)
t_sub = product - b_new
print("A*X -b\n",t_sum.eval())
print("A*X -b\n",t_sub.eval())

一些其他有用的矩阵操作,如按元素相乘、乘以一个标量、按元素相除、按元素余数相除等,可以执行如下语句:

import tensorflow as tf
#创造两个随机矩阵
a = tf.Variable(tf.random_normal([4,5],stddev=2))
b = tf.Variable(tf.random_normal([4,5],stddev=2))

#元素广义相乘(按照元素相乘)
A = a*b
#矩阵成功以一个标量2
B = tf.scalar_mul(2,A)
#元素广义相除(按照元素相除)
C = tf.div(a,b)
#按照元素余数相除
D = tf.mod(a,b)

init_op = tf.global_variables_initializer()
with tf.Session() as sess:
    sess.run(init_op)
    writer = tf.summary.FileWriter('graphs',sess.graph)
    a_R,b_R,A_R,B_R,C_R,D_R = sess.run([a,b,A,B,C,D])
    print("a\n",a_R,"\nb\n",b_R,"a*b\n",A_R,"\n2*a*b\n",B_R,"\na/b\n",C_R,"\na%b\n",D_R)
    writer.close()

tf.div 返回的张量的类型与第一个参数类型一致。

TIP

所有加法、减、除、乘(按元素相乘)、取余等矩阵的算术运算都要求两个张量矩阵是相同的数据类型,否则就会产生错误。可以使用 tf.cast() 将张量从一种数据类型转换为另一种数据类型。

5 TensorFlow TensorBoard可视化数据流图

TensorFlow 使用 TensorBoard 来提供计算图形的图形图像。这使得理解、调试和优化复杂的神经网络程序变得很方便。TensorBoard 也可以提供有关网络执行的量化指标。它读取 TensorFlow 事件文件,其中包含运行 TensorFlow 会话期间生成的摘要数据。

具体做法

使用 TensorBoard 的第一步是确定想要的 OP 摘要。以 DNN 为例,通常需要知道损失项(目标函数)如何随时间变化。在自适应学习率的优化中,学习率本身会随时间变化。可以在 tf.summary.scalar OP 的帮助下得到需要的术语摘要。假设损失变量定义了误差项,我们想知道它是如何随时间变化的:

loss = tf...
tf.summary.scalar('loss',loss)

还可以使用tf.summary.histogram可视化梯度、权重或特定层的输出分布:

output_tensor = tf.matmul(input_tensor,weights) +biases
tf.summary.histogram('output',output_tensor)

摘要将在会话操作中生成。可以在计算图中定义 tf.merge_all_summaries OP 来通过一步操作得到摘要,而不需要单独执行每个摘要操作。

生成的摘要需要用事件文件写入:

tf.summary.Filewriter:
    writer = tf.summary.Filewriter('summary_dir',sess.graph)

这会将所有摘要和图形写入 summary_dir 目录中。现在,为了可视化摘要,需要从命令行中调用 TensorBoard:

tensorboard --logdir=summary_dir

接下来,打开浏览器并输入地址 http://localhost:6006/(或运行 TensorBoard 命令后收到的链接)。

你会看到类似于神经网络图 ,顶部有很多标签。Graphs(图表)选项卡能将运算图可视化。

Last modification:August 22nd, 2019 at 10:14 pm
如果觉得我的文章对你有用,请随意赞赏