CMU NLP课程总结—— Structured Prediction Basics

本文是对CMU NLP系列课程第十节课的总结。这门课开始到接下来的三节课都是说的同一个大主题——Structured prediction。

What is Structured Prediction?

相信很多人对文本分类问题都比较熟悉,本人也是从这个任务开始NLP道路的。它的目标定义很简单,将一个文本样本输入到一个模型中,然后让模型输出一个结果标签,这个标签可以是一个二分类的标签,也可以是一个多分类的标签。而除了这种范式的问题外,NLP领域还有一大问题范式:序列类任务。这种任务最大的特点就是输出的标签是一个序列。而根据具体的任务形式,可以将序列类任务分成两种子类:

  • 序列标注任务,对于一段文本,以词或者字符等基本语素为基本单位,模型输出的标签序列与原始文本的语素序列是一一对应的。具体的任务例子有词性标注,实体识别等。
  • 序列生成任务,即大家熟知的NLG任务。与序列标注任务不同,它不要求输出的标签序列与原始文本序列一一对应,长度也不需要相同。常见的序列生成任务有机器翻译,文本摘要,智能对话回答生成等。

当然也可以将序列标注任务视作特殊的序列生成任务来做,只不过要加上长度的限定条件。

这种序列类任务的共同特点是每个文本样本都会有指数级或者无限量级的标签序列候选组合。对于词性标注来说,假设一共有n种词性,那么对一个长度为L的文本,所有可能出现的标签序列组合有 n^L 种。对于序列生成任务如机器翻译来说,抛开一些模型的限定条件,它可能生成的标签序列即目标语种词序列是无限的。

基于上述特点,要解上述问题,需要设计一些方法,将问题结构化,使得模型能够较为高效得去学习,去训练。常用的结构化预测模型有:基于RNN的decoder、卷积网络作为decoder、引入注意力机制的decoder、条件随机场等等。

而常用的训练算法有:

  • Structured perceptron,structured large margin
  • sampling corruptions of data
  • 使用动态规划算法
  • 使用强化学习或者minimum risk training(该方法在我的文本摘要总结一文中有描述)

下面以一个简单的实例来描述为何要使用专用的结构化预测模型去做序列类问题。

词性标注实例

假设我们目前仅仅使用一个双向LSTM编码一个文本序列,然后每个timestep直接加一个linear的分类器做词性标注的训练,训练的目标损失是交叉熵,如图所示:

这是一个最简单的序列标注模型,通过BILSTM建模了文本之间的序列信息,模型效果其实也还不错。但是它有一个缺点:该模型并不能建模标签之间的结构信息。

事实上,无论是词性标注还是实体识别,标签之间的结构信息都是非常重要的。对于自然语言来说,语法、句法等是最能体现人类语言特性的要素。还是以词性标注为例,如图:

最上面三列的词性序列其实都是符合语法的,但是最终得到的句子的意思大不相同。第一个序列中,将flies标为了动词,则它表示的是“时光飞逝不可逆转”;而第三个序列中,将time标位了一个动词,则它表示“用弓箭一样的方法来衡量苍蝇的时间”(虽然符合语法,但是不太像人话)。遇到这种情况,如果不用模型处理的话,一般会统计每个位置预测标签,取频率最大的作为最终标注结果,所以就得到了最后一个标签序列。

那么如何去建模标签之间的关系呢?最直观的是可以借鉴seq2seq的思想,将每个时刻预测得到的标签一并输入到下一个时刻的神经元中参与下个时刻的标签计算中,如图:

需要注意的是上述描述的过程都是在inference阶段考虑如何将标签之间的关系嵌入到模型输出计算中,并没有牵扯到训练。而常用的seq2seq训练方法当然是teacher-forcing方法,基本思想就是训练时,并不是将当前时刻输出的标签输入到下个时刻的神经元中,而是将真实的标签来代替(具体的内容可以在我对文本摘要的总结一文中找到)。

Teahcer-forcing and normalize method

teacher-forcing训练方法会带来一个问题:exposure bias。因为我们只能在训练阶段利用真实的标签,而inference阶段我们没有真实标签可用,只能用每个时刻预测的标签。因此会导致模型无法处理误差传递问题,即当一个时刻的标签预测错误时,这个错误会逐渐向后传递,而模型即使发现了错误也无法阻止错误传递。

另外,我们在inference阶段通过计算候选序列的分数来帮助我们选择最终序列,这个分数计算根据不同的归一化模式一般有两种形式:

1、locally normalized models:即一个序列的分数是由每个时刻上对标签计算输出的概率的乘积得到: P(Y|X)=\prod_{j=1}^{|Y|}\frac{e^{S(y_j|X,y_1,...,y_{j-1})}}{\sum_{\tilde{y}\in V}e^{S(\tilde{y}|X,y_1,...,y_{j-1})}}

2、globally normalized models:顾名思义,这是一种全局归一化的方法,每个时刻并不需要做归一化计算(即不需要softmax,只需要输出的logits),使用logits计算得到一个标签序列的分值后,再对所有候选标签序列做全局的分值归一化: P(Y|X)=\frac{e^{\sum_{j=1}^{|Y|}S(y_j|X,y_1,...,y_{j-1})}}{\sum_{\tilde{Y}\in V^*}e^{\sum_{j=1}^{|\tilde{Y}|}S(\tilde{y_j}|X,\tilde{y_1},...,\tilde{y_{j-1}})}}

locally normalized方法虽然计算效率比较高,不需要把所有的候选都一一算分数,但是它有一个很突出的问题:label bias。简单点说,就是会倾向于选择拥有较少决策的状态序列。如图所示:

当前有如下的序列路径供计算选择,假设r->o的路径是一个很差的选择,但是由于从r到o只有这一条路径的选择,因此P(o|r)=1,使用locally normalized计算的时候,就会错误的把这种高概率乘进去。

当然global normalized虽然不会有label bias问题,但是它也有自己的缺陷:由于它的归一化项需要计算候选序列集合中的所有序列分数,因此计算效率很差,成本较高,因此需要设计一些方法来缓解这个问题,目前一般有两种候选方法:

  1. 使用全部候选序列集合的子集。
  2. 设计专用的结构化预测模型,可以高效得考虑到所有的候选序列。

下面我们主要是聚焦于第二个方法,来介绍一些结构化预测模型和训练方法。

Structured Perceptron

首先来介绍一个算法叫Structured Perceptron(以下简称SP算法)。从名字就可以看出来,它基于感知机模型,同时融入了结构化预测的思想。

SP算法的核心思想为:找到一个最好的序列标签预测结果,如果它的分数比真实标签序列的分数还要高,此时就需要给参数更新梯度,惩罚错误。具体的模型算法如图所示:

这里需要注意一点,图上的S()即代表一个标签序列的分数,我们如何去计算分数最高的那个标签序列呢?当然不可能把所有候选序列都计算分数然后取最大值,这样做计算效率太低,成本太高。因此这里需要使用一些trick来得到最高分数的标签序列。常用的就是基于搜索的解码方法,如使用动态规划(如维特比)等算法通过搜索一次性得到分数最高的路径。后续介绍的所有global normalization方法中都是使用该模式。不过课程给出的github代码中的实现并非使用search-based的decoding方式,这个在看代码的时候需要搞清楚。

有的同学可能会有疑问,这样做的意义在哪里?下面我来简单说一下自己的理解:

每个标签序列的分数表示模型对该标签序列的置信程度,因此给定的真实的标签序列的分数应当是最高的,此时若模型预测出来的序列分数最高值比真实标签序列还高,说明模型当前还未学习到真实的标签信息,因此需要更新梯度,惩罚错误。

传统的SP算法最初是为线性模型设计的,但是也可以将其应用到神经网络中。此时我们可以为神经网络设计一个Structured Perceptron loss(以下简称SP loss):

l_{percept}(X,Y)=max(0,S(\hat{Y}|X;\theta)-S(Y|X;\theta))\\

其实细看这个损失函数与之前的SP算法思想是一样的,对分数最高的候选标签序列才会去计算损失,并更新梯度:

与之前提到的teacher-forcing训练方式不同,这个损失需要在训练阶段,就需要使用一些decoding算法做inference找分数最高的候选标签序列。

Contrasting Perceptron and Global Normalization

一般的全局归一化损失函数形式为: l_{global}(X,Y;\theta)=-log\frac{e^{S(Y|X)}}{\sum_{\tilde{Y}}e^{S(\tilde{Y}|X)}}

我们可以将SP算法和全局归一化损失模型结合,得到如下的损失函数形式:

l_{global-percept}(X,Y)=\sum_{\tilde{Y}}max(0,S(\tilde{Y}|X;\theta)-S(Y|X;\theta))

注意这个公式与上一小节介绍的SP loss不同,上面使用是 \hat{Y} ,这里用的是 \tilde{Y} ,这里表示对所有可能的候选标签算SP loss,然后求和。因此,它同样具备全局归一化概率模型的缺点,即计算效率比较低。

下面总结一下SP训练方式和teacher-forcing训练方式各自的缺点:

  • SP训练模式,由于每次只会对分数最高的候选分数进行损失计算和梯度更新,所以每次都会有部分分数不能被更新学习
  • teacher-forcing会导致exposure biaslabel bias问题

单独使用上述某种方法都会有一定的问题,那么可以做这样的一个折中操作:

使用teacher-forcing做预训练,然后用SP方式做finetune。

另外,Perceptron也有其自身的局限性,如图:

上述两条线为模型做的两种不同的decision hyperplane,对于Perceptron来说,上述两条线的Perceptron loss都是0,无法区分那个预测更好。

但是学习过机器学习的同学肯定知道,这样一个问题很容易就能被SVM解决,它使用了最大化间隔思想(关于SVM的内容我以前也在文章中总结过),因此上图中的短划线效果比较好。

所以,我们可以借鉴SVM中的hinge loss,来为原始是SP loss增加一些margin,使得我们能够通过最大化间隔这样的思想来对上述情况进行处理。

Hinge loss and Cost-sensitive Training

hinge loss和perception loss的函数曲线如图所示:

可以看到,hinge loss比perception多考虑了margin间隔因素,因此我们可以设计一个hinge loss来做结构化预测:

l_{hinge}(x,y;\theta)=max(0,m+S(\hat{y}|x;\theta)-S(y|x;\theta))\\

上述loss与perception loss的区别在于多设计了一个margin,当我们的错误答案和真实答案分数差在margin范围内,则都会有loss。

Cost-augmented Hinge

对于上述的hinge loss来说,每个错误答案的错误程度都是一样的。但是实际情况下,一个错误答案的错误程度会在不同程度上影响序列后续的预测,因此我们需要设计一个cost函数,表示每个错误答案和正确答案之间的差异程度。因此,有如下的损失函数:

l_{ca-hinge}(x,y;\theta)=max(0,cost(\hat{y},y)+S(\hat{y}|x;\theta)-S(y|x;\theta))\\

可以看到,实际上是用cost作为hinge loss的margin了。

常用的序列之间的差异计算方法有:

  • zero-one cost:只要序列不一样,cost就为1
  • hamming cost:对每个位置上不同的元素的cost设为1,然后将所有位置的cost累加。
  • 编辑距离
  • BLEU,ROUGE等

Structured Hinge loss

我们将结构化预测,hinge loss,cost-augmented这些元素都综合起来,就可以得到下列基于最大化间隔思想的结构化hinge loss:

\hat{Y}=argmax_{\tilde{Y}\ne Y}cost(\tilde{Y},Y)+S(\tilde{Y}|X;\theta)\\ l_{ca-hinge}(X,Y;theta)=max(0,cost(\hat{Y},Y)+S(\hat{Y}|X;\theta)-S(Y|X;\theta))

可以看到在找分数最高的序列时就将cost因子考虑到了。

其他一些缓解exposure bias的方法

对于Structured Hinge loss,相比于teacher-forcing训练方式,它有其固有的局限性:

1、不会将所有的候选序列纳入计算,因此训练会非常不稳定,训练过程会有震荡。

2、需要在训练的时候就做decoding以找到分数最大的候选序列,所以训练效率很慢。

因此讲师在课上又讲了一些其他实现较为简单的方法来缓解在teacher-forcing训练时exposure bias的问题,下面简单总结一下这些方法。

Sample Mistakes inTraining

DAgger。这个方法与我之前在seq2seq文章中讲的scheduled sampling方法有些类似,但是不太一样。它的思想为在训练阶段根据每个时刻的score分布使用采样的方式产生用于输入到下一个时刻的计算单元中。另外本时刻的标签输出还是使用正常的loss流程来产生。因此一个时刻模型会产生两个标签输出,这两个标签输出可能一样也可能不一样,如图所示:

Drop out Inputs

该方法的基本思想是不需要每个时刻都输出标签输入到下一个时刻的计算单元中,而是以一定的概率来决定是否将本时刻输出输入到下一个时刻计算中,如图所示:

这个方法可以保证模型不会过于依赖获取的预测结果,从而在一定程度上缓解错误的传递。

Corrupt Training Data

该方法称为Reward augmented maximum likelihood(RAML),我去看了下原论文,该方法的提出主要是基于强化学习中的reward思想,但是解决了强化学习中的一个问题,即需要对模型输出概率做样本采样,采样的好坏极大决定了模型的效果,且采样极大依赖于模型的输出结果。而RAML则是不需要根据模型的输出概率采样,而是根据一个平稳分布来采样样本,然后仍然使用最大化似然估计来训练,可以达到近似于强化学习的效果。这个平稳分布如下:

其中, \tau 表示一个温度系数,这个温度系数的用法我在flyai中关于采样方法做decoding的章节中有介绍过,核心思想是能够控制最后输出的概率分布的性状(平原型还是山峰型)。Z表示归一化项, r(y,y^*) 表示奖励函数,对于不同的任务,需要设计合适的奖励函数。对于结构化预测问题来说,可以尝试使用上述的几个cost计算方法作为奖励函数,一般是cost越高,奖励越小,采样的概率就越小,反之亦然。

可以看到上述采样方法并不需要根据模型输出的概率分布来采样,因此效率更高。

总结

本节课的内容是我之前接触比较少的,感觉比较新颖,就总结了一下。虽然目前做序列标注任务(ner等)时很少见到使用上述的一些结构化预测方法,但是对于扩展思路很有帮助,其中有些思想感觉可以尝试与目前的一些sota模型结合一下,如将hinge loss融入到ner中,是否可以缓解样本不均衡的问题呢?这个还有待我在实践中去验证。另外本次的内容如有错误之处,还请同学们随时指出。

发布于 2019-12-21 21:35