机器学习实战(十一):Training Deep Neural Nets

深层DNN可能出现的问题:

  • 梯度消失、梯度爆炸问题,影响深度神经网络,导致底层训练困难
  • 网络庞大,训练缓慢
  • 容易overfitting

本章将会探讨梯度消失的流行解决方案,训练大模型明显提速的优化器(相对于平坦梯度下降 ),浏览针对大型神经网络的正则化技术。

Vanishing/Exploding Gradients Problems

第十章中讲到:反向传播算法再输出层反向作用到输入层的过程中传播误差梯度,一旦算法计算出成本函数梯度,就会根据梯度修正每一个参数。
DNN受制于不稳定梯度,不同层可能以完全不同速度来学习。

  • 梯度消失:梯度随着算法进展到更底层越来越小,导致梯度下降在更底层网络连接权值更新上基本没有改变,训练收敛不到好的结果。
  • 梯度爆炸:梯度随着算法进展到更底层越来越大,导致很多层的权值疯狂增大,使得算法发散(常出现在循环神经网络)

梯度消失的解决:用逻辑S激活函数和权重初始化技术结合(均值为0,方差为1正态分布随机初始化/均值为0的双曲正切函数):每一层输出方差比输入方差大很多,依层增加到激活函数最高层饱和。
在这里插入图片描述
如上图逻辑激活函数:输入增大(正或负方向),函数在0或1饱和,导数接近0——反向传播起作用时,并没有梯度通过网络反向作用,同时反向传播至顶层几乎没有梯度被稀释,所以也基本上没给低层留下什么。

Xavier and He Initialization

缓和梯度消失:让信号在两个方向都正确流动,预测保持正向,反向传播梯度保持反方向。不希望信号消亡、稀释、爆炸。要保持每一层输入输出方差一直,反向流动方差也要一致(很难实现),折中方案:连接权重按如下公式随机初始化。
如图所示为Xavier(Glorot)初始化:
ReLU激活函数及其变种(ELU函数)的初始化称为He初始化。
在这里插入图片描述

TF用tf.layers.dense()代替tensorflow.contrib.layers.fully_connected(),默认He初始化:

import tensorflow as tf

reset_graph()

n_inputs = 28 * 28  # MNIST
n_hidden1 = 300

X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")

he_init = tf.variance_scaling_initializer()
hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu,
                          kernel_initializer=he_init, name="hidden1")

Nonsaturating Activation Functions(非饱和激活函数)

ReLU在DNN表现好的原因是计算快,同时并不稀释正值,但会出现dying ReLU问题:当神经元权重更新到神经元输入的总权重为负值时,神经元开始输出0,除非ReLU的梯度为0且输入为负,则神经元不会开始工作。

解决此问题就要使用ReLU变种——如leaky ReLU(带泄露性整流函数):LeakyReLU_α=max(αz,z)。α表示函数泄露程度,即z<0的坡度,默认设为0.01(既保证不会死,又保证能醒过来)。(其实设0.2结果更好,但默认为0.01)
在这里插入图片描述

列举几个ReLU变种的激活函数:
RReLU:在训练中α为给定区间随机值,测试为固定平均值,可做正则降低overfitting风险
PReLU:α在训练中学习(作为反向传播参数),在大图片集表现好,小数据集容易overfitting。

ELU(加速线性单元)优于ReLU所有变种(训练时间更短,测试集表现更好),定义如下:
在这里插入图片描述
在这里插入图片描述
与ReLU不同之处:

  • z<0为负,允许单元平均输出接近0,缓解梯度消失,α通常设为1(用来z为极大负数接近本值)
  • z<0有一非零梯度,避免单元消失
  • 函数整体平滑,z=0处没有抖动,提高GB

ELU主要缺陷为,计算速度比ReLU和其变种慢(即使用更快收敛弥补)

DNN隐藏层使用激活函数优先顺序:ELU>leaky ReLU>ReLU>tanh>S(逻辑函数)
关注性能:leaky ReLU;不想改变超参数,默认α(leaky ReLU:0.01,ELU:1);时间性能充沛,交叉验证评估;过拟合:RReLU;大训练集:PReLU

TF调用:

reset_graph()

X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")

def leaky_relu(z, name=None):
    return tf.maximum(0.01 * z, z, name=name)

hidden1 = tf.layers.dense(X, n_hidden1, activation=leaky_relu, name="hidden1")

Batch Normalization(批量一体化)

He初始化+ELU可以降低梯度消失/爆炸,但不能保证,使用批量一体化(BN)解决:在每一层激活函数之前在模型里加简单零中心化和归一化输入,再通过每层两个新参数,一个用来缩放结果,一个用来移动结果——让模型学会最佳规模和每层输入平均值。

零中心化和归一化输入需要评估输入平均值和标准方差,操作如下:
在这里插入图片描述
在这里插入图片描述

测试期间可以 用整个训练集平均值和方差替代经验平均值和经验标准方差。γ(scale), β (offset), μ (mean), and σ (standard deviation)为每一批量归一化层来学习的。

使用饱和激活函数(如tanh)和逻辑激活函数有效的改善了梯度消失问题,也可以使用更高的学习率,不仅减少了训练步骤而且效果很好,BN还可以同时正则化,降低其他正则化技术需求。
BN同时增加了复杂度,预测运算速度变慢。

一旦找到合适的γ(scale), β (offset),训练速度会迅速提升,否则很慢。

TF应用ELU

建图

reset_graph()

n_inputs = 28 * 28  # MNIST
n_hidden1 = 300
n_hidden2 = 100
n_outputs = 10

定义X,y:

X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
y = tf.placeholder(tf.int32, shape=(None), name="y")

定义DNN:


with tf.name_scope("dnn"):
    hidden1 = tf.layers.dense(X, n_hidden1, activation=leaky_relu, name="hidden1")
    hidden2 = tf.layers.dense(hidden1, n_hidden2, activation=leaky_relu, name="hidden2")
    logits = tf.layers.dense(hidden2, n_outputs, name="outputs")

定义成本函数loss:

with tf.name_scope("loss"):
    xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)
    loss = tf.reduce_mean(xentropy, name="loss")

定义optimizer:

learning_rate = 0.01

with tf.name_scope("train"):
    optimizer = tf.train.GradientDescentOptimizer(learning_rate)
    training_op = optimizer.minimize(loss)

定义eval评估:

with tf.name_scope("eval"):
    correct = tf.nn.in_top_k(logits, y, 1)
    accuracy = tf.reduce_mean(tf.cast(correct, tf.float32))

初始变量并保存:

init = tf.global_variables_initializer()
saver = tf.train.Saver()

加载数据(下载到本地),划分选集:

from keras.datasets import mnist
(X_train, y_train), (X_test, y_test) =mnist.load_data('mnist/mnist.npz')
X_train = X_train.astype(np.float32).reshape(-1, 28*28) / 255.0
X_test = X_test.astype(np.float32).reshape(-1, 28*28) / 255.0
y_train = y_train.astype(np.int32)
y_test = y_test.astype(np.int32)
X_valid, X_train = X_train[:5000], X_train[5000:]
y_valid, y_train = y_train[:5000], y_train[5000:]

定义取mini-batch算法:

def shuffle_batch(X, y, batch_size):
    rnd_idx = np.random.permutation(len(X))
    n_batches = len(X) // batch_size
    for batch_idx in np.array_split(rnd_idx, n_batches):
        X_batch, y_batch = X[batch_idx], y[batch_idx]
        yield X_batch, y_batch

小批量训练:

n_epochs = 40
batch_size = 50

with tf.Session(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值