TensorFlow - 将L2正则化和dropout引入网络。这有意义吗?

25

我目前正在参加优达学城深度学习课程,其中涉及到人工神经网络(ANN)。

我已经成功地构建和训练了网络,并对所有权重和偏差引入了L2正则化。现在,我正在尝试使用隐藏层的dropout来提高泛化性能。我想知道,在隐藏层引入L2正则化并使用dropout是否有意义?如果有,如何正确操作?

在dropout期间,我们会关闭隐藏层中一半的激活,将其余神经元输出的数量翻倍。当使用L2时,我们会计算所有隐藏权重的L2范数。但我不确定在使用dropout时如何计算L2范数。我们关闭了一些激活,难道我们不应该从L2计算中删除现在“未使用”的权重吗?关于这个问题的任何参考资料都会有用,我没有找到任何信息。

如果您感兴趣,以下是我使用L2正则化的ANN代码:

#for NeuralNetwork model code is below
#We will use SGD for training to save our time. Code is from Assignment 2
#beta is the new parameter - controls level of regularization. Default is 0.01
#but feel free to play with it
#notice, we introduce L2 for both biases and weights of all layers

beta = 0.01

#building tensorflow graph
graph = tf.Graph()
with graph.as_default():
      # Input data. For the training data, we use a placeholder that will be fed
  # at run time with a training minibatch.
  tf_train_dataset = tf.placeholder(tf.float32,
                                    shape=(batch_size, image_size * image_size))
  tf_train_labels = tf.placeholder(tf.float32, shape=(batch_size, num_labels))
  tf_valid_dataset = tf.constant(valid_dataset)
  tf_test_dataset = tf.constant(test_dataset)

  #now let's build our new hidden layer
  #that's how many hidden neurons we want
  num_hidden_neurons = 1024
  #its weights
  hidden_weights = tf.Variable(
    tf.truncated_normal([image_size * image_size, num_hidden_neurons]))
  hidden_biases = tf.Variable(tf.zeros([num_hidden_neurons]))

  #now the layer itself. It multiplies data by weights, adds biases
  #and takes ReLU over result
  hidden_layer = tf.nn.relu(tf.matmul(tf_train_dataset, hidden_weights) + hidden_biases)

  #time to go for output linear layer
  #out weights connect hidden neurons to output labels
  #biases are added to output labels  
  out_weights = tf.Variable(
    tf.truncated_normal([num_hidden_neurons, num_labels]))  

  out_biases = tf.Variable(tf.zeros([num_labels]))  

  #compute output  
  out_layer = tf.matmul(hidden_layer,out_weights) + out_biases
  #our real output is a softmax of prior result
  #and we also compute its cross-entropy to get our loss
  #Notice - we introduce our L2 here
  loss = (tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(
    out_layer, tf_train_labels) +
    beta*tf.nn.l2_loss(hidden_weights) +
    beta*tf.nn.l2_loss(hidden_biases) +
    beta*tf.nn.l2_loss(out_weights) +
    beta*tf.nn.l2_loss(out_biases)))

  #now we just minimize this loss to actually train the network
  optimizer = tf.train.GradientDescentOptimizer(0.5).minimize(loss)

  #nice, now let's calculate the predictions on each dataset for evaluating the
  #performance so far
  # Predictions for the training, validation, and test data.
  train_prediction = tf.nn.softmax(out_layer)
  valid_relu = tf.nn.relu(  tf.matmul(tf_valid_dataset, hidden_weights) + hidden_biases)
  valid_prediction = tf.nn.softmax( tf.matmul(valid_relu, out_weights) + out_biases) 

  test_relu = tf.nn.relu( tf.matmul( tf_test_dataset, hidden_weights) + hidden_biases)
  test_prediction = tf.nn.softmax(tf.matmul(test_relu, out_weights) + out_biases)



#now is the actual training on the ANN we built
#we will run it for some number of steps and evaluate the progress after 
#every 500 steps

#number of steps we will train our ANN
num_steps = 3001

#actual training
with tf.Session(graph=graph) as session:
  tf.initialize_all_variables().run()
  print("Initialized")
  for step in range(num_steps):
    # Pick an offset within the training data, which has been randomized.
    # Note: we could use better randomization across epochs.
    offset = (step * batch_size) % (train_labels.shape[0] - batch_size)
    # Generate a minibatch.
    batch_data = train_dataset[offset:(offset + batch_size), :]
    batch_labels = train_labels[offset:(offset + batch_size), :]
    # Prepare a dictionary telling the session where to feed the minibatch.
    # The key of the dictionary is the placeholder node of the graph to be fed,
    # and the value is the numpy array to feed to it.
    feed_dict = {tf_train_dataset : batch_data, tf_train_labels : batch_labels}
    _, l, predictions = session.run(
      [optimizer, loss, train_prediction], feed_dict=feed_dict)
    if (step % 500 == 0):
      print("Minibatch loss at step %d: %f" % (step, l))
      print("Minibatch accuracy: %.1f%%" % accuracy(predictions, batch_labels))
      print("Validation accuracy: %.1f%%" % accuracy(
        valid_prediction.eval(), valid_labels))
      print("Test accuracy: %.1f%%" % accuracy(test_prediction.eval(), test_labels))

6
为什么要对偏差进行正则化? - Tamim Addari
3个回答

18

好的,经过一些额外的努力,我成功地将L2和dropout引入了我的网络中,代码如下。与没有dropout(有L2的情况下)的相同网络相比,我获得了轻微的改进。我仍然不确定引入L2和dropout是否真的值得这个努力,但至少它可以工作并略微改善结果。

#ANN with introduced dropout
#This time we still use the L2 but restrict training dataset
#to be extremely small

#get just first 500 of examples, so that our ANN can memorize whole dataset
train_dataset_2 = train_dataset[:500, :]
train_labels_2 = train_labels[:500]

#batch size for SGD and beta parameter for L2 loss
batch_size = 128
beta = 0.001

#that's how many hidden neurons we want
num_hidden_neurons = 1024

#building tensorflow graph
graph = tf.Graph()
with graph.as_default():
  # Input data. For the training data, we use a placeholder that will be fed
  # at run time with a training minibatch.
  tf_train_dataset = tf.placeholder(tf.float32,
                                    shape=(batch_size, image_size * image_size))
  tf_train_labels = tf.placeholder(tf.float32, shape=(batch_size, num_labels))
  tf_valid_dataset = tf.constant(valid_dataset)
  tf_test_dataset = tf.constant(test_dataset)

  #now let's build our new hidden layer
  #its weights
  hidden_weights = tf.Variable(
    tf.truncated_normal([image_size * image_size, num_hidden_neurons]))
  hidden_biases = tf.Variable(tf.zeros([num_hidden_neurons]))

  #now the layer itself. It multiplies data by weights, adds biases
  #and takes ReLU over result
  hidden_layer = tf.nn.relu(tf.matmul(tf_train_dataset, hidden_weights) + hidden_biases)

  #add dropout on hidden layer
  #we pick up the probabylity of switching off the activation
  #and perform the switch off of the activations
  keep_prob = tf.placeholder("float")
  hidden_layer_drop = tf.nn.dropout(hidden_layer, keep_prob)  

  #time to go for output linear layer
  #out weights connect hidden neurons to output labels
  #biases are added to output labels  
  out_weights = tf.Variable(
    tf.truncated_normal([num_hidden_neurons, num_labels]))  

  out_biases = tf.Variable(tf.zeros([num_labels]))  

  #compute output
  #notice that upon training we use the switched off activations
  #i.e. the variaction of hidden_layer with the dropout active
  out_layer = tf.matmul(hidden_layer_drop,out_weights) + out_biases
  #our real output is a softmax of prior result
  #and we also compute its cross-entropy to get our loss
  #Notice - we introduce our L2 here
  loss = (tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(
    out_layer, tf_train_labels) +
    beta*tf.nn.l2_loss(hidden_weights) +
    beta*tf.nn.l2_loss(hidden_biases) +
    beta*tf.nn.l2_loss(out_weights) +
    beta*tf.nn.l2_loss(out_biases)))

  #now we just minimize this loss to actually train the network
  optimizer = tf.train.GradientDescentOptimizer(0.5).minimize(loss)

  #nice, now let's calculate the predictions on each dataset for evaluating the
  #performance so far
  # Predictions for the training, validation, and test data.
  train_prediction = tf.nn.softmax(out_layer)
  valid_relu = tf.nn.relu(  tf.matmul(tf_valid_dataset, hidden_weights) + hidden_biases)
  valid_prediction = tf.nn.softmax( tf.matmul(valid_relu, out_weights) + out_biases) 

  test_relu = tf.nn.relu( tf.matmul( tf_test_dataset, hidden_weights) + hidden_biases)
  test_prediction = tf.nn.softmax(tf.matmul(test_relu, out_weights) + out_biases)



#now is the actual training on the ANN we built
#we will run it for some number of steps and evaluate the progress after 
#every 500 steps

#number of steps we will train our ANN
num_steps = 3001

#actual training
with tf.Session(graph=graph) as session:
  tf.initialize_all_variables().run()
  print("Initialized")
  for step in range(num_steps):
    # Pick an offset within the training data, which has been randomized.
    # Note: we could use better randomization across epochs.
    offset = (step * batch_size) % (train_labels_2.shape[0] - batch_size)
    # Generate a minibatch.
    batch_data = train_dataset_2[offset:(offset + batch_size), :]
    batch_labels = train_labels_2[offset:(offset + batch_size), :]
    # Prepare a dictionary telling the session where to feed the minibatch.
    # The key of the dictionary is the placeholder node of the graph to be fed,
    # and the value is the numpy array to feed to it.
    feed_dict = {tf_train_dataset : batch_data, tf_train_labels : batch_labels, keep_prob : 0.5}
    _, l, predictions = session.run(
      [optimizer, loss, train_prediction], feed_dict=feed_dict)
    if (step % 500 == 0):
      print("Minibatch loss at step %d: %f" % (step, l))
      print("Minibatch accuracy: %.1f%%" % accuracy(predictions, batch_labels))
      print("Validation accuracy: %.1f%%" % accuracy(
        valid_prediction.eval(), valid_labels))
      print("Test accuracy: %.1f%%" % accuracy(test_prediction.eval(), test_labels))

1
关于dropout的原始论文确实特别提到了这种配置,所以你尝试这个应该没问题。不过我要注意的是,我认为在偏置上不包括L2正则化只包括在权重上才是正常的。http://www.jmlr.org/papers/volume15/srivastava14a.old/source/srivastava14a.pdf http://stats.stackexchange.com/questions/153605/no-regularisation-term-for-bias-unit-in-neural-network - David Parks
2
@DavidParks 看起来我们需要在偏置上也包含L2。请查看TensorFlow代码示例MNIST,链接:https://github.com/tensorflow/tensorflow/blob/master/tensorflow/models/image/mnist/convolutional.py,搜索“l2_loss”函数调用即可。 - Petr Shypila
2
以此为例:我们有一个单一的特征x,和它的取值y,我们对数据进行线性拟合,y=mx+b。如果所有数据点都聚集在y=1000附近且方差很小,我们需要一个较大的偏置来将直线向上移动到1000。这并不是调整的问题,而是数据所在的位置。问题出现在当我们过度依赖某个特征时。偏置只是一个偏移量。话虽如此,我最近绘制了分类和回归问题中权重和偏置的直方图,但在两种情况下都没有看到过大的偏置。因此,我怀疑它并未引起明显的问题。 - David Parks
3
@PetrShypila 提供的例子似乎有误。据我所知,对偏置应用正则化是没有意义的。偏置不会使您的模型过度拟合,因此不应受到惩罚。这是关于正则化的另一个课程:https://www.youtube.com/watch?v=ef2OPmANLaM(顺便说一句,我在3000步没有对偏置进行正则化就获得了93%的准确性)。 - Julien
2
我从未见过在损失函数中乘以beta。这是常见的吗?这应该是一个需要调整的参数吗? - O.rka
显示剩余4条评论

9

使用多种正则化方法没有任何负面影响。事实上,有一篇论文《Dropout: A Simple Way to Prevent Neural Networks from Overfitting》研究了它的帮助程度。显然,不同的数据集会有不同的结果,但对于您的MNIST:

enter image description here

您可以看到,Dropout + Max-norm 给出了最低的错误率。除此之外,您的代码存在大错误

您在权重和偏置上使用了 l2_loss:

beta*tf.nn.l2_loss(hidden_weights) +
beta*tf.nn.l2_loss(hidden_biases) +
beta*tf.nn.l2_loss(out_weights) +
beta*tf.nn.l2_loss(out_biases)))

您不应该惩罚高偏差。因此,应删除偏差上的l2_loss。

4
实际上,原始论文使用的是max-norm正则化而不是L2正则化,此外还包括dropout: “神经网络在约束条件||w||2 ≤ c下进行优化。当w超出该范围时,通过将w投影到半径为c的球面上来强制实施该约束。这也被称为max-norm正则化,因为它意味着任何权重的范数可以达到的最大值是c。”(http://jmlr.org/papers/volume15/srivastava14a/srivastava14a.pdf
您可以在这里找到有关此正则化方法的良好讨论:https://plus.google.com/+IanGoodfellow/posts/QUaCJfvDpni

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接