预训练模型最开始是在图像领域提出的,获得了良好的效果,近几年才被广泛应用到自然语言处理各项任务中。
(1) 2003 年 Bengio 提出神经网络语言模型 NNLM,从此统一了 NLP 的特征形式 ——Embedding;
(2) 2013 年 Mikolov 提出词向量 Word2vec,延续 NNLM 又引入了大规模预训练(Pretrain)的思路;
(3) 2017 年 Vaswani 提出 Transformer 模型,实现用一个模型处理多种 NLP 任务。
(4) 基于 Transformer 架构,2018 年底开始出现一大批预训练语言模型 (3 个预训练代表性模型 BERT [2018]、XLNet [2019] 和 MPNet [2020]),刷新众多 NLP 任务,形成新的里程碑事件。
预训练模型的应用通常分为两步:
第一步:在计算性能满足的情况下用某个较大的数据集训练出一个较好的模型。
第二步:根据不同的任务,改造预训练模型,用新任务的数据集在预训练模型上进行微调。
预训练模型的好处是训练代价较小,配合下游任务可以实现更快的收敛速度,并且能够有效地提高模型性能,尤其是对一些训练数据比较稀缺的任务。换句话说,预训练方法可以认为是让模型基于一个更好的初始状态进行学习,从而能够达到更好的性能。
要讲自然语言的预训练,得先从图像领域的预训练说起。
图像领域的预训练
设计好网络结构以后,对于图像来说一般是 CNN 的多层叠加网络结构,可以先用某个训练集合比如训练集合 A 或者训练集合 B 对这个网络进行预先训练,在 A 任务上或者 B 任务上学会网络参数,然后存起来以备后用。假设我们面临第三个任务 C,网络结构采取相同的网络结构,在比较浅的几层 CNN 结构,网络参数初始化的时候可以加载 A 任务或者 B 任务学习好的参数,其它 CNN 高层参数仍然随机初始化。
之后我们用 C 任务的训练数据来训练网络,此时有两种做法,一种是浅层加载的参数在训练 C 任务过程中不动,这种方法被称为 “Frozen”;
另外一种是底层网络参数尽管被初始化了,在 C 任务训练过程中仍然随着训练的进程不断改变,这种一般叫 “Fine-Tuning”,顾名思义,就是更好地把参数进行调整使得更适应当前的 C 任务。
对于层级的 CNN 结构来说,不同层级的神经元学习到了不同类型的图像特征,由底向上特征形成层级结构。
如果我们手头是个人脸识别任务,训练好网络后,把每层神经元学习到的特征可视化肉眼看一看每层学到了啥特征,你会看到最底层的神经元学到的是线段等特征,图示的第二个隐层学到的是人脸五官的轮廓,第三层学到的是人脸的轮廓,通过三步形成了特征的层级结构,越是底层的特征越是所有不论什么领域的图像都会具备的比如边角线弧线等底层基础特征,越往上抽取出的特征越与手头任务相关。
正因为此,所以预训练好的网络参数,尤其是底层的网络参数抽取出特征跟具体任务越无关,越具备任务的通用性,所以这是为何一般用底层预训练好的参数初始化新任务网络参数的原因。
而高层特征跟任务关联较大,实际可以不用使用,或者采用 Fine-tuning 用新数据集合清洗掉高层无关的特征抽取器。
一般我们用 ImageNet 来做网络的预训练,主要有两点,一方面 ImageNet 是图像领域里有超多事先标注好训练数据的数据集合,分量足是个很大的优势,量越大训练出的参数越靠谱;另外一方面因为 ImageNet 有 1000 类,类别多,算是通用的图像数据,跟领域没太大关系,所以通用性好。
Word Embedding
现有的机器学习方法往往无法直接处理文本数据,因此需要找到合适的方法,将文本数据转换为数值型数据,由此引出了 Word Embedding 的概念,Word Embedding 算法携带了语义信息且维度经过压缩便于运算。
语言模型
为了能够量化地衡量哪个句子更像一句人话,可以设计如上图所示函数,核心函数 P 的思想是根据句子里面前面的一系列前导单词预测后面单词的概率大小。
神经网络语言模型
NNLM 是从语言模型出发 (即计算概率角度),构建神经网络针对目标函数对模型进行最优化,训练的起点是使用神经网络去搭建语言模型实现词的预测任务,并且在优化过程后模型的副产品就是词向量。
Word2Vec
2013 年最火的用语言模型做 Word Embedding 的工具是 Word2Vec,后来又出了 Glove。
Word2Vec 有两种训练方法,一种叫 CBOW,核心思想是从一个句子里面把一个词抠掉,用这个词的上文和下文去预测被抠掉的这个词;
第二种叫做 Skip-gram,和 CBOW 正好反过来,输入某个单词,要求网络预测它的上下文单词。
使用 Word2Vec 或者 Glove,通过做语言模型任务,就可以获得每个单词的 Word Embedding。
Word Embedding 的使用
我们有个 NLP 的下游任务,比如 QA,就是问答问题,所谓问答问题,指的是给定一个问题 X,给定另外一个句子 Y, 要判断句子 Y 是否是问题 X 的正确答案。
句子中每个单词以 Onehot 形式作为输入,然后乘以 Word Embedding 矩阵 Q,就直接取出单词对应的 Word Embedding。
使用 Word Embedding 等价于把 Onehot 层到 embedding 层的网络用预训练好的参数矩阵 Q 初始化。
这跟前面讲的图像领域的低层预训练过程其实是一样的,区别无非 Word Embedding 只能初始化第一层网络参数,再高层的参数就无能为力了。
下游 NLP 任务在使用 Word Embedding 的时候也类似图像有两种做法,一种是 Frozen,就是 Word Embedding 那层网络参数固定不动;另外一种是 Fine-Tuning,就是使用新的训练集合训练,在训练过程中,更新 Word Embedding 这层参数。
Word Embedding 的问题
是多义词问题。多义词是自然语言中经常出现的现象,也是语言灵活性和高效性的一种体现。
多义词对 Word Embedding 来说有什么负面影响?如上图所示,比如多义词 Bank,有两个常用含义,但是 Word Embedding 在对 bank 这个单词进行编码的时候,是区分不开这两个含义的,因为它们尽管上下文环境中出现的单词不同,但是在用语言模型训练的时候,不论什么上下文的句子经过 word2vec,都是预测相同的单词 bank,而同一个单词占的是同一行的参数空间,这导致两种不同的上下文信息都会编码到相同的 word embedding 空间里去。所以 word embedding 无法区分多义词的不同语义,这就是它的一个比较严重的问题。
从 Word Embedding 到 ELMO
ELMO 是 “Embedding from Language Models” 的简称。在此之前的 Word Embedding 本质上是个静态的方式,所谓静态指的是训练好之后每个单词的表达就固定住了,以后使用的时候,不论新句子上下文单词是什么,这个单词的 Word Embedding 不会跟着上下文场景的变化而改变。
ELMO 的本质思想是:事先用语言模型学好一个单词的 Word Embedding,此时多义词无法区分,实际使用 Word Embedding 的时候,单词已经具备了特定的上下文了,这个时候可以根据上下文单词的语义去调整单词的 Word Embedding 表示,这样经过调整后的 Word Embedding 更能表达在这个上下文中的具体含义,自然也就解决了多义词的问题了。所以 ELMO 本身的思路是根据当前上下文对 Word Embedding 动态调整。
ELMO 采用了典型的两阶段过程,第一个阶段是利用语言模型进行预训练;第二个阶段是在做下游任务时,从预训练网络中提取对应单词的网络各层的 Word Embedding 作为新特征补充到下游任务中。
使用这个网络结构利用大量语料做语言模型任务就能预先训练好这个网络,如果训练好这个网络后,输入一个新句子 ,句子中每个单词都能得到对应的三个 Embedding: 最底层是单词的 Word Embedding,往上走是第一层双向 LSTM 中对应单词位置的 Embedding,这层编码单词的句法信息更多一些;再往上走是第二层 LSTM 中对应单词位置的 Embedding,这层编码单词的语义信息更多一些。也就是说,ELMO 的预训练过程不仅仅学会单词的 Word Embedding,还学会了一个双层双向的 LSTM 网络结构,而这两者后面都有用。
上图展示了下游任务的使用过程,比如我们的下游任务仍然是 QA 问题,此时对于问句 X,我们可以先将句子 X 作为预训练好的 ELMO 网络的输入,这样句子 X 中每个单词在 ELMO 网络中都能获得对应的三个 Embedding,之后给予这三个 Embedding 中的每一个 Embedding 一个权重 a,这个权重可以学习得来,根据各自权重累加求和,将三个 Embedding 整合成一个。然后将整合后的这个 Embedding 作为 X 句在自己任务的那个网络结构中对应单词的输入,以此作为补充的新特征给下游任务使用。对于上图所示下游任务 QA 中的回答句子 Y 来说也是如此处理。因为 ELMO 给下游提供的是每个单词的特征形式,所以这一类预训练的方法被称为 “Feature-based Pre-Training”。
从 Word Embedding 到 GPT
GPT 是 “Generative Pre-Training” 的简称,从名字看其含义是指的生成式的预训练。GPT 也采用两阶段过程,第一个阶段是利用语言模型进行预训练,第二阶段通过 Fine-tuning 的模式解决下游任务。
Transformer
Transformer 是个叠加的 “自注意力机制(Self Attention)” 构成的深度网络,是目前 NLP 里最强的特征提取器。
Transformer 是一种基于 encoder-decoder 结构的模型。在机器翻译任务上的表现超过了 RNN,CNN,只用 encoder-decoder 和 attention 机制就能达到很好的效果,最大的优点是可以高效地并行化。
自注意力机制模型
人类视觉通过快速扫描全局图像,获得需要重点关注的目标区域,也就是一般所说的注意力焦点,而后对这一区域投入更多注意力资源,以获取更多所需要关注目标的细节信息,而抑制其他无用信息。
这是人类利用有限的注意力资源从大量信息中快速筛选出高价值信息的手段.
深度学习中的注意力机制从本质上讲和人类的选择性视觉注意力机制类似,核心目标也是从众多信息中选择出对当前任务目标更关键的信息。
Attention 在同一个英语句子内单词间产生的联系。
Self Attention 可以捕获同一个句子中单词之间的一些句法特征(比如图展示的有一定距离的短语结构)或者语义特征(比如图展示的 its 的指代对象 Law)。
很明显,引入 Self Attention 后会更容易捕获句子中长距离的相互依赖的特征,因为如果是 RNN 或者 LSTM,需要依次序序列计算,对于远距离的相互依赖的特征,要经过若干时间步步骤的信息累积才能将两者联系起来,而距离越远,有效捕获的可能性越小。
SelfAttention 在计算过程中会直接将句子中任意两个单词的联系通过一个计算步骤直接联系起来,所以远距离依赖特征之间的距离被极大缩短,有利于有效地利用这些特征。除此外,SelfAttention 对于增加计算的并行性也有直接帮助作用。这是为何 Self Attention 逐渐被广泛使用的主要原因。
GPT 如何使用
把任务的网络结构改造成和 GPT 的网络结构是一样的。然后,在做下游任务的时候,利用第一步预训练好的参数初始化 GPT 的网络结构,对网络参数进行 Fine-tuning,使得这个网络更适合解决手头的问题。
从 GPT 和 ELMO 及 word2Vec 到 Bert
Bert 采用和 GPT 完全相同的两阶段模型,首先是语言模型预训练;其次是使用 Fine-Tuning 模式解决下游任务。和 GPT 的最主要不同在于在预训练阶段采用了类似 ELMO 的双向语言模型,当然另外一点是语言模型的数据规模要比 GPT 大。
BERT 本质上是一个自编码(Auto Encoder)语言模型,为了能见多识广,BERT 使用 3 亿多词语训练,采用 12 层双向 Transformer 架构。注意,BERT 只使用了 Transformer 的编码器部分,可以理解为 BERT 旨在学习庞大文本的内部语义信息。
具体训练目标之一,是被称为掩码语言模型的 MLM。即输入一句话,给其中 15% 的字打上 “mask” 标记,经过 Embedding 输入和 12 层 Transformer 深度理解,来预测 “mask” 标记的地方原本是哪个字。
input: 欲把西[mask]比西子,淡[mask]浓抹总相宜 |
例如我们输入 “欲把西 [mask] 比西子,淡 [mask] 浓抹总相宜” 给 BERT,它需要根据没有被 “mask” 的上下文,预测出掩盖的地方是 “湖” 和 “妆”。
MLM 任务的灵感来自于人类做完形填空。挖去文章中的某些片段,需要通过上下文理解来猜测这些被掩盖位置原先的内容。
训练目标之二,是预测输入的两句话之间是否为上下文(NSP)的二分类问题。继续输入 “欲把西 [湖] 比西子,淡 [妆] 浓抹总相宜”,BERT 将预测这两句话的组合是否合理(这个例子是 “yes”)。(随后的研究者对预训练模型探索中证明,NSP 任务过于简单,对语言模型的训练作用并不是很大)
通过这两个任务和大规模语料训练,BERT 语言模型可以很好学习到文本之间的蕴含的关系。
NLP 的四大任务
绝大部分 NLP 问题可以归入上图所示的四类任务中:
一类是序列标注,这是最典型的 NLP 任务,比如中文分词,词性标注,命名实体识别,语义角色标注等都可以归入这一类问题,它的特点是句子中每个单词要求模型根据上下文都要给出一个分类类别。
第二类是分类任务,比如我们常见的文本分类,情感计算等都可以归入这一类。它的特点是不管文章有多长,总体给出一个分类类别即可。
第三类任务是句子关系判断,比如 Entailment,QA,语义改写,自然语言推理等任务都是这个模式,它的特点是给定两个句子,模型判断出两个句子是否具备某种语义关系;
第四类是生成式任务,比如机器翻译,文本摘要,写诗造句,看图说话等都属于这一类。它的特点是输入文本内容后,需要自主生成另外一段文字。
根据任务选择不同的预训练数据初始化 encoder 和 decoder 即可。这是相当直观的一种改造方法。当然,也可以更简单一点,比如直接在单个 Transformer 结构上加装隐层产生输出也是可以的。不论如何,从这里可以看出,NLP 四大类任务都可以比较方便地改造成 Bert 能够接受的方式。这其实是 Bert 的非常大的优点,这意味着它几乎可以做任何 NLP 的下游任务,具备普适性,这是很强的。
BERT 的应用案例
下载 bert 预训练模型
Google - BERT 源码 https://github.com/google-research/bert 下载预训练模型。
我这里选择中文的 BERT,下载解压后的目录如下:
安装 bert-as-service
顾名思义,将 BERT 模型直接封装成一个服务,堪称上手最快的 BERT 工具。作者是肖涵博士。
使用 pip 安装:
pip install bert-serving-server # server |
开启 BERT service
bert-serving-start -model_dir E:\nlp\chinese_L-12_H-768_A-12 |
使用客户端获取句子编码
案例一 查找最相近的句子
根据 bert 获取句子向量,并计算出句子之间的余弦相似度,找出最相似的句子。
# 导入bert客户端 |
案例二 简单模糊搜索
将问题编码为向量:
from bert_serving.client import BertClient |
最后,我们准备接收新查询并针对现有问题执行简单的 “模糊” 搜索。为此,每次出现新查询时,我们都将其编码为向量,并使用来计算其点积 doc_vecs。将结果递减排序;并返回前 k 个类似的问题,如下所示:
while True: |
现在运行代码并键入查询,查看此搜索引擎如何处理模糊匹配:
案例三 法条推荐
介绍
根据刑事法律文书中的案情描述和事实部分,预测本案涉及的相关法条;
数据说明
所使用的数据集是来自 “中国裁判文书网” 公开的刑事法律文书,其中每份数据由法律文书中的案情描述和事实部分组成,同时也包括每个案件所涉及的法条、被告人被判的罪名和刑期长短等要素。
数据集共包括 268 万刑法法律文书,共涉及 202 条罪名,183 条法条,刑期长短包括 0-25 年、无期、死刑。
数据利用 json 格式储存,每一行为一条数据,每条数据均为一个字典。
fact: 事实描述
meta: 标注信息,标注信息中包括:
criminals: 被告 (数据中均只含一个被告)
punish_of_money: 罚款 (单位:元)
accusation: 罪名
relevant_articles: 相关法条
term_of_imprisonment: 刑期
刑期格式 (单位:月)
death_penalty: 是否死刑
life_imprisonment: 是否无期
imprisonment: 有期徒刑刑期
这里是简单的一条数据展示:
{ |
实现流程
进入 OpenCLaP 下载刑事文书 BERT,并运行 bert-as-service 服务。
创建一个连接到 BertServer 的 BertClient
from bert_serving.client import ConcurrentBertClient |
获取编码向量和标签
def get_encodes(x): |
构建 TensorFlow DNN 模型的分类器
estimator = DNNClassifier( |
训练和评估
# 输入函数 |
运行 tensorboard 可视化训练过程
tensorboard --logdir=law-model |
案例四 互联网新闻情感分析
介绍
对新闻情绪进行分类,0 代表正面情绪、1 代表中性情绪、2 代表负面情绪。
数据说明
Field | Type | Description |
---|---|---|
id | String | 新闻 ID News ID |
text | String | 新闻正文内容 Content of news text |
label | String | 新闻情感标签 Emotional label in news |
实现流程
加载数据集
def load_dataset(filepath): |
网络类,全连接层
class Net(nn.Module): |
计算每个 batch 的准确率
def batch_accuracy(pre, label): |
训练
for epoch in range(EPOCHS): |
测试
net.load_state_dict(torch.load('net.pt')) |