Syntactic Structure
句法结构其实是语言学中非常重要的一个分支。同样,在NLP领域,句法结构也是很重要的。如果能在模型中考虑到句法特征,那么对于例如说文本生成任务,模型的效果肯定会有所提升。现在,主流的句法结构分析方法有两种:Constituency Parsing(成分句法分析)与Dependency Parsing(依存文法分析)。
Dependency Parsing
依存文法分析考虑的是句子中单词与单词之间的依存关系,而这种依存关系其实就是我们学习的语法,例如主谓、动宾、形容词修饰名词等等。根据这样的关系,我们可以将原来的句子转化成一棵树。这棵树中的每一个节点代表一个单词,而每一条边就代表了一种关系,以下图为例:
这个树的根节点我们自行选定的,因此,当我们的选定的根节点不同时,得到的树结构肯定也大不相同。而依存句法分析的任务,或者说我们构建的Parser模型所要实现的目标就是在所有可能的树结构中选择出最合适的那一棵。通常,我们会设定一个虚假的根节点ROOT,这样我们就可以保证句子中句子中每一个单词都依赖于一个结点。
另外一种表示方式就是直接在句子上用箭头标注出关系,而不把它变成一棵树,例如:
Projectivity
这里补充一个小知识点,就是这种依存结构的投影性。如果在上图的表示方法中,箭头与箭头之间没有交叉,那么我们就说这个结构时projective的。如果一棵依存句法树想被转化成CFG树,那么它就必须是projective的。但是在依存文法分析中,我们通常是允许non-projective结构存在的。
下面我们来看依存句法分析的方法,我们重点介绍两种,一个是Transition-based Parsing,另一种就是Neural Dependency Parsing。
Transition-based Parsing
Transition-based parsing在过去机器学习还没有发展起来的时候是基于贪心的,效率比较低;在加入机器学习之后,模型的效率得到了很大提升(MaltParser)。不过两种parser的主要做法都是一样的,下面来讲解一下。
Transition-based parser有四个需要知道的概念:
- a stack :这里面存储的是正在被进行处理的单词
- a buffer :这里面存储的是待处理的单词
- a set of dependency arcs A:这里面存储的就是我们所有的依存关系,可以用一条边来表示。是中最顶层的词,是中次顶层的词
- a set of actions:总共有三种操作:shift入栈、left左箭头、right右箭头
起初的时候,我们的栈只有Fake ROOT,也可以叫,而buffer 中是我们所有的单词。我们最终的目标是:中只剩ROOT,为空,A中存储的就是我们最终的结果。
我们直接看一个例子来理解它是怎么操作的,假设原句是“I ate fish”
那么现在关键的问题就是我们怎么决定每一步到底应该是Left Arc还是Right Arc。其实仔细想想,这就是个分类问题,类别的总数是,表示关系的数量,+1是因为要选择left还是right。因此我们只需要训练一个分类器来做这件事情就可以了。这样一来,我们就把时间复杂度降到了线性
Neural Dependency Parsing
上面所谈到的Transition-based Parsing对人工feature engineering要求非常高,因为即便我们使用机器学习分类器,我们也需要大量的特征,而这种特征只能提前人工进行设定,而且这种特征通常都是高维的稀疏矩阵。回想一下我们的CRF是不是也有过这样的问题?那么比较好的一种解决方案就是使用深度学习的方法进行特征提取。在2014年,Manning教授和DanQi女神提出了Neural Dependency Parsing,取得了非常好的效果。
这里神经网络的输入由三部分组成:
- word embedding,作者使用了词向量
- POS tags的dense representation
- dependency labels的dense representation
这三部分向量concat起来作为模型的输入。最初提出用深度神经网络的方法时,只使用了一层全连接加一个softmax输出,但已经取得了非常好的效果。
Evaluation
衡量Dependency Parser效果的metrics有两种,一种叫UAS,即只要箭头方向正确即可;另一种叫做LAS,即箭头方向和对应的label必须同时正确。
Constituency Parsing
另外一种句法分析方式就是Constituency Parsing,成分句法分析。上面讲到的依存句法分析更多的是关注句子中单词之间的依存关系,而成分句法分析则是将句子拆解为更小的部分(短语)进行理解。例如,一个句子是由一个名词短语和一个动词短语组成的,这个动词短语又是由一个名词短语和一个动词短语构成的……通过这样层次化的拆解,我们就能得到一棵句子的成分树,如下图所示:
我们希望设计出一种算法,能够解析出正确的语法结构,并且学习到句子的向量化表示。
Recursive Neural Network
考虑如何去表示一个短语的语义,我们也可以像word2vec一样把一个短语映射到向量空间。注意到成分语法结构是一个递归的树结构,因此我们可以利用单词之间的结合规则 (就是子节点结合成为一个父节点) 将几个词向量结合成为一个短语向量,这个短语向量也能够学习到它包含单词的语义。
那么怎么来实现这件事呢?我们既要考虑到句子的语法结构,又要学习到句子的向量化表示。考虑Recurrent Neural Network,这个模型可以用来学习句子向量,但是它无法学习层次化的结构(hierarchical structure),而且它的偏倚性会导致模型过于重视最后一个隐状态向量。于是,我们使用的是Recursive Neural Network。Recursive Neural Network其实是Recurrent Neural Network的超集,Recurre Neural Network只不过是RNN的链状形式。
Naive RNN
首先来看最简单的RNN形式,我们输入两个子节点的向量表示,输出一个合并后的向量化表示和一个得分,这个得分就代表了这个两个节点合并的合理程度。
那么不难想到最简单的RNN结构就是先将两个节点的向量concatnate起来,再使用一个线性层得到合并后的向量化表示,再用合并后的向量表示乘一个参数向量得到我们的分数。这里的权重矩阵是全局共享的。
然后,基于这个模型,我们就可以从左到右遍历句子,每次将得分最高 (贪心思想) 的两个单词或短语结合在一起,重复这个过程,最终就得到了整个句子的语义表达
但是我们知道这个简单的模型效果肯定不会太好,因为使用单一的全局共享的矩阵,你是没有办法学习到两个单词之间的联系的,对比Attention。而且不同的语法结构都使用一个矩阵,这想想也不合适。
Syntactically-Untied RNN
一个进化的版本就是SU-RNN,既然只用一个不好,那我们就多用几个。SU-RNN就是针对不同的句法使用不同的矩阵
事实证明这个进化版模型效果好了一些。但存在的一个问题就是模型的效率,作者采用了PCFG解决
下面这张效果图稍作解释,SU-RNN会学习到一棵树中更为重要的子树,举例来说,给定一个短语“the cat and”,那么模型就会学习到“the cat”这个NP是更加重要的,在图中呈现为红色比较深。
Matrix-Vector RNN
还有一个没有解决的问题就是如何解决修饰关系,例如副词修饰动词,形容词修饰名词,在这种场景中,被修饰的那个词是重要的,修饰词是没有意义的,它更像是一种运算符或者函数,作用是增加或削弱它所修饰单词的意义。因此,Manning和他的团队又提出了第三版RNN,也就是Matrix-Vector RNN。具体来说,我们不再只使用一个向量来表达一个单词,而是使用一个向量加一个矩阵。
在训练的过程中,我们既要计算结合后的Vector Meaning,也要计算结合后的Matrix Meaning。具体来说假设两个词的vector和matrix分别为、,那么计算结合后的vector meaning和matrix meaning方式如下:
其实我们可以感觉出来,这里的matrix meaning就是来帮我们学习修饰关系的,有点Attention那感觉。下面这幅图展示了MV-RNN的效果,还是很不错的。
下面又提到了更好的数据集能极大提升模型的效果,Stanford自己制作的Treebank数据集应用在各种模型上都得到了一些提升。
Recursive Neural Tensor Network
终极版本。之前我们只是用一个矩阵,那么可不可以用一个更高维的呢?作者在这里提出了用多个矩阵组成的三维cube来得到多个score,其实也有点像Multi-Head Attention的思想。