使用BERT进行多标签分类

9
我希望使用BERT模型和Tensorflow进行多标签分类。为此,我想要改编BERT github存储库中的示例run_classifier.py,该示例演示了如何使用Google Research提供的预训练权重(例如BERT-Base,Cased)来进行简单分类。我有X个不同的标签,其值为0或1,因此我想向原始BERT模型添加一个新的大小为X的密集层,并使用sigmoid_cross_entropy_with_logits激活函数。因此,在理论部分,我认为我已经做好了准备。问题是,我不知道如何附加一个新的输出层并仅使用我的数据集重新训练这个新层,同时使用现有的BertModel类。
这里是来自run_classifier.py的原始create_model()函数,我猜我需要进行修改。但我有点不知所措。
def create_model(bert_config, is_training, input_ids, input_mask, segment_ids,
                 labels, num_labels, use_one_hot_embeddings):
  """Creates a classification model."""
  model = modeling.BertModel(
      config=bert_config,
      is_training=is_training,
      input_ids=input_ids,
      input_mask=input_mask,
      token_type_ids=segment_ids,
      use_one_hot_embeddings=use_one_hot_embeddings)

  output_layer = model.get_pooled_output()

  hidden_size = output_layer.shape[-1].value

  output_weights = tf.get_variable(
      "output_weights", [num_labels, hidden_size],
      initializer=tf.truncated_normal_initializer(stddev=0.02))

  output_bias = tf.get_variable(
      "output_bias", [num_labels], initializer=tf.zeros_initializer())

  with tf.variable_scope("loss"):
    if is_training:
      # I.e., 0.1 dropout
      output_layer = tf.nn.dropout(output_layer, keep_prob=0.9)

    logits = tf.matmul(output_layer, output_weights, transpose_b=True)
    logits = tf.nn.bias_add(logits, output_bias)
    probabilities = tf.nn.softmax(logits, axis=-1)
    log_probs = tf.nn.log_softmax(logits, axis=-1)

    one_hot_labels = tf.one_hot(labels, depth=num_labels, dtype=tf.float32)

    per_example_loss = -tf.reduce_sum(one_hot_labels * log_probs, axis=-1)
    loss = tf.reduce_mean(per_example_loss)

    return (loss, per_example_loss, logits, probabilities)

这里是同一个函数,经过我的一些修改,但有一些东西缺失了(还有错误的地方?)

def create_model(bert_config, is_training, input_ids, input_mask, segment_ids, labels, num_labels):
  """Creates a classification model."""
  model = modeling.BertModel(
      config=bert_config,
      is_training=is_training,
      input_ids=input_ids,
      input_mask=input_mask,
      token_type_ids=segment_ids)

  output_layer = model.get_pooled_output()

  hidden_size = output_layer.shape[-1].value

  output_weights = tf.get_variable("output_weights", [num_labels, hidden_size],initializer=tf.truncated_normal_initializer(stddev=0.02))

  output_bias = tf.get_variable("output_bias", [num_labels], initializer=tf.zeros_initializer())

  with tf.variable_scope("loss"):
    if is_training:
      # I.e., 0.1 dropout
      output_layer = tf.nn.dropout(output_layer, keep_prob=0.9)

    logits = tf.matmul(output_layer, output_weights, transpose_b=True)
    logits = tf.nn.bias_add(logits, output_bias)
    probabilities = tf.nn.softmax(logits, axis=-1)
    log_probs = tf.nn.log_softmax(logits, axis=-1)

    per_example_loss = tf.nn.sigmoid_cross_entropy_with_logits(labels=labels, logits=logits)

    loss = tf.reduce_mean(per_example_loss)

    return (loss, per_example_loss, logits, probabilities)

除了以下这些我已经在代码中进行了适应,并且没有遇到问题:

  • DataProcessor用于加载和解析我的自定义数据集
  • 将标签变量的类型从数字值更改为数组,无论在何处使用它

所以,如果有人知道我应该做什么来解决我的问题,或者甚至指出我可能犯的一些明显错误,我会很高兴听到。

注:

  • 我找到了this article,它与我正在尝试做的事情非常相似,但它使用PyTorch,我无法将其转换为Tensorflow。
1个回答

9

您希望将模型输出的单个分布(所有得分总和为一)所用的softmax替换为sigmoid,sigmoid可用于每个类别的独立分布(每个输出都有是/否分布)。

因此,您需要正确更改损失函数,并且还需要更改概率计算方式。具体来说,应该是:

probabilities = tf.sigmoid(logits)

在这种情况下,您不需要 log_probs

谢谢! 我能否设置哪一层是可训练的(就像Keras中的“冻结”和“解冻”),这样我只需重新训练最后一层? - Nakeuh
2
您可以在 output_layer 上应用 tf.stop_gradient,这将防止 BERT 模型中的反向传播。 - Jindřich

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