500 行 Python 代码做一个英文解析器(1)(3)
总结
我感觉,语言技术,特别是那些相关语法,特别神秘。我不能想象什么样的程序可以实现。
我认为对于人们来说,最好的解决方案可能相当复杂是很自然的。200,000 行的Java包感觉为宜。
但是,仅仅实现一个单一算法时,算法代码往往很短。当你只实现一种算法时,在写之前你确实知道要写什么,你不需要关注任何不必要的具有很大性能影响的抽象概念。
注释
[1] 我真的不确定如何计算Stanford解析器的代码行数。它的jar文件装载了200k大小内容,包括大量不同的模型。这并不重要,但在50k左右似乎是安全的。
[2]例如,如何解析“John’s school of music calls”?你需要确认“John’s school”短语和“John’s school calls”、“John’s school of music calls”有相同的结构。对可以放入短语的不同的“插槽”进行推理是我们推理句法分析的关键途径。你能想到每个短语为具有不同形状的连接器,你需要插入不同的插槽——每个短语也有一定数量不同形状的插槽。我们正试图弄清楚什么样的连接器在什么地方,因此可以搞清句子是如何连接在一起的。
[3]这里有使用了“深度学习”技术的 Stanford 解析器更新版本,此版本准确性更高。但是,最终模型的准确度仍排在最好的移进归约分析器后面。这是一篇伟大的文章,该想法在一个语法分析器上实现,这个语法分析器是不是最先进其实并不重要。
[4]一个细节:Stanford 依赖关系实际上是给定黄金标准短语结构树自动生成的。参考这里的Stanford依赖转换器页面:http://nlp.stanford.edu/software/stanford-dependencies.shtml。
无根据猜测
长期以来,增量语言处理算法是科学界的主要兴趣。如果你想要编写一个语法分析器来测试人类语句处理器如何工作的理论,那么,这个分析器需要建立部分解释器。这里有充分的证据,包括常识性反思,它设立我们不缓存的输入,说话者完成表达立即分析。
但与整齐的科学特征相比,当前算法胜出!尽我所能告诉大家,胜出的秘诀就是:
- 增量。早期的文字限制搜索。
- 错误驱动。训练包含一个发生错误即更新的操作假设。
和人类语句处理的联系看起来诱人。我期待看到这些工程的突破是否带来一些心理语言学方面的进步。
参考书目
NLP 的文献几乎完全开放。所有相关论文都可以在这里找到:http://aclweb.org/anthology/。
我所描述的解析器是动态oracle arc-hybrid 系统的实现:
Goldberg, Yoav; Nivre, Joakim
Training Deterministic Parsers with Non-Deterministic Oracles
TACL 2013
然而,我编写了自己的特征。arc-hybrid 系统的最初描述在这里:
Kuhlmann, Marco; Gomez-Rodriguez, Carlos; Satta, Giorgio
Dynamic programming algorithms for transition-based dependency parsers
ACL 2011
这里最初描述了动态oracle训练方法:
A Dynamic Oracle for Arc-Eager Dependency Parsing
Goldberg, Yoav; Nivre, Joakim
COLING 2012
当Zhang 和 Clark 研究定向搜索时,这项工作依赖于以转换为基础的解析器在准确性上的重大突破。他们发表了很多论文,但首选的引用是:
Zhang, Yue; Clark, Steven
Syntactic Processing Using the Generalized Perceptron and Beam Search
Computational Linguistics 2011 (1)
另外一篇重要的文章是这个短篇的特征工程文章,这篇文章进一步提高了准确性:
Zhang, Yue; Nivre, Joakim
Transition-based Dependency Parsing with Rich Non-local Features
ACL 2011
作为定向解析器的学习框架,广义的感知器来自这篇文章
Collins, Michael
Discriminative Training Methods for Hidden Markov Models: Theory and Experiments with Perceptron Algorithms
EMNLP 2002
实验细节
文章开头的结果引用了华尔街日报语料库第22条。Stanford 解析器执行如下:
- java -mx10000m -cp "$scriptdir/*:" edu.stanford.nlp.parser.lexparser.LexicalizedParser \
- -outputFormat "penn" edu/stanford/nlp/models/lexparser/englishFactored.ser.gz $*
应用了一个小的后处理,撤销Stanford 解析器为数字添加的假设标记,使数字符合 PTB 标记:
- """Stanford parser retokenises numbers. Split them."""
- import sys
- import re
- qp_re = re.compile('\xc2\xa0')
- for line in sys.stdin:
- line = line.rstrip()
- if qp_re.search(line):
- line = line.replace('(CD', '(QP (CD', 1) + ')'
- line = line.replace('\xc2\xa0', ') (CD ')
- print line
由此产生的PTB格式的文件转换成使用 Stanford 转换器的依赖关系:
- for f in $1/*.mrg; do
- echo $f
- grep -v CODE $f > "$f.2"
- out="$f.dep"
- java -mx800m -cp "$scriptdir/*:" edu.stanford.nlp.trees.EnglishGrammaticalStructure \
- -treeFile "$f.2" -basic -makeCopulaHead -conllx > $out
- done
我不能轻易的读取了,但它应该只是使用相关文献的一般设置,将一个目录下的每个.mrg文件转换成一个CoNULL格式的 Stanford 基本依赖文件。
接着我从华尔街日报语料库第22条转换了黄金标准树进行评估。准确的分数是指所有未标记标识中未标记的附属分数如弧头索引)
为了训练 parser.py,我将华尔街日报语料库 02-21 的黄金标准 PTB 树结构输出到同一个转换脚本中。
一言以蔽之,Stanford 模型和 parser.py 在同一组语句中进行训练,在我们知道答案的持有测试集上进行预测。准确性是指我们答对多少正确的语句首词。
在一个 2.4Ghz 的 Xeon 处理器上测试速度。我在服务器上进行了实验,为 Stanford 解析器提供了更多内存。parser.py 系统在我的MacBook Air上运行良好。在parser.py 的实验中,我使用了PyPy;与早期的基准相比,CPython大约快了一半。
parser.py 运行如此之快的一个原因是它进行未标记解析。根据以往的实验,经标记的解析器可能慢400倍,准确度提高大约1%。如果你能访问数据,使程序适应已标记的解析器对读者来说将是很好的锻炼机会。
RedShift 解析器的结果是从版本 b6b624c9900f3bf 取出的,运行如下:
- ./scripts/train.py -x zhang+stack -k 8 -p ~/data/stanford/train.conll ~/data/parsers/tmp
- ./scripts/parse.py ~/data/parsers/tmp ~/data/stanford/devi.txt /tmp/parse/
- ./scripts/evaluate.py /tmp/parse/parses ~/data/stanford/dev.conll
译文链接: http://blog.jobbole.com/67009/
评论关闭