BERT微调做中文文本分类

BERT模型在NLP各项任务中大杀四方,那么我们如何使用这一利器来为我们日常的NLP任务来服务呢?我们首先介绍使用BERT做文本分类任务。
重写读取数据的类 需要根据文件格式重写读取数据的类,只要能够正常读取数据即可。以下代码将x文本记为不含标签训练数据,y文本记为标签数据。

class StatutesProcessor(DataProcessor):def _read_txt_(self, data_dir, x_file_name, y_file_name): # 定义我们的读取方式,我的工程中已经将x文本和y文本分别存入txt文件中,没有分隔符 # 用gfile读取,打开一个没有线程锁的的文件IO Wrapper # 基本上和python原生的open是一样的,只是在某些方面更高效一点 with tf.gfile.Open(data_dir + x_file_name, 'r') as f: lines_x = [x.strip() for x in f.readlines()] with tf.gfile.Open(data_dir + y_file_name, 'r') as f: lines_y = [x.strip() for x in f.readlines()] return lines_x, lines_ydef get_train_examples(self, data_dir): lines_x, lines_y = self._read_txt_(data_dir, 'train_x_no_seg.txt', 'train_y.txt') examples = [] for (i, line) in enumerate(zip(lines_x, lines_y)): guid = 'train-%d' % i # 规范输入编码 text_a = tokenization.convert_to_unicode(line[0]) label = tokenization.convert_to_unicode(line[1]) # 这里有一些特殊的任务,一般任务直接用上面的就行,下面的label操作可以注释掉 # 这里因为y会有多个标签,这里按单标签来做 label = label.strip().split()[0]# 这里不做匹配任务,text_b为None examples.append( InputExample(guid=guid, text_a=text_a, label=label) ) return examplesdef get_dev_examples(self, data_dir): lines_x, lines_y = self._read_txt_(data_dir, 'val_x_no_seg.txt', 'val_y.txt') examples = [] for (i, line) in enumerate(zip(lines_x, lines_y)): guid = 'train-%d' % i # 规范输入编码 text_a = tokenization.convert_to_unicode(line[0]) label = tokenization.convert_to_unicode(line[1]) label = label.strip().split()[0]# 这里不做匹配任务,text_b为None examples.append( InputExample(guid=guid, text_a=text_a, label=label) ) return examplesdef get_test_examples(self, data_dir): lines_x, lines_y = self._read_txt_(data_dir, 'test_x_no_seg.txt', 'test_y.txt') examples = [] for (i, line) in enumerate(zip(lines_x, lines_y)): guid = 'train-%d' % i # 规范输入编码 text_a = tokenization.convert_to_unicode(line[0]) label = tokenization.convert_to_unicode(line[1]) label = label.strip().split()[0]# 这里不做匹配任务,text_b为None examples.append( InputExample(guid=guid, text_a=text_a, label=label) ) return examplesdef get_labels(self): # 我事先统计了所有出现的y值,放在了vocab_y.txt里 # 因为这里没有原生的接口,这里暂时这么做了,只要保证能读到所有的类别就行了 with tf.gfile.Open('data/statutes_small/vocab_y.txt', 'r') as f: vocab_y = [x.strip() for x in f.readlines()] return vocab_y

重写之后,需要加到processors的字典中
def main(_): tf.logging.set_verbosity(tf.logging.INFO)processors = { "cola": ColaProcessor, "mnli": MnliProcessor, "mrpc": MrpcProcessor, "xnli": XnliProcessor, "News2019": StatutesProcessor }

这里面的News2019就是我们新添加的读取数据的类
根据需要重写评估指标的类
def metric_fn(per_example_loss, label_ids, logits, is_real_example): predictions = tf.argmax(logits, axis=-1, output_type=tf.int32) accuracy = tf.metrics.accuracy( labels=label_ids, predictions=predictions, weights=is_real_example) loss = tf.metrics.mean(values=per_example_loss, weights=is_real_example) f1_macro = f1_score(label_ids, predictions, average='macro', sample_weight=is_real_example)return { "eval_accuracy": accuracy, "eval_loss": loss, "eval_f1": f1_macro }

可在metric_fn()函数中比啊那些自己需要的测评指标比如Precision,Recall,F,AUC或者其他测评指标。
加载预训练的模型
if __name__ == "__main__": flags.mark_flag_as_required("data_dir") flags.mark_flag_as_required("task_name") flags.mark_flag_as_required("vocab_file") flags.mark_flag_as_required("bert_config_file") flags.mark_flag_as_required("init_checkpoint") flags.mark_flag_as_required("output_dir") flags.mark_flag_as_required("do_train") flags.mark_flag_as_required("do_eval") flags.mark_flag_as_required("do_predict") tf.app.run()

需要我们事先下载预训练好的中文模型,这其中上面的vocab_file:vocab.txt和bert_config_file: bert_config.json和init_checkpoint:bert_model.ckpt均在下载好的中文模型压缩包中。接下来只需要在命令行输入相关命令即可执行。
小结 对BERT进行微调来解决下游任务比较简单。主要包含步骤如下:
  • 根据数据格式重写读取数据类或利用现有的数据读取函数来对数据格式进行修改
  • 根据要求增删相关评估指标
  • 调用预训练模型进行训练
【BERT微调做中文文本分类】笔者认为有时间的话建议大家能够从了解transformer模型开始来进一步理解BERT模型,只有弄清楚原理以后再应用的时候才能得心应手。接下来笔者会找时间介绍如何使用BERT模型微调做中文实体识别以及序列生成等相关任务的博客。

    推荐阅读