-
2018-11-29 20:07:02
句法分析(syntactic parsing)是自然语言处理中的关键技术之一,它是对输入的文本句子进行分析以得到句子的句法结构的处理过程。对句法结构进行分析,一方面是语言理解的自身需求,句法分析是语言理解的重要一环,另一方面也为其它自然语言处理任务提供支持。例如句法驱动的统计机器翻译需要对源语言或目标语言(或者同时两种语言)进行句法分析;语义分析通常以句法分析的输出结果作为输入以便获得更多的指示信息。
根据句法结构的表示形式不同,最常见的句法分析任务可以分为以下三种:
-
句法结构分析(syntactic structure parsing),又称短语结构分析(phrase structure parsing),也叫成分句法分析(constituent syntactic parsing)。作用是识别出句子中的短语结构以及短语之间的层次句法关系。
-
依存关系分析,又称依存句法分析(dependency syntactic parsing),简称依存分析,作用是识别句子中词汇与词汇之间的相互依存关系。
-
深层文法句法分析,即利用深层文法,例如词汇化树邻接文法(Lexicalized Tree Adjoining Grammar, LTAG)、词汇功能文法(Lexical Functional Grammar, LFG)、组合范畴文法(Combinatory Categorial Grammar, CCG)等,对句子进行深层的句法以及语义分析。
今天学习一波依存句法分析,依存句法分析属于浅层句法分析。
首先我们依旧来看看它的定义:
-
百度百科定义:依存句法是由法国语言学家L.Tesniere最先提出。它将句子分析成一颗依存句法树,描述出各个词语之间的依存关系。也即指出了词语之间在句法上的搭配关系,这种搭配关系是和语义相关联的。
-
维基百科定义:The dependency-based parse trees of dependency grammars see all nodes as terminal, which means they do not acknowledge the distinction between terminal and non-terminal categories. They are simpler on average than constituency-based parse trees because they contain fewer nodes.
-
在自然语言处理中,用词与词之间的依存关系来描述语言结构的框架称为依存语法(dependence grammar),又称从属关系语法。利用依存句法进行句法分析也是自然语言理解的重要技术之一。
依存分析的一些重要概念:
-
依存句法认为“谓语”中的动词是一个句子的中心,其他成分与动词直接或间接地产生联系。
-
依存句法理论中,“依存”指词与词之间支配与被支配的关系,这种关系不是对等的,这种关系具有方向。确切的说,处于支配地位的成分称之为支配者(governor,regent,head),而处于被支配地位的成分称之为从属者(modifier,subordinate,dependency)。
-
依存语法本身没有规定要对依存关系进行分类,但为了丰富依存结构传达的句法信息,在实际应用中,一般会给依存树的边加上不同的标记。
-
依存语法存在一个共同的基本假设:句法结构本质上包含词和词之间的依存(修饰)关系。一个依存关系连接两个词,分别是核心词(head)和依存词(dependent)。依存关系可以细分为不同的类型,表示两个词之间的具体句法关系。
依存分析方法简介:
-
基于规则的方法: 早期的基于依存语法的句法分析方法主要包括类似CYK的动态规划算法、基于约束满足的方法和确定性分析策略等。
-
基于统计的方法:统计自然语言处理领域也涌现出了一大批优秀的研究工作,包括生成式依存分析方法、判别式依存分析方法和确定性依存分析方法,这几类方法是数据驱动的统计依存分析中最为代表性的方法。
-
基于深度学习的方法:近年来,深度学习在句法分析课题上逐渐成为研究热点,主要研究工作集中在特征表示方面。传统方法的特征表示主要采用人工定义原子特征和特征组合,而深度学习则把原子特征(词、词性、类别标签)进行向量化,在利用多层神经元网络提取特征。
依存分析器的性能评价:
通常使用的指标包括无标记依存正确率(unlabeled attachment score,UAS)、带标记依存正确率(labeled attachment score, LAS)、依存正确率(dependency accuracy,DA)、根正确率(root accuracy,RA)、完全匹配率(complete match,CM)等。这些指标的具体意思如下:
-
无标记依存正确率(UAS):测试集中找到其正确支配词的词(包括没有标注支配词的根结点)所占总词数的百分比。
-
带标记依存正确率(LAS):测试集中找到其正确支配词的词,并且依存关系类型也标注正确的词(包括没有标注支配词的根结点)占总词数的百分比。
-
依存正确率(DA):测试集中找到正确支配词非根结点词占所有非根结点词总数的百分比。
-
根正确率(RA):有二种定义,一种是测试集中正确根结点的个数与句子个数的百分比。另一种是指测试集中找到正确根结点的句子数所占句子总数的百分比。
-
完全匹配率(CM):测试集中无标记依存结构完全正确的句子占句子总数的百分比。
依存分析任务数据集:
-
Penn Treebank:Penn Treebank是一个项目的名称,项目目的是对语料进行标注,标注内容包括词性标注以及句法分析。
-
SemEval-2016 Task 9中文语义依存图数据:http://ir.hit.edu.cn/2461.html
-
CoNLL经常开放句法分析的学术评测。
比如2018年的通用句法分析评测任务:http://universaldependencies.org/conll18/
2009年多语言多语言的句法依存和语义角色联合评测任务:http://ufal.mff.cuni.cz/conll2009-st/
2008年英语的依存句法-语义角色联合评测任务:https://www.clips.uantwerpen.be/conll2008/
2007年多语言依存分析评测:https://www.clips.uantwerpen.be/conll2007/
最后当然是推荐一波依存分析的工具:
-
StanfordCoreNLP:斯坦福的,提供依存句法分析功能。
Github地址:https://github.com/Lynten/stanford-corenlp
官网:https://stanfordnlp.github.io/CoreNLP/
-
Hanlp:HanLP是一系列模型与算法组成的NLP工具包。提供了中文依存句法分析功能。
Github地址:https://github.com/hankcs/pyhanlp
官网:http://hanlp.linrunsoft.com/
-
SpaCy:工业级的自然语言处理工具,遗憾的是不支持中文。
Gihub地址:https://github.com/explosion/spaCy
官网:https://spacy.io/
-
FudanNLP:复旦大学自然语言处理实验室开发的中文自然语言处理工具包,包含信息检索: 文本分类、新闻聚类;中文处理: 中文分词、词性标注、实体名识别、关键词抽取、依存句法分析、时间短语识别;结构化学习: 在线学习、层次分类、聚类。
Github地址:https://github.com/FudanNLP/fnlp
参考:
1.统计自然语言处理
2.中文信息处理报告-2016
更多AI、ML、NLP干货资源请关注公众号:AI小白入门(ID: StudyForAI):
更多相关内容 -
-
依存句法分析代码.py
2019-05-20 23:56:44自然语言理解,依存句法,句法分析,机器学习,人工智能 -
自然语言处理 中英文句法分析与依存句法分析——文本和代码
2019-05-25 10:36:35在使用StanfordCoreNLP对文本句子进行分析时,需要先对句子进行分词 nlp.word_tokenize(sentence) 然后对分词后的句子进行...再之后就是句法分析与依存句法分析 nlp.parse(sentence) nlp.dependency_parse(sentence) -
DDParser:百度开源的依存句法分析系统
2021-03-11 15:24:56人员和商业合作伙伴共享效果领先的依存句法分析技术,我们开源了基于大规模分类数据训练的高级的依存句法分析工具,并提供一键式安装及预测服务,用户只需一条命令即可获取依存句法分析结果。 项目介绍 DDParser... -
依存句法分析
2022-04-11 11:39:49定义HanLP的定义依存句法分析,是指识别语句中词与词之间的依存关系,并揭示其句法结构,包括主谓关系、动宾关系、核心关系等。用依存语言学来理解语义,精准掌握用户意图 百度ddparser的定义依存句法分析是自然语言...捂脸
定义
HanLP的定义
依存句法分析,是指识别语句中词与词之间的依存关系,并揭示其句法结构,包括主谓关系、动宾关系、核心关系等。用依存语言学来理解语义,精准掌握用户意图
百度ddparser的定义
依存句法分析是自然语言处理核心技术之一,旨在通过分析句子中词语之间的依存关系来确定句子的句法结构。
依存句法分析作为底层技术,可直接用于提升其他NLP任务的效果,这些任务包括但不限于语义角色标注、语义匹配、事件抽取等。LTP的定义
依存语法 (Dependency Parsing, DP) 通过分析语言单位内成分之间的依存关系揭示其句法结构。 直观来讲,依存句法分析识别句子中的“主谓宾”、“定状补”这些语法成分,并分析各成分之间的关系。
小插曲,这些项目中的依存句法实现均来自yzhangcs/parser。
数据集解释
标注数据集分成两种格式(conllu和ocnllx),其中一种是以conllx结尾,标注示例如下:
1
2
3
4
5
6
7
8
91 新华社 _ NN NN _ 7 dep _ _
2 兰州 _ NR NR _ 7 dep _ _
3 二月 _ NT NT _ 7 dep _ _
4 十五日 _ NT NT _ 7 dep _ _
5 电 _ NN NN _ 7 dep _ _
6 ( _ PU PU _ 7 punct _ _
7 记者 _ NN NN _ 0 root _ _
8 曲直 _ NR NR _ 7 dep _ _
9 ) _ PU PU _ 7 punct _ _其中第二列表示分词,第四或者第五表示词性,第七列表示当前词和第几个位置的词是有依存关系的,第八列表示其对应的依存关系是什么。
dataset for ctb8 in Stanford Dependencies 3.3.0 standard.
实现
注意:本文的实现是采用biaffine的方式实现。另外以biaffine_dep进行讲解。
我一共使用两种方式进行实现,一个是一个biaffine,和biaffine_ner任务做法一致。第二种就是
yzhangcs
的做法。biaffine_ner实现方式
这种方式是将其变成一个n * n 的矩阵问题,在这个矩阵中预测哪些span为词和词构成依存关系,以及对应的关系是什么,所以这里是一个纯粹的分类问题。
数据处理代码可参考这里
按照依存句法的定义:
- 当前词只能依存一个其他词,但是可以被多个其他词所组成依存关系。
- 如果A依存D,B或者C都在A和D中间,那么B和C都只能在A和D之内进行依存。
所以根据上图所示,每一行只会有一个值不为0.
这里额外插一句哈,与biaffine_ner一样,作者是使用这种临接矩阵的方式来解决嵌套ner的问题,不过与依存句法相比,可能存在的问题就是过于稀疏。但是与依存句法相比有一个特征,就是只会上三角(triu/tril)为1,下三角不会为1,这里可以做mask,具体可看biaffine_ner。
模型结构为:
从下往上看,第一层可以使用lstm或者bert进行提取特征,特征有两部分,一是词,二是词性。第二层为FFNN_Start和FFNN_End,为啥子叫这个名字,俺也不清楚,反正你就知道是两个MLP,分别接收第一层的输入。第三层是BIaffine classifiner,BIaffine classifiner的代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40import torch
# 假设768是mlp出来的hidden_size.
# batch_size, sequence_length, hidden_size = 32, 128,768
class Biaffine(object):
def __init__(self, n_in=768, n_out=2, bias_x=True, bias_y=True):
self.n_in = n_in
self.n_out = n_out
self.bias_x = bias_x
self.bias_y = bias_y
self.weight = nn.Parameter(torch.Tensor(n_out, n_in + bias_x, n_in + bias_y))
def forward(self, x, y):
if self.bias_x:
x = torch.cat((x, torch.ones_like(x[..., :1])), -1)
if self.bias_y:
y = torch.cat((y, torch.ones_like(y[..., :1])), -1)
b = x.shape[0] # 32
o = self.weight.shape[0] # 2
x = x.unsqueeze(1).expand(-1, o, -1, -1) # torch.Size([32, 2, 128, 769])
weight = self.weight.unsqueeze(0).expand(b, -1, -1, -1) # torch.Size([32, 2, 769, 769])
y = y.unsqueeze(1).expand(-1, o, -1, -1) # torch.Size([32, 2, 128, 769])
# torch.matmul(x, weight): torch.Size([32, 2, 128, 769])
# y.permute((0, 1, 3, 2)).shape: torch.Size([32, 2, 769, 128])
s = torch.matmul(torch.matmul(x, weight), y.permute((0, 1, 3, 2)))
if s.shape[1] == 1:
s = s.squeeze(dim=1)
return s # torch.Size([32, 2, 128, 128])
if __name__ == '__main__':
biaffine = Biaffine()
x = torch.rand(32, 128, 768)
y = torch.rand(32, 128, 768)
print(biaffine.forward(x, y).shape)关于biaffine的解释,当然还有triaffine,这个后面有机会再看。总之这里将其变成了batch_size seq_length seq_length * n_label的矩阵。
那如何理解biaffine呢,我觉得下图说的非常在理。关于bilinear,也可以看ltp bilinear。
当然,这里不止这一种方式,你也可以参考ShannonAI/mrc-for-flat-nested-ner的实现方式,他的方式
更为直接,这里:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32def forward(self, input_ids, token_type_ids=None, attention_mask=None):
"""
Args:
input_ids: bert input tokens, tensor of shape [seq_len]
token_type_ids: 0 for query, 1 for context, tensor of shape [seq_len]
attention_mask: attention mask, tensor of shape [seq_len]
Returns:
start_logits: start/non-start probs of shape [seq_len]
end_logits: end/non-end probs of shape [seq_len]
match_logits: start-end-match probs of shape [seq_len, 1]
"""
bert_outputs = self.bert(input_ids, token_type_ids=token_type_ids, attention_mask=attention_mask)
sequence_heatmap = bert_outputs[0] # [batch, seq_len, hidden]
batch_size, seq_len, hid_size = sequence_heatmap.size()
start_logits = self.start_outputs(sequence_heatmap).squeeze(-1) # [batch, seq_len, 1]
end_logits = self.end_outputs(sequence_heatmap).squeeze(-1) # [batch, seq_len, 1]
# for every position $i$ in sequence, should concate $j$ to
# predict if $i$ and $j$ are start_pos and end_pos for an entity.
# [batch, seq_len, seq_len, hidden]
start_extend = sequence_heatmap.unsqueeze(2).expand(-1, -1, seq_len, -1)
# [batch, seq_len, seq_len, hidden]
end_extend = sequence_heatmap.unsqueeze(1).expand(-1, seq_len, -1, -1)
# [batch, seq_len, seq_len, hidden*2]
span_matrix = torch.cat([start_extend, end_extend], 3)
# [batch, seq_len, seq_len]
span_logits = self.span_embedding(span_matrix).squeeze(-1)
return start_logits, end_logits, span_logits两个mlp在不同的位置进行unsqueeze,然后进行concat,嘿嘿,这种方式挺骚气并容易理解的。
至此模型结构以及整理流程说明基本已经结束,损失函数就是使用交叉熵。
我用这种方式验证了biaffine_ner和使用这种方式来做dependency parser任务,在对dependency parser结果中,效果不是很好,总结原因上述也提到了,临接矩阵太过稀疏,好歹ner还有一个上三角矩阵做mask。额外插一句,biaffine_ner这论文水的有点严重呀,妥妥的依存句法的思想呀。更多吐槽看这里。
那么,有没有一种方式可以将这个任务分成两个部分,一是预测哪些词之间成依存关系,二是对应的标签是什么。然后分别计算各自的loss??
yzhangcs实现方式
这种数据处理并没有变成临接矩阵,而是简简单单的如这所示
但是模型结构使用了四个MLP,一共分成两组,一组叫
arc_mlp_d
,arc_mlp_h
,一组叫rel_mlp_d
,rel_mlp_h
,代码可参考这里,分别用来预测arc和rel,emmmm,就是哪些词成依存关系和对应的relation。然后各自经过各自的biaffine classfiner,看这一行,作者在非可能位置进行填充
-math.inf
,这也算是一个小技巧了吧,get到了。——————-重头戏来了,如何计算loss呢,这里手动分割—————————
看compute_loss函数,在进行计算arc loss时,就是简单的套交叉熵即可,但是在进行计算relation的时候,这一行,
s_rel
根据真实的arcs
所对应的位置索引降维的s_rel
,简单来讲就是我直接获取真实的arcs那一维,从而利用了arcs的特征,然后后续接一个交叉熵进行计算loss,最终俩loss相加最为最终loss。相应在decode部分这里也能概述这行做法。
不过后续关于生成最大树,emmm,为啥我这么叫,因为就是获取概率最大的那棵树嘛,这里作者提供了两种算法来实现,
eisner
和mst
,具体实现就不讲了。总结
至此可以看出,在biaffine那层获取词和词之间的关联程度,非常nice的做法,后面就是将其变成一个分类问题来解决,arc分类和rel分类是不同的,这个需要注意。
再额外插一句,感觉目前的句法分析就是依存句法的天下了哇,像Constituency Parser感觉没有很宽广的发展了。更多可看我这,手动狗头。
-
句法分析和依存句法分析(Python实现)
2020-05-02 10:51:42文章目录 环境 使用 Reference 环境 Python3安装 nltk库...('我', 'PN')表示我是句子中的PN(代词) ('NLP', 'NN')表示NLP是句子中的NN(名词) nsubj表示我是爱的名词主语 dobj表示NLP是爱的直接宾语 更多句法符号表示见...环境
- Python3安装
- nltk库安装
在cmd中输入pip install nltk
,如果显示pip不是内部或外部命令,那就是未配置环境变量,自行百度。 - 下载StanfordParser
下载好进行解压,得到文件夹
stanford-parser-full-2018-10-17
,文件夹的内容如下:
解压文件夹中的stanford-parser-3.9.2-models.jar
,
得到使用
import os from nltk.parse import stanford # 这里的三个引用文件都是根据我的目录来的 # 只需要根据你的实际目录更改D:\Google\stanford-parser-full-2018-10-17即可 os.environ['STANFORD_PARSER'] = r"D:\Google\stanford-parser-full-2018-10-17\stanford-parser.jar" os.environ['STANFORD_MODELS'] = r"D:\Google\stanford-parser-full-2018-10-17\stanford-parser-3.9.2-models.jar" parser = stanford.StanfordDependencyParser(model_path=r"D:\Google\stanford-parser-full-2018-10-17\edu\stanford\nlp\models\lexparser\chinesePCFG.ser.gz") res = list(parser.parse(['我','爱','NLP'])) for row in res[0].triples(): print(row)
输出:
(('爱', 'VV'), 'nsubj', ('我', 'PN')) (('爱', 'VV'), 'dobj', ('NLP', 'NN'))
('爱', 'VV')
表示爱
是句子中的VV
(动词)
('我', 'PN')
表示我
是句子中的PN
(代词)
('NLP', 'NN')
表示NLP
是句子中的NN
(名词)nsubj
表示我
是爱
的名词主语
dobj
表示NLP
是爱
的直接宾语
更多句法符号表示见https://www.jianshu.com/p/5c461cf096c4
Reference
-
基于神经网络的依存句法分析总结及代码详解
2018-08-29 16:58:18而本文将介绍一个基于神经网络的依存句法分析器,它基于arc-standard 系统,使用分类器根据从配置信息中提取的特征来预测正确的转换操作。尽管它的性能比基于搜索的解析器略差,但是它的计算效率非常高。本代...上一篇文章CS224n之句法分析总结,介绍了句法分析以及具体的依存分析中的arc-standard算法。arc-standard系统是transition systems中最流行的一个系统之一。而本文将介绍一个基于神经网络的依存句法分析器,它基于arc-standard 系统,使用分类器根据从配置信息中提取的特征来预测正确的转换操作。尽管它的性能比基于搜索的解析器略差,但是它的计算效率非常高。本代码已经上传github
1、模型
模型如下图所示:
模型包括输入层,隐含层和softmax层。隐含层的激活函数比较特殊,使用cube激活函数也就是取3次方。arc-standard 算法介绍过,一个configuration包括Stack,Buffer,依存弧集合。如图最下方所示,这就是一个具体的configuration,包含了stack,buffer,依存弧集合的信息。模型根据configuration信息来提取出一个特征向量,这个特征向量由(words,POS tags,arc labels)三个向量拼接而成。这个向量因此包含了对应configuration的信息。模型的目标就是输入特征向量,然后预测出对应的转换类型,如LEFT-ARC,SHIFT等。预测出转换类型就进行相应的转换操作,这样就更新了配置信息,然后得到新的向量,再输入模型中预测,如此循环。最后就能得到依存弧集合找出句子中依存关系。
首先模型使用了 word embeddings,将one-hot编码转为词向量,不仅word,对应单词的词性和依存关系标签也被映射为向量,同样有自己的embeddings矩阵。那么如何根据配置信息提取出特征向量呢?根据论文所讲述的,系统会分别选择18、18、12个元素作为 xw,xt,xl x w , x t , x l 的值。
对于 xw x w ,栈和缓冲区的前3个单词:s1,s2,s3,b1,b2,b3;堆栈顶部两个单词的第一个和第二个最左/最右边的子项,也就是依赖于该单词的单词,如果没有就是NULL。lc1(si), rc1(si), lc2(si), rc2(si), i = 1, 2.;堆栈顶部两个单词的最左边或最左边节点的最左边或最左边节点(孩子的孩子)lc1(lc1(si)), rc1(rc1(si)), i = 1, 2. 最后将这18个单词输入经过word embeddings映射为词向量。
对于 xt x t 选择 xw x w 中18个词的对应词性作为输入值。
对于 xl x l 选择除了堆栈/缓冲区上的6个字之外的单词的相应弧标签。这些元素同样根据自己的embeddings映射为向量。
这样将所有的向量拼接起来就是input layer的输入。2、代码详解
(1)数据集操作
首先语料库是这样的:
第一列是单词在句子中的序号,第二列是单词,第五列是POS tags,第七列是所依赖的序号,第八列是依赖关系也是arc labels。先用read_conll方法分别读取训练集,验证集,测试集数据,将语料库中的word,pos,head,label存储在examples变量中,代码如下:
def read_conll(in_file, lowercase=False, max_example=None): examples = [] with open(in_file) as f: word, pos, head, label = [], [], [], [] for line in f.readlines(): sp = line.strip().split('\t') if len(sp) == 10: if '-' not in sp[0]: word.append(sp[1].lower() if lowercase else sp[1]) pos.append(sp[4]) head.append(int(sp[6])) label.append(sp[7]) elif len(word) > 0: examples.append({'word': word, 'pos': pos, 'head': head, 'label': label}) word, pos, head, label = [], [], [], [] if (max_example is not None) and (len(examples) == max_example): break if len(word) > 0: examples.append({'word': word, 'pos': pos, 'head': head, 'label': label}) return examples
接下来把获得的信息向量化,使用one-hot编码,代码如下:
def vectorize(self, examples): vec_examples = [] for ex in examples: word = [self.ROOT] + [self.tok2id[w] if w in self.tok2id else self.UNK for w in ex['word']] pos = [self.P_ROOT] + [self.tok2id[P_PREFIX + w] if P_PREFIX + w in self.tok2id else self.P_UNK for w in ex['pos']] head = [-1] + ex['head'] label = [-1] + [self.tok2id[L_PREFIX + w] if L_PREFIX + w in self.tok2id else -1 for w in ex['label']] vec_examples.append({'word': word, 'pos': pos, 'head': head, 'label': label}) return vec_examples
接下来使用create_instances方法产生训练集,先初始化stack,buffer,arc,将三个参数放入get_oracle方法中self.get_oracle(stack, buf, ex) ex为examples其中一句话的部分。这个方法会根据输入的参数的信息根据ex中head返回一个转换操作,用数字代替,0为left-arc,1为right-arc,2为SHIFT。代码如下:
def get_oracle(self, stack, buf, ex): if len(stack) < 2: return self.n_trans - 1 i0 = stack[-1] i1 = stack[-2] h0 = ex['head'][i0] h1 = ex['head'][i1] l0 = ex['label'][i0] l1 = ex['label'][i1] if self.unlabeled: if (i1 > 0) and (h1 == i0): return 0 elif (i1 >= 0) and (h0 == i1) and \ (not any([x for x in buf if ex['head'][x] == i0])): return 1 else: return None if len(buf) == 0 else 2 else: if (i1 > 0) and (h1 == i0): return l1 if (l1 >= 0) and (l1 < self.n_deprel) else None elif (i1 >= 0) and (h0 == i1) and \ (not any([x for x in buf if ex['head'][x] == i0])): return l0 + self.n_deprel if (l0 >= 0) and (l0 < self.n_deprel) else None else: return None if len(buf) == 0 else self.n_trans - 1
create_instances会根据stack, buf, arcs, ex信息提取特征向量,函数为extract_features(stack, buf, arcs, ex),代码如下:
def extract_features(self, stack, buf, arcs, ex): if stack[0] == "ROOT": stack[0] = 0 def get_lc(k): return sorted([arc[1] for arc in arcs if arc[0] == k and arc[1] < k]) def get_rc(k): return sorted([arc[1] for arc in arcs if arc[0] == k and arc[1] > k], reverse=True) p_features = [] l_features = [] features = [self.NULL] * (3 - len(stack)) + [ex['word'][x] for x in stack[-3:]] features += [ex['word'][x] for x in buf[:3]] + [self.NULL] * (3 - len(buf)) if self.use_pos: p_features = [self.P_NULL] * (3 - len(stack)) + [ex['pos'][x] for x in stack[-3:]] p_features += [ex['pos'][x] for x in buf[:3]] + [self.P_NULL] * (3 - len(buf)) for i in xrange(2): if i < len(stack): k = stack[-i-1] lc = get_lc(k) rc = get_rc(k) llc = get_lc(lc[0]) if len(lc) > 0 else [] rrc = get_rc(rc[0]) if len(rc) > 0 else [] features.append(ex['word'][lc[0]] if len(lc) > 0 else self.NULL) features.append(ex['word'][rc[0]] if len(rc) > 0 else self.NULL) features.append(ex['word'][lc[1]] if len(lc) > 1 else self.NULL) features.append(ex['word'][rc[1]] if len(rc) > 1 else self.NULL) features.append(ex['word'][llc[0]] if len(llc) > 0 else self.NULL) features.append(ex['word'][rrc[0]] if len(rrc) > 0 else self.NULL) if self.use_pos: p_features.append(ex['pos'][lc[0]] if len(lc) > 0 else self.P_NULL) p_features.append(ex['pos'][rc[0]] if len(rc) > 0 else self.P_NULL) p_features.append(ex['pos'][lc[1]] if len(lc) > 1 else self.P_NULL) p_features.append(ex['pos'][rc[1]] if len(rc) > 1 else self.P_NULL) p_features.append(ex['pos'][llc[0]] if len(llc) > 0 else self.P_NULL) p_features.append(ex['pos'][rrc[0]] if len(rrc) > 0 else self.P_NULL) if self.use_dep: l_features.append(ex['label'][lc[0]] if len(lc) > 0 else self.L_NULL) l_features.append(ex['label'][rc[0]] if len(rc) > 0 else self.L_NULL) l_features.append(ex['label'][lc[1]] if len(lc) > 1 else self.L_NULL) l_features.append(ex['label'][rc[1]] if len(rc) > 1 else self.L_NULL) l_features.append(ex['label'][llc[0]] if len(llc) > 0 else self.L_NULL) l_features.append(ex['label'][rrc[0]] if len(rrc) > 0 else self.L_NULL) else: features += [self.NULL] * 6 if self.use_pos: p_features += [self.P_NULL] * 6 if self.use_dep: l_features += [self.L_NULL] * 6 features += p_features + l_features assert len(features) == self.n_features return features
然后将返回的向量以及对应get_oracle返回的转换保存在instances数组中,分别作为训练数据和标签。接下来执行刚才得到的转换操作,更新stack,buffer,arc。再循环执行上面的操作。如果一句话有n个单词,则执行循环2×n次,因为每个单词都要进栈一次,出栈一次。 这样模型的数据集就完成了。 create_instances函数的代码如下:
def create_instances(self, examples): all_instances = [] succ = 0 for id, ex in enumerate(logged_loop(examples)): n_words = len(ex['word']) - 1 # arcs = {(h, t, label)} stack = [0] buf = [i + 1 for i in xrange(n_words)] arcs = [] instances = [] for i in xrange(n_words * 2): gold_t = self.get_oracle(stack, buf, ex) if gold_t is None: break legal_labels = self.legal_labels(stack, buf) assert legal_labels[gold_t] == 1 instances.append((self.extract_features(stack, buf, arcs, ex), legal_labels, gold_t)) if gold_t == self.n_trans - 1: stack.append(buf[0]) buf = buf[1:] elif gold_t < self.n_deprel: arcs.append((stack[-1], stack[-2], gold_t)) stack = stack[:-2] + [stack[-1]] else: arcs.append((stack[-2], stack[-1], gold_t - self.n_deprel)) stack = stack[:-1] else: succ += 1 all_instances += instances return all_instances
(2)模型训练
其实模型非常简单,就是普通的全连接神经网络加sotfmax分类。框架用的tensorflow。过程依次是:1. 添加占位符 2. 创建feed_dict 3. 创建embedding层,4、add_prediction_op 网络的前向传播操作 5、add_loss_op 定义loss 使用softmax_cross_entropy_with_logits损失。6、 定义优化器 add_training_op
代码都在q2_parser_model.py 文件中。大家可以去看看。(3)解析
系统在解析中执行贪心解码。在每一步,我们从当前配置c中提取所有相应的单词,POS和标签嵌入,计算隐藏层h(c),并选择具有最高分数的转换,然后再执行转换。
总结
本篇文章跟上一篇文章介绍了NLP中的技术句法分析,以及讲解了基于神经网络的依存分析算法。这两篇文章只是笔者自己的总结概括,如果大家想深入了解句法分析可以去CS224n公开课上学习,对于基于神经网络的依存分析算法可以查看这篇论文:《A Fast and Accurate Dependency Parser using Neural Networks》,我的github上就有.
-
HanLP:中文分词词性标注命名实体识别依存句法分析语义依存分析新词发现用自动生成的摘要进行文本分类聚类...
2021-02-03 15:10:59MSRA,OntoNotes三套规范),依存句法分析(SD,UD规范),成分法分析,语义依存分析(SemEval16,DM,PAS,PSD四套规范),语义角色标注,词干提取,词法语法特征提取,抽象意义(AMR)。 量体裁衣,HanLP提供... -
python实现中文依存句法分析
2018-04-11 17:52:34需求:python实现中文依存句法分析(最初尝试了NLTK库来实现,发现只能实现英文,不能应用中文场景)方法一、斯坦福依存句法分析处理中文斯坦福依存句法分析参考教程地址:... 2:下载Stanford ... -
hmm的matlab代码-HanLP:自然语言处理中文分词词性标注命名实体识别依存句法分析新词发现关键词短语提取自动...
2021-05-19 20:48:43hmm的matlab代码HanLP: ...依存句法分析 KMeans、Repeated Bisection、自动推断聚类数目k 词向量训练、加载、词语相似度计算、语义运算、查询、KMeans聚类 文档语义相似度计算 部分默认模型训练自小型 -
HanLp句法分析和依存句法可视化、保存到json文件中
2021-04-29 12:36:22HanLp句法分析和依存句法可视化、保存到json...我们每次将HanLp依存句法分析结果保存在txt文件中,然后用dependence viewer打开就可以了。 还是以“我希望所有喜欢我的人都能够幸福平平安安的过好这一辈子”为例,保存 -
句法分析:依存分析(Dependency Parsing)
2020-12-16 10:11:05句法分析(syntactic parsing)是自然语言处理中的关键技术之一,它是对输入的文本句子进行分析以得到句子的句法结构的处理过程。对句法结构进行分析,一方面是语言理解的自身需求,句法分析是语言理解的重要一环,... -
依存句法分析总结
2020-04-28 16:40:59依存句法分析(Dependency Parsing,DP)通过分析语言单位内成分之间的依存关系,揭示其句法结构。直观来讲,就是分析句子中的“主谓宾”、“定状补”这些语法成分,并分析各成分的关系。对句法结构进行分析,一方面... -
自然语言处理(NLP)之依存句法分析的可视化及图分析
2021-05-11 10:42:25自然语言处理(NLP)之依存句法分析的可视化及图分析 依存句法分析的效果虽然没有像分词、NER的效果来的好,但也有其使用价值,在日常的工作中,我们免不了要和其打交道。如何分析依存句法分析的结果,一个重要的方面... -
详解依存句法分析
2020-07-11 20:41:58依存句法分析 语法分析(syntactic parsing )是自然语言处理中一个重要的任务,其目标是分析句子的语法结构并将其表示为容易理解的结构(通常是树形结构)。同时,语法分析也是所有工具性NLP任务中较为高级、较为复杂的... -
《自然语言处理入门》12.依存句法分析--提取用户评论
2020-02-18 20:07:13依存句法分析12.1 短语结构树12.2 依存句法树12.3 依存句法分析12.4 基于转移的依存句法分析12.5 依存句法分析 API12.6 案例: 基于依存句法分析的意见抽取12.7 GitHub 笔记转载于GitHub项目:... -
依存句法分析 python & pyltp实现
2021-01-14 10:38:12依存句法分析 python & pyltp实现本文用python,借助pyltp实现依存句法分析。有关pyltp的详细介绍可以查阅其官方网站。说明:使用前需要先下载ltp的模型文件,下载地址:百度云整体思路是先分词,在词性标注,... -
中文依存句法结构分析
2020-02-29 18:40:48"依存分析:" , nlp . dependency_parse ( sentence ) ) # 句法树解析 print ( "句子解析:\n" , nlp . parse ( sentence ) ) # 生成节点关系列表 tree = Tree . fromstring ( nlp . parse ( sentence ) ... -
PHP如何调用百度AI开放平台中NLP语言处理基础技术的“依存句法分析”等接口?
2021-04-10 09:50:58《PHP如何调用百度AI开放平台中NLP语言处理基础技术的“依存句法分析”等接口?》要点:本文介绍了PHP如何调用百度AI开放平台中NLP语言处理基础技术的“依存句法分析”等接口?,希望对您有用。如果有疑问,可以联系... -
中文自然语言处理--Pyhanlp 实现依存句法分析
2021-04-02 00:35:34依存句法(Dependency Parsing, DP)通过分析语言单位内成分之间的依存关系揭示其句法结构。直观来讲,依存句法的目的在于分析识别句子中的“主谓宾”、“定状补”这些语法成分,并分析各成分之间的关系。 from ... -
.依存句法分析--提取用户评论
2020-09-08 11:05:25依存句法分析--提取用户评论 2020-02-252020-02-25 15:18:28阅读 1940笔记转载于GitHub项目:https://github.com/NLP-LOVE/Introduction-NLP12. 依存句法分析语法分析(syntactic parsing )是自然语言处理中一个重要... -
LTP 依存句法分析
2019-01-07 17:25:33直观来讲,依存句法分析识别句子中的“主谓宾”、“定状补”这些语法成分,并分析各成分之间的关系。 #依存句法分析模型 parser = Parser() parser.load(os.path.join(MODELDIR, "parser.model")) ... -
NLP(十二)依存句法分析的可视化及图分析
2019-07-29 23:05:34依存句法分析的效果虽然没有像分词、NER的效果来的好,但也有其使用价值,在日常的工作中,我们免不了要和其打交道。笔者这几天一直在想如何分析依存句法分析的结果,一个重要的方面便是其可视化和它的图分析。 ... -
依存句法分析:原理、应用
2018-11-23 16:14:38句法:句子的各个组成部分的排列以及相互关系,研究句子类型和句子成分。 语法:词法和句法合称为语法。 词性(词类):词的类型。具有相同句法功能、能在同样的组合位置中出现的词,聚合成一个词类(词性)。 ... -
依存句法深度学习
2018-11-09 11:20:35依存句法深度学习,最新方法,不错的文章论文依存句法深度学习,最新方法,不错的文章论文 -
Stanford CoreNLP超简单安装及简单使用,句法分析及依存句法分析
2021-05-07 08:20:06Stanford CoreNLP超简单安装及简单使用,句法分析及依存句法分析,使用jupyter notebook 今天我们来使用Stanford CoreNLP进行简单的句法分析,我使用的是jupyter notebook。网上关于Stanford CoreNLP工具的安装...