Parsing 使用Lisp处理自然语言解析树时处理(,)和(…)以及其他标点符号

Parsing 使用Lisp处理自然语言解析树时处理(,)和(…)以及其他标点符号,parsing,nlp,common-lisp,tagging,part-of-speech,Parsing,Nlp,Common Lisp,Tagging,Part Of Speech,我的问题与词性标记和解析的自然语言句子的后处理有关。具体地说,我正在编写一个Lisp后处理器组件,它将一个句子解析树(例如,斯坦福解析器生成的一个)作为输入,从该解析树中提取为生成解析而调用的短语结构规则,然后生成一个规则和规则计数表。输入和输出示例如下: (1) 句子: John said that he knows who Mary likes (2) 分析器输出: (ROOT (S (NP (NNP John)) (VP (VBD said) (SBAR

我的问题与词性标记和解析的自然语言句子的后处理有关。具体地说,我正在编写一个Lisp后处理器组件,它将一个句子解析树(例如,斯坦福解析器生成的一个)作为输入,从该解析树中提取为生成解析而调用的短语结构规则,然后生成一个规则和规则计数表。输入和输出示例如下:

(1) 句子:

John said that he knows who Mary likes
(2) 分析器输出:

(ROOT
  (S
    (NP (NNP John))
    (VP (VBD said)
      (SBAR (IN that)
        (S
          (NP (PRP he))
          (VP (VBZ knows)
            (SBAR
              (WHNP (WP who))
              (S
                (NP (NNP Mary))
                (VP (VBZ likes))))))))))
(ROOT
  (S
    (NP (PRP I))
    (VP (VBD said)
      (ADVP (RB no)
        (CC and)
        (RB then))
      (SBAR
        (S
          (NP (PRP I))
          (VP (VBD did)
            (NP (PRP it))
            (ADVP (RB anyway))))))))
 (ROOT
   (S
     (S
       (NP (PRP I))
       (VP (VBD said)
         (INTJ (UH no))))
     (, ,)
     (CC and)
     (S
       (ADVP (RB then))
       (NP (PRP I))
       (VP (VBD did)
         (NP (PRP it))
         (ADVP (RB anyway))))
     (. .)))
(3) 此解析树的我的Lisp程序后处理器输出:

(S --> NP VP)             3
(NP --> NNP)              2
(VP --> VBZ)              1
(WHNP --> WP)             1
(SBAR --> WHNP S)         1
(VP --> VBZ SBAR)         1
(NP --> PRP)              1
(SBAR --> IN S)           1
(VP --> VBD SBAR)         1
(ROOT --> S)              1
 (WRITE-RULES-AND-COUNTS-SORTED 
              (PRODUCE-PARSE-TREE-WITH-PUNCT-FROM-FILE-READ filename))
注意句子(1)中缺少标点符号。那是故意的。我在分析Lisp中的标点符号时遇到困难——正是因为某些标点符号(例如逗号)是为特殊目的保留的。但是,解析没有标点符号的句子会改变解析规则以及这些规则中包含的符号的分布,如下所示:

(4) 输入句子:

I said no and then I did it anyway
(5) 分析器输出:

(ROOT
  (S
    (NP (NNP John))
    (VP (VBD said)
      (SBAR (IN that)
        (S
          (NP (PRP he))
          (VP (VBZ knows)
            (SBAR
              (WHNP (WP who))
              (S
                (NP (NNP Mary))
                (VP (VBZ likes))))))))))
(ROOT
  (S
    (NP (PRP I))
    (VP (VBD said)
      (ADVP (RB no)
        (CC and)
        (RB then))
      (SBAR
        (S
          (NP (PRP I))
          (VP (VBD did)
            (NP (PRP it))
            (ADVP (RB anyway))))))))
 (ROOT
   (S
     (S
       (NP (PRP I))
       (VP (VBD said)
         (INTJ (UH no))))
     (, ,)
     (CC and)
     (S
       (ADVP (RB then))
       (NP (PRP I))
       (VP (VBD did)
         (NP (PRP it))
         (ADVP (RB anyway))))
     (. .)))
(6) 输入句子(带标点符号):

(7) 分析器输出:

(ROOT
  (S
    (NP (NNP John))
    (VP (VBD said)
      (SBAR (IN that)
        (S
          (NP (PRP he))
          (VP (VBZ knows)
            (SBAR
              (WHNP (WP who))
              (S
                (NP (NNP Mary))
                (VP (VBZ likes))))))))))
(ROOT
  (S
    (NP (PRP I))
    (VP (VBD said)
      (ADVP (RB no)
        (CC and)
        (RB then))
      (SBAR
        (S
          (NP (PRP I))
          (VP (VBD did)
            (NP (PRP it))
            (ADVP (RB anyway))))))))
 (ROOT
   (S
     (S
       (NP (PRP I))
       (VP (VBD said)
         (INTJ (UH no))))
     (, ,)
     (CC and)
     (S
       (ADVP (RB then))
       (NP (PRP I))
       (VP (VBD did)
         (NP (PRP it))
         (ADVP (RB anyway))))
     (. .)))
请注意,包含标点符号是如何完全重新排列解析树的,并且还涉及不同的POS标记(因此,意味着调用了不同的语法规则来生成它),因此包含标点符号很重要,至少对我的应用程序来说是如此

我需要的是找到一种在规则中包含标点符号的方法,这样我就可以生成如下规则,例如,出现在表(3)中,如下所示:

(8) 所需规则:

S --> S , CC S .
像(8)这样的规则实际上是我正在编写的特定应用程序所需要的

但我发现在Lisp中这样做很困难:例如,在(7)中,我们观察到(,)和(…)的外观,这两个在Lisp中处理起来都有问题

我在下面包含了我的相关Lisp代码。请注意,我是一名Lisp黑客,因此我的代码不是特别漂亮或高效。如果有人能建议我如何修改下面的代码,以便我可以解析(7)以生成一个类似(3)的表,其中包含类似(8)的规则,我将非常感激

以下是与此任务相关的Lisp代码:

(defun WRITE-RULES-AND-COUNTS-SORTED (sent)
  (multiple-value-bind (rules-list counts-list)
      (COUNT-RULES-OCCURRENCES sent)
    (setf comblist (sort (pairlis rules-list counts-list) #'> :key #'cdr))
    (format t "~%")
    (do ((i 0 (incf i)))
        ((= i (length comblist)) NIL)
      (format t "~A~26T~A~%" (car (nth i comblist)) (cdr (nth i comblist))))
    (format t "~%")))


 (defun COUNT-RULES-OCCURRENCES (sent)
   (let* ((original-rules-list (EXTRACT-GRAMMAR sent))
          (de-duplicated-list (remove-duplicates original-rules-list :test #'equalp))
          (count-list nil))
     (dolist (i de-duplicated-list)
       (push (reduce #'+ (mapcar #'(lambda (x) (if (equalp x i) 1 0)) original-rules-list) ) count-list))
     (setf count-list (nreverse count-list))
    (values de-duplicated-list count-list)))


 (defun EXTRACT-GRAMMAR (sent &optional (rules-stack nil))
   (cond ((null sent) 
          NIL)
         ((and (= (length sent) 1)
               (listp (first sent))
               (= (length (first sent)) 2)
               (symbolp (first (first sent)))
               (symbolp (second (first sent))))
          NIL)
         ((and (symbolp (first sent)) 
               (symbolp (second sent)) 
               (= 2 (length sent)))
          NIL)
         ((symbolp (first sent))
          (push (EXTRACT-GRAMMAR-RULE sent) rules-stack)
          (append rules-stack (EXTRACT-GRAMMAR (rest sent)   )))
         ((listp (first sent))
          (cond ((not (and (listp (first sent)) 
                           (= (length (first sent)) 2) 
                           (symbolp (first (first sent))) 
                           (symbolp (second (first sent)))))
                 (push (EXTRACT-GRAMMAR-RULE (first sent)) rules-stack)
                 (append rules-stack (EXTRACT-GRAMMAR (rest (first sent))) (EXTRACT-GRAMMAR (rest sent) )))
               (t (append rules-stack (EXTRACT-GRAMMAR (rest sent)  )))))))


(defun EXTRACT-GRAMMAR-RULE (sentence-or-phrase)
  (append (list (first sentence-or-phrase))
          '(-->)
          (mapcar #'first (rest sentence-or-phrase))))
代码调用如下(使用(1)作为输入,产生(3)作为输出):


Common Lisp中的S表达式

在常见的Lisp s表达式中,字符(如
等)是默认语法的一部分

如果希望在Lisp s表达式中使用任意名称的符号,则必须对其进行转义。使用反斜杠转义单个字符或使用一对竖线转义多个字符:

CL-USER 2 > (loop for symbol in '(\, \. | a , b , c .|)
                  do (describe symbol))

\, is a SYMBOL
NAME          ","
VALUE         #<unbound value>
FUNCTION      #<unbound function>
PLIST         NIL
PACKAGE       #<The COMMON-LISP-USER package, 76/256 internal, 0/4 external>

\. is a SYMBOL
NAME          "."
VALUE         #<unbound value>
FUNCTION      #<unbound function>
PLIST         NIL
PACKAGE       #<The COMMON-LISP-USER package, 76/256 internal, 0/4 external>

| a , b , c .| is a SYMBOL
NAME          " a , b , c ."
VALUE         #<unbound value>
FUNCTION      #<unbound function>
PLIST         NIL
PACKAGE       #<The COMMON-LISP-USER package, 76/256 internal, 0/4 external>
NIL

更新

感谢Joswig博士的评论和代码演示:两者都非常有用

在上面的问题中,我对克服这个事实感兴趣,并且。是Lisp默认语法的一部分(或至少符合这一事实)。最后我写了一个函数product-PARSE-TREE-WITH-PUNCT-FROM-FILE-READ。它所做的是在一个解析树中从一个文件中读取一系列字符串;修剪字符串中的空白;将字符串连接在一起以形成解析树的字符串表示形式;然后逐字扫描这个字符串,搜索要修改的标点符号实例。这项修改落实了乔斯维格博士的建议。最后,修改后的字符串转换为树(列表表示),然后发送到提取器以生成规则表和计数。为了实现,我将StackOverflow上其他地方的代码与我自己的原始代码拼凑在一起。结果(当然,并非所有标点符号都可以处理,因为这只是一个演示):

请注意,GET-PARSE-TREE-FROM-FILE只从只包含一棵树的文件中读取一棵树。当然,这两个功能还没有准备好进入黄金时段

最后,可以处理包含(Lisp保留)标点符号的解析树,从而达到原始目标,如下所示(用户提供包含一个解析树的文件名):

产生以下输出:

(NP --> PRP)                  3
(PP --> IN NP)                2
(VP --> VB PP)                1
(S --> VP)                    1
(VP --> VBD)                  1
(NP --> NN CC NN)             1
(ADVP --> RB)                 1
(PRN --> , ADVP PP ,)         1
(S --> PRN NP VP)             1
(WHADVP --> WRB)              1
(SBAR --> WHADVP S)           1
(NP --> NN)                   1
(NP --> DT NN)                1
(ADVP --> NP IN)              1
(VP --> VBD ADVP NP , SBAR)   1
(S --> NP VP)                 1
(S --> S : S .)               1
(ROOT --> S)                  1
该输出是使用以下输入(另存为文件名)的结果: