4.1深层神经网络
线性模型的局限性:如果只通过线性变换,任意层次的全连接神经网络和单层神经网络模型的表达能力没有任何区别,而他们都是线性模型。利用tensorflow游乐园中的这个例子,就无法用linear来拟合。
使用非线性函数来激活:在原来xW的基础上加上偏置项bias,每个节点的输出在加权和的基础上还做了一个非线性变换。如sigmoid函数:
f
(
x
)
=
1
1
+
e
−
x
f(x) = \frac{1}{1+e^{-x}}
f(x)=1+e−x1
于是原来的传播表示为:f(xW+b),偏置项b可以看作是一个输出永远为1的节点。
几个tensorflow提供的而非线性激活函数的表示:tf.nn.relu tf.sigmoid tf.tanh
a = tf.nn.relu(tf.matmul(x,w1)+biases1)
多层网络解决异或运算:用一层的干之际无法模拟异或运算的功能,只有当加入因此那个层之后才行。
4.2损失函数定义
经典损失函数:分类和回归是监督学习的两大种类。
交叉熵H(p,q) = -
∑
x
p
(
x
)
l
o
g
q
(
x
)
\sum_{x}p(x)logq(x)
∑xp(x)logq(x),注意顺序不能变,表示通过概率分布q来表达概率分布p的困难程度,其中p表示正确答案,q表示预测值。交叉熵越小,表示两个概率分布越接近。
softmax回归本身是一个可以作为优化算法的东西,到那时tensorflow中只作为一层额外的处理层,只是为了将输出变成概率分布。
s
o
f
t
m
a
x
(
y
)
i
=
e
y
i
∑
j
=
1
n
e
y
i
softmax(y)_{i} = \frac{e^{y_{i}}}{\sum_{j=1}^{n}e^{y_{i}}}
softmax(y)i=∑j=1neyieyi
交叉熵:
cross_entropy = -tf.reduce_mean(y_*tf.log(tf.clip_by_value(y, 1e-10, 1.0)))
其中y_表示政务而结果,y表示预测值,tf.clip_by_value是将张量限制在了一个范围之内。小于的部分全部变成最小值,大于的部分全部变成最大值。
因为交叉熵一般和softmax一起使用,tensorflow对这两个功能进行封装形成:
cross_entropy = tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y)
回归问题:
是对于一个具体数值的预测,不是一个实现定义好的类别,一般只有一个输出节点,而这个就是预测值,最常用的是均方误差MSE:
M
S
E
(
y
,
y
′
)
=
∑
i
=
1
n
(
y
i
−
y
i
′
)
2
n
MSE(y,y')=\frac{\sum_{i=1}^{n}(y_{i}-y'_{i})^{2}}{n}
MSE(y,y′)=n∑i=1n(yi−yi′)2
mse = tf.reduce_mean(tf.squre(y_ - y))
自定义的损失函数:举例预测商品销量偏多或者偏少的损失函数。
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)
其中tf.reduce_sum表示所有值相加,tf.where()表示满足条件时候选择某一个值,tf.greater(x,y)表示比较x和y两个张量的大小。
完整的代码:
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]
#在随机产生正确预测值Y时候加上一个比较小的0.05的噪声
#训练神经网络
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)
4.3神经网络优化算法
梯度下降算法主要用于优化单个参数的取值,而反向传播算法给出了所有参数上搞笑使用梯度下降算法,是训练神经网络的核心算法。
梯度下降发缺点:1不能保证被优化的函数达到全局最优解 2计算时间太长
若使用随机梯度下降甚至可能无法达到局部最优,折中两种方法,每次计算一小部分训练数据的损失函数,这一小部分数据称为一个batch(每次一个batch上优化神经网络参数并不会比单个数据慢太多,另外,每一次使用一个batch可以大大减小收敛所需要的迭代次数)
4.4进一步优化
学习率不能过大也不能过小,指数衰减法。
global_step = tf.Variable(0)
LEARNING_RATE = tf.train.exponential_decay(0.1, global_step, 100, 0.96, staircase=True)
#学习初始率0.1,每训练100轮后学习率乘以0.96
train_op = tf.train.GradientDescentOptimizer(LEARNING_RATE).minimize(y, global_step=global_step)
过度拟合:
指当一个模型过于复杂之后,他可以很好的记忆每个训练数据中随机噪声的部分,而忘记了去学习通用的特征。
常用的方法:正则化regularization,在损失函数中加入刻画模型复杂程度的指标。常见的有L1正则化和L2正则化。在优化是不止优化损失函数,同时应加入一个复杂程度的函数。
比较简单的写法:
loss=tf.reduce_mean(tf.square(y_ -y) + tf.contrib.layers.l2_regularizer(lambda)(w))
但是一般我们使用collection形式来记录:
#首先定义一个名叫losses的集合,可以自动加入正则项
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.relu(tf.matmul(cur_layer, weight) + bias)
#下一层
in_dimension = layer_dimension[i]
y= cur_layer
# mse的损失函数
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'))
滑动平均模型:
tensorflow中使用tf.train.ExponentialMovingAverage来实现滑动平均模型,需要提供一个衰减率decay和维护一个影子变量shadow variable,而每次这个都会更新为:
s
h
a
d
o
w
v
a
r
i
a
b
l
e
=
d
e
c
a
y
∗
s
h
a
d
o
w
v
a
r
i
a
b
l
e
+
(
1
−
d
e
c
a
y
)
∗
v
a
r
i
a
b
l
e
shadowvariable = decay*shadowvariable+(1-decay)*variable
shadowvariable=decay∗shadowvariable+(1−decay)∗variable
decay越大表示模型越稳定,同时还可以通过num_updates参数来设置每次使用的衰减率。
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)])