【问题标题】:How do I manipulate parse trees?如何操作解析树?
【发布时间】:2011-04-11 05:39:15
【问题描述】:

我一直在玩弄自然语言解析树并以各种方式操纵它们。我一直在使用斯坦福大学的 Tregex 和 Turgeon 工具,但代码很乱,不适合我主要使用 Python 的环境(这些工具是 Java,不适合调整)。我想要一个工具集,当我需要更多功能时可以轻松破解。是否有其他工具非常适合在树上进行模式匹配,然后操作那些匹配的分支?

例如,我想将以下树作为输入:

(ROOT
  (S
    (NP
      (NP (NNP Bank))
      (PP (IN of)
        (NP (NNP America))))
    (VP (VBD used)
      (S
        (VP (TO to)
          (VP (VB be)
            (VP (VBN called)
              (NP
                (NP (NNP Bank))
                (PP (IN of)
                  (NP (NNP Italy)))))))))))

和(这是一个简化的例子):

  1. 查找标签为 NP 的任何节点,该节点的第一个子节点的标签为 NP,一些子节点名为“Bank”,第二个子节点的标签为 PP。
  2. 如果匹配,则取出 PP 节点的所有子节点并将它们移动到匹配的 NP 子节点的末尾。

例如,取树的这一部分:

(NP
  (NP (NNP Bank))
  (PP (IN of)
    (NP (NNP America))))

把它变成这样:

(NP
  (NP (NNP Bank) (IN of) (NP (NNP America))))

由于我的输入树是 S 表达式,我考虑过使用 Lisp(嵌入到我的 Python 程序中),但时间太长了,以至于我在 Lisp 中编写了任何重要的东西,以至于我什至不知道从哪里开始。

描述模式的好方法是什么?什么是描述操作的好方法?考虑这个问题的好方法是什么?

【问题讨论】:

    标签: lisp nlp pattern-matching stanford-nlp s-expression


    【解决方案1】:

    美丽在旁观者的眼中。但是你永远不会说如何 Tregex 或 Turgeon 的代码是一团糟。这听起来更像是您无法处理 Java 或更高级的抽象,因此您正在寻找用 Python 编写的具体内容。

    手写树匹配和转换功能没有错。事实上,我们过去一直这样做。但是在前几百个之后,似乎必须有更好的方法,因此我们转向使用 Tregex 和 Turgeon 的领域特定语言。这通常被视为一种值得称赞的编程风格。请参阅Wikipedia。它们是具有精确语法规范等的指定语言。这是您使用它们的示例。

    Tree t = Tree.valueOf("(ROOT (S (NP (NP (NNP Bank)) (PP (IN of) (NP (NNP America)))) (VP (VBD used) (S (VP (TO to) (VP (VB be) (VP (VBN called) (NP (NP (NNP Bank)) (PP (IN of) (NP (NNP Italy)))))))))))");
    TregexPattern pat = TregexPattern.compile("NP <1 (NP << Bank) <2 PP=remove");
    TsurgeonPattern surgery = Tsurgeon.parseOperation("excise remove remove");
    Tsurgeon.processPattern(pat, surgery, t).pennPrint();
    

    请注意,Java 代码实际上比 Lisp 代码,这正是因为使用了特定领域的语言。很难看出这怎么能更简单:指定模式、指定操作、应用。

    但是,如果您更喜欢手写方法来匹配树上的模式并将它们更改为 Python 中的其他树,那么我们非常欢迎您去做。

    【讨论】:

    • 是否有使用 SP 树正则表达式的文档?还是 javadocs 是迄今为止唯一的文档?
    • 啊,曼宁教授您好,很抱歉在没有提供具体原因的情况下批评您的工作。我想破解代码,但我发现 100,000 行代码对于添加一点抽象层有点令人生畏。我非常感谢 Tregex/Turgeon 的存在。我只是好奇是否可以更简洁地编写类似的 DSL。仍然存在 JavaPython 交互的问题,我以一种不令人满意的方式解决了这个问题,但它(在某种程度上)有效。
    • @gnucom:我承认文档可能会更好/更广泛。但是还有其他一些资源。 ppt 幻灯片nlp.stanford.edu/software/tregex/… 是介绍tregex 模式的最佳场所。 GUI 应用程序中有有用的帮助屏幕。有关更多信息,请参阅:nlp.stanford.edu/software/tregex-faq.shtml#b
    • @guidoism:顺便说一句,这不是我的工作。 DSL 和大部分代码是由 Roger Levy 和 Galen Andrew 编写的。我确信 DSL 可以更简洁地用其他更适合的语言编写,例如 Scala 或 Clojure。 Java 一点也不简洁,也不是特别面向编写 DSL。但我认为你的主要观点是,如果你只是想添加一些特殊情况并且你对工具和代码不太熟悉,那么使用树函数实际上比使用 DSL 更容易,你必须更改分词器和解析器等。
    【解决方案2】:

    这是使用 Lisp 的典型案例。您需要一个将另一个函数映射到树上的函数。

    这是一个使用 Common Lisp 的过程匹配示例。 Lisp 中有可以处理列表结构的匹配器,可以使用这些匹配器。使用列表匹配器可以简化示例(有关使用模式匹配器的示例,请参阅我的其他答案)。

    代码:

    (defun node-children (node)
      (rest node))
    
    (defun node-name (node)
      (second node))
    
    (defun node-type (node)
      (first node))
    
    
    (defun treemap (tree matcher transformer)
      (cond ((null tree) nil)
            ((consp tree)
             (if (funcall matcher tree)
                 (funcall transformer tree)
               (cons (node-type tree)
                     (mapcar (lambda (child)
                               (treemap child matcher transformer))
                             (node-children tree)))))
            (t tree))))
    

    例子:

    (defvar *tree*
      '(ROOT
        (S
         (NP
          (NP (NNP Bank))
          (PP (IN of)
              (NP (NNP America))))
         (VP (VBD used)
             (S
              (VP (TO to)
                  (VP (VB be)
                      (VP (VBN called)
                          (NP
                           (NP (NNP Bank))
                           (PP (IN of)
                               (NP (NNP Italy))))))))))))
    
    
    
    (defun example ()
      (pprint
       (treemap *tree*
                (lambda (node)
                  (and (= (length (node-children node)) 2)
                       (eq (node-type (first (node-children node))) 'np)
                       (some (lambda (node)
                               (eq (node-name node) 'bank))
                             (children (first (node-children node))))
                       (eq (first (second (node-children node))) 'pp)))
                (lambda (node)
                  (list (node-type node)
                        (append (first (node-children node))
                                (node-children (second (node-children node)))))))))
    

    运行示例:

    CL-USER 75 > (example)
    
    (ROOT
     (S
      (NP
       (NP (NNP BANK) (IN OF) (NP (NNP AMERICA))))
      (VP
       (VBD USED)
       (S
        (VP
         (TO TO)
         (VP
          (VB BE)
          (VP
           (VBN CALLED)
           (NP
            (NP
             (NNP BANK)
             (IN OF)
             (NP (NNP ITALY)))))))))))
    

    【讨论】:

      【解决方案3】:

      这是 Common Lisp 的第二个版本。这次我使用的是模式匹配器

      我正在使用一个与 Lisp 数据匹配模式的函数。 PMATCH:MATCH 是 Winston/Horn, Lisp, 3rd Edition 一书中的模式匹配器的增强版本。有类似的模式匹配功能可用。

      数据和我的其他答案一样。

      树映射函数改为使用模式匹配器。如果匹配成功,则 PMATCH:MATCH 函数返回 T 或绑定的关联列表。如果匹配不成功,则返回 NIL。 PMATCH:INSTANTIATE-PATTERN 采用一个模式和一组绑定。它返回一个新的列表结构,其中模式变量被替换为绑定。

      (defun treemapp (tree pattern transformer)
        (cond ((null tree) nil)
              ((consp tree)
               (let ((bindings (pmatch:match pattern tree)))
                 (if bindings
                     (pmatch:instantiate-pattern transformer bindings)
                   (cons (node-type tree)
                         (mapcar (lambda (child)
                                   (treemapp child pattern transformer))
                                 (node-children tree))))))
              (t tree)))
      

      示例使用 now 模式。

      模式是一个列表结构。 #?symbol 匹配单个项目并为符号创建绑定。 #$symbol 匹配项目列表并为符号创建绑定。

      转换器是一种将通过绑定实例化的模式。

      (defun example1 ()
        (pprint (treemapp *tree*
                          '(NP (NP (#?type bank)) (PP #$children))
                          '(NP (NP (#?type bank) #$children)))))
      

      运行此代码会返回与我的其他答案相同的结果。

      【讨论】:

      • 好的,treemapp 可以适应使用 optima lib (github.com/m2ym/optima) 但这仍然包含一个限制,转换仅在树的第一个匹配中完成。
      猜你喜欢
      • 1970-01-01
      • 2016-04-04
      • 2016-08-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多