====== Trabajo Práctico 1 - Modelado de Lenguaje ====== [[materias:pln|(volver a la página principal)]] En este trabajo práctico implementaremos varios modelos de lenguaje y realizaremos algunos experimentos con ellos. * Repositorio: https://github.com/PLN-FaMAF/PLN-2015/tree/practico1. * Fecha de entrega: 16/09 23/09 a las 23:59. ===== Instrucciones ===== El código base para el proyecto se encuentra en el [[https://github.com/PLN-FaMAF/PLN-2015/tree/practico1|branch ''practico1'' del repositorio del a materia]]. La entrega del proyecto es a través de github. Por lo tanto, deben **hacer un "fork" del repositorio** dentro de sus cuentas de github. Además del código fuente, **deben elaborar un README** con una breve explicación de lo que hicieron en cada ejercicio. El README puede estar en texto plano (txt), markdown (md) o restrucured text (rst), y debe estar incluído dentro de la carpeta ''languagemodeling''. Criterios de Evaluación: * Estilo de codificación (chequeado con flake8 y a ojo). * Diseño del código: uso de clases, herencia, etc. * Uso y aprobación de tests (provistos y definidos por uds.). * Uso apropiado de git (commits granulares, logs informativos). * Resultados. * README. ===== Ejercicio 1: Corpus ===== * Elegir corpus de texto en lenguaje natural de más de 5Mb de tamaño. * Cargar el corpus usando un "corpus reader" de NLTK (e.g. ''PlaintextCorpusReader'') o definiendo uno propio. * El "corpus reader" debe proveer un método ''sents'' que permita iterar sobre las oraciones tokenizadas del corpus. * Revisar a ojo la correcta tokenización y segmentado en oraciones. De ser necesario, probar otras formas de tokenización/segmentado. * Modificar el script ''train.py'' para utilizar nuestro corpus. Documentación: * http://www.nltk.org/howto/corpus.html * http://www.nltk.org/book/ch03.html#regular-expressions-for-tokenizing-text * http://www.nltk.org/_modules/nltk/tokenize/punkt.html * Ejemplo dado en clase de cómo tokenizar con expresiones regulares: http://nbviewer.ipython.org/url/cs.famaf.unc.edu.ar/~francolq/Tokenizaci%C3%B3n%20con%20NLTK.ipynb * [[https://docs.python.org/3/howto/regex.html|Python 3: Regular Expression HOWTO]] ===== Ejercicio 2: Modelo de n-gramas ===== Implementar un modelo de n-gramas con marcadores de comienzo y fin de oración ('''' y ''''). Interfaz de la clase ''NGram'' (en ''ngram.py''): class NGram: def __init__(self, n, sents): """ n -- order of the model. sents -- list of sentences, each one being a list of tokens. """ def count(self, tokens): """Count for an n-gram or (n-1)-gram. tokens -- the n-gram or (n-1)-gram tuple. """ def cond_prob(self, token, prev_tokens=None): """Conditional probability of a token. token -- the token. prev_tokens -- the previous n-1 tokens (optional only if n = 1). """ def sent_prob(self, sent): """Probability of a sentence. Warning: subject to underflow problems. sent -- the sentence as a list of tokens. """ def sent_log_prob(self, sent): """Log-probability of a sentence. sent -- the sentence as a list of tokens. """ Tests: $ nosetests languagemodeling/tests/test_ngram.py ===== Ejercicio 3: Generación de Texto ===== * Implementar en ''ngram.py'' una clase ''NGramGenerator'' para generar oraciones de lenguaje natural. * Programar un script ''generate.py'' para cargar un modelo de n-gramas y generar oraciones con él. * Generar oraciones usando n-gramas con n en {1, 2, 3, 4}. Armar una figura similar a la Figura 4.3 de Jurafsky & Martin (2008). Incluirla en el README. Funciones a implementar en ''ngram.py'': class NGramGenerator: def __init__(self, model): """ model -- n-gram model. """ def generate_sent(self): """Randomly generate a sentence.""" def generate_token(self, prev_tokens=None): """Randomly generate a token, given prev_tokens. prev_tokens -- the previous n-1 tokens (optional only if n = 1). """ Interfaz de ''generate.py'': $ python languagemodeling/scripts/generate.py --help Generate natural language sentences using a language model. Usage: generate.py -i -n generate.py -h | --help Options: -i Language model file. -n Number of sentences to generate. -h --help Show this screen. Tests: $ nosetests languagemodeling/tests/test_ngram_generator.py Documentación: * https://en.wikipedia.org/wiki/Inverse_transform_sampling ===== Ejercicio 4: Suavizado "add-one" ===== * Implementar el suavizado "add-one" en ''ngram.py'' en una clase ''AddOneNGram''. * La clase debe tener **la misma interfaz que ''NGram''** más el método ''V'' especificado abajo. * Calcular V como el tamaño del alfabeto incluyendo el marcador ''''. * Agregar al script de entrenamiento (train.py) una opción de línea de comandos que permita utilizar add-one en lugar de n-gramas clásicos. * Entrenar sobre nuestro corpus y guardar los modelos resultantes para varios valores de n (1, 2, 3 y 4). Interfaz de la clase ''AddOneNGram'' (en ''ngram.py''): class AddOneNGram: """ Todos los métodos de NGram. """ def V(self): """Size of the vocabulary. """ Nueva interfaz de ''train.py'': $ python languagemodeling/scripts/train.py --help Train an n-gram model. Usage: train.py -n [-m ] -o train.py -h | --help Options: -n Order of the model. -m Model to use [default: ngram]: ngram: Unsmoothed n-grams. addone: N-grams with add-one smoothing. -o Output model file. -h --help Show this screen. Tests: $ nosetests languagemodeling/tests/test_addone_ngram.py ===== Ejercicio 5: Evaluación de Modelos de Lenguaje ===== * Separar el corpus en entrenamiento y test (90% y 10% resp.). * Implementar el cálculo de log-probability, cross-entropy y perplejidad. * Programar un script ''eval.py'' para cargar un modelo de lenguajes y evaluarlo sobre el conjunto de test. * Calcular perplejidad de los modelos entrenados en el ejercicio anterior. Reportar los resultados en el README. Interfaz de ''eval.py'': $ python languagemodeling/scripts/eval.py --help Evaulate a language model using the test set. Usage: eval.py -i eval.py -h | --help Options: -i Language model file. -h --help Show this screen. ===== Ejercicio 6: Suavizado por Interpolación ===== * Implementar el suavizado por interpolación en ''ngram.py'' en una clase ''InterpolatedNGram''. * Calcular lambdas en términos de un único parámetro gamma (ver documentación abajo). * Usar add-one para el nivel más bajo (unigramas). * Usar datos held-out (un 10% de train) y barrido para elegir valor para gamma. * Agregar al script de entrenamiento (train.py) una opción de línea de comandos que permita utilizar este modelo. * Calcular y reportar perplejidad para varios valores de ''n'' (1, 2, 3 y 4). Reportar los resultados en el README. Interfaz de la clase ''InterpolatedNGram'' (en ''ngram.py''): class InterpolatedNGram: def __init__(self, n, sents, gamma=None, addone=True): """ n -- order of the model. sents -- list of sentences, each one being a list of tokens. gamma -- interpolation hyper-parameter (if not given, estimate using held-out data). addone -- whether to use addone smoothing (default: True). """ """ Todos los métodos de NGram. """ Tests: $ nosetests languagemodeling/tests/test_interpolated_ngram.py Documentación: * http://www.cs.columbia.edu/~mcollins/lm-spring2013.pdf * https://class.coursera.org/nlangp-001/lecture/55 * https://class.coursera.org/nlangp-001/lecture/57 ===== Ejercicio 7: Suavizado por Back-Off con Discounting ===== * Implementar el suavizado por back-off con discounting en ''ngram.py'' en una clase ''BackOffNGram''. * Usar add-one para el nivel más bajo (unigramas). * Usar datos held-out (un 10% de train) y barrido para elegir valor para beta. * Agregar al script de entrenamiento (train.py) una opción de línea de comandos que permita utilizar este modelo. * Calcular y reportar perplejidad para varios valores de ''n'' (1, 2, 3 y 4). Reportar los resultados en el README. Interfaz de la clase ''BackOffNGram'' (en ''ngram.py''): class BackOffNGram: def __init__(self, n, sents, beta=None, addone=True): """ Back-off NGram model with discounting as described by Michael Collins. n -- order of the model. sents -- list of sentences, each one being a list of tokens. beta -- discounting hyper-parameter (if not given, estimate using held-out data). addone -- whether to use addone smoothing (default: True). """ """ Todos los métodos de NGram. """ def A(self, tokens): """Set of words with counts > 0 for a k-gram with 0 < k < n. tokens -- the k-gram tuple. """ def alpha(self, tokens): """Missing probability mass for a k-gram with 0 < k < n. tokens -- the k-gram tuple. """ def denom(self, tokens): """Normalization factor for a k-gram with 0 < k < n. tokens -- the k-gram tuple. """ Tests: $ nosetests languagemodeling/tests/test_backoff_ngram.py Documentación: * http://www.cs.columbia.edu/~mcollins/lm-spring2013.pdf * https://class.coursera.org/nlangp-001/lecture/51 * https://class.coursera.org/nlangp-001/lecture/53 ===== Ejercicio 8 (punto bonus): Reordenamiento de Palabras ó Atribución de Autoría ===== * Elegir y resolver uno de los dos ejercicios siguientes de Jurafsky & Martin (2008): * Ejercicio 4.9: Reordenamiento de palabras. * Ejercicio 4.10: Atribución de autoría. * Sobre el ejercicio 4.9: * Implementar una clase que, dada una lista de palabras desordenadas de una oración, devuelva el ordenamiento más probable de acuerdo a un modelo de lenguaje. * Implementar un script que tome el conjunto de test y para cada oración, la desordene y la vuelva a ordenar usando un modelo de lenguaje dado como parámetro. * Usar las métricas BLEU y distancia de edición para evaluar la calidad de los reordenamientos (respecto de las oraciones originales). En NLTK: * ''nltk.metrics.distance.edit_distance'' * ''nltk.align.bleu_score.bleu'' * Sobre el ejercicio 4.10: * Se requiere tener documentos de dos o más clases diferentes. Por ejemplo, escritos de dos autores diferentes. * Definir conjuntos de entrenamiento y test para cada clase (90% y 10% resp.). * Entrenar un modelo de lenguaje para cada clase, siguiendo las indicaciones del libro. * Implementar un script que tome los conjuntos de test y adivine a qué clase corresponde cada uno.