Representación vectorial de textos (Parte 1)

NLP - Analítica Estratégica de Datos


Fundación Universitaria Konrad Lorenz
Docente: Viviana Márquez vivianam.penama@konradlorenz.edu.co
Clase #5: Marzo 11, 2021

Codeshare: https://codeshare.io/2BNvnL

Retroalimentación Taller 3

⌛ En la clase anterior

  • Adquisición de datos
    • Web Scraping
  • Herramientas generales de limpieza de texto
    • ASCII, Unicode
    • RegEx
    • Otros métodos útiles en Python
    • Traducción

Flujo de datos en un proyecto de NLP (pipeline)


Flujo de datos en un proyecto de NLP (pipeline) --- En clases anteriores


Flujo de datos en un proyecto de NLP (pipeline) --- En clases anteriores


Flujo de datos en un proyecto de NLP (pipeline) --- Hoy


🚀 Hoy veremos...

  • Herramientas específicas de pre-procesamiento de texto en NLP
    • Palabras vacías
    • Tokenización
    • Stemming
    • Lematización
    • Etiquetado gramatical
  • Repaso de Feature Engineering en Machine Learning
  • Representación de datos en forma numérica
  • Espacio semántico vectorial
  • Métodos de vectorización
    • One-Hot Encoding
    • Bag of Words
    • Bag of N-Grams

Herramientas específicas de

pre-procesamiento de texto en NLP

Tokenización

Tokenización (Analizador léxico) es el proceso de convertir una secuencia de caracteres en una secuencia de tokens (componentes léxicos)

Léxico: Conjunto de palabras que conforma un determinado lecto. (Ósea, el vocabulario)

In [15]:
import re

texto = "¡Hola! ¿Cómo estás? Este es mi número: 555-777-888"
texto = texto.lower() # Minúscula 
texto = re.sub(r"[\W\d]", " ", texto) # Dejar sólo letras
texto = texto.strip()
texto = texto.split() # Tokenizar
texto
Out[15]:
['hola', 'cómo', 'estás', 'este', 'es', 'mi', 'número']

Palabras vacías (Stopwords)

In [19]:
# ¿Problemas en la instalación?
import nltk
nltk.download('stopwords') # Esta línea se corre sólo la primera vez

from nltk.corpus import stopwords
stopwords_sp = stopwords.words('spanish')
[nltk_data] Downloading package stopwords to
[nltk_data]     /Users/vivianamarquez/nltk_data...
[nltk_data]   Package stopwords is already up-to-date!
In [21]:
texto = [palabra for palabra in texto if palabra not in stopwords_sp]
texto
Out[21]:
['hola', 'cómo', 'número']

Stemming

Stemming es el proceso de remover los sufijos para reducir una palabra a su raíz para que todas sus variantes sean representadas de la misma manera.

Los algoritmos más populares son:

  • Porter
  • Snowball

Aunque muchas veces el proceso de stemming lingüísticamente no produce la forma correcta base de la palabra, se usa frecuentemente en los motores de busqueda para traer los resultados relevantes.

In [22]:
from nltk.stem.snowball import SnowballStemmer
englishStemmer = SnowballStemmer("english")
spanishStemmer = SnowballStemmer("spanish")

🚗

In [23]:
englishStemmer.stem("car")
Out[23]:
'car'
In [24]:
englishStemmer.stem("cars")
Out[24]:
'car'

🎤

In [25]:
spanishStemmer.stem("cantar")
Out[25]:
'cant'
In [26]:
spanishStemmer.stem("cantamos")
Out[26]:
'cant'

Nuestro texto

In [27]:
texto
Out[27]:
['hola', 'cómo', 'número']
In [28]:
texto_stem = []

for palabra in texto:
    raiz = spanishStemmer.stem(palabra)
    texto_stem.append(raiz)
    
texto_stem
Out[28]:
['hol', 'com', 'numer']

Lematización

Lematización es el proceso de hallar el lema de una palabra (representante de todas las formas de una misma palabra).

  • ¡No es lo mismo que stemming!

En lingüística, el lema es una unidad autónoma constituyente del léxico de un idioma.

In [30]:
import nltk 
from nltk.stem import WordNetLemmatizer
nltk.download('wordnet') # Sólo se corre la primera vez

lemmatizer = WordNetLemmatizer()

lemmatizer.lemmatize("dancing", pos='v')
[nltk_data] Downloading package wordnet to
[nltk_data]     /Users/vivianamarquez/nltk_data...
[nltk_data]   Package wordnet is already up-to-date!
Out[30]:
'dance'
In [31]:
englishStemmer.stem('dancing')
Out[31]:
'danc'

• El stemming funciona con palabras sin conocer su contexto y es por eso que la raíz tiene una precisión menor, pero, es más rápida que la lematización.

• La lematización devuelve una palabra real.

• Si no importa el nivel de precisión y todo lo que necesitas es velocidad el stemming es mejor.

👮‍♀️ Check Point

https://pollev.com/vivianamarqu288

Etiquetado gramatical

  • POST: Part-of-speech tagging
  • Etiquetar cada palabra según su parte de la oración

https://pollev.com/vivianamarqu288

  • Sustantivo: Jessica

  • Adjetivo: Bella

  • Artículo: La

  • Pronombre: Ella

  • Verbo: Cantar

  • Adverbio: Rápidamente

  • Interjección: ¡Ay!

  • Preposición: Entre

  • Conjunción: y

Instalar librerias:

  • pip install spacy
  • python -m spacy download es_core_news_sm
In [41]:
import es_core_news_sm

nlp = es_core_news_sm.load()
doc = nlp("¡Hola! ¿Cómo estás? Este es mi número 314-1592. ¡Vamos a bailar porque es juernes!")
[(palabra.text, palabra.pos_) for palabra in doc]
Out[41]:
[('¡', 'INTJ'),
 ('Hola', 'INTJ'),
 ('!', 'PUNCT'),
 ('¿', 'PUNCT'),
 ('Cómo', 'PRON'),
 ('estás', 'VERB'),
 ('?', 'PUNCT'),
 ('Este', 'PRON'),
 ('es', 'AUX'),
 ('mi', 'DET'),
 ('número', 'NOUN'),
 ('314-1592', 'NUM'),
 ('.', 'PUNCT'),
 ('¡', 'PUNCT'),
 ('Vamos', 'AUX'),
 ('a', 'ADP'),
 ('bailar', 'VERB'),
 ('porque', 'SCONJ'),
 ('es', 'AUX'),
 ('juernes', 'NOUN'),
 ('!', 'PUNCT')]
In [44]:
from spacy import displacy 

displacy.render(doc, style="dep", jupyter=True, options = {'distance': 200})
¡ INTJ Hola! ¿ INTJ Cómo PRON estás? VERB Este PRON es AUX mi DET número NOUN 314-1592. ¡ NUM Vamos AUX a ADP bailar VERB porque SCONJ es AUX juernes! NOUN punct obl nsubj cop det appos aux mark mark cop advcl

Flujo de datos en un proyecto de NLP (pipeline)


✅ Limpieza y Pre-Procesamineto de Texto

  • Herramientas generales de limpieza de texto

    • ASCII, Unicode
    • RegEx
    • Traducción
    • Otros métodos útiles en Python
  • Herramientas específicas de pre-procesamiento de texto en NLP

    • Palabras vacías
    • Tokenización
    • Stemming
    • Lematización
    • Etiquetado gramatical

Flujo de datos en un proyecto de NLP (pipeline) --- Hoy


Feature Engineering (1) (2)


  • Es el proceso de usar el conocimiento del dominio para crear atributos que sirvan para entrenar un modelo de aprendizaje automático.
  • "Garbage in, garbage out" -- Malos atributos, malos resultados.
  • Podemos cambiar de un sistema cartesiano $(x,y)$ a un sistema polar $(r,\theta)$ con una simple transformación de coordenadas: $$r = \sqrt{x^2 + y^2} \Rightarrow \theta = \tan^{-1} \left(\dfrac{y}{x}\right)$$
  • Ahora vemos que es fácil dividir el conjunto usando $r=2$

Otros ejemplos de Feature Engineering

  • Atributos categóricos-- Ejemplo: Género, edad (cubetas), raza(variable ficticia)
  • Datos continuos
  • Valores faltantes y valores atípicos
  • Normalización
  • Fechas
  • Feature engineering para NLP

Feature Engineering para NLP

Representación de datos en forma numérica

Ejemplo: Imágenes

  • Una imagen es representada en un computador en la forma de una matriz donde cada $celda[i,j]$ representa el píxel $i,j$ de la imagen.

  • De manera similar, un video es una colección de fotogramas, donde cada fotograma es una imágen. Por lo tanto, cualquier video puede ser representado como una colección de matrices.

  • (Des)afortunadamente, representar texto de manera numérica no es tan sencillo.

Espacio semántico vectorial

  • Las redes neuronales pueden hacer que las máquinas entiendan las analogías como los humanos [Mikolov et al., 2013]
In [2]:
%%time

#Libraries
import sys
import numpy as np
import re

#Useful functions
def load_glove(filename):
    dic = {}
    with open(filename) as f:
        for line in f:
            vec = line.split()
            dic[vec[0]]= np.array(vec[1:], dtype=float)
    return dic

def analogies(gloves, x, y, z, n):
    dif_1 = gloves[x] - gloves[y]
    distances=[]
    for key,val in gloves.items():
        if z!=key:
            dif_2 = gloves[z]-gloves[key]
            distances.append((np.linalg.norm(dif_1-dif_2),key))
    distances.sort()
    return [d[1] for d in distances[0:n]]

gloves = load_glove("../archivos/glove.6B.300d.txt")
CPU times: user 25.9 s, sys: 865 ms, total: 26.8 s
Wall time: 26.8 s
In [3]:
def print_analogy():
    print("Enter a word or 'x:y as z:'")
    cmd = input("> ")

    while cmd!=None:
        try:  
            match = re.search(r'(\w+):(\w+) as (\w+):', cmd)
            x = match.group(1).lower()
            y = match.group(2).lower()
            z = match.group(3).lower()

            words = analogies(gloves, x, y, z, 5)

            print("%s is to %s as %s is to {%s}" % (x,y,z,' '.join(words)))
            cmd = input("> ")
        except:
            print("Bye mis cielas!")
            break
In [45]:
print_analogy()
Enter a word or 'x:y as z:'
> man:king as woman:
man is to king as woman is to {king queen monarch mother princess}
> bogota:colombia as caracas:
bogota is to colombia as caracas is to {venezuela colombia bolivia ecuador peru}
> exit()
Bye mis cielas!

¿Qué hay bajo el capó?


  • Imaginemos que todas las palabras y sus significados viven en un espacio de altas dimensiones. Nosotros lo llamáremos espacio semántico vectorial

  • Cada dimensión en el espacio semántico vectorial representa algún aspecto del significado de la palabra

  • Los conceptos y las palabras que significan cosas similares deben vivir cerca en este espacio

¿Cómo sabemos qué significa una palabra?


Nosotros aprendemos a través de la experiencia


Las máquinas también aprenden a través de la experiencia


¿Qué hay bajo el capó? Representación numérica de textos


¡Matemáticas!
</big></b>


  • significado($man$) - significado($king$) + significado($queen$) = significado($woman$)

Representación vectorial de textos

  • Existen varios métodos
  • Lo que diferencia un método del otro es qué tan bien captura las propiedades lingüísticas del texto que representa y la cantidad de espacio que ocupa en memoria

  • Métodos más populares:
    • One-Hot Encoding
    • Bag of Words (Bolsa de palabras)
    • Bag of N-Grams (Bolsa de n-gramas)
    • TF-IDF
    • Word embeddings (word2vec)
      • CBOW (Bolsa de palabras continua)
      • SkipGram

(Paréntesis)

Corpus lingüístico: Conjunto amplio y estructurado de ejemplos reales de uso de la lengua.

🛠️ One-Hot Encoding

Mapear cada palabra en el vocabulario del corpus de texto a una identificación única

In [47]:
import pandas as pd 
import numpy as np

corpus = {'D1': 'perro muerde hombre',
          'D2': 'hombre muerde perro',
          'D3': 'perro come carne',
          'D4': 'hombre come comida'}

corpus = pd.DataFrame.from_dict(corpus, orient='index', columns=['texto'])
corpus
Out[47]:
texto
D1 perro muerde hombre
D2 hombre muerde perro
D3 perro come carne
D4 hombre come comida
In [68]:
# Obtener el vocabulario
vocabulario = corpus['texto'].str.cat(sep=" ")
vocabulario = set(vocabulario.split())
vocabulario = [(palabra,i) for i,palabra in enumerate(vocabulario)]
print(vocabulario)
[('carne', 0), ('perro', 1), ('muerde', 2), ('comida', 3), ('come', 4), ('hombre', 5)]
In [74]:
# One-Hot Encoding
vocab_onehot = np.array([[0]*len(vocabulario)]*len(vocabulario))

for palabra,i in vocabulario:
    vocab_onehot[i,i] = 1
    # print(f"Palabra: {palabra}")
    # print(f"One-hot encoding: {vocab_onehot[i,:]}")
    # print()
    
vocab_onehot = pd.DataFrame(vocab_onehot)
vocab_onehot.columns = [palabra[0] for palabra in vocabulario]
vocab_onehot.index = [palabra[0] for palabra in vocabulario]
vocab_onehot
Out[74]:
carne perro muerde comida come hombre
carne 1 0 0 0 0 0
perro 0 1 0 0 0 0
muerde 0 0 1 0 0 0
comida 0 0 0 1 0 0
come 0 0 0 0 1 0
hombre 0 0 0 0 0 1
In [75]:
def one_hot_encoder(frase):
    frase_onehot = []
    for palabra in frase.split():
        frase_onehot.append(vocab_onehot[palabra].tolist())
    print(frase_onehot)
In [84]:
one_hot_encoder("perro muerde hombre perro")
[[0, 1, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 1], [0, 1, 0, 0, 0, 0]]

Ventajas

  • Es intuitivo y fácil de entender
  • La implementación es directa

Desventajas

  • Genera una matriz dispersa
  • El vector de cada frase no tiene un tamaño constante
  • No tiene noción de similitud entre palabras
  • Problema de fuera de vocabulario

🛠️ Bag of Words (BoW) -- Bolsa de Palabras

  • Representar el texto como una bolsa de palabras (ignorando orden y contexto)
  • Si dos piezas de texto tienen casi las mismas palabras, entonces pertenecen a la misma bolsa
In [85]:
print(vocabulario) # sigue siendo el mismo
[('carne', 0), ('perro', 1), ('muerde', 2), ('comida', 3), ('come', 4), ('hombre', 5)]
In [89]:
def bow(frase):
    frase_bow = [0]*len(vocabulario)
    for palabra,i in vocabulario:
        if palabra in frase.split():
            frase_bow[i] = 1
    print(frase_bow)
In [90]:
bow("perro muerde hombre")
[0, 1, 1, 0, 0, 1]
In [91]:
bow("perro muerde hombre")
[0, 1, 1, 0, 0, 1]
In [101]:
one_hot_encoder("perro muerde hombre")

np.sum(np.array([[0, 1, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 1]]),0)
[[0, 1, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 1]]
Out[101]:
array([0, 1, 1, 0, 0, 1])

Scikit-learn

  • Es una biblioteca para aprendizaje automático de software libre para el lenguaje de programación Python
In [133]:
from sklearn.feature_extraction.text import CountVectorizer

count_vect = CountVectorizer(binary=True)
bow_rep = count_vect.fit_transform(corpus.texto.values)

print("Vocabulario: ", count_vect.vocabulary_)

# print(corpus.iloc[0])
# print(bow_rep[0].toarray())

corpus['bow'] = [row for row in bow_rep.toarray()]
corpus
Vocabulario:  {'perro': 5, 'muerde': 4, 'hombre': 3, 'come': 1, 'carne': 0, 'comida': 2}
Out[133]:
texto bow
D1 perro muerde hombre [0, 0, 0, 1, 1, 1]
D2 hombre muerde perro [0, 0, 0, 1, 1, 1]
D3 perro come carne [1, 1, 0, 0, 0, 1]
D4 hombre come comida [0, 1, 1, 1, 0, 0]
In [ ]:
 

Si no nos importa la frecuencia, binary=True

In [135]:
ej = ["perro y perro son amigos perros"]
temp = count_vect.transform(ej)
print(count_vect.vocabulary_)
temp.toarray()
{'perro': 5, 'muerde': 4, 'hombre': 3, 'come': 1, 'carne': 0, 'comida': 2}
Out[135]:
array([[0, 0, 0, 0, 0, 1]])

Ventajas

  • Es intuitivo y fácil de entender
  • La implementación es directa
  • El vector de cada frase tiene un tamaño constante

Desventajas

  • Genera una matriz dispersa (¿solución?)
  • No tiene noción de similitud entre palabras
  • Problema de fuera de vocabulario
  • Se pierde el orden de la información

Método bastante usado en la industria

🛠️ Bag of N-Grams (BoN) -- Bolsa de n-gramas


https://pollev.com/vivianamarqu288

  • Hasta el momento solo hemos visto las palabras como unidades independientes
  • Con la bolsa de n-gramas capturamos un poco de contexto y orden
In [136]:
corpus
Out[136]:
texto bow
D1 perro muerde hombre [0, 0, 0, 1, 1, 1]
D2 hombre muerde perro [0, 0, 0, 1, 1, 1]
D3 perro come carne [1, 1, 0, 0, 0, 1]
D4 hombre come comida [0, 1, 1, 1, 0, 0]
In [140]:
count_vect = CountVectorizer(ngram_range=(1,2))
bow_rep = count_vect.fit_transform(corpus.texto.values)
print("Vocabulario: ", count_vect.vocabulary_)
print("Representación bow 'perro muerde hombre'", bow_rep[0].toarray())
Vocabulario:  {'perro': 11, 'muerde': 8, 'hombre': 5, 'perro muerde': 13, 'muerde hombre': 9, 'hombre muerde': 7, 'muerde perro': 10, 'come': 1, 'carne': 0, 'perro come': 12, 'come carne': 2, 'comida': 4, 'hombre come': 6, 'come comida': 3}
Representación bow 'perro muerde hombre' [[0 0 0 0 0 1 0 0 1 1 0 1 0 1]]

Ventajas

  • Captura alguna información sobre el contexto y orden

Desventajas

  • Genera una matriz dispersa ¡rápidamente!
  • Problema de fuera de vocabulario

Más sobre n-gramas

🤓 Recapitulando: Hoy aprendímos...

  • Herramientas específicas de pre-procesamiento de texto en NLP
    • Palabras vacías
    • Tokenización
    • Stemming
    • Lematización
    • Etiquetado gramatical
  • Repaso de Feature Engineering en Machine Learning
  • Representación de datos en forma numérica
  • Espacio semántico vectorial
  • Métodos de vectorización
    • One-Hot Encoding
    • Bag of Words
    • Bag of N-Grams

🚧 ✋ Expectativas del Proyecto Final

Modo:

  • Máximo 3 personas por grupo
  • Exposición ~10 min y repositorio de GitHub
  • ABRIL 15 - Plan de Proyecto (Documento con descripción del proyecto)
  • JUNIO 3 - Entrega Proyecto
  • JUNIO 3 y 10 - Exposiciones (La fecha puede cambiar)

Proyecto:

  • Los datos pueden ser personales, del internet, o de su empresa (pedir permiso).
  • Tener objetivo claro
  • Pre-procesamiento
  • Modelo de NLP/Machine Learning
  • Visualización

Ideas para conseguir datos:

¡Tiempo de taller!

Taller # 4: Representación vectorial de textos (Parte 1)

Fecha de entrega: Marzo 18, 2021. (Antes del inicio de la próxima clase)

Proxima clase(s): Representación vectorial de textos (Parte 2)