整理自《TensorFlow实战google深度学习框架》
3.1 TensorFlow计算模型--计算图
(1)计算图的概念
Tensor就是张量,可以被简单的理解为多维数组,表面了它的数据结构。Flow体现了它的计算模型,它直观地表达了张量之间通过计算相互转化的过程。TensorFlow是一个通过计算图的形式来表述计算的编程系统,每一个计算都是计算图上的一个节点,而节点之间的边描述了计算之间的依赖关系。
(2)计算图的使用
- 通过 a.graph查看张量所属的计算图,通过tf.get_default_graph函数可以获取当前默认的计算图(#print a.graph is tf.get_default_graph())
- 通过 tf.Graph函数来生成新的计算图
- 通过 tf.Graph.device 函数来指定运行计算的设备,这为TensorFlow使用GPU提供了机制(计算图不仅用来隔离张量和计算,还提供了管理张量和计算的机制)
1. 定义两个不同的图
import tensorflow as tf
g1 = tf.Graph()
with g1.as_default():
v = tf.get_variable("v", [1], initializer = tf.zeros_initializer()) # 设置初始值为0
g2 = tf.Graph()
with g2.as_default():
v = tf.get_variable("v", [1], initializer = tf.ones_initializer()) # 设置初始值为1
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")))
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")))
输出:
[ 0.]
[ 1.]
3.2 TensorFlow数据模型--张量
(1)张量的概念
从功能角度上看,张量可以被简单理解为多维数组。其中零阶张量表示标量(scalar);第一阶张量为向量(vector),也就是一个一维数组;第n阶张量可以理解为一个n维数组。
2. 张量的概念
import tensorflow as tfa = tf.constant([1.0, 2.0], name="a")
b = tf.constant([2.0, 3.0], name="b")
result = a + b
print result
sess = tf.InteractiveSession ()
print(result.eval())
sess.close()
输出:
Tensor("add:0", shape=(2,), dtype=float32)
[ 3. 5.]
从上面的代码可以看出TensorFlow中的张量和Numpy中的数组不同。在张量中并非保存真正的数字,而是保存如何得到这些数字的计算过程。一个张量主要保存了三个属性:名字(name)、维度(shape)和类型(type)。
张量的第一个属性名字不仅是一个张量的唯一标识符,它同样给出了这个张量是如何计算出来的。由于计算图的每一个节点代表了一个计算,计算的结果就保存在张量之中。所以张量和计算图上节点所代表的计算结果是对应的。这样张量的命名就可以通过“node:src_output”的形式来给出,其中node为节点的名称,src_output表示当前张量来自节点的第几个输出。
张量的第二个属性是张量的维度(shape)。比如上面样例中shape=(2,)说明了张量result是一个一维数组,这个数组的长度为2.
张量的第三个属性是类型(type),如果加数a是整数而加数b是实数,这样程序会报类型不匹配的错误。
(2)张量的使用
第一类用途是对中间计算结果的引用,通过张量来存储中间结果,这样可以方便获取中间结果。
第二类情况是当计算图构造完成之后,张量可以用来获得计算结果,也就是得到真实的数字,如tf.Session().run(result)语句来得到计算结果。
3.3 TensorFlow运行模型--会话
前面两节介绍了如何组织数据和运算,这节介绍如何使用会话(session)来执行定义好的运算。会话拥有并管理TensorFlow程序运行时的所有资源。当所有计算完成后需要关闭会话来帮助系统回收资源,否则可能出现资源泄露的问题。
3.1 创建和关闭会话
创建一个会话。
sess = tf.Session()
# 使用会话得到之前计算的结果。
print(sess.run(result))
# 关闭会话使得本次运行中使用到的资源可以被释放。
sess.close()
3.2 使用with statement 来创建会话
with tf.Session() as sess:
print(sess.run(result))
3.3 指定默认会话
sess = tf.Session()
with sess.as_default():
print(result.eval())
# 下面的两个命令有相同的功能。
print(sess.run(result))
print(result.eval(session=sess))
4. 使用tf.InteractiveSession构建会话(提供一种在交互式环境下直接构建默认会话的函数,省去将产生的会话注册为默认会话的过程)
sess = tf.InteractiveSession ()
print(result.eval())
sess.close()
5. 通过ConfigProto配置会话(可以配置类似并行的线程数、GPU分配策略、运行超时时间等参数)
config=tf.ConfigProto(allow_soft_placement=True, log_device_placement=True)
sess1 = tf.InteractiveSession(config=config)
sess2 = tf.Session(config=config)
3.4 TensorFlow 实现神经网络
(1)TensorFlow游乐场
http://playground.tensorflow.org
(2)向前传播算法简介
向前传播算法通过矩阵乘法的方式表达,在TensorFlow中矩阵乘法是非常泳衣实现的。
a=tf.matmul(x,w1)
y=tf.matmul(a,w2)
(3)神经网络参数与TensorFlow变量
变量的作用就是保存和更新神经网络中的参数,TensorFlow中变量(tf.Variable)的作用就是保存和更新神经网络中的参数。
weights=tf.Variable(tf.random_normal([2,3],stddev=2))
TensorFlow中的tf.Variable
函数随机数和常数的生成:
函数名 | 随机数分布 | 主要参数 |
---|---|---|
tf.random_normal |
正态分布 | 平均值、标准差、取值类型 |
tf.truncated_normal |
满足正态分布的随机值,但若随机值偏离平均值超过2个标准差,则这个数会被重新随机 | 平均值、标准差、取值类型 |
tf.random_uniform |
平均分布 | 最大、最小值、取值类型 |
tf.random_gamma |
Gramma分布 | 形状参数alpha、尺度参数beta、取值类型 |
函数名 | 功能 | 示例 |
---|---|---|
tf.zeros |
产生全0的数组 | tf.zeros([2, 3],tf.int32) |
tf.ones |
产生全1的数组 | tf.ones([2, 3],tf.int32) |
tf.fill |
产生一个全部为给定数组的数组 | tf.fill([2,3], 9) |
tf.constant |
产生一个给定值的常量 | tf.constant([2,3,4]) |
也可以通过其他变量的初始值来初始化新的变量。
w2=tf.Variable(weights.initialized_value())
w3=tf.Variable(weights.initialized_value()*2.0)
以下样例介绍了如何通过变量实现神经网络的参数并实现前向传播的过程。
import tensorflow as tf
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.constant([[0.7, 0.9]])
a = tf.matmul(x, w1)
y = tf.matmul(a, w2)
sess = tf.Session()
sess.run(w1.initializer)
sess.run(w2.initializer)
print(sess.run(y))
sess.close()
[[ 3.95757794]]
第一步定义了计算图所有的计算:w1、w2、a和y,但这些被定义的计算并未真正的运行。
第二步声明一个会话,并通过会话计算结果。当会话定义完成后就可以开始真正运行定义好的计算了,但在计算y之前,需要将所用到的变量初始化。
虽然直接调用每个变量的初始化过程是一个可行的方案,但当变量数目增多,或者变量之间存在依赖关系时,单个调用的方案就比较麻烦了。下面演示通过tf.initialize_all_variables函数实现初始化所有变量的过程。
init_op=tf.initialize_all_variables()
sess.run(init_op)
所有变量都会被自动加入GraphKeys.VARIABLES这个集合,可通过tf.all_variables函数拿到计算图上所有的变量。
当构建机器学习模型时,可以通过变量声明函数中的trainable参数来区分需要优化的参数(比如神经网络中的参数)和其他参数(比如迭代的轮数)如果声明变量时参数trainable为True,那么这个变量会被加入GraphKeys.TRAINABLE_VARIABLES集合。
类似张量,变量中最重要的两个属性是维度(shape)和类型(type),变量的类型不可改变,但维度可以通过设置参数validate_shape=Flase.
tf.assign(w1,w2,validate_shape=Flase)
(4)通过TensorFlow训练神经网络模型
使用监督学习的方式设置神经网络参数需要有一个标注好的训练数据集。在神经网络优化算法中,最常用的方法就是方向传播算法(backpropagation),这是一个迭代过程。
因为神经网络的训练过程会需要经过几百万轮甚至几亿轮的迭代,这样计算图会非常大,而且利用率低。TensorFlow提供了placeholder机制用于提供输入数据,相当于定义了一个位置,这个位置中的数据在程序运行时再指定,类型需要指定且不可改变,维度可以推导得出,所以不一定要给出。
下面给出通过placeholde实现向前传播算法的代码。
import tensorflow as tf
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=(1, 2), name="input")
a = tf.matmul(x, w1)
y = tf.matmul(a, w2)
sess = tf.Session()
init_op = tf.global_variables_initializer()
sess.run(init_op)
print(sess.run(y, feed_dict={x: [[0.7,0.9]]}))#需要提供一个feed_dict来指定x的取值
[[ 3.95757794]]
也可以增加多个输入,将1*2的矩阵改为n*2的矩阵
x = tf.placeholder(tf.float32, shape=(3, 2), name="input")
a = tf.matmul(x, w1)
y = tf.matmul(a, w2)
sess = tf.Session()
#使用tf.global_variables_initializer()来初始化所有的变量
init_op = tf.global_variables_initializer()
sess.run(init_op)
print(sess.run(y, feed_dict={x: [[0.7,0.9],[0.1,0.4],[0.5,0.8]]}))
[[ 3.95757794]
[ 1.15376544]
[ 3.16749239]]
在得到一个batch的向前传播结果之后,需要定义一个损失函数来刻画当前的预测值和真实答案之间的差距。然后通过反向传播算法来调整神经网络参数的取值使得差距可以被缩小。
#定义真实值和预测值之间的交叉熵
cross_entropy = -tf.reduce_mean(y_ * tf.log(tf.clip_by_value(y, 1e-10, 1.0))learning_rate=0.001
#定义反向传播优化方法train_step = tf.train.AdamOptimizer(learning_rate).minimize(cross_entropy)
在定义了反向传播算法之后,通过运行sess.run(train_step)就可以对所有在 GraphKeys.TRAINABLE_VARIABLES集合中的变量进行优化,使得batch下损失函数更小。
(5)完整神经网络样例程序
import tensorflow as tf
from numpy.random import RandomState
batch_size=8
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)
rdm=RandomState(1)
dataset_size=128
X=rdm.rand(dataset_size,2)
Y=[[int (x1+x2<1)] for (x1,x2) in X]
with tf.Session() as sess:
init_op=tf.initialize_all_variables()
sess.run(init_op)
print sess.run(w1)
print sess.run(w2)
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 trainint step(s),cross entropy on all data is %g"%(i,total_cross_entropy))
print sess.run(w1)
print sess.run(w2)
-------------------------------------------------------------------------
[[-0.8113182 1.4845988 0.06532937]
[-2.4427042 0.0992484 0.5912243 ]]
[[-0.8113182 ]
[ 1.4845988 ]
[ 0.06532937]]
After 0 trainint step(s),cross entropy on all data is 0.0674925
After 1000 trainint step(s),cross entropy on all data is 0.0163385
After 2000 trainint step(s),cross entropy on all data is 0.00907547
After 3000 trainint step(s),cross entropy on all data is 0.00714436
After 4000 trainint step(s),cross entropy on all data is 0.00578471
[[-1.9618275 2.582354 1.6820377]
[-3.4681718 1.0698231 2.11789 ]]
[[-1.824715 ]
[ 2.6854665]
[ 1.418195 ]]
可总结出训练神经网络的过程可以分为以下3个步骤:
1.定义数据网络的结构和向前传播的输出结果
2.定义损失函数以及选择反向传播优化的算法
3.生成会话并且在训练数据上反复运行反向传播优化算法
参考: