分析用户的真实意图人类语言不同于计算机语言,人类语言没有结构,即使有一些语法规则,这些规则也往往充满歧义.在有大量用户输入语料的情况下,我们需要根据用户的输入来分析用户的意图。例如,如果我们想看看用户是否有购买产品的想法,我们必须使用分析算法将用户的输入转换为结构化数据,并从这种结构中提取有用的信息。NLP解析算法的一般步骤是分词、词性标记和句法分析。分词和词性标记可以通过条件随机场和隐马尔可夫模型等模型来解决。近些年也有人用神经网络来做,??比较成熟,暂时不讨论。本文主要讨论最重要的一步,句法分析。两种句法分析工具目前的句法分析工具主要分为两大类:依存语法和成分关系。成分分析将文本分成子短语。JohnseesBill这句话分为上图所示的结构。首先,Bill这个词是一个名词短语,而see是一个动词。根据预设的语法规则,动词+名词短语可以组成一个动词短语,然后名词+动词短语可以组成一个完整的句子。最流行的成分分析是上下文无关文法(ContextFreeGrammar)及其变体,例如概率上下文无关文法(ProbabilisticContextFreeGrammar)。而依存文法则将词按照修饰关系连接起来形成一棵树,树中的每个节点代表一个词。子节点的词依赖于父节点,每条边都标准化了依赖的类型。John是动词sees的主语,Bill是动词sees的宾语。成分分析的缺点是搜索空间太大,建树的时间往往与可供选择的节点数有关。组件分析需要在计算过程中不断建立新的节点,而依赖分析不需要建立新的节点。自然语言中存在歧义,例如上下文无关文法中存在规则“C<-AB”和“D<-AB”,那么在计算AB应该合成什么节点时有两种选择,多种歧义组合,使得成分分析的搜索空间爆炸式增长,必须设计一些算法进行剪枝等操作。但是依赖分析不创建节点,所以不存在这样的问题。但是组件分析保存的信息比依赖分析多,所以可以通过一定的规则将组件树直接转化为依赖树。句法分析算法依赖于语法树的构建,我们可以将其视为一系列状态转换。当前状态由三部分组成,s是当前栈,b是剩余未解析词的数组,以及一组依赖关系A。初始状态为s=[ROOT],b=[w_1,...,w_n],A=?。终止状态是b为空。S只有一个节点ROOT,解析出的树就是集合A。定义s_i为栈顶的第i个节点。b_i是第i个未解析的词。可以定义如下状态转换:LEFT-ARC(l):添加s_1—>s_2的一个标记为l的依赖,并从栈中移除s_2。RIGHT-ARC(l):添加s_2的标记为l的依赖—>s_1,并从栈中移除s_1。SHIFT:将b_1移出未解析词数组并将其放入堆栈。假设我们需要解析句子“HewantsaMac.”。解析过程如下:最后得到一棵树。在每个状态中,我们都有许多可选的转换。关于如何选择正确的transition,一般有两种策略:greedyorsearch。目前的结果表明,虽然贪婪比搜索结果稍差,但解析速度要快很多。所以在日常使用中基本都是用贪心算法。传统分析算法的难点传统分析算法需要根据当前状态和预设规则提取特征。比如当前栈顶的前两个词,当前前几个未解析的词等。但是这些特征存在以下问题:稀疏。这些特征,尤其是词汇特征,非常稀疏。依赖语法的分析依赖于单词之间的关系。有可能两个词之间的距离很远,所以只提取栈顶的前两个词作为特征是不能满足需要的。必须使用高维特征。一旦维度很高,势必会使特征变得非常稀疏。不完整。人的经验是有偏差的,专家总结出来的特征提取规则总是不完整的。解析算法的大部分时间都花在了提取特征上。据统计,90%以上的时间花在了特征提取上。这时候神经网络就需要出来给我们一个估计,哪个是最好的状态转移。递归神经网络(RecursiveNeuralNetwork)词嵌入是将词表示为低维稠密实数向量。自从词向量技术被提出以来,已经出现了多种获取句法和语义向量表示的方法,该技术在自然语言处理领域发挥着重要作用。如何用密集向量表示短语是使用词向量的一个难题。在成分分析中,业界使用递归神经网络(RNN)来解决这个问题。RNN是用于对句子建模的通用模型。一个句子的句法树中的左右子节点通过一层线性神经网络组合在一起,根节点处这一层神经网络的参数代表了整个句子。RNN可以给语法树中的所有叶子节点一个固定长度的向量表示,然后递归地为中间节点创建向量表示。假设我们的语法二叉树是p_2—>ap_1,p_1—>bc,即p_2有子节点a,p_1,p_1有子节点b,c。然后节点表示为其中W是RNN的参数矩阵。为了计算一个父节点是否合理,我们可以用一个线性层来打分,score(p_i)=vp_i。v是要训练的参数向量。在构建树的过程中,我们使用这种方法来评估各种可能的构造并选择最佳的一个。基于神经网络的依赖分析,但是RNN只能处理二进制组合,不适合做依赖分析。因为依赖分析的一个节点可能有很多子节点。因此有学者提出用递归卷积神经网络(RCNN)来解决这个问题。通过使用RCNN,我们能够捕获单词和短语的句法和组合语义表示。RCNN的架构能够处理任何k-forked解析树。RCNN是一种通用架构,不仅可以用于依赖分析,还可以用于对文章的语义进行建模,将任意长度的文本转换为固定长度的向量。1.RCNN单元对于依赖树上的每个节点,我们用一个RCNN单元来表示该节点和它的子节点之间的关系,然后再用一层Pooling层来获得最大信息量的表示。每个RCNN单元依次是其父级RCNN单元的输入。下图显示了RCNN如何表示短语“HewantsaMac”。graphLRa{"W(a)"}-->RCNN_UNit_mac[RCNNUNit]mac{"W(mac)"}-->RCNN_UNit_mac[RCNNUnit]RCNN_UNit_mac-->X_mac{"X(mac)"}He{"W(He)"}-->RCNN_UNit_wants["RCNNUnit"]wants{"W(wants)"}-->RCNN_UNit_wantsX_mac-->RCNN_UNit_wantsP{"W(.)"}-->RCNN_UNit_wantsRCNN_UNit_wants-->X_wants{"X(wants)"}X_wants-->RCNN_Unit_root["RCNNUnit"]root{"W(root)"}-->RCNN_Unit_rootRCNN_Unit_root-->X_root{"X(root)"}RCNN单元的内部结构如图下图:2.RCNN单元的构建首先对于每个单词,我们需要将其转换为一个向量。这一步可以在开始的时候使用训练好的向量,然后在训练的时候根据反向传播进行更新。距离嵌入(DistanceEmbedding),除了词嵌入,我们还需要对一个词与其子节点之间的距离进行编码。直观上,距离近的词更容易有修饰关系。比如上面的例子,Mac到a的距离是-1,到wants的距离是-2。距离嵌入编码了关于子树的更多信息。***将词向量和距离向量作为卷积层的输入。与一般的解析树不同,依赖解析树的每个节点都有两个向量表示。一个是节点的词的词向量表示w,另一个是节点的词向量表示x。对于父节点h,和某个子节点c_i,利用卷积隐藏层计算它们的组合表示向量,即组合矩阵和距离嵌入连接的向量。tanh用作激发层。计算卷积后,对于有K个子节点的节点,我们得到K个向量。对于Z的每一行,我们使用MaxPooling来选择最有用的信息。您最终得到了该短语的矢量表示。得到向量表示后,再计算哪个子树更合理。这时候也可以利用线性层来打分。3.训练对于RCNN,可以使用最大距离的标准来训练。我们选择得分最高的解析树和给定的标准解析树。定义两棵树之间的距离为树中依赖标签不一致的节点数。损失函数是神经网络的参数,D是训练集,score定义如上。当使用正则化项来最小化损失时,正确树的得分增加,错误树的得分降低。实体识别在使用依存分析得到解析树之后,我们可以从树中提取我们想要的任何短语。例如,我们要提取短语“wantssth”。可以通过下面的算法得到。defwant_phrase(sentence):result=defaultdict(list)fortokeninsentence:#遍历所有tokeniftoken.head.lemma=='want'andtoken.dep==dobj:#如果当前token的依赖指向want及其变形,以及依赖关系这是dobj。那么这个就是潜在的目标词组t,依赖词在潜在的目标词组中,依赖关系为neg,实际上是不想要的,需要从目标词组中移除。delresult[token.head]#ThisisasituationIdon'tlikereturnresult结论本文介绍了如何在传统解析算法中使用深度学习技术。在实践中,深度学习减少了数据工程师编码大量特征的时间,效果比人工提取特征要好得多。将神经网络应用于解析算法是一个非常有前途的方向。【本文为栏目组织《机器之心》原创文章,微信公众号《机器之心(id:almosthuman2014)》】点此查看作者更多好文
