TensorFlow实战Google深度学习框架 笔记 第四章

TensorFlow实战Google深度学习框架 笔记 第四章

去线性化

TensorFlow提供七种非线性激活函数,tf.nn.relu, tf.sigmoid, tf.tanh最为常见。




加入偏置项和激活函数的神经网络结构图

以下代码展示了上述前向传播实现过程。

1
2
a = tf.nn.relu(tf.matmul(x, w1) + biases1)
y = tf.nn.relu(tf.matmul(a, w2) + biases2)

损失函数

经典损失函数

分类问题回归问题是监督学习的两大种类。

分类问题

交叉熵 $H(p,q)$ 描述了通过概率 $q$ 表达概率 $p$ 的困难程度,值越小表示越容易。

$$
H(p,q)= -\sum_{x}p(x)\log q(x)
$$

softmax回归将一个$n$维数据转换为一组满足概率分布条件的数值。

$$
softmax(y)_i=y’_i=\frac{e^{y_i}}{\sum^n_{j=1}e^{y_j}}
$$

交叉熵代码示例

1
2
# 以y_表示正确结果,y表示预测结果
cross_entropy = -tf.reduce_mean(y_ * tf.log(tf.clip_by_value(y, 1e-10, 1.0)))

其中:

  • tf.clip_by_value函数可以将一个张量中的数值限制在一个范围之内避免一些运算错误(比如 log0 是无效的)。
1
2
3
v = tf.constant([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
print tf.clip_by_value(v, 2.5, 4.5).eval()
# 输出 [[2.5 2.5 3.] [4. 4.5 4.5]]
  • tf.log函数对张量中所有元素依次求对数。
1
2
3
v = tf.constant([1.0, 2.0, 3.0])
print tf.log(v).eval()
# 输出[0. 0.69314718 1.09861231]
  • 矩阵元素对应相乘用”*“,矩阵作矩阵乘法用tf.matmul
1
2
3
4
5
6
v1 = tf.constant([[1.0, 2.0], [3.0, 4.0]])
v2 = tf.constant([[5.0, 6.0], [7.0, 8.0]])
print (v1 * v2).eval()
# 输出 [[5. 12.] [ 21. 32.]]
print tf.matmul(v1, v2).eval()
# 输出 [[19. 22.] [ 43. 50.]]
  • tf.reduce_mean求均值
1
2
3
v = tf.constant([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
print tf.reduce_mean(v).eval()
# 输出值为3.5

交叉煽一般会与 softmax 回归一起使用,可以直接通过以下代码来实现使用了 softmax 回归之后的交叉熵损失函数:

1
2
cross_entropy = tf.nn.softmax_cross_entropy_with_logits(
labels = y_, logits=y)

以上 y 代表了原始神经网络的输出结果,而 y_ 给出了标准答案。

回归问题

与分类问题不同,回归问题解决的是对具体数值的预测。
对于回归问题,最常用的损失函数是均方误差 (MSE, mean squared error)。定义如下:

$$
MSE(y,y’)=\frac{\sum_{i=1}^n(y_i-y’_i)^2}n
$$

其中$y_i$为一个batch中第i个数据的正确答案,而$y_i’$ 为神经网络给出的预测值。
代码实现

1
2
mse = tf.reduce_mean(tf.square(y_ - y))
# y代表了神经网络的输出答案, y_代表了标准答案。

自定义损失函数

例如(书上p79):

$$
Loss(y, y’)=\sum^n_{i=1}f(y_i, y’_i)
$$

$$
f(x,y)=\begin{cases}
{a(x-y)} & {x > y} \
{b(y-x)} & {x \le y}
\end{cases}
$$

代码实现:

1
loss = tf.reduce_sum(tf.where(tf.greater(v1, v2), (v1 - v2) * a, (v2 - v1) * b))

tf.greater会比较这两个输入张量中每一个元素的大小,并返回比较结果 。
tf.where函数有三个参数。第一个为选择条件根据, 当选择条件为 True 时,tf.where 函数会选择第二个参数中的值, 否则使用第三个参数中的值。
代码示例:

1
2
3
4
5
6
7
8
9
import tensorflow as tf
v1 = tf.constant([1.0, 2.0, 3.0, 4.0])
v2 = tf.constant([4.0, 3.0, 2.0, 1.0])
sess = tf.InteractiveSession()
print tf.greater(v1, v2).eval()
# 输出 [False False True True]
print tf.where(tf.greater(v1, v2), v1, v2).eval()
# 输出[4. 3. 3. 4.]
sess.close()

使用自定义的损失函数进行神经网络训练的示例代码如下:

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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import tensorflow as tf
from numpy.random import RandomState
# 定义神经网络的相关参数和变量。
batch_size = 8
x = tf.placeholder(tf.float32, shape=(None, 2), name="x-input")
y_ = tf.placeholder(tf.float32, shape=(None, 1), name='y-input')
w1= tf.Variable(tf.random_normal([2, 1], stddev=1, seed=1))
y = tf.matmul(x, w1)

# 定义损失函数使得预测少了的损失大,于是模型应该偏向多的方向预测。
loss_less = 10
loss_more = 1
loss = tf.reduce_sum(tf.where(tf.greater(y, y_), (y - y_) * loss_more, (y_ - y) * loss_less))
train_step = tf.train.AdamOptimizer(0.001).minimize(loss)

# 生成模拟数据集
rdm = RandomState(1)
X = rdm.rand(128,2)
Y = [[x1+x2+(rdm.rand()/10.0-0.05)] for (x1, x2) in X]

#训练模型。
with tf.Session() as sess:
init_op = tf.global_variables_initializer()
sess.run(init_op)
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:
print("After %d training step(s), w1 is: " % (i))
print sess.run(w1), "\n"
print "Final w1 is: \n", sess.run(w1)

'''
After 0 training step(s), w1 is:
[[-0.81031823]
[ 1.4855988 ]]

After 1000 training step(s), w1 is:
[[ 0.01247112]
[ 2.1385448 ]]

After 2000 training step(s), w1 is:
[[ 0.45567414]
[ 2.17060661]]

After 3000 training step(s), w1 is:
[[ 0.69968724]
[ 1.8465308 ]]

After 4000 training step(s), w1 is:
[[ 0.89886665]
[ 1.29736018]]

Final w1 is:
[[ 1.01934695]
[ 1.04280889]]
'''

#重新定义损失函数,使得预测多了的损失大,于是模型应该偏向少的方向预测。
loss_less = 1
loss_more = 10
loss = tf.reduce_sum(tf.where(tf.greater(y, y_), (y - y_) * loss_more, (y_ - y) * loss_less))
train_step = tf.train.AdamOptimizer(0.001).minimize(loss)

with tf.Session() as sess:
init_op = tf.global_variables_initializer()
sess.run(init_op)
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:
print("After %d training step(s), w1 is: " % (i))
print sess.run(w1), "\n"
print "Final w1 is: \n", sess.run(w1)

'''
After 0 training step(s), w1 is:
[[-0.81231821]
[ 1.48359871]]

After 1000 training step(s), w1 is:
[[ 0.18643527]
[ 1.07393336]]

After 2000 training step(s), w1 is:
[[ 0.95444274]
[ 0.98088616]]

After 3000 training step(s), w1 is:
[[ 0.95574027]
[ 0.9806633 ]]

After 4000 training step(s), w1 is:
[[ 0.95466018]
[ 0.98135227]]

Final w1 is:
[[ 0.95525807]
[ 0.9813394 ]]
'''

#定义损失函数为MSE。
loss = tf.losses.mean_squared_error(y, y_)
train_step = tf.train.AdamOptimizer(0.001).minimize(loss)

with tf.Session() as sess:
init_op = tf.global_variables_initializer()
sess.run(init_op)
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:
print("After %d training step(s), w1 is: " % (i))
print sess.run(w1), "\n"
print "Final w1 is: \n", sess.run(w1)

'''
After 0 training step(s), w1 is:
[[-0.81031823]
[ 1.4855988 ]]

After 1000 training step(s), w1 is:
[[-0.13337609]
[ 1.81309223]]

After 2000 training step(s), w1 is:
[[ 0.32190299]
[ 1.52463484]]

After 3000 training step(s), w1 is:
[[ 0.67850214]
[ 1.25297272]]

After 4000 training step(s), w1 is:
[[ 0.89473999]
[ 1.08598232]]

Final w1 is:
[[ 0.97437561]
[ 1.0243336 ]]
'''

神经网络优化算法

神经网络大致遵循以下过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
batch_size = n

# 每次读取一小部分数据作为当前的训练数据来执行反向传播算法。
x = tf.placeholder(...)
y_ = tf.placeholder(...)

# 定义神经网络结构和优化算法。
loss = ...
train_step = tf.train.AdamOptimizer(0.001).minimize(loss)

# 训练
with tf.Session() as sess:
# 参数初始化
...
# 迭代跟新参数
for i in range(STEPS):
# 准备 batch_size 个训练数据。一般将所有训练数据随机打乱之后再选取可以得到更好的优化效果。
current_X, current_Y = ...
sess.run(train_step, feed_dict={x: current_X, y_: current_Y})

神经网络进一步优化

学习率

tf.train.exponential_decay 函数实现了指数衰减学习率,实现了以下代码功能:

1
2
decayed_learning_rate = \
learning_rate * decay_rate ^ (global_step / decay_steps)
  • decayed_learning_rate 为每一轮优化时使用的学习率
  • learning_rate 为事先设定的初始学习率
  • decay_rate 为衰减系数
  • decay_steps 为衰减速度

tf.train.exponential_decay 函数可以通过设置参数 staircase 选择不同的衰减方式。 staircase 的默认值为 False。当 staircase 的值被设置为 True 时, global_step /decay_steps 会被转化成整数。这使得学习率成为一个阶梯函数( staircase function ),如下图所示:




指数衰减学习率随着法代轮数的变化图

tf.train.exponential_decay 示例:

1
2
3
4
5
6
7
8
9
global step = tf.Variable(O)

# 通过 exponential_decay 函数生成学习率。
learning_rate = tf.train.exponential_decay(
0.1, global_step, 100, 0.96, staircase=True)
# 使用指数衰减的学习率。在 minimize 函数中传入 global_step 将自动更新
# global_step 参数,从而使得学习率也得到相应更新。
learning_step = tf.train.GradientDescentOptimizer(learning_rate)\
.minimize( ... my loss ... , global_step = global_step)

过拟合问题

L1 正则化会让参数变得更稀疏,而 L2 正则化不会。
L2 正则化不会让参数变得稀疏的原因是当参数很小时,比如 0.001 ,这个参数的平方基本上就可以忽略了,于是模型不会进一步将这个参数调整为 0。
其次,L1 正则化的计算公式不可导,而 L2 正则化公式可导。
带 L2 正则化的损失函数定义:

1
2
3
4
w = tf.Variable(tf.random_normal([2, 1), stddev=1, seed=1))
y = tf.matmul(x, w)

loss = tf.reduce_mean(tf.square(y_ - y)) + tf.contrib.layers.12_regularizer(lambda) (w)

lambda 参数表示了正则化项的权重。

TensorFlow 提供:
tf.contrib.layers.l1_regularizer 可以计算 L1 正则化项的值。
tf.contrib.layers.l2_regularizer 可以计算 L2 正则化项的值。
以下为两个正则化的计算实例:

1
2
3
4
5
6
7
8
weights = tf.constant([[1.0, -2.0], [-3.0, 4.0]])

with tf.Session() as sess:
#输出为(|1|+|-2|+|-3|+|4|)×0.5=5。其中 0.5 为正则化项的权重。
print sess.run(tf.contrib.layers.11_regularizer(.5)(weights))
#输出为(1*1 + (-2)*(-2) + (-3)*(-3) + 4*4)/2×0.5=7.5
#TensorFlow 会将L2的正则化损失值除以 2 使得求导得到的结果更加简洁 。
print sess.run(tf.contrib.layers.12_regularizer(.5)(weights))

可以使用 TensorFlow 中提供的集合 (collention) 解决训练参数增多后更加复杂的问题。
以下代码给出了通过集合计算一个 5 层神经网络带 L2 正则化的损失函数的计算方法。

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
# 生成模拟数据集。
import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np

data = []
label = []
np.random.seed(0)

# 以原点为圆心,半径为1的圆把散点划分成红蓝两部分,并加入随机噪音。
for i in range(150):
x1 = np.random.uniform(-1,1)
x2 = np.random.uniform(0,2)
if x1**2 + x2**2 <= 1:
data.append([np.random.normal(x1, 0.1),np.random.normal(x2,0.1)])
label.append(0)
else:
data.append([np.random.normal(x1, 0.1), np.random.normal(x2, 0.1)])
label.append(1)

data = np.hstack(data).reshape(-1,2)
label = np.hstack(label).reshape(-1, 1)
plt.scatter(data[:,0], data[:,1], c=label,
cmap="RdBu", vmin=-.2, vmax=1.2, edgecolor="white")
plt.show()


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
# 定义一个获取权重,并自动加入正则项到损失的函数。
def get_weight(shape, lambda1):
var = tf.Variable(tf.random_normal(shape), dtype=tf.float32)
tf.add_to_collection('losses', tf.contrib.layers.l2_regularizer(lambda1)(var))
return var

# 定义神经网络。
x = tf.placeholder(tf.float32, shape=(None, 2))
y_ = tf.placeholder(tf.float32, shape=(None, 1))
sample_size = len(data)

# 每层节点的个数
layer_dimension = [2,10,5,3,1]

n_layers = len(layer_dimension)

cur_layer = x
in_dimension = layer_dimension[0]

# 循环生成网络结构
for i in range(1, n_layers):
out_dimension = layer_dimension[i]
weight = get_weight([in_dimension, out_dimension], 0.003)
bias = tf.Variable(tf.constant(0.1, shape=[out_dimension]))
cur_layer = tf.nn.elu(tf.matmul(cur_layer, weight) + bias)
in_dimension = layer_dimension[i]

y= cur_layer

# 损失函数的定义。
mse_loss = tf.reduce_sum(tf.pow(y_ - y, 2)) / sample_size
tf.add_to_collection('losses', mse_loss)
loss = tf.add_n(tf.get_collection('losses'))

# 训练不带正则项的损失函数mse_loss。
# 定义训练的目标函数mse_loss,训练次数及训练模型
train_op = tf.train.AdamOptimizer(0.001).minimize(mse_loss)
TRAINING_STEPS = 40000

with tf.Session() as sess:
tf.global_variables_initializer().run()
for i in range(TRAINING_STEPS):
sess.run(train_op, feed_dict={x: data, y_: label})
if i % 2000 == 0:
print("After %d steps, mse_loss: %f" % (i,sess.run(mse_loss, feed_dict={x: data, y_: label})))

# 画出训练后的分割曲线
xx, yy = np.mgrid[-1.2:1.2:.01, -0.2:2.2:.01]
grid = np.c_[xx.ravel(), yy.ravel()]
probs = sess.run(y, feed_dict={x:grid})
probs = probs.reshape(xx.shape)

plt.scatter(data[:,0], data[:,1], c=label,
cmap="RdBu", vmin=-.2, vmax=1.2, edgecolor="white")
plt.contour(xx, yy, probs, levels=[.5], cmap="Greys", vmin=0, vmax=.1)
plt.show()

'''
After 0 steps, mse_loss: 22.745703
After 2000 steps, mse_loss: 0.061583
After 4000 steps, mse_loss: 0.049916
After 6000 steps, mse_loss: 0.040501
After 8000 steps, mse_loss: 0.032112
After 10000 steps, mse_loss: 0.025778
After 12000 steps, mse_loss: 0.019642
After 14000 steps, mse_loss: 0.016576
After 16000 steps, mse_loss: 0.014309
After 18000 steps, mse_loss: 0.012851
After 20000 steps, mse_loss: 0.011881
After 22000 steps, mse_loss: 0.011130
After 24000 steps, mse_loss: 0.010511
After 26000 steps, mse_loss: 0.010117
After 28000 steps, mse_loss: 0.009695
After 30000 steps, mse_loss: 0.009219
After 32000 steps, mse_loss: 0.008900
After 34000 steps, mse_loss: 0.008421
After 36000 steps, mse_loss: 0.007951
After 38000 steps, mse_loss: 0.007678
'''


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
# 训练带正则项的损失函数loss。
# 定义训练的目标函数loss,训练次数及训练模型
train_op = tf.train.AdamOptimizer(0.001).minimize(loss)
TRAINING_STEPS = 40000

with tf.Session() as sess:
tf.global_variables_initializer().run()
for i in range(TRAINING_STEPS):
sess.run(train_op, feed_dict={x: data, y_: label})
if i % 2000 == 0:
print("After %d steps, loss: %f" % (i, sess.run(loss, feed_dict={x: data, y_: label})))

# 画出训练后的分割曲线
xx, yy = np.mgrid[-1:1:.01, 0:2:.01]
grid = np.c_[xx.ravel(), yy.ravel()]
probs = sess.run(y, feed_dict={x:grid})
probs = probs.reshape(xx.shape)

plt.scatter(data[:,0], data[:,1], c=label,
cmap="RdBu", vmin=-.2, vmax=1.2, edgecolor="white")
plt.contour(xx, yy, probs, levels=[.5], cmap="Greys", vmin=0, vmax=.1)
plt.show()

'''
After 0 steps, loss: 4.712693
After 2000 steps, loss: 0.143827
After 4000 steps, loss: 0.111660
After 6000 steps, loss: 0.085316
After 8000 steps, loss: 0.066900
After 10000 steps, loss: 0.060303
After 12000 steps, loss: 0.059699
After 14000 steps, loss: 0.059239
After 16000 steps, loss: 0.058850
After 18000 steps, loss: 0.058719
After 20000 steps, loss: 0.058361
After 22000 steps, loss: 0.058352
After 24000 steps, loss: 0.058352
After 26000 steps, loss: 0.058351
After 28000 steps, loss: 0.058351
After 30000 steps, loss: 0.058351
After 32000 steps, loss: 0.058351
After 34000 steps, loss: 0.058350
After 36000 steps, loss: 0.058350
After 38000 steps, loss: 0.058351
'''


以上,tf.get_collection(‘losses’)返回一个列表,这个列表是所有这个集合中的元素。这些元素就是损失函数的不同部分,将它们加起来就可以得到最终的损失函数。

滑动平均模型

TensorFlow 中提供了tf.train.ExponentialMovingAverage 来实现滑动平均模型, 它对每一个变量会维护一个影子变量 ( shadow variable ),这个影子变量的初始值就是相应变量的初始值,而每次运行变量更新时,影子变量的值会更新为 :
shadow_variable = decay × shadow_variable + (1 - decay) × variable
其中 shadow_ variable 为影子变量, variable 为待更新的变量,decay 为衰减率。
如果在 ExponentialMovingAverage 初始化时提供了 num_updates 参数,那么每次使用的
衰减率将是 :

$$
min{decay, \frac{1 + num_updates}{10+num_updates}}
$$

示例代码:

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
import tensorflow as tf

# 定义变量及滑动平均类
v1 = tf.Variable(0, dtype=tf.float32)
step = tf.Variable(0, trainable=False)
ema = tf.train.ExponentialMovingAverage(0.99, step)
maintain_averages_op = ema.apply([v1])

# 查看不同迭代中变量取值的变化。
with tf.Session() as sess:

# 初始化
init_op = tf.global_variables_initializer()
sess.run(init_op)
print sess.run([v1, ema.average(v1)])

# 更新变量v1的取值
sess.run(tf.assign(v1, 5))
sess.run(maintain_averages_op)
print sess.run([v1, ema.average(v1)])

# 更新step和v1的取值
sess.run(tf.assign(step, 10000))
sess.run(tf.assign(v1, 10))
sess.run(maintain_averages_op)
print sess.run([v1, ema.average(v1)])

# 更新一次v1的滑动平均值
sess.run(maintain_averages_op)
print sess.run([v1, ema.average(v1)])

'''
[0.0, 0.0]
[5.0, 4.5]
[10.0, 4.5549998]
[10.0, 4.6094499]
'''

为了提高笔记效率,部分代码引用自网络,链接:
https://github.com/caicloud/tensorflow-tutorial/blob/master/Deep_Learning_with_TensorFlow/1.4.0/Chapter04/

Comments

Your browser is out-of-date!

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

×