Aprendizaje computacional en grandes volúmenes de texto

Mario Graff (mgraffg@ieee.org, mario.graff@infotec.mx)

Sabino Miranda (sabino.miranda@infotec.mx)

Daniela Moctezuma (dmoctezuma@centrogeo.edu.mx)

Eric S. Tellez (eric.tellez@infotec.mx)

CONACYT, INFOTEC y CentroGEO

https://github.com/ingeotec

  • Representación vectorial del texto
    • Normalización
    • Tokenización (n-words, q-grams, skip-grams)
    • Pesado de texto (TFIDF)
    • Medidas de similitud
  • Aprendizaje supervisado
    • Modelo general de aprendizaje; Entrenamiento, test, score (accuracy, recall, precision, f1)
    • Máquinas de soporte vectorial (SVM)
    • Programación genética (EvoDAG)
    • Distant supervision
  • $\mu$TC
    • Pipeline de transformaciones
    • Optimización de parámetros
    • Clasificadores
    • Uso del $\mu$TC
  • Aplicaciones
    • Análisis de sentimientos
    • Determinación de autoría
    • Clasificación de noticias
    • Spam
    • Género y edad
  • Conclusiones

Procesamiento de Lenguaje Natural (NLP)

  • $d=s_1\cdots s_n$ es un documento donde $s \in \Sigma$, $\Sigma$ es un alfabeto de tamaño $\sigma = |\Sigma|$
  • Twitter tendría: $26^{140} \simeq 1.248 \times 10^{198}$
  • Reglas sobre que símbolos se pueden unir
  • Noción de términos o palabras, i.e., morfología
  • Reglas sobre como las palabras se pueden combinar, i.e., sintaxis y gramática
  • Problema sumamente complicado
    • Reglas
    • Variantes
    • Excepciones
    • Errores
  • Conceptos que 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

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

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

  • Enfoque teórico (muchas simplificaciones)
    • Lógica
    • Lingüistica
    • Semántica
  • Enfoque práctico supone muchas cosas
    • Se fija el lenguaje
    • Se fija el problema
    • Se supone que mientras más técnicas sofísticadas se usen, mejores resultados se tendrán
  • Ambos enfoques suponen ausencia de errores

Nuestro enfoque se basa en:

  • Aprendizaje computacional
  • Optimización combinatoria

Características:

  • Independiente del lenguaje
  • Robusto a errores

Esta compuesto por:

  • Una serie de funciones de transformación de texto
  • Una serie de tokenizadores
  • Filtros de palabras
  • Algoritmos de pesado de términos

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

In [ ]:
from microtc.textmodel import norm_chars
text = "Autoridades de la Ciudad de México aclaran que el equipo del cineasta mexicano no fue asaltado, pero sí una riña ahhh."

diac, dup y punc

Autoridades de la Ciudad de México aclaran que el equipo del cineasta mexicano no fue asaltado, pero sí una riña ahhh.


In [ ]:
diac = norm_chars(text, del_diac=True, del_dup=False, del_punc=False).replace('~', ' ')
Markdown("## diac\n" + diac)

In [ ]:
dup = norm_chars(text, del_diac=False, del_dup=True, del_punc=False).replace('~', ' ')
Markdown("## dup\n" + dup)

In [ ]:
punc = norm_chars(text, del_diac=False, del_dup=False, del_punc=True).replace('~', ' ')
Markdown("## punc\n" + punc)

In [ ]:
from microtc.emoticons import EmoticonClassifier
from microtc.params import OPTION_GROUP, OPTION_DELETE
text = "Hoy es un día feliz :) :) o no :( "

emo

Hoy es un día feliz :) :) o no :(


In [ ]:
emo = EmoticonClassifier()
group = emo.replace(text, OPTION_GROUP)
delete = emo.replace(text, OPTION_DELETE)
Markdown("## delete\n%s\n## group\n%s" % (delete, group))

In [ ]:
from IPython.core.display import Markdown
import re
text = "@mgraffg pon atención para sacar 10 en http://github.com/INGEOTEC"

lc

@mgraffg pon atención para sacar 10 en http://github.com/INGEOTEC


In [ ]:
lc = text.lower()
print(lc)

num

@mgraffg pon atención para sacar 10 en http://github.com/INGEOTEC


In [ ]:
delete = re.sub(r"\d+\.?\d+", "", text)
group = re.sub(r"\d+\.?\d+", "_num", text)
Markdown("## delete\n%s\n## group\n%s" % (delete, group))

url

@mgraffg pon atención para sacar 10 en http://github.com/INGEOTEC


In [ ]:
delete = re.sub(r"https?://\S+", "", text)
group = re.sub(r"https?://\S+", "_url", text)
Markdown("## delete\n%s\n## group\n%s" % (delete, group))

usr

@mgraffg pon atención para sacar 10 en http://github.com/INGEOTEC


In [ ]:
delete = re.sub(r"@\S+", "", text)
group = re.sub(r"@\S+", "_usr", text)
Markdown("## delete\n%s\n## group\n%s" % (delete, group))

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


In [ ]:
from microtc.textmodel import TextModel
text = "que buena esta la platica"
model = TextModel([], token_list=[-1, -2])

n-words

que buena esta la platica


In [ ]:
model = TextModel([], token_list=[-1])
words = model.tokenize(text)
model = TextModel([], token_list=[-2])
biw = model.tokenize(text)
Markdown("## -1\n %s\n## -2\n%s" % (", ".join(words), ", ".join(biw)))

q-grams

que buena esta la platica


In [ ]:
model = TextModel([], token_list=[3])
words = model.tokenize(text)
model = TextModel([], token_list=[4])
biw = model.tokenize(text)
Markdown("## 3\n %s\n## 4\n%s" % (", ".join(words), ", ".join(biw)))

skip-grams

que buena esta la platica


In [ ]:
model = TextModel([], token_list=[(2, 1)])
words = model.tokenize(text)
model = TextModel([], token_list=[(2, 2)])
biw = model.tokenize(text)
Markdown("## (2, 1)\n %s\n## (2, 2)\n%s" % (", ".join(words), ", ".join(biw)))

¿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 palabra

La idea es que un algoritmo de aprendizaje tenga un poco más de soporte para determinar que $T$ es parecido a $T'$

Pesado de texto

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

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


In [ ]:
docs = ["buen dia microtc", "excelente dia", "buenas tardes",
	"las vacas me deprimen", "odio los lunes", "odio el trafico",
	"la computadora", "la mesa", "la ventana"]
l = ["* " + x for x in docs]
Markdown("# Corpus\n" + "\n".join(l))

TFIDF

buen dia microtc


In [ ]:
from microtc.textmodel import TextModel
model = TextModel(docs, token_list=[-1])
print(model[docs[0]])

Opciones de pesado

  • gensim
    • LSI
    • LDA
    • Random projections

Normalizadores dependientes del lenguaje - B4MSA

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

Preguntas

  • Incluir otra normalización
  • Incluir otro tokenizador
  • Incluir otro pesado