{ "cells": [ { "cell_type": "markdown", "id": "95fdf1c6-20c1-4695-92c3-1a30d118da9e", "metadata": { "tags": [] }, "source": [ "# <span style=\"color:green\"><center>Aprendizaje Profundo</center></span>" ] }, { "cell_type": "markdown", "id": "9ab2d584-a384-4517-96c0-b085f6adfe24", "metadata": { "tags": [] }, "source": [ "# <span style=\"color:red\"><center>Transformers- Tokenizadores</center></span>" ] }, { "cell_type": "markdown", "id": "ac6d5c0f-edf4-4e0a-8260-4f6a251eedd3", "metadata": {}, "source": [ "<center>Tokenizadores HuggingFace</center>" ] }, { "cell_type": "markdown", "id": "2814b837-8220-4f6c-8d62-4f76d8652736", "metadata": {}, "source": [ "## <span style=\"color:blue\">Autores</span>" ] }, { "cell_type": "markdown", "id": "f9f2eeab-4acc-4a4f-bb79-9e2d45a810d0", "metadata": {}, "source": [ "1. Alvaro Mauricio Montenegro Díaz, ammontenegrod@unal.edu.co\n", "2. Daniel Mauricio Montenegro Reyes, dextronomo@gmail.com " ] }, { "cell_type": "markdown", "id": "b002dc78-cb0a-47d5-a17f-c6df384b96f8", "metadata": {}, "source": [ "## <span style=\"color:blue\">Diseño gráfico y Marketing digital</span>\n", " " ] }, { "cell_type": "markdown", "id": "05789c43-41b3-494d-afaa-a370002d9ccf", "metadata": {}, "source": [ "1. Maria del Pilar Montenegro Reyes, pmontenegro88@gmail.com " ] }, { "cell_type": "markdown", "id": "bcda43ed-5f98-4ffa-b3d6-dd268a5fdc0e", "metadata": {}, "source": [ "## <span style=\"color:blue\">Asistentes</span>" ] }, { "cell_type": "markdown", "id": "d7a21b86-13b2-4c1b-824f-96dce45f3e4c", "metadata": {}, "source": [] }, { "cell_type": "markdown", "id": "e4447d4d-a448-40d6-98cd-cbaebf381d7c", "metadata": {}, "source": [ "## <span style=\"color:blue\">Referencias</span> " ] }, { "cell_type": "markdown", "id": "d23caca7-9f44-4ca3-a8c9-2c4cfd44ee39", "metadata": {}, "source": [ "1. [HuggingFace. Transformers ](https://huggingface.co/transformers/)\n", "1. [HuggingFace. Models](https://huggingface.co/course/chapter2/3?fw=tf)\n", "1. [HuggingFace. Tokenizers](https://huggingface.co/course/chapter2/3?fw=tf)\n", "1. [Tutorial Transformer de Google](https://www.tensorflow.org/text/tutorials/transformer)\n", "1. [Transformer-chatbot-tutorial-with-tensorflow-2](https://blog.tensorflow.org/2019/05/transformer-chatbot-tutorial-with-tensorflow-2.html) \n", "1. [Transformer Architecture: The positional encoding](https://kazemnejad.com/blog/transformer_architecture_positional_encoding/)\n", "1. [Illustrated Auto-attención](https://towardsdatascience.com/illustrated-self-attention-2d627e33b20a)\n", "1. [Illustrated Attention](https://towardsdatascience.com/attn-illustrated-attention-5ec4ad276ee3#0458)\n", "1. [Neural Machine Translation by Jointly Learning to Align and Translate (Bahdanau et. al, 2015)](https://arxiv.org/pdf/1409.0473.pdf)\n", "1. [Effective Approaches to Attention-based Neural Machine Translation (Luong et. al, 2015)](https://arxiv.org/pdf/1508.04025.pdf)\n", "1. [Attention Is All You Need (Vaswani et. al, 2017)](https://arxiv.org/pdf/1706.03762.pdf)\n", "1. [Self-Attention GAN (Zhang et. al, 2018)](https://arxiv.org/pdf/1805.08318.pdf)\n", "1. [Sequence to Sequence Learning with Neural Networks (Sutskever et. al, 2014)](https://arxiv.org/pdf/1409.3215.pdf)\n", "1. [TensorFlow’s seq2seq Tutorial with Attention (Tutorial on seq2seq+attention)](https://github.com/tensorflow/nmt)\n", "1. [Lilian Weng’s Blog on Attention (Great start to attention)](https://lilianweng.github.io/lil-log/2018/06/24/attention-attention.html#a-family-of-attention-mechanisms)\n", "1. [Jay Alammar’s Blog on Seq2Seq with Attention (Great illustrations and worked example on seq2seq+attention)](https://jalammar.github.io/visualizing-neural-machine-translation-mechanics-of-seq2seq-models-with-attention/)\n", "1. [Google’s Neural Machine Translation System: Bridging the Gap between Human and Machine Translation (Wu et. al, 2016)](https://arxiv.org/pdf/1609.08144.pdf)\n", "1. [Adam: A method for stochastic optimization](https://arxiv.org/pdf/1412.6980.pdf)\n", "1. [BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding](https://arxiv.org/pdf/1810.04805.pdf)" ] }, { "cell_type": "markdown", "id": "2a0da79b-44dc-460c-ac1e-b71ade087a9c", "metadata": {}, "source": [ "## <span style=\"color:blue\">Contenido</span>" ] }, { "cell_type": "markdown", "id": "15195d1b-1dfc-4e35-a28d-5c6dad544252", "metadata": {}, "source": [ "* [Introducción](#Introducción)\n", "* [Tokenización basada en palabras](#Tokenización-basada-en-palabras)\n", "* [Tokenización basada en caracteres](#Tokenización-basada-en-caracteres)\n", "* [Tokenización basada en subpalabras](#Tokenización-basada-en-subpalabras)\n", "* [Tokenización basada en otros enfoques](#Tokenización-basada-en-otros-enfoques)\n", "* [Carga y guarda](#Carga-y-guarda)\n", "* [Entubamiento del proceso de tokenización](#Entubamiento-del-proceso-de-tokenización)\n", "\n" ] }, { "cell_type": "markdown", "id": "afe895d3-c25d-49d7-9b47-ff4835ba2b95", "metadata": {}, "source": [ "## <span style=\"color:blue\">Introducción</span>" ] }, { "cell_type": "markdown", "id": "16be1833-6cb5-4ec7-9b09-320a67e9097e", "metadata": {}, "source": [ "Los tokenizadores constituyen una parte clave del entubamiento de PLN. La traea de un tokenizador es transformar un texto en datos que pueden ser procesados por el modelo.\n", "\n", "Los modelos solamente procesan números. Así que un tokenizador transforma un texto en bruto (*raw text*) en una secuencia de números." ] }, { "cell_type": "markdown", "id": "f0bb3463-df86-4f45-acb5-74240b5f814c", "metadata": {}, "source": [ "## <span style=\"color:blue\">Tokenizadores basados en palabras</span>" ] }, { "cell_type": "markdown", "id": "6e387873-9395-4dd2-a855-288252560cdf", "metadata": {}, "source": [ "Inicialmente vamos a pensar en tokenización basada en palabras (*word-based*). La siguiente imagen ilustra le meta inicial de un toeknizador: dividir el texto bruto en palabras (o subpalabras) para encontrar una representación numérica del texto." ] }, { "cell_type": "markdown", "id": "347b3aca-16aa-425b-ae78-822d9337ba35", "metadata": {}, "source": [ "<figure>\n", "<center>\n", "<img src=\"../Imagenes/word_based_tokenization.png\" width=\"800\" height=\"600\" align=\"center\"/>\n", "</center>\n", "<figcaption>\n", "<p style=\"text-align:center\">Ejemplos de tokenización por palabras</p>\n", "</figcaption>\n", "</figure>\n", "\n", "Fuente: [HuggingFace Transformers course](https://huggingface.co/course/chapter2/4?fw=tf)" ] }, { "cell_type": "markdown", "id": "db9d502b-2a80-43cc-8119-2a74425b61ed", "metadata": {}, "source": [ "La división del texto en palabras puede hacerse directamente con la función **split** de Python." ] }, { "cell_type": "code", "execution_count": 2, "id": "5a0145f6-f04b-4d91-a11c-5e34d0887390", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['Daniel', 'es', 'un', 'profesor', 'de', 'inteligencia', 'artificial']\n" ] } ], "source": [ "tokenized_text = \" Daniel es un profesor de inteligencia artificial\".split()\n", "print(tokenized_text)" ] }, { "cell_type": "markdown", "id": "44180ba0-38b9-4fe0-a691-9e9e1ab33864", "metadata": {}, "source": [ "Existen distintas variaciones de tokenizadores por palabras, los cuales incluyen reglas extras para la puntuación. Estos tokenizadores pueden llegar a tener un vocabulario muy grande. El `vocabulario` es definido como el total de tokens independientes en el corpus.\n", "\n", "A cada palabra (token) se le asigna un ID, el cual empieza en 0 y va hasta el tamaño del vocabulario. El modelo utiliza este ID para identificar cada palabra. \n", "\n", "De momento no existe un vocabulario universal único, por lo que los tokenizadores en los modelos preentrenado son específicos en cada uno de ellos. Por supuesto existen tokenizadores para trabajar desde cero (from scratch), pero el resultado está asociado a su corpus.\n", "\n", "\n", "Un primer pensamiento que podemos tener es justamente cubrir un lenguaje natural con un tokenizador basado en palabras. Sin embargo esto resulta demasiado costoso para el modelo y en realidad innecesario. El costo no es tanto en la codificación, que por ejemplo en el inglés puede supera las 500.000 palabras, sino en lo que eso significa para su incorporación en el modelo. Recuerde que la capa de embedding (incrsutamiento) es una capa densa que debe entrenarse y que den entrada tiene como número de neuronas de entrada el tamaño del vocabulario. Además cada sentencia, entra palabra por palabra, o que rápidamente lleva a cargas excesivas, tanto para la inferencia, como para el entrenamiento.\n", "\n" ] }, { "cell_type": "markdown", "id": "22e4bdc1-76d1-4d28-a825-92951702fe5c", "metadata": {}, "source": [ "## <span style=\"color:blue\">Tokenización basada en caracteres</span>" ] }, { "cell_type": "markdown", "id": "73d0bbc5-e30b-4142-98f5-aa467116076f", "metadata": {}, "source": [ "En el otro extremo se encuentran los tokenizadores basados en caracteres. En general estos generan vocabularios mucho mas cortos. En este caso aparecen problemas diferentes a los tokenizadores por palabras. A diferencia de la tokenización por palabras en donde existen similaridades entre ellas, en la tokenizacipon por caracteres, tal similaridad se pierde. Además algunos problemas aparecen, relacionados con el manejo de la puntuación. \n", "\n", "Sinembargo esto difiere entre lso diferente lenguajes naturales. Por ejemplo en chino, cada caracter carga más información que un caracter en un lenguaje latino. La siguiente imagen ilustra una tokenización por caracteres latinos." ] }, { "cell_type": "markdown", "id": "a0256332-814c-4832-8462-38a9836641ec", "metadata": {}, "source": [ "<figure>\n", "<center>\n", "<img src=\"../Imagenes/character_based_tokenization.png\" width=\"800\" height=\"600\" align=\"center\"/>\n", "</center>\n", "<figcaption>\n", "<p style=\"text-align:center\">Ejemplos de tokenización por caracteres</p>\n", "</figcaption>\n", "</figure>\n", "\n", "Fuente: [HuggingFace Transformers course](https://huggingface.co/course/chapter2/4?fw=tf)" ] }, { "cell_type": "markdown", "id": "4d030ed3-f5bb-4b45-855c-909c83f6489f", "metadata": {}, "source": [ "## <span style=\"color:blue\">Tokenización basada en subpalabras</span>" ] }, { "cell_type": "markdown", "id": "d05e30c2-d1b2-40e2-9a35-f38e937dbf15", "metadata": {}, "source": [ "En este tipo de tokenizadores se toma lo mejor de los dos mundos anteriores. La tokenización basada en subpalabras descanasa en el principio de que *palabras frecuentemente usadas no deben subdivisirse más, pero palabras menos frecuentes pueden eventualmente ser subdivididas en subpalabras cons siginficado. Dos ejemplos de tokenización basados en subpalabras son los siguientes.\n", "\n", "1. Las palabras *perro* y *perros* son diferentes. Pero es claro que se refieren a lo mismo. Si se codifican separadamente con ID's distintos, esta similaridad natural se pierde. Una solución que puede adoptrase es por ejemplo codificar las palabras 'perro' y 's'. por separado. Un tokenizador entrenado de esta forma haría la tokenización d elas dos palabras de la siguiente forma:\n", " * 'perro' -> {'perro'} -> {{35}}\n", " * 'perros' -> {'perro', 's'} -> {{35}, {60}}\n", " * 'casa' -> {'casa'} -> {{85}}\n", " * 'casas' -> {'casa', 's'} -> {{85}, {60}}\n", " \n", "Observe que 'perros' y 'casas' comparten el hecho de ser palabras plurales.\n", "\n", "2. Las palabras *bailar*, *bailamos*, *bailaríamos*, *bailaremos* se tokenizarían como\n", " * 'bailar* -> {'baila', 'r'} -> {{110},{59}}\n", " * 'bailamos* -> {'baila', 'mos'} -> {{110},{90}}\n", " * 'bailaríamos* -> {'baila', 'ríamos'} -> {{110},{95}}\n", " * 'bailaremos* -> {'baila', 'remos'} -> {{110},{98}}\n", " \n", "Asi, sucesivamente. " ] }, { "cell_type": "markdown", "id": "377a5bab-cb79-4556-847a-e8aa43949243", "metadata": {}, "source": [ "## <span style=\"color:blue\">Tokenización basada en otros enfoques</span>" ] }, { "cell_type": "markdown", "id": "da5b5992-5eec-438c-b752-b0f0f007b786", "metadata": { "tags": [] }, "source": [ "Existen muchas más técnicas. Por ejemplo:\n", "\n", "1. Byte-level, usado en [GPT-2](https://cdn.openai.com/research-covers/language-unsupervised/language_understanding_paper.pdf)\n", "1. WordPiece, usado en [BERT](https://arxiv.org/pdf/1810.04805.pdf)\n", "1. SentencePiece o Unigram, usado en varios modelos multilingua." ] }, { "cell_type": "markdown", "id": "87c744b4-6dee-4198-a35f-8400c7bccd29", "metadata": {}, "source": [ "## <span style=\"color:blue\">Carga y guarda</span>" ] }, { "cell_type": "markdown", "id": "b1aadd15-43e4-4ef1-a831-6aa04eab7cf8", "metadata": {}, "source": [ "Cargar y guardar tokenizadores es tan fácil como con los modelos. Tambiés se bsa en los métodos **from_trained** y **save_pretrained**. Vamos a cargar el tokenizer BERT." ] }, { "cell_type": "markdown", "id": "fc0ef327-6d72-4b13-9606-aeb0c3baa893", "metadata": {}, "source": [ "Para correr el cuaderno original de HuggingFace para Tensorflow en Colab vaya a [Huggingface notebook](https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/chapter2/section4_tf.ipynb). Para Torch [aquí](https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/chapter2/section4_pt.ipynb)." ] }, { "cell_type": "raw", "id": "e76fd28f-ef5a-452b-b562-ffd48a9fb361", "metadata": {}, "source": [ "!conda install -c huggingface transformers\n", "!conda install -c conda-forge sentencepiece" ] }, { "cell_type": "code", "execution_count": 1, "id": "7f2ae66c-ff6e-473c-9050-c26bb8c7b394", "metadata": {}, "outputs": [], "source": [ "from transformers import BertTokenizer\n", "\n", "# Instancia el tokenizador parea la configuración bert-base-cased\n", "tokenizer = BertTokenizer.from_pretrained('bert-base-cased')\n" ] }, { "cell_type": "raw", "id": "4c9a94a4-6c89-48ee-af49-1aa4645539e7", "metadata": {}, "source": [ "# torch\n", "from transformers import BertTokenizer\n", "\n", "tokenizer = BertTokenizer.from_pretrained(\"bert-base-cased\")" ] }, { "cell_type": "markdown", "id": "bc8a5929-4883-467b-9216-ccd6f14313c8", "metadata": {}, "source": [ "Ahora podemos usar el tokenizador" ] }, { "cell_type": "code", "execution_count": 2, "id": "7a3e2d9c-8f1f-4e07-9ed8-30942f1b6caf", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'input_ids': [101, 7993, 170, 11303, 1200, 2443, 1110, 3014, 102], 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1]}" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tokenizer('Using a transformer network is simple')" ] }, { "cell_type": "markdown", "id": "66966487-1bff-4252-a5d7-b93b805f365d", "metadata": {}, "source": [ "Guardar un tokenizador es idéntico a salvar un modelo:" ] }, { "cell_type": "raw", "id": "b728f94a-0a87-4ee7-b86e-da8fc2a57235", "metadata": {}, "source": [ "tokenizer.save_pretrained('directorio_en_mi_computador')" ] }, { "cell_type": "markdown", "id": "f59ddf13-d2d0-4752-b6d7-3601cbc02619", "metadata": {}, "source": [ "## <span style=\"color:blue\">Entubamiento del proceso de tokenización</span>" ] }, { "cell_type": "markdown", "id": "9d8c32e9-fa01-4380-8ebf-314085b6174c", "metadata": {}, "source": [ "### Codificación" ] }, { "cell_type": "markdown", "id": "d168cd4b-1c2b-42c6-ab63-bedc5888b2ba", "metadata": {}, "source": [ "El proceso de trasladar texto a números se llama codificación. La codificación tiene dos pasos: tokenización y conversión a ID's." ] }, { "cell_type": "markdown", "id": "35fcedc9-f92f-4704-b6a3-ad5b0b62a632", "metadata": {}, "source": [ "#### Tokenización" ] }, { "cell_type": "markdown", "id": "74aae644-4dde-46d4-9dfb-9684ad228c0f", "metadata": {}, "source": [ "Este proceso es hechp por el método **tokenize** del tokenizer." ] }, { "cell_type": "code", "execution_count": 4, "id": "d3df89f3-d84b-4e45-b639-97e7b3ffdabc", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['Using', 'a', 'Trans', '##former', 'network', 'is', 'easy']\n" ] } ], "source": [ "from transformers import AutoTokenizer\n", "tokenizer = AutoTokenizer.from_pretrained('bert-base-cased')\n", "\n", "sequence = 'Using a Transformer network is easy'\n", "tokens = tokenizer.tokenize(sequence)\n", "\n", "print(tokens)" ] }, { "cell_type": "markdown", "id": "68439a60-0ad4-44fd-b5cb-35cf1d90c9d3", "metadata": {}, "source": [ "#### Calcula ID's" ] }, { "cell_type": "code", "execution_count": 5, "id": "7ecd0505-1e93-460b-8da8-5913a3400246", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[7993, 170, 13809, 23763, 2443, 1110, 3123]\n" ] } ], "source": [ "ids = tokenizer.convert_tokens_to_ids(tokens)\n", "print(ids)" ] }, { "cell_type": "markdown", "id": "0a4a68ac-7890-405b-a64e-79974fe617c1", "metadata": {}, "source": [ "#### Convierte a tensor para poder enviar al modelo" ] }, { "cell_type": "code", "execution_count": 7, "id": "db0c71f6-791b-4a03-931b-37e5510aba8b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tf.Tensor([ 7993 170 13809 23763 2443 1110 3123], shape=(7,), dtype=int32)\n" ] } ], "source": [ "import tensorflow as tf\n", "\n", "ids_tensor = tf.constant(ids)\n", "\n", "print(ids_tensor)" ] }, { "cell_type": "code", "execution_count": 8, "id": "e6d4aa4e-6782-4079-b76b-6c57d4a2a357", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor([ 7993, 170, 13809, 23763, 2443, 1110, 3123])\n" ] } ], "source": [ "# torch\n", "import torch\n", "\n", "ids_tensor = torch.tensor(ids)\n", "print(ids_tensor)" ] }, { "cell_type": "markdown", "id": "a646a485-b220-4b7d-8a0a-d9f88acaba57", "metadata": {}, "source": [ "### Decodificación" ] }, { "cell_type": "markdown", "id": "f1d15db2-ba71-480b-9626-dffc64f34fab", "metadata": {}, "source": [ "La decodificación es el camino inverso ala codificación. Es decir, se reupera el texto desde los ID's." ] }, { "cell_type": "code", "execution_count": 11, "id": "12790390-9225-44e5-9911-196b66f51793", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Using a Transformer network is easy\n" ] } ], "source": [ "decoded_text = tokenizer.decode(ids)\n", "print(decoded_text)" ] }, { "cell_type": "code", "execution_count": null, "id": "d4047871-a1a2-44c9-b8ec-54e1ba1c3b7e", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.10" } }, "nbformat": 4, "nbformat_minor": 5 }