TensorFlow实践3:深层神经网络

3.1 深度学习与神经网络

深度学习可以说成是深层神经网络的代名词,但是深度学习更加强调——多层和非线性。主要是因为神经网络的线性模型有一定局限性。

3.1.1线性角度

线性模型的局限性

线性模型叠加后还是线性模型,而线性模型能解决的问题只能是线性可分问题,现实是绝大多数问题都是线性不可分的,这个问题就是线性模型的局限性所在。

激活函数去线性化

如果每一个神经元输出的是非线性的函数,那么整个函数也就是非线性的了。本着这样的思想,这里对原结构进行修改,比如:

《TensorFlow实践3:深层神经网络》

b是偏置项,在神经网络结构图中表示为1;

《TensorFlow实践3:深层神经网络》 也就是非线性函数,常用的有ReLU函数和sigmoid函数,如下:

《TensorFlow实践3:深层神经网络》
《TensorFlow实践3:深层神经网络》 ReLU
《TensorFlow实践3:深层神经网络》
《TensorFlow实践3:深层神经网络》 sigmoid
《TensorFlow实践3:深层神经网络》
《TensorFlow实践3:深层神经网络》

代码中表示:

a = tf.nn.relu(tf.matmul(x, w1) + biases1)

3.1.2 多层角度——网络解决异或运算

多层可以直接解决异或问题,而单层无法完成,理论推导略过,实际测试情况示例:

下面这张图是仅仅用感知机去划分的结果,分类不明显:

《TensorFlow实践3:深层神经网络》
《TensorFlow实践3:深层神经网络》

下面这张是使用ReLU并循环141次的结果,分类效果明显:

《TensorFlow实践3:深层神经网络》
《TensorFlow实践3:深层神经网络》

3.2 如何设定神经网络的优化目标?

损失函数

神经网络的优化目标通过损失函数lossfunction来定义。损失函数依靠预测结果和期望结果之间差异来判定最终分类情况。实际情况中会根据实际应用选择分类情况。

3.2.1分类问题中讨论

多分类中多用交叉熵(cross entropy)来评判输出和期望的接近程度。

《TensorFlow实践3:深层神经网络》

公式中两个概率 《TensorFlow实践3:深层神经网络》 代表正确的概率, 《TensorFlow实践3:深层神经网络》 代表预测值。两者不能互换位置,因为这里表示预测情况下正确分类的情况。交叉熵是描述概率距离的情况,交叉熵值越小,表明预测和真实越接近。

将交叉熵分类的结果也表示成概率的形式利于最后设定阈值进行分类,这里使用softmax完成,示意图如下:

《TensorFlow实践3:深层神经网络》
《TensorFlow实践3:深层神经网络》

具体公式:

《TensorFlow实践3:深层神经网络》

也就是,利用输出除以整体和的形式表达出每个输出的情况。

3.2.1回归问题中的讨论

回归问题常用损失函数是均方误差(MSE:mean squared error)

mse = tf.reducemean(tf.square(y_ - y))

3.3 神经网络算法优化

优化中常使用梯度下降法优化单个参数,使用反向传播算法在实现在所有参数上使用梯度下降法。一般过程:

1)向前传播算法获取预测值,并求出预测和真实之间差距;

2)向后传播算法利用损失函数计算每一个参数的梯度,再利用参数和学习率更新参数;

假设参数为 《TensorFlow实践3:深层神经网络》 ,则对应的参数更新公式:

《TensorFlow实践3:深层神经网络》

其中, 《TensorFlow实践3:深层神经网络》 是学习率(learning rate)确定更新的“步长”, 对《TensorFlow实践3:深层神经网络》 中的参数进行求导确定向量方向;

这种方法存在两个问题,首先是因为初值设置问题,得到的可能不是全局最优;另外一个问题就是时间问题,因为要根据全部数据的损失进行计算,所以非常耗时。

针对时间问题,采用随机梯度下降法,每次随机抽取数据计算,但是这种问题最大的弊病是有可能随机的数据可能不能代表全部数据,最终结果甚至有可能达不到局部最优解。

最终,采用每次计算一小部分数据的方法,基本的训练过程如下:

import tensorflow as tf
from numpy.random import RandomState

# 定义训练数据集的大小
batch_size = n

# 每次只采用一小部分数据作为当前的训练数据执行反向传播
x = tf.placeholder(tf.float32, shape=(None, 2), name="x-input")
y_= tf.placeholder(tf.float32, shape=(None, 1), name='y-input')

# 定义损失函数和反向传播算法
loss= ···
train_step = tf.train.AdamOptimizer(0.001).minimize(loss)

# 创建一个绘画来运行TensorFlow
with tf.Session() as sess:
    ···
    for i in range(STEPS):
        start = ···
        end = ···
        sess.run([train_step, y, y_], feed_dict={x: X[start:end], y_: Y[start:end]})

学习率的设置

学习率也就是参数更新的步长,它用来衡量每次参数更新的大小。最合理的设置是初始时可以采用大步长快速接近最优,然后采用小步长靠近,避免大步长直接跳过最优。

tensorflow中使用指数下降的学习率,符合上述要求,使用代码:

global_step = tf.Variable(0)

# 通过learing_tate函数生成学习率,初始0.1,每100轮乘0.96
# True的话步长呈现阶梯下降,默认为false
learning_rate = tf.train.exponential_decay(
    0.1, global_step, 100, 0.96 staircase=True)

# 使用学习率
# 在minimize中传入学习率将会自动更新 
learning_step = tf.train.GradientDescentOptimizer(learning_rate)\
    .minimize(···my loss···, global_step=global_step)

过拟合问题

拟合有三种情况,欠拟合、正确拟合、过拟合。过拟合就是牺牲模型复杂度的前提下提升模型对训练数据的精确度,但是导致测试数据结果更差。

常用解决方法是——正则化,regularization。正则化也就是在损失函数中加入衡量模型复杂度的参数。例如:

《TensorFlow实践3:深层神经网络》

《TensorFlow实践3:深层神经网络》 一般有两种,L1正则化和L2正则化,下面实例L2正则化:

《TensorFlow实践3:深层神经网络》

刻画模型复杂度一般只涉及参数 《TensorFlow实践3:深层神经网络》 ,和偏置无关,所以这里考虑 《TensorFlow实践3:深层神经网络》 的范数来进行复杂度衡量。涉及的参数和越多,意味着模型复杂度越高。

完整代码示例:

1.1生成坐标数据集

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)       # 横坐标,返回区间内的随机数,float类型
    x2 = np.random.uniform(0, 2)        # 纵坐标
    if x1 ** 2 + x2 ** 2 <= 1:      # 2的2次方,在内圆随机加入噪声,并修改label
        data.append([np.random.normal(x1, 0.1), np.random.normal(x2, 0.1)])      # 正太分布
        label.append(0)    # 圆内0是红色
    else:
        data.append([np.random.normal(x1, 0.1), np.random.normal(x2, 0.1)])
        label.append(1)
# hstack把数据连接在一起;reshape重构矩阵形式
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()

《TensorFlow实践3:深层神经网络》

# 2.定义一个获取权重,并自动加入正则项到损失函数
# collection解决损失函数和计算损失函数部分因分离产生的不便
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

# 3.定义神经网络
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]

# 通过一个循环来生成5层全连接的神经网络结果
for i in range(1, n_layers):
    # layer_dimension[i]:为下一层节点个数
    out_dimension = layer_dimension[i]
    # 生成当前层中权重的变量,并将变量的L2正则化损失加入计算图集合
    weight = get_weight([in_dimension, out_dimension], 0.003)
    bias = tf.Variable(tf.constant(0.1, shape=[out_dimension]))
    # 使用elu激活函数
    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'))

# 4.训练不带正则项的损失函数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()

《TensorFlow实践3:深层神经网络》
《TensorFlow实践3:深层神经网络》

import c4regularizer
2018-03-22 15:42:18.379044: I C:\tf_jenkins\workspace\rel-win\M\windows\PY\35\tensorflow\core\platform\cpu_feature_guard.cc:140] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2
After 0 steps, mse_loss: 3.434271
After 2000 steps, mse_loss: 0.064096
After 4000 steps, mse_loss: 0.047538
After 6000 steps, mse_loss: 0.032365
After 8000 steps, mse_loss: 0.026592
After 10000 steps, mse_loss: 0.023896
After 12000 steps, mse_loss: 0.023353
After 14000 steps, mse_loss: 0.023039
After 16000 steps, mse_loss: 0.022783
After 18000 steps, mse_loss: 0.022473
After 20000 steps, mse_loss: 0.021916
After 22000 steps, mse_loss: 0.021452
After 24000 steps, mse_loss: 0.020944
After 26000 steps, mse_loss: 0.020410
After 28000 steps, mse_loss: 0.019890
After 30000 steps, mse_loss: 0.019321
After 32000 steps, mse_loss: 0.018772
After 34000 steps, mse_loss: 0.018275
After 36000 steps, mse_loss: 0.017871
After 38000 steps, mse_loss: 0.017544

滑动平均模型

滑动平均模型可以一定程度上提升随机梯度下降算法训练神经网络的表现,那么什么事滑动平均模型呢?滑动平均模型主要是控制模型更新的速度。滑动平均模型依靠影子变量来实现对速度的调控,如下:

《TensorFlow实践3:深层神经网络》

其中,decay为衰减率,实际使用中通常使用0.999等非常靠近1的数值;这里可以看出下一次更新的变量 《TensorFlow实践3:深层神经网络》 也就占了极小的比例,所以模型迭代中参数变量的变动也就降低了。代码中使用 《TensorFlow实践3:深层神经网络》 实现。但是前期的速度同样也变得很低,为了提高前期速度。 《TensorFlow实践3:深层神经网络》 初始化时提供了 《TensorFlow实践3:深层神经网络》 参数来提升前期更新速度:

《TensorFlow实践3:深层神经网络》

详细看代码解释:

import tensorflow as tf

# 定义一个变量用于计算滑动平均,这里初始值为0
v1 = tf.Variable(0, dtype=tf.float32)
# 设定步数模拟神经网络迭代次数
step = tf.Variable(0, trainable=False)

# 定义滑动平均类,衰减率为0.99
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))
    # 更新滑动平均值,衰减min(0.99,(1+step)/(10+step))=0.1
    # 滑动平均被更新:0.1*0+0.9*5=4.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))
    # 更新滑动平均值,衰减min(0.99,(1+step)/(10+step)约等于0.999)=0.9
    # 滑动平均被更新:0.99*0.45+0.01*10=4.5555
    # 后期更新速度慢
    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)]))

结果:

import c4movingaverage
2018-03-23 14:04:42.347085: I C:\tf_jenkins\workspace\rel-win\M\windows\PY\35\tensorflow\core\platform\cpu_feature_guard.cc:140] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2
[0.0, 0.0]
[5.0, 4.5]
[10.0, 4.5549998]
[10.0, 4.6094499]

小结

本节内容,先讲到深度学习相较于线性神经网络的特点:非线性和多层。非线性更适合实际问题,多层则解决了异或的问题。然后从 如何设定神经网络的优化目标入手讲到了损失函数,并分贝在分类和回归问题中简单讨论了损失函数。

然后给出了一个完整的深度神经网络的训练实例,在其中讨论了如何动态地设置学习率以及如何利用正则化应对过拟合的问题。为了进一步提升表现,讲述了通过滑动平均的方法参数更新的速率。

    原文作者:文强孙
    原文地址: https://zhuanlan.zhihu.com/p/34780588
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞