<jupyter_start><jupyter_text>Clasificación de texto  Un enfoque basado en $\mu TC$ **Seminario de la Sociedad Matemática Mexicana SMM 2016**    <NAME> INFOTEC             CONACyT -- INFOTEC  Agenda - ¿Qué es $\mu TC$- ¿En qué consiste la tarea de clasificación de texto?- ¿Cómo esta compuesto $\mu TC$?- Estado del arte- Cómo se compara $\mu TC$ con el estado del arte- Qué falta en $\mu TC$- Ejemplos  Categorización de texto El problema consiste en, dado un texto $d$, determinar la(s) categoría(s) a la que pertenece en un conjunto $C$ de categorias, previamente conocido.Más formalmente:Dado un conjunto de categorias $\cal{C} = \{c_1, ..., c_m\}$, determinar el subconjunto de categorias$C_d \in \wp(\cal{C})$ a las que pertenece $d$. Notese que $C_t$ puede ser vacio o $\cal{C}$.  Clasificación de texto La _clasificación_ de texto es una especialización del problema de categorización, donde $|C_d| = 1$, esto es $d$ solo puede ser asignado a una categoría.Es un problema de interés en la industria y la acádemia, con aplicaciones variadas a distintas áreas del conocimiento.- Análisis de sentimiento- Determinación de autoría, e.g., género, edad, estilo, etc.- Detección de spam- Categorización de noticias- Clasificación de idioma  Procesamiento de Lenguaje Natural Un documento $d=s_1\cdots s_n$ es simplemente la concatenación de símbolos $s \in \Sigma$. Donde, $\Sigma$ es un _alfabeto_ de tamaño $\sigma = |\Sigma|$Notese qué el número de textos posibles de tamaño $n$ es $\sigma^n$, por ejemplo, limitados a texto en inglés en Twitter se tienen    $$ 26^{140} \simeq 1.248 \times 10^{198} $$Sin emabargo, en lenguaje natural, este número no suele ser tan grande:  - existen reglas sobre que símbolos se pueden unir  - más aún, hay noción de _terminos_ o _palabras_, i.e., _morfología_  - también, hay reglas sobre como las palabras se pueden combinar, i.e., _sintaxis y gramática_Sin embargo, es un problema sumamente complicado, hay muchas reglas, variantes, excepciones, errores, etc.Y por si fuera poco, aunque los conceptos existen en esencia, aparecen de manera diferente en todos los lenguajes Además, esta el problema semántico:- un término $s_i$ tiene significados diferentes (antónimos)- lo contrario también existe, $s_i \not= s_j$ pero que son idénticos en significado (sinónimos)- en ambos casos, el significado preciso depende del contexto- también hay casos _aproximados_ de todo lo anterior- ironias, sarcamos, etc.... hay muchísimos problemas abiertos. **NLP** es complicado, de hecho es _AI-complete_  Nuestro Enfoque Por su complejidad, trabajar en NLP tiene una gran cantidad de problemas abiertos, en particular nosotros nos enfocamos en la clasificación de texto escrito de manera informal (e.g., Twitter).Para esto se utiliza un _pipeline_ estándar![Pipeline](fig-pipeline.png)No es la única opción, pero fijar el pipeline es útil como ya se expondrá. El enfoque teórico suele ser muy complicado, y en realidad poco efectivo en la práctica, dadas las simplificaciones necesarias para hacerlo manejable- Lógica- Lingüistica- SemánticaEl enfoque práctico supone muchas cosas, en particular es un tanto casuístico:    - Se fija el lenguaje- Se fija el problema- Y la raíz de todos los males, muchas veces se supone que entre más técnicas sofísticadas se usen, mejores resultados se tendránEn ambos enfoques se suele suponer que ausencia de errores de diferentes fuentes, sin embargo, es la regla cuando el texto que se analiza fue generado por usuarios de una red social, e.g. Twitter.  ¿Qué es $\mu TC$? micro TC o $\mu TC$ es un clasificador de texto desarrollado enel _Laboratorio de Análisis Computacional de Grandes Cúmulos de Información_(o _Laboratorio de BigDATA_) de INFOTEC, sede Aguascalientes.Esta disponible para ser clonado en [https://github.com/INGEOTEC/microTC](https://github.com/INGEOTEC/microTC). Esta escrito en Python 3.5 para sacar ventaja de unicode. También se puede instalar utilizando `pip` y `conda`. En particular, nuestro enfoque se basa en _aprendizaje computacional_ y _optimización combinatoria_. Hemos probado que este esquema es muy competitivo en la práctica. Además, con la adecuada selección de las funciones podemos lograr que $\mu TC$ se independiente del lenguaje y robusto a errores.Esta compuesto por:- una serie de funciones de transformación de texto- una serie de tokenizadores- filtros de palabras y- algoritmos de pesado de términosTodo esto orquestado mediante un algoritmo de optimización combinatoria  Lista de parametros  Normalizadores multilenguaje |   nombre  | valores             |        descripción                   ||-----------|---------------------|--------------------------------------||	del-punc | yes, no | Determina si las puntuaciones deben removerse ||	del-d1   | yes, no | Determina si se deben borrar letras repetidas ||	del-diac | yes, no | Determina si los simbolos que no ocupan espacios deben ser removidos ||	lc       | yes, no | Determina si los símbolos deben ser normalizados en minúsculas ||	emo      | remove, group, none | Controla como deben tratarse los emoticones ||	num      | remove, group, none | `........................` números ||	url      | remove, group, none | `........................` urls ||	usr      | remove, group, none | `........................` usuarios |configuraciones: 1296  Normalizadores dependientes del lenguaje |   nombre  | valores             |        descripción                   ||-----------|---------------------|--------------------------------------||	stem   | yes, no | Determina si a las palabras se les aplica _stemming_. ||	neg    | yes, no | Determina si los operadores de negación son manejados de manera especial ||	sw | remove, group, none | Controla como los _stopwords_ son manejados |configuraciones: 12  Tokenizadores Los tokenizadores son en realidad una lista de tokenizadores, y están definidos tokenizer un elemento en $\wp{(\text{n-words} \cup \text{q-grams} \cup \text{skip-grams})} \setminus \{\emptyset\}$|   nombre  | valores             |        descripción                   ||-----------|---------------------|--------------------------------------||	n-words    | $\{1,2,3\}$      | Longitud de n-gramas de palabras (n-words) ||	q-grams  | $\{1,2,3,4,5,6,7\}$ | Longitud de q-gramas de caracteres) ||	skip-grams  | $\{(2,1), (3, 1), (2, 2), (3, 2)\}$ | Lista de skip-grams|configuraciones: 16383  Parametros para pesado |   nombre  | valores             |        descripción                   ||-----------|---------------------|--------------------------------------||token_min_filter | $\{0.01, 0.03, 0.1, 0.30, -1, -5, -10\}$ | Filtro de frequencias bajas ||token_max_filter | $\{0.9, 99, 1.0\}$ | Filtro de frequencias altas ||	tfidf    | yes, no | Determina si se debe realizar un pesado TFIDF de terminos |configuraciones = 42<jupyter_code>conf = 42 * 16383 * 12 * 1292
time = conf * 10 / 60 / 24 / 365.25
print(conf, time)<jupyter_output>10668085344 202830.73511293635<jupyter_text>Con esto tenemos un total de $10,668,085,344$ configuraciones. Dependiendo del tamaño de cada colección, cada configuración se evalua en tiempo diferente.Una tarea típica de análisis de sentimientos tiene un costo por configuración de cerca de 10 min. en una computadora de relativamente nueva, i.e., Intel(R) Xeon(R) CPU E5-2640 v3 @ 2.60GHz.Esto da un total de $202,830.74$ años de tiempo de cómputo.Un enfoque _naïve_ requiere una cantidad enorme de computadoras para parallelizar y distribuir este proceso, por esta razón, es mejor utilizar algoritmos eficientes para optimizar la búsqueda de la mejor configuración<jupyter_code>from sympy import symbols, sin, cos, exp, tan, sqrt
from sympy.plotting import plot, plot3d
from matplotlib import pyplot
x, y = symbols('x y')
plot(-x**2+200, axis=False, show=False).save("opt-1a.png")
plot(sin(x)*sin(1/(x*x)), axis=False, show=False).save('opt-1b.png')
plot3d((-x**2 - y**2, (x, -5, 5), (y, -5, 5)), axis=False, show=False).save('opt-2a.png')
plot3d(sin(x*y), axis=False, show=False).save('opt-2b.png')<jupyter_output><empty_output><jupyter_text>Optimización combinatoria Para tener algo práctico utilizamos una aproximación a encontrar la configuración óptima, en a lo más unas cuantas horas.Para esto necesitamos un espacio de búsqueda (dominio) y una función de aptitud.El espacio de búsqueda son todas las configuraciones.La función de aptitud es una médida de _que tan atinado_ es nuestro clasificador. Deseamos seleccionar la configuración que maximice la función de aptitud. Notesé que:- El espacio de configuración no esta en $\mathbb{R}^n$- En la configuración/parámetros son una mezcla de reales y categóricos- No hay coordenadas como talAfortunadamente- Se pueden definir vecindades entre configuraciones- Se puede construir una gráfica- Se puede samplear el espacio- Se puede definir una función de aptitud de nodos  Maximizando un objetivo (ejemplo) |fácil|un poco más complicado||-|-|||||||  Resultados Experimentales ![datasets](descripcion-datasets.png) ![performance](performance-competencias.png) ![performance 2](performance2.png) ![performance 3](performance3.png)  Gracias por su atención  ¿Comentarios y preguntas? - Para más información sobre el _Posgrado en Ciencias en Ciencia de Datos_ aquí en Aguascalientes: - Esta presentación la puedes obtener en: - $\mu TC$ lo puedes conseguir en: - Página de INFOTEC   Slides de soporte   Sobre el pesado El pesado de tokens esta fijo a TFIDF. Su nombre viene de la formulación $tf \times idf$$tf$ es _term frequency_; es una medida de importancia **local** del término $t$ en el documento $d$, de manera normalizada esta definida como:    $$tf(t,d) = \frac{freq(t, d)}{\max_{w \in d}{freq(w, d)}}$$entre más veces aparece en el documento $d$, $t$ es más importante$idf$ quiere decir _inverse document frequency_; es una medida **global** a la colección $D$, esta definida como:$$ idf(t,d) = log{\frac{|D|}{1+|{d \in D: t \in d}|}} $$entre más veces aparece $t$ en la colección, el término es más común y menos discriminante; por lo tanto, menos importante  Sobre el clasificador El clasificador es un algoritmo de aprendizaje computacional que dado un objeto, decide finalmente la etiqueta o clase de ese objeto. Tiene dos etapas bien definidas- **Entrenamiento.** Dado un conjunto de ejemplos en un espacio vectorial, con etiquetas, el algoritmo intenta _aprender_ las características que definen cada clase- **Predicción.** La idea es que una vez entrenado, el algoritmo puede recibir objetos no vistos durante la etapa de entrenamiento y asignales la clase adecuadaEn particular, esta fijo como un _Support Vector Machine_ (SVM) con kernel lineal  ¿Por qué es robusto a errores?Considere los siguientes textos $T=I\_like\_vanilla$, $T' = I\_lik3\_vanila$Para fijar ideas pongamos que se usar el coeficiente de Jaccard como medida de similitud, i.e.$$\frac{|\{{I, like, vanilla}\} \cap \{{I, lik3, vanila}\}|}{|\{{I, like, vanilla}\} \cup \{{I, lik3, vanila}\}|} = 0.2$$$$Q^T_3 = \{ I\_l, \_li, lik, ike, ke\_, e\_v, \_va, van, ani, nil, ill, lla \}$$$$Q^{T'}_3 = \{ I\_l, \_li, lik, ik3, k3\_, 3\_v, \_va, van, ani, nil, ila \}$$Bajo la misma medida$$\frac{|Q^T_3 \cap Q^{T'}_3|}{|Q^T_3 \cup Q^{T'}_3|} = 0.448.$$Se puede ver que estos conjuntos son más similares que los tokenizados por palabraLa idea es que un algoritmo de aprendizaje tenga un poco más de soporte para determinar que $T$ es parecido a $T'$<jupyter_code>def emoformat(A, emo):
    s = "  ".join([a[0] for a in A if a[1] == emo])
    return s[:1000] + "..."
    
with open('microTC/microtc/resources/emoticons.json') as f:
    A = list(json.load(f).items())
    A.sort()
    S = dict(
        pos=emoformat(A, '_pos'),
        neg=emoformat(A, '_neg'),
        neu=emoformat(A, '_neu'),
        none=emoformat(A, '_none'),
    )

output = ["## Emoticones y emojis clasificados por sentimiento ##"]
for k, v in S.items():
    output.append("## Clase `{0}` ##".format(k.upper()))
    output.append(v)

Markdown("\n".join(output))
from itertools import combinations
text = "qué buena esta la plática"
tokenizers = [1, 2, 3, -1, -2, (2,1), (2,2)]

num = 1
output = ["""

## Tokenizadores Ejemplos ##
La codificación es:

- entero positivo: q-grama de caracter
- entero engativo: w-word (n-grama de palabra)
- tupla: skip-gram (tamaño, salto)

"""]
for ltokens in range(len(tokenizers)):
    output.append('## Combinaciones de tamaño {0} ##'.format(ltokens+1))
    output.append('|id|combinación|tokens|')
    output.append('|--|-----------|------|')
    for comb in combinations(tokenizers, ltokens+1):
        model = TextModel([], token_list=comb)
        output.append("|{0}|{1}|{2}|".format(num, comb, ", ".join(model.tokenize(text))))
        num += 1

Markdown("\n".join(output))<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1306084096781771, 'fraction_numerical': 0.01746143606608723, 'mean_word_length': 3.6376180101670297, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 13, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '3', 'dir': 'jupyter-structured-clean-dedup', 'id': '651544', 'license': 'Apache-2.0', 'n_tokens_mistral': 4393, 'n_tokens_neox': 4215, 'n_words': 1587, 'path': 'smm2016.ipynb', 'repo_name': 'INGEOTEC/pres-MSA-SMM2016', 'size': '12788'}