TensorFlow学习笔记(2) 深层神经网络

一、深度学习与深层神经网络

深度学习是指一类通过多层非线性变换对高复杂性数据建模算法的合集,深层神经网络就是实现多层非线性变换最常用的一种方法。而深度学习两个最重要的特性就是多层非线性

在上一篇最后所建立的神经网络即为线性的,但是在现实世界中,绝大多数的问题时无法线性分割的,因此需要一个激活函数(Activation)来使其变得非线性化。如果一个神经元的输出通过一个非线性函数,那么整个神经网络的模型就不是线性了,这个函数就是激活函数。

在TF中提供了1种不同的非线性激活函数,tf.nn.rule、tf.sigmoid、tf.tanh是其中比较常用的几个。同时TF也支持自己定义激活函数,以下为其用法,其中biases为偏置项:

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

深度学习的另外一个重要性质是多层变换,在输入层和输出层之间加入若干个隐藏层,深层神经网络有组合提取的功能,对于不易解决提取特征向量的问题有很大帮助。

二、损失函数定义

神经网络模型的效果以及优化的目标是通过损失函数来定义的。分类问题和回归问题是监督学习的两大种类。针对不同的问题使用不同的损失函数来优化模型。

对于分类问题,一般使用交叉熵(corss entropy)来判断一个输出向量和期望向量有多接近。交叉熵刻画了两个概率分布之间的距离,然而神经网络的输出却不一定是一个概率分布。在TF种,使用Softmax回归作为额外的处理层得到概率分布的输出结果。

假设正确答案(1,0,0),预测结果(0.5,0.4,0.1)。则其交叉熵为H((1,0,0),(0.4,0.4,0.1)) = - (1*log0.5 + 0*log0.4 + 0*log0.1)=0.3。通过TF代码实现如下:

corss_entropy = - tf.reduce_mean(y_, tf.log(tf.clip_by_value(y, 1e-10, 1.0)))
#其中y_代表真实结果,y代表预测结果。
#tf.clip_by_value(y, 1e-10, 1.0)可以将y值限定在一个范围
##若y小于1e-10,则y=1e-10.y大于1.0同理
#tf.log求对数
#tf.reduce_mean求均值

TF对上述过程进行了封装,提供了一个函数来实现求得Softmax回归之后的交叉熵:

corss_entropy = tf.nn.softmax_cross_entropy_with_logits(logits=y, labels=y_)
#在只有一个正确答案的分类问题种,可以使用以下函数加速计算过程
cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=y_)

对于回归问题,比如房价预测、销量预测等等,最常用的损失函数是均方误差(MSE,mean squared error)。用TF程序实现如下:

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

除了上述两种外,损失函数也可以进行自定义。比如,一个商品成本为1,利润为10,那么少预测一个就少挣10元,多预测一个就少挣1元,则通过TF构建该损失函数:

loss = tf.reduce_sum(tf.where(tf.greater(y, y_), (y-y_)*loss_more, (y_-y)*loss_less))
#tf.greater(y, y_)::若y>y_则返回True , 否则返回Flase
#tf.where(tf.greater(y, y_), (y-y_)*loss_more, (y_-y)*loss_less)
#若tf.greater(y, y_)为True,则返回(y-y_)*loss_more,,否则返回(y_-y)*loss_less

利用该损失函数构成简单的神经网络:

import tensorflow as tf
from numpy.random import RandomState

batch_size = 8

# 定义神经网络的结构和前向传播的输出结果
w1 = tf.Variable(tf.random_normal([2, 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')
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)
dataset_size = 128
X = rdm.rand(dataset_size, 2)
Y = [[x1 + x2 + rdm.rand()/10.0-0.05] for (x1, x2) in X]

init_op = tf.initialize_all_variables()

# 生成会话并在训练数据上反复运行反向传播优化算法
with tf.Session() as sess:
    sess.run(init_op)
    print('w1\n', sess.run(w1))

    STEPS = 5000
    for i in range(STEPS):
        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 % 500 == 0:
            total_loss = sess.run(loss, feed_dict={x: X, y_: Y})
            print("After %d training steps, cross entropy on all data is %g" % (i, total_loss))
    print('w1\n', sess.run(w1))

三、神经网络优化算法

通过反向传播算法可以调整神经网络中参数的取值,一般来说,反向传播算法给出了一个高效的方式在所有参数上使用梯度下降法,从而使神经网络模型在训练数据集上的损失函数尽可能小。使用梯度下降算法优化函数y=x^2 可以表示为下表:

轮数 当前轮参数值 梯度*学习率 更新后参数值
1 5 2*5*0.3=3 2
2 2 2*2*0.3=1.2 0.8
3 0.8 2*0.8*0.3=0.48 0.32
4 0.32 2*0.32*0.3=0.192 0.128
5 0.128 2*0.128*0.3=0.0768 0.0512

在实际应用中一般每次计算一小部分训练数据的损失函数,这一小部分被称为一个batch。

四、神经网络的进一步优化

1.学习率的设置

学习率使用来控制参数更新的速度。学习率过大,则参数不会收敛到一个极值,学习率过小,则大大降低优化速度。为了解决这种方法,TF提供了一种灵活的学习率设置方法——指数衰减法:tf.train.exponential_decay函数实现了指数衰减学习率。

可以实现功能为:decayed_learning_rate=learning_rate*decay_rate^(global_step/decay_step)。其中,learning_rate为初始学习率,decay_rate为衰减指数,decay_step衰减速度即经过多少轮后衰减一次。其中可以通过参数设置staircase选择不同的衰减方式,若为False,则连续衰减;若为True,则阶梯式衰减。代码为:

gobal_step = tf.Variable(0)
learning_rate = tf.train.exponential_decay(0.1, global_step, 100, 0.96, staircase=True)
learning_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(...my loss..., gobal_step=...)
#设定了初始学习率为0.1, 每训练100轮以后学习率乘以0.96

2.过拟合问题

过拟合是指过度拟合训练数据中的噪音而忽视了问题的整体规律。为了避免这个问题,常用的解决方法是正则化(regularization)。正则化的思想是在损失函数中引入刻画模型复杂程度的指标。一共有两种。

一种是L1正则化:R(w)=||w||1。一种是L2正则化:R(w)=||w||2^2。由于L2可导而L1不可导,所以对含有L2正则化损失函数的优化更加简洁。在实践中也可以将l1和l2同时使用:R(w)=a||w||1+(1-a)||w||2^2。

在TF中,以下为简单的带L2正则化的损失函数定义:

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.l2_regularizer(lambda)(w)
#其中lambda表示正则化项的权重,w为需要计算正则化损失的参数。
#若计算L1正则化值,则为tf.contrib.layers.l1_regularizer(lambda)(w)

下面为通过集合计算一个五层神经网络带L2正则化的损失函数的计算方法:

'''
建立一个五层的神经网络
并使用TF提供的集合得出带L2正则化的损失函数
'''

import tensorflow as tf

def get_weight(shape, lamda):
    var = tf.Variable(tf.random_normal(shape), dtype=tf.float32)
    tf.add_to_collection('losses', tf.contrib.layers.l2_regularizer(lamda)(var))
    return var

x = tf.placeholder(tf.float32, shape=(None, 2))
y_ = tf.placeholder(tf.float32, shape=(None, 1))
batch_size = 8
layer_dimension = [2, 10, 10, 10, 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.001)
    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]

mse_loss = tf.reduce_mean(tf.square(y_ - cur_layer))
tf.add_to_collection('losses', mse_loss)
loss = tf.add_n(tf.get_collection('losses'))

3.滑动平均模型

滑动平均模型可以使得模型在测试数据上更加robust。在TF中提供了tf.train.ExponentialMovingAverage来实现滑动平均模型。在初始化模型时,需要给定两个参数。第一个参数为decay衰减率,用来控制模型更新的速度。模型将会对每一个变量维护一个影子变量(shadow varialbe),初始值为相应变量的初始值,并进行下列的更新:

shadow_variable=decay*shadow_variable+(1-decay)*variable

另一个参数为num_updates,其值常与神经网络的迭代轮数相等。每次进行滑动平均的衰减率的值则变为:

decay = min{decay,(1+num_updates)/(10+num_updates)}

以下通过一段代码表示如何使用:

import tensorflow as tf

#定义一个变量用于计算其滑动平均。
v1 = tf.Variable(0, dtype=tf.float32)
#step定义i为神经网络中迭代的轮数,即作为num_steps用于动态控制衰减率
step = tf.Variable(0, trainable=False)

#定义了一个滑动平均的类。初始化时给定衰减率(decay)为0.99和控制衰减率变化的num_steps参数step。
ema = tf.train.ExponentialMovingAverage(0.99, step)
#定义一个滑动平均的操作
maintain_averages_op = ema.apply([v1])

init_op = tf.initialize_all_variables()

with tf.Session() as sess:
    sess.run(init_op)
    #v1=0 滑动平均结果也为0
    print(sess.run([v1, ema.average(v1)]))
    #[0.0, 0.0]

    # 令v1=5,step=0.
    sess.run(tf.assign(v1, 5))
    sess.run(maintain_averages_op)
    #衰减率为min{0.99,(1+0)/(10+0)}=0.1
    print(sess.run([v1, ema.average(v1)]))
    #[5.0, 4.5]

    # 令v1=10,step=10000.
    sess.run(tf.assign(step, 10000))
    sess.run(tf.assign(v1, 10))
    sess.run(maintain_averages_op)
    # 衰减率为min{0.99,(1+10000)/(10+10000)}=0.99
    print(sess.run([v1, ema.average(v1)]))
    #[10.0, 4.555]

    sess.run(maintain_averages_op)
    #更新后的v1滑动平均值为 0.99*4.555+0.01*10=4.6094499
    print(sess.run([v1, ema.average(v1)]))
    #[10.0, 4.60945]

猜你喜欢

转载自blog.csdn.net/qyf394613530/article/details/79239962
今日推荐