|
import numpy as np |
|
import matplotlib.pyplot as plt |
|
from collections import Counter |
|
from string import punctuation |
|
|
|
class Tokenizer(): |
|
def __init__(self): |
|
|
|
self.vocab = None |
|
self.pad_idx = 0 |
|
self.unk_idx = 1 |
|
|
|
def preprocessing(self, texts: list): |
|
|
|
''' |
|
Método que pre procesa un documento, transformando las palabras a minúsculas, eliminando puntuaciones y caracterés "\n". Devuelve los textos pre procesados. |
|
texts: documentos a ser procesados (list) |
|
''' |
|
|
|
texts = [text.lower() for text in texts] |
|
texts = [''.join([c for c in text if c not in punctuation]) for text in texts] |
|
texts = [text.split('\n') for text in texts] |
|
texts = [' '.join(text) for text in texts] |
|
|
|
return texts |
|
|
|
def train(self, texts: list): |
|
|
|
''' |
|
Método que entrena el tokenizador, construyendo un vocabulario de tokens y su codificación respectiva. |
|
texts: documentos que el tokenizador usa para construir el vocabulario (list) |
|
''' |
|
|
|
|
|
texts = self.preprocessing(texts) |
|
|
|
|
|
megadoc = ' '.join(texts) |
|
words = megadoc.split() |
|
|
|
self.counts = Counter(words) |
|
self.vocab = sorted(self.counts, key = self.counts.get, reverse = True) |
|
self.vocab_to_int = {word: ii for ii, word in enumerate(self.vocab, 2)} |
|
self.vocab_to_int[self.unk_idx] = '<unk>' |
|
self.vocab_to_int[self.pad_idx] = '<pad>' |
|
|
|
self.int_to_vocab = {value: key for key, value in self.vocab_to_int.items()} |
|
|
|
def encode(self, texts: list): |
|
|
|
''' |
|
Método que usa el vocabulario construido para codificar textos. Devuelve los textos codificados. |
|
texts: Documentos a ser codificados (list) |
|
''' |
|
|
|
if self.vocab_to_int is None: |
|
raise ValueError('Debes entrenar el tokenizador primero') |
|
|
|
encoded_text = [[self.vocab_to_int[word] if word in self.vocab_to_int.keys() else 1 for word in text.split()] for text in texts] |
|
|
|
return encoded_text |
|
|
|
def decode(self, texts: list): |
|
|
|
''' |
|
Método que usa el vocabulario construido para decodificar textos. Devuelve los textos decodificados. |
|
texts: Documentos a ser decodificados (list) |
|
''' |
|
|
|
if self.vocab is None: |
|
raise ValueError('Debes entrenar el tokenizador primero') |
|
|
|
decoded_text = [[self.int_to_vocab[word] if word in self.int_to_vocab.keys() else 'unk' for word in text] for text in texts] |
|
|
|
return decoded_text |
|
|
|
def filter_text(self, encoded_text: list, encoded_labels: list, min_tokens = 1, max_tokens = 1e6): |
|
|
|
''' |
|
Método que filtra una colección de documentos en función de la cantidad de tokens. Devuelve la coleccion de documentos filtrados. |
|
encoded_text: Textos codificados a ser filtrados (list) |
|
encoded_labels: Etiquetas a filtrar en función del texto (list) |
|
min_tokens: Cantidad mínima de tokens permitida (int) |
|
max_tokens: Cantidad máxima de tokens permitida (int) |
|
''' |
|
|
|
print('Documentos antes de eliminación:', len(encoded_text)) |
|
|
|
|
|
filter_idx = [ii for ii, text in enumerate(encoded_text) if min_tokens <= len(text) <= max_tokens] |
|
|
|
|
|
encoded_text = [encoded_text[ii] for ii in filter_idx] |
|
|
|
|
|
encoded_labels = np.array([encoded_labels[ii] for ii in filter_idx]) |
|
|
|
print('Documentos después de eliminación:', len(encoded_text)) |
|
|
|
return encoded_text, encoded_labels |
|
|
|
def padding(self, encoded_text: list, vector_size: int): |
|
|
|
''' |
|
Método que hace padding a una secuencia, fijando el largo de las secuencias en un número determinado vector_size: |
|
Las secuencias con largo mayor a vector_size, son acortadas por la derecha hasta ese valor. |
|
Las secuencias con largo menor a vector_size, son llenadas con 0s por la izquierda hasta completar ese valor. |
|
Retorna la secuencia modificada. |
|
|
|
encoded_text: lista con los textos a modificar (list) |
|
vector_size: largo de los documentos a fijar (int) |
|
''' |
|
|
|
features = np.zeros((len(encoded_text), vector_size), dtype = int) |
|
|
|
for i, row in enumerate(encoded_text): |
|
features[i, -len(row):] = np.array(row)[:vector_size] |
|
|
|
return features |
|
|
|
def tokenize(self, texts: list, |
|
|
|
): |
|
|
|
''' |
|
Método que tokeniza documentos a partir del vocabulario construido. Devuelve los documentos codificados en un largo de tamaño vector_size |
|
texts: Textos a ser tokenizados (list) |
|
vector_size: Largo de los textos de salida (int) |
|
''' |
|
|
|
if self.vocab is None: |
|
raise ValueError('Debes entrenar el tokenizador primero') |
|
|
|
if self.vector_size is None: |
|
raise ValueError('Debes especificar vector_size en objeto Tokenizer (tokenizer.vector_size = x)') |
|
|
|
|
|
texts = self.preprocessing(texts) |
|
|
|
|
|
encoded_text = self.encode(texts) |
|
|
|
|
|
features = self.padding(encoded_text, self.vector_size) |
|
|
|
return features |
|
|
|
def graph_distribution(self, encoded_text): |
|
|
|
''' |
|
Método que grafica la distribución del largo de los documentos de entrenamiento. |
|
''' |
|
|
|
if self.vocab is None: |
|
raise ValueError('Debes entrenar el tokenizador primero') |
|
|
|
text_lens = Counter([len(text) for text in encoded_text]) |
|
|
|
plt.figure(figsize = (12, 6)) |
|
plt.bar(text_lens.keys(), text_lens.values()) |
|
plt.title('Distribución del largo de documentos en el Dataset') |
|
plt.xlabel('Cantidad de tokens') |
|
plt.ylabel('Frecuencia') |
|
plt.show() |
|
|
|
def __len__(self): |
|
return len(self.vocab_to_int) if self.vocab is not None else 0 |