Combinando un Tokenizer en una gramática y un analizador con NLTK

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

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

(Pregunta inicial: ¿Debo intentar comenzar una gramática desde cero o debo comenzar con una gramática predefinida? Si debo comenzar con otra gramática, ¿cuál es una buena para comenzar con el inglés?)

Supongamos que tengo la 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 poder analizar la frase “Un automóvil tiene ruedas”. Sé que los tokenizadores suministrados pueden averiguar mágicamente qué palabras son verbos / sustantivos, etc. ¿Cómo puedo usar la salida del tokenizador para indicar a la gramática que “ruedas” es un sustantivo?

Puede ejecutar un etiquetador de POS sobre su texto y luego adaptar su gramática para trabajar con tags 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) 

El análisis es un problema difícil, muchas cosas pueden salir mal.

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

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

Cuando tenga una lista de tokens de entrada, puede usar un etiquetador para tratar de determinar el POS de cada palabra y usarla para desambiguar las secuencias de tags 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 etiquetador de POS ya lo ha hecho. En segundo lugar, mejora el manejo de palabras desconocidas, es decir. palabras que no están en tu gramática, asignando también a esas palabras una etiqueta (con suerte, la correcta). Combinar un analizador y un etiquetador de esta manera es algo común.

Las tags POS formarán los pre-terminales en su gramática. Los pre-terminales son los lados izquierdos de las producciones con solo los 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áctico, solo no terminales en ambos lados, producciones y producciones léxicas, un no terminal que va a un 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” desde cualquier terminal en RHS con no terminales en ellas.

Podría ser necesario tener algún tipo de mapeo entre las tags POS y pre-terminales si desea hacer más (o menos) distinciones de tags de grano fino en su gramática que lo que produce su etiquetador. A continuación, puede inicializar el gráfico con los resultados del etiquetador, es decir. elementos pasivos de la categoría apropiada que abarca cada token de entrada. Lamentablemente no sé NTLK, pero estoy seguro de que hay una forma sencilla de hacerlo. Cuando se sembró el gráfico, el análisis puede continuar normalmente, y cualquier árbol de análisis se puede extraer (incluyendo las palabras) de 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 altamente ambiguo. No sé qué tipo de corpus de texto está intentando analizar, pero si es algo como 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 texto de algunos tamaños van desde un par de cientos hasta varios miles de análisis, todo dependiendo de su gramática y de la precisión de los resultados que necesite. Dado este banco de árboles, se puede inferir automáticamente un PCFG correspondiente. El PCFG se puede utilizar como un modelo simple para clasificar los árboles de análisis.

Todo esto es mucho trabajo por hacer tú mismo. ¿Para qué estás usando los resultados del análisis? ¿Ha mirado otros recursos en NTLK u otros paquetes como StanfordParser o BerkeleyParser?

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

Tomo muchas oraciones diferentes y las etiqueté con partes del discurso para un proyecto en el que estoy trabajando. Desde allí hice lo que StompChicken sugirió, extrayendo las tags de las tuplas (palabra, etiqueta) y utilizando esas tags como los “terminales” (los nodos inferiores del árbol cuando creamos una oración completamente etiquetada).

En última instancia, esto no se ajusta a mi deseo de marcar los sustantivos principales en las frases nominales, ya que no puedo insertar el sustantivo principal “palabra” en la gramática, ya que la gramática solo tiene las tags.

Entonces, lo que hice fue usar el conjunto de tuplas (palabra, etiqueta) para crear un diccionario de tags, con todas las palabras con esa etiqueta como valores para esa etiqueta. Luego imprimo este diccionario en el archivo screen / grammar.cfg (gramática libre de contexto).

El formulario que utilizo para imprimir funciona perfectamente con la configuración de un analizador mediante la carga de un archivo de gramática (parser = nltk.load_parser (‘grammar.cfg’). Una de las líneas que genera se ve así: VBG -> “fencing” | “bonging” | “cantidad” | “living” … más de 30 palabras más …

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

Espero que esto ayude a cualquier persona que desee automatizar el etiquetado de un gran corpus y que aún tenga 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 ''