Combinar un Tokenizador en una Gramática y un analizador sintáctico con NLTK


Estoy haciendo mi camino a través del libro NLTK y parece que no puedo hacer algo que parece ser un primer paso natural para construir una gramática decente.

Mi objetivo es construir una gramática para un corpus de texto en particular.

(Pregunta inicial: ¿Debería siquiera intentar empezar una gramática desde cero o debería empezar con una gramática predefinida? Si debo comenzar con otra gramática, ¿cuál es una buena para comenzar en inglés?)

Supongamos que tengo lo siguiente gramática simple:

simple_grammar = nltk.parse_cfg("""
S -> NP VP
PP -> P NP
NP -> Det N | Det N PP
VP -> V NP | VP PP
Det -> 'a' | 'A'
N -> 'car' | 'door'
V -> 'has'
P -> 'in' | 'for'
 """);

Esta gramática puede analizar una oración muy simple, como:

parser = nltk.ChartParser(simple_grammar)
trees = parser.nbest_parse("A car has a door")

Ahora quiero extender esta gramática para manejar oraciones con otros sustantivos y verbos. ¿Cómo agrego esos sustantivos y verbos a mi gramática sin definirlos manualmente en la gramática?

Por ejemplo, supongamos que quiero ser capaz de analizar la frase "Un coche tiene ruedas". Sé que los tokenizadores suministrados pueden mágicamente averiguar qué palabras son verbos/sustantivos, etc. Cómo puedo usar la salida del tokenizador para decirle a la gramática que "ruedas" es un sustantivo?

Author: speedplane, 2011-02-01

3 answers

Podría ejecutar un etiquetador POS sobre su texto y luego adaptar su gramática para trabajar en etiquetas POS en lugar de palabras.

> text = nltk.word_tokenize("A car has a door")
['A', 'car', 'has', 'a', 'door']

> tagged_text = nltk.pos_tag(text)
[('A', 'DT'), ('car', 'NN'), ('has', 'VBZ'), ('a', 'DT'), ('door', 'NN')]

> pos_tags = [pos for (token,pos) in nltk.pos_tag(text)]
['DT', 'NN', 'VBZ', 'DT', 'NN']

> simple_grammar = nltk.parse_cfg("""
  S -> NP VP
  PP -> P NP
  NP -> Det N | Det N PP
  VP -> V NP | VP PP
  Det -> 'DT'
  N -> 'NN'
  V -> 'VBZ'
  P -> 'PP'
  """)

> parser = nltk.ChartParser(simple_grammar)
> tree = parser.parse(pos_tags)
 15
Author: Stompchicken,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-02 12:51:42

El análisis es un problema complicado, ¡muchas cosas pueden salir mal!

Desea (al menos) tres componentes aquí, un tokenizador, un etiquetador y, finalmente, el analizador sintáctico.

Primero necesita tokenizar el texto en ejecución en una lista de tokens. Esto puede ser tan fácil como dividir la cadena de entrada alrededor de espacios en blanco, pero si está analizando texto más general, también necesitará manejar números y puntuación, lo cual no es trivial. Por ejemplo, los períodos de terminación de la sentencia a menudo no se consideran parte de la palabra a la que se adjunta, pero los períodos que marcan una abreviatura a menudo lo son.

Cuando tiene una lista de tokens de entrada, puede usar un etiquetador para tratar de determinar el POS de cada palabra, y usarlo para desambiguar las secuencias de etiquetas de entrada. Esto tiene dos ventajas principales: Primero acelera el análisis, ya que ya no tenemos que considerar hipótesis alternativas autorizadas por palabras ambiguas, ya que el POS-tagger ya lo ha hecho. En segundo lugar, mejora el manejo de palabras desconocidas, es decir. palabras que no están en su gramática, por también asignando a esas palabras una etiqueta (esperemos que la correcta). La combinación de un analizador y un etiquetador de esta manera es común.

Las POS-tags entonces conformarán los pre-terminales en su gramática, Los pre-terminales son los lados izquierdos de las producciones con solo terminales como su lado derecho. Es decir en N - > "casa", V - > "salto" etc. N y V son preterminales. Es bastante común tener la gramática con sintáctica, solo no terminales en ambos lados, producciones y producciones léxicas, una no terminal va a una terminal. Esto tiene sentido lingüístico la mayor parte del tiempo, y la mayoría de los analizadores CFG requieren que la gramática esté en esta forma. Sin embargo, uno podría representar cualquier CFG de esta manera creando "producciones ficticias" de cualquier terminal en RHSes con no terminales en ellos.

Podría ser necesario tener algún tipo de mapeo entre POS-tags y pre-terminales si desea hacer más (o menos) distinciones de etiquetas de grano fino en su gramática de lo que produce su etiquetador. Puedes luego inicialice el gráfico con los resultados del etiquetador, es decir. elementos pasivos de la categoría apropiada que abarcan cada token de entrada. Lamentablemente no sé NTLK, pero estoy seguro de que hay una manera sencilla de hacer esto. Cuando el gráfico es sembrado, el análisis puede continuar como normal, y cualquier árbol de análisis puede ser extraído (incluyendo las palabras) de la manera regular.

Sin embargo, en la mayoría de las aplicaciones prácticas, encontrará que el analizador puede devolver varios análisis diferentes, ya que el lenguaje natural es muy ambiguo. No se qué tipo de corpus de texto está tratando de analizar, pero si se trata de algo parecido al lenguaje natural, probablemente tendrá que construir algún tipo de modelo de selección de análisis, esto requerirá un banco de árboles,una colección de árboles de análisis de algún tamaño que van desde un par de cientos a varios miles de análisis, todo dependiendo de su gramática y la precisión de los resultados que necesita. Dado este treebank uno puede inferir automagicamente un PCFG correspondiente a él. El PCFG se puede utilizar entonces como modelo simple para clasificar los árboles de análisis.

Todo esto es mucho trabajo por hacer. ¿Para qué estás usando los resultados de análisis? ¿Has visto otros recursos en el NTLK u otros paquetes como el StanfordParser o el BerkeleyParser?

 11
Author: Johan Benum Evensberget,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-03 09:34:47

Sé que esto es un año después, pero quería agregar algunas ideas.

Tomo muchas oraciones diferentes y las etiqueta con partes del discurso para un proyecto en el que estoy trabajando. A partir de ahí estaba haciendo como StompChicken sugirió, tirando de las etiquetas de las tuplas (palabra, etiqueta) y utilizando esas etiquetas como los "terminales" (los nodos inferiores del árbol como creamos una oración completamente etiquetada).

En última instancia, esto no se ajusta a mi deseo de marcar sustantivos principales en frases nominales, ya que no puedo tirar de la sustantivo principal "palabra" en la gramática, ya que la gramática solo tiene las etiquetas.

Así que lo que hice fue usar el conjunto de (palabra, etiqueta) tuplas para crear un diccionario de etiquetas, con todas las palabras con esa etiqueta como valores para esa etiqueta. Luego imprimo este diccionario en la pantalla / gramática.archivo cfg (context free grammar).

El formulario que uso para imprimirlo funciona perfectamente con la configuración de un analizador a través de la carga de un archivo de gramática (parser = nltk.load_parser('gramática.cfg"). Una de las líneas es genera se ve así: VBG - > "esgrima | | "bonging" | "amounting" / "living"... más de 30 palabras...

Así que ahora mi gramática tiene las palabras reales como terminales y asigna las mismas etiquetas que nltk.tag_pos lo hace.

Espero que esto ayude a cualquier otra persona que quiera automatizar el etiquetado de un corpus grande y todavía tener las palabras reales como terminales en su gramática.

import nltk
from collections import defaultdict

tag_dict = defaultdict(list)

...
    """ (Looping through sentences) """

        # Tag
        tagged_sent = nltk.pos_tag(tokens)

        # Put tags and words into the dictionary
        for word, tag in tagged_sent:
            if tag not in tag_dict:
                tag_dict[tag].append(word)
            elif word not in tag_dict.get(tag):
                tag_dict[tag].append(word)

# Printing to screen
for tag, words in tag_dict.items():
    print tag, "->",
    first_word = True
    for word in words:
        if first_word:
            print "\"" + word + "\"",
            first_word = False
        else:
            print "| \"" + word + "\"",
    print ''
 11
Author: John,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-02 15:18:32