21

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

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

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

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

它们是否仅出于历史原因而使用?

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

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

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

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

2 回答 2

16

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

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

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

LSTMs 的大多数应用只是简单地用小的随机权重初始化 LSTMs,这在许多问题上都能很好地工作。但是这个初始化有效地将遗忘门设置为 0.5。这引入了每时间步长 0.5 倍的消失梯度,当长期依赖关系特别严重时,这可能会导致问题。这个问题可以通过简单地将遗忘门偏差初始化为一个较大的值(例如 1 或 2)来解决。通过这样做,遗忘门将被初始化为接近 1 的值,从而实现梯度流。

也可以看看:

于 2018-08-23T14:47:56.227 回答
2

在大多数网络中,在 conv 层之后都有一个 batchnorm 层,它有一个偏差。因此,如果您有一个 batchnorm 层,则没有任何收益。请参阅: 不能在卷积层中同时使用偏差和批量归一化

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

于 2020-12-14T20:24:44.280 回答