#!/usr/bin/env python # coding: utf-8 # # Etiquetado de Secuencias con Feature Forge # # - http://feature-forge.readthedocs.org/en/latest/ # # # ## Historias # # - [Definición de *history* (p. 4)](http://www.cs.columbia.edu/~mcollins/fall2014-loglineartaggers.pdf) (notas de Michael Collins sobre MEMMs) # # Para etiquetar una secuencia reducimos este problema a un problema de clasificación. Una historia es una instancia de clasificación, que es una tupla con los siguientes elementos: # # - `sent`: La oración entera. # - `prev_tags`: Una tupla con las `n` etiquetas anteriores # - `i`: El índice de la palabra a etiquetar. # In[1]: from collections import namedtuple History = namedtuple('History', 'sent prev_tags i') # Por ejemplo, para la oración "El gato come pescado ." con `n = 2` tenemos las siguientes historias válidas: # In[2]: sent = 'El gato come pescado .'.split() histories = [ History(sent, ('', ''), 0), History(sent, ('', 'D'), 1), History(sent, ('D', 'N'), 2), History(sent, ('N', 'V'), 3), History(sent, ('V', 'N'), 4) ] histories # ## Features básicos # # - http://feature-forge.readthedocs.org/en/latest/feature_definition.html#the-basics # # Para cada instancia de clasificación (historias) debemos calcular un conjunto de características relevantes (*features*) para la clasificación. # # Por ejemplo, podemos usar como feature la palabra a etiquetar en minúsculas (un feature de tipo string): # In[3]: def word_lower(h): """Feature: current lowercased word. h -- a history. """ sent, i = h.sent, h.i return sent[i].lower() [word_lower(h) for h in histories] # También podemos usar como feature si la palabra comienza con mayúsculas o no (un feature de tipo booleano): # In[4]: def word_istitle(h): """Feature: is the current word titlecased? h -- a history. """ sent, i = h.sent, h.i return sent[i].istitle() [word_istitle(h) for h in histories] # ## Vectorización # # - http://feature-forge.readthedocs.org/en/latest/feature_evaluation.html#basic-usage # # Para usar los features que definimos debemos construir un vectorizador, que convierta los valores de los features en entradas en una matriz: # In[5]: from featureforge.vectorizer import Vectorizer features = [word_lower, word_istitle] vect = Vectorizer(features) # Como cualquier componente de scikit-learn, el vectorizador debe entrenarse para definir los features concretos y el mapeo a columnas de una matriz: # In[6]: vect.fit(histories) # Luego podemos vectorizar cualquier historia, como por ejemplo la que corresponde a etiquetar la primer palabra de la oración "Come salmón el mormón .". # In[7]: h = History('Come salmón el mormón .'.split(), ('', ''), 0) m = vect.transform([h]) m.toarray() # Los features activos son el de la columna 0 y el de la columna 3. Podemos ver qué significa cada uno de ellos: # In[8]: print(vect.column_to_feature(0)) print(vect.column_to_feature(0)[0]._name) print(vect.column_to_feature(5)) print(vect.column_to_feature(3)[0]._name, vect.column_to_feature(3)[1]) # La columna 0 indica que la palabra comienza en mayúsculas, mientras que la columna 3 indica que la palabra en minúsculas es "come". # ## Features Paramétricos # # - http://feature-forge.readthedocs.org/en/latest/feature_definition.html#advanced-feature-definition # # Algunos features pueden tener parámetros que permitan diferentes instanciaciones. Estos features se pueden definir como subclases de la clase `Feature`. Por ejemplo, podemos definir un feature booleano que indica si una palabra es más larga que un número dado: # In[9]: from featureforge.feature import Feature class WordLongerThan(Feature): def __init__(self, n): self.n = n self._name = 'word_longer_than_{}'.format(n) def _evaluate(self, h): """Feature: is the current word longer than n? h -- a history. """ sent, i = h.sent, h.i return len(sent[i]) > self.n # Una vez definido, podemos instanciar el feature con un valor particular: # In[10]: word_longer_than_two = WordLongerThan(2) # Luego, podemos usarlo como un feature normal, por ejemplo evaluándolo sobre las historias o incluyéndolo en un vectorizador. Por ejemplo, lo evaluamos para las historias correspondientes a la oración "El gato come pescado.": # In[11]: list(zip('El gato come pescado .'.split(), [word_longer_than_two(h) for h in histories])) # También podemos agregar el feature al vectorizador: # In[12]: features = [word_lower, word_istitle, word_longer_than_two] vect = Vectorizer(features) vect.fit(histories) h = History('Come salmón el mormón .'.split(), ('', ''), 0) m = vect.transform([h]) m.toarray() # En este caso la segunda columna corresponde al nuevo feature: # In[13]: vect.column_to_feature(1)[0]._name