Fork me on GitHub

TensorFlow基础篇与搭建深层神经网络

本文是 Tensorflow:实战Google深度学习框架的第三章与第四章。

第3章 TensorFlow入门

0.1 查看已安装tensorflow版本

由于tensorflow版本不同,可能一些函数的调用也有变换,这时候可能需要查看tensorflow版本,可以在终端输入查询命令如下:

1
2
3
python //windows下cmd进入python环境,linux下终端类似
import tensorflow as tf
tf.__version__

查询tensorflow安装路径为:

1
tf.__path__

参考自:查看已安装tensorflow版本

0.2 Tensorflow:实战Google深度学习框架 源码下载

3.2 TensorFlow数据模型——张量

3.2.1 张量的概念

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

3.2.2 张量的使用

两大类。

一是对中间结果的引用。

二是用来获得计算的结果。tf.Session().run(result)

3.3 TensorFlow运行模型——会话

3.3.1 创建和关闭会话

1
2
3
4
5
6
7
8
# 创建一个会话。
sess = tf.Session()
# 使用会话得到之前计算的结果。
print(sess.run(result))
# 关闭会话使得本次运行中使用到的资源可以被释放。
sess.close()

3.3.2 使用with statement 来创建会话

1
2
3
4
5
6
7
with tf.Session() as sess:
print(sess.run(result))
# 下面的两个命令有相同的功能。
print(sess.run(result))
print(result.eval(session=sess))

3.3.3 指定默认会话

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

3.3.4 通过ConfigProto配置会话

1
2
3
config=tf.ConfigProto(allow_soft_placement=True, log_device_placement=True)
sess1 = tf.InteractiveSession(config=config)
sess2 = tf.Session(config=config)

3.4 TensorFlow实现神经网络

3.4.2 前向传播算法简介

tf.matmul 矩阵乘法

3.4.3 神经网络参数与tensorflow变量

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

产生一个[2,3]的矩阵,矩阵中元素是均值为0,方差为2的随机数。

1.TensorFlow随机数生成函数

函数名称 随机数分布 主要参数
tf.random_normal 正态分布 平均值、标准差、取值类型
tf.truncated_normal 正太分布,但如果随机出来的值离平均值超过2个标准差,那么这个数将会被重新随机 平均值、标准差、取值类型
tf.random_uniform 平均分布 最小、最大取值、取值类型
tf.random_gramma Gramma分布 形状参数alpha、尺度参数beta、取值类型

2.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]

声明了变量之后,程序的第二步会声明一个会话(session)。并通过会话计算结果。

在真正开始计算之前,必须对变量进行初始化:

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

变量分为需要优化的参数(比如神经网络中的参数)和其他参数。
trainable = True,则该变量会加入GraphKeys.TRAINABLE_VARIABLES集合。

维度(shape)和类型(type)是变量最重要的两个属性。

3.4.4 通过tensorflow训练神经网络模型

监督学习的思想。

神经网络优化算法中,最常用的是反向传播算法。

x = tf.constant([[0.7, 0.9]])

常量表示样例导致计算图特变大。使用placeholder机制提供输入数据。

在placeholder定义的时候,这个位置上的数据类型是需要指定的。和其他张量一样,placeholder的类型不可以被改变。

1
2
3
4
5
6
7
8
9
10
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时一个字典(map),在字典中需要给出每个用到的placeholder的取值,否则运行会报错。

输入的数据一般是一个batch,不止一个,placeholder也支持输入多个数据。

1
2
3
4
5
6
7
8
9
10
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]]}))

在得到一个batch的前向传播结果之后,需要定义一个损失函数来刻画当前的预测值与真实答案之间的差距。然后通过反向传播算法来调整网络参数之间的取值使得差距可以被缩小。

3.4.5 完整的神经网络样例程序

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
import tensorflow as tf
from numpy.random import RandomState
# 1. 定义神经网络的参数,输入和输出节点。
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')
# 2. 定义前向传播过程,损失函数及反向传播算法。
a = tf.matmul(x, w1)
y = tf.matmul(a, w2)
# tf.clip_by_value(A, min, max):输入一个张量A,把A中的每一个元素的值都压缩在min和max之间。小于min的让它等于min,大于max的元素的值等于max。
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)
# 3. 生成模拟数据集。
rdm = RandomState(1)
dataset_size = 128
X = rdm.rand(dataset_size, 2)
Y = [[int(x1 + x2 < 1)] for (x1, x2) in X]
# 4. 创建一个会话来运行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("\n")
# 训练模型。
STEPS = 5000 # 设定训练的轮数
for i in range(STEPS):
start = (i * batch_size) % 128
end = (i * batch_size) % 128 + batch_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))
# 输出训练后的参数取值。
print("\n")
print("w1:", sess.run(w1))
print("w2:", sess.run(w2))

输出为:

训练之前的神经网络的参数值:

w1: [[-0.81131822 1.48459876 0.06532937]
[-2.4427042 0.0992484 0.59122431]]

w2: [[-0.81131822]
[ 1.48459876]
[ 0.06532937]]

可以发现,随着训练的进行,交叉熵是逐渐减小的。交叉熵越小说明预测的结果和真实的结果差距越小。

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

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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
训练之后的神经网络的参数值:
>
> w1: [[-1.9618274 2.58235407 1.68203783]
> [-3.46817183 1.06982327 2.11789012]]
>
> w2: [[-1.82471502]
> [ 2.68546653]
> [ 1.41819513]]
# 第4章 深层神经网络
## 4.1 深度学习与深层神经网络
### 4.1.1 线性模型的局限性
### 4.1.2 激活函数实现去线性化
激活函数:tf.nn.relu、tf.sigmoid和tf.tanh。
### 4.1.3 多层网络解决异或运算
单层感知机无法模拟异或运算。加入了隐含层之后,就可以解决异或问题。
## 4.2损失函数的定义
### 4.2.1 经典损失函数
交叉熵(cross entropy)。用q来表示p的交叉熵为:
$$H(p,q)=-\sum_x p(x)\text{log} q(x)$$
p代表的是正确答案,q代表的是预测值。交叉熵刻画的是两个概率分布的距离,也就是说交叉熵越小,两个概率分布越接近。
`softmax`回归:将神经网络的输出变成一个概率分布。
交叉熵的代码实现:
cross_entropy = -tf.reduce_mean(y_ * tf.log(tf.clip_by_value(y, 1e-10, 1.0)))
其中,`tf.clip_by_value`的用法
`tf.clip_by_value(A, min, max):`输入一个张量A,把A中的每一个元素的值都压缩在`min`和`max`之间。小于`min`的让它等于`min`,大于`max`的元素的值等于`max`。
import tensorflow as tf;
import numpy as np;
A = np.array([[1,1,2,4], [3,4,8,5]])
with tf.Session() as sess:
print sess.run(tf.clip_by_value(A, 2, 5))
输出:
> [[2 2 2 4]
>
> [3 4 5 5]]
`tf.log`是对张量中所有元素依次求对数。
`*`操作是元素之间直接相乘,矩阵乘法是`tf.matmul`。
上面三个计算得到的结果是nxm的矩阵。
`tf.reduce_mean`的用法。
v = tf.constant([[1.0, 2.0, 3.0, 4.0], [4.0, 5.0, 6.0, 7.0]])
sess = tf.Session()
init_op = tf.global_variables_initializer()
sess.run(init_op)
print(tf.reduce_mean(v).eval(session=sess))
# 程序输出为:4.0 = sum/8
### 4.2.2 自定义损失函数
当然tensorflow也支持自定义损失函数。
## 4.3 神经网络优化算法
假设用$\theta$表示神经网络中的参数,$J(\theta)$表示在给定的参数取值下,训练集上损失函数的大小,那么整个优化过程可以抽象为:寻找一个参数$\theta$,使得$J(\theta)$最小。
对于参数$\theta$,其梯度为$\frac{\partial}{\partial\theta}J(\theta)$。有了梯度,还需要学习率$\eta $(learning rate)来控制每次参数更新的幅度。
因此,参数更新的公式为:
$$\theta_{n+1} = \theta_n - \eta \frac{\partial}{\partial\theta}J(\theta)$$
需要注意的是梯度下降算法并不能保证达到全局最优解,此外还存在计算时间过长的问题。
为了加速训练过程,可以使用随机梯度下降(stochastic gradient descent)算法。每一轮迭代中随机优化某一条训练数据的损失函数。
在实际应用中,采用折中的方法:**每次计算一小部分训练数据的损失函数**。这一小部分数据被称为<font color=AA00AA>**batch**</font>。
## 4.4 神经网络进一步优化
### 4.4.1 学习率的设置
假设我们要最小化函数 $y=x^2$, 选择初始点 $x_0=5$。
#### 1. 学习率为1时
```python
import tensorflow as tf
TRAINING_STEPS = 10
LEARNING_RATE = 1
x = tf.Variable(tf.constant(5, dtype=tf.float32), name="x")
y = tf.square(x)
train_op = tf.train.GradientDescentOptimizer(LEARNING_RATE).minimize(y)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
for i in range(TRAINING_STEPS):
sess.run(train_op)
x_value = sess.run(x)
print "After %s iteration(s): x%s is %f."% (i+1, i+1, x_value)

结果是:

1
2
3
4
5
6
7
8
9
10
After 1 iteration(s): x1 is -5.000000.
After 2 iteration(s): x2 is 5.000000.
After 3 iteration(s): x3 is -5.000000.
After 4 iteration(s): x4 is 5.000000.
After 5 iteration(s): x5 is -5.000000.
After 6 iteration(s): x6 is 5.000000.
After 7 iteration(s): x7 is -5.000000.
After 8 iteration(s): x8 is 5.000000.
After 9 iteration(s): x9 is -5.000000.
After 10 iteration(s): x10 is 5.000000.

学习率为1的时候,x在5和-5之间震荡。

2. 学习率为0.001时

将上述代码中的学习率设为0.001(很小),如下:

1
LEARNING_RATE = 0.001

运行结果为:

1
2
3
4
5
6
7
8
9
10
After 1 iteration(s): x1 is 4.990000.
After 101 iteration(s): x101 is 4.084646.
After 201 iteration(s): x201 is 3.343555.
After 301 iteration(s): x301 is 2.736923.
After 401 iteration(s): x401 is 2.240355.
After 501 iteration(s): x501 is 1.833880.
After 601 iteration(s): x601 is 1.501153.
After 701 iteration(s): x701 is 1.228794.
After 801 iteration(s): x801 is 1.005850.
After 901 iteration(s): x901 is 0.823355.

学习率为0.001的时候,下降速度过慢,在901轮时才收敛到0.823355。

3. 使用指数衰减的学习率

将上述代码中的学习率设为指数衰减的方式,如下:

1
LEARNING_RATE = tf.train.exponential_decay(0.1, global_step, 1, 0.96, staircase=True)

运行结果:

1
2
3
4
5
6
7
8
9
10
After 1 iteration(s): x1 is 4.000000, learning rate is 0.096000.
After 11 iteration(s): x11 is 0.690561, learning rate is 0.063824.
After 21 iteration(s): x21 is 0.222583, learning rate is 0.042432.
After 31 iteration(s): x31 is 0.106405, learning rate is 0.028210.
After 41 iteration(s): x41 is 0.065548, learning rate is 0.018755.
After 51 iteration(s): x51 is 0.047625, learning rate is 0.012469.
After 61 iteration(s): x61 is 0.038558, learning rate is 0.008290.
After 71 iteration(s): x71 is 0.033523, learning rate is 0.005511.
After 81 iteration(s): x81 is 0.030553, learning rate is 0.003664.
After 91 iteration(s): x91 is 0.028727, learning rate is 0.002436.

使用指数衰减的学习率,在迭代初期得到较高的下降速度,可以在较小的训练轮数下取得不错的收敛程度。

4.4.2 过拟合问题

为了避免过拟合,一个非常常用的方法是正则化(regularization),加入刻画模型复杂程度的指标$R(w)$,优化时优化$J(\theta)+\lambda R(w) $。

常用的正则化方法有:L1正则化和L2正则化。
$$R(w) =||w||_1 =\sum_i|w_i|$$
$$R(w) =||w||_2^2 =\sum_i|w_i^2|$$

无论哪一种正则化的方式,其思想都是通过限制权重的大小,使得模型不能任意拟合训练数据中的随机噪声。

区别在于,L1正则化会使得参数变得更加稀疏,而L2正则化则不会;此外,L1正则化的计算公式不可导,而L2正则化公式可导。

------ 本文结束感谢您的阅读 ------
坚持原创技术分享,您的支持将鼓励我继续创作!