第三章- 【TensorFlow 入门】

主要内容:TensorFlow的计算模型、数据模型和运行模型

1、TensorFlow计算模型—计算图

计算图是TensorFlow中最基本的一个概念,TensorFlow中的所有计算都会被转化为计算图上的节点。计算图是用来描述TensorFlow中的计算。
计算图概念
Tensor: 张量,张量可以被简单理解为多维数组。
Flow: 中文表示为“流”,直观地表达了张量之间通过计算相互转化的过程。

TensorFlow中的每一个计算都是计算图上的一个节点,而节点之间的边描述了计算之间的依赖关系。
依赖关系:一个运算的输入依赖于另外一个运算的输出,那么这两个运算有依赖关系。

1.1计算图的使用

TensorFlow程序分为两个阶段:第一个阶段需要定义计算图中所有的计算,第二阶段为执行计算
计算定义示例如下:

import tensorflow as tf
a=tf.constant([1.0,2.0],name='a')
b=tf.constant([2.0,3.0],name='b')
result = a + b 

TensorFlow程序中,系统会自动维护一个默认的计算图,通过tf.get_default_graph函数可以获取当前默认的计算图,可以通过a.graph可以查看张量所属的计算图,如果没有特意指定,所以这个计算图应该等于当前默认的计算图。print(a.graph is tf.get_default_grapha())
除了默认的计算图,TensorFlow支持通过tf.Graph函数来生成新的计算图。不同的计算图上的张量和运算都不会共享。以下代码示意了如何在不同计算图上定义和使用变量。

import tensorflow as tf
g1=tf.Graph()
with g1.as_default():
   #在计算图g1中定义变量"v",并设置初始值为0。
	v=tf.get_variable(“v”,shape=[1],initializer=tf.zeros_initializer)
	
g2=tf.Graph()
with g2.as_default():
   #在计算图g2中定义变量"v",并设置初始值为1。
	v=tf.get_variable(“v”,shape=[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):
     		#在计算图g1中,变量“v”的取值应该为0,所以下面这行会输出[0.]。
     		print(sess.run(tf.get_variable("v")))

#在计算图g2中读取变量“v”的取值。
with tf.Session(graph=g2) as sess:
     tf.global_variables_initializer().run()
     with tf.variable_scope("",reuse=True):
     		#在计算图g2中,变量“v”的取值应该为1,所以下面这行会输出[1.]。
     		print(sess.run(tf.get_variable("v")))

TensorFlow中的计算图不仅仅可以隔离张量和计算,它还提供了管理张量和计算的机制。计算图可以通过tf.Graph.device函数来指定运行计算的设备。这为TensorFlow使用GPU提供了机制。以下程序可以将加法计算跑在GPU上。

g = tf.Graph()
#指定计算运行的设备
with g.device('/gpu:0'):
	result = a + b

在一个计算图中,可以通过集合(collection)来管理不同类别的资源。
比如通过tf.add_to_collection函数可以将资源加入一个或多个集合中,然后通过tf.get_collection获取一个集合里面的所有资源。这里的资源可以是张量、变量或者运行TensorFlow程序所需要的队列资源,等等。

2、TensorFlow数学模型—张量

在TensorFlow程序中,所有的数据都通过张量的形式来表示,从功能的角度上看,张量可以被简单理解为多维数组。张量是TensorFlow管理数据的形式。

  • 零阶张量: 标量,也就是一个数;
  • 一阶张量: 向量,也就是一维数组;
  • n阶张量: n维数组。

张量中并没有真正保存数字,它保存是如何得到这些数字的计算过程。

import tensorflow as tf
# tf.constant是一个计算,这个计算的结果为一个张量,就保存在变量a中。
a = tf.constant([1.0,2.0],name = 'a')
b = tf.constant([2.0,3.0],name = 'b')
result = tf.add(a,b,name='add')
print(result)
"""
输出:
Tensor("add:0",shape=(2,),dtype=float32)
""" 

张量中主要保存了三个属性:名字(name)、维度(shape)和类型(type)

张量的名字不仅是一个张量的唯一标识符,它同样也给出了这个张量是如何计算出来的。计算图的每一个节点代表了一个计算,计算的结果就保存在张量之中。张量和计算图上节点所代表的计算结果是对应的。这样张量的命名就可以通过“node:src_output”的形式来给出。其中node为节点的名称,src_output表示当前张量来自节点的第几个输出。比如上面代码打出来的"add:0"就说明result这个张量是计算节点“add”输出的第一个结果(编号从0开始)。
第二个属性是张量的维度(shape)。这个属性描述了一个张量的维度信息。比如上面样例中shape=(2,)说明了张量result是一个一维数组,这个数组的长度为2。
第三个属性是类型(type),每一个张量会有一个唯一的类型。

TensorFlow支持14种不同的类型
实数(tf.float32、tf.float64)、整数(tf.int8、tf.int16、tf.int32、tf.int64、tf.uint8)、布尔型(tf.bool)和复数(tf.complex64、tf.complex128)。

2.1张量的使用

张量的使用主要包括两大类

  • 中间结果的引用。
a = tf.constant([1.0,2.0],name='a')
b = tf.constant([3.0,4.0],name='b')
result = a + b
print(a)
#输出结果为
Tensor("a:0", shape=(2,), dtype=float32)

其中a和b其实就是对常量生成这个运算结果的引用,这在做加法运算时就可以直接使用这两个变量,而不需要再去生成这些常量。通过张量来引用计算的中间结果

  • 获得计算结果,也就是得到真实的数字。
    在以上的实例中,可以建立会话,调用run函数来得到张量的具体数字。
tf.Session().run(result)
#输出结果为
array([4., 6.], dtype=float32)

3.TensorFlow运行模型—会话

会话(session):执行定义好的计算。会话拥有并管理TensorFlow程序运行时的所有资源。所有计算完成后需要关闭会话来帮助系统回收资源,否则就可能出现资源泄露的问题。TensorFlow中使用会话的模式有两种,第一种模式需要明确调用会话生成函数和关闭会话函数,这种模式的流程如下:

#创建一个会话
sess = tf.Session()
#使用创建好的会话来得到关心的运算的结果
sess.run(result)
#关闭会话使得本次运行中使用到的资源可以被释放
sess.close()

缺点:当程序出现异常退出时,关闭会话的函数可能就不会被执行从而导致资源泄露。为了解决这个问题,可以采用上下文管理器来使用会话。如下所示:

#创建一个会话,并通过python的上下文管理器来管理这个会话。
with tf.Session() as sess:
	sess.run()
#不需要调用sess.close()函数来关闭会话。
#上下文退出时会话关闭和资源释放也自动完成了。

TensorFlow不会生成默认的会话,而是需要手动指定。当默认的会话别指定之后,可以通过tf.Tensor.eval函数来计算一个张量的取值。以下代码展示了通过设定默认会话来计算张量的取值。

sess=tf.Session()
with sess.as_default():
	print(result.eval())

以下代码可以完成相同的功能

sess=tf.Session()
#以下两个命令有相同的功能
print(sess.run(result))
print(result.eval(session=sess))

在交互环境下,通过设置默认会话的方式来获取张量的取值更加方便。所以TensorFlow提供了一种在交互环境下直接构建默认会话的函数。这个函数就是tf.InteractiveSession。使用这个函数会自动生成的会话注册为默认会话。如下:

sess = tf.InteractiveSeesion()
print(result.eval())
sess.close()

4.TensorFlow实现神经网络算法

4.1神经网络参数与TensorFlow变量

TensorFlow中,变量(tf.Variable)的作用就是保存和更新神经网络中的参数。下面代码给出了TensorFlow中声明一个 2 × 3 2\times 3 2×3的矩阵变量的方法,j矩阵的元素是均值为0,标准差为2的随机数。

weights = tf.Variable(tf.random_normal([2,3],stddev=2))

在神经网络中,偏置项通常采用常数来设置初始值。以下代码给出了一个样例。该样例生成一个初始值全部为0且长度为3的变量。

biases =tf.Variable(tf.zeros([3]))

TensorFlow也支持通过其他变量的初始值来初始化新的变量,如下:

w2 = tf.Variable(weights.initialized_value())
w3 = tf.Variable(weights.initialized_value() *  2.0)

其中,w2和weights参数相同,w3的初始值是weights初始值的两倍。
在TensorFlow中,一个变量的值在被使用之前,这个变量的初始化过程需要被明确地调用。以下样例介绍了如何通过变量实现神经网络的参数并实现前向传播的过程。

import tensorflow as tf

#声明w1、w2两个变量。这里还通过设置seed参数设定了随机种子,这样就可以保证每次运行得到的结果是一样的
w1=tf.Variable(tf.randoom_normal((2,3),stddev=1,seed=1))
w2=tf.Variable(tf.randoom_normal((3,1),stddev=1,seed=1))
#暂时将输入的特征向量定义为一个常量。注意这里x是一个$1\times2$ 
x=tf.constant([[0.7,0.9]])

#采用前面描述的前向传播算法获得神经网络的输出。
a = tf.matmul(x,w1)
y = tf.matmul(a,w2)

sess = tf.Session()

#这里不能通过sess.run(y)来获取y的取值,因为w1和w2都还没有运行初始化过程。以下两行分别初始化w1和w2两个变量。
sess.run(w1.initializer)
sess.run(w2.initializer)
print(sess.run(y))
sess.close()

note:变量虽然给出了初始化的方法,但并未进行初始化的过程,需要调用sess.run(w1.initializer)来执行初始化,然后才能通过会话的run函数来输出结果。

虽然直接调用每个变量的初始化过程是一个可行的方案,但是当变量数目增多,或者变量之间存在依赖关系时,单个调用的方案就比较麻烦了。为了解决这个问题,TensorFlow提供了一个更加便捷的方式来完成变量初始化过程。以下程序通过tf.global_variables_initializer函数实现初始化所有变量的过程。

init_op = tf.global_variables_initializer()
sess.run(init_op)

TensorFlow中,所有的变量都会自动地加入到GraphKeys.VARIABLES这个集合中。通过tf.global_variables()函数可以拿到当前计算图上所有的变量。拿到计算图上所有的变量有助于持久化整个计算图的运行状态。

4.2通过TensorFlow训练神经网络模型

每轮迭代中选取的数据都要通过常量来表示,那么TensorFlow的计算图将会太大。因为每生成一个常量,TensorFlow都会在计算图中增加一个节点。一般来说,一个神经网络的训练过程会需要经过几百万轮甚至几亿轮的迭代,这样计算图就会非常大,而且利用率很低。为了避免这个问题,TensorFlow提供了placeholder机制用于提供输入数据,placeholder相当于定义了一个位置,这个位置中的数据在程序运行时再指定 。这样在程序中就不需要生成大量常量来提供输入数据,而只需要将数据通过placeholder传入TensorFlow计算图。在placeholder定义时,这个位置上的数据类型是需要指定的。

#数据的类型需要给出,维度可以通过数据推导出来,不一定要给出
x = tf.placeholder(tf.float32,shape=(1,2),name="input")
#运行时给出数据信息
print(sess.run(y,feed_dict={x:[[0.7,0.9]]}))

fee_dict是一个字典,需要给出每个用到placeholder的取值。以上只是一个样例,当提供一个batch的训练样例,则需要将shape修改为 n × 2 n\times 2 n×2,这样可以表示n个样例的前向传播过程,每一行为一个样例数据,前向传播结果为 n × 1 n\times 1 n×1的矩阵。如下所示:

x = tf.placeholder(tf.float32,shape=(3,2),name="input")
#运行时给出数据信息
print(sess.run(y,feed_dict={x:[[0.7,0.9],[0.1,0.4],[0.5,0.8]]}))
"""
输出结果为:
[[3.95757794],  #为样例[0.7,0.9]的输出值
[1.15376544],   #为样例[0.1,0.4]的输出值
[3.16749191]]   #为样例[0.5,0.8]的输出值
"""

在得到一个batch的前向传播结果后,需要定义一个损失函数来刻画当前的预测值和真实值之间的差距,然后通过反向传播算法来调整神经网络参数的取值使得差距被缩小。以下代码定义了一个简单的损失函数,并通过TensorFlow定义了反向传播的算法。

#使用sigmoid函数将y转换为0~1之间的数值。转换后y代表预测是正样本的概率,1-y代表预测是负样本的概率
y=tf.sigmoid(y)
#定义损失函数来刻画预测值和真实值之间的差距
 #y_表示真实值
cross_entropy = -tf.reduce_mean(
y_*tf.log(tf.clip_by_value(y,1e-10,1.0))+(1-y_)*tf.log(tf.clip_by_value(1-y),1e-10,1.0))
#定义学习率
learing_rate = 0.001
#定义反向传播算法中用来优化神经网络的参数
train_step = tf.train.AdamOptimizer(learing_rate).minimize(cross_entropy)

之后可以通过sess.run(train_step)来优化GraphKeys.TRAINABLE_VARIABLES集合中的变量。

训练神经网络的过程可以分为以下三个步骤

  • 定义神经网络的结构和前向传播的输出结果
  • 定义损失函数以及选择反向传播优化的算法
  • 生成会话(tf.Session)并且在训练数据上反复运行反向传播优化算法。

无论神经网络结构如何变化,这三个步骤是不变的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值