File size: 6,061 Bytes
a87c588 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
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] # lowercase
texts = [''.join([c for c in text if c not in punctuation]) for text in texts] # delete punctuation
texts = [text.split('\n') for text in texts] # eliminate \n
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)
'''
# preprocessing
texts = self.preprocessing(texts)
# joint text
megadoc = ' '.join(texts)
words = megadoc.split()
self.counts = Counter(words) # Construye un diccionario de palabras. Las claves son las palabras y los valores son la frecuencia
self.vocab = sorted(self.counts, key = self.counts.get, reverse = True) # Ordenamos la palabras por frecuencia
self.vocab_to_int = {word: ii for ii, word in enumerate(self.vocab, 2)} # Construimos diccionario para mapear palabra a número entero. Empezamos los índices en 2
self.vocab_to_int[self.unk_idx] = '<unk>' # token para palabras no reconocidas
self.vocab_to_int[self.pad_idx] = '<pad>' # token para padding
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))
#Extraemos los índices de todos los reviews que tienen longitud cumpliendo los filtros
filter_idx = [ii for ii, text in enumerate(encoded_text) if min_tokens <= len(text) <= max_tokens]
#Nos quedamos solo con los reviews con longitud que cumplen los filtros
encoded_text = [encoded_text[ii] for ii in filter_idx]
#Lo mismo con los labels
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,
#vector_size: int
):
'''
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)')
# preprocessing
texts = self.preprocessing(texts)
# encode
encoded_text = self.encode(texts)
# padding
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]) #Contamos cuantas palabras hay en cada review
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 |