使用 BERT 进行多标签分类

Posted

技术标签:

【中文标题】使用 BERT 进行多标签分类【英文标题】:Doing Multi-Label classification with BERT 【发布时间】:2019-09-24 04:08:59 【问题描述】:

我想用BERT model用Tensorflow做多标签分类。

为此,我想从BERT github repository 改编示例run_classifier.py,这是一个关于如何使用BERT 进行简单分类的示例,使用pre-trained weights given by 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】:

您想用 sigmoid 替换对可能的输出(所有分数总和为 1)进行单一分布建模的 softmax,它为每个类建模独立分布(每个输出都有是/否分布)。

因此,您正确地更改了损失函数,但您还需要更改计算概率的方式。应该是:

probabilities = tf.sigmoid(logits)

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

【讨论】:

谢谢!是否可以设置我想要训练的层(如 Keras 的 freezeunfreeze),这样我只重新训练最后一层? 您可以在output_layer 上应用tf.stop_gradient,它会阻止BERT 模型中的反向传播。

以上是关于使用 BERT 进行多标签分类的主要内容,如果未能解决你的问题,请参考以下文章

BERT-多标签文本分类实战之一——实战项目总览

BERT-多标签文本分类实战之一——实战项目总览

Bert模型做多标签文本分类

BERT-多标签文本分类实战之三——多标签文本分类的方向与常用数据集介绍

BERT-多标签文本分类实战之三——多标签文本分类的方向与常用数据集介绍

BERT-多标签文本分类实战之三——多标签文本分类的方向与常用数据集介绍