资讯动态

依存句法分析:从结构表示到神经网络解析器

发布时间:2026/10/15 13:18:36 来源:尧图企业网站定制
1. 这一讲到底在解决什么问题把语言的句法结构变成可计算的形式先交代一下背景这篇笔记梳理的是 2019 年 CS224N 课程中 Linguistic Structure: Dependency Parsing 这一讲的内容。它要回答的核心问题是——计算机在读一个句子的时候怎么把“谁修饰谁、谁是主干、谁是谁的附属”这种人类凭语感就能判断的东西转化成结构化的、可被算法操作的形式。当时听完这一讲我最大的感受是它其实并不是在教一个孤立模型而是在铺垫一个完整的底层假设——语言不只是词串更是由词之间的结构关系支撑起来的系统。你只有先承认“句子是有骨架的”才能继续谈依存分析、语义角色、甚至机器翻译里的调序问题。这一讲适合两类人精读一类是刚入门 NLP、只见过词向量和简单序列模型的学习者另一类是已经在做实际项目、被长句子和复杂修饰关系折磨过的开发者。课程里没有堆砌复杂的数学更多是带着你把“结构表示”这件事想清楚再给出一条从转移式解析到神经网络的完整技术路线。我在这篇笔记里会按自己的理解把课程里的关键概念重新组织一遍同时补充一些我在复现和测试过程中踩过的坑。如果你只是想快速了解“依存句法分析到底是什么”这篇笔记也够用如果你打算自己写一个解析器那后面关于评测和训练细节的部分会更有参考价值。1.1 为什么裸的词序列不够用先看一个最简单的例子The man saw the dog with the telescope.这句话有两种读法。一种是“这个人用望远镜看见了狗”另一种是“这个人看见了带着望远镜的那条狗”。如果只看词的线性顺序这两种读法对应的词串一模一样词向量拼接出来的表示也几乎无法在语义上区分它们。但一旦画出结构关系区别就清楚了第一种里“with the telescope”是修饰“saw”的方式状语第二种里它是修饰“dog”的后置定语。这种修饰关系上的差异直接影响翻译、问答和信息抽取的结果。这就是结构表示第一个价值它把词序里没有写明、但人类理解句子时默认存在的组合关系显式化。尤其面对长距离依赖时线性模型的劣势更明显——比如“The report that the committee submitted was approved”这句话里“report”和“approved”之间隔着一个从句词之间的欧氏距离很远但句法关系却很近。如果模型只依赖局部窗口就很容易在这种地方出错。课程里提到一个观点我也很认同统计语言模型能捕捉大量局部共现规律但结构信息提供的是组合方式的约束。这就像搭积木词向量告诉你每块积木大概长什么样、和哪些积木经常一起出现但真正决定最终造型的是积木之间怎么搭、谁承重、谁装饰。依存树就是那句“怎么搭”的说明书。1.2 短语结构树与依存树两种建模思路的对比说到句法结构学术界历史上主要有两条路线。第一条是短语结构语法也叫成分语法。它的核心思路是把句子递归地切分成短语块一个句子拆成名词短语和动词短语动词短语再拆成动词和名词短语一层层套下去。这种表示方式用上下文无关文法就能描述规则写起来很规整。宾州树库早期大规模标注采用的就是这套思路一个句子的标注结果像一棵倒过来的分类树每个节点都是一个“成分”或“短语”。第二条路线就是这一讲的主角——依存语法。它不强调“块”的嵌套而是直接刻画词与词之间的二元关系每个词可以有一个头head也可以有若干从属词dependent整棵树的根通常是句子的主要动词或者核心谓词。比如“小猫追老鼠”这句话依存表示就是“追”是根“小猫”是主语从属于“追”“老鼠”是宾语从属于“追”。这种表示天然离语义更近在很多下游任务里比短语结构更方便取用。我见过不少初学者在两个概念之间犯迷糊其实可以用组织架构来类比。短语结构树像是按部门分级公司拆成研发部、市场部研发部再拆成前端组、后端组层层嵌套。依存树像是汇报关系每个员工只向一个直属领导汇报最后所有人直接或间接汇报给 CEO。两种表示都能描述层级但依存树更强调“谁直接听命于谁”而且对语序灵活的语言更友好。从课程给的对比来看依存语法后来在统计模型和神经网络模型里占据主流一个重要原因是它摆脱了“短语必须连续”的约束。像德语、日语、中文里一个词和它的修饰成分之间往往可以插入其他成分短语结构处理起来很别扭依存关系则不受这个限制。1.3 谁是树的根中心词的选择规则在依存树里每个节点除了虚根都有唯一的头节点这个“头”是整棵树的枢纽。对“The quick brown fox jumps”这句话jumps 是根节点fox 是主语quick 和 brown 是修饰语都挂在 fox 下面。对“She gave me a book”来说gave 是根She 是 nsubjme 是 iobjbook 是 obj。确定中心词的时候有一些不成文的优先级动词优先于名词介词短语里介词通常不是头而是依附于被修饰词连词结构中选择整个并列结构的主导词。这些规则在早期基于规则的解析器里是硬编码的后来统计模型通过标注数据自动学到。课程里建议初学者不必死记这些规则多读几棵标注树就有语感了。2. UD 标注与依存关系速查让语法术语变成机器能读的字段这一节的标题我想了很久最后还是用了个偏实操的说法。因为课程在讲完依存树的概念之后马上就到 Universal DependenciesUD标注方案这是目前最主流的跨语言依存标注规范也是 CoNLL 评测和各种开源解析器的通用格式。如果只看概念不看数据格式你依然没法亲手打开一棵树。2.1 为什么需要一棵全人类通用格式的树语言学里历史悠久的问题是不同语言有不同的语法体系同一句话用不同学派的标注规则标注结果可能大相径庭。早期英文用宾州树库的标注体系中文又有清华、北大等不同体系跨语言研究基本是各玩各的。UD 的野心就是把所有这些统一起来统一的词性标签集合、统一的依存关系集合、统一的分词哲学。课程里花了些篇幅介绍 CoNLL-U 格式这是 UD 数据的标准存储格式。每一行代表一个词用制表符分隔若干字段包括词序号、原词、词干、粗粒度词性、细粒度词性、形态特征、头部词序号、依存关系类型等。一个句子用一个空行隔开。有些刚接触的同学第一次看到这种文件会觉得眼花其实只要记住两列最关键第 7 列 HEAD这个词挂在哪个词下面、即头词的序号第 8 列 DEPREL这个词和头词之间的依存关系类型。举个例子句子“The quick brown fox jumps”在 CoNLL-U 里大概是这样的结构两个名词性的词都指向 foxfox 指向 jumpsjumps 的 HEAD 是 0代表根节点。可视化之后就是一棵标准的依存树。我在实际处理数据时通常会写几行加载脚本把第 7、8 列拆出来构成一个有向图这样不管后面做什么都能复用。2.2 高频依存关系先记住这 8 个就够用UD 官方定义的关系类型很多实际使用频率高度集中。课程列出的核心关系里我建议你先分清下面这一组nsubj名词性主语比如“She gave me a book”里的 She。obj直接宾语比如上面的 book。iobj间接宾语比如上面的 me。advmod状语修饰比如“really likes”里的 really。amod形容词修饰比如“quick brown fox”里的 quick 和 brown。nmod名词修饰“the book on the table”里的 on the table 整体作为 book 的名词修饰。case格标记通常对应介词比如 on。det限定词比如 the、a。课程里反复强调一点介词一般不自立山头它通常把自己的“介词宾语”作为依附对象而它自己被标成 case 挂在介宾上头。这个规则和很多人直觉不太一致因为传统语法里“介词短语”通常是整体做状语但在 UD 里介词短语的“中心”是介词后面的名词介词只是它的附属标记。这种设计让结构更扁平也更容易统一处理不同语言的格标记。我记得第一次用工具画一棵“on the table”的树时看到 table 挂在动词下、on 挂在 table 下确实愣了一下。理解了 UD 这个“依附方向”的设计之后再看任意语言的树都会顺很多。2.3 论元与附加语区分“必须有”和“可以有”课程里有一个地方反复出现那就是区分论元arguments和附加语adjuncts/ modifiers。论元是谓语动词在语义上必需的参与者比如“我吃苹果”里的“苹果”附加语是额外环境信息比如“我在公园吃苹果”里的“在公园”。删除论元句子会变得不完整删除附加语句子依然成立。这个区分在标注层面体现为不同类型的依存关系比如 nsubj、obj 属于核心论元advmod、nmod 属于附加成分。分类任务要准确区分它们并不容易主要表现在某些动词对论元的数目要求很灵活比如“我吃饭”和“我吃”都合法有些附加语在特定语境里又像是被动词强烈选择的。这些边界情况是解析错误的主要来源之一。课程里没有给一个“万能判定公式”但给了一个很实用的操作建议先看动词的语义框架再看句子有没有特殊标记比如介词、格标记、语序变化把任务交给基于大量标注语料训练出来的模型。对做项目的我们来说理解这个区分能帮我们设计更好的特征和错误分析——如果模型总是把处所短语误判成宾语问题可能就出在训练语料的论元标注不一致上。3. 转移式依存解析让树在一个状态机里一步步长出来前面两节讲的是“标准答案长什么样”从这一节开始进入“怎么自动算出标准答案”。课程主线是先讲经典的转移式解析器一路推进到神经网络解析器。这个递进顺序很重要因为神经模型里的每个组件——栈、缓冲区、动作历史——都是从经典系统里留传下来的。3.1 arc-standard 系统的状态与动作转移式解析的核心思路是把“构建一棵树”的过程变成“在一个状态机上按顺序执行动作”的过程。常见的 arc-standard 系统维持三个数据结构一个栈、一个缓冲区、一个已经生成的依存弧集合。初始状态是栈里只有虚根缓冲区放着整句话的所有词终止状态是缓冲区为空、栈里只剩根节点。每个时间步系统从三个动作里挑一个SHIFT 表示把缓冲区第一个词压入栈LEFT-ARC 表示在栈顶第二个词和栈顶第一个词之间建立一条弧让第二个词成为头、第一个词成为依赖词然后弹出第一个词RIGHT-ARC 表示反过来让栈顶第一个词成为头、第二个词成为依赖词弹出第二个词。动作的选择由分类器决定经典方法用的是手工特征神经网络方法则把这一步换成 softmax 分类。我第一次接触这个体系时觉得有点绕后来找了个简单句子手动跑了一遍把词一个个移进栈按照语法关系不断构建弧并弹栈整个过程就像流水线作业。跑通一遍之后你对 SHIFT/LEFT/RIGHT 三个动作的作用就再也不会混淆了。课程里反复强调这个看似简单的机制保证了输出一定是一棵合法依存树——每个词只有一个头整体连通没有环。3.2 为什么选择贪心解码而不是全局搜索经典转移解析器在实现时使用贪心策略每个时间步选择概率最高的动作继续执行直到终止。这样做的时间复杂度是 O(n)n 是句子的词数。作为对比基于文法解析的 CKY 算法对短语结构树的时间复杂度是 O(n^3)图式的最大生成树解法通常也是 O(n^2) 到 O(n^3)。在实际产品里解析速度直接决定服务成本O(n) 是一个非常大的优势。代价是贪心带来的错误传播前面一个动作选错了后面的动作都在错误的状态上继续操作整棵树可能偏离很远。早期解决办法是用 beam search保留多个候选状态最后选全局得分最高的那棵。课程里没有太深入 beam search 的细节但对提升准确率来说是个很实用的技巧尤其是测试集句子比较长的时候。3.3 从手工稀疏特征到神经网络分类器经典改良版转移解析器特征主要是围绕栈顶和缓冲区窗口做手工模板比如“栈顶第二个词的词性”“栈顶词的左右孩子标签”“栈顶词和缓冲区首词的距离”等。这些特征能work但维护成本极高而且往往只对特定语言和树库有效。神经网络方案的转变在课程里我觉得是最漂亮的一点不再手工定义模板而是把词本身和词性嵌入成向量拼起来作为输入放进一个隐藏层分类器直接预测下一个动作。模型会自动从数据里学习“哪些组合信号是有用的”。更精细的做法是把“栈顶第二个词的孩子标签”也编码进输入相当于把历史结构信息喂给网络。我在复现时试过几种输入拼法有一个小体会不要只喂两个栈顶词和缓冲区首词把“栈顶词在之前步骤里的动作历史”也作为特征带进去模型稳定性会好一点。尤其处理嵌套从句时历史动作能提醒分类器“现在还在从句内部不应该关栈”。4. 从图视角看依存分析全局最优与双仿射注意力转移式解析虽然快但贪心策略的短视让人不甘心。课程后半部分引入了完全不同的视角不再逐步生成树而是把句子视作一个完全有向图给每一条可能的依存弧打分然后求出得分最高的合法生成树。这就是基于图的解析路线。4.1 完全有向图与最大生成树在该框架下对长度为 n 的句子我们构造一个包含 n1 个节点的图多一个虚根任意两个节点之间都有一条候选弧每条弧有一个分数代表“前一个词是头、后一个词是依赖词”的可信度。最终目标是在所有可能的生成树中找到总得分最高的那颗。这个优化问题可以通过类酷的算法求解比如 Eisner 算法或者 CLE 算法。为了直观理解我通常把这个问题类比成“给每个员工确定唯一的上级同时保证整个公司不要出现孤岛或循环”。图式方法的局部决策未必每一步都最优但在整体上能做到全局最优这是它比贪心转移方法更容易取得较高准确率的原因。课程提醒了一个重要细节直接给任意两条弧同时打分并不能保证结果是一棵合法的树因此求解时需要显式加上树的约束——根节点的入度为零其他节点入度为一且整张图必须连通。Eisner 算法用动态规划隐式保证这些性质这也是它比直接贪心选边更稳健的原因。4.2 双仿射打分从点积到高阶相似度现代基于图的神经解析器打分部分已经标准化核心是一个双仿射biaffine模块。流程大概是先把每个词过一遍 BiLSTM拿到包含上下文信息的编码向量然后对每个词分别接两个不同的 MLP得到“作为头词”的表示 h_i 和“作为依赖词”的表示 d_j最后用双仿射形式计算从词 i 指向词 j 的弧得分。这里有个容易让人困惑的点为什么不用简单的点积或者余弦相似度而要引入双仿射我的理解是点积只能捕捉两个向量在同一维度上的线性相关性而头词和依赖词的关系更复杂需要建模它们在高维空间里的联合模式。双仿射公式在点积的基础上加了一个转换矩阵 W还额外提供了两个偏置项相当于给了模型更大的表达能力能更细地区分“主语-动词”和“修饰语-被修饰语”之间的不同。课程里给出的打分公式是score(i, j) (h_i)^T W (d_j) U h_i V d_j b看起来有点吓人实际拆开就是三部分叠加双线性项用来捕捉头和依赖的交互两个线性项用来保留每个词自身的信息最后再加个全局偏置。学习的时候这个模块会“学会”每种依存关系对应的特征模式。4.3 解析一致性约束让“每个词只有一个头”从算法上被保证图式方法虽然全局最优但如果只在解码时用动态规划训练时仍可能遇到各种不一致的问题。比如神经网络一口气输出所有弧的得分后直接用 argmax 选每条弧的头部完全有可能出现两个词抢同一个头、或者虚根被选成某个词的依赖词的情况。解决办法通常是在训练阶段就让模型感知到树的约束比如使用 TreeCRF 或者基于树结构的损失函数。部分实现也会在解码后加一个后处理把未连接的词单独拉一条弧挂到虚根或者最近的候选头上。这一步虽然朴素但在实际评测中往往能挽救几个关键样例。5. 评测指标与踩坑实录UAS、LAS 和那些不为人注意的小问题写到这里读者应该已经对模型架构有了整体认识。但真正从“看懂”到“在项目里用起来”还差一个关键环节你如何知道你解析得到的树到底好不好以及遇到解析结果不对劲的时候怎么排查。这部分课程讲得相对简要但我在实际项目里这些才是真正耗费时间的地方。5.1 指标怎么读UAS 和 LAS 到底在比什么两个指标贯穿依存分析评测UASUnlabeled Attachment Score预测的弧和真实弧比较只要头部预测正确就算对关系类型不管。LASLabeled Attachment Score弧的头指对了还不够依存关系类型也得完全匹配才算对。可以理解成 LAS 是 UAS 的严格版本UAS 只问“你的老板是不是这个人”LAS 还得问“你和他之间到底是不是上下级关系、是直接领导还是虚线汇报”。评测工具在统计指标时有一个常见争议虚根节点是否参与计算。大多数情况下虚根不参与 UAS/LAS 的统计只有真实词与词之间的弧才会被计入。有些框架默认配置会把虚根也算进去导致分数看起来和论文对不上建议统一使用官方评测脚本。课程里曾给过一些参考数据在标准英文树库上好的神经网络解析器 UAS 能到 95% 上下LAS 在 92% 上下。这个数字对判断模型品质非常有用但不要死板套用到中文或小规模领域数据上。特定领域的解析难度通常更高分数低几个点很正常先看相对提升而不是绝对数值。5.2 我在实际项目中踩过的坑第一个坑是分词与 UD 标注的错位。中文分词如果采用“每个字符一刀切”的策略很多短语关系会退化成为字符间的修饰或并列关系树的结构虽然能建起来但语义信息大量丢失。如果下游想用依存树作为特征一定要在分词阶段就跟标注体系对齐最好是直接用同一套语料训练出来的分词模型。第二个坑是长距离依存尤其是从句里的主谓关系。英文里一个主句的主语和谓语可能被几十个词的从句隔开转移式解析器在栈很深时容易把信息忘掉导致跨多个词的关系匹配失败。后来我在做工程时遇到这种长句子会先用简单策略分析局部短语再把结果拼接到整体树上准确率反而比强行让一个模型解决所有长距离依赖更稳。第三个坑是训练语料里的噪声。如果训练集 LAS 只能到 90%先别急着调网络可以先随机抽几百棵树人工检查。很多时候是标注规范内部不一致比如同一篇文档里同样结构的句子一处把时间词标成 nmod另一处标成 advmod。这种噪声会让模型无所适从你越高频迭代优化越容易在噪声上过拟合。5.3 给新手的复现建议如果你想在一个项目里快速用上依存分析我的建议是第一步先找一个成熟的开源解析器跑通流程把一棵树可视化出来感受一下输出长什么样。第二步再自己写一个简化版训练脚本体验损失函数是怎么反传到双仿射层的。最后再考虑要不要替换实现细节。训练数据格式建议直接用 CoNLL-U不要自己设计自定义格式因为下游工具链和评测脚本都是围绕它开发的。加载完成后第一件要做的事是画一棵树确认你的可视化工具对“头词指向依赖词”的方向约定不会把方向搞反。调试阶段先把 embedding 维度试着调到 300 左右BiLSTM 用两层dropout 设在 0.33 左右。这几个参数虽然不是万能但对大多数树库效果都不差可以作为起点。我见过很多人一上来就尝试各种花哨的注意力变体最后发现瓶颈根本不是模型结构而是输入特征里把词性丢了。6. 最后一个实用的工程小技巧在项目里用依存树时我常常需要把树结构序列化成括号表达式方便与其他组件交互。一个很容易踩的坑是括号嵌套顺序搞错让下游解析器读不了。一个小技巧是先对根节点做前序遍历遇到一个节点时先输出它的标签再递归处理它的子节点最后收右括号。这样虽然看起来像是在重复实现树遍历但能避免很多因递归顺序不稳定导致的 bug。另一方面理解句法结构对下游任务的价值往往比直接用解析器的输出分数更大。比如在做关系抽取时即使不关心完整的树只看两个实体之间最短路径上的依存关系就能得到非常强的特征。这让我越来越觉得这一讲虽然标题是“依存句法分析”但它的思想其实贯穿了整个 NLP 系统设计。再分享一个训练中的小经验解析器的质量受训练语料领域影响极大。用新闻语料训练的模型去解析口语对话UAS 会显著下降。如果你的业务语料比较特殊与其不断调模型不如花时间做 domain adaptation把少量目标领域数据加入训练集。这是成本最低、收益最明显的优化手段。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑