0 - Machine Learning en Lenguaje¶
Hasta ahora habíamos visto como aplicar Machine Learning a problemas donde los datos tenían un caracter numérico. El motivo principal es porque la mayoría de algoritmos tienen un caracter matemático y por ello necesitamos que los datos sean numéricos. Entonces ¿Cómo podemos aplicar algoritmos de Machine Learning sobre datos lingüiscos? Simplemente, necesitamos buscar una forma de representar numéricamente el lenguaje.
Nota: Aunque aquí hablamos de "Machine Learning en Lenguaje", en general el término que se suele usar para referirse a esta área es del nombre "NLP" (Natural Languaje Processing). El NLP incluye los procesos de tratamiento de datos que no requieren de Machine Leanring, pero en general, cuando la gente habla de NLP habla de Machine Learning e Inteligencia Artificial.
Caso Real: Google Search¶
En los años 80-90 empezaron a aparece los primeros buscadores web. Estas páginas servían para buscar páginas web a partir de una búsqueda de texto natural pero... ¿Cómo podemos relacionar lenguaje natural con información digital como páginas webs?¿Cómo sabe qué buscar un buscador? Esto fue uno de los orígenes de la explosión de la lingüistica computacional.
1 - Datos de lenguaje¶
Antes de empezar a hablar de cómo representar numéricamente el lenguaje, vamos a ver algunos conceptos relativos a los tipos de datos que tenemos en los problemas de lenguaje computacional
1.1 - Texto¶
Archivos que contienen información de caracter de texto generalmente completamente desestructurados y sin propósito. La diferencia entre textos y un corpus es que los corpus tienen un caracter más estructurado (por ejemplo, separado por diferentes archivos), un dominio de información y un propósito para su uso. Por ejemplo, el libro de 100 años de soledad sería un texto
import requests
# Web con el libro en txt
web = "https://gist.githubusercontent.com/ismaproco/6781d297ee65c6a707cd3c901e87ec56/raw/20d3520cd7c53d99215845375b1dca16ac827bd7/gabriel_garcia_marquez_cien_annos_soledad.txt"
# Descargamos con la librería request
response = requests.get(web)
# Recojemos el texto de descarga
text = response.text
# Mostramos por pantalla
print(text[:1000])
Gabriel García Márquez Cien años de soledad EDITADO POR "EDICIONES LA CUEVA" Para J omi García Ascot y María Luisa Elio Cien años de soledad Gabriel García Márquez Muchos años después, frente al pelotón de fusilamiento, el coronel Aureliano Buendía había de recordar aquella tarde remota en que su padre lo llevó a conocer el hielo. Macondo era entonces una aldea de veinte casas de barro y cañabrava construidas a la orilla de un río de aguas diáfanas que se precipitaban por un lecho de piedras pulidas, blancas y enormes como huevos prehistóricos. El mundo era tan reciente, que muchas cosas carecían de nombre, y para mencionarlas había que señalarías con el dedo. Todos los años, por el mes de marzo, una familia de gitanos desarrapados plantaba su carpa cerca de la aldea, y con un grande alboroto de pitos y timbales daban a conocer los nuevos inventos. Primero llevaron el imán. Un gitano corpulento, de barba montaraz y manos de gorrión, que se presentó con
Nota: 100 Años de soledad es el mejor libro del mundo mundial
1.2. - Corpus¶
Se denomina corpus a un conjunto de datos de caracter de lenguaje y generalmente de lenguaje natural (por ejemplo, diccionario que realmente no contiene lengauje natural sino definiciones de palabras, no se suele referir como corpus). Algunos ejemplos de corpus son los siguientes:
- Libro almacenado en un .txt
- Carpeta con libros almacenados en .txt
- Tweets de twitter
Los corpus siempre se almacenan en texto plano (esto quiere decir que no se tiene en cuenta la fuente, tipografía o color, como en el word), pero no se tiene en cuenta en esta definición si el corpus está estructurado en uno o varios archivos, si los datos están etiquetados o no o el formato de los archivos en los que se guarda. Por ejemplo, si separamos el libro de 100 años de soledad en páginas para consumirlo por páginas, podríamos decir que constituye un corpus
NOTA: Para tener una definición más formal: https://www.lancaster.ac.uk/fss/courses/ling/corpus/Corpus2/2STAND.HTM
import requests
# Web con el libro en txt
web = "https://gist.githubusercontent.com/ismaproco/6781d297ee65c6a707cd3c901e87ec56/raw/20d3520cd7c53d99215845375b1dca16ac827bd7/gabriel_garcia_marquez_cien_annos_soledad.txt"
# Descargamos con la librería request
response = requests.get(web)
# Recojemos el texto de descarga
text = response.text
# Pie de página
pie_de_pagina = "\n\n\nCien años de soledad \n\n\n\nGabriel García Márquez \n\n"
# Dividimos por pie de página
corpus = text.split(pie_de_pagina)
print("Tipo del corpus:\t", type(corpus))
print("Longitud del corpus:\t", len(corpus))
Tipo del corpus: <class 'list'> Longitud del corpus: 171
corpus[0:1]
['Gabriel García Márquez \n\n\n\nCien años de soledad \n\n\n\nEDITADO POR "EDICIONES LA CUEVA" \n\n\n\nPara J omi García Ascot \ny María Luisa Elio \n']
Nota Recuerda! Es el mejor libro del mundo
1.3. - Documento¶
Independientemente de que un corpus tenga o no múltiples archivos dentro del corpus, puede que el corpus esté compuesto por múltiples instancias independientes. Cada una de estas instancias, independientemente de su longitud (sea una frase o sea un libro entero) se denomina "documento. Algunos ejemplos de documentos son:
- Cada email en un corpus de emails
- Cada tuit en un corpus lleno de tuits
- Cada libro dentro de un corpus de libros
- Cada descripción de un producto, detro de un corpus de descripciones de productos de Amazon
Nota: ¿He dicho ya que 100 años de soledad es el libro más guay?
Anotaciones a nivel documento¶
En algunos casos, sobretodo en Machine Learning supervisado, es común trabajar con corpus que contengan documentos etiquetados. Estan etiquetas se denominan "anotaciones". Cuando cada documento tiene una categoría concretar (por ejemplo cuando queremos hacer un clasificador de documentos)
import pandas as pd
# Truco del almendruco: List Comprehension
pd.DataFrame([
{
"text": text, # Texto original
"sale_aureliano?": "Aureliano" in text, # True si la palabra "Aureliano" está en el texto
}
for text in corpus
]
)
| text | sale_aureliano? | |
|---|---|---|
| 0 | Gabriel García Márquez \n\n\n\nCien años de so... | False |
| 1 | \n\nMuchos años después, frente al pelotón de ... | True |
| 2 | poder de convicción irresistible. Lo envió a l... | False |
| 3 | mantenía enredado en los minúsculos problemas ... | True |
| 4 | Al principio, José Arcadio Buendía era una esp... | True |
| ... | ... | ... |
| 166 | solamente la recuperación del aeroplano podía ... | True |
| 167 | de su filiación. La partida de bautismo más an... | True |
| 168 | de soledad en el aturdimiento de las parrandas... | True |
| 169 | -¡Los amigos son unos hijos de puta! \n\nNigro... | True |
| 170 | cimientos. Sólo entonces descubrió que Amarant... | True |
171 rows × 2 columns
Anotaciones a nivel de palabra¶
Otras veces no queremos etiquetar a nivel de documento sino a nivel de palabra (por ejemplo si queremos hacer un clasificador que detecte los nombres propios de un texto)
1.4. - Vocabulario¶
Generalmente, cuando se trabaja con texto de lenguaje natural en un idioma concreto es fácil encontrarse con palabras mal escritas por diferentes motivos (errores mecanográficos, ortotipográficos, errores de datos,...). Para poder detectar palabras mal escritas y estar seguro que trabajamos con palabras conocidas, se suele definir un conjunto finito de palabras que contiene el idioma.
Este conjunto finito es lo que se denomina vocabulario, pero también denominado a veces como Lexicón o como Diccionario. De manera que cualquier palabra que no está contenida en ese conjunto se considera, potencialmente, un error (out-of-vocabulary). Este tratamiento, en ciencias de la computación, es lo que se denomina una "máquina de estados finitos". Generalmente un vocabulario considerable suele contar con, al menos, entre 10.000 y 50.000 palabras.
Vocabulario con ontología¶
Wordnet es una ontología del lenguaje (principalmente en inglés, pero también está en otros idiomas como el español). Una ontología es una base de datos que no solo guarda entidades del lenguaje (nombres, verboos, significados,...) sino que además también guarda las relaciones que hay entre las entidades (sinónimos, conjugaciones,...)
import nltk
nltk.download('wordnet')
nltk.download('omw-1.4')
from nltk.corpus import wordnet
wordnet_vocab = set()
for synset in wordnet.all_synsets():
for lemma in synset.lemmas('spa'):
if "_" not in lemma.name():
wordnet_vocab.add(lemma.name().lower())
print(f"\nTamaño total del vocabulario: {len(wordnet_vocab)}")
wordnet_vocab = {word:i for i,word in enumerate(sorted(wordnet_vocab))}
[{i:j} for i,j in list(wordnet_vocab.items())[10:20]]
[nltk_data] Downloading package wordnet to [nltk_data] /home/thinbaker/nltk_data... [nltk_data] Downloading package omw-1.4 to [nltk_data] /home/thinbaker/nltk_data...
Tamaño total del vocabulario: 22351
[{'abad': 10},
{'abajo': 11},
{'abalizar': 12},
{'abalorio': 13},
{'abama': 14},
{'abamperio': 15},
{'abanderado': 16},
{'abanderar': 17},
{'abandonar': 18},
{'abandono': 19}]
Vocabulario con dataset - Tokenización¶
Hay veces en las que no tenemos un vocabulario para el dominio específico del corpus (por ejemplo, un corpus sobre literatura de fantasía donde las palabras son muchas veces inventadas) o preferimos usar un vocabulario reducido. En estos casos es común generar el vocabulario a partir de los documentos
Ejercicio - Extracción de vocabulario: Obtener el vocabulario a partir del corpus de 100 Años de Solead¶
vocab = set(text.split(" "))
print(list(vocab)[0:30])
['alcanzaban', 'sintiera', 'desengaños', '\nconocimientos,', 'cultivando', '\nlona', 'testamento', 'afeitarse', 'redujo', 'perfecto,', 'montura.', '\npasar', 'chécheres', 'dejen', 'rato', 'fenómeno.»', '\nbramido', 'Nostradamus,', 'ahuyentaba', 'tardaban', 'dictó', 'saciaba', 'paz', 'humillar', '\naragonés', 'lutos,', 'hela', 'supuesto,', 'empezado,', 'perfeccionarías']
Palabras especiales
El hecho de que se trabaje con palabras finitas hace que no podamos procesar palabras desconocidas (sobretodo cuando trabajamos con corpus generados). Además, muchas veces queremos representar simbólicamente características estructurales como el principio y el fin de texto. Para ello es común añadir palabras especiales al vocabulario.
# Le damos un valor numérico fácil de reconocer
wordnet_vocab['<UNK>'] = -1 # Unknow word
wordnet_vocab['<s>'] = -2 # Begin Of Sentence
wordnet_vocab['</s>'] = -3 # End Of Sentence
Ejemplo - Twitter
2 - Representación numérica - Tokenización¶
text = """Muchos años después, frente al pelotón de fusilamiento, el coronel
Aureliano Buendía había de recordar aquella tarde remota en que su padre lo
llevó a conocer el hielo. Macondo era entonces una aldea de veinte casas de
barro y cañabrava construidas a la orilla de un río de aguas diáfanas que se
precipitaban por un lecho de piedras pulidas, blancas y enormes como huevos
prehistóricos. El mundo era tan reciente, que muchas cosas carecían de nombre,
y para mencionarlas había que señalarías con el dedo. Todos los años, por el mes
de marzo, una familia de gitanos desarrapados plantaba su carpa cerca de la
aldea, y con un grande alboroto de pitos y timbales daban a conocer los nuevos
inventos. Primero llevaron el imán. Un gitano corpulento, de barba montaraz y
manos de gorrión, que se presentó con el nombre de Melquiades, hizo una
truculenta demostración pública de lo que él mismo llamaba la octava maravilla
de los sabios alquimistas de Macedonia. Fue de casa en casa arrastrando dos
lingotes metálicos, y todo el mundo se espantó al ver que los calderos, las
pailas, las tenazas y los anafes se caían de su sitio, y las maderas crujían por
la desesperación de los clavos y los tornillos tratando de desenclavarse, y aun
los objetos perdidos desde hacía mucho tiempo aparecían por donde más se les
había buscado, y se arrastraban en desbandada turbulenta detrás de los fierros
mágicos de Melquíades. «Las cosas, tienen vida propia -pregonaba el gitano con
áspero acento-, todo es cuestión de despertarles el ánima.» José Arcadio
Buendía, cuya desaforada imaginación iba siempre más lejos que el ingenio de la
naturaleza, y aun más allá del milagro y la magia, pensó que era posible
servirse de aquella invención inútil para desentrañar el oro de la tierra.
Melquíades, que era un hombre honrado, le previno: «Para eso no sirve.» Pero
José Arcadio Buendía no creía en aquel tiempo en la honradez de los gitanos, así
que cambió su mulo y una partida de chivos por los dos lingotes imantados.
Úrsula Iguarán, su mujer, que contaba con aquellos animales para ensanchar el
desmedrado patrimonio doméstico, no consiguió disuadirlo. «Muy pronto ha de
sobrarnos oro para empedrar la casa», replicó su marido. Durante varios meses se
empeñó en demostrar el acierto de sus conjeturas. Exploró palmo a palmo la
región, inclusive el fondo del río, arrastrando los dos lingotes de hierro y
recitando en voz alta el conjuro de Melquíades. Lo único que logró desenterrar
fue una armadura del siglo xv con todas sus partes soldadas por un cascote de
óxido, cuyo interior tenía la resonancia hueca de un enorme calabazo lleno de
piedras. Cuando José Arcadio Buendía y los cuatro hombres de su expedición
lograron desarticular la armadura, encontraron dentro un esqueleto calcificado
que llevaba colgado en el cuello un relicario de cobre con un rizo de mujer.
"""
print(text)
Muchos años después, frente al pelotón de fusilamiento, el coronel Aureliano Buendía había de recordar aquella tarde remota en que su padre lo llevó a conocer el hielo. Macondo era entonces una aldea de veinte casas de barro y cañabrava construidas a la orilla de un río de aguas diáfanas que se precipitaban por un lecho de piedras pulidas, blancas y enormes como huevos prehistóricos. El mundo era tan reciente, que muchas cosas carecían de nombre, y para mencionarlas había que señalarías con el dedo. Todos los años, por el mes de marzo, una familia de gitanos desarrapados plantaba su carpa cerca de la aldea, y con un grande alboroto de pitos y timbales daban a conocer los nuevos inventos. Primero llevaron el imán. Un gitano corpulento, de barba montaraz y manos de gorrión, que se presentó con el nombre de Melquiades, hizo una truculenta demostración pública de lo que él mismo llamaba la octava maravilla de los sabios alquimistas de Macedonia. Fue de casa en casa arrastrando dos lingotes metálicos, y todo el mundo se espantó al ver que los calderos, las pailas, las tenazas y los anafes se caían de su sitio, y las maderas crujían por la desesperación de los clavos y los tornillos tratando de desenclavarse, y aun los objetos perdidos desde hacía mucho tiempo aparecían por donde más se les había buscado, y se arrastraban en desbandada turbulenta detrás de los fierros mágicos de Melquíades. «Las cosas, tienen vida propia -pregonaba el gitano con áspero acento-, todo es cuestión de despertarles el ánima.» José Arcadio Buendía, cuya desaforada imaginación iba siempre más lejos que el ingenio de la naturaleza, y aun más allá del milagro y la magia, pensó que era posible servirse de aquella invención inútil para desentrañar el oro de la tierra. Melquíades, que era un hombre honrado, le previno: «Para eso no sirve.» Pero José Arcadio Buendía no creía en aquel tiempo en la honradez de los gitanos, así que cambió su mulo y una partida de chivos por los dos lingotes imantados. Úrsula Iguarán, su mujer, que contaba con aquellos animales para ensanchar el desmedrado patrimonio doméstico, no consiguió disuadirlo. «Muy pronto ha de sobrarnos oro para empedrar la casa», replicó su marido. Durante varios meses se empeñó en demostrar el acierto de sus conjeturas. Exploró palmo a palmo la región, inclusive el fondo del río, arrastrando los dos lingotes de hierro y recitando en voz alta el conjuro de Melquíades. Lo único que logró desenterrar fue una armadura del siglo xv con todas sus partes soldadas por un cascote de óxido, cuyo interior tenía la resonancia hueca de un enorme calabazo lleno de piedras. Cuando José Arcadio Buendía y los cuatro hombres de su expedición lograron desarticular la armadura, encontraron dentro un esqueleto calcificado que llevaba colgado en el cuello un relicario de cobre con un rizo de mujer.
2.1. - Preprocesado¶
Preprocesamos el texto para estandarizarlo
Es común preprocesar el texto antes de empezar a trabajar con él, primero para corregir problemas comunes a la hora de tratar texto (por ejemplo, eliminar espacios múltiples, eliminar) y segundo para preparar para el modelo el texto en particular que vamos a usar (por ejemplo, hay modelos que funcionan mejor sin usar signos de puntuación). Por ejemplo, una función de preprocesamiento podría ser la siguiente
import re
def preprocess(text):
# Elimina espacios extra
text = re.sub(r'\s+', ' ', text)
# Pone el texto en minúscula
text = text.lower()
# Eliminar caracteres no alfanumericos
text = re.sub(r'[^a-zA-Z0-9áéíóúÁÉÍÓÚüÜñÑ\s]', '', text)
return text
text = preprocess(text)
text
'muchos años después frente al pelotón de fusilamiento el coronel aureliano buendía había de recordar aquella tarde remota en que su padre lo llevó a conocer el hielo macondo era entonces una aldea de veinte casas de barro y cañabrava construidas a la orilla de un río de aguas diáfanas que se precipitaban por un lecho de piedras pulidas blancas y enormes como huevos prehistóricos el mundo era tan reciente que muchas cosas carecían de nombre y para mencionarlas había que señalarías con el dedo todos los años por el mes de marzo una familia de gitanos desarrapados plantaba su carpa cerca de la aldea y con un grande alboroto de pitos y timbales daban a conocer los nuevos inventos primero llevaron el imán un gitano corpulento de barba montaraz y manos de gorrión que se presentó con el nombre de melquiades hizo una truculenta demostración pública de lo que él mismo llamaba la octava maravilla de los sabios alquimistas de macedonia fue de casa en casa arrastrando dos lingotes metálicos y todo el mundo se espantó al ver que los calderos las pailas las tenazas y los anafes se caían de su sitio y las maderas crujían por la desesperación de los clavos y los tornillos tratando de desenclavarse y aun los objetos perdidos desde hacía mucho tiempo aparecían por donde más se les había buscado y se arrastraban en desbandada turbulenta detrás de los fierros mágicos de melquíades las cosas tienen vida propia pregonaba el gitano con áspero acento todo es cuestión de despertarles el ánima josé arcadio buendía cuya desaforada imaginación iba siempre más lejos que el ingenio de la naturaleza y aun más allá del milagro y la magia pensó que era posible servirse de aquella invención inútil para desentrañar el oro de la tierra melquíades que era un hombre honrado le previno para eso no sirve pero josé arcadio buendía no creía en aquel tiempo en la honradez de los gitanos así que cambió su mulo y una partida de chivos por los dos lingotes imantados úrsula iguarán su mujer que contaba con aquellos animales para ensanchar el desmedrado patrimonio doméstico no consiguió disuadirlo muy pronto ha de sobrarnos oro para empedrar la casa replicó su marido durante varios meses se empeñó en demostrar el acierto de sus conjeturas exploró palmo a palmo la región inclusive el fondo del río arrastrando los dos lingotes de hierro y recitando en voz alta el conjuro de melquíades lo único que logró desenterrar fue una armadura del siglo xv con todas sus partes soldadas por un cascote de óxido cuyo interior tenía la resonancia hueca de un enorme calabazo lleno de piedras cuando josé arcadio buendía y los cuatro hombres de su expedición lograron desarticular la armadura encontraron dentro un esqueleto calcificado que llevaba colgado en el cuello un relicario de cobre con un rizo de mujer '
PRO Tip: Las expresiones regulares pregúntaselas a chatGPT
2.2. - Tokenización¶
Una vez que tenemos un texto, el primer paso para representarlo numéricamente es tokenizar el texto. Se denomina tokenizar a dividir el texto en las componentes (palabras, letras,... existen múltiples posibilidades) que lo forman
El problema que tiene este tipo de tokenización es que requiere un alto nivel de tokens para que funcione bien, además de que falla cuando hay nuevas palabras o palabras mal escritas. Para resolver este problema se realizan técnias como
- Lemmatización
- Stemming
- Remove Stop Words
Tokenización de caracter¶
La tokenización a nivel de caracter consiste en atribuir a cada caracter (o símbolo del lenguaje) un valor numérico distinto. De esta manera, la tokenización a nivel de caracter consiste en convertir en texto en una lista numérica donde cada elemento de la lista es el valor numérico de cada caracter del lenguaje.
character_vocab = {
'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6, 'g': 7, 'h': 8, 'i': 9,
'j': 10, 'k': 11, 'l': 12, 'm': 13, 'n': 14, 'ñ': 15, 'o': 16, 'p': 17,
'q': 18, 'r': 19, 's': 20, 't': 21, 'u': 22, 'v': 23, 'w': 24, 'x': 25,
'y': 26, 'z': 27, 'á': 28, 'é':29, 'í':30, 'ó':31, 'ú':32, ' ':0}
def letter_tokenize(text, character_vocab):
tokens = []
for letter in text:
token = character_vocab[letter]
tokens.append(token)
return tokens
text_tokenized = letter_tokenize(text, character_vocab)
print(list(text),"\n")
print(text_tokenized)
['m', 'u', 'c', 'h', 'o', 's', ' ', 'a', 'ñ', 'o', 's', ' ', 'd', 'e', 's', 'p', 'u', 'é', 's', ' ', 'f', 'r', 'e', 'n', 't', 'e', ' ', 'a', 'l', ' ', 'p', 'e', 'l', 'o', 't', 'ó', 'n', ' ', 'd', 'e', ' ', 'f', 'u', 's', 'i', 'l', 'a', 'm', 'i', 'e', 'n', 't', 'o', ' ', 'e', 'l', ' ', 'c', 'o', 'r', 'o', 'n', 'e', 'l', ' ', 'a', 'u', 'r', 'e', 'l', 'i', 'a', 'n', 'o', ' ', 'b', 'u', 'e', 'n', 'd', 'í', 'a', ' ', 'h', 'a', 'b', 'í', 'a', ' ', 'd', 'e', ' ', 'r', 'e', 'c', 'o', 'r', 'd', 'a', 'r', ' ', 'a', 'q', 'u', 'e', 'l', 'l', 'a', ' ', 't', 'a', 'r', 'd', 'e', ' ', 'r', 'e', 'm', 'o', 't', 'a', ' ', 'e', 'n', ' ', 'q', 'u', 'e', ' ', 's', 'u', ' ', 'p', 'a', 'd', 'r', 'e', ' ', 'l', 'o', ' ', 'l', 'l', 'e', 'v', 'ó', ' ', 'a', ' ', 'c', 'o', 'n', 'o', 'c', 'e', 'r', ' ', 'e', 'l', ' ', 'h', 'i', 'e', 'l', 'o', ' ', 'm', 'a', 'c', 'o', 'n', 'd', 'o', ' ', 'e', 'r', 'a', ' ', 'e', 'n', 't', 'o', 'n', 'c', 'e', 's', ' ', 'u', 'n', 'a', ' ', 'a', 'l', 'd', 'e', 'a', ' ', 'd', 'e', ' ', 'v', 'e', 'i', 'n', 't', 'e', ' ', 'c', 'a', 's', 'a', 's', ' ', 'd', 'e', ' ', 'b', 'a', 'r', 'r', 'o', ' ', 'y', ' ', 'c', 'a', 'ñ', 'a', 'b', 'r', 'a', 'v', 'a', ' ', 'c', 'o', 'n', 's', 't', 'r', 'u', 'i', 'd', 'a', 's', ' ', 'a', ' ', 'l', 'a', ' ', 'o', 'r', 'i', 'l', 'l', 'a', ' ', 'd', 'e', ' ', 'u', 'n', ' ', 'r', 'í', 'o', ' ', 'd', 'e', ' ', 'a', 'g', 'u', 'a', 's', ' ', 'd', 'i', 'á', 'f', 'a', 'n', 'a', 's', ' ', 'q', 'u', 'e', ' ', 's', 'e', ' ', 'p', 'r', 'e', 'c', 'i', 'p', 'i', 't', 'a', 'b', 'a', 'n', ' ', 'p', 'o', 'r', ' ', 'u', 'n', ' ', 'l', 'e', 'c', 'h', 'o', ' ', 'd', 'e', ' ', 'p', 'i', 'e', 'd', 'r', 'a', 's', ' ', 'p', 'u', 'l', 'i', 'd', 'a', 's', ' ', 'b', 'l', 'a', 'n', 'c', 'a', 's', ' ', 'y', ' ', 'e', 'n', 'o', 'r', 'm', 'e', 's', ' ', 'c', 'o', 'm', 'o', ' ', 'h', 'u', 'e', 'v', 'o', 's', ' ', 'p', 'r', 'e', 'h', 'i', 's', 't', 'ó', 'r', 'i', 'c', 'o', 's', ' ', 'e', 'l', ' ', 'm', 'u', 'n', 'd', 'o', ' ', 'e', 'r', 'a', ' ', 't', 'a', 'n', ' ', 'r', 'e', 'c', 'i', 'e', 'n', 't', 'e', ' ', 'q', 'u', 'e', ' ', 'm', 'u', 'c', 'h', 'a', 's', ' ', 'c', 'o', 's', 'a', 's', ' ', 'c', 'a', 'r', 'e', 'c', 'í', 'a', 'n', ' ', 'd', 'e', ' ', 'n', 'o', 'm', 'b', 'r', 'e', ' ', 'y', ' ', 'p', 'a', 'r', 'a', ' ', 'm', 'e', 'n', 'c', 'i', 'o', 'n', 'a', 'r', 'l', 'a', 's', ' ', 'h', 'a', 'b', 'í', 'a', ' ', 'q', 'u', 'e', ' ', 's', 'e', 'ñ', 'a', 'l', 'a', 'r', 'í', 'a', 's', ' ', 'c', 'o', 'n', ' ', 'e', 'l', ' ', 'd', 'e', 'd', 'o', ' ', 't', 'o', 'd', 'o', 's', ' ', 'l', 'o', 's', ' ', 'a', 'ñ', 'o', 's', ' ', 'p', 'o', 'r', ' ', 'e', 'l', ' ', 'm', 'e', 's', ' ', 'd', 'e', ' ', 'm', 'a', 'r', 'z', 'o', ' ', 'u', 'n', 'a', ' ', 'f', 'a', 'm', 'i', 'l', 'i', 'a', ' ', 'd', 'e', ' ', 'g', 'i', 't', 'a', 'n', 'o', 's', ' ', 'd', 'e', 's', 'a', 'r', 'r', 'a', 'p', 'a', 'd', 'o', 's', ' ', 'p', 'l', 'a', 'n', 't', 'a', 'b', 'a', ' ', 's', 'u', ' ', 'c', 'a', 'r', 'p', 'a', ' ', 'c', 'e', 'r', 'c', 'a', ' ', 'd', 'e', ' ', 'l', 'a', ' ', 'a', 'l', 'd', 'e', 'a', ' ', 'y', ' ', 'c', 'o', 'n', ' ', 'u', 'n', ' ', 'g', 'r', 'a', 'n', 'd', 'e', ' ', 'a', 'l', 'b', 'o', 'r', 'o', 't', 'o', ' ', 'd', 'e', ' ', 'p', 'i', 't', 'o', 's', ' ', 'y', ' ', 't', 'i', 'm', 'b', 'a', 'l', 'e', 's', ' ', 'd', 'a', 'b', 'a', 'n', ' ', 'a', ' ', 'c', 'o', 'n', 'o', 'c', 'e', 'r', ' ', 'l', 'o', 's', ' ', 'n', 'u', 'e', 'v', 'o', 's', ' ', 'i', 'n', 'v', 'e', 'n', 't', 'o', 's', ' ', 'p', 'r', 'i', 'm', 'e', 'r', 'o', ' ', 'l', 'l', 'e', 'v', 'a', 'r', 'o', 'n', ' ', 'e', 'l', ' ', 'i', 'm', 'á', 'n', ' ', 'u', 'n', ' ', 'g', 'i', 't', 'a', 'n', 'o', ' ', 'c', 'o', 'r', 'p', 'u', 'l', 'e', 'n', 't', 'o', ' ', 'd', 'e', ' ', 'b', 'a', 'r', 'b', 'a', ' ', 'm', 'o', 'n', 't', 'a', 'r', 'a', 'z', ' ', 'y', ' ', 'm', 'a', 'n', 'o', 's', ' ', 'd', 'e', ' ', 'g', 'o', 'r', 'r', 'i', 'ó', 'n', ' ', 'q', 'u', 'e', ' ', 's', 'e', ' ', 'p', 'r', 'e', 's', 'e', 'n', 't', 'ó', ' ', 'c', 'o', 'n', ' ', 'e', 'l', ' ', 'n', 'o', 'm', 'b', 'r', 'e', ' ', 'd', 'e', ' ', 'm', 'e', 'l', 'q', 'u', 'i', 'a', 'd', 'e', 's', ' ', 'h', 'i', 'z', 'o', ' ', 'u', 'n', 'a', ' ', 't', 'r', 'u', 'c', 'u', 'l', 'e', 'n', 't', 'a', ' ', 'd', 'e', 'm', 'o', 's', 't', 'r', 'a', 'c', 'i', 'ó', 'n', ' ', 'p', 'ú', 'b', 'l', 'i', 'c', 'a', ' ', 'd', 'e', ' ', 'l', 'o', ' ', 'q', 'u', 'e', ' ', 'é', 'l', ' ', 'm', 'i', 's', 'm', 'o', ' ', 'l', 'l', 'a', 'm', 'a', 'b', 'a', ' ', 'l', 'a', ' ', 'o', 'c', 't', 'a', 'v', 'a', ' ', 'm', 'a', 'r', 'a', 'v', 'i', 'l', 'l', 'a', ' ', 'd', 'e', ' ', 'l', 'o', 's', ' ', 's', 'a', 'b', 'i', 'o', 's', ' ', 'a', 'l', 'q', 'u', 'i', 'm', 'i', 's', 't', 'a', 's', ' ', 'd', 'e', ' ', 'm', 'a', 'c', 'e', 'd', 'o', 'n', 'i', 'a', ' ', 'f', 'u', 'e', ' ', 'd', 'e', ' ', 'c', 'a', 's', 'a', ' ', 'e', 'n', ' ', 'c', 'a', 's', 'a', ' ', 'a', 'r', 'r', 'a', 's', 't', 'r', 'a', 'n', 'd', 'o', ' ', 'd', 'o', 's', ' ', 'l', 'i', 'n', 'g', 'o', 't', 'e', 's', ' ', 'm', 'e', 't', 'á', 'l', 'i', 'c', 'o', 's', ' ', 'y', ' ', 't', 'o', 'd', 'o', ' ', 'e', 'l', ' ', 'm', 'u', 'n', 'd', 'o', ' ', 's', 'e', ' ', 'e', 's', 'p', 'a', 'n', 't', 'ó', ' ', 'a', 'l', ' ', 'v', 'e', 'r', ' ', 'q', 'u', 'e', ' ', 'l', 'o', 's', ' ', 'c', 'a', 'l', 'd', 'e', 'r', 'o', 's', ' ', 'l', 'a', 's', ' ', 'p', 'a', 'i', 'l', 'a', 's', ' ', 'l', 'a', 's', ' ', 't', 'e', 'n', 'a', 'z', 'a', 's', ' ', 'y', ' ', 'l', 'o', 's', ' ', 'a', 'n', 'a', 'f', 'e', 's', ' ', 's', 'e', ' ', 'c', 'a', 'í', 'a', 'n', ' ', 'd', 'e', ' ', 's', 'u', ' ', 's', 'i', 't', 'i', 'o', ' ', 'y', ' ', 'l', 'a', 's', ' ', 'm', 'a', 'd', 'e', 'r', 'a', 's', ' ', 'c', 'r', 'u', 'j', 'í', 'a', 'n', ' ', 'p', 'o', 'r', ' ', 'l', 'a', ' ', 'd', 'e', 's', 'e', 's', 'p', 'e', 'r', 'a', 'c', 'i', 'ó', 'n', ' ', 'd', 'e', ' ', 'l', 'o', 's', ' ', 'c', 'l', 'a', 'v', 'o', 's', ' ', 'y', ' ', 'l', 'o', 's', ' ', 't', 'o', 'r', 'n', 'i', 'l', 'l', 'o', 's', ' ', 't', 'r', 'a', 't', 'a', 'n', 'd', 'o', ' ', 'd', 'e', ' ', 'd', 'e', 's', 'e', 'n', 'c', 'l', 'a', 'v', 'a', 'r', 's', 'e', ' ', 'y', ' ', 'a', 'u', 'n', ' ', 'l', 'o', 's', ' ', 'o', 'b', 'j', 'e', 't', 'o', 's', ' ', 'p', 'e', 'r', 'd', 'i', 'd', 'o', 's', ' ', 'd', 'e', 's', 'd', 'e', ' ', 'h', 'a', 'c', 'í', 'a', ' ', 'm', 'u', 'c', 'h', 'o', ' ', 't', 'i', 'e', 'm', 'p', 'o', ' ', 'a', 'p', 'a', 'r', 'e', 'c', 'í', 'a', 'n', ' ', 'p', 'o', 'r', ' ', 'd', 'o', 'n', 'd', 'e', ' ', 'm', 'á', 's', ' ', 's', 'e', ' ', 'l', 'e', 's', ' ', 'h', 'a', 'b', 'í', 'a', ' ', 'b', 'u', 's', 'c', 'a', 'd', 'o', ' ', 'y', ' ', 's', 'e', ' ', 'a', 'r', 'r', 'a', 's', 't', 'r', 'a', 'b', 'a', 'n', ' ', 'e', 'n', ' ', 'd', 'e', 's', 'b', 'a', 'n', 'd', 'a', 'd', 'a', ' ', 't', 'u', 'r', 'b', 'u', 'l', 'e', 'n', 't', 'a', ' ', 'd', 'e', 't', 'r', 'á', 's', ' ', 'd', 'e', ' ', 'l', 'o', 's', ' ', 'f', 'i', 'e', 'r', 'r', 'o', 's', ' ', 'm', 'á', 'g', 'i', 'c', 'o', 's', ' ', 'd', 'e', ' ', 'm', 'e', 'l', 'q', 'u', 'í', 'a', 'd', 'e', 's', ' ', 'l', 'a', 's', ' ', 'c', 'o', 's', 'a', 's', ' ', 't', 'i', 'e', 'n', 'e', 'n', ' ', 'v', 'i', 'd', 'a', ' ', 'p', 'r', 'o', 'p', 'i', 'a', ' ', 'p', 'r', 'e', 'g', 'o', 'n', 'a', 'b', 'a', ' ', 'e', 'l', ' ', 'g', 'i', 't', 'a', 'n', 'o', ' ', 'c', 'o', 'n', ' ', 'á', 's', 'p', 'e', 'r', 'o', ' ', 'a', 'c', 'e', 'n', 't', 'o', ' ', 't', 'o', 'd', 'o', ' ', 'e', 's', ' ', 'c', 'u', 'e', 's', 't', 'i', 'ó', 'n', ' ', 'd', 'e', ' ', 'd', 'e', 's', 'p', 'e', 'r', 't', 'a', 'r', 'l', 'e', 's', ' ', 'e', 'l', ' ', 'á', 'n', 'i', 'm', 'a', ' ', 'j', 'o', 's', 'é', ' ', 'a', 'r', 'c', 'a', 'd', 'i', 'o', ' ', 'b', 'u', 'e', 'n', 'd', 'í', 'a', ' ', 'c', 'u', 'y', 'a', ' ', 'd', 'e', 's', 'a', 'f', 'o', 'r', 'a', 'd', 'a', ' ', 'i', 'm', 'a', 'g', 'i', 'n', 'a', 'c', 'i', 'ó', 'n', ' ', 'i', 'b', 'a', ' ', 's', 'i', 'e', 'm', 'p', 'r', 'e', ' ', 'm', 'á', 's', ' ', 'l', 'e', 'j', 'o', 's', ' ', 'q', 'u', 'e', ' ', 'e', 'l', ' ', 'i', 'n', 'g', 'e', 'n', 'i', 'o', ' ', 'd', 'e', ' ', 'l', 'a', ' ', 'n', 'a', 't', 'u', 'r', 'a', 'l', 'e', 'z', 'a', ' ', 'y', ' ', 'a', 'u', 'n', ' ', 'm', 'á', 's', ' ', 'a', 'l', 'l', 'á', ' ', 'd', 'e', 'l', ' ', 'm', 'i', 'l', 'a', 'g', 'r', 'o', ' ', 'y', ' ', 'l', 'a', ' ', 'm', 'a', 'g', 'i', 'a', ' ', 'p', 'e', 'n', 's', 'ó', ' ', 'q', 'u', 'e', ' ', 'e', 'r', 'a', ' ', 'p', 'o', 's', 'i', 'b', 'l', 'e', ' ', 's', 'e', 'r', 'v', 'i', 'r', 's', 'e', ' ', 'd', 'e', ' ', 'a', 'q', 'u', 'e', 'l', 'l', 'a', ' ', 'i', 'n', 'v', 'e', 'n', 'c', 'i', 'ó', 'n', ' ', 'i', 'n', 'ú', 't', 'i', 'l', ' ', 'p', 'a', 'r', 'a', ' ', 'd', 'e', 's', 'e', 'n', 't', 'r', 'a', 'ñ', 'a', 'r', ' ', 'e', 'l', ' ', 'o', 'r', 'o', ' ', 'd', 'e', ' ', 'l', 'a', ' ', 't', 'i', 'e', 'r', 'r', 'a', ' ', 'm', 'e', 'l', 'q', 'u', 'í', 'a', 'd', 'e', 's', ' ', 'q', 'u', 'e', ' ', 'e', 'r', 'a', ' ', 'u', 'n', ' ', 'h', 'o', 'm', 'b', 'r', 'e', ' ', 'h', 'o', 'n', 'r', 'a', 'd', 'o', ' ', 'l', 'e', ' ', 'p', 'r', 'e', 'v', 'i', 'n', 'o', ' ', 'p', 'a', 'r', 'a', ' ', 'e', 's', 'o', ' ', 'n', 'o', ' ', 's', 'i', 'r', 'v', 'e', ' ', 'p', 'e', 'r', 'o', ' ', 'j', 'o', 's', 'é', ' ', 'a', 'r', 'c', 'a', 'd', 'i', 'o', ' ', 'b', 'u', 'e', 'n', 'd', 'í', 'a', ' ', 'n', 'o', ' ', 'c', 'r', 'e', 'í', 'a', ' ', 'e', 'n', ' ', 'a', 'q', 'u', 'e', 'l', ' ', 't', 'i', 'e', 'm', 'p', 'o', ' ', 'e', 'n', ' ', 'l', 'a', ' ', 'h', 'o', 'n', 'r', 'a', 'd', 'e', 'z', ' ', 'd', 'e', ' ', 'l', 'o', 's', ' ', 'g', 'i', 't', 'a', 'n', 'o', 's', ' ', 'a', 's', 'í', ' ', 'q', 'u', 'e', ' ', 'c', 'a', 'm', 'b', 'i', 'ó', ' ', 's', 'u', ' ', 'm', 'u', 'l', 'o', ' ', 'y', ' ', 'u', 'n', 'a', ' ', 'p', 'a', 'r', 't', 'i', 'd', 'a', ' ', 'd', 'e', ' ', 'c', 'h', 'i', 'v', 'o', 's', ' ', 'p', 'o', 'r', ' ', 'l', 'o', 's', ' ', 'd', 'o', 's', ' ', 'l', 'i', 'n', 'g', 'o', 't', 'e', 's', ' ', 'i', 'm', 'a', 'n', 't', 'a', 'd', 'o', 's', ' ', 'ú', 'r', 's', 'u', 'l', 'a', ' ', 'i', 'g', 'u', 'a', 'r', 'á', 'n', ' ', 's', 'u', ' ', 'm', 'u', 'j', 'e', 'r', ' ', 'q', 'u', 'e', ' ', 'c', 'o', 'n', 't', 'a', 'b', 'a', ' ', 'c', 'o', 'n', ' ', 'a', 'q', 'u', 'e', 'l', 'l', 'o', 's', ' ', 'a', 'n', 'i', 'm', 'a', 'l', 'e', 's', ' ', 'p', 'a', 'r', 'a', ' ', 'e', 'n', 's', 'a', 'n', 'c', 'h', 'a', 'r', ' ', 'e', 'l', ' ', 'd', 'e', 's', 'm', 'e', 'd', 'r', 'a', 'd', 'o', ' ', 'p', 'a', 't', 'r', 'i', 'm', 'o', 'n', 'i', 'o', ' ', 'd', 'o', 'm', 'é', 's', 't', 'i', 'c', 'o', ' ', 'n', 'o', ' ', 'c', 'o', 'n', 's', 'i', 'g', 'u', 'i', 'ó', ' ', 'd', 'i', 's', 'u', 'a', 'd', 'i', 'r', 'l', 'o', ' ', 'm', 'u', 'y', ' ', 'p', 'r', 'o', 'n', 't', 'o', ' ', 'h', 'a', ' ', 'd', 'e', ' ', 's', 'o', 'b', 'r', 'a', 'r', 'n', 'o', 's', ' ', 'o', 'r', 'o', ' ', 'p', 'a', 'r', 'a', ' ', 'e', 'm', 'p', 'e', 'd', 'r', 'a', 'r', ' ', 'l', 'a', ' ', 'c', 'a', 's', 'a', ' ', 'r', 'e', 'p', 'l', 'i', 'c', 'ó', ' ', 's', 'u', ' ', 'm', 'a', 'r', 'i', 'd', 'o', ' ', 'd', 'u', 'r', 'a', 'n', 't', 'e', ' ', 'v', 'a', 'r', 'i', 'o', 's', ' ', 'm', 'e', 's', 'e', 's', ' ', 's', 'e', ' ', 'e', 'm', 'p', 'e', 'ñ', 'ó', ' ', 'e', 'n', ' ', 'd', 'e', 'm', 'o', 's', 't', 'r', 'a', 'r', ' ', 'e', 'l', ' ', 'a', 'c', 'i', 'e', 'r', 't', 'o', ' ', 'd', 'e', ' ', 's', 'u', 's', ' ', 'c', 'o', 'n', 'j', 'e', 't', 'u', 'r', 'a', 's', ' ', 'e', 'x', 'p', 'l', 'o', 'r', 'ó', ' ', 'p', 'a', 'l', 'm', 'o', ' ', 'a', ' ', 'p', 'a', 'l', 'm', 'o', ' ', 'l', 'a', ' ', 'r', 'e', 'g', 'i', 'ó', 'n', ' ', 'i', 'n', 'c', 'l', 'u', 's', 'i', 'v', 'e', ' ', 'e', 'l', ' ', 'f', 'o', 'n', 'd', 'o', ' ', 'd', 'e', 'l', ' ', 'r', 'í', 'o', ' ', 'a', 'r', 'r', 'a', 's', 't', 'r', 'a', 'n', 'd', 'o', ' ', 'l', 'o', 's', ' ', 'd', 'o', 's', ' ', 'l', 'i', 'n', 'g', 'o', 't', 'e', 's', ' ', 'd', 'e', ' ', 'h', 'i', 'e', 'r', 'r', 'o', ' ', 'y', ' ', 'r', 'e', 'c', 'i', 't', 'a', 'n', 'd', 'o', ' ', 'e', 'n', ' ', 'v', 'o', 'z', ' ', 'a', 'l', 't', 'a', ' ', 'e', 'l', ' ', 'c', 'o', 'n', 'j', 'u', 'r', 'o', ' ', 'd', 'e', ' ', 'm', 'e', 'l', 'q', 'u', 'í', 'a', 'd', 'e', 's', ' ', 'l', 'o', ' ', 'ú', 'n', 'i', 'c', 'o', ' ', 'q', 'u', 'e', ' ', 'l', 'o', 'g', 'r', 'ó', ' ', 'd', 'e', 's', 'e', 'n', 't', 'e', 'r', 'r', 'a', 'r', ' ', 'f', 'u', 'e', ' ', 'u', 'n', 'a', ' ', 'a', 'r', 'm', 'a', 'd', 'u', 'r', 'a', ' ', 'd', 'e', 'l', ' ', 's', 'i', 'g', 'l', 'o', ' ', 'x', 'v', ' ', 'c', 'o', 'n', ' ', 't', 'o', 'd', 'a', 's', ' ', 's', 'u', 's', ' ', 'p', 'a', 'r', 't', 'e', 's', ' ', 's', 'o', 'l', 'd', 'a', 'd', 'a', 's', ' ', 'p', 'o', 'r', ' ', 'u', 'n', ' ', 'c', 'a', 's', 'c', 'o', 't', 'e', ' ', 'd', 'e', ' ', 'ó', 'x', 'i', 'd', 'o', ' ', 'c', 'u', 'y', 'o', ' ', 'i', 'n', 't', 'e', 'r', 'i', 'o', 'r', ' ', 't', 'e', 'n', 'í', 'a', ' ', 'l', 'a', ' ', 'r', 'e', 's', 'o', 'n', 'a', 'n', 'c', 'i', 'a', ' ', 'h', 'u', 'e', 'c', 'a', ' ', 'd', 'e', ' ', 'u', 'n', ' ', 'e', 'n', 'o', 'r', 'm', 'e', ' ', 'c', 'a', 'l', 'a', 'b', 'a', 'z', 'o', ' ', 'l', 'l', 'e', 'n', 'o', ' ', 'd', 'e', ' ', 'p', 'i', 'e', 'd', 'r', 'a', 's', ' ', 'c', 'u', 'a', 'n', 'd', 'o', ' ', 'j', 'o', 's', 'é', ' ', 'a', 'r', 'c', 'a', 'd', 'i', 'o', ' ', 'b', 'u', 'e', 'n', 'd', 'í', 'a', ' ', 'y', ' ', 'l', 'o', 's', ' ', 'c', 'u', 'a', 't', 'r', 'o', ' ', 'h', 'o', 'm', 'b', 'r', 'e', 's', ' ', 'd', 'e', ' ', 's', 'u', ' ', 'e', 'x', 'p', 'e', 'd', 'i', 'c', 'i', 'ó', 'n', ' ', 'l', 'o', 'g', 'r', 'a', 'r', 'o', 'n', ' ', 'd', 'e', 's', 'a', 'r', 't', 'i', 'c', 'u', 'l', 'a', 'r', ' ', 'l', 'a', ' ', 'a', 'r', 'm', 'a', 'd', 'u', 'r', 'a', ' ', 'e', 'n', 'c', 'o', 'n', 't', 'r', 'a', 'r', 'o', 'n', ' ', 'd', 'e', 'n', 't', 'r', 'o', ' ', 'u', 'n', ' ', 'e', 's', 'q', 'u', 'e', 'l', 'e', 't', 'o', ' ', 'c', 'a', 'l', 'c', 'i', 'f', 'i', 'c', 'a', 'd', 'o', ' ', 'q', 'u', 'e', ' ', 'l', 'l', 'e', 'v', 'a', 'b', 'a', ' ', 'c', 'o', 'l', 'g', 'a', 'd', 'o', ' ', 'e', 'n', ' ', 'e', 'l', ' ', 'c', 'u', 'e', 'l', 'l', 'o', ' ', 'u', 'n', ' ', 'r', 'e', 'l', 'i', 'c', 'a', 'r', 'i', 'o', ' ', 'd', 'e', ' ', 'c', 'o', 'b', 'r', 'e', ' ', 'c', 'o', 'n', ' ', 'u', 'n', ' ', 'r', 'i', 'z', 'o', ' ', 'd', 'e', ' ', 'm', 'u', 'j', 'e', 'r', ' '] [13, 22, 3, 8, 16, 20, 0, 1, 15, 16, 20, 0, 4, 5, 20, 17, 22, 29, 20, 0, 6, 19, 5, 14, 21, 5, 0, 1, 12, 0, 17, 5, 12, 16, 21, 31, 14, 0, 4, 5, 0, 6, 22, 20, 9, 12, 1, 13, 9, 5, 14, 21, 16, 0, 5, 12, 0, 3, 16, 19, 16, 14, 5, 12, 0, 1, 22, 19, 5, 12, 9, 1, 14, 16, 0, 2, 22, 5, 14, 4, 30, 1, 0, 8, 1, 2, 30, 1, 0, 4, 5, 0, 19, 5, 3, 16, 19, 4, 1, 19, 0, 1, 18, 22, 5, 12, 12, 1, 0, 21, 1, 19, 4, 5, 0, 19, 5, 13, 16, 21, 1, 0, 5, 14, 0, 18, 22, 5, 0, 20, 22, 0, 17, 1, 4, 19, 5, 0, 12, 16, 0, 12, 12, 5, 23, 31, 0, 1, 0, 3, 16, 14, 16, 3, 5, 19, 0, 5, 12, 0, 8, 9, 5, 12, 16, 0, 13, 1, 3, 16, 14, 4, 16, 0, 5, 19, 1, 0, 5, 14, 21, 16, 14, 3, 5, 20, 0, 22, 14, 1, 0, 1, 12, 4, 5, 1, 0, 4, 5, 0, 23, 5, 9, 14, 21, 5, 0, 3, 1, 20, 1, 20, 0, 4, 5, 0, 2, 1, 19, 19, 16, 0, 26, 0, 3, 1, 15, 1, 2, 19, 1, 23, 1, 0, 3, 16, 14, 20, 21, 19, 22, 9, 4, 1, 20, 0, 1, 0, 12, 1, 0, 16, 19, 9, 12, 12, 1, 0, 4, 5, 0, 22, 14, 0, 19, 30, 16, 0, 4, 5, 0, 1, 7, 22, 1, 20, 0, 4, 9, 28, 6, 1, 14, 1, 20, 0, 18, 22, 5, 0, 20, 5, 0, 17, 19, 5, 3, 9, 17, 9, 21, 1, 2, 1, 14, 0, 17, 16, 19, 0, 22, 14, 0, 12, 5, 3, 8, 16, 0, 4, 5, 0, 17, 9, 5, 4, 19, 1, 20, 0, 17, 22, 12, 9, 4, 1, 20, 0, 2, 12, 1, 14, 3, 1, 20, 0, 26, 0, 5, 14, 16, 19, 13, 5, 20, 0, 3, 16, 13, 16, 0, 8, 22, 5, 23, 16, 20, 0, 17, 19, 5, 8, 9, 20, 21, 31, 19, 9, 3, 16, 20, 0, 5, 12, 0, 13, 22, 14, 4, 16, 0, 5, 19, 1, 0, 21, 1, 14, 0, 19, 5, 3, 9, 5, 14, 21, 5, 0, 18, 22, 5, 0, 13, 22, 3, 8, 1, 20, 0, 3, 16, 20, 1, 20, 0, 3, 1, 19, 5, 3, 30, 1, 14, 0, 4, 5, 0, 14, 16, 13, 2, 19, 5, 0, 26, 0, 17, 1, 19, 1, 0, 13, 5, 14, 3, 9, 16, 14, 1, 19, 12, 1, 20, 0, 8, 1, 2, 30, 1, 0, 18, 22, 5, 0, 20, 5, 15, 1, 12, 1, 19, 30, 1, 20, 0, 3, 16, 14, 0, 5, 12, 0, 4, 5, 4, 16, 0, 21, 16, 4, 16, 20, 0, 12, 16, 20, 0, 1, 15, 16, 20, 0, 17, 16, 19, 0, 5, 12, 0, 13, 5, 20, 0, 4, 5, 0, 13, 1, 19, 27, 16, 0, 22, 14, 1, 0, 6, 1, 13, 9, 12, 9, 1, 0, 4, 5, 0, 7, 9, 21, 1, 14, 16, 20, 0, 4, 5, 20, 1, 19, 19, 1, 17, 1, 4, 16, 20, 0, 17, 12, 1, 14, 21, 1, 2, 1, 0, 20, 22, 0, 3, 1, 19, 17, 1, 0, 3, 5, 19, 3, 1, 0, 4, 5, 0, 12, 1, 0, 1, 12, 4, 5, 1, 0, 26, 0, 3, 16, 14, 0, 22, 14, 0, 7, 19, 1, 14, 4, 5, 0, 1, 12, 2, 16, 19, 16, 21, 16, 0, 4, 5, 0, 17, 9, 21, 16, 20, 0, 26, 0, 21, 9, 13, 2, 1, 12, 5, 20, 0, 4, 1, 2, 1, 14, 0, 1, 0, 3, 16, 14, 16, 3, 5, 19, 0, 12, 16, 20, 0, 14, 22, 5, 23, 16, 20, 0, 9, 14, 23, 5, 14, 21, 16, 20, 0, 17, 19, 9, 13, 5, 19, 16, 0, 12, 12, 5, 23, 1, 19, 16, 14, 0, 5, 12, 0, 9, 13, 28, 14, 0, 22, 14, 0, 7, 9, 21, 1, 14, 16, 0, 3, 16, 19, 17, 22, 12, 5, 14, 21, 16, 0, 4, 5, 0, 2, 1, 19, 2, 1, 0, 13, 16, 14, 21, 1, 19, 1, 27, 0, 26, 0, 13, 1, 14, 16, 20, 0, 4, 5, 0, 7, 16, 19, 19, 9, 31, 14, 0, 18, 22, 5, 0, 20, 5, 0, 17, 19, 5, 20, 5, 14, 21, 31, 0, 3, 16, 14, 0, 5, 12, 0, 14, 16, 13, 2, 19, 5, 0, 4, 5, 0, 13, 5, 12, 18, 22, 9, 1, 4, 5, 20, 0, 8, 9, 27, 16, 0, 22, 14, 1, 0, 21, 19, 22, 3, 22, 12, 5, 14, 21, 1, 0, 4, 5, 13, 16, 20, 21, 19, 1, 3, 9, 31, 14, 0, 17, 32, 2, 12, 9, 3, 1, 0, 4, 5, 0, 12, 16, 0, 18, 22, 5, 0, 29, 12, 0, 13, 9, 20, 13, 16, 0, 12, 12, 1, 13, 1, 2, 1, 0, 12, 1, 0, 16, 3, 21, 1, 23, 1, 0, 13, 1, 19, 1, 23, 9, 12, 12, 1, 0, 4, 5, 0, 12, 16, 20, 0, 20, 1, 2, 9, 16, 20, 0, 1, 12, 18, 22, 9, 13, 9, 20, 21, 1, 20, 0, 4, 5, 0, 13, 1, 3, 5, 4, 16, 14, 9, 1, 0, 6, 22, 5, 0, 4, 5, 0, 3, 1, 20, 1, 0, 5, 14, 0, 3, 1, 20, 1, 0, 1, 19, 19, 1, 20, 21, 19, 1, 14, 4, 16, 0, 4, 16, 20, 0, 12, 9, 14, 7, 16, 21, 5, 20, 0, 13, 5, 21, 28, 12, 9, 3, 16, 20, 0, 26, 0, 21, 16, 4, 16, 0, 5, 12, 0, 13, 22, 14, 4, 16, 0, 20, 5, 0, 5, 20, 17, 1, 14, 21, 31, 0, 1, 12, 0, 23, 5, 19, 0, 18, 22, 5, 0, 12, 16, 20, 0, 3, 1, 12, 4, 5, 19, 16, 20, 0, 12, 1, 20, 0, 17, 1, 9, 12, 1, 20, 0, 12, 1, 20, 0, 21, 5, 14, 1, 27, 1, 20, 0, 26, 0, 12, 16, 20, 0, 1, 14, 1, 6, 5, 20, 0, 20, 5, 0, 3, 1, 30, 1, 14, 0, 4, 5, 0, 20, 22, 0, 20, 9, 21, 9, 16, 0, 26, 0, 12, 1, 20, 0, 13, 1, 4, 5, 19, 1, 20, 0, 3, 19, 22, 10, 30, 1, 14, 0, 17, 16, 19, 0, 12, 1, 0, 4, 5, 20, 5, 20, 17, 5, 19, 1, 3, 9, 31, 14, 0, 4, 5, 0, 12, 16, 20, 0, 3, 12, 1, 23, 16, 20, 0, 26, 0, 12, 16, 20, 0, 21, 16, 19, 14, 9, 12, 12, 16, 20, 0, 21, 19, 1, 21, 1, 14, 4, 16, 0, 4, 5, 0, 4, 5, 20, 5, 14, 3, 12, 1, 23, 1, 19, 20, 5, 0, 26, 0, 1, 22, 14, 0, 12, 16, 20, 0, 16, 2, 10, 5, 21, 16, 20, 0, 17, 5, 19, 4, 9, 4, 16, 20, 0, 4, 5, 20, 4, 5, 0, 8, 1, 3, 30, 1, 0, 13, 22, 3, 8, 16, 0, 21, 9, 5, 13, 17, 16, 0, 1, 17, 1, 19, 5, 3, 30, 1, 14, 0, 17, 16, 19, 0, 4, 16, 14, 4, 5, 0, 13, 28, 20, 0, 20, 5, 0, 12, 5, 20, 0, 8, 1, 2, 30, 1, 0, 2, 22, 20, 3, 1, 4, 16, 0, 26, 0, 20, 5, 0, 1, 19, 19, 1, 20, 21, 19, 1, 2, 1, 14, 0, 5, 14, 0, 4, 5, 20, 2, 1, 14, 4, 1, 4, 1, 0, 21, 22, 19, 2, 22, 12, 5, 14, 21, 1, 0, 4, 5, 21, 19, 28, 20, 0, 4, 5, 0, 12, 16, 20, 0, 6, 9, 5, 19, 19, 16, 20, 0, 13, 28, 7, 9, 3, 16, 20, 0, 4, 5, 0, 13, 5, 12, 18, 22, 30, 1, 4, 5, 20, 0, 12, 1, 20, 0, 3, 16, 20, 1, 20, 0, 21, 9, 5, 14, 5, 14, 0, 23, 9, 4, 1, 0, 17, 19, 16, 17, 9, 1, 0, 17, 19, 5, 7, 16, 14, 1, 2, 1, 0, 5, 12, 0, 7, 9, 21, 1, 14, 16, 0, 3, 16, 14, 0, 28, 20, 17, 5, 19, 16, 0, 1, 3, 5, 14, 21, 16, 0, 21, 16, 4, 16, 0, 5, 20, 0, 3, 22, 5, 20, 21, 9, 31, 14, 0, 4, 5, 0, 4, 5, 20, 17, 5, 19, 21, 1, 19, 12, 5, 20, 0, 5, 12, 0, 28, 14, 9, 13, 1, 0, 10, 16, 20, 29, 0, 1, 19, 3, 1, 4, 9, 16, 0, 2, 22, 5, 14, 4, 30, 1, 0, 3, 22, 26, 1, 0, 4, 5, 20, 1, 6, 16, 19, 1, 4, 1, 0, 9, 13, 1, 7, 9, 14, 1, 3, 9, 31, 14, 0, 9, 2, 1, 0, 20, 9, 5, 13, 17, 19, 5, 0, 13, 28, 20, 0, 12, 5, 10, 16, 20, 0, 18, 22, 5, 0, 5, 12, 0, 9, 14, 7, 5, 14, 9, 16, 0, 4, 5, 0, 12, 1, 0, 14, 1, 21, 22, 19, 1, 12, 5, 27, 1, 0, 26, 0, 1, 22, 14, 0, 13, 28, 20, 0, 1, 12, 12, 28, 0, 4, 5, 12, 0, 13, 9, 12, 1, 7, 19, 16, 0, 26, 0, 12, 1, 0, 13, 1, 7, 9, 1, 0, 17, 5, 14, 20, 31, 0, 18, 22, 5, 0, 5, 19, 1, 0, 17, 16, 20, 9, 2, 12, 5, 0, 20, 5, 19, 23, 9, 19, 20, 5, 0, 4, 5, 0, 1, 18, 22, 5, 12, 12, 1, 0, 9, 14, 23, 5, 14, 3, 9, 31, 14, 0, 9, 14, 32, 21, 9, 12, 0, 17, 1, 19, 1, 0, 4, 5, 20, 5, 14, 21, 19, 1, 15, 1, 19, 0, 5, 12, 0, 16, 19, 16, 0, 4, 5, 0, 12, 1, 0, 21, 9, 5, 19, 19, 1, 0, 13, 5, 12, 18, 22, 30, 1, 4, 5, 20, 0, 18, 22, 5, 0, 5, 19, 1, 0, 22, 14, 0, 8, 16, 13, 2, 19, 5, 0, 8, 16, 14, 19, 1, 4, 16, 0, 12, 5, 0, 17, 19, 5, 23, 9, 14, 16, 0, 17, 1, 19, 1, 0, 5, 20, 16, 0, 14, 16, 0, 20, 9, 19, 23, 5, 0, 17, 5, 19, 16, 0, 10, 16, 20, 29, 0, 1, 19, 3, 1, 4, 9, 16, 0, 2, 22, 5, 14, 4, 30, 1, 0, 14, 16, 0, 3, 19, 5, 30, 1, 0, 5, 14, 0, 1, 18, 22, 5, 12, 0, 21, 9, 5, 13, 17, 16, 0, 5, 14, 0, 12, 1, 0, 8, 16, 14, 19, 1, 4, 5, 27, 0, 4, 5, 0, 12, 16, 20, 0, 7, 9, 21, 1, 14, 16, 20, 0, 1, 20, 30, 0, 18, 22, 5, 0, 3, 1, 13, 2, 9, 31, 0, 20, 22, 0, 13, 22, 12, 16, 0, 26, 0, 22, 14, 1, 0, 17, 1, 19, 21, 9, 4, 1, 0, 4, 5, 0, 3, 8, 9, 23, 16, 20, 0, 17, 16, 19, 0, 12, 16, 20, 0, 4, 16, 20, 0, 12, 9, 14, 7, 16, 21, 5, 20, 0, 9, 13, 1, 14, 21, 1, 4, 16, 20, 0, 32, 19, 20, 22, 12, 1, 0, 9, 7, 22, 1, 19, 28, 14, 0, 20, 22, 0, 13, 22, 10, 5, 19, 0, 18, 22, 5, 0, 3, 16, 14, 21, 1, 2, 1, 0, 3, 16, 14, 0, 1, 18, 22, 5, 12, 12, 16, 20, 0, 1, 14, 9, 13, 1, 12, 5, 20, 0, 17, 1, 19, 1, 0, 5, 14, 20, 1, 14, 3, 8, 1, 19, 0, 5, 12, 0, 4, 5, 20, 13, 5, 4, 19, 1, 4, 16, 0, 17, 1, 21, 19, 9, 13, 16, 14, 9, 16, 0, 4, 16, 13, 29, 20, 21, 9, 3, 16, 0, 14, 16, 0, 3, 16, 14, 20, 9, 7, 22, 9, 31, 0, 4, 9, 20, 22, 1, 4, 9, 19, 12, 16, 0, 13, 22, 26, 0, 17, 19, 16, 14, 21, 16, 0, 8, 1, 0, 4, 5, 0, 20, 16, 2, 19, 1, 19, 14, 16, 20, 0, 16, 19, 16, 0, 17, 1, 19, 1, 0, 5, 13, 17, 5, 4, 19, 1, 19, 0, 12, 1, 0, 3, 1, 20, 1, 0, 19, 5, 17, 12, 9, 3, 31, 0, 20, 22, 0, 13, 1, 19, 9, 4, 16, 0, 4, 22, 19, 1, 14, 21, 5, 0, 23, 1, 19, 9, 16, 20, 0, 13, 5, 20, 5, 20, 0, 20, 5, 0, 5, 13, 17, 5, 15, 31, 0, 5, 14, 0, 4, 5, 13, 16, 20, 21, 19, 1, 19, 0, 5, 12, 0, 1, 3, 9, 5, 19, 21, 16, 0, 4, 5, 0, 20, 22, 20, 0, 3, 16, 14, 10, 5, 21, 22, 19, 1, 20, 0, 5, 25, 17, 12, 16, 19, 31, 0, 17, 1, 12, 13, 16, 0, 1, 0, 17, 1, 12, 13, 16, 0, 12, 1, 0, 19, 5, 7, 9, 31, 14, 0, 9, 14, 3, 12, 22, 20, 9, 23, 5, 0, 5, 12, 0, 6, 16, 14, 4, 16, 0, 4, 5, 12, 0, 19, 30, 16, 0, 1, 19, 19, 1, 20, 21, 19, 1, 14, 4, 16, 0, 12, 16, 20, 0, 4, 16, 20, 0, 12, 9, 14, 7, 16, 21, 5, 20, 0, 4, 5, 0, 8, 9, 5, 19, 19, 16, 0, 26, 0, 19, 5, 3, 9, 21, 1, 14, 4, 16, 0, 5, 14, 0, 23, 16, 27, 0, 1, 12, 21, 1, 0, 5, 12, 0, 3, 16, 14, 10, 22, 19, 16, 0, 4, 5, 0, 13, 5, 12, 18, 22, 30, 1, 4, 5, 20, 0, 12, 16, 0, 32, 14, 9, 3, 16, 0, 18, 22, 5, 0, 12, 16, 7, 19, 31, 0, 4, 5, 20, 5, 14, 21, 5, 19, 19, 1, 19, 0, 6, 22, 5, 0, 22, 14, 1, 0, 1, 19, 13, 1, 4, 22, 19, 1, 0, 4, 5, 12, 0, 20, 9, 7, 12, 16, 0, 25, 23, 0, 3, 16, 14, 0, 21, 16, 4, 1, 20, 0, 20, 22, 20, 0, 17, 1, 19, 21, 5, 20, 0, 20, 16, 12, 4, 1, 4, 1, 20, 0, 17, 16, 19, 0, 22, 14, 0, 3, 1, 20, 3, 16, 21, 5, 0, 4, 5, 0, 31, 25, 9, 4, 16, 0, 3, 22, 26, 16, 0, 9, 14, 21, 5, 19, 9, 16, 19, 0, 21, 5, 14, 30, 1, 0, 12, 1, 0, 19, 5, 20, 16, 14, 1, 14, 3, 9, 1, 0, 8, 22, 5, 3, 1, 0, 4, 5, 0, 22, 14, 0, 5, 14, 16, 19, 13, 5, 0, 3, 1, 12, 1, 2, 1, 27, 16, 0, 12, 12, 5, 14, 16, 0, 4, 5, 0, 17, 9, 5, 4, 19, 1, 20, 0, 3, 22, 1, 14, 4, 16, 0, 10, 16, 20, 29, 0, 1, 19, 3, 1, 4, 9, 16, 0, 2, 22, 5, 14, 4, 30, 1, 0, 26, 0, 12, 16, 20, 0, 3, 22, 1, 21, 19, 16, 0, 8, 16, 13, 2, 19, 5, 20, 0, 4, 5, 0, 20, 22, 0, 5, 25, 17, 5, 4, 9, 3, 9, 31, 14, 0, 12, 16, 7, 19, 1, 19, 16, 14, 0, 4, 5, 20, 1, 19, 21, 9, 3, 22, 12, 1, 19, 0, 12, 1, 0, 1, 19, 13, 1, 4, 22, 19, 1, 0, 5, 14, 3, 16, 14, 21, 19, 1, 19, 16, 14, 0, 4, 5, 14, 21, 19, 16, 0, 22, 14, 0, 5, 20, 18, 22, 5, 12, 5, 21, 16, 0, 3, 1, 12, 3, 9, 6, 9, 3, 1, 4, 16, 0, 18, 22, 5, 0, 12, 12, 5, 23, 1, 2, 1, 0, 3, 16, 12, 7, 1, 4, 16, 0, 5, 14, 0, 5, 12, 0, 3, 22, 5, 12, 12, 16, 0, 22, 14, 0, 19, 5, 12, 9, 3, 1, 19, 9, 16, 0, 4, 5, 0, 3, 16, 2, 19, 5, 0, 3, 16, 14, 0, 22, 14, 0, 19, 9, 27, 16, 0, 4, 5, 0, 13, 22, 10, 5, 19, 0]
Este tipo de forma de tokenización no es una tokenización que se suela usar a día de hoy en el NLP. El principal motivo es que realmente los caracteres no tienen "unidad de significado" como para que su tokenización aporte significado (por ejemplo, dos anagramas tienen los mismos tokens de letra pero significado completamente distinto). Esta tokenización la hemos comentando simplemente por objetivo divulgativo.
Tokenización de palabra¶
La tokenización a nivel de palabra es mucho más común, ya que refleja realmente el vocabulario de un idioma. A diferencia de la tokenización a nivel de letra, este tipo de tokenización no es completa (i.e. siempre podemos encontrarnos palabras nuevas que no se pueden tokenizar). Para ello se suele definir un token reservado <UNK> como "cajón de sastre" para tokenizar cualquier palabra fuera del vocabulario.
def word_tokenize(text, vocab):
tokens = []
for word in text.split(' '):
# Si la palabra no está en el vocabulario le ponemos el token "unkown"
if word not in vocab:
token = wordnet_vocab['<UNK>']
# Si la palabra está en el vocabulario le ponemos el token de la palabra
else:
token = wordnet_vocab[word]
tokens.append(token)
return tokens
print(text.split())
print(word_tokenize(text, wordnet_vocab))
['muchos', 'años', 'después', 'frente', 'al', 'pelotón', 'de', 'fusilamiento', 'el', 'coronel', 'aureliano', 'buendía', 'había', 'de', 'recordar', 'aquella', 'tarde', 'remota', 'en', 'que', 'su', 'padre', 'lo', 'llevó', 'a', 'conocer', 'el', 'hielo', 'macondo', 'era', 'entonces', 'una', 'aldea', 'de', 'veinte', 'casas', 'de', 'barro', 'y', 'cañabrava', 'construidas', 'a', 'la', 'orilla', 'de', 'un', 'río', 'de', 'aguas', 'diáfanas', 'que', 'se', 'precipitaban', 'por', 'un', 'lecho', 'de', 'piedras', 'pulidas', 'blancas', 'y', 'enormes', 'como', 'huevos', 'prehistóricos', 'el', 'mundo', 'era', 'tan', 'reciente', 'que', 'muchas', 'cosas', 'carecían', 'de', 'nombre', 'y', 'para', 'mencionarlas', 'había', 'que', 'señalarías', 'con', 'el', 'dedo', 'todos', 'los', 'años', 'por', 'el', 'mes', 'de', 'marzo', 'una', 'familia', 'de', 'gitanos', 'desarrapados', 'plantaba', 'su', 'carpa', 'cerca', 'de', 'la', 'aldea', 'y', 'con', 'un', 'grande', 'alboroto', 'de', 'pitos', 'y', 'timbales', 'daban', 'a', 'conocer', 'los', 'nuevos', 'inventos', 'primero', 'llevaron', 'el', 'imán', 'un', 'gitano', 'corpulento', 'de', 'barba', 'montaraz', 'y', 'manos', 'de', 'gorrión', 'que', 'se', 'presentó', 'con', 'el', 'nombre', 'de', 'melquiades', 'hizo', 'una', 'truculenta', 'demostración', 'pública', 'de', 'lo', 'que', 'él', 'mismo', 'llamaba', 'la', 'octava', 'maravilla', 'de', 'los', 'sabios', 'alquimistas', 'de', 'macedonia', 'fue', 'de', 'casa', 'en', 'casa', 'arrastrando', 'dos', 'lingotes', 'metálicos', 'y', 'todo', 'el', 'mundo', 'se', 'espantó', 'al', 'ver', 'que', 'los', 'calderos', 'las', 'pailas', 'las', 'tenazas', 'y', 'los', 'anafes', 'se', 'caían', 'de', 'su', 'sitio', 'y', 'las', 'maderas', 'crujían', 'por', 'la', 'desesperación', 'de', 'los', 'clavos', 'y', 'los', 'tornillos', 'tratando', 'de', 'desenclavarse', 'y', 'aun', 'los', 'objetos', 'perdidos', 'desde', 'hacía', 'mucho', 'tiempo', 'aparecían', 'por', 'donde', 'más', 'se', 'les', 'había', 'buscado', 'y', 'se', 'arrastraban', 'en', 'desbandada', 'turbulenta', 'detrás', 'de', 'los', 'fierros', 'mágicos', 'de', 'melquíades', 'las', 'cosas', 'tienen', 'vida', 'propia', 'pregonaba', 'el', 'gitano', 'con', 'áspero', 'acento', 'todo', 'es', 'cuestión', 'de', 'despertarles', 'el', 'ánima', 'josé', 'arcadio', 'buendía', 'cuya', 'desaforada', 'imaginación', 'iba', 'siempre', 'más', 'lejos', 'que', 'el', 'ingenio', 'de', 'la', 'naturaleza', 'y', 'aun', 'más', 'allá', 'del', 'milagro', 'y', 'la', 'magia', 'pensó', 'que', 'era', 'posible', 'servirse', 'de', 'aquella', 'invención', 'inútil', 'para', 'desentrañar', 'el', 'oro', 'de', 'la', 'tierra', 'melquíades', 'que', 'era', 'un', 'hombre', 'honrado', 'le', 'previno', 'para', 'eso', 'no', 'sirve', 'pero', 'josé', 'arcadio', 'buendía', 'no', 'creía', 'en', 'aquel', 'tiempo', 'en', 'la', 'honradez', 'de', 'los', 'gitanos', 'así', 'que', 'cambió', 'su', 'mulo', 'y', 'una', 'partida', 'de', 'chivos', 'por', 'los', 'dos', 'lingotes', 'imantados', 'úrsula', 'iguarán', 'su', 'mujer', 'que', 'contaba', 'con', 'aquellos', 'animales', 'para', 'ensanchar', 'el', 'desmedrado', 'patrimonio', 'doméstico', 'no', 'consiguió', 'disuadirlo', 'muy', 'pronto', 'ha', 'de', 'sobrarnos', 'oro', 'para', 'empedrar', 'la', 'casa', 'replicó', 'su', 'marido', 'durante', 'varios', 'meses', 'se', 'empeñó', 'en', 'demostrar', 'el', 'acierto', 'de', 'sus', 'conjeturas', 'exploró', 'palmo', 'a', 'palmo', 'la', 'región', 'inclusive', 'el', 'fondo', 'del', 'río', 'arrastrando', 'los', 'dos', 'lingotes', 'de', 'hierro', 'y', 'recitando', 'en', 'voz', 'alta', 'el', 'conjuro', 'de', 'melquíades', 'lo', 'único', 'que', 'logró', 'desenterrar', 'fue', 'una', 'armadura', 'del', 'siglo', 'xv', 'con', 'todas', 'sus', 'partes', 'soldadas', 'por', 'un', 'cascote', 'de', 'óxido', 'cuyo', 'interior', 'tenía', 'la', 'resonancia', 'hueca', 'de', 'un', 'enorme', 'calabazo', 'lleno', 'de', 'piedras', 'cuando', 'josé', 'arcadio', 'buendía', 'y', 'los', 'cuatro', 'hombres', 'de', 'su', 'expedición', 'lograron', 'desarticular', 'la', 'armadura', 'encontraron', 'dentro', 'un', 'esqueleto', 'calcificado', 'que', 'llevaba', 'colgado', 'en', 'el', 'cuello', 'un', 'relicario', 'de', 'cobre', 'con', 'un', 'rizo', 'de', 'mujer'] [-1, 2379, -1, 9531, 742, -1, 6016, -1, -1, 5384, -1, -1, -1, 6016, 17904, -1, 20332, 18181, 7684, -1, -1, 15492, -1, -1, 6, 5071, -1, 10684, -1, 8146, 7992, -1, 814, 6016, 21638, -1, 6016, 2569, 22136, -1, -1, 6, 12347, 15308, 6016, -1, 18755, 6016, -1, -1, -1, -1, -1, 16769, -1, 12541, 6016, -1, -1, -1, 22136, -1, -1, -1, -1, -1, 14362, 8146, 20288, 17859, -1, -1, 5459, -1, 6016, 14806, 22136, -1, -1, -1, -1, -1, -1, -1, 6099, -1, -1, 2379, 16769, -1, 13756, 6016, 13448, -1, 9028, 6016, -1, -1, -1, -1, -1, 4014, 6016, 12347, 814, 22136, -1, -1, 10183, 785, 6016, -1, 22136, -1, -1, 6, 5071, -1, -1, -1, 17040, -1, -1, 11307, -1, 10008, -1, 6016, 2520, -1, 22136, -1, 6016, -1, -1, -1, -1, -1, -1, 14806, 6016, -1, -1, -1, -1, 6204, -1, 6016, -1, -1, -1, -1, -1, 12347, 15047, 13369, 6016, -1, -1, -1, 6016, 13075, -1, 6016, 3765, 7684, 3765, -1, 7214, -1, -1, 22136, 20772, -1, 14362, -1, -1, 742, 21694, -1, -1, -1, -1, -1, -1, -1, 22136, -1, -1, -1, -1, 6016, -1, 19507, 22136, -1, -1, -1, 16769, 12347, 6506, 6016, -1, -1, 22136, -1, -1, -1, 6016, -1, 22136, -1, -1, -1, -1, -1, -1, 14308, 20662, -1, 16769, -1, -1, -1, -1, -1, 3218, 22136, -1, -1, 7684, -1, -1, 6763, 6016, -1, -1, -1, 6016, -1, -1, 5459, -1, 21790, -1, -1, -1, 10008, -1, 22307, 206, 20772, -1, 5770, 6016, -1, -1, -1, -1, 1699, -1, -1, -1, 11171, -1, 19348, -1, -1, -1, -1, 11593, 6016, 12347, 14567, 22136, -1, -1, 926, -1, 13921, 22136, 12347, 13138, -1, -1, 8146, 16819, 19246, 6016, -1, 11921, 11954, -1, -1, -1, 15326, 6016, 12347, 20666, -1, -1, 8146, -1, 10873, -1, -1, -1, -1, -1, 14790, -1, -1, -1, 1699, -1, 14790, -1, 7684, -1, 20662, 7684, 12347, 10912, 6016, -1, -1, -1, -1, -1, -1, -1, 22136, -1, 15761, 6016, -1, 16769, -1, 7214, -1, -1, -1, -1, -1, 14338, -1, -1, -1, -1, 1308, -1, 7942, -1, -1, 15860, 7193, 14790, -1, -1, 14418, 17187, -1, 6016, -1, 15326, -1, -1, 12347, 3765, -1, -1, 13400, -1, -1, -1, -1, -1, 7684, 6206, -1, 242, 6016, 20127, -1, -1, -1, 6, -1, 12347, 18063, -1, -1, 9387, -1, 18755, -1, -1, 7214, -1, 6016, 10688, 22136, -1, 7684, 21982, 987, -1, -1, 6016, -1, -1, 22348, -1, -1, 6495, -1, -1, 1786, -1, 19363, -1, -1, -1, 20127, -1, -1, 16769, -1, -1, 6016, -1, -1, 11834, -1, 12347, -1, -1, 6016, -1, 7913, -1, 12881, 6016, -1, -1, -1, 1699, -1, 22136, -1, 5720, -1, 6016, -1, 8858, -1, -1, 12347, 1786, -1, -1, -1, -1, -1, -1, -1, -1, 7684, -1, 5754, -1, -1, 6016, 4589, -1, -1, 18584, 6016, 14338, -1]
2.3. - N-gramas¶
En algunos casos no se usan ni letras ni palabras, sino una combinación de ambos. Una forma de combinar los dos es por medio de ngramas
Tokenización de N-gramas de letras¶
La tokenización por letras o por palabras buscan representar numéricamente símbolos del lenguaje. Sin embargo, otras veces se usan técnicas basadas en estadística. Una de ellas son los N-gramas. Los N-gramas a nivel de letra consiste en un "conjunto" de letras que tienen un token asigniado.
N-gram
from nltk.util import ngrams
n = 2
ngramas = [''.join(ngram) for ngram in ngrams(text,n)]
ngramas[0:10]
['mu', 'uc', 'ch', 'ho', 'os', 's ', ' a', 'añ', 'ño', 'os']
from nltk.util import ngrams
n=5
ngramas = [''.join(ngram) for ngram in ngrams(text,n)]
ngramas[0:7]
['mucho', 'uchos', 'chos ', 'hos a', 'os añ', 's año', ' años']
N-gram word boundaries
El problema que tienen los ngramas a nivel de letras de muchos caracteres es que, como vemos, a veces mezclan palabras. Hay veces que se quiere evitar ello haciendo ngramas solamente de las palabras concretas
from nltk.util import ngrams
n = 3
ngramas = []
# Separamos cada letra
for word in text.split(' '):
ngramas += [''.join(ngram) for ngram in ngrams(word,n)]
ngramas[0:7]
['muc', 'uch', 'cho', 'hos', 'año', 'ños', 'des']
N-gram with padding
El problema que tienen los N-gramas superiores a 4-gramas es que perdemos la capacidad de representar sufijos y prefijos. La técnica de padding permite resolver este problema añadiendo los tokens de inicio y fin de palabra,
from nltk.util import ngrams
n = 3
ngramas = []
# Separamos cada letra
for word in text.split(' '):
ngramas += [''.join(ngram) for ngram in ngrams(word,n, pad_left=True, left_pad_symbol='<s>', pad_right=True, right_pad_symbol='</s>')]
ngramas[0:7]
['<s><s>m', '<s>mu', 'muc', 'uch', 'cho', 'hos', 'os</s>']
El token "begin of string" y "end of string" son dos tokens recurrentes en el mundo del NLP. Como vemos, sirven para dar estructura y poder representar, por ejemplo, cuando hay un inicio y fin de palabra
NOTA: Aquí hemos representado los tokens como
<s>y</s>, pero muchas otras veces tienen otras nomenclaturas como por ejemplo[BOS]y[EOS]
Every-gram
Cuando usamos Ngramas a nivel de caracter, también suele ser útil querer que haya ngramas de diferentes dimensiones para poder construir cualquier palabra. Para ello es común construir los everygramas esto nos permite construir un vocabulario completo basado en ngramas
from nltk.lm.preprocessing import padded_everygram_pipeline
n = 5
train_data, _ = padded_everygram_pipeline(n, text.split())
vocab = set([''.join(ngram) for word in train_data for ngram in word])
vocab = {word:id for id,word in enumerate(vocab)}
[{i:j} for i,j in list(vocab.items())[0:10]]
[{'te</s></s></s>': 0},
{'<s>ded': 1},
{'lejos': 2},
{'sabi': 3},
{'<s><s>mel': 4},
{'esen': 5},
{'ai': 6},
{'rve': 7},
{'<s><s><s>as': 8},
{'rba': 9}]
Byte Pair Encoding
Un tokenizador más moderno es el Byte Pair Encoding. Este tokenizador parte de las letras y "aprende" del dataset a ver frecuencias comunes de coocurrencia. Las coocurrencias comunes le asigna un nuevo token, así hasta llevar
from tokenizers import Tokenizer, models, ByteLevelBPETokenizer
# Esto es magia de la librerís de transformer
tokenizer = Tokenizer(models.BPE())
tokenizer.train_from_iterator(corpus)
output = tokenizer.encode(text)
# Mostrar resultados
print("Tokens:", output.tokens)
print("IDs:", output.ids)
#print("Decoded:", tokenizer.decode(output.ids))
Tokens: ['muchos años ', 'después ', 'frente al ', 'pelotón de ', 'fusilamiento ', 'el coronel ', 'aur', 'eli', 'ano ', 'bu', 'endía ', 'había de ', 'recordar ', 'aquella ', 'tarde remota ', 'en que su padre lo llevó a conocer el hiel', 'o ', 'ma', 'con', 'do ', 'era entonces ', 'una ', 'alde', 'a de veinte ', 'casas de barro y cañabr', 'ava ', 'constru', 'idas ', 'a la orilla de un ', 'río de aguas diáfan', 'as que se ', 'precipit', 'aban ', 'por un ', 'lecho de ', 'piedras ', 'puli', 'das ', 'blancas y ', 'enormes ', 'como ', 'huev', 'os ', 'prehistóri', 'cos ', 'el mundo ', 'era tan ', 'reci', 'ente que ', 'muchas ', 'cosas ', 'carecían de ', 'nombre ', 'y ', 'para ', 'mencion', 'arlas ', 'había que ', 'señal', 'arías ', 'con el ', 'dedo ', 'todos los ', 'años ', 'por el ', 'mes de ', 'marzo ', 'una ', 'famili', 'a de ', 'gitanos ', 'desarr', 'ap', 'ados ', 'plant', 'aba su ', 'carpa ', 'cerca de la ', 'alde', 'a y ', 'con un ', 'grande ', 'alboroto de ', 'pit', 'os y ', 'tim', 'bales ', 'daban a ', 'conocer ', 'los nuevos ', 'invent', 'os ', 'primero ', 'llevaron el ', 'im', 'án ', 'un ', 'gitano ', 'corpul', 'ent', 'o de ', 'barba ', 'montar', 'az ', 'y man', 'os de ', 'gor', 'ri', 'ón ', 'que se ', 'present', 'ó con el nombre de ', 'mel', 'qui', 'ad', 'es ', 'hizo una ', 'trucul', 'enta ', 'demostración ', 'públic', 'a de lo que ', 'él mismo ', 'llam', 'aba la ', 'oc', 'tava ', 'maravi', 'lla ', 'de los sabi', 'os ', 'alquimi', 'stas de ', 'mac', 'ed', 'oni', 'a fue ', 'de casa en casa ', 'arrastrando ', 'dos ', 'lingotes ', 'metáli', 'cos y ', 'todo el mundo ', 'se espant', 'ó al ', 'ver ', 'que los ', 'calder', 'os las ', 'pai', 'las ', 'las ', 'tenaz', 'as y los ', 'anaf', 'es se ', 'caían ', 'de su ', 'siti', 'o y las ', 'maderas ', 'cruj', 'ían ', 'por la desesper', 'ación de los ', 'clav', 'os y los ', 'torni', 'llos ', 'tratando de ', 'desen', 'clav', 'arse y ', 'aun ', 'los objet', 'os perdidos ', 'desde hacía mucho tiempo ', 'aparecían ', 'por donde ', 'más ', 'se les ', 'había ', 'busc', 'ado y ', 'se arrastr', 'aban en ', 'des', 'ban', 'dada ', 'tur', 'bul', 'enta ', 'detrás de los ', 'fierros ', 'mági', 'cos de ', 'mel', 'quíad', 'es ', 'las cosas ', 'tienen ', 'vida ', 'propia ', 'pregon', 'aba el ', 'gitano ', 'con áspero ', 'ac', 'ento ', 'todo ', 'es cuesti', 'ón de ', 'despertar', 'les ', 'el ánim', 'a ', 'j', 'osé ', 'ar', 'cadi', 'o ', 'bu', 'endía ', 'cuya ', 'desaforada ', 'imaginación ', 'iba ', 'siempre más lej', 'os ', 'que el ', 'ingeni', 'o de la ', 'naturalez', 'a y ', 'aun ', 'más allá del ', 'milagr', 'o y la ', 'magi', 'a ', 'pensó que ', 'era posible ', 'servirse de ', 'aquella ', 'invenci', 'ón ', 'inútil para ', 'desentrañ', 'ar el or', 'o de la ', 'tierra ', 'mel', 'quíad', 'es que ', 'era un hombre ', 'honr', 'ado ', 'le ', 'previn', 'o para ', 'eso ', 'no sir', 've ', 'pero ', 'j', 'osé ', 'ar', 'cadi', 'o ', 'bu', 'endía ', 'no ', 'creía ', 'en aquel tiempo ', 'en la ', 'honrad', 'ez ', 'de los ', 'gitanos ', 'así que ', 'cambi', 'ó su ', 'mulo ', 'y una ', 'partida de ', 'chivos ', 'por los ', 'dos ', 'lingotes imant', 'ados ', 'ú', 'rsu', 'la ', 'iguar', 'án ', 'su ', 'mujer que ', 'contaba con ', 'aquellos ', 'animales ', 'para ', 'ensanch', 'ar el ', 'des', 'medr', 'ado ', 'patrimonio ', 'domésti', 'co ', 'no ', 'consiguió ', 'disuadi', 'rlo ', 'muy pronto ', 'ha de ', 'sobr', 'arnos ', 'oro para ', 'empedr', 'ar la casa ', 'replic', 'ó su ', 'marido ', 'durante ', 'varios meses ', 'se empeñó en ', 'demostr', 'ar el ', 'aciert', 'o de sus ', 'conjetur', 'as ', 'explor', 'ó palmo a palm', 'o la ', 'regi', 'ón ', 'inclusive el ', 'fondo del ', 'río ', 'arrastrando los ', 'dos ', 'ling', 'otes de ', 'hierro y ', 'reci', 'tando en ', 'voz alta ', 'el conjur', 'o de ', 'mel', 'quíad', 'es ', 'lo único que ', 'logró ', 'desenterr', 'ar ', 'fue una ', 'armadura del ', 'siglo ', 'xv ', 'con todas sus ', 'partes ', 'soldad', 'as por un ', 'casco', 'te de ', 'óxido ', 'cuyo interior ', 'tenía la ', 'resonancia ', 'huec', 'a de un ', 'enorme ', 'calab', 'azo ', 'lleno de ', 'piedras ', 'cuando ', 'j', 'osé ', 'ar', 'cadi', 'o ', 'bu', 'endía ', 'y los ', 'cuatro ', 'hombres de su ', 'expedición ', 'lograron ', 'desarticul', 'ar la ', 'armad', 'ura ', 'encontraron dentro un ', 'esquel', 'eto ', 'cal', 'cific', 'ado que ', 'llevaba ', 'colg', 'ado en el ', 'cuello un ', 'reli', 'cari', 'o de ', 'cobre ', 'con un ', 'riz', 'o de ', 'mujer '] IDs: [5207, 549, 2496, 4404, 12846, 5371, 1716, 200, 201, 358, 1957, 2007, 5717, 790, 18568, 23233, 95, 7530, 143, 125, 11672, 161, 1770, 9218, 23622, 17053, 1513, 5102, 23436, 22738, 7642, 5709, 245, 1256, 7303, 8560, 9239, 1032, 13623, 13482, 243, 3070, 109, 9422, 505, 15201, 3357, 597, 6103, 2370, 1360, 22999, 1301, 119, 195, 14264, 8932, 7879, 4092, 11112, 514, 16452, 1643, 918, 476, 14731, 9404, 161, 676, 171, 2955, 6021, 258, 359, 2134, 2184, 20670, 7335, 1770, 305, 574, 2595, 21242, 909, 448, 403, 14612, 14657, 1836, 9102, 1850, 109, 8043, 10189, 213, 838, 150, 4746, 7876, 135, 154, 8030, 12610, 1339, 27796, 256, 6577, 173, 185, 389, 3220, 28230, 2625, 244, 110, 127, 7040, 14111, 2871, 13208, 2029, 11620, 3783, 1003, 1001, 190, 25062, 3577, 776, 18058, 109, 4734, 16412, 3905, 241, 585, 25363, 15033, 10171, 328, 14263, 8386, 3410, 3094, 29183, 1027, 667, 1501, 5561, 26796, 14779, 156, 156, 6880, 6178, 26723, 5958, 17490, 556, 5429, 5559, 17565, 10012, 253, 17449, 7984, 2018, 6195, 18028, 3499, 1969, 7775, 2018, 17465, 1975, 15615, 22577, 21136, 18259, 4573, 252, 3340, 208, 887, 1506, 15641, 4013, 160, 841, 7856, 587, 2475, 2871, 21304, 22635, 8306, 3409, 2625, 731, 127, 2446, 10358, 1253, 1749, 6240, 906, 4746, 29819, 182, 523, 513, 28591, 406, 6315, 316, 27128, 93, 59, 5966, 100, 259, 95, 358, 1957, 1591, 14031, 10156, 1279, 18606, 109, 638, 4054, 356, 4131, 305, 1975, 21420, 3102, 2914, 7534, 93, 2801, 16299, 22032, 790, 4540, 185, 21473, 6779, 12536, 356, 2593, 2625, 731, 2050, 19166, 5198, 178, 159, 16998, 1428, 1729, 16164, 1804, 348, 59, 5966, 100, 259, 95, 358, 1957, 169, 3411, 13580, 223, 13967, 371, 594, 2955, 8353, 801, 1370, 9278, 3168, 8230, 10642, 1176, 328, 23244, 359, 91, 260, 105, 24694, 838, 166, 7074, 17558, 3596, 3833, 195, 5039, 698, 160, 20482, 178, 22756, 2313, 532, 169, 2757, 7714, 14787, 4713, 6582, 1464, 26226, 27013, 6087, 9694, 2750, 1370, 1867, 1041, 6359, 22939, 1754, 698, 11715, 2055, 23385, 117, 5228, 28269, 1450, 2344, 185, 13512, 10320, 10147, 19812, 328, 8842, 12003, 10469, 597, 25031, 10063, 27152, 154, 2625, 731, 127, 2662, 3252, 6025, 157, 4292, 22781, 4390, 25118, 11451, 8314, 1743, 15321, 9313, 25012, 8905, 20401, 2582, 10552, 8025, 2767, 4127, 17280, 3206, 10030, 8560, 291, 59, 5966, 100, 259, 95, 358, 1957, 1947, 1347, 18241, 21679, 12913, 16004, 577, 2639, 1555, 21128, 5365, 2728, 347, 27539, 2190, 1380, 1748, 2002, 20823, 4175, 1718, 154, 5360, 574, 4242, 154, 743]
Nota: Ejemplo detallado de Byte-Pair Encoding
Tokenización de N-gramas de palabras¶
from nltk.util import ngrams
n=2
ngramas = [' '.join(ngram) for ngram in ngrams(text.split(),n)]
ngramas[0:5]
['muchos años', 'años después', 'después frente', 'frente al', 'al pelotón']
from nltk.util import ngrams
n=3
ngramas = [' '.join(ngram) for ngram in ngrams(text.split(),n)]
ngramas[0:3]
['muchos años después', 'años después frente', 'después frente al']
from nltk.lm.preprocessing import padded_everygram_pipeline
n = 3
data, _ = padded_everygram_pipeline(n, [text.split(' ')])
vocab = set([' '.join(ngram) for word in data for ngram in word])
vocab = {word:id for id,word in enumerate(vocab)}
[{i:j} for i,j in list(vocab.items())[0:10]]
[{'': 0},
{'el nombre de': 1},
{'el desmedrado': 2},
{'gitano con': 3},
{'las maderas crujían': 4},
{'lejos': 5},
{'pública de': 6},
{'todos los': 7},
{'maderas crujían': 8},
{'los objetos': 9}]
Ejercicio - ¿Qué tokenización usar para un problema?¶
La tokenización es una forma de representar numéricamente texto. Hemos visto diferentes formas de tokenizar ¿Cuáles son los pros y contras de cada una?
Character tokenizer
- Pro:
- Contra:
Word tokenizer
- Pro:
- Contra:
Char n-gram tokenizer
- Pro:
- Contra:
Word n-gram tokenizer
- Pro:
- Contra:
3. - Representación vectorial¶
En la sección anterior hemos visto como representar texto de manera numérica a partir de tokens, sin embargo esta representación numérica no nos dice nada sobre cómo de parecidas son unas palabras y otras (p.e. la la palabra "balón" puede tener el token 34 y la palabra "pelota" el token 7624, aunque el significado de estas dos palabras es parecido, el token no nos dice nada)
Para resolver este problema existe otro tipo de reprensentación, que se denomina representación vectorial. La diferencia entre un token y un vector es que un token asigna un valor numérico a una palabra mientras que un vector es una secuencia ordenada de números.
Ejercicio - Teoría vectorial
¿Qué es un vector?
¿Qué diferencias tiene con un número?
¿Qué propiedades tiene?
¿Por qué es interesante para representación del lenguaje?
Vamos a crear un dataset de ejemplo para ver cómo podemos aplicar esto de los vectores
import pandas as pd
# Crear un dataset de ejemplo
data = [
{"id": 1, "category": "News", "text": "La economía global sigue en recuperación tras la pandemia. La economía es importante."},
{"id": 2, "category": "Sports", "text": "El equipo local ganó el partido. La recuperación del equipo fue impresionante."},
{"id": 3, "category": "Technology", "text": "La nueva tecnología mejora la recuperación de datos en servidores."},
{"id": 4, "category": "News", "text": "Las noticias locales destacan la importancia de la economía."},
{"id": 5, "category": "Sports", "text": "El equipo nacional se prepara para la próxima competición internacional."},
{"id": 6, "category": "Technology", "text": "Los avances en inteligencia artificial transforman la tecnología."},
{"id": 7, "category": "News", "text": "La recuperación económica es un tema prioritario en las noticias globales."},
{"id": 8, "category": "Sports", "text": "Los aficionados apoyan al equipo durante los partidos."},
{"id": 9, "category": "Technology", "text": "La tecnología está en constante evolución para mejorar la eficiencia de los sistemas."}
]
# Convertir a un DataFrame
data = pd.DataFrame(data).set_index('id')
data
| category | text | |
|---|---|---|
| id | ||
| 1 | News | La economía global sigue en recuperación tras ... |
| 2 | Sports | El equipo local ganó el partido. La recuperaci... |
| 3 | Technology | La nueva tecnología mejora la recuperación de ... |
| 4 | News | Las noticias locales destacan la importancia d... |
| 5 | Sports | El equipo nacional se prepara para la próxima ... |
| 6 | Technology | Los avances en inteligencia artificial transfo... |
| 7 | News | La recuperación económica es un tema prioritar... |
| 8 | Sports | Los aficionados apoyan al equipo durante los p... |
| 9 | Technology | La tecnología está en constante evolución para... |
corpus = data.text.to_list()
corpus
['La economía global sigue en recuperación tras la pandemia. La economía es importante.', 'El equipo local ganó el partido. La recuperación del equipo fue impresionante.', 'La nueva tecnología mejora la recuperación de datos en servidores.', 'Las noticias locales destacan la importancia de la economía.', 'El equipo nacional se prepara para la próxima competición internacional.', 'Los avances en inteligencia artificial transforman la tecnología.', 'La recuperación económica es un tema prioritario en las noticias globales.', 'Los aficionados apoyan al equipo durante los partidos.', 'La tecnología está en constante evolución para mejorar la eficiencia de los sistemas.']
3.1. - Vectorización Bag of Words¶
Técnica para vectorizar frases a partir de las palabras que las componen como vemos en la imagen de abajo. A cada frase se le asigna un vector es un "contador" de el número de veces que aparece cada palabra del vocabulario.
import nltk
nltk.download('stopwords')
from sklearn.feature_extraction.text import CountVectorizer
# El vectorizador de sklearn ya hace automáticamente la tokenizacion
vectorizer = CountVectorizer(
lowercase=True,
preprocessor=preprocess, # Función para preprocesar el texto
analyzer='word', # Tipo de tokenización: ‘word’, ‘char’, ‘char_wb’
stop_words= nltk.corpus.stopwords.words('spanish'), # Stop Words
ngram_range=(1,2), # Ngramas generados
#max_df=10, # No tiene en cuenta las palabras que salen maś de N veces
#min_df=2, # No tiene en cuenta las palabras que salen menos de de N veces
max_features=10, # Nº de dimensiones que genera el vectorizador
vocabulary=None, # Si queremos pasarle un vocabulario (wordnet)
).fit(corpus)
[nltk_data] Downloading package stopwords to [nltk_data] /home/thinbaker/nltk_data... [nltk_data] Unzipping corpora/stopwords.zip.
3.2. - Vectorización TF-idf¶
Uno de los principales problemas que tiene el Bag Of Words, es que hay muchas palabras muy frecuentes que no aportan infromación prácticamente. TF-idf lo que hace es que no solo tiene encuenta si una palabra ha aparecido en muchas veces en un documento, es que además las palabras que aparecen mucho en el corpus no les asigna menos valor
import nltk
import requests
nltk.download('stopwords')
from sklearn.feature_extraction.text import TfidfVectorizer
# El vectorizador de sklearn ya hace automáticamente la tokenizacion
vectorizer = TfidfVectorizer(
lowercase=True,
preprocessor=preprocess, # Función para preprocesar el texto
analyzer='word', # Tipo de tokenización: ‘word’, ‘char’, ‘char_wb’
stop_words= nltk.corpus.stopwords.words('spanish'), # Stop Words
ngram_range=(1, 3), # Ngramas generados
max_df=10, # No tiene en cuenta las palabras que salen maś de N veces
min_df=2, # No tiene en cuenta las palabras que salen menos de de N veces
max_features=10, # Nº de dimensiones que genera el vectorizador
vocabulary=None, # Si queremos pasarle un vocabulario (wordnet)
).fit(corpus)
[nltk_data] Downloading package stopwords to [nltk_data] /home/thinbaker/nltk_data... [nltk_data] Package stopwords is already up-to-date!
vectorizer.get_feature_names_out()
array(['economía', 'equipo', 'noticias', 'recuperación', 'tecnología'],
dtype=object)
import numpy as np
sample = corpus[0:1]
X = vectorizer.transform(sample).toarray()
print(sample, "\n")
print(X)
['La economía global sigue en recuperación tras la pandemia. La economía es importante.'] [[0.93350279 0. 0. 0.35857013 0. ]]
3.3 - Base de conocimiento¶
Cuando tenemos un dataset que queremos vectorizar para aplicar casos de uso de Machine Learning, es común guardar los vectores junto con el texto. El conjunto de datos de texto, junto con sus vectores (y otros metadatos) se le suele denominar "Base de conocimiento"
database = data[['text']]
vectors = pd.DataFrame(vectorizer.transform(corpus).toarray())
database = database.join(vectors)
database
| text | 0 | 1 | 2 | 3 | 4 | |
|---|---|---|---|---|---|---|
| id | ||||||
| 1 | La economía global sigue en recuperación tras ... | 0.000000 | 0.914715 | 0.000000 | 0.404100 | 0.000000 |
| 2 | El equipo local ganó el partido. La recuperaci... | 0.000000 | 0.000000 | 0.000000 | 0.662128 | 0.749391 |
| 3 | La nueva tecnología mejora la recuperación de ... | 0.707107 | 0.000000 | 0.707107 | 0.000000 | 0.000000 |
| 4 | Las noticias locales destacan la importancia d... | 0.000000 | 1.000000 | 0.000000 | 0.000000 | 0.000000 |
| 5 | El equipo nacional se prepara para la próxima ... | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 |
| 6 | Los avances en inteligencia artificial transfo... | 0.000000 | 0.000000 | 0.793009 | 0.609210 | 0.000000 |
| 7 | La recuperación económica es un tema prioritar... | 0.000000 | 1.000000 | 0.000000 | 0.000000 | 0.000000 |
| 8 | Los aficionados apoyan al equipo durante los p... | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 |
| 9 | La tecnología está en constante evolución para... | NaN | NaN | NaN | NaN | NaN |
Dataset Mesirve¶
MessIRve es un conjunto de datos a gran escala para recuperación de información (IR) en español, diseñado para capturar mejor las necesidades de información de los hispanohablantes en diferentes países.
Las queries se obtienen de la API de autocompletado de Google y los documentos relevantes son párrafos de la Wikipedia en español que contienen respuestas de los "fragmentos destacados" de la búsqueda de Google. Esta estrategia de recopilación de datos se inspira en GooAQ.
import pandas as pd
#https://huggingface.co/datasets/spanish-ir/messirve
df = pd.read_parquet("hf://datasets/spanish-ir/messirve/es/train-00000-of-00001.parquet")
df
/home/thinbaker/Workspace/Curso-de-IA-y-NLP/.venv/lib/python3.13/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html from .autonotebook import tqdm as notebook_tqdm
| id | query | docid | docid_text | query_date | answer_date | match_score | expanded_search | answer_type | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 3812723 | a como se saca el porcentaje | 59256#13 | El valor porcentual se calcula multiplicando e... | 2024-03-30 | 2024-04-19 | 0.9011 | False | feat_snip |
| 1 | 3812733 | a cual dedo se pone el anillo de compromiso | 956254#2 | Pero desde hace cientos de años, se dice que l... | 2024-03-30 | 2024-04-25 | 0.7400 | False | feat_snip |
| 2 | 3812734 | a cual lleva tilde | 254539#6 | Los pronombres interrogativos y exclamativos "... | 2024-03-30 | 2024-04-19 | 1.0000 | False | feat_snip |
| 3 | 3812757 | a cuales países emigran los dominicanos | 8005#46 | Todos los niveles de la educación se desplomar... | 2024-03-30 | 2024-04-19 | 1.0000 | False | feat_snip |
| 4 | 3812773 | a cuantas calorias equivale un julio | 40424#5 | Para convertir las kilocalorías en kilojulios ... | 2024-04-04 | 2024-04-19 | 1.0000 | False | feat_snip |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 23471 | 3948801 | sobre qué cuerpo de agua caminó jesús | 7917425#1 | Según los Evangelios, un anochecer Jesús y sus... | 2024-03-30 | 2024-04-19 | 0.8008 | False | feat_snip |
| 23472 | 3948814 | tipo de uva para vino blanco | 7363536#2 | Las que producen principalmente vinos blancos ... | 2024-03-30 | 2024-04-25 | 0.9481 | False | feat_snip |
| 23473 | 3948829 | x donde orina la mujer | 28992#4 | En la mujer la uretra tiene una longitud de 3,... | 2024-03-30 | 2024-04-19 | 0.9375 | False | feat_snip |
| 23474 | 3948831 | xbox cual es el mas nuevo | 58676#4 | La Xbox fue la primera de una marca continua d... | 2024-03-30 | 2024-04-19 | 1.0000 | False | feat_snip |
| 23475 | 3948833 | xbox cual es el ultimo modelo | 6243734#18 | El 12 de diciembre de 2019, durante el evento ... | 2024-03-30 | 2024-04-25 | 0.9307 | False | feat_snip |
23476 rows × 9 columns
import nltk
nltk.download('stopwords')
from sklearn.feature_extraction.text import TfidfVectorizer
corpus = df['docid_text'].to_list()
queries = df['query'].to_list()
# El vectorizador de sklearn ya hace automáticamente la tokenizacion
vectorizer = TfidfVectorizer(
lowercase=True,
analyzer='word', # Tipo de tokenización: ‘word’, ‘char’, ‘char_wb’
stop_words= nltk.corpus.stopwords.words('spanish'), # Stop Words
ngram_range=(1, 3), # Ngramas generados
#max_df=200, # No tiene en cuenta las palabras que salen maś de N veces
#min_df=2, # No tiene en cuenta las palabras que salen menos de de N veces
max_features=5000, # Nº de dimensiones que genera el vectorizador
vocabulary=None, # Si queremos pasarle un vocabulario (wordnet)
).fit(corpus)
[nltk_data] Downloading package stopwords to [nltk_data] /home/thinbaker/nltk_data... [nltk_data] Package stopwords is already up-to-date!
database = df[['id','docid_text']]
vectors = pd.DataFrame(vectorizer.transform(corpus).toarray())
database = database.join(vectors).set_index('id')
database
| docid_text | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | ... | 4990 | 4991 | 4992 | 4993 | 4994 | 4995 | 4996 | 4997 | 4998 | 4999 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| id | |||||||||||||||||||||
| 3812723 | El valor porcentual se calcula multiplicando e... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3812733 | Pero desde hace cientos de años, se dice que l... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3812734 | Los pronombres interrogativos y exclamativos "... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3812757 | Todos los niveles de la educación se desplomar... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3812773 | Para convertir las kilocalorías en kilojulios ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 3948801 | Según los Evangelios, un anochecer Jesús y sus... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3948814 | Las que producen principalmente vinos blancos ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3948829 | En la mujer la uretra tiene una longitud de 3,... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3948831 | La Xbox fue la primera de una marca continua d... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3948833 | El 12 de diciembre de 2019, durante el evento ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
23476 rows × 5001 columns
Caso Real: ElasticSearch¶
ElasticSearch es una empresa tecnológica cuyo prooducto es la creación de bases de conocimiento y vectores
4 - Information Retrieval¶
Tando con BoW como con TFidf somos capaces de construir vectores de documentos a partir de las palabras que lo componen. A diferencia de la tokenización, donde cada palabra tiene un número cualquiera, aquí cabría esperar que dos textos "parecidos" tengan palabras parecidas y por tanto "vectores" parecidos.
Information Retrieval es un conjunto de técnicas del lenguaje que nos permite "buscar" la información correcta dentro de un corpus a partir de un texto o información de entrada (p.e. buscador de google). Gracias a técnicas como la vectorización de documentos con TF-idf o BoW podemos buscar información a partir de un texto de entrada
4.1. - Buscador vectorial¶
El proceso de consulta consiste en buscar, para un vector de entrada dato (como por ejemplo una pregunta) el documento de la base de conocimiento cuyo vector sea más próximo. Para ello la pregunta es ¿Cómo definimos la distancia? Existen diferentes métodos
NOTA: Estos sistemas son similares a cómo funcionan los sistemas de recomendación
Distancia del coseno
database
| docid_text | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | ... | 4990 | 4991 | 4992 | 4993 | 4994 | 4995 | 4996 | 4997 | 4998 | 4999 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| id | |||||||||||||||||||||
| 3812723 | El valor porcentual se calcula multiplicando e... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3812733 | Pero desde hace cientos de años, se dice que l... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3812734 | Los pronombres interrogativos y exclamativos "... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3812757 | Todos los niveles de la educación se desplomar... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3812773 | Para convertir las kilocalorías en kilojulios ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 3948801 | Según los Evangelios, un anochecer Jesús y sus... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3948814 | Las que producen principalmente vinos blancos ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3948829 | En la mujer la uretra tiene una longitud de 3,... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3948831 | La Xbox fue la primera de una marca continua d... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3948833 | El 12 de diciembre de 2019, durante el evento ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
23476 rows × 5001 columns
import numpy as np
from scipy.spatial.distance import cosine
sample_query = df['query'].to_list()[-1]
def vector_search_cosine(
sample_query : str,
database: pd.DataFrame,
vectorizer: TfidfVectorizer,
n_samples: int=1
)->list[str]:
database = database.copy()
query = vectorizer.transform([sample_query]).toarray().flatten()
database['distance'] = database.drop('docid_text',axis=1).apply(
lambda x: cosine(x,query), axis=1)
return database[['docid_text','distance']
].sort_values(ascending=True, by='distance'
).drop_duplicates(
).head(n_samples)
response = vector_search_cosine(sample_query, database, vectorizer,5)
print()
print(sample_query)
response
/home/thinbaker/Workspace/Curso-de-IA-y-NLP/.venv/lib/python3.13/site-packages/scipy/spatial/distance.py:670: RuntimeWarning: invalid value encountered in scalar divide dist = 1.0 - uv / math.sqrt(uu * vv)
xbox cual es el ultimo modelo
| docid_text | distance | |
|---|---|---|
| id | ||
| 3929957 | Xbox Game Pass Ultimate permite tener Xbox Gam... | 0.307013 |
| 3948831 | La Xbox fue la primera de una marca continua d... | 0.432677 |
| 3813054 | Microsoft reveló la Xbox Series X|S bajo el no... | 0.445098 |
| 3928653 | Xbox Network, anteriormente llamada Xbox Live ... | 0.454775 |
| 3913226 | Después de su lanzamiento, Microsoft comenzó a... | 0.469305 |
vector_search_cosine("donde murió jesucristo según la biblia", database, vectorizer)
/home/thinbaker/Workspace/Curso-de-IA-y-NLP/.venv/lib/python3.13/site-packages/scipy/spatial/distance.py:670: RuntimeWarning: invalid value encountered in scalar divide dist = 1.0 - uv / math.sqrt(uu * vv)
| docid_text | distance | |
|---|---|---|
| id | ||
| 3828664 | Según los evangelios, después de la crucifixió... | 0.637184 |
NOTA: Existen más métricas de distancia como la distancia euclidea, distancia de jaqqard o distancia de manhatan
4.2. - Reducción de dimensionalidad¶
Como hemos visto antes, un problema común de sistemas de embeddings como TF-idf o BoW es que generan vectores de muchas componentes (>1000) pero que la mayoría son ceros. El hecho de que estos vectores sean tan grandes relentiza el proceso de cálculo
Para resolver este problema es común usar algoritmos de reducción de dimensionadlidad. Los algoritmos de reducción de dimensionalidad permiten reducir las dimensiones de estos vectores manteniendo su capacidad de servir para recuperar información
from sklearn.decomposition import TruncatedSVD
dim_reducer = TruncatedSVD(
n_components=300,
n_iter=2,
random_state=42
).fit(vectors)
vectors_reduced = dim_reducer.transform(vectors)
print('database dim: ', vectors.shape)
print('database_reduced dim: ', vectors_reduced.shape)
database dim: (23476, 5000) database_reduced dim: (23476, 300)
database = df[['id','docid_text']]
vectors_reduced = pd.DataFrame(dim_reducer.transform(vectors))
database = database.join(vectors_reduced).set_index('id')
database
| docid_text | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | ... | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| id | |||||||||||||||||||||
| 3812723 | El valor porcentual se calcula multiplicando e... | 0.101309 | -0.048874 | -0.009246 | -0.006465 | -0.006615 | 0.013301 | 0.026364 | -0.007705 | 0.011117 | ... | 0.006672 | -0.008418 | 0.029761 | 0.017679 | -0.019280 | -0.034027 | 0.022672 | 0.026440 | -0.006050 | 0.019004 |
| 3812733 | Pero desde hace cientos de años, se dice que l... | 0.070875 | -0.017471 | -0.001315 | 0.021827 | -0.019800 | 0.023386 | -0.006207 | 0.064112 | 0.052960 | ... | -0.006107 | -0.020094 | 0.010664 | 0.021630 | 0.024331 | 0.003448 | -0.001889 | -0.016665 | 0.018480 | -0.016309 |
| 3812734 | Los pronombres interrogativos y exclamativos "... | 0.012739 | -0.004170 | -0.000646 | 0.003105 | -0.001864 | -0.005657 | -0.000103 | 0.008897 | -0.000150 | ... | -0.003607 | 0.011823 | -0.004049 | -0.013310 | -0.011878 | 0.003226 | -0.007701 | -0.001584 | 0.020261 | 0.012497 |
| 3812757 | Todos los niveles de la educación se desplomar... | 0.136747 | 0.045089 | 0.024410 | 0.044869 | -0.014956 | -0.018065 | 0.000781 | 0.094831 | -0.000960 | ... | 0.012470 | 0.002425 | -0.058907 | 0.028717 | -0.004345 | -0.010596 | 0.024096 | 0.064527 | 0.001972 | -0.011738 |
| 3812773 | Para convertir las kilocalorías en kilojulios ... | 0.009958 | -0.008304 | -0.001792 | -0.004647 | 0.001908 | 0.002068 | 0.005368 | 0.002464 | -0.000973 | ... | 0.028804 | 0.006963 | -0.016690 | -0.042867 | -0.034473 | 0.027952 | -0.026400 | -0.015229 | 0.069918 | -0.037813 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 3948801 | Según los Evangelios, un anochecer Jesús y sus... | 0.093556 | 0.025760 | 0.000060 | 0.032494 | 0.075984 | 0.007356 | 0.026050 | 0.024614 | -0.013526 | ... | -0.000937 | -0.025982 | -0.007641 | 0.013667 | -0.006449 | 0.000936 | -0.008582 | 0.004272 | -0.002052 | -0.041900 |
| 3948814 | Las que producen principalmente vinos blancos ... | 0.060128 | -0.014871 | -0.012811 | -0.023672 | 0.005837 | 0.005290 | 0.043502 | 0.003002 | -0.018364 | ... | 0.012607 | -0.011980 | 0.008714 | -0.005692 | -0.038961 | 0.008731 | -0.024596 | -0.023508 | -0.001406 | 0.039989 |
| 3948829 | En la mujer la uretra tiene una longitud de 3,... | 0.061864 | 0.000666 | -0.016448 | -0.029152 | 0.007138 | 0.027010 | -0.005445 | 0.024285 | 0.014169 | ... | 0.022257 | 0.015753 | -0.091944 | 0.036999 | -0.008720 | 0.045421 | -0.012187 | 0.028738 | 0.007659 | 0.036026 |
| 3948831 | La Xbox fue la primera de una marca continua d... | 0.028345 | 0.012231 | 0.004093 | 0.031227 | -0.022533 | -0.022875 | 0.023859 | -0.011295 | -0.008597 | ... | 0.016959 | -0.011282 | -0.005160 | 0.013929 | 0.052419 | 0.003014 | -0.003935 | 0.100863 | 0.025225 | 0.030256 |
| 3948833 | El 12 de diciembre de 2019, durante el evento ... | 0.076794 | 0.037150 | 0.012336 | 0.064977 | -0.045045 | -0.049744 | 0.038868 | -0.026829 | -0.024606 | ... | -0.002906 | -0.013003 | -0.018023 | 0.012601 | 0.022891 | 0.009692 | 0.011538 | 0.062864 | -0.003248 | 0.010823 |
23476 rows × 301 columns
Consulta - Reducción de dimensionalidad
import numpy as np
from scipy.spatial.distance import cosine
sample_query = df['query'].to_list()[-1]
def reduced_vector_search_cosine(
sample_query : str,
database: pd.DataFrame,
vectorizer: TfidfVectorizer,
dim_reducer: TruncatedSVD,
n_samples: int=1
)->list[str]:
database = database.copy()
query = vectorizer.transform([sample_query]).toarray()
query = dim_reducer.transform(query).flatten()
database['distance'] = database.drop('docid_text',axis=1).apply(
lambda x: cosine(x,query), axis=1)
return database[['docid_text','distance']
].sort_values(ascending=True, by='distance'
).drop_duplicates(
).head(n_samples)
response = reduced_vector_search_cosine(sample_query, database, vectorizer,dim_reducer,5)
print()
print(sample_query)
response
/home/thinbaker/Workspace/Curso-de-IA-y-NLP/.venv/lib/python3.13/site-packages/scipy/spatial/distance.py:670: RuntimeWarning: invalid value encountered in scalar divide dist = 1.0 - uv / math.sqrt(uu * vv)
xbox cual es el ultimo modelo
| docid_text | distance | |
|---|---|---|
| id | ||
| 3948831 | La Xbox fue la primera de una marca continua d... | 0.117187 |
| 3813063 | Microsoft reveló la Xbox Series X|S bajo el no... | 0.183750 |
| 3913226 | Después de su lanzamiento, Microsoft comenzó a... | 0.187429 |
| 3929957 | Xbox Game Pass Ultimate permite tener Xbox Gam... | 0.194127 |
| 3928653 | Xbox Network, anteriormente llamada Xbox Live ... | 0.267419 |
Ejercicio - Buscador Híbrido¶
Los búscadores híbridos son aquellos buscadores que nos permiten combinar diferentes tipos de búsqueda. Hoy hemos visto la búsqueda vectorial sin embargo existen otros dipos de búsqueda, como por ejemplo la búsqueda por índices. La búsqueda por índices es un tipo de búsqueda en la que el objetivo es buscar si una palabra está literalmente en el texto o no
keywords = ["xbox", "one"]
def index_search(
keywords : list[str],
database: pd.DataFrame,
n_samples: int=1
)->list[str]:
return database["docid_text"].loc[database['docid_text'].apply(
lambda x: all([keyword in x.lower().split() for keyword in keywords]))
].drop_duplicates(
).head(n_samples)
response = index_search(keywords,database, 5)
print()
print(keywords)
response
['xbox', 'one']
id 3840870 Xbox One cuenta con una GPU integrada basada e... 3841424 La Xbox fue la primera de una marca continua d... 3841425 Dicho dispositivo original fue la primera cons... 3841430 Microsoft declaró que Xbox Series X sería cuat... 3897096 Su lanzamiento fue el 15 de noviembre de 2013 ... Name: docid_text, dtype: str
El objetivo es hacer un buscador que siga las siguientes caraterísticas
Primero filtra aquellos textos que contienen todas las keywords (si no se le pasa keywords no filtra ninguno)
Después, sobre los que se han generado se calcula la distancia del coseno y devuelve los n_samples primeros
Además permite búsquedas negativas (no devolver textos que contengan alguna de las palabras que aparecen como "negativas)
def hibrid_search(
database,
keywords = ["xbox"],
sample_query = "xbox cual es el ultimo modelo",
negative_words = ["ps5"],):
pass