思源 王淑婷编辑

预训练BERT,官方代码发布前他们是这样用TensorFlow解决的

这个月谷歌提出的 BERT 受到了很多关注,该研究凭借预训练模型刷新了 11 项 NLP 任务的当前最优性能记录。论文作者表示这个月月末会放出代码与预训练模型,但目前还没有发布。因此很多研究者尝试使用中等数据集降低计算力,或使用 OpenAI 的 Transformer 预训练模型作为初始化条件。

本文介绍的两个 BERT 实现项目分别基于 TensorFlow 和 Keras,其中基于 TensorFlow 的项目会使用中等数据集与其它技巧降低计算力,并发现使用 TextCNN 代替 Transformer 主干网络,且保留 BERT 预训练任务也能得到非常好的效果。而基于 Keras 的项目尝试使用预训练的 OpenAI Transformer 作为初始化权重,并以较小的计算力重新训练 BERT 预训练模型,再将该预训练的 BERT 应用到不同任务。

这两个项目都在尝试使用 BERT 核心思想,并以较小的计算成本应用于其它 NLP 任务。当然如果读者希望使用大型 BERT 预训练模型,还需要等谷歌官方发布代码与模型。

BERT 简介

BERT 的全称是基于 Transformer 的双向编码器表征,其中「双向」表示模型在处理某一个词时,它能同时利用前面的词和后面的词两部分信息。这种「双向」的来源在于 BERT 与传统语言模型不同,它不是在给定所有前面词的条件下预测最可能的当前词,而是随机遮掩一些词,并利用所有没被遮掩的词进行预测。下图展示了三种预训练模型,其中 BERT 和 ELMo 都使用双向信息,OpenAI GPT 使用单向信息。

如上所示为不同预训练模型的架构,BERT 可以视为结合了 OpenAI GPT 和 ELMo 优势的新模型。其中 ELMo 使用两条独立训练的 LSTM 获取双向信息,而 OpenAI GPT 使用新型的 Transformer 和经典语言模型只能获取单向信息。BERT 的主要目标即在 OpenAI GPT 的基础上对预训练任务做一些改进,以同时利用 Transformer 深度模型与双向信息的优势。

BERT 的核心过程非常简洁,它会先从数据集抽取两个句子,其中第二句是第一句的下一句概率是 50%,这样就能学习句子之间的关系。其次随机去除两个句子中的一些词,并要求模型预测这些词是什么,这样就能学习句子内部的关系。最后再将经处理的句子传入大型 Transformer 模型,并通过两个损失函数同时学习上面两个目标就能完成训练。

TensorFlow 实现项目简介

BERT 最近在 10 几项 NLP 任务上取得了新进展,这个项目是《BERT:Pre-training of Deep Bidirectional Transformers for Language Understanding》和《Attention is all You Need》这两篇论文的 tensorflow 实现。

这个项目提供了预训练方法与代码,并做了一些调整以加快收敛速度。这一份 TensorFlow 实现在使用中等数据集下计算力并不是太大,所以感兴趣的读者也可以尝试使用。当然,希望使用大型预训练 BERT 模型的读者可以等谷歌发布官方模型。

项目地址:https://github.com/brightmart/bert_language_understanding

预训练和微调实验

项目作者把 Transfprmer 换成 TextCNN,替换了 BERT 的主干网络,结果发现使用大量原始数据用遮蔽语言模型预训练的模型可以显著提高性能,因此他们认为预训练和微调策略是独立于模型和预训练任务的。因此,可以修正主干网络添加更多的预训练任务或者定义一些新的预训练任务,预训练不限于遮蔽语言模型或预测下一句的任务。让人惊讶的是,对于中等规模的数据集(比如说一百万条数据)来说,即使不使用外部数据,只要借助于预训练任务(如带掩码的语言模型),性能也可以大幅提升,而且模型可以更快地收敛。有时在微调阶段,训练仅需几个 epoch。

目的

虽然开放源码(tensor2tensor)和 Transformer 与 BERT 的官方实现实现即将到来,但是有可能很难懂,不易理解。该项目的作者并不打算完全复制原始文件,而是要应用主要思想,以更好的方式解决 NLP 问题。本文的大部分工作是去年由另一个 GitHub 项目修改完成的:文本分类https://github.com/brightmart/text_classification)。

性能

下图是在中等规模数据集(cail2018,45 万)上文本分类任务的测试效果,其中采用 BERT 预训练思想的 TextCNN 要显著优于其它模型:

下图是在小规模数据集(private,10 万)上的测试效果:

TensorFlow 实现项目细节

使用方法

如果想在 Masked 语言模型上预训练 BERT 模型,并在新 NLP 任务上使用它,那么使用方法主要可以分为两步骤。值得注意的是,该项目并没有提供预训练模型,所以需要大量计算力的预训练过程仍然需要自行执行。

1. 通过 BERT 预训练语言模型

 python train_bert_lm.py [DONE]

2. 在新任务微调模型

 python train_bert_fine_tuning.py [Done]

在项目作者的试验中,即使在微调的起点,刚刚从预训练模型恢复参数也能获得比从头训练更低的损失。预训练模型的 F1 值同样要比从头训练的高,且从头训练的 F1 值还需要从零开始增长。

此外为了快速测试新想法与模型,可以将超参数 test_mode 设置为 True,在这种模式下模型只会加载少量的数据进行测试,因此训练非常迅速。

在基本步骤中,还可以通过 Transform 解决文本分类问题:

python train_transform.py [DONE, but a bug exist prevent it from converge, welcome you to fix, email: brightmart@hotmail.com]

预训练和微调过程还有其它一些可选超参数,它们控制了模型的大小与训练过程:

  • d_model:模型维度,默认为 [512]。

  • num_layer:层级数,默认为 [6]。

  • num_header:自注意力机制的 Head 数,默认为 [8]。

  • d_k:Key(K)和 Query(Q)的维度,默认为 [64]。

  • d_v:Value(V)的维度,默认为 [64]。

default hyperparameter is d_model=512,h=8,d_k=d_v=64(big). if you have want to train the model fast, or has a small data set 

or want to train a small model, use d_model=128,h=8,d_k=d_v=16(small), or d_model=64,h=8,d_k=d_v=8(tiny).

实现细节

首先,TensorFlow 的实现环境比较简单:python 3+ tensorflow 1.10。其次,实现时要注意以下问题:

1. 预训练和微调阶段之间有哪些能够共享和无法共享的参数

基本来说,预训练和微调阶段的主干网络使用的所有参数都能共享。

既然可以共享很多参数,那微调阶段只需学习很少的参数,此外这两个阶段的词嵌入也可以共享。

因此,在微调的初始阶段已经学习了大部分参数

2. 如何实现带 Mask 的语言模型

为了让事情变得简单点,可以根据文件生成很多句子。把每个句子截断并填充到相同的长度,然后随机选择一个单词,用 [MASK]、单词本身或一个随机单词替换它。

3. 如何使微调阶段变得更高效并同时不影响在预训练阶段学到的结果和知识?

在微调阶段使用较小的学习率,因此只需在很小的范围内进行调整。

Keras 实现

基于 TensorFlow 的实现同样没有提供预训练语言模型,这样的模型在预训练阶段会需要大量的计算力,这样的计算力需求对于很多研究者与开发者都是接受不了的。但是现在的官方实现与预训练模型仍然没有放出来,因此有开发者利用 OpenAI 预训练的 Transformer 作为初始化参数,并训练新的 BERT 预训练模型,这种方式大大降低了计算力需求。

项目地址:https://github.com/Separius/BERT-keras

在这个 Keras 实现项目中,作者用预训练的 OpenAI Transformer 作为初始化条件,并训练新的 BERT,项目作者表示这样可以不使用 TPU 而实现预训练。以下展示了 Keras 实现的主要语句,包括加载 OpenAI Transformer 预训练模型、加载 BERT 模型和保存新的预训练权重等。

# this is a pseudo code you can read an actual working example in tutorial.ipynb
text_encoder = MyTextEncoder(**my_text_encoder_params) # you create a text encoder (sentence piece and openai's bpe are included)
lm_generator = lm_generator(text_encoder, **lm_generator_params) # this is essentially your data reader (single sentence and double sentence reader with masking and is_next label are included)
task_meta_datas = [lm_task, classification_task, pos_task] # these are your tasks (the lm_generator must generate the labels for these tasks too)
encoder_model = create_transformer(**encoder_params) # or you could simply load_openai()
trained_model = train_model(encoder_model, task_meta_datas, lm_generator, **training_params) # it does both pretraing and finetuning
trained_model.save_weights('my_awesome_model') # save it
model = load_model('my_awesome_model', encoder_model) # load it later and use it!

作者表示这个项目有一些很重要说明,针对不同的任务与需求,可以根据这些说明修改模型结构和预训练过程。

  • 这个库的核心观点是使用 OpenAI 的预训练模型作为训练新模型的初始状态,因此通过 GPU 就能训练 BERT

  • 通过 Keras 加载 OpenAI 模型已经在 TensorFlow 后端和 Theano 后端得到测试。

  • 对于大多数 NLP 模型,能使用这个项目定义的数据生成器和任务元数据,即使在其它框架中也是。

  • 数据集和 Transformer 都会执行一些单元测试,如果你不太了解代码可以阅读这些测试。

  • 还可以使用其它编码器进行训练,例如 LSTM 或 BiQRNN 等。

  • 当官方代码发布后会发生什么?数据读取器仍然会保持稳定,甚至可以导入官方发布的权重到这个库中(作者认为他会完成这一过程,因为实际的 Transformer 还是比较容易实现的)

  • 作者强烈建议阅读项目中的 tutorial.ipynb 文件,它展示了整个项目的使用过程。

重要的代码概念

  • 任务:有两个一般任务,句子级任务(如下一句预测和情感分析)和 token 级任务(如词性标注命名实体识别)。

  • 句子:「句子」表示一段带有标签和所有内容的实例,它为每个任务提供了一个目标(句子级任务的单个标注值,token 级任务的每个 token 标签)和一个掩码;对于 token 级任务,除了忽略 padding 外,还要使用第一个符号向量预测类别(BERT 中的 [CLS] 符号)。

  • TaskWeightScheduler:项目作者希望从语言模型开始训练,然后迁移到到分类任务,我们可以用这个类快速实现。

  • special_tokens:pad, start, end, delimiter, mask 

工程KerasTensorFlow实现BERT
5
相关数据
基于Transformer 的双向编码器表征技术

BERT是谷歌发布的基于双向 Transformer的大规模预训练语言模型,该预训练模型能高效抽取文本信息并应用于各种NLP任务,并刷新了 11 项 NLP 任务的当前最优性能记录。BERT的全称是基于Transformer的双向编码器表征,其中“双向”表示模型在处理某一个词时,它能同时利用前面的词和后面的词两部分信息。

权重技术

线性模型中特征的系数,或深度网络中的边。训练线性模型的目标是确定每个特征的理想权重。如果权重为 0,则相应的特征对模型来说没有任何贡献。

词嵌入技术

词嵌入是自然语言处理(NLP)中语言模型与表征学习技术的统称。概念上而言,它是指把一个维数为所有词的数量的高维空间嵌入到一个维数低得多的连续向量空间中,每个单词或词组被映射为实数域上的向量。

参数技术

在数学和统计学裡,参数(英语:parameter)是使用通用变量来建立函数和变量之间关系(当这种关系很难用方程来阐述时)的一个数量。

收敛技术

在数学,计算机科学和逻辑学中,收敛指的是不同的变换序列在有限的时间内达到一个结论(变换终止),并且得出的结论是独立于达到它的路径(他们是融合的)。 通俗来说,收敛通常是指在训练期间达到的一种状态,即经过一定次数的迭代之后,训练损失和验证损失在每次迭代中的变化都非常小或根本没有变化。也就是说,如果采用当前数据进行额外的训练将无法改进模型,模型即达到收敛状态。在深度学习中,损失值有时会在最终下降之前的多次迭代中保持不变或几乎保持不变,暂时形成收敛的假象。

文本分类技术

该技术可被用于理解、组织和分类结构化或非结构化文本文档。文本挖掘所使用的模型有词袋(BOW)模型、语言模型(ngram)和主题模型。隐马尔可夫模型通常用于词性标注(POS)。其涵盖的主要任务有句法分析、情绪分析和垃圾信息检测。

学习率技术

在使用不同优化器(例如随机梯度下降,Adam)神经网络相关训练中,学习速率作为一个超参数控制了权重更新的幅度,以及训练的速度和精度。学习速率太大容易导致目标(代价)函数波动较大从而难以找到最优,而弱学习速率设置太小,则会导致收敛过慢耗时太长

损失函数技术

在数学优化,统计学,计量经济学,决策理论,机器学习和计算神经科学等领域,损失函数或成本函数是将一或多个变量的一个事件或值映射为可以直观地表示某种与之相关“成本”的实数的函数。

超参数技术

在机器学习中,超参数是在学习过程开始之前设置其值的参数。 相反,其他参数的值是通过训练得出的。 不同的模型训练算法需要不同的超参数,一些简单的算法(如普通最小二乘回归)不需要。 给定这些超参数,训练算法从数据中学习参数。相同种类的机器学习模型可能需要不同的超参数来适应不同的数据模式,并且必须对其进行调整以便模型能够最优地解决机器学习问题。 在实际应用中一般需要对超参数进行优化,以找到一个超参数元组(tuple),由这些超参数元组形成一个最优化模型,该模型可以将在给定的独立数据上预定义的损失函数最小化。

TensorFlow技术

TensorFlow是一个开源软件库,用于各种感知和语言理解任务的机器学习。目前被50个团队用于研究和生产许多Google商业产品,如语音识别、Gmail、Google 相册和搜索,其中许多产品曾使用过其前任软件DistBelief。

词性标注技术

词性标注是指为分词结果中的每个单词标注一个正确的词性的程序,也即确定每个词是名词、动词、形容词或其他词性的过程。

注意力机制技术

我们可以粗略地把神经注意机制类比成一个可以专注于输入内容的某一子集(或特征)的神经网络. 注意力机制最早是由 DeepMind 为图像分类提出的,这让「神经网络在执行预测任务时可以更多关注输入中的相关部分,更少关注不相关的部分」。当解码器生成一个用于构成目标句子的词时,源句子中仅有少部分是相关的;因此,可以应用一个基于内容的注意力机制来根据源句子动态地生成一个(加权的)语境向量(context vector), 然后网络会根据这个语境向量而不是某个固定长度的向量来预测词。

命名实体识别技术

命名实体识别(NER)是信息提取(Information Extraction)的一个子任务,主要涉及如何从文本中提取命名实体并将其分类至事先划定好的类别,如在招聘信息中提取具体招聘公司、岗位和工作地点的信息,并将其分别归纳至公司、岗位和地点的类别下。命名实体识别往往先将整句拆解为词语并对每个词语进行此行标注,根据习得的规则对词语进行判别。这项任务的关键在于对未知实体的识别。基于此,命名实体识别的主要思想在于根据现有实例的特征总结识别和分类规则。这些方法可以被分为有监督(supervised)、半监督(semi-supervised)和无监督(unsupervised)三类。有监督学习包括隐形马科夫模型(HMM)、决策树、最大熵模型(ME)、支持向量机(SVM)和条件随机场(CRF)。这些方法主要是读取注释语料库,记忆实例并进行学习,根据这些例子的特征生成针对某一种实例的识别规则。

查询技术

一般来说,查询是询问的一种形式。它在不同的学科里涵义有所不同。在信息检索领域,查询指的是数据库和信息系统对信息检索的精确要求

语言模型技术

语言模型经常使用在许多自然语言处理方面的应用,如语音识别,机器翻译,词性标注,句法分析和资讯检索。由于字词与句子都是任意组合的长度,因此在训练过的语言模型中会出现未曾出现的字串(资料稀疏的问题),也使得在语料库中估算字串的机率变得很困难,这也是要使用近似的平滑n元语法(N-gram)模型之原因。

推荐文章
暂无评论
暂无评论~