目录
计算图的概念
计算图的使用
张量的概念
张量的使用
TensorFlow运行模型——会话
TensorFlow实现神经网络
条目7
条目8
条目9
计算图的概念 |
TensorFlow 的名字中已经说明了它最重要的两个概念--Tensor 和Flow。Tensor 就是张量,可以被简单地理解为多维数组。Flow 则体现了它的计算模型,它直观地表达了张量之间通过计算相互转化的过程。TensorFlow 是一个通过计算图的形式来表述计算的编程系统。TensorFlow 中的每一个计算都是计算图上的一个节点,而节点之间的边描述了计算之间的依赖关系。下图为两个向量相加的计算图:图中a、b这两个常量不依赖任何计算,而add计算则依赖读取两个常量的取值。
计算图的使用 |
TensorFlow程序分为两个阶段。第一个阶段定义计算图中的所有的计算。第二阶段执行计算。以下代码是第一个阶段,在这个过程中,TensorFlow会自动将定义的计算转化为计算图上的节点:
# -*- coding: utf-8 -*- import tensorflow as tf a = tf.constant([1.0, 2.0], name="a") b = tf.constant([2.0, 3.0], name="b") res = a + b
在TensorFlow程序中,系统会自动维护一个默认的计算图,通过tf.get_default_graph()函数可以获取当前默认的计算图。
通过a.graph可以查看张量所属的计算图。因为没有特意制定,应该属于默认计算图。
print(a.graph is tf.get_default_graph()) # True
除了使用默认的计算图,TensorFlow 支持通过tf.Graph 函数来生成新的计算图。不同计算图上的张量和运算都不会共享。以下代码示意了如何在不同计算图上定义和使用变量。
# -*- coding: utf-8 -*- import tensorflow as tf g1 = tf.Graph() with g1.as_default(): # 在计算图g1中定义变量v,并设置初始值为0 v = tf.get_variable("v", [1], initializer = tf.zeros_initializer()) g2 = tf.Graph() with g2.as_default(): # 在计算图g1中定义变量v,并设置初始值为1 v = tf.get_variable("v", [1], initializer = tf.ones_initializer()) # 在图g1中读取变量v的取值 with tf.Session(graph = g1) as sess: tf.global_variables_initializer().run() with tf.variable_scope("", reuse=True): print(sess.run(tf.get_variable("v"))) # [ 0.] with tf.Session(graph = g2) as sess: tf.global_variables_initializer().run() with tf.variable_scope("", reuse=True): print(sess.run(tf.get_variable("v"))) # [ 1.]
TensorFlow中的计算图不仅仅可以用来隔离张量和计算,还提供了管理张量和计算的机制。计算图可以通过tf.Graph.device函数来制定运行计算的设备。这为TensorFlow使用GPU提供了机制。下面程序可以将加法计算跑在GPU上。
# -*- coding: utf-8 -*- import tensorflow as tf a = tf.constant([1.0, 2.0], name="a") b = tf.constant([2.0, 3.0], name="b") g = tf.Graph() with g.device('/gpu:0'): res = a + b
张量的概念 |
从TensorFlow 的名字就可以看出张量(tensor) 是一个很重要的概念。在TensorFlow程序中,所有的数据都通过张量的形式来表示。从功能的角度上看,张量可以被简单理解为多维数组。其中零阶张量表示标量(scalar),也就是一个数; 第一阶张量为向量(vector),也就是一个一维数组 ;第n 阶张量可以理解为一个n 维数组。但张量在TensorFlow 中的实现并不是直接采用数组的形式,它只是对TensorFlow 中运算结果的引用。在张量中并没有真正保存数字,它保存的是如何得到这些数字的计算过程。还是以向量加法为例,当运行如下代码时,并不会得到加法的结果,而会得到对结果的一个引用。可以看出一个张量中保存了三个属性:名字(name)、维度(shape)、类型(type)
# -*- coding: utf-8 -*- import tensorflow as tf a = tf.constant([1.0, 2.0], name="a") b = tf.constant([2.0, 3.0], name="b") res = a + b print(a) print(res) # Tensor("a:0", shape=(2,), dtype=float32) # Tensor("add:0", shape=(2,), dtype=float32) add:0就说明res这个张量是计算节点(add)输出的第一个结果
张量的第三个属性是类型(type),每一个张量会有一个唯一的类型。TensorFlow 会对参与运算的所有张量进行类型的检查,当发现类型不匹配时会报错。比如运行下面这段程序时就会得到类型不匹配的错误:
# -*- coding: utf-8 -*- import tensorflow as tf a = tf.constant([1, 2], name="a") b = tf.constant([2.0, 3.0], name="b") res = a + b # ValueError: Tensor conversion requested dtype int32 for Tensor with dtype float32: 'Tensor("b:0", shape=(2,), dtype=float32)'
如果将第一个加数指定成实数类型“a= tf.constant([1,2],name="a",dtype=tf.float32)”,那么两个加数的类型相同就不会报错了。如果不指定类型,TensorFlow 会给出默认的类型,比如不带小数点的数会被默认为int32,带小数点的会默认为float32。因为使用默认类型有可能会导致潜在的类型不匹配问题,所以一般建议通过指定dtype 来明确指出变量或者常量的类型。TensorFlow 支持14 种不同的类型,主要包括了实数(tf.float32、tf.float64)、整数(tf.int8、tf.int16、tf.int32、tf.int64、tf.uint8 )、布尔型(tf.bool )和复数(tf.complex64、tf.complex 128 )。
# -*- coding: utf-8 -*- import tensorflow as tf a = tf.constant([1, 2], name="a",dtype=tf.float32) b = tf.constant([2.0, 3.0], name="b") res = a + b print("ok") # ok
张量的使用 |
和TensorFlow 的计算模型相比,TensorFlow 的数据模型相对比较简单。张量使用主要可以总结为两大类。第一类用途是对中间计算结果的引用。当一个计算包含很多中间结果时,使用张量可以大大提高代码的可读性。以下为使用张量和不使用张量记录中间结果来完成向量相加的功能的代码对比。
# -*- coding: utf-8 -*- import tensorflow as tf # 使用张量记录中间结果 a = tf.constant([1.0, 2.0], name="a") b = tf.constant([2.0, 3.0], name="b") res = a + b # 不使用张量记录中间结果,可读性差 res = tf.constant([1.0, 2.0], name="a") + tf.constant([2.0, 3.0], name="b")
从上面的样例程序可以看到a 和b 其实就是对常量生成这个运算结果的引用,这样在做加法时就可以直接使用这两个变量,而不需要再去生成这些常量。当计算的复杂度增加时(比如在构建深层神经网络时) 通过张量来引用计算的中间结果可以使代码的可阅读性大大提升。同时通过张量来存储中间结果,这样可以方便获取中间结果。比如在卷积神经网络中,卷积层或者池化层有可能改变张量的维度,通过result.get_shape 函数来获取结果张量的维度信息可以免去人工计算的麻烦。
使用张量的第二类情况是当计算图构造完成之后,张量可以用来获得计算结果,也就是得到真实的数字。虽然张量本身没有存储具体的数字,但是通过会话(session),就可以得到这些具体的数字。比如在上述代码中,可以使用tf.Session().run(result)语句来得到计算结果。
TensorFlow运行模型——会话 |
前面的两节介绍了TensorFlow 是如何组织数据和运算的。本节将介绍如何使用TensorFlow 中的会话(session) 来执行定义好的运算。会话拥有并管理TensorFlow 程序运行时的所有资源。当所有计算完成之后需要关闭会话来帮助系统回收资源,否则就可能出现资源泄漏的问题。TensorFlow 中使用会话的模式一般有两种,第一种模式需要明确调用会话生成函数和关闭会话函数,这种模式的代码流程如下。
# -*- coding: utf-8 -*- import tensorflow as tf a = tf.constant([1.0, 2.0], name="a") b = tf.constant([2.0, 3.0], name="b") res = a + b # 创建一个会话 sess = tf.Session() # 使用创建好的会话计算关心的结果 print(sess.run(res)) # 关闭会话释放资源 sess.close() # [ 3. 5.]
使用这种模式时,在所有计算完成之后,需要明确调用Session.close 函数来关闭会话并释放资源。然而,当程序因为异常而退出时,关闭会话的函数可能就不会被执行从而导致资源泄漏。为了解决异常退出时资源释放的问题,TensorFlow 可以通过Python 的上下文管理器来使用会话。以下代码展示了如何使用这种模式。
# -*- coding: utf-8 -*- import tensorflow as tf a = tf.constant([1.0, 2.0], name="a") b = tf.constant([2.0, 3.0], name="b") res = a + b # 创建一个会话,并通过Python中的上下文管理器来管理这个会话 with tf.Session() as sess: print(sess.run(res)) # 不再使用sess.close()关闭会话释放资源,当退出上下文时资源自动释放
通过Python 上下文管理器的机制,只要将所有的计算放在“with”的内部就可以。当上下文管理器退出时候会自动释放所有资源。这样既解决了因为异常退出时资源释放的问题,同时也解决了忘记调用Session.close 函数而产生的资源泄漏。
之前介绍过TensorFlow会自动生成一个默认的计算图,如果没有特殊指定,运算会自动加入这个计算图中。TensorFlow 中的会话也有类似的机制,但TensorFlow 不会自动生成默认的会话,而是需要手动指定。当默认的会话被指定之后可以通过tf.Tensor.eval 函数来计算一个张量的取值。以下代码展示了通过设定默认会话计算张量的取值。
TensorFlow实现神经网络 |
神经网络中的参数是神经网络实现分类或者回归问题中重要的部分。本小节将更加具体地介绍TensorFlow 是如何组织、保存以及使用神经网络中的参数的。在TensorFlow 中,变量(tf.Variable) 的作用就是保存和更新神经网络中的参数。和其他编程语言类似,TensorFlow 中的变量也需要指定初始值。因为在神经网络中,给参数赋予随机初始值最为常见,所以一般也使用随机数给TensorFlow 中的变量初始化。下面一段代码给出了一种在TensorFlow 中声明一个2X3 的矩阵变量的方法:
w = tf.Variable(tf.random_normal([2,3], mean=0, stddev=2))
这段代码调用了TensorFlow 变量的声明函数tf.Variable。在变量声明函数中给出了初始化这个变量的方法。TensorFlow 中变量的初始值可以设置成随机数、常数或者是通过其他变量的初始值计算得到。在上面的样例中,tf.random_normal([2,3],stddev=2)会产生一个2X3 的矩阵,矩阵中的元素是均值为0,标准差为2 的随机数。tf.random_normal 函数可以通过参数mean 来指定平均值,在没有指定时默认为0。通过满足正太分布的随机数来初始化神经网络中的参数是一个非常常用的方法。除了正太分布的随机数,TensorFlow 还提供了一些其他的随机数生成器,下表列出了TensorFlow 目前支持的所有随机数生成器。
TensorFlow 也支持通过常数来初始化一个变量。下表给出了TensorFlow 中常用的常量声明方法。
在神经网络中,偏置项(bias) 通常会使用常数来设置初始值。以下代码给出了一个样例:
b = tf.Variable(tf.zeros([3]))
TensorFlow 也支持通过其他变量的初始值来初始化新的变量。以下代码给出了具体的方法:
w = tf.Variable(tf.random_normal([2,3], mean=0, stddev=2))
w2 = tf.Variable(w.initialized_value())
以下样例介绍了如何通过变量实现神经网络的参数并实现前向传播的过程。
# -*- coding: utf-8 -*- import tensorflow as tf # 声明w1、w2两个变量 w1= tf.Variable(tf.random_normal([2, 3], stddev=1, seed=1)) w2= tf.Variable(tf.random_normal([3, 1], stddev=1, seed=1)) # 暂时将输入特征向量定义为一个常量。x是一个1*2的矩阵 x = tf.constant([[0.7, 0.9]]) # 前向传播 a = tf.matmul(x,w1) y = tf.matmul(a,w2) with tf.Session() as sess: sess.run(tf.initialize_all_variables()) #初始化所有变量 print(sess.run(y)) #[[ 3.95757794]]
完整代码:
# -*- coding: utf-8 -*- import tensorflow as tf from numpy.random import RandomState # 定义神经网络的参数,输入和输出节点 batch_size = 8 # 定义训练数据batch_size w1= tf.Variable(tf.random_normal([2, 3], stddev=1, seed=1)) #定义神经网络的参数 w2= tf.Variable(tf.random_normal([3, 1], stddev=1, seed=1)) x = tf.placeholder(tf.float32, shape=(None, 2), name="x-input") # y_= tf.placeholder(tf.float32, shape=(None, 1), name='y-input') # 定义前向传播过程 a = tf.matmul(x, w1) y = tf.matmul(a, w2) # 损失函数及反向传播算法 cross_entropy = -tf.reduce_mean(y_ * tf.log(tf.clip_by_value(y, 1e-10, 1.0))) train_step = tf.train.AdamOptimizer(0.001).minimize(cross_entropy) # 0.001是学习率 # 生成模拟数据集 dataset_size = 128 rdm = RandomState(1) X = rdm.rand(dataset_size,2) Y = [[int(x1+x2 < 1)] for (x1, x2) in X] # x1+x2<1为正样本 # 创建一个会话来运行TensorFlow程序 with tf.Session() as sess: init_op = tf.global_variables_initializer() sess.run(init_op) # 初始化变量 # 输出目前(未经训练)的参数取值。 print("w1:", sess.run(w1)) print("w2:", sess.run(w2)) print(" ") # w1: [[-0.81131822 1.48459876 0.06532937] # [-2.4427042 0.0992484 0.59122431]] # w2: [[-0.81131822] # [ 1.48459876] # [ 0.06532937]] # 训练模型。 STEPS = 5000 # 设定训练轮数 for i in range(STEPS): start = (i*batch_size) % dataset_size end = min(start+batch_size,dataset_size) # 通过选取的样本训练神经网络并更新参数 sess.run(train_step, feed_dict={x: X[start:end], y_: Y[start:end]}) if i % 1000 == 0: #每隔一段时间计算所有数据上的交叉熵并输出 total_cross_entropy = sess.run(cross_entropy, feed_dict={x: X, y_: Y}) print("After %d training step(s), cross entropy on all data is %g" % (i, total_cross_entropy)) # After 0 training step(s), cross entropy on all data is 0.0674925 # After 1000 training step(s), cross entropy on all data is 0.0163385 # After 2000 training step(s), cross entropy on all data is 0.00907547 # After 3000 training step(s), cross entropy on all data is 0.00714436 # After 4000 training step(s), cross entropy on all data is 0.00578471 # 交叉熵越小说明预测结果和真实结果差距较小 # 输出训练后的参数取值。 print(" ") print("w1:", sess.run(w1)) print("w2:", sess.run(w2)) # w1: [[-1.96182752 2.58235407 1.68203771] # [-3.46817183 1.06982315 2.11788988]] # w2: [[-1.82471502] # [ 2.68546653] # [ 1.41819501]]
1.定义神经网络的结构和前向传播的输出结果。
2.定义损失函数以及选择反向传播优化的算法。
3.生成会话(tf.Session) 并且在训练数据上反复运行反向传播优化算法。
无论神经网络的结构如何变化,这3 个步骤是不变的。
条目7 |
引例
现有训练集如下,请训练一个决策树模型,对未来的西瓜的优劣做预测。
条目8 |
引例
现有训练集如下,请训练一个决策树模型,对未来的西瓜的优劣做预测。
条目9 |
引例
现有训练集如下,请训练一个决策树模型,对未来的西瓜的优劣做预测。