BERT模型在多类别文本分类时的precision, recall, f1值的计算

  BERT预训练模型在诸多NLP任务中都取得最优的结果。在处理文本分类问题时,即可以直接用BERT模型作为文本分类的模型,也可以将BERT模型的最后层输出的结果作为word embedding导入到我们定制的文本分类模型中(如text-CNN等)。总之现在只要你的计算资源能满足,一般问题都可以用BERT来处理,此次针对公司的一个实际项目——一个多类别(61类)的文本分类问题,其就取得了很好的结果。

  我们此次的任务是一个数据分布极度不平衡的多类别文本分类(有的类别下只有几个或者十几个样本,有的类别下又有几千个样本),在不做不平衡数据处理且不采用BERT模型时,其取得的F1值只有50%,而在不做不平衡数据处理但采用BERT模型时,其F1值能达到65%,但是在用bert模型时获得F1值时却存在一些问题。

  在tensorflow中只提供了二分类的precision,recall,f1值的计算接口,而bert源代码中的run_classifier.py文件中训练模型,验证模型等都是用的estimator API,这些高层API极大的限制了修改代码的灵活性。好在tensorflow源码中有一个方法可以计算混淆矩阵的方法,并且会返回一个operation。注意:这个和tf.confusion_matrix()不同,具体看源代码中下面这段代码:

        elif mode == tf.estimator.ModeKeys.EVAL:

            def metric_fn(per_example_loss, label_ids, logits, num_labels):
                predictions = tf.argmax(logits, axis=-1, output_type=tf.int32)
                accuracy = tf.metrics.accuracy(
                    labels=label_ids, predictions=predictions)
                    # 这里的metrics时我们定义的一个python文件,在下面会介绍
                conf_mat = metrics.get_metrics_ops(label_ids, predictions, num_labels)

                loss = tf.metrics.mean(values=per_example_loss)
                return {
                    "eval_accuracy": accuracy,
                    "eval_cm": conf_mat,
                    "eval_loss": loss,
                }

  验证时的性能指标计算都在这个方法里面,而且在return的这个字典中每个值必须是一个tuple。以accuracy为例,tf.metrics.accuracy返回的是一个(accuracy, update_op)这样一个tuple,而我们上一段说的tf.confusion_matrix只返回一个混淆矩阵。因此在这里我们使用一个内部的方法,方法导入如下:

from tensorflow.python.ops.metrics_impl import _streaming_confusion_matrix

这个方法会返回一个(confusion_matrix, update_op)的tuple。我们新建一个metrics.py文件,里面的代码如下:

import numpy as np
import tensorflow as tf
from tensorflow.python.ops.metrics_impl import _streaming_confusion_matrix

def get_metrics_ops(labels, predictions, num_labels):  # 得到混淆矩阵和update_op,在这里我们需要将生成的混淆矩阵转换成tensor
    cm, op = _streaming_confusion_matrix(labels, predictions, num_labels)
    tf.logging.info(type(cm))
    tf.logging.info(type(op))

    return (tf.convert_to_tensor(cm), op)

def get_metrics(conf_mat, num_labels):
  # 得到numpy类型的混淆矩阵,然后计算precision,recall,f1值。
    precisions = []
    recalls = []
    for i in range(num_labels):
        tp = conf_mat[i][i].sum()
        col_sum = conf_mat[:, i].sum()
        row_sum = conf_mat[i].sum()

        precision = tp / col_sum if col_sum > 0 else 0
        recall = tp / row_sum if row_sum > 0 else 0

        precisions.append(precision)
        recalls.append(recall)

    pre = sum(precisions) / len(precisions)
    rec = sum(recalls) / len(recalls)
    f1 = 2 * pre * rec / (pre + rec)

    return pre, rec, f1

最上面一段代码中return的字典中的值可以在run_classifier.py中main函数中的下面一段代码中得到:

    if FLAGS.do_eval:
        eval_examples = processor.get_dev_examples(FLAGS.data_dir)
        num_actual_eval_examples = len(eval_examples)
        if FLAGS.use_tpu:
            # TPU requires a fixed batch size for all batches, therefore the number
            # of examples must be a multiple of the batch size, or else examples
            # will get dropped. So we pad with fake examples which are ignored
            # later on. These do NOT count towards the metric (all tf.metrics
            # support a per-instance weight, and these get a weight of 0.0).
            while len(eval_examples) % FLAGS.eval_batch_size != 0:
                eval_examples.append(PaddingInputExample())

        eval_file = os.path.join(FLAGS.output_dir, "eval.tf_record")
        file_based_convert_examples_to_features(
            eval_examples, label_list, FLAGS.max_seq_length, tokenizer, eval_file)

        tf.logging.info("***** Running evaluation *****")
        tf.logging.info("  Num examples = %d (%d actual, %d padding)",
                        len(eval_examples), num_actual_eval_examples,
                        len(eval_examples) - num_actual_eval_examples)
        tf.logging.info("  Batch size = %d", FLAGS.eval_batch_size)

        # This tells the estimator to run through the entire set.
        eval_steps = None
        # However, if running eval on the TPU, you will need to specify the
        # number of steps.
        if FLAGS.use_tpu:
            assert len(eval_examples) % FLAGS.eval_batch_size == 0
            eval_steps = int(len(eval_examples) // FLAGS.eval_batch_size)

        eval_drop_remainder = True if FLAGS.use_tpu else False
        eval_input_fn = file_based_input_fn_builder(
            input_file=eval_file,
            seq_length=FLAGS.max_seq_length,
            is_training=False,
            drop_remainder=eval_drop_remainder)
     # result中就是return返回的字典
        result = estimator.evaluate(input_fn=eval_input_fn, steps=eval_steps)

        output_eval_file = os.path.join(FLAGS.output_dir, "eval_results.txt")
        with tf.gfile.GFile(output_eval_file, "w") as writer:
            tf.logging.info("***** Eval results *****")
              # 我们可以拿到混淆矩阵(现在时numpy的形式),调用metrics.py文件中的方法来得到precision,recall,f1值
            pre, rec, f1 = metrics.get_metrics(result["eval_cm"], len(label_list))
            tf.logging.info("eval_precision: {}".format(pre))
            tf.logging.info("eval_recall: {}".format(rec))
            tf.logging.info("eval_f1: {}".format(f1))
            tf.logging.info("eval_accuracy: {}".format(result["eval_accuracy"]))
            tf.logging.info("eval_loss: {}".format(result["eval_loss"]))

            np.save("conf_mat.npy", result["eval_cm"])

通过上面的代码拿到混淆矩阵后,调用metrics.py文件中的get_metrics方法就可以得到precision,recall,f1值。

原文地址:https://www.cnblogs.com/jiangxinyang/p/10341392.html

时间: 2024-12-29 11:54:31

BERT模型在多类别文本分类时的precision, recall, f1值的计算的相关文章

图示详解BERT模型的输入与输出

一.BERT整体结构 BERT主要用了Transformer的Encoder,而没有用其Decoder,我想是因为BERT是一个预训练模型,只要学到其中语义关系即可,不需要去解码完成具体的任务.整体架构如下图: 多个Transformer Encoder一层一层地堆叠起来,就组装成了BERT了,在论文中,作者分别用12层和24层Transformer Encoder组装了两套BERT模型,两套模型的参数总数分别为110M和340M. 二.再次理解Transformer中的Attention机制.

BERT模型介绍

前不久,谷歌AI团队新发布的BERT模型,在NLP业内引起巨大反响,认为是NLP领域里程碑式的进步.BERT模型在机器阅读理解顶级水平测试SQuAD1.1中表现出惊人的成绩:全部两个衡量指标上全面超越人类,并且还在11种不同NLP测试中创出最佳成绩,包括将GLUE基准推至80.4%(绝对改进7.6%),MultiNLI准确度达到86.7%(绝对改进率5.6%)等.BERT模型是以Transformer编码器来表示,本文在详细介绍BERT模型,Transformer编码器的原理可以参考(). 论文

使用BERT模型生成句子序列向量

之前我写过一篇文章,利用bert来生成token级向量(对于中文语料来说就是字级别向量),参考我的文章:<使用BERT模型生成token级向量>.但是这样做有一个致命的缺点就是字符序列长度最长为512(包含[cls]和[sep]).其实对于大多数语料来说已经够了,但是对于有些语料库中样本的字符序列长度都比较长的情况,这就有些不够用了,比如我做一个法院文书领域预测任务,里面的事实部分许多都大于1000字,我做TextCharCNN的时候定义的最大长度为1500(能够涵盖百分之95以上的样本).

win10 + 独显 + Anaconda3 + tensorflow_gpu1.13 安装教程(跑bert模型)

这里面有很多坑,最大的坑是发现各方面都装好了结果报错  Loaded runtime CuDNN library: 7.3.1 but source was compiled with: 7.4.1,这是由于最新的tensorflow1.13需要用 Cudnn7.4.1编译.这个问题,StackOverflow上有人问到,但是目前依然未有人给出解决办法,下文会详述. 1. 去nvida官网下载显卡对应的驱动 2.下载安装Anaconda3,安装时勾选加入环境变量 3. 进入Anaconda Pr

matlab(8) Regularized logistic regression : 不同的λ(0,1,10,100)值对regularization的影响,对应不同的decision boundary\ 预测新的值和计算模型的精度predict.m

不同的λ(0,1,10,100)值对regularization的影响\ 预测新的值和计算模型的精度 %% ============= Part 2: Regularization and Accuracies =============% Optional Exercise:% In this part, you will get to try different values of lambda and % see how regularization affects the decisio

两个文本框异动任何一个能即时更新计算结果

在GridView控件中,有两个文本框,用户更改某一个文本框的值,能即时计算结果.Insus.NET列举一个小例子,来演示些功能.先创建一个对象(类): 在网页中,创建一个集合,假设说,一张采购单有多个物品组件,这样说,采购单就是一个集合,不过些例,只是采购一个物品. 把上面的集合,绑定给GridView数据控件,绑定前,先写Html markup,下划线的部分就是重点部位. 开始在cs绑定数据给GridView控件: 下面来写写OnRowCreated="GridViewPO_RowCreat

BERT模型内部结构解析

BERT强大的特征抽取能力在各方面的应用如火如荼的展开,今日准备要对BERT输出的特征进行组合,以期能具有更好地变现,现在对model对象做详细的分析. 直观上,我们能够利用bert的信息有:1)pool out [CLS]:2)encoder layer:3)hidden state first output 我们加载bert的过程如下: model = modeling.BertModel(config=bert_config, is_training=self.__is_training,

使用R进行分类时,ROC的绘制和AUC的计算

在之前的这篇日志中,我简单介绍了使用R进行Logistic Regression时,Recall,Precision,TPR,FPR等等的计算,但是如果依照这种方法绘制ROC曲线(关于ROC和AUC的概念,网上有很多介绍,例如:http://beader.me/2013/12/15/auc-roc/ )就太过于麻烦了,需要手动调整分类阈值.其实R也提供了最基本的ROC曲线绘制的工具包(Package),结合这篇日志,我也同时介绍一下R Package的安装和学习方法: 在这里:http://ww

[转]卡方分箱中卡方值的计算

<div class="show-content-free"> <p>关于卡方分箱,网上有很多文章,但几乎没有文章介绍分箱时相邻区间卡方值计算的方法,而本文在介绍卡方分箱的同时,重点介绍了相邻区间卡方值的计算方法.通过本文,希望大家能对卡方分箱有清楚透彻的认识.</p> 分箱是什么 分箱是将连续的变量离散化,将多状态的离散变量合并成少状态.这里要注意的是,不仅仅是连续变量要分箱,状态多的离散变量也需要分箱,之前接触过公司内特征工程的项目,里边就将超过