写在前面
自然语言处理是人工智能领域的一个重要分支,旨在使计算机能够理解、处理和生成人类语言。随着信息技术的飞速发展,全球范围内的文本数据呈指数级增长,如何高效地处理和分析这些海量语言信息成为了一个亟待解决的问题。NLP的研究不仅涉及语言学、计算机科学、认知科学等多个学科的交叉,还在机器翻译、信息检索、自动文摘、问答系统等实际应用中展现出巨大的潜力。然而,自然语言的复杂性、歧义性以及不断变化的语言现象也给NLP带来了诸多挑战。通过结合理性主义和经验主义的研究方法,NLP正在逐步突破技术瓶颈,为人类提供更加智能化的语言处理服务。
本系列文章是我的学习笔记,涵盖了入门的基础知识与模型以及对应的上机实验,截图截取自老师的课程ppt。
- 概论
- 词汇分析
- 句法分析--语法分析
- 语篇分析
- 语义分析
- 语义计算
- 语言模型
- 文本摘要
- 情感分析
- 部分对应上机实验
目录
概述
句法分析是指对输入的单词序列(一般为句子)判断其构成是否合乎给定的语法,分析合乎语法的句子的句法结构。
句法结构一般用树状数据结构表示。完成这种分析过程的程序模块称为句法分析器。
句法分析的任务:一般不考虑(1),着重考虑(2)(3)。
- (1)判断输入的字符串是否属于某种语言;
- (2)消除输入句子中的词法和结构等方面的歧义;
- 句法结构歧义的识别和消解是句法分析面临的主要困难。
- (3)分析输入句子的内部结构。
- 1.语法的形式化表示和词条信息描述问题
- 形式化的语法规则构成了规则库
- 词条信息(包括词性、动词的配价和中心词信息等)由词典或相关词表提供
- 规则库与词典或相关词表构成了句法分析的知识库
- 2.分析算法的设计
语法形式化
在自然语言处理中广泛使用的是上下文无关文法(CFG)和基于约束的文法(合一语法),常用的基于约束的语法有:
- 功能合一语法(FUG)
- 树链接语法(TAG)
- 词汇功能语法(LFG)
- 广义的短语结构语法(GPSG)
- 中心语驱动的短语结构语法(HPSG)
基本方法
- 基于规则的句法分析方法的基本思路是:由人工组织语法规则,建立语法知识库,通过条件约束和检查来实现句法结构歧义的消除。
- CYK分析算法、欧雷分析算法、线图分析算法、移进-规约算法、GLR分析算法、左角分析算法
- 这些分析方法分为三种类型:
- 自顶向下的分析方法:实现的是规则推导的过程,分析树从根结点开始不断生长,最后形成分析句子的叶结点;
-
自底向上的分析方法:与自顶向下相反,如CYK分析算法、线图分析算法、移进-规约算法、GLR分析算法;
-
两者结合的分析方法:如左角分析算法。
- 通常用于机器翻译、树库标注等。
- 优点:分析算法可以利用手工编写的语法规则分析出输入句子所有可能的句法结构;对于特定的领域和目的,利用手工编写的有针对性的规则能够较好地处理输入句子中的部分歧义和一些超语法现象。
- 缺点:对于一个中等长度的输入句子来说,要利用大覆盖度的语法规则分析出所有可能的句子结构是非常困难的,分析过程的复杂性往往使程序无法实现;即使能够分析出句子所有可能的结构,也难以在巨大的句法分析结果集合中实现有效的消歧,并选择出最有可能的分析结果;手工编写的规则一般带有一定的主观性,对于实际应用系统来说,往往难以覆盖大领域的所有复杂语言;手工编写规则本身是一件大工作的复杂劳动,而且编写的规则对特定的领域有密切的相关性,不利于句法分析系统向其他领域移植;与实用化需求还有相当大的差异(先验知识永远是有限的)。
依存句法分析
L. Tesnière 的理论
一切结构句法现象可以概括为关联(connexion)、组合(jonction)和转位(tanslation)这三大核心。句法关联建立起词与词之间的从属关系,这种从属关系是由支配词和从属词联结而成;动词是句子的中心,并支配其他成分,它本身不受其他任何成分的支配。
Tesnière 还在《结构句法基础》中将化学中“价”的概念引入依存语法中。“价” 亦称“配价”或“向”(法文valence,德文valenz,英文valence/ valency),一个动词所能支配的行动元(名词词组)的个数即为该动词的价数。也就是说,它能支配几个行动元,它就是几价动词。如汉语中:
- 零价动词:“刮风”等;
- 一价动词:“病、醉、休息、咳嗽、游泳”等;
- 二价动词:“爱、采、参观、讨论”等;
- 三价动词:“给、送、告诉、赔偿”等。
在依存语法理论中,“依存”就是指词与词之间支配与被支配的关系,这种关系不是对等的,而是有方向的。处于支配地位的成分称为支配者(governor, regent, head),而处于被支配地位的成分称为从属者(modifier, subordinate, dependency)。
简单示例(含代码)
- 有向图用带有方向的弧(或称边,edge)来表示两个成分之间的依存关系,支配者在有向弧的发出端,被支配者在箭头端,我们通常说被支配者依存于支配者。
- “是”是一个二价动词。(必须同时关联主语和表语才能构成完整意义)
- 限定词修饰谁说明他属于谁。
- 左图是用树表示的依存结构,树中子节点依存于该节点的父节点。
- 右图是带有投射线的树结构,实线表示依存联结关系, 位置低的成份依存于位置高的成份,虚线为投射线。
spacy
中英文有分别的模型。
import spacy
import json
from spacy import displacy
# 加载中文模型
nlp = spacy.load("zh_core_web_sm")
# ("en_core_web_sm") 英文模型
# 分析句子
text = "北京是中国的首都。"
doc = nlp(text)
# 打印依存关系(每个词的head和dep_标签)
for token in doc:
print(f"{token.text} -> {token.head.text} ({token.dep_})")
# --- 结构化输出 (JSON) ---
structured_output = {
"text": text,
"tokens": [token.text for token in doc],
"part_of_speech": [token.pos_ for token in doc],
"dependency_parsing": [
{
"token": token.text,
"head": token.head.text,
"dependency": token.dep_,
"start_char": token.idx,
"end_char": token.idx + len(token.text)
}
for token in doc
],
"named_entities": [
{
"text": ent.text,
"type": ent.label_,
"start_char": ent.start_char,
"end_char": ent.end_char
}
for ent in doc.ents
]
}
# 打印JSON结果
print("=== JSON 输出 ===")
print(json.dumps(structured_output, indent=2, ensure_ascii=False))
# 生成可视化HTML文件
html = displacy.render(doc, style="dep", page=True) # page=True生成完整HTML
with open("dep_tree.html", "w", encoding="utf-8") as f:
f.write(html)
# # 可选:输出XML
# xml_output = dicttoxml(structured_output, attr_type=False, root_name="nlp_analysis")
# print("\n=== XML 输出 ===")
# print(xml_output.decode('utf-8'))
print("已生成HTML文件:dep_tree.html") # 用浏览器打开此文件
# 可视化依存树
displacy.render(doc, style="dep", jupyter=True) # Jupyter中直接显示
北京 -> 首都 (nsubj)
是 -> 首都 (cop)
中国 -> 首都 (nmod:assmod)
的 -> 中国 (case)
首都 -> 首都 (ROOT)
。 -> 首都 (punct)
=== JSON 输出 ===
{
"text": "北京是中国的首都。",
"tokens": [
"北京",
"是",
"中国",
"的",
"首都",
"。"
],
"part_of_speech": [
"PROPN",
"VERB",
"PROPN",
"PART",
"NOUN",
"PUNCT"
],
"dependency_parsing": [
{
"token": "北京",
"head": "首都",
"dependency": "nsubj",
"start_char": 0,
"end_char": 2
},
{
"token": "是",
"head": "首都",
"dependency": "cop",
"start_char": 2,
"end_char": 3
},
{
"token": "中国",
"head": "首都",
"dependency": "nmod:assmod",
"start_char": 3,
"end_char": 5
},
{
"token": "的",
"head": "中国",
"dependency": "case",
"start_char": 5,
"end_char": 6
},
{
"token": "首都",
"head": "首都",
"dependency": "ROOT",
"start_char": 6,
"end_char": 8
},
{
"token": "。",
"head": "首都",
"dependency": "punct",
"start_char": 8,
"end_char": 9
}
],
"named_entities": [
{
"text": "北京",
"type": "GPE",
"start_char": 0,
"end_char": 2
},
{
"text": "中国",
"type": "GPE",
"start_char": 3,
"end_char": 5
}
]
}
已生成HTML文件:dep_tree.html
<IPython.core.display.HTML object>
<!DOCTYPE html>
<html lang="zh">
<head>
<title>displaCy</title>
</head>
<body style="font-size: 16px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; padding: 4rem 2rem; direction: ltr">
<figure style="margin-bottom: 6rem">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:lang="zh" id="bfdfb09ae99b47a3a97643f5192b60ed-0" class="displacy" width="925" height="487.0" direction="ltr" style="max-width: none; height: 487.0px; color: #000000; background: #ffffff; font-family: Arial; direction: ltr">
<text class="displacy-token" fill="currentColor" text-anchor="middle" y="397.0">
<tspan class="displacy-word" fill="currentColor" x="50">北京</tspan>
<tspan class="displacy-tag" dy="2em" fill="currentColor" x="50">PROPN</tspan>
</text>
<text class="displacy-token" fill="currentColor" text-anchor="middle" y="397.0">
<tspan class="displacy-word" fill="currentColor" x="225">是</tspan>
<tspan class="displacy-tag" dy="2em" fill="currentColor" x="225">VERB</tspan>
</text>
<text class="displacy-token" fill="currentColor" text-anchor="middle" y="397.0">
<tspan class="displacy-word" fill="currentColor" x="400">中国</tspan>
<tspan class="displacy-tag" dy="2em" fill="currentColor" x="400">PROPN</tspan>
</text>
<text class="displacy-token" fill="currentColor" text-anchor="middle" y="397.0">
<tspan class="displacy-word" fill="currentColor" x="575">的</tspan>
<tspan class="displacy-tag" dy="2em" fill="currentColor" x="575">PART</tspan>
</text>
<text class="displacy-token" fill="currentColor" text-anchor="middle" y="397.0">
<tspan class="displacy-word" fill="currentColor" x="750">首都。</tspan>
<tspan class="displacy-tag" dy="2em" fill="currentColor" x="750">NOUN</tspan>
</text>
<g class="displacy-arrow">
<path class="displacy-arc" id="arrow-bfdfb09ae99b47a3a97643f5192b60ed-0-0" stroke-width="2px" d="M70,352.0 C70,2.0 750.0,2.0 750.0,352.0" fill="none" stroke="currentColor"/>
<text dy="1.25em" style="font-size: 0.8em; letter-spacing: 1px">
<textPath xlink:href="#arrow-bfdfb09ae99b47a3a97643f5192b60ed-0-0" class="displacy-label" startOffset="50%" side="left" fill="currentColor" text-anchor="middle">nsubj</textPath>
</text>
<path class="displacy-arrowhead" d="M70,354.0 L62,342.0 78,342.0" fill="currentColor"/>
</g>
<g class="displacy-arrow">
<path class="displacy-arc" id="arrow-bfdfb09ae99b47a3a97643f5192b60ed-0-1" stroke-width="2px" d="M245,352.0 C245,89.5 745.0,89.5 745.0,352.0" fill="none" stroke="currentColor"/>
<text dy="1.25em" style="font-size: 0.8em; letter-spacing: 1px">
<textPath xlink:href="#arrow-bfdfb09ae99b47a3a97643f5192b60ed-0-1" class="displacy-label" startOffset="50%" side="left" fill="currentColor" text-anchor="middle">cop</textPath>
</text>
<path class="displacy-arrowhead" d="M245,354.0 L237,342.0 253,342.0" fill="currentColor"/>
</g>
<g class="displacy-arrow">
<path class="displacy-arc" id="arrow-bfdfb09ae99b47a3a97643f5192b60ed-0-2" stroke-width="2px" d="M420,352.0 C420,177.0 740.0,177.0 740.0,352.0" fill="none" stroke="currentColor"/>
<text dy="1.25em" style="font-size: 0.8em; letter-spacing: 1px">
<textPath xlink:href="#arrow-bfdfb09ae99b47a3a97643f5192b60ed-0-2" class="displacy-label" startOffset="50%" side="left" fill="currentColor" text-anchor="middle">nmod:assmod</textPath>
</text>
<path class="displacy-arrowhead" d="M420,354.0 L412,342.0 428,342.0" fill="currentColor"/>
</g>
<g class="displacy-arrow">
<path class="displacy-arc" id="arrow-bfdfb09ae99b47a3a97643f5192b60ed-0-3" stroke-width="2px" d="M420,352.0 C420,264.5 560.0,264.5 560.0,352.0" fill="none" stroke="currentColor"/>
<text dy="1.25em" style="font-size: 0.8em; letter-spacing: 1px">
<textPath xlink:href="#arrow-bfdfb09ae99b47a3a97643f5192b60ed-0-3" class="displacy-label" startOffset="50%" side="left" fill="currentColor" text-anchor="middle">case</textPath>
</text>
<path class="displacy-arrowhead" d="M560.0,354.0 L568.0,342.0 552.0,342.0" fill="currentColor"/>
</g>
</svg>
</figure>
</body>
</html>
LTP
以下是 LTP(哈工大语言技术平台) 中定义的 14种汉语依存关系 的完整表格:
- LTP不支持英文
from ltp import LTP
import matplotlib.pyplot as plt
import networkx as nx
import json
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
ltp = LTP("LTP/small", cache_dir=".\pretrained-models\ltp") # 自动下载模型
result = ltp.pipeline("北京是中国的首都。", tasks=["cws", "pos", "ner", "srl", "dep", "sdp"])
# 使用字典格式作为返回结果
# 输出中间结果
print(result['cws']) # 分词
print(result['pos']) # 词性
print(result['sdp']) # 语义依存
print(result['dep']) # 句法依存
# 依存关系是以两个列表的形式存在的
words = result['cws']
print(words)
heads = result['dep']['head']
print(heads)
labels = result['dep']['label']
print(labels)
# --- 结构化输出 (JSON) ---
structured_output = {
"text": sentence,
"tokens": result['cws'],
"part_of_speech": result['pos'],
"named_entities": [{"text": ner[0], "type": ner[1], "start": ner[2], "end": ner[3]}
for ner in result['ner']],
"dependency_parsing": [
{"head": head, "dependent": dep_idx+1, "relation": label}
for dep_idx, (head, label) in enumerate(zip(result['dep']['head'], result['dep']['label']))
],
"semantic_dependency": result['sdp'] # 语义依存
}
# 打印JSON结果(缩进2格)
print(json.dumps(structured_output, indent=2, ensure_ascii=False))
# 构造图
G = nx.DiGraph()
for i, word in enumerate(words):
G.add_node(i + 1, label=word)
for dep_idx, (head, label) in enumerate(zip(heads, labels)):
dependent = dep_idx + 1
G.add_edge(head, dependent, label=label)
# 可视化
pos = nx.spring_layout(G)
node_labels = nx.get_node_attributes(G, 'label')
edge_labels = nx.get_edge_attributes(G, 'label')
plt.figure(figsize=(10, 6))
nx.draw(G, pos, with_labels=True, labels=node_labels, node_color='lightblue', node_size=2000, font_size=14, arrows=True)
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels, font_color='red')
plt.title("依存句法分析图")
plt.axis('off')
plt.show()
config.json: 100%|███████████████████████████████████| 9.29k/9.29k [00:00<00:00, 17.0MB/s]
pytorch_model.bin: 100%|███████████████████████████████| 180M/180M [01:47<00:00, 1.67MB/s]
tokenizer_config.json: 100%|███████████████████████████████████| 19.0/19.0 [00:00<?, ?B/s]
vocab.txt: 100%|████████████████████████████████████████| 110k/110k [00:00<00:00, 192kB/s]
tokenizer.json: 100%|███████████████████████████████████| 269k/269k [00:00<00:00, 512kB/s]
added_tokens.json: 100%|███████████████████████████████████████| 2.00/2.00 [00:00<?, ?B/s]
special_tokens_map.json: 100%|███████████████████████████████████| 112/112 [00:00<?, ?B/s]
['北京', '是', '中国', '的', '首都', '。']
['ns', 'v', 'ns', 'u', 'n', 'wp']
{'head': [2, 0, 5, 3, 2, 2], 'label': ['EXP', 'Root', 'FEAT', 'mDEPD', 'LINK', 'mPUNC']}
{'head': [2, 0, 5, 3, 2, 2], 'label': ['SBV', 'HED', 'ATT', 'RAD', 'VOB', 'WP']}
{
"text": "北京是中国的首都。",
"tokens": [
"北京",
"是",
"中国",
"的",
"首都",
"。"
],
"part_of_speech": [
"ns",
"v",
"ns",
"u",
"n",
"wp"
],
"named_entities": [
{
"text": "Ns",
"type": "北京",
"start": 0,
"end": 0
},
{
"text": "Ns",
"type": "中国",
"start": 2,
"end": 2
}
],
"dependency_parsing": [
{
"head": 2,
"dependent": 1,
"relation": "SBV"
},
{
"head": 0,
"dependent": 2,
"relation": "HED"
},
{
"head": 5,
"dependent": 3,
"relation": "ATT"
},
{
"head": 3,
"dependent": 4,
"relation": "RAD"
},
{
"head": 2,
"dependent": 5,
"relation": "VOB"
},
{
"head": 2,
"dependent": 6,
"relation": "WP"
}
],
"semantic_dependency": {
"head": [
2,
0,
5,
3,
2,
2
],
"label": [
"EXP",
"Root",
"FEAT",
"mDEPD",
"LINK",
"mPUNC"
]
}
}
-
从 HuggingFace 仓库下载模型文件:
-
LTP官方模型地址:https://huggingface.co/LTP
-
进入
small
分支,下载全部文件(包括config.json
、pytorch_model.bin
等)。
-
-
将文件保存到本地目录(如
D:/ltp_models/small
)。
nltk-Stanford CoreNLP
中英文有分别的模型
import os
from nltk.parse.stanford import StanfordDependencyParser
# 设置路径(替换为你的实际路径)
os.environ["STANFORD_PARSER"] = "C:/stanford-parser/stanford-parser.jar"
os.environ["STANFORD_MODELS"] = "C:/stanford-parser/stanford-parser-4.2.0-models.jar"
# 初始化解析器(需Java环境)
parser = StanfordDependencyParser(
path_to_jar=os.environ["STANFORD_PARSER"],
path_to_models_jar=os.environ["STANFORD_MODELS"],
model_path="edu/stanford/nlp/models/lexparser/chinesePCFG.ser.gz" # 指定中文模型
)
# 分析句子
sentence = "北京是中国的首都。"
result = list(parser.raw_parse(sentence))
# 打印依存关系
for dep in result:
print(dep.to_conll(4)) # CoNLL格式输出
# 绘制树状图(需安装matplotlib)
dep.tree().draw() # 弹出窗口显示图形
-
下载Stanford Parser:
-
直接下载链接(需选择版本):
-
主程序:
stanford-parser-4.2.0.zip
(示例版本) -
中文模型:
stanford-chinese-corenlp-2020-05-12-models.jar
-
-
解压文件:
-
将下载的
.zip
和.jar
文件解压到本地目录(如C:/stanford-parser
)。
-
hanlp
中英文有分别的模型
import os
import hanlp
import matplotlib.pyplot as plt
import networkx as nx
# === 设置 HanLP 模型本地缓存路径 ===
# save_dir = r'D:\Microsoft VS Code\vs work\codeworkvs\pretrained-models\HanLP_model_cache' # 你想保存模型的文件夹路径
# os.environ['HANLP_HOME'] = HANLP_HOME
# print(hanlp.pretrained.mtl.ALL) # 可见一共多少可使用的模型(大小、语言)
# 加载 HanLP 的多任务模型
hanlp_model = hanlp.load(hanlp.pretrained.mtl.OPEN_TOK_POS_NER_SRL_DEP_SDP_CON_ELECTRA_SMALL_ZH)
# 输入句子进行分析
text = "北京是中国的首都。"
doc = hanlp_model(text)
print(doc)
# 输出结果检查
print("分词:", doc['tok'])
print("词性:", doc['pos'])
print("依存句法:", doc['dep'])
# === 构建依存图并可视化 ===
# 设置中文显示
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
words = doc['tok'] # 分词结果,如 ['北京', '是', '中国', '的', '首都', '。']
deps = doc['dep'] # 依存关系,格式为 [(head_index, relation), ...]
# 创建有向图
G = nx.DiGraph()
# 添加节点(词语)
for i, word in enumerate(words):
G.add_node(i + 1, label=word) # 节点编号从1开始,对应词语位置
# 添加根节点
G.add_node(0, label='ROOT')
# 添加边(依存关系)
for dependent_idx, (head_idx, relation) in enumerate(deps, start=1):
# dependent_idx 是当前词的位置(1-based)
# head_idx 是支配词的位置(0表示ROOT)
G.add_edge(head_idx, dependent_idx, label=relation)
# 布局与绘图
pos = nx.spring_layout(G)
node_labels = nx.get_node_attributes(G, 'label')
edge_labels = nx.get_edge_attributes(G, 'label')
plt.figure(figsize=(10, 6))
nx.draw(G, pos, with_labels=True, labels=node_labels,
node_color='lightblue', node_size=2000,
font_size=14, arrows=True)
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels,
font_color='red')
plt.title("HanLP依存句法分析图")
plt.axis('off')
plt.show()
{
"tok": [
"北京",
"是",
"中国",
"的",
"首都",
"。"
],
"pos": [
"NR",
"VC",
"NR",
"DEG",
"NN",
"PU"
],
"ner": [
["北京", "LOCATION", 0, 1],
["中国", "LOCATION", 2, 3]
],
"srl": [
[["北京", "ARG0", 0, 1], ["是", "PRED", 1, 2], ["中国的首都", "ARG1", 2, 5]]
],
"dep": [
[2, "top"],
[0, "root"],
[5, "assmod"],
[3, "assm"],
[2, "attr"],
[2, "punct"]
],
"sdp": [
[[0, "Root"], [2, "Exp"]],
[[3, "mMod"]],
[[5, "Poss"]],
[[3, "mAux"]],
[[2, "Clas"]],
[[2, "mPunc"]]
],
"con": [
"TOP",
[["IP", [["NP", [["NR", ["北京"]]]], ["VP", [["VC", ["是"]], ["NP", [["DNP", [["NP", [["NR", ["中国"]]]], ["DEG", ["的"]]]], ["NP", [["NN", ["首都"]]]]]]]], ["PU", ["。"]]]]]
]
}
分词: ['北京', '是', '中国', '的', '首都', '。']
词性: ['NR', 'VC', 'NR', 'DEG', 'NN', 'PU']
依存句法: [(2, 'top'), (0, 'root'), (5, 'assmod'), (3, 'assm'), (2, 'attr'), (2, 'punct')]
依存语法的4条公理
1970年计算语言学家J. Robinson在论文《依存结构和转换规则》中提出了依存语法的4条公理:
- (1) 一个句子只有一个独立的成分;
- (2) 句子的其他成分都从属于某一成分;
- (3) 任何一成分都不能依存于两个或多个成分;
- (4) 如果成分A直接从属于成分B,而成分C在句子中位于A和B之间,那么,成分C或者从属于A,或者从属于B,或者从属于A和B之间的某一成分。
这4条公理相当于对依存图和依存树的形式约束为:
- 单一父结点(single headed)
- 连通(connective)
- 无环(acyclic)
- 可投射(projective)
由此来保证句子的依存分析结果是一棵有“根(root) ”的树结构。
依存语法的优势:
- 简单,直接按照词语之间的依存关系工作,是天然词汇化的;
- 不过多强调句子中的固定词序,对自由语序的语言分析更有优势;
- 受深层语义结构的驱动,词汇的依存本质是语义的;
- 形式化程度较短语结构语法浅,对句法结构的表述更为灵活。
依存句法分析方法
依存句法分析(dependency parsing)的任务就是分析出句子中所有词汇之间的依存关系。建立一个依存句法分析器一般需要完成以下三部分工作:
- 依存句法结构描述
- 分析算法设计与实现
- 文法规则或参数学习
目前依存句法结构描述一般采用有向图方法或依存树方法,所采用的句法分析算法可大致归为4类:
- 生成式的分析方法(generative parsing)
- 基本思想是:采用联合概率模型Score(x, y|θ)(其中,x为输入句子,y 为依存分析结构,θ为模型的参数)生成一系列依存句法树,并赋予其概率分值,然后采用相关算法找到概率打分最高的分析结果作为最后输出。这是一种完全句法分析方法,它搜索整个概率空间,得到整个句子的依存分析结果。
- 此类方法的准确率较高,但采用联合概率模型,在进行概率乘积分解时做了不尽合理的假设,不易加入语言特征;因为采用全局搜索,算法的复杂度较高,一般为O(n^3)或O(n^5);不易处理非投射现象。
- 判别式的分析方法(discriminative parsing)
- 在点和边组成的生成树(spanning tree)中找到加权和分值最高的边的组合。生成树中任意两个由词表示的节点之间都有边,根据特征和权值为每条边打分,求解最佳分析结果转化为搜索打分最高的最大生成树问题。
- 在点和边组成的生成树(spanning tree)中找到加权和分值最高的边的组合。生成树中任意两个由词表示的节点之间都有边,根据特征和权值为每条边打分,求解最佳分析结果转化为搜索打分最高的最大生成树问题。
- 其中,s 表示打分值,y 是句子x 的一棵依存树, (i, j) 是y中的结点对。f(·)是取值为 1 或 0 的高维二元特征函数向量,表示结点xi 和 xj 之间的依存关系,如果一棵依存分析树中两个词存在依存关系, 例如:“打”和“球”,则 f(i, j)=1,否则,f(i, j) =0。w 是特征 f(i, j) 的权值向量,w在确定了特征后由样本训练得到。
- 采用判别式模型,避开了联合概率模型所要求的独立性假设;具有较好的可计算性,使诸多机器学习和运筹学的方法得以应用,并可处理非投射现象;分析准确率较高。但整句内的全局搜索,不易使用动态特征;同样由于是全局搜索,算法复杂度较高。
- 决策式的(确定性的)分析方法(deterministic parsing)
- 基本思想:模仿人的认知过程,按照特定方向每次读入一个词。每读入一个词,都要根据当前状态做出决策(比如判断是否与前一个词发生依存关系)。一旦决策做出,将不再改变。所做决策即“采取什么样的分析动作(action)”。分析过程可以看作是一步一步地作用于输入句子之上的分析动作(action)的序列。是一种阶段性方法。
- 移近-归约算法:当前分析状态的格局(configuration)是一个三元组:(S, I, A),S, I, A分别表示栈、未处理结点序列(Input)和依存弧集合(Arcs)。分析体系主要包含两种分析动作组合,一种是采用标准移进-规约方式,使用Left-Reduce、Right-Reduce 和 Shift 三种动作。
- Arc-eager 分析算法- 4种分析动作(Actions)
- 基本思路:在每一个状态(configuration)下根据当前状态提取特征, 然后通过分类决定下一步应该采取的“动作”(action):移进(shift)、左弧(left-arc)、右弧(right-arc)、归约(reduce),执行分类器选择的最优动作,转换到下一个状态。
- 具体实现:1. 标注大量的依存关系句法树,建立训练集。每个句子都可以一对一地转换为动作序列;2. 确定特征集合,以构造动作分类器。
- 基本思路:在每一个状态(configuration)下根据当前状态提取特征, 然后通过分类决定下一步应该采取的“动作”(action):移进(shift)、左弧(left-arc)、右弧(right-arc)、归约(reduce),执行分类器选择的最优动作,转换到下一个状态。
- 算法可以使用之前产生的所有句法结构作为特征;可以达到线性复杂度:O(n)。但以局部最优的加和代替全局最优,导致错误传递;不可处理非投射现象,准确率稍逊于全局最优算法。
- 基本思想:模仿人的认知过程,按照特定方向每次读入一个词。每读入一个词,都要根据当前状态做出决策(比如判断是否与前一个词发生依存关系)。一旦决策做出,将不再改变。所做决策即“采取什么样的分析动作(action)”。分析过程可以看作是一步一步地作用于输入句子之上的分析动作(action)的序列。是一种阶段性方法。
- 基于约束满足的分析方法(constraint satisfaction parsing)
依存句法分析器性能评价
- 无标记依存正确率(unlabeled attachment score, UA):所有词中找到其正确支配词的词所占的百分比,没有找到支配词的词(即根结点)也算在内。
- 带标记依存正确率(labeled attachment score, LA):所有词中找到其正确支配词并且依存关系类型也标注正确的词所占的百分比,根结点也算在内。
- 依存正确率(dependency accuracy, DA):所有非根结点词中找到其正确支配词的词所占的百分比。
- 根正确率(root accuracy, RA):有两种定义方式(对单根结点语言或句子来说,二者是等价的):
- (1)正确根结点的个数与句子个数的比值;
- (2)所有句子中找到正确根结点的句子所占的百分比。
- 完全匹配率(complete match, CM):所有句子中无标记依存结构完全正确的句子所占的百分比。
- 依据不同训练语料和测试语料获得的性能指标没有可比性;
- 相同的分析方法采用不同的参数可能获得不同的分析结果;
- 来自不同论文的测试结果往往没有可比性,除非作者明确说明与对比系统具有严格相同的测试条件和数据。
在评价性能时,可使用标注数据集:UniversalDependendencies
- 标注的每一行代表一个词,每一列包含各项语法信息,包括了顺序编号、词语、(跨语言通用)词性标签、详细词性、形态特征、依存父节点、依存关系、其他关系。
LTP模型的依存标签有较高的粗粒度,需要与该标签建立如下的映射表:
ltp_to_ud = {
# 核心关系
"HED": "root", # 核心谓词
# 主谓宾
"SBV": "nsubj", # 主语
"VOB": "obj", # 直接宾语
"IOB": "iobj", # 间接宾语(LTP较少用)
"FOB": "obl", # 前置宾语(如"把"字句)
# 定中/状中
"ATT": ["amod", "nmod", "nummod"], # 定中关系(需根据上下文细化)
"ADV": ["advmod", "mark", "case"], # 状中关系(需细化)
# 并列
"COO": "conj", # 并列关系
"LAD": "cc", # 左附加(连词)
# 特殊结构
"POB": ["obl", "nmod"], # 介宾关系
"RAD": ["aux", "mark", "discourse"], # 右附加(助词/标记)
"CMP": "ccomp", # 补语
"DBL": "advcl", # 兼语结构
"WP": "punct", # 标点
# 其他
"IS": "appos", # 独立语
"MT": "vocative", # 称呼语
"VV": "xcomp", # 连动式
}
HanNLP如下:
hanlp_to_ud = {
# 状语类
"advmod": "advmod",
"mark": "mark",
# 标点
"punct": "punct",
# 定中关系
"assmod": "det", # HanLP的"assmod"(关联修饰) → UD的"det"(限定词)
"assm": "case", # HanLP的"assm" → UD的"case"(格标记)
"nummod": "nummod",
"amod": "amod",
"nn": "nmod", # HanLP的"nn"(名词修饰) → UD的"nmod"
# 主谓宾
"nsubj": "nsubj",
"dobj": "obj",
"top": "nsubj", # HanLP的"top"(话题) → UD的"nsubj"
# 特殊结构
"root": "root",
"asp": "aux", # HanLP的"asp"(体标记) → UD的"aux"(助动词)
"cpm": "mark:rel", # HanLP的"cpm" → UD的"mark:rel"(关系化标记)
"rcmod": "acl:relcl", # HanLP的"rcmod" → UD的"acl:relcl"(关系从句)
"dep": "conj", # HanLP的"dep" → UD的"conj"(并列)
"cop": "cop", # 系动词
"attr": "nmod", # HanLP的"attr" → UD的"nmod"
"mmod": "aux", # HanLP的"mmod" → UD的"aux"
"cc": "cc", # 连词
"pobj": "nmod", # HanLP的"pobj"(介宾) → UD的"nmod"
}
短语结构与依存结构的关系
短语结构可转换为依存结构,实现方法:
- 定义中心词抽取规则,产生中心词表;
- 根据中心词表,为句法树中每个节点选择中心子节点;
- 将非中心子节点的中心词依存到中心子节点的中心词上,得到相应的依存结构。
汉英句法结构特点对比
保证句法分析器的输入为完全正确的词性序列,仅仅考虑句子结构本身的问题。
- 汉语比英语更少地使用功能词(function words),且没有形态变化:
- 汉语中不使用限定词(“这、这个、那个”等)的名词普遍存在,复数标记(“们”等)有限并且很少出现。
- 英语短语绝大多数以左部为中心,而汉语短语比较复杂,大多数短语类是以右部为短语中心,除了动词和介词的补语在它们的中心词之后。这种差异意味着在英语句子中附加在动词后面的补语引起的歧义是句法分析器需要解决的主要问题,而在汉语句子中很少有这种歧义存在。
- 在汉语句子中没有做主语的先行代词的情况普遍存在,但在英语中这种情况很少出现。这样就使得汉语句法分析器很难判断一个输入到底是没有主语的子句(IP)结构还是仅仅是一个动词短语VP,如:He thinks it is true. / 他认为是对的。
从本质上讲,英语是一种“结构型”语言,一个完整的句法结构即表示一个完整的句子。当多个单句连接起来构成复句的时候,单句与单句之间需要有显式的连接词或者短语。汉语则不同,汉语“表意型”的语言特点,使得汉语句子通常受语义的牵引,一个句子是表达一个完整意义的语言单元,这种特点在长句中表现得特别明显。因此,在汉语中存在一种独特的长句构成方式,就是一连串独立的简单句通过逗号或分号,连接成一个复杂的“句群”式的长句。
这些长句内部的各个简单句是为了表意的需要而连接在一起的,它们彼此的句法结构完全是独立的,表示彼此之间逻辑关系的连接词不是必需的。因此,在很多情况下,它们之间的分隔标记仅仅是一个逗号或者分号。这类长句在汉语中称之为“流水复句”,例如:“我现已步入中年,每天挤车,搞得我精疲力尽,这种状况,直接影响我的工作,家里的孩子也没人照顾。”
从中文资源联盟 (Chinese LDC) 发布的汉语树库(TCT 973)中随机地抽取出 4431 个长度超过20个词的长句,其中,流水复句有1830个,占全部长句的41.3%[李幸, 2005]。
汉语长句的层次化句法分析方法:
- (1) 对包含“分割”标点的长句进行分割;
- (2) 对分割后的各个子句分别进行句法分析(即第一级分析),分析得到的各个最大概率的子树根节点的词类或者短语类别标记作为第二级句法分析的输入;
- (3) 通过第二遍分析找到各子句或短语之间的结构关系,从而获得最终整句的最大概率分析树。
总结
句法分析是对句子结构进行语法合规性判断和结构解析的过程,其核心任务包括消除歧义和分析句子内部结构,通常以树状数据结构表示句法关系。句法分析器的构建涉及语法形式化表示(如上下文无关文法、功能合一语法等)和分析算法设计(如自顶向下、自底向上或混合方法)。基于规则的方法依赖人工编写规则,虽能处理特定领域歧义但存在覆盖不足和主观性问题;依存句法分析则以词间支配关系为核心,强调动词的配价和中心词作用,遵循单一父节点、连通性等公理。工具如spacy、LTP、Stanford CoreNLP和HanLP实现了依存分析,支持中英文模型,通过可视化展示依存关系。汉语与英语的句法差异显著,如汉语缺少形态变化、流水复句结构复杂,需分层分析。性能评价指标包括无标记依存正确率、根正确率等,但需注意数据和方法的一致性。短语结构可通过中心词抽取转换为依存结构。