fasText es un método, creado por Facebook, para generar representaciones vectoriales de palabras (embeddings). Es similar a Word2Vec pero tiene la característica de modelar no sólo palabras completas sino también subcadenas (subwords). Gracias a esto, y a la noción de "composición", puede construir la representación de una palabra que nunca vio en el entrenamiento, a partir de la combinación de l representaciones de sus partes/subcadenas.
En este notebook entrenaremos representaciones fastText a partir de un corpus en español y posteriormente realizaremos agrupamiento usando la técnica de clustering espectral.
Vamos a instalar fastText de manera nativa usando el repositorio de github y la línea de comandos:
!wget https://github.com/facebookresearch/fastText/archive/v0.9.1.zip
!unzip v0.9.1.zip
%cd fastText-0.9.1
!make
--2021-08-05 12:54:51-- https://github.com/facebookresearch/fastText/archive/v0.9.1.zip Resolviendo github.com (github.com)... 140.82.112.3 Conectando con github.com (github.com)[140.82.112.3]:443... conectado. Petición HTTP enviada, esperando respuesta... 302 Found Ubicación: https://codeload.github.com/facebookresearch/fastText/zip/v0.9.1 [siguiente] --2021-08-05 12:54:52-- https://codeload.github.com/facebookresearch/fastText/zip/v0.9.1 Resolviendo codeload.github.com (codeload.github.com)... 140.82.113.9 Conectando con codeload.github.com (codeload.github.com)[140.82.113.9]:443... conectado. Petición HTTP enviada, esperando respuesta... 200 OK Longitud: no especificado [application/zip] Guardando como: “v0.9.1.zip.1” v0.9.1.zip.1 [ <=> ] 4.13M 781KB/s en 5.5s 2021-08-05 12:54:58 (770 KB/s) - “v0.9.1.zip.1” guardado [4327207] Archive: v0.9.1.zip b5b7d307274ce00ef52198fbc692ed3bd11d9856 replace fastText-0.9.1/.circleci/cmake_test.sh? [y]es, [n]o, [A]ll, [N]one, [r]ename: ^C /home/mijangos/Documentos/NLP/Notebooks/fastText-0.9.1 make: No se hace nada para 'opt'.
Una vez instalado fasText, podemos empezar a entrenar modelos de representaciones vectoriales a partir de un corpus.
Montamos el sistema de archivos de Drive:
#Montamos el contenido de Google Drive
#from google.colab import drive
#drive.mount('/content/drive')
Para entrenar el método de Fasttext, utilizaremos la combinación de dos corpus pequeños: a) el Corpus del Español Mexicano COntemporáneo (CEMC); y b) el corpus paralelo náhuatl-español Axolotl (la parte en español).
El entrenamiento se hace a través de la línea de comandos, se pueden específicar diversos hiperparámetros (Consultar documentación). Dos parámetros necesarios son el archivo input y el archivo output, que generará el entrenamiento.
#Se generan dos archivos: cemc.bin (modelo) y cemc.vec (este último es literalmente un archivo de texto con un vector por línea)
#Se pueden descargar usando el visualizador de archivos (pestaña izq.) fastText-0.9.1/result
!mkdir result
!./fasttext cbow -input /content/drive/My\ Drive/Curso_RIIAA/data/cemcytodo.txt -output result/cemc
terminate called after throwing an instance of 'std::invalid_argument' what(): /content/drive/My Drive/Curso_RIIAA/data/cemcytodo.txt cannot be opened for training! Aborted (core dumped)
Una vez generado el modelo, podemos utilizar estos vectores de diferentes maneras, por ejemplo para una palabra devolver las palabras más cercanas/similares:
!./fasttext nn result/cemc.bin
terminate called after throwing an instance of 'std::invalid_argument' what(): result/cemc.bin has wrong file format! Aborted (core dumped)
Podemos cargar los modelos obtenidos de Fasttext y manipularlos directamente desde python, utilizando el paquete gensim.
# Install / Upgrade Gensim
!pip install --upgrade gensim
Collecting gensim Downloading https://files.pythonhosted.org/packages/40/3d/89b27573f56abcd1b8c9598b240f53c45a3c79aa0924a24588e99716043b/gensim-3.8.0-cp36-cp36m-manylinux1_x86_64.whl (24.2MB) |████████████████████████████████| 24.2MB 69.8MB/s Requirement already satisfied, skipping upgrade: numpy>=1.11.3 in /usr/local/lib/python3.6/dist-packages (from gensim) (1.16.4) Requirement already satisfied, skipping upgrade: smart-open>=1.7.0 in /usr/local/lib/python3.6/dist-packages (from gensim) (1.8.4) Requirement already satisfied, skipping upgrade: six>=1.5.0 in /usr/local/lib/python3.6/dist-packages (from gensim) (1.12.0) Requirement already satisfied, skipping upgrade: scipy>=0.18.1 in /usr/local/lib/python3.6/dist-packages (from gensim) (1.3.1) Requirement already satisfied, skipping upgrade: boto3 in /usr/local/lib/python3.6/dist-packages (from smart-open>=1.7.0->gensim) (1.9.205) Requirement already satisfied, skipping upgrade: requests in /usr/local/lib/python3.6/dist-packages (from smart-open>=1.7.0->gensim) (2.21.0) Requirement already satisfied, skipping upgrade: boto>=2.32 in /usr/local/lib/python3.6/dist-packages (from smart-open>=1.7.0->gensim) (2.49.0) Requirement already satisfied, skipping upgrade: s3transfer<0.3.0,>=0.2.0 in /usr/local/lib/python3.6/dist-packages (from boto3->smart-open>=1.7.0->gensim) (0.2.1) Requirement already satisfied, skipping upgrade: jmespath<1.0.0,>=0.7.1 in /usr/local/lib/python3.6/dist-packages (from boto3->smart-open>=1.7.0->gensim) (0.9.4) Requirement already satisfied, skipping upgrade: botocore<1.13.0,>=1.12.205 in /usr/local/lib/python3.6/dist-packages (from boto3->smart-open>=1.7.0->gensim) (1.12.205) Requirement already satisfied, skipping upgrade: chardet<3.1.0,>=3.0.2 in /usr/local/lib/python3.6/dist-packages (from requests->smart-open>=1.7.0->gensim) (3.0.4) Requirement already satisfied, skipping upgrade: urllib3<1.25,>=1.21.1 in /usr/local/lib/python3.6/dist-packages (from requests->smart-open>=1.7.0->gensim) (1.24.3) Requirement already satisfied, skipping upgrade: certifi>=2017.4.17 in /usr/local/lib/python3.6/dist-packages (from requests->smart-open>=1.7.0->gensim) (2019.6.16) Requirement already satisfied, skipping upgrade: idna<2.9,>=2.5 in /usr/local/lib/python3.6/dist-packages (from requests->smart-open>=1.7.0->gensim) (2.8) Requirement already satisfied, skipping upgrade: python-dateutil<3.0.0,>=2.1; python_version >= "2.7" in /usr/local/lib/python3.6/dist-packages (from botocore<1.13.0,>=1.12.205->boto3->smart-open>=1.7.0->gensim) (2.5.3) Requirement already satisfied, skipping upgrade: docutils<0.15,>=0.10 in /usr/local/lib/python3.6/dist-packages (from botocore<1.13.0,>=1.12.205->boto3->smart-open>=1.7.0->gensim) (0.14) Installing collected packages: gensim Found existing installation: gensim 3.6.0 Uninstalling gensim-3.6.0: Successfully uninstalled gensim-3.6.0 Successfully installed gensim-3.8.0
Cargamos el modelo:
from gensim.models.wrappers import FastText
#Carga modelo pre-entrenado
model = FastText.load_fasttext_format('result/cemc')
Una vez cargado el modelo podemos jugar con los vectores directamente desde python. Aquí también se pueden buscar palabras similares o bien determinar la similitud entre una y otra palabra:
#Buscar las palabras más similares a query
print(model.most_similar('azteca'))
print(model.most_similar('mexicano'))
#Similitud entre dos palabras dasdas
print(model.similarity('mexico', 'país'))
[('zapoteca', 0.9719510078430176), ('orilla', 0.9412146806716919), ('bonilla', 0.9368422627449036), ('barbilla', 0.9355900883674622), ('zorrilla', 0.934384822845459), ('polla', 0.9336952567100525), ('chueca', 0.9323766827583313), ('azuela', 0.9320937395095825), ('chilena', 0.9304165244102478), ('borbolla', 0.9289524555206299)] [('americano', 0.9667645692825317), ('mexico', 0.9505746364593506), ('norteamericano', 0.9450423717498779), ('africano', 0.9262045621871948), ('republicano', 0.9257711172103882), ('latinoamericano', 0.9056142568588257), ('mexicanismo', 0.9027756452560425), ('universitario', 0.8979648351669312), ('organizador', 0.8943582773208618), ('italiano', 0.8912588357925415)] 0.31398705
Una de las ventajas de fastText es que, además de obtener los vectores de palabras que se encontraban en el vocabulario de entrenamiento, es capaz de construir representaciones vectoriales de palabras que no estaban en este vocabulario (Out-Of-Vocabulary words, OOV). Esto se realiza a través de una operación de composición de subwords.
#Palabra dentro del vocabulario
existent_word = "computadora"
print(existent_word in model.wv.vocab)
#Obtención del vector de esta palabra
vector_computadora = model.wv[existent_word]
#Palabra oov
oov_word = "computadorsota"
print(oov_word in model.wv.vocab)
#Obtención del vector de oov
vector_oov = model.wv[oov_word]
#Similitud entre ambos
print(model.similarity(existent_word, oov_word))
True False 0.9610355
Una vez obtenidos los vectores de Fasttext, podemos aplicar el algoritmo de spectral clustering, vamos a agrupar y visualizar los datos obtenidos.
#Paquetería necesaria
import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from sklearn.cluster import KMeans
import pandas as pd
import networkx as nx
from scipy.linalg import eig
from operator import itemgetter
Dado que Fasttext permite representar palabras OOV, podemos darle cualquier lista arbitraría de léxico y obtener vectores que los representen.
#Lista de palabras para trabajar
vocab_words = ['amo','amas','amamos','ama','aman','come','como','comemos','comen','toco','tocas','tocan','tocamos','gato','minino','gatito','gatos','mininos',
'flor','flores','mininito','computadora','computadoras']
Obtenemos los vectores que representan a las palabras anteriores y los guardamos en un arreglo.
#Tamaño del vocabulario
N = len(vocab_words)
#Matrix de NxNúmero de dimmensiones
X = np.zeros((N,100))
#Llenamos la matriz con los vectores palabra
for i,w in enumerate(vocab_words):
X[i] = model.wv[w]
print(X.shape)
(23, 100)
Podemos visualizar estos datos a partir de la siguiente función:
#Función para plotear
def plot_words(Z,ids,color='blue'):
#Reduce a dos dimensiones con PCA
Z = PCA(n_components=2).fit_transform(Z)
r=0
#Plotea las dimensiones
plt.scatter(Z[:,0],Z[:,1], marker='o', c=color)
for label,x,y in zip(ids, Z[:,0], Z[:,1]):
#Agrega las etiquetas
plt.annotate(label, xy=(x,y), xytext=(-1,1), textcoords='offset points', ha='center', va='bottom')
r+=1
#Ploteo de los datos
plot_words(X, vocab_words)
plt.show()