卷积层中的偏差(bias)对测试准确度是否真的有影响?

24

我理解在小网络中需要偏置来移动激活函数。但对于具有多层CNN、池化、Dropout和其他非线性激活的深度网络,偏置真的有影响吗?卷积滤波器正在学习局部特征,并且对于给定的卷积输出通道,使用相同的偏置。

这不是这个链接的重复。上面的链接仅解释了小型神经网络中偏置的作用,并没有尝试解释包含多个CNN层、Dropout、池化和非线性激活函数的深度网络中偏置的作用。

我进行了一个简单的实验,结果表明从conv层中去除偏置对最终测试准确性没有影响。 已训练两个模型,测试精度几乎相同(一个没有偏差稍微好一点)。

  • model_with_bias,
  • model_without_bias(在conv层中未添加偏差)

它们只是出于历史原因而被使用吗?

如果使用偏置不会提高准确性,那么我们不应该省略它们吗?这样可以减少需要学习的参数。

如果有比我更深入的了解,希望能够解释这些偏差在深度网络中的重要性(如果有的话)。

这是完整的代码和实验结果:bias-VS-no_bias experiment

batch_size = 16
patch_size = 5
depth = 16
num_hidden = 64

graph = tf.Graph()

with graph.as_default():

  # Input data.
  tf_train_dataset = tf.placeholder(
    tf.float32, shape=(batch_size, image_size, image_size, num_channels))
  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)

  # Variables.
  layer1_weights = tf.Variable(tf.truncated_normal(
      [patch_size, patch_size, num_channels, depth], stddev=0.1))
  layer1_biases = tf.Variable(tf.zeros([depth]))
  layer2_weights = tf.Variable(tf.truncated_normal(
      [patch_size, patch_size, depth, depth], stddev=0.1))
  layer2_biases = tf.Variable(tf.constant(1.0, shape=[depth]))
  layer3_weights = tf.Variable(tf.truncated_normal(
      [image_size // 4 * image_size // 4 * depth, num_hidden], stddev=0.1))
  layer3_biases = tf.Variable(tf.constant(1.0, shape=[num_hidden]))
  layer4_weights = tf.Variable(tf.truncated_normal(
      [num_hidden, num_labels], stddev=0.1))
  layer4_biases = tf.Variable(tf.constant(1.0, shape=[num_labels]))

  # define a Model with bias .
  def model_with_bias(data):
    conv = tf.nn.conv2d(data, layer1_weights, [1, 2, 2, 1], padding='SAME')
    hidden = tf.nn.relu(conv + layer1_biases)
    conv = tf.nn.conv2d(hidden, layer2_weights, [1, 2, 2, 1], padding='SAME')
    hidden = tf.nn.relu(conv + layer2_biases)
    shape = hidden.get_shape().as_list()
    reshape = tf.reshape(hidden, [shape[0], shape[1] * shape[2] * shape[3]])
    hidden = tf.nn.relu(tf.matmul(reshape, layer3_weights) + layer3_biases)
    return tf.matmul(hidden, layer4_weights) + layer4_biases

  # define a Model without bias added in the convolutional layer.
  def model_without_bias(data):
    conv = tf.nn.conv2d(data, layer1_weights, [1, 2, 2, 1], padding='SAME')
    hidden = tf.nn.relu(conv ) # layer1_ bias is not added 
    conv = tf.nn.conv2d(hidden, layer2_weights, [1, 2, 2, 1], padding='SAME')
    hidden = tf.nn.relu(conv) # + layer2_biases)
    shape = hidden.get_shape().as_list()
    reshape = tf.reshape(hidden, [shape[0], shape[1] * shape[2] * shape[3]])
    # bias are added only in Fully connected layer(layer 3 and layer 4)
    hidden = tf.nn.relu(tf.matmul(reshape, layer3_weights) + layer3_biases)
    return tf.matmul(hidden, layer4_weights) + layer4_biases

  # Training computation.
  logits_with_bias = model_with_bias(tf_train_dataset)
  loss_with_bias = tf.reduce_mean(
    tf.nn.softmax_cross_entropy_with_logits(labels=tf_train_labels, logits=logits_with_bias))

  logits_without_bias = model_without_bias(tf_train_dataset)
  loss_without_bias = tf.reduce_mean(
    tf.nn.softmax_cross_entropy_with_logits(labels=tf_train_labels, logits=logits_without_bias))

  # Optimizer.
  optimizer_with_bias = tf.train.GradientDescentOptimizer(0.05).minimize(loss_with_bias)
  optimizer_without_bias = tf.train.GradientDescentOptimizer(0.05).minimize(loss_without_bias)

  # Predictions for the training, validation, and test data.
  train_prediction_with_bias = tf.nn.softmax(logits_with_bias)
  valid_prediction_with_bias = tf.nn.softmax(model_with_bias(tf_valid_dataset))
  test_prediction_with_bias = tf.nn.softmax(model_with_bias(tf_test_dataset))

  # Predictions for without
  train_prediction_without_bias = tf.nn.softmax(logits_without_bias)
  valid_prediction_without_bias = tf.nn.softmax(model_without_bias(tf_valid_dataset))
  test_prediction_without_bias = tf.nn.softmax(model_without_bias(tf_test_dataset))

num_steps = 1001

with tf.Session(graph=graph) as session:
  tf.global_variables_initializer().run()
  print('Initialized')
  for step in range(num_steps):
    offset = (step * batch_size) % (train_labels.shape[0] - batch_size)
    batch_data = train_dataset[offset:(offset + batch_size), :, :, :]
    batch_labels = train_labels[offset:(offset + batch_size), :]
    feed_dict = {tf_train_dataset : batch_data, tf_train_labels : batch_labels}
    session.run(optimizer_with_bias, feed_dict=feed_dict)
    session.run(optimizer_without_bias, feed_dict = feed_dict)
  print('Test accuracy(with bias): %.1f%%' % accuracy(test_prediction_with_bias.eval(), test_labels))
  print('Test accuracy(without bias): %.1f%%' % accuracy(test_prediction_without_bias.eval(), test_labels))

输出:

初始化

测试准确率(有偏置): 90.5%

测试准确率(无偏置): 90.6%


4
卷积层与其他层一样,需要偏置项。这是因为偏置项在神经网络中的作用与其他层相似。 - fractals
我理解在小型网络中需要偏置来移动激活函数。但是对于具有CNN层和其他非线性激活的深度网络,偏置是否有所不同?省略上述偏置项几乎没有任何影响。 - Aparajuli
我对tensorflow还很陌生,但是在完成了coursera上deepmind.ai的Conv2d NN之后,我认为tensorflow会自动处理偏差:Andrew Nugyen:“你不需要担心偏差变量,因为你很快就会发现TensorFlow函数会处理偏差。”也许你看到的性能相同(带偏差略差),是因为它们都有偏差,只是你给带偏差的那个额外添加了一组重复的偏差项。如果你查看nn.conv2d方法,你会发现它包含一个偏差,该偏差在卷积之后添加。 - brian_ds
2个回答

19
学习算法如梯度下降通过调整权重来调整偏置。与权重不同的是,偏置独立于前一层的输出。从概念上讲,偏置是由具有固定激活值1的神经元输入引起的,因此通过减去Δ值和学习速率的乘积来更新。

在大型模型中,删除偏置输入并没有太大的影响,因为每个节点可以根据其所有输入的平均激活创建一个偏置节点��根据大数定律,这将近似于正态分布。在第一层中,这取决于输入分布的能力。 在小型网络上,当然需要有偏置输入,但在大型网络上,去掉它几乎没有任何差别

虽然在大型网络中它没有区别,但仍取决于网络架构。例如在LSTM中:

大多数LSTM应用程序仅使用小随机权重初始化LSTM,在许多问题上效果良好。但是,这种初始化有效地将遗忘门设置为0.5。这会导致每个时间步长的梯度消失,因子为0.5,当长期依赖性特别严重时可能会出现问题。通过将遗忘门的偏置初始化为大值,例如1或2,可以解决此问题。这样做可以让遗忘门在初始化时开始更加开放。

遗忘门将被初始化为接近1的值,以实现梯度流。

另请参见:


我理解偏置在神经网络中的作用。然而,在卷积层中,我们只想学习依赖于前一层的局部特征,例如边缘、图案、矩等,我们真的需要偏置吗?是否有关于测试准确性的结果比较的解释,包括使用和不使用偏置的情况? - Aparajuli
1
在一个大模型中,移除偏置输入几乎没有什么影响,因为每个节点都可以通过其所有输入的平均激活来生成一个偏置节点,根据大数定律,这将近似于正态分布。在第一层,这种情况取决于您的输入分布。例如,在MNIST中,输入的平均激活大致是恒定的。当然,在小网络上,您需要一个偏置输入,但在大网络上,删除它几乎没有任何影响。(但是,为什么要删除它呢?) - Amir
我的观点正是你所写的:“在小型网络中,当然需要偏置输入,但在大型网络中,去除它几乎没有任何影响。”如果没有影响,为什么要添加它。去除偏置意味着要学习的参数更少,训练时间更短。在更深层次的架构中,可以少学习100到甚至数千个参数。 - Aparajuli
@Aparajuli,在今天的神经网络架构中,数百个偏差相比于数百万个参数可以忽略不计。不幸的是,我找不到数学上的原因。 - Amir

4
在大多数网络中,卷积层后面会有一个批量归一化层,并且该层具有偏置。因此,如果您使用了批量归一化层,则没有增益。详情请参见: Can not use both bias and batch normalization in convolution layers 否则,从数学角度来看,您正在学习不同的函数。然而,事实证明,特别是对于简单问题的非常复杂的网络,您可能会在没有偏差的情况下实现几乎与有偏差相同的结果,但最终会使用更多的参数。根据我的经验,在深度学习中使用比所需参数多2-4倍的因素很少会影响性能 - 特别是如果您进行正则化。因此,很难注意到任何区别。但是,您可以尝试使用较少的通道(我认为卷积的深度并不像通道的数量那么重要),并查看偏差是否有所不同。我猜会有所不同。

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