{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "2uyu9gygYJae" }, "source": [ "Tensores\n", "========" ] }, { "cell_type": "markdown", "metadata": { "id": "ARqmzjgYYJam" }, "source": [ "Introducción\n", "------------\n", "\n", "Podemos pensar en un tensor como un arreglo multidimensional, es decir, que son generalizaciones de los vectores y las matrices. En general, diremos que cualquier arreglo de numeros organizados en una estructura regular con una determinada cantidad de ejes es un tensor.\n", "\n", "De esta forma un vector es un tensor unidimensional (o de orden 1) y una matriz es un tensor de 2 dimensiones (o de orden 2). Por lo tnato, muchas de las operaciones que se pueden realizar sobre vetores y matrices también pueden ser reformuladas para ejecutarse sobre tensores.\n", "\n", "\n", "\n", "*Imagen de Cassie Kozyrkov (@quaesita)*\n", "\n", "Algunas consideraciones:\n", "\n", "- Todos los valores dentro de un tensor son del mismo tipo.\n", "- Las dimensiones de los datos son las dimensiones del tensor.\n", "- Las dimensiones del tensor son conocidas o al menos parcialmente conocidas." ] }, { "cell_type": "markdown", "metadata": { "id": "9ENE-GAxYJao" }, "source": [ "Tensores en Python\n", "------------------" ] }, { "cell_type": "markdown", "metadata": { "id": "u7SZKjrWYJaq" }, "source": [ "Los tensores en `Python` se pueden representar utilizando arreglos n-dimensionales. Diferentes librerias permiten representar tensores.\n", "\n", "En `numpy`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "uoGwvLU5YJas", "outputId": "d5775f39-ee04-441d-8d97-724e565ec9cb" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "import numpy as np\n", "\n", "t = np.array([\n", " [[1,2,3], [4,5,6], [7,8,9]],\n", " [[11,12,13], [14,15,16], [17,18,19]],\n", " [[21,22,23], [24,25,26], [27,28,29]],\n", " ])\n", "\n", "print(type(t))" ] }, { "cell_type": "markdown", "metadata": { "id": "fUv0s4CDYJa0" }, "source": [ "En `TensorFlow`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "SWzG-dcUYJa2", "outputId": "28022a67-2fec-430e-8b3c-79cd3d710a6d" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "import tensorflow as tf\n", "\n", "t = tf.constant([\n", " [[1,2,3], [4,5,6], [7,8,9]],\n", " [[11,12,13], [14,15,16], [17,18,19]],\n", " [[21,22,23], [24,25,26], [27,28,29]],\n", " ])\n", "\n", "print(type(t))" ] }, { "cell_type": "markdown", "metadata": { "id": "9qudgzq3YJa4" }, "source": [ "En `PyTorch`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "LPovAQLEYJa5", "outputId": "274a706b-52fa-4590-c271-543ccb4e413b" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "import torch\n", "\n", "t = torch.tensor([\n", " [[1,2,3], [4,5,6], [7,8,9]],\n", " [[11,12,13], [14,15,16], [17,18,19]],\n", " [[21,22,23], [24,25,26], [27,28,29]],\n", " ])\n", "\n", "print(type(t))" ] }, { "cell_type": "markdown", "metadata": { "id": "Nt5XKkavYJa6" }, "source": [ "Notemos la diferencia con un arreglo en `Python`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "Q_HxnSaXYJa8", "outputId": "1b2de58c-e9b3-4d48-bc3c-6290d0584aee" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "arreglo = [\n", " [[1,2,3], [4,5,6], [7,8,9]],\n", " [[11,12,13], [14,15,16], [17,18,19]],\n", " [[21,22,23], [24,25,26], [27,28,29]],\n", " ]\n", "\n", "print(type(arreglo))" ] }, { "cell_type": "markdown", "metadata": { "id": "mcwqqMVDYJa9" }, "source": [ "**Nota:** Veremos que en la mayoria de los casos `numpy.ndarray` es un tipo compatible para `tensorflow.python.framework.ops.EagerTensor` y `torch.Tensor`, sin embargo el tipo `list` no lo es. En esos casos, podemos utilizar:" ] }, { "cell_type": "code", "source": [ "np.asarray(arreglo)" ], "metadata": { "id": "Wjsz8iZ6YP_A" }, "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "9ZzlqlPTYJa9" }, "source": [ "### Dimensiones de un tensor" ] }, { "cell_type": "markdown", "metadata": { "id": "39Snm5u0YJa-" }, "source": [ "Podemos acceder a las dimensiones de un tensor en cualquier momento utilizando la instrucción `shape`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "hYIKhDrwYJa-", "outputId": "e317064a-a7ec-4aa1-95cc-aad619d8e737" }, "outputs": [ { "data": { "text/plain": [ "torch.Size([3, 3, 3])" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "t.shape" ] }, { "cell_type": "markdown", "metadata": { "id": "RMDj7ch6YJa_" }, "source": [ "En este caso vemos que este tensor tiene 3 dimensiones. Cada una de las cuales tiene 3 elementos. Estos numeros que aparecen aqui se llaman `ejes` o `axis` y reciben numeros ordinales para identificarlos. La primera dimension corresponde a `axis=0`, la segunda a `axis=1` y la tercera a `axis=2`." ] }, { "cell_type": "markdown", "metadata": { "id": "kfZP0byCYJbA" }, "source": [ "Dado que un tensor no es mas que un arreglo de numeros, podemos cambiar sus dimensiones para llevarlo a otras dimensiones. Por ejemplo, el siguiente codigo crea un tensor de 1 dimensión con 30 elementos:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "pSdR9s4zYJbA", "outputId": "50872f31-e5d1-453b-fba1-80b4a76a99a9" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23\n", " 24 25 26 27 28 29]\n" ] } ], "source": [ "t = np.arange(30)\n", "print(t)" ] }, { "cell_type": "markdown", "metadata": { "id": "mHidvbE_YJbB" }, "source": [ "Las dimensiones de este tensor son:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "bC7wP9NxYJbC", "outputId": "5768071f-a30d-48aa-e076-ca812a26a6b1" }, "outputs": [ { "data": { "text/plain": [ "(30,)" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "t.shape" ] }, { "cell_type": "markdown", "metadata": { "id": "wiCwQdl1YJbE" }, "source": [ "Podemos convertir este tensor de 1 dimension a uno de 3 dimensiones:\n", "\n", "\n", "\n", "\n", "*Fuente de la imagen: https://www.tensorflow.org/guide/tensor*" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "P9szsjTDYJbF", "outputId": "19cb7b5b-394e-48b1-cc03-316b57c8d5bb" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[[ 0 1 2 3 4]\n", " [ 5 6 7 8 9]]\n", "\n", " [[10 11 12 13 14]\n", " [15 16 17 18 19]]\n", "\n", " [[20 21 22 23 24]\n", " [25 26 27 28 29]]]\n", "Shape: (3, 2, 5)\n" ] } ], "source": [ "t = t.reshape((3, 2, 5))\n", "print(t)\n", "print(\"Shape:\", t.shape)" ] }, { "cell_type": "markdown", "metadata": { "id": "RMi_hoPoYJbF" }, "source": [ "> Nota: Si trabaja con tensore en `TensorFlow`, la operación anterior se realiza como `tf.reshape(t, (3 , 2, 5))`. No te como especificar estas operaciones en este framework a diferencia de `numpy` o `pytorch`." ] }, { "cell_type": "markdown", "metadata": { "id": "WCKjSc6IYJbG" }, "source": [ "En muchas ocaciones necesitamos eliminar alguna de las dimensiones en particular.\n", "\n", "\n", "\n", "*Fuente de la imagen: https://www.tensorflow.org/guide/tensor*" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "56y4PzvrYJbH", "outputId": "9b2c4154-9edb-447b-fc76-97d6c39a4e90" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[ 0 1 2 3 4 5 6 7 8 9]\n", " [10 11 12 13 14 15 16 17 18 19]\n", " [20 21 22 23 24 25 26 27 28 29]]\n", "Shape: (3, 10)\n" ] } ], "source": [ "t = t.reshape((3, -1))\n", "print(t)\n", "print(\"Shape:\", t.shape)" ] }, { "cell_type": "markdown", "metadata": { "id": "WKJ8yAgfYJbI" }, "source": [ "Noten como eliminamos la ultima posición del tensor. El valor `-1` indica \"la cantidad de elementos que entren\". Esto significa que queremos que la primera componente del tensor sea de dimension 3 y la segunda de \"la cantidad restante de elementos\". Es importante que las dimensiones tengan sentido con la cantidad de elementos para evitar un error:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "PFl9Z_JqYJbJ", "outputId": "7fb7bd29-4255-48c1-a4f2-325f9c30e23b" }, "outputs": [ { "ename": "ValueError", "evalue": "cannot reshape array of size 30 into shape (4,newaxis)", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m/tmp/ipykernel_13468/1980147985.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mt\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mreshape\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mValueError\u001b[0m: cannot reshape array of size 30 into shape (4,newaxis)" ] } ], "source": [ "t.reshape((4, -1))" ] }, { "cell_type": "markdown", "metadata": { "id": "Dp4CjhNcYJbK" }, "source": [ "#### Agregando dimensiones" ] }, { "cell_type": "markdown", "metadata": { "id": "l3bEDnV0YJbK" }, "source": [ "Podemos agregar dimensiones extras pero sin distorcionar las otras dimensiones. Esto es util cuando por ejemplo la entrada de una red es una \"lote\" de valores pero nosotros queremos introducir solo un valor. Asi podemos convertir un tensor de 2 dimensiones en uno de 3." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "XXfsyidXYJbL", "outputId": "18926279-c003-481d-ba9c-bba0e2e3c441" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Dimensiones antes: (3, 10)\n" ] } ], "source": [ "print(\"Dimensiones antes:\", t.shape)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "g_WtCWEAYJbM", "outputId": "fd486aef-1c43-477a-9203-dad03e037e00" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Dimensiones despues: (1, 3, 10)\n" ] } ], "source": [ "t = t.reshape((1,3,10))\n", "\n", "print(\"Dimensiones despues:\", t.shape)" ] }, { "cell_type": "markdown", "metadata": { "id": "C5E-xcpMYJbN" }, "source": [ "#### Permutando dimensiones\n", "\n", "Podemos cambiar el orden de las dimensiones. Esto es util cuando queremos ver la información de otra forma: La instrucción `permute` permite hacerlo, pero no está disponible en todos los tipos de tensores, solo en `TensorFlow` y `PyTorch`" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "NtbE4jlGYJbN" }, "outputs": [], "source": [ "t_torch = torch.tensor(t)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "X01KV7WBYJbN", "outputId": "f37447ba-0249-4c57-9a3f-2e2a87f005e0" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Dimensiones antes: torch.Size([1, 3, 10])\n" ] } ], "source": [ "print(\"Dimensiones antes:\", t_torch.shape)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "8Rmueam3YJbR", "outputId": "7ff86a71-14f7-43a0-962a-7d429726fd36" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Dimensiones despues: torch.Size([3, 1, 10])\n" ] } ], "source": [ "t_torch = t_torch.permute(1,0,2)\n", "print(\"Dimensiones despues:\", t_torch.shape)" ] }, { "cell_type": "markdown", "metadata": { "id": "zjf63zRGYJbR" }, "source": [ "#### Quitando dimensiones\n", "\n", "De igual forma podemos eliminarla si no la necesitaramos. La instrucción `squeeze` nos permite eliminar alguna dimensión cuya cantidad de elementos es 1. El argumento `axis=0` indica cual es la dimensión a quitar, en este caso la primera:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "r5z68yFJYJbS", "outputId": "d5f784f6-4ef0-41c1-cbd9-eaca56242fc4" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Dimensiones antes: (1, 3, 10)\n" ] } ], "source": [ "print(\"Dimensiones antes:\", t.shape)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "3gdXbld0YJbS", "outputId": "f9e5a6fd-5daf-43ae-d96d-b7bf62c4edd0" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Dimensiones despues: (3, 10)\n" ] } ], "source": [ "t = t.squeeze(axis=0)\n", "\n", "print(\"Dimensiones despues:\", t.shape)" ] }, { "cell_type": "markdown", "metadata": { "id": "zG3wAcxCYJbT" }, "source": [ "### Tipos de dato de un tensor" ] }, { "cell_type": "markdown", "metadata": { "id": "Rhe0167PYJbj" }, "source": [ "Todos los elementos de un tensor deben tener el mismo tipo de datos. Podemos ver el tipo de datos de cualquier tensor con la propiedad `dtype`" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "2QXVxP5gYJbj", "outputId": "e7160f67-eea5-4784-a70e-a664d92e5d24" }, "outputs": [ { "data": { "text/plain": [ "dtype('int64')" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "t.dtype" ] }, { "cell_type": "markdown", "metadata": { "id": "TxsxFekNYJbk" }, "source": [ "En muchos casos necesitamos cambiar el tipo de dato a otro, por ejemplo `float32` o `int32`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "H3pvMguTYJbk", "outputId": "7d178f07-24e5-4f7f-d2e8-1668594a8bcf" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "float32\n" ] } ], "source": [ "t_float = t.astype('float32')\n", "print(t_float.dtype)" ] }, { "cell_type": "markdown", "metadata": { "id": "2UrqKN_hYJbl" }, "source": [ "Esto es importante sobre todo cuando estamos integrando datos de diferentes origines y necesitamos que todos los tensores tengan tipos que sean compatibles y con la misma precisión." ] }, { "cell_type": "markdown", "metadata": { "id": "e0JSdJZcYJbm" }, "source": [ "### Operaciones sobre tensores" ] }, { "cell_type": "markdown", "metadata": { "id": "uGcbi2guYJbn" }, "source": [ "De igual forma que podemos sumar, multiplicar y dividir un vector, estas operaciones estan definidas para los tensores. Dependiendo del `Framework` que utilizamos, serán las operaciones disponibles. En general, todos implementan las mismas APIs:" ] }, { "cell_type": "markdown", "metadata": { "id": "KjCcyCRvYJbn" }, "source": [ "En `tensorflow` por ejemplo:\n", " \n", "```\n", "tf.add(a, b)\n", "tf.substract(a, b)\n", "tf.multiply(a, b)\n", "tf.div(a, b)\n", "tf.pow(a, b)\n", "tf.exp(a)\n", "tf.sqrt(a)\n", "```" ] }, { "cell_type": "markdown", "metadata": { "id": "jAGLY0SrYJbo" }, "source": [ "Otras estructuras de datos\n", "---------------------------" ] }, { "cell_type": "markdown", "metadata": { "id": "ybKBN4LLYJbo" }, "source": [ "No dedemos confundir los tensores con otras estructuras comunes como ser los `DataFrame` en `Pandas`. Un `DataFrame` tiene 2 grandes diferencias con un `Tensor`:\n", "\n", "1. Los `DataFrame` tienen 2 dimensiones. Las `Series` tienen dimensiones 1.\n", "2. Los `DataFrame` y las `Series` tienen un nombre de columna." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "6EXzM6EjYJbp" }, "outputs": [], "source": [ "datos = [\n", " [1, 2, 3],\n", " [4, 5, 6],\n", " [7, 8, 9],\n", " ]" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "gskbu-kPYJbq" }, "outputs": [], "source": [ "import pandas as pd\n", "\n", "df = pd.DataFrame(datos, columns=[\"col1\", \"col2\", \"col3\"])" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "JQr7KxJlYJbq", "outputId": "a5e93a7d-7c97-418c-e999-f5c8f6b0918c" }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
col1col2col3
0123
1456
2789
\n", "
" ], "text/plain": [ " col1 col2 col3\n", "0 1 2 3\n", "1 4 5 6\n", "2 7 8 9" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df" ] }, { "cell_type": "markdown", "metadata": { "id": "Nda4pRjtYJbr" }, "source": [ "Podemos de todas formas convertir desde `DataFrame` a `numpy.ndarray`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "GQW4wb58YJbs", "outputId": "c6095bfa-6070-42b4-95dd-8f3aabe0bf4c" }, "outputs": [ { "data": { "text/plain": [ "array([[1, 2, 3],\n", " [4, 5, 6],\n", " [7, 8, 9]])" ] }, "execution_count": 58, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.to_numpy()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.11" }, "colab": { "provenance": [] } }, "nbformat": 4, "nbformat_minor": 0 }