Hands On Machine Learning with Scikit Learn and TensorFlow(第十一章)

匿名 (未验证) 提交于 2019-12-02 22:56:40

Vanishing/Exploding Gradients Problems

梯度消失的定义:在反向传播过程中,梯度的值会越来越小,这样导致:使用梯度下降更新参数时,更新的参数基本没有变,训练也不会收敛,无法达到最优解。

梯度爆炸的定义:在反向传播过程中,梯度的值越来越大,这样导致:权重很大,造成算法发散,在RNN中常见。

Xavier and He Initialization

我们不想让信号消失,也不想让信号过大或者饱和。为了让信号正确的流动,信号不管在前向传播还是反向传播过程中,都需要使输入时的方差等于输出时的方差。不可能同时保证这样,除非这一层的输入和输出的连接相等。在实际中,我们使用Xavier初始化权重也能达到满意的效果。n为输入的数量。

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

He初始化不像Xavier初始化一样考虑输入和输出的数量,He只考虑输入的数量。

Nonsaturating Activation Functions

Leaky ReLU基本好过普通的ReLU,定义为LeakyReLUα(z) = max(αz, z)

Elu的优势:

  1. 值小于0,也能有梯度,避免单元死亡(输出为0)。
  2. 处处平滑,加速梯度下降。

劣势:速度慢,因为有指数函数。在训练过程中,可以被快速的收敛所抵消,但是测试中比ReLU慢

在Tensorflow中使用如下,ELU

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

leaky_relu

 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。在进行训练时,使用的均值以及方差都是在Mini-batch上进行测试的。

在测试阶段,使用全体训练集的均值以及方差。

使用了这种技术,不仅可以避免梯度爆炸/消失,还可以使用大的学习速率。并且带有一点正则的效果(不是正则),减少了例如dropout的使用。

输入量不需要Normalize,因为第一层的隐藏层会进行Batch normalize。

Implementing Batch Normalization with TensorFlow

 import tensorflow as tf n_inputs = 28 * 28 n_hidden1 = 300 n_hidden2 = 100 n_outputs = 10 X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X") training = tf.placeholder_with_default(False, shape=(), name='training') hidden1 = tf.layers.dense(X, n_hidden1, name="hidden1") bn1 = tf.layers.batch_normalization(hidden1, training=training, momentum=0.9) bn1_act = tf.nn.elu(bn1) hidden2 = tf.layers.dense(bn1_act, n_hidden2, name="hidden2") bn2 = tf.layers.batch_normalization(hidden2, training=training, momentum=0.9) bn2_act = tf.nn.elu(bn2) logits_before_bn = tf.layers.dense(bn2_act, n_outputs, name="outputs") logits = tf.layers.batch_normalization(logits_before_bn, training=training, momentum=0.9

training 这个placeholder:在训练的时候设置为True,测试时候为False,这个placeholder告诉tf.layers.batch_normalization()在True的时候使用当前mini-batch的mean和标准差(训练阶段),False的时候使用整个训练集的均值以及标准差(测试阶段)。

Ϊ

一个好的momentum 值为接近1,0.9,0.99,0.999,数据集越大,mini-batch越小,那么需要的9也就越多(越接近1)。

使用python自带的partial函数可以自动设置默认值。

 from functools import partial my_batch_norm_layer = partial(tf.layers.batch_normalization, training=training, momentum=0.9) hidden1 = tf.layers.dense(X, n_hidden1, name="hidden1") bn1 = my_batch_norm_layer(hidden1) bn1_act = tf.nn.elu(bn1) hidden2 = tf.layers.dense(bn1_act, n_hidden2, name="hidden2") bn2 = my_batch_norm_layer(hidden2) bn2_act = tf.nn.elu(bn2) logits_before_bn = tf.layers.dense(bn2_act, n_outputs, name="outputs") logits = my_batch_norm_layer(logits_before_bn)

batch_normalization()创建了一些少量操作,必须在训练的每一步都评估,来更新moving averages,moving averages用来评估训练集的均值以及标准差。这些操作自动被放进UPDATE_OPS。我们只需要把这些操作放在一个list里取出来,运行就可以。

 extra_update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS) with tf.Session() as sess:     init.run()     for epoch in range(n_epochs):         for iteration in range(mnist.train.num_examples // batch_size):             X_batch, y_batch = mnist.train.next_batch(batch_size)             sess.run([training_op, extra_update_ops],                       feed_dict={training: True, X: X_batch, y: y_batch})         accuracy_val = accuracy.eval(feed_dict={X: mnist.test.images,                                             y: mnist.test.labels})         print(epoch, "Test accuracy:", accuracy_val)     save_path = saver.save(sess, "./my_model_final.ckpt")

Gradient Clipping

 threshold = 1.0 optimizer = tf.train.GradientDescentOptimizer(learning_rate) grads_and_vars = optimizer.compute_gradients(loss) capped_gvs = [(tf.clip_by_value(grad, -threshold, threshold), var)                for grad, var in grads_and_vars] training_op = optimizer.apply_gradients(capped_gvs)

Reusing Pretrained Layers

Reusing a TensorFlow Model

首先使用import_meta_graph()函数把operations引入进默认的图表。

随后为了取出用于训练用的operations和tensors,你需要get_tensor_by_name()以及get_operation_by_name()。

 X = tf.get_default_graph().get_tensor_by_name("X:0") y = tf.get_default_graph().get_tensor_by_name("y:0") accuracy = tf.get_default_graph().get_tensor_by_name("eval/accuracy:0") training_op = tf.get_default_graph().get_operation_by_name("GradientDescent")

要想知道tensor的名字,可以使用tensorboard,要想使用tensorboard,必须使用FileWriter来把graph输出至本地。或者使用get_operations()的方法来列出所有的operations

 for op in tf.get_default_graph().get_operations():     print(op.name)

假如你是这个模型的作者,除了清楚的给出operations的名字,还可以创建一个collection,来把所有重要的operations都放在里面

 for op in (X, y, accuracy, training_op):     tf.add_to_collection("my_important_ops", op)

然后使用你模型的人,可以很快速的调用你的模型,

 X, y, accuracy, training_op = tf.get_collection("my_important_ops")

然后你就可以加载权重,并且用你的数据来训练。

 with tf.Session() as sess:     saver.restore(sess, "./my_model_final.ckpt")      for epoch in range(n_epochs):         for X_batch, y_batch in shuffle_batch(X_train, y_train, batch_size):             sess.run(training_op, feed_dict={X: X_batch, y: y_batch})         accuracy_val = accuracy.eval(feed_dict={X: X_valid, y: y_valid})         print(epoch, "Validation accuracy:", accuracy_val)      save_path = saver.save(sess, "./my_new_model_final.ckpt")    

假如你有构建graph图的代码,那么就不需要使用import_meta_graph()方法,即上面的都不需要做,直接加载权重就可以,前提是图的构建要和原始的图一样,多添加层或者修改层会报错。

使用import_meta_graph()加载的是完整的整个图,但是你可以忽略某些层,在上面进行修改,随意添加层(如文章开头的那样)。

 reset_graph()  n_hidden4 = 20  # new layer n_outputs = 10  # new layer  saver = tf.train.import_meta_graph("./my_model_final.ckpt.meta")  X = tf.get_default_graph().get_tensor_by_name("X:0") y = tf.get_default_graph().get_tensor_by_name("y:0")  hidden3 = tf.get_default_graph().get_tensor_by_name("dnn/hidden4/Relu:0")  new_hidden4 = tf.layers.dense(hidden3, n_hidden4, activation=tf.nn.relu, name="new_hidden4") new_logits = tf.layers.dense(new_hidden4, n_outputs, name="new_outputs")  with tf.name_scope("new_loss"):     xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=new_logits)     loss = tf.reduce_mean(xentropy, name="loss")  with tf.name_scope("new_eval"):     correct = tf.nn.in_top_k(new_logits, y, 1)     accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name="accuracy")  with tf.name_scope("new_train"):     optimizer = tf.train.GradientDescentOptimizer(learning_rate)     training_op = optimizer.minimize(loss)  init = tf.global_variables_initializer() new_saver = tf.train.Saver()   with tf.Session() as sess:     init.run()     saver.restore(sess, "./my_model_final.ckpt")      for epoch in range(n_epochs):         for X_batch, y_batch in shuffle_batch(X_train, y_train, batch_size):             sess.run(training_op, feed_dict={X: X_batch, y: y_batch})         accuracy_val = accuracy.eval(feed_dict={X: X_valid, y: y_valid})         print(epoch, "Validation accuracy:", accuracy_val)      save_path = new_saver.save(sess, "./my_new_model_final.ckpt")

在使用Saver取回图的过程中,必须指定你需要恢复的变量,否则tensorflow会提示图不匹配。

假如有原始的架构,也可以直接在原始架构上修改。只不过多了一步,在图中定义Saver时,需要把恢复的变量传入。

 reset_graph()  n_inputs = 28 * 28  # MNIST n_hidden1 = 300 # reused n_hidden2 = 50  # reused n_hidden3 = 50  # reused n_hidden4 = 20  # new! n_outputs = 10  # new!  X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X") y = tf.placeholder(tf.int32, shape=(None), name="y")  with tf.name_scope("dnn"):     hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu, name="hidden1")       # reused     hidden2 = tf.layers.dense(hidden1, n_hidden2, activation=tf.nn.relu, name="hidden2") # reused     hidden3 = tf.layers.dense(hidden2, n_hidden3, activation=tf.nn.relu, name="hidden3") # reused     hidden4 = tf.layers.dense(hidden3, n_hidden4, activation=tf.nn.relu, name="hidden4") # new!     logits = tf.layers.dense(hidden4, n_outputs, name="outputs")                         # new!  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")  with tf.name_scope("eval"):     correct = tf.nn.in_top_k(logits, y, 1)     accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name="accuracy")  with tf.name_scope("train"):     optimizer = tf.train.GradientDescentOptimizer(learning_rate)     training_op = optimizer.minimize(loss)    reuse_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES,                                scope="hidden[123]") # regular expression restore_saver = tf.train.Saver(reuse_vars) # to restore layers 1-3  init = tf.global_variables_initializer() saver = tf.train.Saver()  with tf.Session() as sess:     init.run()     restore_saver.restore(sess, "./my_model_final.ckpt")      for epoch in range(n_epochs):                                            # not shown in the book         for X_batch, y_batch in shuffle_batch(X_train, y_train, batch_size): # not shown             sess.run(training_op, feed_dict={X: X_batch, y: y_batch})        # not shown         accuracy_val = accuracy.eval(feed_dict={X: X_valid, y: y_valid})     # not shown         print(epoch, "Validation accuracy:", accuracy_val)                   # not shown      save_path = saver.save(sess, "./my_new_model_final.ckpt")

Reusing Models from Other Frameworks

首先取出我们需要重用的W和b。和常规不同,每一个tensorflow变量都有一个关联分配操作(associated assignment operation
),此操作用来初始化。通过得到这些分配操作的handle(和变量拥有相同的名字,后面加上/Assign)来开始。我们还需要得到每个分配操作的第二个输入量的handle,在这个例子中,第二个输入量对应一个值,这个值用来初始化变量。只需要在run init中把需要初始化的值输入就可以。

 original_w = [[1., 2., 3.], [4., 5., 6.]] # Load the weights from the other framework original_b = [7., 8., 9.]                 # Load the biases from the other framework  X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X") hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu, name="hidden1") # [...] Build the rest of the model  # Get a handle on the assignment nodes for the hidden1 variables graph = tf.get_default_graph() assign_kernel = graph.get_operation_by_name("hidden1/kernel/Assign") assign_bias = graph.get_operation_by_name("hidden1/bias/Assign") init_kernel = assign_kernel.inputs[1] init_bias = assign_bias.inputs[1]  init = tf.global_variables_initializer()  with tf.Session() as sess:     sess.run(init, feed_dict={init_kernel: original_w, init_bias: original_b})     # [...] Train the model on your new task     print(hidden1.eval(feed_dict={X: [[10.0, 11.0]]}))  # not shown in the book     #[[ 61.  83. 105.]]

使用tf.layers.dense()函数创建的层,weights变量在里面叫做kernel,biases变量叫做bias。

Freezing the Lower Layers

 train_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES,                                scope="hidden[34]|outputs") training_op = optimizer.minimize(loss, var_list=train_vars)

 reuse_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES,                                scope="hidden[123]") # regular expression restore_saver = tf.train.Saver(reuse_vars) # to restore layers 1-3  init = tf.global_variables_initializer() saver = tf.train.Saver()  with tf.Session() as sess:     init.run()     restore_saver.restore(sess, "./my_model_final.ckpt")      for epoch in range(n_epochs):         for X_batch, y_batch in shuffle_batch(X_train, y_train, batch_size):             sess.run(training_op, feed_dict={X: X_batch, y: y_batch})         accuracy_val = accuracy.eval(feed_dict={X: X_valid, y: y_valid})         print(epoch, "Validation accuracy:", accuracy_val)      save_path = saver.save(sess, "./my_new_model_final.ckpt")

第二种方法是添加stop_gradient()层,在这个层之下的所有层都会被冻结。

 with tf.name_scope("dnn"):     hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu,                               name="hidden1") # reused frozen     hidden2 = tf.layers.dense(hidden1, n_hidden2, activation=tf.nn.relu,                               name="hidden2") # reused frozen     hidden2_stop = tf.stop_gradient(hidden2)     hidden3 = tf.layers.dense(hidden2_stop, n_hidden3, activation=tf.nn.relu,                               name="hidden3") # reused, not frozen     hidden4 = tf.layers.dense(hidden3, n_hidden4, activation=tf.nn.relu,                               name="hidden4") # new!     logits = tf.layers.dense(hidden4, n_outputs, name="outputs") # new!

接下来的训练和上面的训练过程一样。

 reuse_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES,                                scope="hidden[123]") # regular expression restore_saver = tf.train.Saver(reuse_vars) # to restore layers 1-3  init = tf.global_variables_initializer() saver = tf.train.Saver()  with tf.Session() as sess:     init.run()     restore_saver.restore(sess, "./my_model_final.ckpt")      for epoch in range(n_epochs):         for X_batch, y_batch in shuffle_batch(X_train, y_train, batch_size):             sess.run(training_op, feed_dict={X: X_batch, y: y_batch})         accuracy_val = accuracy.eval(feed_dict={X: X_valid, y: y_valid})         print(epoch, "Validation accuracy:", accuracy_val)      save_path = saver.save(sess, "./my_new_model_final.ckpt")

Caching the Frozen Layers


 import numpy as np  n_batches = len(X_train) // batch_size  ##275  with tf.Session() as sess:     init.run()     restore_saver.restore(sess, "./my_model_final.ckpt")          h2_cache = sess.run(hidden2, feed_dict={X: X_train})     h2_cache_valid = sess.run(hidden2, feed_dict={X: X_valid}) # not shown in the book      for epoch in range(n_epochs):         shuffled_idx = np.random.permutation(len(X_train))         hidden2_batches = np.array_split(h2_cache[shuffled_idx], n_batches)         y_batches = np.array_split(y_train[shuffled_idx], n_batches)         for hidden2_batch, y_batch in zip(hidden2_batches, y_batches):             sess.run(training_op, feed_dict={hidden2:hidden2_batch, y:y_batch})          accuracy_val = accuracy.eval(feed_dict={hidden2: h2_cache_valid, # not shown                                                 y: y_valid})             # not shown         print(epoch, "Validation accuracy:", accuracy_val)               # not shown      save_path = saver.save(sess, "./my_new_model_final.ckpt")

注意这里的training_op为只训练3,4,以及输出层。

 train_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES,                                scope="hidden[34]|outputs") training_op = optimizer.minimize(loss, var_list=train_vars)

hidden2_batches为一个列表,长度为n_batches的大小,为275.列表中的每一个值都是一个numpy数组。

zip()

首先冻结所有层,训练模型,查看效果。然后试着解锁最上的一层(或者最上的两层),训练的数据越多,能解锁的层也就越多。假如没有获得好的效果,而且训练数据比较少,丢弃最上的一层或者两层,冻结余下的所有层。你可以重复这么做,直到找到合适的重用层的个数。假如训练数据比较多,那么可以替换最上层,或者在最上层以上多增添几层。

Model Zoos

Unsupervised Pretraining

直到2010,梯度消失问题缓解才渐渐使用纯粹的反向传播训练神经网络。现在更多的使用自编码器进行预训练,而不是受限玻尔兹曼机。

Pretraining on an Auxiliary Task

可以建立一个辅助任务,这个辅助任务可以得到大量的标注数据。然后使用这个辅助任务的低阶层当作特征检测器来为你的实际任务重用。

例如你想要做一个人脸识别任务recognize faces,你可以先从互联网上下载很多人的图片,可以先做一个可以鉴定两张图片是否是同一张图片的神经网络。这个神经网络的低层肯定学到了人脸的低阶特征。

又或者先产生很多训练实例,全部标注为好,然后人为破坏它,破坏之后的训练实例标注为差。例如先建立一个判断句子好坏的神经网络,差的实例通过随机改变句子中的一个单词来获得,例如the dog sleeps,变为the dog they,只要这个神经能正确判断,这个神经网络的低阶层可以用作自然语言处理。

Faster Optimizers

Momentum Optimization

常规的梯度下降不考虑之前的梯度是怎么样的,假如局部的梯度很小,那么梯度更新的会非常缓慢。

使用momentum可以使得算法快速找到局部最优

 optimizer = tf.train.MomentumOptimizer(learning_rate=learning_rate,                                         momentum=0.9)

Nesterov Accelerated Gradient

 optimizer = tf.train.MomentumOptimizer(learning_rate=learning_rate,                                        momentum=0.9, use_nesterov=True)

Adam Optimization

 optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)

大多数算法都会产生稠密的模型,意思为非0.假如你需要更快的运行速度,或者少量的内存,你需要稀疏模型。

你可以这么做,把过小的权重设置为0,或者使用L1正则,L1正则会尽可能的把权重设置为0.

可以使用Follow Te Regularized Leader (FTRL)技术结合L1正则使得模型更稀疏。在tensorflow,FTRLOptimizer类中。

Learning Rate Scheduling

在学习开始的时候使用大的学习速率,一旦停止快速更新,那么就减少学习速率。比直接使用一个常量学习速率好。

  1. 分段预先确定常量学习率(Predetermined piecewise constant learning rate):例如首先设置学习率为0.1,然后50个epochs为0.001.尽管能工作的很好,但是需要调试以找到何时使用最佳学习率。
  2. Exponential scheduling:利用迭代次数t,建立函数式

    ,函数值的值为学习率。能工作的很好,只是需要调整学习率。这个式子就好比每进行10次迭代,那么学习率就在原始学习率的基础上下降1/10。
  3. Power scheduling:函数式为

    ,超参数c一般设置为1.比上面的指数下降的要慢。

 initial_learning_rate = 0.1 decay_steps = 10000 decay_rate = 1/10 global_step = tf.Variable(0, trainable=False, name="global_step") learning_rate = tf.train.exponential_decay(initial_learning_rate, global_step,                                            decay_steps, decay_rate) optimizer = tf.train.MomentumOptimizer(learning_rate, momentum=0.9) training_op = optimizer.minimize(loss, global_step=global_step)

这里面

为0.1,r=10000,就是说每进行10000次迭代,那么学习率就下降为原来的1/10.因为在training_op中传入了全局次数变量,在每进行一次训练都会增加它。

AdaGrad, RMSProp, and Adam这类算法在训练的时候能自动的减少学习率。所以不需要额外的学习策略。

Early Stopping

L1和L2可以限制神经网络的连接权重。常规使用tensorflow如下:

 n_inputs = 28 * 28  # MNIST n_hidden1 = 300 n_outputs = 10  X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X") y = tf.placeholder(tf.int32, shape=(None), name="y")  with tf.name_scope("dnn"):     hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu, name="hidden1")     logits = tf.layers.dense(hidden1, n_outputs, name="outputs") W1 = tf.get_default_graph().get_tensor_by_name("hidden1/kernel:0") W2 = tf.get_default_graph().get_tensor_by_name("outputs/kernel:0")  scale = 0.001 # l1 regularization hyperparameter  with tf.name_scope("loss"):     xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y,                                                               logits=logits)     base_loss = tf.reduce_mean(xentropy, name="avg_xentropy")     reg_losses = tf.reduce_sum(tf.abs(W1)) + tf.reduce_sum(tf.abs(W2))     loss = tf.add(base_loss, scale * reg_losses, name="loss")

 scale = 0.001 my_dense_layer = partial(     tf.layers.dense, activation=tf.nn.relu,     kernel_regularizer=tf.contrib.layers.l1_regularizer(scale))  with tf.name_scope("dnn"):     hidden1 = my_dense_layer(X, n_hidden1, name="hidden1")     hidden2 = my_dense_layer(hidden1, n_hidden2, name="hidden2")     logits = my_dense_layer(hidden2, n_outputs, activation=None,                             name="outputs")

tensorflow在图内部建立计算对应每层权重正则。tensorflow会自动的把这些节点放到一个特殊的collection中,这个collection中包含所有的正则损失。

 with tf.name_scope("loss"):                                     # not shown in the book     xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(  # not shown         labels=y, logits=logits)                                # not shown     base_loss = tf.reduce_mean(xentropy, name="avg_xentropy")   # not shown     reg_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)     loss = tf.add_n([base_loss] + reg_losses, name="loss")

最后需要把所有层的正则放到总的损失中。

Dropout

最流行的正则方法当属dropout,即使是state-of-art的方法,使用dropout也能获得大约2%的提升。

使用dropout的神经网络更鲁棒。

使用dropout可以使得其中的一个节点不依赖旁边节点,也不会依赖一些输入节点,此节点会关注连接它的所有输入。然后就使得神经网络对于输入不敏感。

另一种理解 dropout是:使用dropout的神经网络在训练的每一步都产生一个不同的网络。dropout可以看成所有这些网络值得平均。

需要注意得是:假如不做任何操作,以p(keep probability)=0.5得情况为例,那么在测试的时候,每个神经元会连接 2倍于训练时的神经元。为避免此情况,需要在测试的时候把权重降低为原来的0.5(1-p)。还有一种替换做法,可以在训练的时候,给每个输出的神经元除以p.

在tensorflow中使用

  tf.layers.dropout() 

此函数随机丢弃其中的一些节点(把他们设置为0),并且让剩余的神经元除以keep_probability,在训练结束之后,这些函数就不起作用了。

 training = tf.placeholder_with_default(False, shape=(), name='training')  dropout_rate = 0.5  # == 1 - keep_prob X_drop = tf.layers.dropout(X, dropout_rate, training=training)  with tf.name_scope("dnn"):     hidden1 = tf.layers.dense(X_drop, n_hidden1, activation=tf.nn.relu,                               name="hidden1")     hidden1_drop = tf.layers.dropout(hidden1, dropout_rate, training=training)     hidden2 = tf.layers.dense(hidden1_drop, n_hidden2, activation=tf.nn.relu,                               name="hidden2")     hidden2_drop = tf.layers.dropout(hidden2, dropout_rate, training=training)     logits = tf.layers.dense(hidden2_drop, n_outputs, name="outputs")

需要注意的是此函数

 tf.layers.dropout()  ###1

与以下函数不同。

 tf.nn.dropout()   ###2

上面第一种那个函数在非训练阶段会自动关闭,而第二种不会

需要像前面的Batch Normalization一样,在训练阶段把dropout的training设置为true,非训练阶段设置为默认值False

过拟合就增加丢弃比率,未拟合就降低丢弃比率。

Max-Norm Regularization

给权重限制,满足

。实际中我们可以这么做,在一次训练完之后计算

,然后裁剪w,通过

 threshold = 1.0 weights = tf.get_default_graph().get_tensor_by_name("hidden1/kernel:0") clipped_weights = tf.clip_by_norm(weights, clip_norm=threshold, axes=1) clip_weights = tf.assign(weights, clipped_weights)

第一行获得一个第一层隐藏层权重的handle.随后使用clip_by_norm()函数建立一个operation,此函数会沿着第2个axis裁剪weights的值,然后每一个行向量的最大范数为1.最后进行赋值操作。

 threshold = 1.0 weights = tf.get_default_graph().get_tensor_by_name("hidden1/kernel:0") clipped_weights = tf.clip_by_norm(weights, clip_norm=threshold, axes=1) clip_weights = tf.assign(weights, clipped_weights)

第二层也进行相同的操作

 weights2 = tf.get_default_graph().get_tensor_by_name("hidden2/kernel:0") clipped_weights2 = tf.clip_by_norm(weights2, clip_norm=threshold, axes=1) clip_weights2 = tf.assign(weights2, clipped_weights2)

 with tf.Session() as sess:                                              # not shown in the book     init.run()                                                          # not shown     for epoch in range(n_epochs):                                       # not shown         for X_batch, y_batch in shuffle_batch(X_train, y_train, batch_size): # not shown             sess.run(training_op, feed_dict={X: X_batch, y: y_batch})             clip_weights.eval()             clip_weights2.eval()                                        # not shown         acc_valid = accuracy.eval(feed_dict={X: X_valid, y: y_valid})   # not shown         print(epoch, "Validation accuracy:", acc_valid)                 # not shown      save_path = saver.save(sess, "./my_model_final.ckpt")   

按照以上方法可以运行,但是太繁琐,采用如下方法。定义一个max_norm_regularizer()函数

 def max_norm_regularizer(threshold, axes=1, name="max_norm",                          collection="max_norm"):     def max_norm(weights):         clipped = tf.clip_by_norm(weights, clip_norm=threshold, axes=axes)         clip_weights = tf.assign(weights, clipped, name=name)         tf.add_to_collection(collection, clip_weights)         return None # there is no regularization loss term     return max_norm

max-norm regularization不需要在总损失后面添加正则项。这也是max_norm()函数返回None的原因。但是还是需要在训练的每一步都运行clip_weights。这也就是把clip_weights放到一个collection里面的原因。按照如上定义可以直接在tf.layers.dense中添加正则方法。

 max_norm_reg = max_norm_regularizer(threshold=1.0)  with tf.name_scope("dnn"):     hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu,                               kernel_regularizer=max_norm_reg, name="hidden1")     hidden2 = tf.layers.dense(hidden1, n_hidden2, activation=tf.nn.relu,                               kernel_regularizer=max_norm_reg, name="hidden2")     logits = tf.layers.dense(hidden2, n_outputs, name="outputs")

训练还是和往常一样,只不过在训练之后需要多运行裁剪操作

 clip_all_weights = tf.get_collection("max_norm")  with tf.Session() as sess:     init.run()     for epoch in range(n_epochs):         for X_batch, y_batch in shuffle_batch(X_train, y_train, batch_size):             sess.run(training_op, feed_dict={X: X_batch, y: y_batch})             sess.run(clip_all_weights)         acc_valid = accuracy.eval(feed_dict={X: X_valid, y: y_valid}) # not shown         print(epoch, "Validation accuracy:", acc_valid)   

Data Augmentation

数据增强包括:平移,旋转,尺寸变化。让模型对于位置,方向,目标尺寸很宽容。假如你想让模型对于光照很宽容,那么你可以使用不同量的光照强度来产生图片。

Practical Guidelines

默认DNN设置:

  1. 无法找到合适学习率的情况下,例如收敛太慢,增加学习率导致收敛快了但是精度不是全局最优,需要使用指数衰减。
  2. 训练集太小,需要使用数据增强。
  3. 需要稀疏模型,可以使用L1正则,还可以把较小权重值直接归为0。假如你需要一个更稀疏的模型,那么可以使用FTRL来代替Adam优化,并上L1正则。
  4. 假如你需要一个很快的模型,可以去掉Batch-Normalization,用leaky Relu去代替ELU。还可以使用稀疏模型的方法。

Exercises

  1. 可以把bias设置为0
  2. ELU与RELU相比的优点:1.ELU考虑了输出为负的情况,对于某一层所有节点的输出平均为0,不像RELU一样,只会输出正值。缓解了梯度消失。2.在所有地方导数都不为负,避免了神经元的死亡(RELU中会出现)。3.ELU处处平滑,不像RElu一样在0处不连续,在0处的这种突变会导致梯度下降变慢。
  3. ELU一般为默认。假如你想要程序变快,那么可以使用leaky RELU,常规ReLU的简单性使得ReLU被广泛使用,虽然它的效果比不过ELU和leaky ReLU。tanh用于输出-1,1之间的值,现在很少用。logistic activation function通常用于输出概率的地方,它的输出为0,1,一般也不用于隐藏层,除了在自编码器中。softmax通常用在输出为多个独立分类的地方。
  4. momentum的值设置为0.999会导致模型收敛的更慢。
  5. 稀疏模型的获得方法,1.把过小的权重值归为0。2.使用L1正则。3.使用L1正则加上FTRL优化器。
  6. dropout会使得训练变慢,假如把p设置为0.5,那么变慢2倍。但是在测试的时候每影响。
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!