2015-09-05 8 views
2

Мой вопрос связан с пост-обработкой предложений речи с выражением части речи и синтаксического анализа. В частности, я пишу компонент постпроцессора Lisp, который принимает в качестве входных данных дерево синтаксического анализа (например, созданное Stanford Parser), извлекает из этого дерева синтаксиса правила фразовой структуры, вызываемые для генерации синтаксического анализа , а затем создает таблицу правил и счетчиков правил. Пример ввода и вывода будет следующим:Обработка (,,) и (...) и других знаков препинания при обработке дерева синтаксического разбора с помощью Lisp

(1) предложение:

John said that he knows who Mary likes 

(2) выходного Parser:

(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)))))))))) 

(3) My лисповской вывод программы пост-процессора для этого дерева разбора:

(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 

Обратите внимание на отсутствие пунктуации в предложении (1). Это намеренно. У меня возникли проблемы с анализом пунктуации в Lisp - именно потому, что некоторые пунктуации (например, запятые) зарезервированы для специальных целей. Но разбор предложения без знаков препинания изменяет распределение правил синтаксического анализа, а также символы, содержащихся в этих правилах, как показано на следующем:

(4) Ввод предложение:

I said no and then I did it anyway 

(5) выход Parser :

(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)))))))) 

(6) Входной предложение (с пунктуацией):

I said no, and then I did it anyway. 

(7) Выход Parser:

(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.

Я включил мой соответствующий код Лиспа ниже. Обратите внимание, что я неофит-хакер, но мой код не особенно хорош или эффективен. Если бы кто-то мог предположить, как я могу изменить свой ниже код, чтобы я мог проанализировать (7), чтобы создать таблицу типа (3), которая включает правило вроде (8), я был бы очень благодарен.

Вот мой Лисп код отношение к этой задаче:

(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) в качестве выходного сигнала):

(WRITE-RULES-AND-COUNTS-SORTED '(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))))))))))) 

ответ

4

s-выражения в Common Lisp

В Common Lisp s-выражения символы, такие как ,, . и другие являются частью о f - синтаксис по умолчанию.

Если вам нужны символы с произвольными именами в s-выражениях Lisp, вам нужно их избежать. Либо использовать обратную косую черту, чтобы избежать отдельных символов или использовать пару вертикальных столбиков, чтобы избежать нескольких символов:

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 

Tokenizing/PARSING

Если вы хотите иметь дело с другими входными форматами и не з-выражений , вы можете захотеть выполнить токенизацию/разбор ввода самостоятельно.

Примитивный пример:

CL-USER 11 > (mapcar (lambda (string) 
         (intern string "CL-USER")) 
        (split-sequence " " "S --> S , CC S .")) 
(S --> S \, CC S \.) 
0

UPDATE:

Спасибо доктору Joswig, за ваши комментарии и для кода демо: Оба были весьма полезны.

В приведенном выше вопросе я заинтересован в преодолении того факта, что и. являются частью синтаксиса по умолчанию Lisp (или, по крайней мере, согласуются с этим фактом). Итак, что я делал, это написать функцию ПРОДУКТ-ПАРСЕ-ДЕРЕВО-С-ПАНК-ОТ-ФАЙЛ-ЧИТАТЬ. То, что он делает, читается в одном дереве разбора из файла, как ряд строк; выравнивает белое пространство от струн; объединяет строки вместе, чтобы сформировать строковое представление дерева синтаксического анализа; и затем сканирует эту строку, символ по символу, ища экземпляры пунктуации для изменения. Модификация реализует предложение доктора Йосвига. Наконец, измененная строка преобразуется в дерево (представление списка), а затем отправляется в экстрактор для создания таблицы правил и подсчета. Для реализации я объединил биты кода, найденные в другом месте в StackOverflow вместе с моим собственным исходным кодом. Результат (не все знаки препинания могут быть обработаны, конечно, так как это просто демо):

(defun PRODUCE-PARSE-TREE-WITH-PUNCT-FROM-FILE-READ (file-name) 
    (let ((result (make-array 1 :element-type 'character :fill-pointer 0 :adjustable T)) 
     (list-of-strings-to-process (mapcar #'(lambda (x) (string-trim " " x)) 
             (GET-PARSE-TREE-FROM-FILE file-name))) 
     (concatenated-string nil) 
     (punct-list '(#\, #\. #\; #\: #\! #\?)) 
     (testchar nil) 
     (string-length 0)) 
    (setf concatenated-string (format nil "~{ ~A~}" list-of-strings-to-process)) 
    (setf string-length (length concatenated-string)) 
    (do ((i 0 (incf i))) 
     ((= i string-length) NIL) 
     (setf testchar (char concatenated-string i)) 
     (cond ((member testchar punct-list) 
      (vector-push-extend #\| result) 
      (vector-push-extend testchar result) 
      (vector-push-extend #\| result)) 
      (t (vector-push-extend testchar result)))) 
    (reverse result) 
    (with-input-from-string (s result) 
     (loop for x = (read s nil :end) until (eq x :end) collect x)))) 


(defun GET-PARSE-TREE-FROM-FILE (file-name) 
    (with-open-file (stream file-name) 
    (loop for line = (read-line stream nil) 
     while line 
     collect line))) 

Обратите внимание, что GET-PARSE-ДЕРЕВО-FROM-FILE читает только один дерево из файла, который состоит только из одного дерева , Эти две функции, конечно, не готовы к прайм-тайму!

И, наконец, дерево разбора, содержащий (Лисп-зарезервирован) знаки препинания могут быть обработаны - и, таким образом, первоначальная цель встретились - следующим образом (пользователь передает имя файла, содержащего одно дерево разбора):

(WRITE-RULES-AND-COUNTS-SORTED 
       (PRODUCE-PARSE-TREE-WITH-PUNCT-FROM-FILE-READ filename)) 

следующий вывод производится:

(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 

Этот выход был результат, используя следующий вход (сохранить как имя файла):

(ROOT 
    (S 
    (S 
     (NP (PRP It)) 
     (VP (VBD was) 
     (ADVP 
      (NP (DT the) (NN day)) 
      (IN before)) 
     (NP (NN yesterday)) 
     (, ,) 
     (SBAR 
      (WHADVP (WRB when)) 
      (S 
      (PRN (, ,) 
       (ADVP (RB out)) 
       (PP (IN of) 
       (NP (NN happiness) 
        (CC and) 
        (NN mirth))) 
       (, ,)) 
      (NP (PRP I)) 
      (VP (VBD decided)))))) 
    (: :) 
    (S 
     (VP (VB go) 
     (PP (IN for) 
      (NP (PRP it))))) 
    (. !))) 

 Смежные вопросы

  • Нет связанных вопросов^_^