赞
踩
BERT是一种预训练语言表示的方法,在大量文本语料(维基百科)上训练了一个通用的“语言理解”模型,然后用这个模型去执行想做的NLP任务。BERT比之前的方法表现更出色,因为它是第一个用在预训练NLP上的无监督的、深度双向系统。
无监督意味着BERT只需要用纯文本语料来训练,这点非常重要,因为海量的文本语料可以在各种语言的网络的公开得到。
预训练表示可以是上下文无关的,也可以是上下文相关的,而且,上下文相关的表示可以是单向的或双向的。上下文无关模型例如word2vec或GloVe可以为词表中的每一个词生成一个单独的“词向量”表示,所以“bank”这个词在“bank deposit”(银行)和“river bank”(岸边)的表示是一样的。上下文相关的模型会基于句子中的其他词生成每一个词的表示。
BERT建立在最近的预训练相关表示工作之上——Semi-supervised Sequence Learning, Generative Pre-Training, ELMo,和ULMFit——但是关键是这些模型都是单向的或浅双向的。这以为意味着每个词之和它左边或右边的词相关。例如,在句子“I made an bank deposit”中,“bank”的单向表示只基于“I made a”而没有“deposit”。一些以前的工作也有结合了单独的左上下文和右上下文的,但只是用了一种简单的方式。BERT同时用左边和右边内容表示“bank”——“I made a … deposit”——从一个深度网络非常底层就开始了,所以说,他是deeply bidirectional(深层双向)的。
BERT用了一种简单的方法:我们遮蔽了输入的15%的单词,通过一个深层的双向transformer Encoder来运行整个序列,然后只预测被遮蔽的单词。
Input: the man went to the [MASK1] . he bought a [MASK2] of milk.
Labels: [MASK1] = store; [MASK2] = gallon
为了学习句子间的关系,我们也训练了一个简单的任务,能后用任何一种单语言语料:给出两个句子A和B,B是A的下一句,或者是语料中的两个随机句子?
Sentence A: the man went to the store .
Sentence B: he bought a gallon of milk .
Label: IsNextSentence
Sentence A: the man went to the store .
Sentence B: penguins are flightless .
Label: NotNextSentence
然后我们花了很长时间在大语料(Wikipedia + BookCorpus)上训练了一个大模型(12层到24层的Transformer),这就是BERT。
使用BERT后两个步骤:Pre-training和fine-tuning
Pre-training相当昂贵(四天,4到16台TPU),但每种语言是一次性程序(最近的模型只有英语,但多语言模型近期会发布)。我们正在发布大量的google论文中被训练的pre-trained模型。这样,多数NLP研究者将不用从最底层去训练模型。
Fine-tunin不昂贵。论文中的所有结果从相同的预训练模型开始,可以在一台单独的云TPU上一小时就可以复现,或者在GPU上需要几个小时。例如,SQuAD,任务,在一台单独的TPU上训练30分钟就可以达到F2值91%,这是一个经典的单系统。
BERT另一个重要的方面是,他极容易被用于多种NLP任务。论文中,我们展示了经典的结果 sentence-level (e.g., SST-2), sentence-pair-level (e.g., MultiNLI), word-level (e.g., NER), and span-level (e.g., SQuAD)他们都没修改过任务。
重点:论文中所有的结果是在一台64G RAM的TPU上调优的。所以用一台12-16GRAM的GPU去复现论文中BERT-Large的结果是不可能,因为,能fit到内存里的最大batch size太小了。我们正在努力提交代码能允许在GPU上用更大的batch size。详情看out of memory issues.
代码在TensorFlow1.11.0上测试,用python2 和python3(用python2更多,因为google内部用python2更多)。
用Bert-Base调优的例子用给定的超参数可以运行在至少12G内存的GPU上。
运行这个例子之前,你需要运行脚本下载GLUE数据集,并解压到$GLUE_DIR
,然后,下载BERT-Base并解压到目录$BERT_BASE_DIR
这个例子对BERT-Base在MRPC(包括3600个例子,在多数GPU上运行只要几分钟)
export BERT_BASE_DIR=/path/to/bert/uncased_L-12_H-768_A-12 export GLUE_DIR=/path/to/glue python run_classifier.py \ --task_name=MRPC \ --do_train=true \ --do_eval=true \ --data_dir=$GLUE_DIR/MRPC \ --vocab_file=$BERT_BASE_DIR/vocab.txt \ --bert_config_file=$BERT_BASE_DIR/bert_config.json \ --init_checkpoint=$BERT_BASE_DIR/bert_model.ckpt \ --max_seq_length=128 \ --train_batch_size=32 \ --learning_rate=2e-5 \ --num_train_epochs=3.0 \ --output_dir=/tmp/mrpc_output/
得到如下结果:
***** Eval results *****
eval_accuracy = 0.845588
eval_loss = 0.505248
global_step = 343
loss = 0.505248
意思是Dev准确度为84.55%,像MRPC这样的小数据集有一个高方差在准确度上,即使是从相同的checkpoint上开始训练。如果训练多次(输出到不同的out_dir),你会发现结果再84%到88%之间。
一些其他的预训练模型在run_classifier.py上直接执行,所以按照这些例子去用BERT执行但句子或句子对分类任务特别直接明了。
提示:你可能看到了一条信息 Running train on CPU。这只是说明他挣运行在一些出云TPU以外的机器上,包括GPU。
训练好了分类器,就可以用与预测模式,通过 --do_predice=true命令。输入文件夹需要有一个test.tsv文件。输出被写在了输出文件夹中的test_results.tsv文件中。每行包括一个样例的输出,列为类别概率
export BERT_BASE_DIR=/path/to/bert/uncased_L-12_H-768_A-12export GLUE_DIR=/path/to/glueexport TRAINED_CLASSIFIER=/path/to/fine/tuned/classifier
python run_classifier.py \
--task_name=MRPC \
--do_predict=true \
--data_dir=$GLUE_DIR/MRPC \
--vocab_file=$BERT_BASE_DIR/vocab.txt \
--bert_config_file=$BERT_BASE_DIR/bert_config.json \
--init_checkpoint=$TRAINED_CLASSIFIER \
--max_seq_length=128 \
--output_dir=/tmp/mrpc_output/
代码解压,模型解压,模型文件包括:
bert_config.json
bert_model.ckpt.data-00000-of-00001
bert_model.ckpt.index
bert_model.ckpt.meta
vocab.txt
看了下vocab.txt,一共两万多个,汉字简体+繁体显然没那么多,有一些英文字符,无意义字符串等
class ChiProcessor(DataProcessor): """ Processor for chinese multi-classes data set""" def __init__(self): self.labels = set() def get_train_examples(self, data_dir): """See base class.""" return self._create_examples(self._read_tsv(os.path.join(data_dir, "train.tsv")), "train") def get_test_examples(self, data_dir): """See base class.""" return self._create_examples(self._read_tsv(os.path.join(data_dir, "test.tsv")), "test") def get_dev_examples(self, data_dir): """See base class.""" return self._create_examples(self._read_tsv(os.path.join(data_dir, "dev.tsv")), "dev") def get_labels(self, data_dir): lines = self._read_tsv(os.path.join(data_dir, "train.tsv")) for line in lines: label = tokenization.convert_to_unicode(line[0]) self.labels.add(label) return list(self.labels) def _create_examples(self, lines, set_type): 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]) self.labels.add(label) examples.append(InputExample(guid=guid, text_a=text_a, text_b=None, label=label)) return examples
_read_tsv()函数可直接使用;新建_create_examples函数用来将读取的数据转化为InputExample格式。
我自己的train.tsv,格式如下:
label1 \t text1
label1 \t text2
label2 \t text3
......
查看InputExample类,定义如下:
class InputExample(object): """A single training/test example for simple sequence classification.""" def __init__(self, guid, text_a, text_b=None, label=None): """Constructs a InputExample. Args: guid: Unique id for the example. text_a: string. The untokenized text of the first sequence. For single sequence tasks, only this sequence must be specified. text_b: (Optional) string. The untokenized text of the second sequence. Only must be specified for sequence pair tasks. label: (Optional) string. The label of the example. This should be specified for train and dev examples, but not for test examples. """ self.guid = guid self.text_a = text_a self.text_b = text_b self.label = label
可以看到text_b,和label为可选。
在处理分类问题是,text_a为分类文本,label为类标;在处理文本相似性问题是,text_a为文本1,text_b为文本2,label为是否相似标记;相似问题可抽象为二分类问题。
def get_labels函数,为获取分类类标列表,在分类数少的情况下可以直接给出,如:
def get_labels(self):
"""See base class."""
return ["0", "1"]
分类较多的情况下,可以按照上面的代码读取train文件中的类标,或者直接把类标写入一个文件进行读取。
2. 在主函数中添加processor
def main(_):
tf.logging.set_verbosity(tf.logging.INFO)
processors = {
"cola": ColaProcessor,
"mnli": MnliProcessor,
"mrpc": MrpcProcessor,
"xnli": XnliProcessor,
"chi": ChiProcessor,
}
添加后,可通过–task_name=chi调用ChiProcessor读取训练验证测试文件。
在data文件,下准备train.tsv, dev.tsv, test.tsv文件。运行:
export BERT_BASE_DIR=/path/to/bert/uncased_L-12_H-768_A-12 export DATA_DIR=/path/to/data python run_classifier_mine.py \ --task_name=chi \ --do_train=true \ --do_eval=true \ --do_predict=false \ --data_dir=$DATA_DIR \ --vocab_file=$BERT_BASE_DIR/vocab.txt \ --bert_config_file=$BERT_BASE_DIR/bert_config.json \ --init_checkpoint=$BERT_BASE_DIR/bert_model.ckpt \ --max_seq_length=128 \ --train_batch_size=32 \ --learning_rate=2e-5 \ --num_train_epochs=3.0 \ --output_dir=/path/to/output
–do_train、–do_eval、–do_predict需至少有一个为true。–do_predict=true时,需要准备test.tsv,预测的结果也会一并写入–output_dir目录下。
export BERT_BASE_DIR=/path/to/bert/uncased_L-12_H-768_A-12
export DATA_DIR=/path/to/data
# TRAINED_CLASSIFIER为刚刚训练的输出目录,无需在进一步指定模型模型名称,否则分类结果会不对
export TRAINED_CLASSIFIER=/path/to/fine/tuned/classifier
python run_classifier.py \
--task_name=chi \
--do_predict=true \
--data_dir=$DATA_DIR \
--vocab_file=$BERT_BASE_DIR/vocab.txt \
--bert_config_file=$BERT_BASE_DIR/bert_config.json \
--init_checkpoint=$TRAINED_CLASSIFIER \
--max_seq_length=128 \
--output_dir=/tmp/output/
预测结果会写入–output_dir目录,格式:每行为一条数据的预测记过,每列为每个类别的概率。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。