TensorFlow实战Google深度学习框架 笔记 第二、三章

TensorFlow实战Google深度学习框架 笔记 第二、三章

第二章 TensorFlow 环境搭建

简单示例代码:

1
2
3
4
5
6
7
8
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
sess = tf.Session()
sess.run(result)
---输出---
array([3., 5.], dtype=float32)

第三章 TensorFlow 入门

3.1 计算图

  • 系统默认维护一个计算图,通过tf.get_default_graph获取当前默认计算图。
  • 通过a.graph查看张量a所属计算图。示例:
1
2
3
print(a.graph is tf.get_default_graph())
---输出---
True
  • 在不同计算图上定义和使用变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import tensorflow as tf

g1 = tf.Graph()
with g1.as_default():
#g1中定义"v", 设置为0。
v = tf.get_variable(
"v", initializer = tf.zeros_initializer(shape = [1])
)

g2 = tf.Graph()
with g2.as_default():
#g2中定义"v", 设置为1。
v = tf.get_variable(
"v", initializer = tf.ones_initializer(shape = [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")))
#g1中"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")))
#g2中"v"输出1。
  • 对计算图指定计算设备
1
2
3
4
g = tf.Graph()
# 指定计算运行的设备。
with g.device('/gpu:0'):
result = a + b
  • 在一个计算图中,可以通过集合(collection)管理不同类别的资源。通过tf.add_to_collection函数可以将资源加入一个或多个集合中,然后通过tf.get_collection获取一个集合里面的所有资源。这里的资源可以是张量、变量或者运行TensorFlow 程序所需要的队列资源,等等。TensorFlow 也自动管理了一些最常用的集合:
集合名称 集合内容 使用场景
tf.GraphKeys.VARIABLES 所有变量 持久化 TensorFlow 模型
tf.GraphKeys.TRAINABLE_VARIABLES 可学习的变量(一般指神经网络中的参数) 模型训练、生成模型可视化内容
tf.GraphKeys.SUMMARIES 日志生成相关的张量 TensorFlow 计算可视化
tf.GraphKeys.QUEUE_RUNNERS 处理输入的QueueRunner 输入处理
tf.GraphKeys.MOVING_AVERAGE_VARIABLES 所有计算了滑动平均值的变量 计算变量的滑动平均值

3.2 张量

张量可以理解为多维数组,一阶张量为标量(scalar),二阶张量为向量(vector),n阶张量可以理解为n维数组。张量不保存数值,保存的是计算数值的过程。

1
2
3
4
5
6
7
8
import tensorflow as tf

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”输出的第一个结果。
  • 维度:描述张量的维度信息。shape=(2,)说明了result是一个一维数组,这个数组的长度为2。
  • 类型:计算类型需匹配否则报错。例,若将以上代码的a小数点去掉,成为a = tf.constant([1 , 2], name="a")则运行报错:
    ValueError: Tensor conversion requested dtype int32 for Tensor with dtype float32:'Tensor("b:0", shape=(2,), dtype=float32)'
    可以指定a的类型则不会出问题:
    a = tf.constant([1,2], name="a", dtype=tf.float32)

TensorFlow支持的14种不同类型:
实数(tf.float32, tf.float64), 整数(tf.int8, tf.int16, tf.int32, tf.int64, tf.unit8), 布尔型(tf.bool)和复数(tf.complex64, tf.complex128)。
通过result.get_shape函数可以获取结果张量的维度信息。
张量没有存储计算结果,但是可以通过会话获得,例如通过tf.Session().run(result)获得result结果。

3.3 会话(TensorFlow运行模型)

使用会话的方式:

  1. 手动释放:
1
2
3
4
5
6
7
# 创建一个会话。
sess = tf.Session()
# 使用这个创建好的会话来得到关心的运算的结果。比如可以调用sess.run(result),
# 来得到 3.1 节样例中张量 result 的取值。
sess.run(...)
# 关闭会话使得本次运行巾使用到的资源可以被释放。
sess.close()
  1. 自动释放:
1
2
3
4
5
6
# 创建一个会话,并通过 Python 中的上下文管理器来管理这个会话。
with tf.Session() as sess:
# 使用创建好的会话来计算关心的结果。
sess.run (...)
# 不需要再调用"Session.close()"函数来关闭会话,
# 当上下文退出时会话关闭和资源释放也自动完成了。

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

1
2
3
sess = tf.Session()
with sess.as default():
print(result. eval())

以下代码功能相同

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

使用tf.lnteractiveSession函数会自动将生成的会话注册为默认会话。

1
2
3
sess = tf.InteractiveSession()
prirnt(result. eval())
sess.close()

以上,无论使用哪种方法都可以通过 ConfigProto Protocol BufferCD来配置需要生成的会话,方法:

1
2
3
config = tf.ConfigProto(allow soft placement=True, log_device_placement=True)
sessl = tf.InteractiveSession(config=config)
sess2 = tf.Session(config=config)

ConfigProto常用配置参数:

  • allow_soft_placement:
    这是一个布尔型的参数,默认为False,当它为 True 时,在以下任意一个条件成立时, GPU 上的运算可以放到 CPU 上进行 :
    1. 运算无法在 GPU 上执行。
    2. 没有 GPU 资源 (比如运算被指定在第二个 GPU 上运行,但是机器只有一个 GPU)。
    3. 运算输入包含对 CPU 计算结果的引用。
  • log_device_placement:
    这也是一个布尔型的参数,当它为 True 时日志中将会记录每个节点被安排在哪个设备上以方便调试。生产环境中设置为 False 可以减少日志量。

3.4 TensorFlow实现神经网络

矩阵乘法:

1
2
a = tf.matmul(x, w1)
y = tf.matmul(a, w2)

变量初始化

1
2
#产生一个 2×3 的矩阵,矩阵中的元素是均值为 0,标准差为 2 的随机数。可以通过参数 mean 来指定平均值,默认为0。
weights = tf.Variable(tf.random_normal([2, 3]), stddev=2))

TensorFlow 随机数生成函数表

函数名称 随机数分布 主要参数
tf.random_normal 正态分布 平均值、标准差、取值类型
tf.truncated_normal 正态分布,但如果随机出来的值偏离平均值超过2个标准差就重新随机 平均值、标准差、取值类型
tf.random_uniform 均匀分布 最小、最大取值,取值类型
tf.random_gamma Gamma分布 形状参数alpha、尺度参数 beta、取值类型

TensorFlow常数生成函数表

函数名称 功能 样例
tf.zeros 产生全 0 的数组 tf.zeros([2, 3], int32) -> [[0, 0, 0], [0, 0, 0]]
tf.ones 产生全 1 的数组 tf.ones([2, 3], int32) -> [[1, 1, 1], [1, 1, 1]]
tf.fill 产生一个全部为给定数字的数组 tf.fill([2, 3], 9) -> [[9, 9, 9], [9, 9, 9]]
tf.constant 产生一个给定值的常量 tf.constant([1, 2, 3]) -> [1, 2, 3]

一些以常数初始化数值的方法:

1
2
3
4
5
6
biases = tf.Variable(tf.zeros([3]))
# 生成一个初始值全部为 0 且长度为 3 的变量
w2 = tf.Variable(weights.initialized_value())
# w2 的初始值被设置成了与 weights 变量相同
w3 = tf.Variable(weights.initialized_value() * 2.0)
# w3 的初始值则是 weights 初始值的两倍

一个前向传播过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import tensorflow as tf
# 声明 w1、w2 两个变盘。这里还通过 seed 参数设定了随机种子,
# 这样可以保证每次运行得到的结果是一样的。
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]])
# 通过 3.4.2 节描述的前向传播算法获得神经网络的输出。
a = tf.matmul(x, w1)
y = tf.matmul(a, w2)
sess = tf.Session()
# 与 3.4.2 中的计算不同,这里不能直接通过 sess.run(y) 来获取 y 的取值,
# 因为 w1 和 w2 都还没有运行初始化过程。以下两行分别初始化了 w1 和 w2 两个变量。
sess.run(w1.initializer) # 初始化 w1。
sess.run(w2.initializer) # 初始化 w2。
# 输出[[3.95757794]]。
print(sess.run(y))
sess.close()

当需要初始化的变量变多,可以使用以下方法初始化:

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

  • 通过 tf.global_variables() 函数可以拿到当前计算图上所有的变量。
  • 可以通过变量声明函数中的 trainable 参数来区分需要优化的参数(比如神经网络中的参数)和其他参数(比如选代的轮数)。如果声明变量时参数 trainable 为 True,那么这个变量将会被加入到GraphKeys.TRAINABLE_VARIABLES 集合。
  • 类似张量,维度(shape)和类型 (type) 也是变量最重要的两个属性,一个变量在构建之后,它的类型就不能再改变了。
1
2
3
4
5
6
7
w1 = tf.Variable(tf.random_normal([2, 3], stddev=1), name="w1")
w2 = tf.Variable(tf.random_normal([2, 3], dtype=tf.float64, stddev=1),
name="w2")
w1.assign(w2)
'''
程序将报错:
TypeError: Input 'value' of 'Assign' Op has type float64 that does not match type float32 of argument 'ref'.
  • 维度在程序运行中是有可能改变的,但是需要通过设置参数validate_shape=False。
1
2
3
4
5
6
7
8
w1 = tf.Variable(tf.random_normal([2, 3], stddev=1), name = "w1")
w2 = tf.Variable(tf.random_normal([2, 2], stddev=1), name = "w2")
# 下面这句会报维度不匹配的错误:
# ValueError: Dimension 1 in both shapes must be equal, but are 3 and 2
# for 'Assign_1' (op: 'Assign') with input shapes: [2, 3], [2, 2].
tf.assign(w1, w2)
# 这一句可以被成功执行。
tf.assign(w1, w2, validate_shape=False)

完整训练模型

  1. placeholder预占用输入数据位置
1
2
3
x = tf.placeholder(tf.float32, shape = (3, 2), name="input")
# 因为 x 在定义时指定了 n 为 3,所以在运行前向传播过程时需要提供 3 个样例数据。
print(sess.run(y, feed_dict={x: [[0.7, 0.9), [0.1, 0.4] , [0.5, 0.8]]}))
  1. 定义一个简单的损失函数
1
2
3
4
5
6
7
8
9
10
11
12
# 使用 sigmoid 函数将 y 转换为 0~1 之间的数值。转换后 y 代表预测是正样本的概率, 1-y 代表
# 预测是负样本的概率 。
y=tf.sigmoid(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)))
# 定义学习率,在第 4 章中将更加具体的介绍学习率。
learning_rate = 0.001
# 定义反向传播算法来优化神经网络中的参数。
train_step =\
tf.train.AdamOptimizer(learning_rate) .minimize (cross_entropy)

以上代码中,train_step定义了反向传播优化方法,通常包括三种tf.train.GradientDescentOptimizer、 tf.train.AdamOptimizer和tf.train.MomentumOptimizer。
在定义了反向传播算法之后,通过运行 sess.run(train_ step)就可以对所有在 GraphKeys .TRAINABLE_VARIABLES 集合中的变量进行优化,使得在当前 batch 下损失函数更小。

完整样例程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import tensorflow as tf

# NumPy 是一个科学计算的工具包,这里通过 NumPy 工具包生成模拟数据集。
from numpy.random import RandomState

# 定义训练数据 batch 的大小。
batch_size = 8

# 定义神经网络的参数,这里还是沿用 3.4.2 小节中给出的神经网络结构 。
w1 = tf.Variable(tf.random_normal([2, 3], stddev=1 , seed=1))
w2 = tf.Variable(tf.random_normal([3, 1], stddev=1 , seed=1))

# 在 shape 的一个维度上使用 None 可以方便使用不同的 batch 大小。在训练时需要把数据分
# 成比较小的 batch , 但是在测试时,可以一次性使用全部的数据。当数据集比较小时这样比较
# 方便测试,但数据集比较大时,将大量数据放入一个 batch 可能会导致内存溢出。
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)

# 定义损失函数和反向传播的算法。
y = tf.sigmoid(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)))
train_step = tf.train.AdamOptimizer(0.001).minimize(cross_entropy)

# 通过随机数生成一个模拟数据集。
rdm = RandomState(1)
dataset_size = 128
X = rdm.rand(dataset_size, 2)
# 定义规则来给出样本的标签。在这里所有 x1+x2<1 的样例都被认为是正样本(比如零件合格),
# 而其他为负样本(比如零件不合格)。和 TensorFlow 游乐场中的表示法不大一样的地方是,
# 在这里使用 0 来表示负样本, 1 来表示正样本。大部分解决分类问题的神经网络都会采用
# 0 和 1 的表示方法。
Y = [[int(x1+x2 < 1)] for (x1, x2) in X]
# 创建一个会话来运行 TensorFlow 程序。
with tf.Session() as sess:
init_op = tf.global_variables_initializer()
# 初始化变量。
sess.run(init_op)

print(sess.run(w1))
print(sess.run(w2))

'''
在训练之前神经网络参数的值:
w1 = [[-0.81131822, 1.48459876, 0.06532937]
[-2.44270396, 0.0992484, 0.59122431]]
w2 = [[-0.81131822), [1.48459876], [0.06532937]]
'''

# 设定训练的轮数。
STEPS = 5000
for i in range (STEPS):
# 每次选取 batch_size 个样本进行训练。
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 1.89805
After 1000 training step (s), cross entropy on all data is 0.655075
After 2000 training step (s), cross entropy on all data is 0.626172
After 3000 training step (s), cross entropy on all data is 0.615096
After 4000 training step (s), cross entropy on all data is 0.610309
通过这个结果可以发现随着训练的进行,交叉煽是逐渐变小的。交叉熵越小说明
预测的结果和真实的结果差距越小。
'''
print(sess.run(w1))
print(sess.run(w2))
'''
在训练之后神经网络参数的值:
w1 = [[0.02476984, 0.5694868, 1.69219422]
[-2.19773483, -0.23668921, 1.11438966]]
w2 = [[-0.45544702], [0.49110931], [-0.9811033]]

可以发现这两个参数的取值已经发生了变化,这个变化就是训练的结果。
它使得这个神经网络能更好地拟合提供的训练数据。
'''

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×