Además, esta el problema semántico:
... hay muchísimos problemas abiertos. NLP es complicado, de hecho es AI-complete
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}$.
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.
Nuestro enfoque se basa en:
Características:
Esta compuesto por:
| 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."
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 :( "
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"
@mgraffg pon atención para sacar 10 en http://github.com/INGEOTEC
In [ ]:
lc = text.lower()
print(lc)
@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))
@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))
@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))
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])
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)))
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)))
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)))
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'$
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))
In [ ]:
from microtc.textmodel import TextModel
model = TextModel(docs, token_list=[-1])
print(model[docs[0]])
| 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 |