====== Trabajo Práctico 2 - Etiquetado de Secuencias ======
[[materias:pln|(volver a la página principal)]]
En este trabajo práctico implementaremos varios modelos de etiquetado de
secuencias y realizaremos algunos experimentos con ellos.
* Repositorio: https://github.com/PLN-FaMAF/PLN-UBA2018.
* Fecha de entrega: 22/02 a las 23:59.
===== Instrucciones =====
El código base para el proyecto se encuentra en el [[https://github.com/PLN-FaMAF/PLN-UBA2018|repositorio de la 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 ''tagging''.
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 AnCora: Estadísticas de etiquetas POS =====
Programar un script ''stats.py'' que muestre la siguiente información del corpus:
* Estadísticas básicas:
* Cantidad de oraciones.
* Cantidad de ocurrencias de palabras.
* Cantidad de palabras (vocabulario).
* Cantidad de etiquetas (vocabulario de tags).
* Etiquetas más frecuentes: Una tabla con las 10 etiquetas más frecuentes y la siguiente información para cada una:
* Cantidad de veces que aparece (frecuencia), y porcentaje del total.
* Cinco palabras más frecuentes con esa etiqueta.
* **En el README, agregar a mano una breve descripción del significado de la etiqueta.**
* Niveles de ambigüedad de las palabras: Una figura similar a la Figura 5.10 de Jurafsky & Martin (2008). Para cada nivel de ambigüedad (de 1 a 9) mostrar:
* Cantidad de palabras y porcentaje del total.
* Cinco palabras más frecuentes.
* Incluir todas las estadísticas en el README.
Uso del script:
$ python tagging/scripts/stats.py
Documentación:
* [[http://clic.ub.edu/corpus/|Corpus AnCora]]
* [[https://web.archive.org/web/20160325024315/http://nlp.lsi.upc.edu/freeling/doc/tagsets/tagset-es.html|Etiquetas EAGLES]]
* [[https://nlp.stanford.edu/software/spanish-faq.shtml#tagset|Stanford CoreNLP simplified tagset]]
===== Ejercicio 2: Baseline Tagger =====
* Programar un etiquetador baseline, que elija para cada palabra su etiqueta más frecuente observada en entrenamiento.
* Para las palabras desconocidas, devolver la etiqueta 'nc0s000' (nombre común singular).
* Entrenar y evaluar el modelo baseline del ejercicio anterior. Reportar los resultados en el README.
* **Bonus**: Graficar la matriz de confusión como un mapa de calor (ver documentación abajo).
Interfaz de ''BaselineTagger'' en ''baseline.py'':
class BaselineTagger:
def __init__(self, n, tagged_sents):
"""
tagged_sents -- training sentences, each one being a list of pairs.
"""
def tag(self, sent):
"""Tag a sentence.
sent -- the sentence.
"""
def tag_word(self, w):
"""Tag a word.
w -- the word.
"""
def unknown(self, w):
"""Check if a word is unknown for the model.
w -- the word.
"""
Tests:
$ nosetests tagging/tests/test_baseline.py
Ejemplo de uso de los scripts:
$ python tagging/scripts/train.py -o baseline
$ python tagging/scripts/eval.py -i baseline
Documentación:
* http://scikit-learn.org/stable/auto_examples/model_selection/plot_confusion_matrix.html
===== Ejercicio 3: Features para Etiquetado de Secuencias =====
* Implementar en ''features.py'' los siguientes features básicos:
* ''word_lower'': la palabra actual en minúsculas.
* ''word_istitle'': la palabra actual empieza en mayúsculas.
* ''word_isupper'': la palabra actual está en mayúsculas.
* ''word_isdigit'': la palabra actual es un número.
* También implementar los siguientes features paramétricos:
* ''NPrevTags(n)'': la tupla de los últimos ''n'' tags.
* ''PrevWord(f)'': Dado un feature ''f'', aplicarlo sobre la palabra anterior en lugar de la actual.
Interfaz de los features paramétricos en ''features.py'':
class NPrevTags(Feature):
def __init__(self, n):
"""Feature: n previous tags tuple.
n -- number of previous tags to consider.
"""
def _evaluate(self, h):
"""n previous tags tuple.
h -- a history.
"""
class PrevWord(Feature):
def __init__(self, f):
"""Feature: the feature f applied to the previous word.
f -- the feature.
"""
def _evaluate(self, h):
"""Apply the feature to the previous word in the history.
h -- the history.
"""
Tests:
$ nosetests tagging/tests/test_features.py
Documentación:
* [[http://nbviewer.ipython.org/url/cs.famaf.unc.edu.ar/~francolq/Etiquetado%20de%20Secuencias%20con%20Feature%20Forge.ipynb|Etiquetado de Secuencias con Feature Forge]]
===== Ejercicio 4: Maximum Entropy Markov Models =====
* Implementar un MEMM con el siguiente //pipeline// de scikit-learn:
* Vectorizador (''featureforge.vectorizer.Vectorizer'') con los features definidos en el ejercicio anterior.
* Clasificador de máxima entropía (''sklearn.linear_model.LogisticRegression'').
* Implementar un algoritmo de tagging en el método ''tag'' usando //beam inference// con un //beam// de tamaño 1.
* Agregar al script de entrenamiento (train.py) una opción de línea de comandos que permita utilizar el MEMM con distintos valores de ''n''.
* Entrenar y evaluar para varios valores de ''n'' (1, 2, 3 y 4).
* Probar también los siguientes clasificadores:
* ''sklearn.naive_bayes.MultinomialNB''
* ''sklearn.svm.LinearSVC''
* Reportar los resultados en el README. Reportar también tiempo de evaluación.
* **Bonus**: Inventar y agregar features que mejoren la calidad del tagger.
Interfaz de ''MEMM'' en ''memm.py'':
class MEMM:
def __init__(self, n, tagged_sents):
"""
n -- order of the model.
tagged_sents -- list of sentences, each one being a list of pairs.
"""
def sents_histories(self, tagged_sents):
"""
Iterator over the histories of a corpus.
tagged_sents -- the corpus (a list of sentences)
"""
def sent_histories(self, tagged_sent):
"""
Iterator over the histories of a tagged sentence.
tagged_sent -- the tagged sentence (a list of pairs (word, tag)).
"""
def sents_tags(self, tagged_sents):
"""
Iterator over the tags of a corpus.
tagged_sents -- the corpus (a list of sentences)
"""
def sent_tags(self, tagged_sent):
"""
Iterator over the tags of a tagged sentence.
tagged_sent -- the tagged sentence (a list of pairs (word, tag)).
"""
def tag(self, sent):
"""Tag a sentence.
sent -- the sentence.
"""
def tag_history(self, h):
"""Tag a history.
h -- the history.
"""
def unknown(self, w):
"""Check if a word is unknown for the model.
w -- the word.
"""
Tests:
$ nosetests tagging/tests/test_memm.py
Documentación:
* Introducción a scikit-learn para clasificación de textos:
* http://scikit-learn.org/stable/tutorial/text_analytics/working_with_text_data.html
* [[http://nbviewer.ipython.org/url/cs.famaf.unc.edu.ar/~francolq/Clasificaci%C3%B3n%20de%20Texto%20con%20scikit-learn.ipynb#|Mi versión del mismo tutorial]]
* Beam inference:
* [[https://class.coursera.org/nlp/lecture/133|video lecture]] (ir al minuto 07:10)
* [[https://d396qusza40orc.cloudfront.net/nlp/slides/04-02-Maximum_Entropy_Sequence_Models-v2.pdf|slides]]
===== Ejercicio 5 (punto bonus): Algoritmo de Viterbi para MEMMs (con Beam) =====
Implementar el algoritmo de Viterbi para obtener la secuencia de tags de máxima probabilidad de acuerdo a un MEMM:
* Para obtener las probabilidades de los tags, usar el método ''predict_proba'' si el clasificador lo tiene (e.g. ''LogisticRegression'' y ''MultinomialNB''). Si no, usar la exponenciación (base 2) del método ''decision_function'' (e.g. ''LinearSVC'').
* Beam: En cada paso del Viterbi, guardar sólo los ''k'' taggings más probables, a donde ''k'' es un parámetro de la clase.
* Evaluar para varios clasificadores (''LogisticRegression'' y ''LinearSVC''), para varios valores de ''n'' (1, 2, 3 y 4), y para varios valores de ''k'' (1, 2 y 3). Reportar los resultados en el README. Reportar también tiempo de evaluación.