文章预览:
0. bert简介
- BERT(Bidirectional Encoder Representation from Transformers)是google-research在2018年10月提出的一种预训练模型,在11种不同NLP测试中创出SOTA表现,成为NLP发展史上里程碑式的模型成就。
- 本篇文章从实践入手,带领大家进行Bert的中文文本分类模型的训练和推理的使用教程。
1. bert结构
1. bert中文分类模型训练
bert训练主要分为两个阶段:预训练阶段和 Fine-tuning 阶段。
什么是预训练?
BERT是一个预训练的模型,那么什么是预训练呢?举例子进行简单的介绍
假设已有A训练集,先用A对网络进行预训练,在A任务上学会网络参数,然后保存以备后用,当来一个新的任务B,采取相同的网络结构,网络参数初始化的时候可以加载A学习好的参数,其他的高层参数随机初始化,之后用B任务的训练数据来训练网络,当加载的参数保持不变时,称为"frozen",当加载的参数随着B任务的训练进行不断的改变,称为“fine-tuning”,即更好地把参数进行调整使得更适合当前的B任务
优点:当任务B的训练数据较少时,很难很好的训练网络,但是获得了A训练的参数,会比仅仅使用B训练的参数更优
预训练
预训练阶段与 Word2Vec,ELMo 等类似,是在大型数据集上根据一些预训练任务训练得到。预训练需要巨大的运算资源,google官方预训练一个语言模型,需要在 4 到 16 个 Cloud TPU 训练四天的时间,幸运的是,google官方开源了许多预训练模型,包括中文预训练模型。大多数 NLP 研究人员永远不需要从头开始预训练他们自己的模型。
微调
Fine-tuning 阶段是后续用于一些下游任务的时候,在预训练模型基础之上进行微调,例如文本分类,词性标注,问答系统等,BERT 无需调整结构就可以在不同的任务上进行微调。
1 下载bert项目代码
https://github.com/google-research/bert
代码结构
- 预训练
在开源的代码中,预训练的入口是在run_pretraining.py。 - 微调
而微调的入口针对不同的任务分别在run_classifier.py和run_squad.py。
其中run_classifier.py用于文本分类任务的训练。
而run_squad.py用于阅读理解任务的训练。
2 下载中文预训练模型
对于中文而言,google公布了一个参数较小的BERT预训练模型。
- 模型下载地址
https://github.com/google-research/bert/blob/master/multilingual.md
- 进入下载页面,选择:BERT-Base, Chinese进行下载。
-
下载完成后,解压至与run_classifier.py同一级目录。
-
模型文件说明
bert_model.ckpt:存储得模型变量
vocab.txt:中文文本采用的字典,
bert_config.json:是bert在训练时,可选调整的一些配置参数。
3 制作中文训练数据集
对中文文本数据进行分类模型的训练,需要编写程序对训练数据进行处理,使其按照要求传入bert模型进行训练,而BERT代码中processor类就是负责对模型的输入数据进行处理。
们以分类任务为例,介绍如何修改processor类来运行自己数据集上的fine-tune。在run_classsifier.py文件中我们可以看到,google对于一些公开数据集已经写了一些processor,如XnliProcessor,MnliProcessor,MrpcProcessor和ColaProcessor。这给我们提供了一个很好的示例,指导我们如何针对自己的数据集来写processor。
class kedataProcessor(DataProcessor):
"""Processor for the XNLI data set."""
def get_train_examples(self, data_dir):
return self._create_examples(
self._read_tsv(os.path.join(data_dir, "train.tsv")), "train")
def get_dev_examples(self, data_dir):
return self._create_examples(
self._read_tsv(os.path.join(data_dir, "dev.tsv")), "dev")
def get_test_examples(self, data_dir):
return self._create_examples(
self._read_tsv(os.path.join(data_dir, "test.tsv")), "test")
def get_labels(self):
# 这里返回的为具体的你的分类的类别
labelf = open(FLAGS.data_dir+'\label.csv', 'r', newline='', encoding = 'gb2312')
labeldata = csv.reader(labelf, delimiter="\t")
labelList=[]
for line in labeldata:
label=line[1]
labelList.append(label)
return labelList
def _create_examples(self, lines, set_type):
"""Creates examples for the training and dev sets."""
examples = []
for (i, line) in enumerate(lines):
guid = "%s-%s" % (set_type, i)
text_a = tokenization.convert_to_unicode(line[1])
label = tokenization.convert_to_unicode(line[0])
# print('guid:',guid," text:",text_a,' label:',label)
examples.append(
InputExample(guid=guid, text_a=text_a, text_b=None, label=label))
return examples
自定义的processor里需要继承DataProcessor,并重载获取label的get_labels和获取单个输入的get_train_examples,get_dev_examples和get_test_examples函数。其分别会在main函数的FLAGS.d