卷积层中的偏差真的会对测试精度产生影响吗?

Posted

技术标签:

【中文标题】卷积层中的偏差真的会对测试精度产生影响吗?【英文标题】:Does bias in the convolutional layer really make a difference to the test accuracy? 【发布时间】:2019-01-28 06:44:54 【问题描述】:

我知道在小型网络中需要偏差来改变激活函数。但是在具有多层 CNN、池化、dropout 和其他非线性激活的 Deep 网络的情况下,Bias 真的有影响吗?卷积滤波器正在学习局部特征并且对于给定的卷积输出通道使用相同的偏置。

这不是this link 的欺骗。上述链接仅解释了偏差在小型神经网络中的作用,并没有试图解释偏差在包含多个 CNN 层、drop-out、池化和非线性激活函数的深度网络中的作用。

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

model_with_bias, model_without_bias(卷积层中未添加偏差)

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

如果使用偏差不能提高准确性,我们不应该忽略它们吗?需要学习的参数更少。

如果有比我知识更深的人能够解释这些偏见在深度网络中的重要性(如果有的话),我将不胜感激。

这里是完整代码和实验结果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%

【问题讨论】:

卷积层需要偏差,原因与其他层需要偏差相同。 ***.com/questions/2480650/… 我知道在小型网络中需要偏差来转移激活函数。但是对于具有 CNN 层和其他非线性激活的 Deep 网络,Bias 是否有所作为?省略上面的偏差项几乎没有区别。 我是 tensorflow 的新手,但刚刚使用 coursera 课程 deepmind.ai 完成了 Conv2d NN,我的印象是 tensorflow 会自动处理偏差:Andrew Nugyen:“你不知道”无需担心偏差变量,因为您很快就会看到 TensorFlow 函数会处理偏差。”也许您看到的是相同的性能(偏差稍差),因为它们都有偏差,您只是给有偏差的那个提供了一组额外的重复偏差项。如果您查看 nn.conv2d 方法,您会发现它包含在卷积之后添加的偏差。 【参考方案1】:

偏差通过学习算法与权重一起调整,例如 梯度下降。 偏差与权重的不同之处在于它们是 独立于前一层的输出。概念上的偏见是 由来自固定激活值为 1 的神经元的输入引起,因此 通过减去 delta 值的乘积来更新 学习率。

在大型模型中,去除偏置输入几乎没有什么区别,因为每个节点都可以从其所有输入的平均激活中产生一个偏置节点,根据大数定律,这将大致是正常的。在第一层,发生这种情况的能力取决于您的输入分布。 在小型网络上,您当然需要偏置输入,但在大型网络上,移除它几乎没有区别

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

LSTM 的大多数应用只是简单地初始化 LSTM 随机权重可以很好地解决许多问题。但是这个 初始化有效地将遗忘门设置为 0.5。这 引入了一个消失梯度,每个时间步的因子为 0.5, 每当长期依赖关系存在时,这可能会导致问题 特别严重。这个问题可以通过简单的初始化 忘记门偏向一个较大的值,例如 1 或 2。通过这样做, 遗忘门将被初始化为接近 1 的值, 启用梯度流。

另见:

The rule of bias in Neural network What is bias in Neural network An Empirical Exploration of Recurrent Network Architectures

【讨论】:

我了解 Bias 在神经网络中的作用。但是,在卷积层中,我们只想学习局部特征,例如边缘、模式、矩等 依赖于前一层,我们真的需要偏差吗?对有偏差和无偏差的测试准确度比较结果有何解释? @Aparajuli 在大型模型中,去除偏差输入几乎没有什么区别,因为每个节点都可以从其所有输入的平均激活中产生一个偏差节点,根据大数定律,这将大致正常。在第一层,发生这种情况的能力取决于您的输入分布。例如,对于 MNIST,输入的平均激活大致是恒定的。在小型网络上,您当然需要偏置输入,但在大型网络上,移除它几乎没有什么区别。 (但是,你为什么要删除它?) 我的观点正是您所写的“在小型网络上,您当然需要偏置输入,但在大型网络上,删除它几乎没有区别”如果没有区别,为什么要添加它。消除偏差意味着要学习的参数更少,训练时间更少。需要学习的参数要少 100 个甚至上千个(在更深层次的架构中)。 @Aparajuli,在当今的神经网络架构中,与数百万个参数相比,数百个偏差可以忽略不计。不幸的是,我找不到数学原因。【参考方案2】:

在大多数网络中,在 conv 层之后都有一个 batchnorm 层,它有一个偏差。因此,如果您有一个 batchnorm 层,则没有任何收益。看: Can not use both bias and batch normalization in convolution layers

否则,从数学的角度来看,您正在学习不同的函数。然而,事实证明,特别是如果你有一个非常复杂的网络来解决一个简单的问题,你可能会在没有偏差的情况下实现几乎相同的事情,而不是在有偏差的情况下,但最终会使用更多参数。根据我的经验,使用比所需参数多 2-4 倍的参数很少会损害深度学习的性能——尤其是在你进行正则化的情况下。因此,很难注意到任何差异。但是,您可能会尝试使用少量通道(我认为网络的深度不如卷积的通道数重要)并查看偏差是否会产生影响。我猜是的。

【讨论】:

以上是关于卷积层中的偏差真的会对测试精度产生影响吗?的主要内容,如果未能解决你的问题,请参考以下文章

不同的RDBMS会对ORM性能测试结果产生影响吗?

高精度的IMU、MEMS陀螺的进口产品稳定吗?

我还能提高 GPS 精度吗?

任意精度数字和 Javascript,Google Web Toolkit

gps校点后左右偏差一米多

探索VGG网络与LeNet网络对精度的影响