Last active
February 19, 2024 21:25
-
-
Save rafaelvareto/e1a3946261d2f285f0bf2422a77fcbd9 to your computer and use it in GitHub Desktop.
19_02-programacao-diferenci-vel.ipynb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "cells": [ | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "view-in-github", | |
| "colab_type": "text" | |
| }, | |
| "source": [ | |
| "<a href=\"https://colab.research.google.com/gist/rafaelvareto/e1a3946261d2f285f0bf2422a77fcbd9/19_02-programacao-diferenci-vel.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "2728a6f2", | |
| "metadata": { | |
| "id": "2728a6f2" | |
| }, | |
| "source": [ | |
| "# Programação Diferenciável (Aprendizado Profundo - UFMG)\n", | |
| "\n", | |
| "## Preâmbulo\n", | |
| "\n", | |
| "O código abaixo consiste dos imports comuns. Além do mais, configuramos as imagens para ficar de um tamanho aceitável e criamos algumas funções auxiliares. No geral, você pode ignorar a próxima célula." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "a2ef04d2", | |
| "metadata": { | |
| "id": "a2ef04d2" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "# -*- coding: utf8\n", | |
| "\n", | |
| "import matplotlib.pyplot as plt" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "7ee22830", | |
| "metadata": { | |
| "id": "7ee22830" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "plt.rcParams['figure.figsize'] = (8, 5)\n", | |
| "\n", | |
| "plt.rcParams['axes.axisbelow'] = True\n", | |
| "plt.rcParams['axes.labelsize'] = 16\n", | |
| "plt.rcParams['axes.linewidth'] = 1\n", | |
| "plt.rcParams['axes.spines.bottom'] = True\n", | |
| "plt.rcParams['axes.spines.left'] = True\n", | |
| "plt.rcParams['axes.titlesize'] = 16\n", | |
| "plt.rcParams['axes.ymargin'] = 0.1\n", | |
| "\n", | |
| "plt.rcParams['font.family'] = 'serif'\n", | |
| "\n", | |
| "plt.rcParams['axes.grid'] = True\n", | |
| "plt.rcParams['grid.color'] = 'lightgrey'\n", | |
| "plt.rcParams['grid.linewidth'] = .1\n", | |
| "\n", | |
| "plt.rcParams['xtick.labelsize'] = 16\n", | |
| "plt.rcParams['xtick.bottom'] = True\n", | |
| "plt.rcParams['xtick.direction'] = 'out'\n", | |
| "plt.rcParams['xtick.major.size'] = 10\n", | |
| "plt.rcParams['xtick.major.width'] = 1\n", | |
| "plt.rcParams['xtick.minor.size'] = 3\n", | |
| "plt.rcParams['xtick.minor.width'] = .5\n", | |
| "plt.rcParams['xtick.minor.visible'] = True\n", | |
| "\n", | |
| "plt.rcParams['ytick.labelsize'] = 16\n", | |
| "plt.rcParams['ytick.left'] = True\n", | |
| "plt.rcParams['ytick.direction'] = 'out'\n", | |
| "plt.rcParams['ytick.major.size'] = 10\n", | |
| "plt.rcParams['ytick.major.width'] = 1\n", | |
| "plt.rcParams['ytick.minor.size'] = 3\n", | |
| "plt.rcParams['ytick.minor.width'] = .5\n", | |
| "plt.rcParams['ytick.minor.visible'] = True\n", | |
| "\n", | |
| "plt.rcParams['legend.fontsize'] = 16\n", | |
| "\n", | |
| "plt.rcParams['lines.linewidth'] = 4\n", | |
| "plt.rcParams['lines.markersize'] = 10" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "2df6a530", | |
| "metadata": { | |
| "id": "2df6a530" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "plt.style.use('tableau-colorblind10') # use um estilo colorblind!\n", | |
| "plt.ion()" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "0648be06", | |
| "metadata": { | |
| "id": "0648be06" | |
| }, | |
| "source": [ | |
| "## 1. Tensores em Numpy\n", | |
| "\n", | |
| "O primeiro passo para usar numpy é importar a biblioteca.\n" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "f7d132a0", | |
| "metadata": { | |
| "id": "f7d132a0" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "import numpy as np" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "d5dba789", | |
| "metadata": { | |
| "id": "d5dba789" | |
| }, | |
| "source": [ | |
| "Quando pensamos no lado prático do aprendizado profundo, um aspecto chave que ajuda na implementação de novos algoritmos é a chamada programação diferenciável. Na próxima aula vamos voltar na mesma. No momento, o importante é salientar que a programação diferenciável faz uso extensivo de Tensores." | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "Um [Tensor](http://en.wikipedia.org/wiki/Tensor) é uma generalização de matrizes para mais dimensões. Quando falamos de tensores, temos três casos especiais e um genérico que engloba os outros três:\n", | |
| "\n", | |
| "\n", | |
| "1. **Escalar:** Um tensor de zero dimensões." | |
| ], | |
| "metadata": { | |
| "id": "40aqkx3bQLSl" | |
| }, | |
| "id": "40aqkx3bQLSl" | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "8290c1a3", | |
| "metadata": { | |
| "id": "8290c1a3" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "1" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "d1c675fd", | |
| "metadata": { | |
| "id": "d1c675fd" | |
| }, | |
| "source": [ | |
| "2. **Vetor:** Um tensor de uma dimensão." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "fcc9d3d7", | |
| "metadata": { | |
| "id": "fcc9d3d7" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "np.array([1, 2])" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "59189d49", | |
| "metadata": { | |
| "id": "59189d49" | |
| }, | |
| "source": [ | |
| "3. **Matrizes:** Um tensor de duas dimensões." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "8c649337", | |
| "metadata": { | |
| "id": "8c649337" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "np.array([[1, 2], [3, 4]])" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "be770aea", | |
| "metadata": { | |
| "id": "be770aea" | |
| }, | |
| "source": [ | |
| "4. **Tensores**. Caso geral, representam n-dimensões. Na figura temos um tensor 3x3x3.\n", | |
| "\n", | |
| "\n", | |
| "# " | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "No exemplo abaixo, temos um tensor de dimensão $3 \\times 2 \\times 2$." | |
| ], | |
| "metadata": { | |
| "id": "uPNvuQMrQ8Gx" | |
| }, | |
| "id": "uPNvuQMrQ8Gx" | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "ce0624d4", | |
| "metadata": { | |
| "id": "ce0624d4" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "X = np.random.randn(3, 2, 2) # Gera números aleatórios de uma normal N(0, 1)\n", | |
| "X" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "Note que ao selecionar elementos da primeira dimensão ficamos com matrizes $2 \\times 2$." | |
| ], | |
| "metadata": { | |
| "id": "E1Ybc0pHRUNS" | |
| }, | |
| "id": "E1Ybc0pHRUNS" | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "fd2a9a86", | |
| "metadata": { | |
| "id": "fd2a9a86" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "X[0]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "a9b07a30", | |
| "metadata": { | |
| "id": "a9b07a30" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "X[1]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "26769cb9", | |
| "metadata": { | |
| "id": "26769cb9" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "X[2]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "5d62c20e", | |
| "metadata": { | |
| "id": "5d62c20e" | |
| }, | |
| "source": [ | |
| "### 1.1) Indexando\n", | |
| "\n", | |
| "Sendo X uma matriz:" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "4b7360f7", | |
| "metadata": { | |
| "id": "4b7360f7" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "X = np.array([[1, 2], [3, 4]])\n", | |
| "X" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "cc670b09", | |
| "metadata": { | |
| "id": "cc670b09" | |
| }, | |
| "source": [ | |
| "Podemos selecionar uma linha com a sintaxe `X[i]`, sendo `i` um inteiro." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "35bfe57f", | |
| "metadata": { | |
| "id": "35bfe57f" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "X[0] # pegando a primeira linha de X" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "4a505a42", | |
| "metadata": { | |
| "id": "4a505a42" | |
| }, | |
| "source": [ | |
| "Podemos selecionar uma coluna com a sintaxe `X[:, j]`, sendo `j` um inteiro." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "fd6b53ef", | |
| "metadata": { | |
| "id": "fd6b53ef" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "X[:, 1] # pegando a segunda coluna de X" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "84299746", | |
| "metadata": { | |
| "id": "84299746" | |
| }, | |
| "source": [ | |
| "Podemos selecionar mais de uma linha ou coluna utilizando a sintaxe `X[um_vetor]` ou `X[:, um_vetor]`, respectivamente." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "ca8b6131", | |
| "metadata": { | |
| "id": "ca8b6131" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "X = np.array([[1, 2, 3], [4, 5, 6]])\n", | |
| "X" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "cols = [0, 1]\n", | |
| "X[:, cols] # iremos pegar a primeira e segunda colunas de X" | |
| ], | |
| "metadata": { | |
| "id": "_CtY99AejnG1" | |
| }, | |
| "id": "_CtY99AejnG1", | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "440b143a", | |
| "metadata": { | |
| "id": "440b143a" | |
| }, | |
| "source": [ | |
| "Podemos selecionar linhas e colunas também indexando o tensor através de um vetor booleano.\n", | |
| "\n", | |
| "A sintaxe `X[vetor_booleano]` retorna as linhas (ou colunas quando `X[:, vetor_booleano]`) onde o vetor é `True`." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "e666700b", | |
| "metadata": { | |
| "id": "e666700b" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "X[[True, False]] # selecionamos apenas a primeira linha" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "fd88174a", | |
| "metadata": { | |
| "id": "fd88174a" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "X[:, [True, False, True]] # selecionamos apenas a primeira e última coluna" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "f953c649", | |
| "metadata": { | |
| "id": "f953c649" | |
| }, | |
| "source": [ | |
| "### 1.2) Shape, Reshape e Ravel\n", | |
| "\n", | |
| "Todo vetor, matriz e tensor pode ser redimensionado.\n", | |
| "\n", | |
| "Observe como no tensor abaixo temos `3x2x2=12` elementos. Podemos redimensionar os mesmos para outros tensores com 12 elementos." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "9bb1cce1", | |
| "metadata": { | |
| "id": "9bb1cce1" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "X = np.random.rand(3, 2, 2)\n", | |
| "print(X.shape, X)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "c80f667a", | |
| "metadata": { | |
| "id": "c80f667a" | |
| }, | |
| "source": [ | |
| "Podemos redimensionar os elementos como uma matriz $2 \\times 6$:" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "e8f8bc0c", | |
| "metadata": { | |
| "id": "e8f8bc0c" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "X_matrix = X.reshape((2, 6))\n", | |
| "print(X_matrix.shape, X_matrix)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "Em Numpy (e PyTorch), podemos fazer com que a biblioteca infira uma dimensão utilizando `-1`:" | |
| ], | |
| "metadata": { | |
| "id": "ZWT0OKyqTQHG" | |
| }, | |
| "id": "ZWT0OKyqTQHG" | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "d535c1f4", | |
| "metadata": { | |
| "id": "d535c1f4" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "X_matrix = X.reshape((2, -1))\n", | |
| "print(X_matrix.shape, X_matrix) # Note que também temos uma matriz de dimensão: 2x6" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "caf67b53", | |
| "metadata": { | |
| "id": "caf67b53" | |
| }, | |
| "source": [ | |
| "Podemos redimensionar os elementos para um outro tensor:" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "f7904f56", | |
| "metadata": { | |
| "id": "f7904f56" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "X_tensor = X.reshape((6, 2, 1))\n", | |
| "X_tensor.shape" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "dbf5211c", | |
| "metadata": { | |
| "id": "dbf5211c" | |
| }, | |
| "source": [ | |
| "E, por fim, podemos redimensionar os elementos como um vetor, realizando uma operação de `flattening`:" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "0a9829bc", | |
| "metadata": { | |
| "id": "0a9829bc" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "X_vetor = X.flatten()\n", | |
| "X_vetor.shape" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "**Observação:** Podemos redimensionar os elementos como um vetor de 3 formas principais:\n", | |
| "1. Através da função `flatten`, como visto acima;\n", | |
| "2. Através da função `ravel` (presente tanto em Numpy quanto em PyTorch);\n", | |
| "3. Através de um `.reshape`, passando como parâmetro o número dos elementos (ou -1).\n", | |
| "\n", | |
| "Os 3 métodos possuem algumas sutilezas que diferenciam um dos outros. Para mais informações consulte o seguinte [link](https://www.techentice.com/numpy-difference-between-reshape-flatten/#:~:text=Use%20flatten()%20when%20you,create%20only%201-D%20array.)." | |
| ], | |
| "metadata": { | |
| "id": "4iWl-PbBUN8D" | |
| }, | |
| "id": "4iWl-PbBUN8D" | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "01da6073", | |
| "metadata": { | |
| "id": "01da6073" | |
| }, | |
| "source": [ | |
| "## 2. Tensores em PyTorch\n", | |
| "\n", | |
| "PyTorch é o arcabouço que vamos usar para as nossas tarefas. Assim como o NumPy, o Pytorch é uma biblioteca de processamento vetorial/matricial/tensorial. Operações sobre os tensores do Pytorch possuem sintaxe consideravelmente parecida com operações sobre tensores do NumPy.\n", | |
| "\n", | |
| "O mesmo faz uso de tensores bem similares ao NumPy. Porém, com PyTorch conseguimos fazer uso da GPU." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "bb86fa6c", | |
| "metadata": { | |
| "id": "bb86fa6c" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "import torch" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "eef55b0f", | |
| "metadata": { | |
| "id": "eef55b0f" | |
| }, | |
| "source": [ | |
| "### 2.1) Casting para o dispositivo correto\n", | |
| "\n", | |
| "Como usaremos processamento vetorial principalmente em GPUs para aprendizado profundo, primeiramente é possível verificar se há uma GPU disponível com o trecho de código abaixo, armazenando os tensores nos dispositivos apropriados." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "77e07616", | |
| "metadata": { | |
| "id": "77e07616" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "if torch.cuda.is_available():\n", | |
| " device = torch.device('cuda')\n", | |
| "else:\n", | |
| " device = torch.device('cpu')\n", | |
| "\n", | |
| "print(device)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "Podemos também realizar essa verificação em uma linha de código (+ pytônico)" | |
| ], | |
| "metadata": { | |
| "id": "e-U9ClDLWA9O" | |
| }, | |
| "id": "e-U9ClDLWA9O" | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n", | |
| "print(device)" | |
| ], | |
| "metadata": { | |
| "id": "upFrmCUhV3eu" | |
| }, | |
| "id": "upFrmCUhV3eu", | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "0fa1fb4b", | |
| "metadata": { | |
| "id": "0fa1fb4b" | |
| }, | |
| "source": [ | |
| "### 2.2.) Tensores no Pytorch" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "fe7618b5", | |
| "metadata": { | |
| "id": "fe7618b5" | |
| }, | |
| "source": [ | |
| "Para criar tensores novos, podemos utilizar a função `torch.tensor`, similar à função `numpy.array` da biblioteca Numpy:" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "2835208e", | |
| "metadata": { | |
| "id": "2835208e" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "tns = torch.tensor([1, 2, 3, 4, 5, 6])\n", | |
| "print(tns)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "09edf473", | |
| "metadata": { | |
| "id": "09edf473" | |
| }, | |
| "source": [ | |
| "Podemos redimensionar os tensores de maneira similar ao que vimos em Numpy através da função `view`:" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "657b44b4", | |
| "metadata": { | |
| "id": "657b44b4" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "print(tns.view(2, 3))" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "Assim como mencionado em Numpy, podemos utilizar `-1` para fazer com que a biblioteca faça uma inferência no formato necessário de acordo com os elementos restantes:" | |
| ], | |
| "metadata": { | |
| "id": "mxMi2Up9WwbL" | |
| }, | |
| "id": "mxMi2Up9WwbL" | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Note que obtemos o mesmo tensor anterior\n", | |
| "print(tns.view(2, -1))" | |
| ], | |
| "metadata": { | |
| "id": "SubQAAGRna8P" | |
| }, | |
| "id": "SubQAAGRna8P", | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "**Observação:** Em PyTorch, além de possuirmos a função `view`, também possuímos a função `reshape`. Ambas possuem algumas diferenças sutis. Caso queira obter mais informações sobre tais diferenças, acesse o seguinte [link](https://stackoverflow.com/questions/49643225/whats-the-difference-between-reshape-and-view-in-pytorch). Porém, no geral, iremos trabalhar mais com a função `view` ao desenvolver códigos utilizando PyTorch." | |
| ], | |
| "metadata": { | |
| "id": "VpQDTi7NW9vN" | |
| }, | |
| "id": "VpQDTi7NW9vN" | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "aa018512", | |
| "metadata": { | |
| "id": "aa018512" | |
| }, | |
| "source": [ | |
| "Podemos criar tensores previamente preenchidos por elementos, como 0s através da função `torch.zeros` e 1s através da função `torch.ones`:" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "25be82ea", | |
| "metadata": { | |
| "id": "25be82ea" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "tns_0 = torch.zeros(2, 3) # iniciando um tensor com 0s\n", | |
| "tns_1 = torch.ones(2, 3) # iniciando um tensor com 1s\n", | |
| "\n", | |
| "print(tns_0)\n", | |
| "print(tns_1)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "cda70838", | |
| "metadata": { | |
| "id": "cda70838" | |
| }, | |
| "source": [ | |
| "Similar à funções do Numpy, podemos iniciar tensores com valores aleatórios:" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "a907a653", | |
| "metadata": { | |
| "id": "a907a653" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "tns_u = torch.rand(2, 3) # valores que seguem uma distribuição uniforme no intervalo [0,1)\n", | |
| "print(tns_u)\n", | |
| "\n", | |
| "tns_n = torch.randn(2, 3) # valores que seguem uma distribuição normal N(0,1)\n", | |
| "print(tns_n)\n", | |
| "\n", | |
| "tns_perm = torch.randperm(6) # valores que são uma permutação aleatória no intervalo [0, 5]\n", | |
| "print(tns_perm)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "960deb73", | |
| "metadata": { | |
| "id": "960deb73" | |
| }, | |
| "source": [ | |
| "Similar à Numpy, podemos realizar operações de soma, multiplicação, entre outras, com tensores:" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "20c406a0", | |
| "metadata": { | |
| "id": "20c406a0" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "print(tns_u)\n", | |
| "print(tns_n)\n", | |
| "\n", | |
| "tns_sum = tns_u + tns_n\n", | |
| "print(tns_sum)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "3fda5520", | |
| "metadata": { | |
| "id": "3fda5520" | |
| }, | |
| "source": [ | |
| "Similar à Numpy, podemos indexar os tensores da mesma forma apresentada anteriormente:" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "1875ca00", | |
| "metadata": { | |
| "id": "1875ca00" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "print(tns_sum[1, 1]) # Indexando um elemento\n", | |
| "print(tns_sum[0, :]) # Indexando uma linha (pode também ser tns_sum[0])\n", | |
| "print(tns_sum[:, 1]) # Indexando uma coluna" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "b6911ae8", | |
| "metadata": { | |
| "id": "b6911ae8" | |
| }, | |
| "source": [ | |
| "Podemos utilizar a função `torch.from_numpy` para converter um tensor de Numpy para PyTorch; ou a função `.numpy()` para converter um tensor de PyTorch para Numpy:" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "6ca112e7", | |
| "metadata": { | |
| "id": "6ca112e7" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "np_arr = np.random.randn(2, 3)\n", | |
| "print(np_arr, np_arr.dtype)\n", | |
| "\n", | |
| "torch_tns = torch.from_numpy(np_arr)\n", | |
| "print(torch_tns)\n", | |
| "\n", | |
| "arr = torch_tns.numpy()\n", | |
| "print(arr)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "bddafbb4", | |
| "metadata": { | |
| "id": "bddafbb4" | |
| }, | |
| "source": [ | |
| "Por fim, podemos concatenar dois, ou mais, tensores com a função `torch.cat`:\n", | |
| "- O parâmetro `dim` em PyTorch é análogo ao parâmetro `axis` do Numpy. Nele, iremos informar sobre qual dimensão queremos que uma certa operação seja feita. No caso abaixo, ao informarmos a primeira dimensão (0), estamos dizendo para o PyTorch efetuar uma concatenação dos dois tensores a partir da sua primeira dimensão, ou seja, \"colando\" os tensores verticalmente (no sentido das linhas)." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "01f1dc76", | |
| "metadata": { | |
| "id": "01f1dc76" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "print((tns_0.shape, tns_1.shape))\n", | |
| "tns_cat = torch.cat((tns_0, tns_1), dim=0)\n", | |
| "print(tns_cat, tns_cat.shape)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "af99b899", | |
| "metadata": { | |
| "id": "af99b899" | |
| }, | |
| "source": [ | |
| "Várias outras operações sobre tensores do Pytorch podem ser vistas nos seguintes tutoriais:\n", | |
| "1. https://jhui.github.io/2018/02/09/PyTorch-Basic-operations/\n", | |
| "2. https://pytorch.org/tutorials/beginner/pytorch_with_examples.html" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "82afff56", | |
| "metadata": { | |
| "id": "82afff56" | |
| }, | |
| "source": [ | |
| "## Conjunto de Problemas 1: Vetorização" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "23f98d71", | |
| "metadata": { | |
| "id": "23f98d71" | |
| }, | |
| "source": [ | |
| "Antes de continuar, vamos importar algumas funções que serão utilizadas para testar o resultado dos seus algoritmos.\n", | |
| "\n", | |
| "Estas, vão vir do módulo `testing` do numpy." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "cae67b57", | |
| "metadata": { | |
| "id": "cae67b57" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "from numpy.testing import assert_equal\n", | |
| "from numpy.testing import assert_almost_equal\n", | |
| "from numpy.testing import assert_array_almost_equal" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "74e733c8", | |
| "metadata": { | |
| "id": "74e733c8" | |
| }, | |
| "source": [ | |
| "Seu objetivo é medir a velocidade das operações de álgebra linear para diferentes níveis de vetorização.\n", | |
| "\n", | |
| "1. Construa duas matrizes $ A $ e $ B $ com entradas aleatórias Gaussianas de tamanho $ 128 \\times 256 $.\n", | |
| "**Dica:** Use o módulo time para mensurar o tempo da operação." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "aa6d230e", | |
| "metadata": { | |
| "id": "aa6d230e" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "# Implemente a sua solução aqui" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "b704bc02", | |
| "metadata": { | |
| "id": "b704bc02" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "# testes, não apague as linhas!!\n", | |
| "assert_equal((128, 256), A.shape)\n", | |
| "assert_equal((128, 256), B.shape)\n", | |
| "\n", | |
| "# A chamada .numpy() converte os vetores em vetores numpy. Útil para testes!\n", | |
| "Anp = A.numpy()\n", | |
| "Bnp = A.numpy()\n", | |
| "\n", | |
| "# testando média e desvio padrão\n", | |
| "assert_almost_equal(Anp.mean(), 0, decimal=2)\n", | |
| "assert_almost_equal(Anp.std(ddof=1), 1, decimal=2)\n", | |
| "\n", | |
| "assert_almost_equal(Bnp.mean(), 0, decimal=2)\n", | |
| "assert_almost_equal(Bnp.std(ddof=1), 1, decimal=2)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "000d92ec", | |
| "metadata": { | |
| "id": "000d92ec" | |
| }, | |
| "source": [ | |
| "2. Calcule $C = AB^T$, tratando $ A $ como uma matriz, mas computando o resultado para cada coluna de $ B $. Em outras palavras, realize um produto matricial utilizando um laço `for`! Pare realizar este código, é importante entender o conceito de broadcasting.\n", | |
| "\n", | |
| "Em código numpy e torch, a operação de broadcasting replica linhas e colunas de tensores para realizar operações. Para entender melhor, leia o [documento](https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html). A figura abaixo exemplifica broadcasting. No geral, as dimensões de arrays casam, as operações são realizadas (primeira linha da figura). Mesmo quando as dimensões não casem, se a última dimensão for compatível é feito a replicação (broadcasting), ver a segunda linha da figura. Por fim, mesmo quando as dimensões não casam mas uma delas é 1 (4x1 + 1x3 na linha 3), é feito broadcasting.\n", | |
| "\n", | |
| "#\n", | |
| "\n", | |
| "\n", | |
| "\n" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "**Dica:** Você deverá fazer o código em uma linha apenas. Para isso, você vai focar no caso da linha 2 da figura. Multiplique uma linha de A por B. Depois disso, use `.sum(axis=...)` para realizar a soma na dimensão correta." | |
| ], | |
| "metadata": { | |
| "id": "Af0Fc_LDcAnl" | |
| }, | |
| "id": "Af0Fc_LDcAnl" | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "fc16b00a", | |
| "metadata": { | |
| "id": "fc16b00a" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "C = np.zeros((128, 128))\n", | |
| "for linha in range(A.shape[0]):\n", | |
| " # implemente aqui a sua solução\n", | |
| " pass" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "ab8a4750", | |
| "metadata": { | |
| "id": "ab8a4750" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "# testes, não apague as linhas!!\n", | |
| "Cteste = np.matmul(A, B.T) # faz a leitura, realiza operação\n", | |
| "assert_array_almost_equal(Cteste, C, decimal=3)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "66b51a8f", | |
| "metadata": { | |
| "id": "66b51a8f" | |
| }, | |
| "source": [ | |
| "3. Calcule $ C = AB^t $ usando operações matriciais. Ou seja, sem usar nenhum laço. Ao mensurar o tempo, ficou mais rápido?" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "4b5e6529", | |
| "metadata": { | |
| "id": "4b5e6529" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "# Implemente aqui a sua solução" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "95dc9237", | |
| "metadata": { | |
| "id": "95dc9237" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "# testes, não apague as linhas!!\n", | |
| "Cteste = np.matmul(A, B.T) # faz a leitura, realiza operação\n", | |
| "assert_array_almost_equal(Cteste, C, decimal=3)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "- Observando o tempo gasto nas duas formas de calcular $C = AB^T$, podemos perceber que ao utilizarmos funções nativas da biblioteca (como `np.matmul` e `torch.mm`), temos uma redução significativa do tempo se compararmos à utilização de um laço `for`." | |
| ], | |
| "metadata": { | |
| "id": "6qrAitJhAVcq" | |
| }, | |
| "id": "6qrAitJhAVcq" | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "f347bdc4", | |
| "metadata": { | |
| "id": "f347bdc4" | |
| }, | |
| "source": [ | |
| "## Conjunto de Problemas 2: Computação eficiente de memória\n", | |
| "\n", | |
| "Crie duas matrizes aleatórias de tamanho $4096 \\times 4096$. Chame as mesmas de $A$ e $B$ novamente.\n", | |
| "\n" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "7756d932", | |
| "metadata": { | |
| "id": "7756d932" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "# Implemente aqui a sua solução" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "5. Crie uma função que recebe as matrizes $A$, $B$ e $C$, e um número de iterações para atualizar $C$, de forma que $C = AB^T + C$. Essa função deve, primeiro, calcular a multiplicação de matrizes entre $A$ e $B$ e depois adicionar o valor à $C$, de acordo com o número de iterações. A mesma deve atualizar $C$ sem alocar memória nova para essa variável." | |
| ], | |
| "metadata": { | |
| "id": "ysUoOzLOBJ0e" | |
| }, | |
| "id": "ysUoOzLOBJ0e" | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "34a9bc94", | |
| "metadata": { | |
| "id": "34a9bc94" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "def update_c(C, A, B, n_iter=2):\n", | |
| " # implemente aqui a sua solução\n", | |
| " pass" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "37eb815e", | |
| "metadata": { | |
| "id": "37eb815e" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "# testes não apague!\n", | |
| "Ct = torch.zeros(A.shape)\n", | |
| "Cteste = (Ct + np.matmul(A, B.T))\n", | |
| "Cteste = (Cteste + np.matmul(A, B.T))\n", | |
| "\n", | |
| "C = torch.zeros(A.shape)\n", | |
| "update_c(C, A, B, 2)\n", | |
| "assert_array_almost_equal(Cteste, C.numpy(), decimal=3)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "e87c26d4", | |
| "metadata": { | |
| "id": "e87c26d4" | |
| }, | |
| "source": [ | |
| "## Conjunto de Problemas 3: Programação Diferenciável\n", | |
| "\n", | |
| "Agora vamos aprender um dos pontos chaves de fazer uso de bibliotecas como pytorch/tensorflow/etc, a programação diferenciável. Diferente do exercício que vocês fizeram na mão, usando a biblioteca conseguimos derivar de forma automágica. Portanto, observe como o código abaixo deriva a função seno." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "5e88cfa6", | |
| "metadata": { | |
| "id": "5e88cfa6" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "x = np.linspace(-10, 10, 1000)\n", | |
| "x_torch = torch.tensor(x, requires_grad=True)\n", | |
| "\n", | |
| "y = torch.sin(x_torch) # seno original\n", | |
| "\n", | |
| "# Devido ao fato de que o y final não é um escalar precisamos passar o vetor v tal que v é a jacobiana\n", | |
| "# pela qual vamos multiplicar as jacobianas da variavel node (neste caso, x) conforme descrito em\n", | |
| "# https://pytorch.org/tutorials/beginner/blitz/autograd_tutorial.html\n", | |
| "# veremos mais sobre o processo de backprogation, jacobiano, etc no futuro\n", | |
| "v = torch.ones(x_torch.shape, dtype=torch.double)\n", | |
| "\n", | |
| "# Como aqui x da origem a y diretamente, nosso v sera apenas um vetor de 1's com a mesma dimensao de x\n", | |
| "y.backward(v)\n", | |
| "\n", | |
| "plt.plot(x_torch.detach().numpy(), y.detach().numpy(), 'g', label='sin(x)')\n", | |
| "plt.plot(x_torch.detach().numpy(), x_torch.grad.numpy(), 'b', label='sin\\'(x)')" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "6f1894fc", | |
| "metadata": { | |
| "id": "6f1894fc" | |
| }, | |
| "source": [ | |
| "O resultado é a mesma curva da função cosseno! Para entender melhor o autograd, leia a seção respectiva do [pyTorch Blitz](https://pytorch.org/tutorials/beginner/blitz/autograd_tutorial.html)." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "03ce34b9", | |
| "metadata": { | |
| "id": "03ce34b9" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "plt.plot(x_torch.detach().numpy(), x_torch.grad.numpy(), 'g', label='sin\\'(x)')\n", | |
| "plt.plot(x_torch.detach().numpy(), torch.cos(x_torch).detach().numpy(), 'b', label='cos(x)')" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "u92eQeXdeNOT" | |
| }, | |
| "source": [ | |
| "6. Derive a função logística usando pytorch.\n", | |
| "\n", | |
| "$$f(x) = \\frac{1}{1 + e^{-x}}$$" | |
| ], | |
| "id": "u92eQeXdeNOT" | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "b4d299bd" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "x = np.linspace(-10, 10, 1000) # Não mude o valor de x!\n", | |
| "x_torch = torch.from_numpy(x)" | |
| ], | |
| "id": "b4d299bd" | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Implemente aqui a sua solução\n", | |
| "# Dica: não se esqueça de definir requires_grad de x_torch como True!" | |
| ], | |
| "metadata": { | |
| "id": "Sz96h_KWIgxK" | |
| }, | |
| "id": "Sz96h_KWIgxK", | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "Vd2DxHxbeNOU" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "# testes, não apagar\n", | |
| "y_test = 1.0/(1 + np.exp(-x))\n", | |
| "derivada_teste = y_test * (1 - y_test)\n", | |
| "assert_array_almost_equal(derivada_teste, x_torch.grad.numpy(), decimal=3)" | |
| ], | |
| "id": "Vd2DxHxbeNOU" | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "nOkegnX_eNOU" | |
| }, | |
| "source": [ | |
| "A operação *detach* permite quebrar a computação em várias partes. Em particular, isto é útil para aplicar a regra da cadeia. Suponha que $u = f(x)$ e $z = g(u)$, pela regra da cadeia, temos $\\frac{dz}{dx}$ = $\\frac{dz}{du}\\frac{du}{dx}$. Para calcular $\\frac{dz}{du}$, podemos primeiro separar $u$ da computação e, em seguida, chamar `z.backward()` para calcular o primeiro termo.\n", | |
| "\n", | |
| "Observe no caso abaixo como derivamos $u = x^2$. A resposta deve ser $2x$ para cada termo `[0, 1, 2, 3]`." | |
| ], | |
| "id": "nOkegnX_eNOU" | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "pOBoYtmseNOU" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "x = torch.arange(4, dtype =torch.float)\n", | |
| "x.requires_grad_(True)\n", | |
| "\n", | |
| "u = x * x\n", | |
| "jacobX = torch.ones(x.shape)\n", | |
| "u.backward(jacobX)\n", | |
| "x.grad" | |
| ], | |
| "id": "pOBoYtmseNOU" | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "fqrzNdjpeNOV" | |
| }, | |
| "source": [ | |
| "Agora vamos fazer $z = u^3$ e computar as derivadas intermediarias." | |
| ], | |
| "id": "fqrzNdjpeNOV" | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "e1QHvjsieNOV" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "x = torch.arange(4, dtype = torch.float)\n", | |
| "x.requires_grad_(True)\n", | |
| "\n", | |
| "u = x * x\n", | |
| "v = u.detach() # u ainda mantém o grafo computacional\n", | |
| "v.requires_grad_(True)\n", | |
| "z = v * v * v\n", | |
| "\n", | |
| "print(z)\n", | |
| "\n", | |
| "jacobX = torch.ones(x.shape)\n", | |
| "u.backward(jacobX)\n", | |
| "x.grad" | |
| ], | |
| "id": "e1QHvjsieNOV" | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "hFkdndNZeNOV" | |
| }, | |
| "source": [ | |
| "Acima temos a derivada de $x^2$. Abaixo temos a derivada de $g(x^2)$." | |
| ], | |
| "id": "hFkdndNZeNOV" | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "r8lr0dy7eNOW" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "jacobV = torch.ones(v.shape)\n", | |
| "z.backward(jacobV)\n", | |
| "v.grad" | |
| ], | |
| "id": "r8lr0dy7eNOW" | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "HN0-MnYSeNOW" | |
| }, | |
| "source": [ | |
| "7. Agora, sendo $f(x) = 1 + x^2$ e $g(x) = 1 + 7 f(x)^4$. Vamos aplicar a regra da cadeia em pytorch" | |
| ], | |
| "id": "HN0-MnYSeNOW" | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "x = torch.arange(4, dtype = torch.float)\n", | |
| "x" | |
| ], | |
| "metadata": { | |
| "id": "Qzhd59L0JUjO" | |
| }, | |
| "id": "Qzhd59L0JUjO", | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Implemente a derivada de f(x) em função de x\n", | |
| "# Dica: não se esqueça de definir requires_grad de x como True!" | |
| ], | |
| "metadata": { | |
| "id": "KRCJd7k8ItND" | |
| }, | |
| "id": "KRCJd7k8ItND", | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "PxBQ8YZ6eNOX" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "# Testando a sua solução\n", | |
| "# Aqui pode ser meio confuso, mas o gradiente referente à derivada de\n", | |
| "# f(x) com relação a x, por exemplo, fica armazenada no tensor x, mais especificamente em x.grad\n", | |
| "assert_array_almost_equal([0, 2, 4, 6], x.grad.numpy())" | |
| ], | |
| "id": "PxBQ8YZ6eNOX" | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "spFaaW1yeNOX" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "# Implemente a derivada de g(x) em função de f(x)\n", | |
| "# Dica: não se esqueça de definir requires_grad da sua variável auxiliar como True!" | |
| ], | |
| "id": "spFaaW1yeNOX" | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Testando a sua solução (caso seja necessário, modifique o nome da sua variável auxiliar para 'u')\n", | |
| "# Note que agora o gradiente referente à derivada está presente na variável 'u', e não mais em 'x'\n", | |
| "assert_array_almost_equal([28, 224, 3500, 28000], u.grad.numpy())" | |
| ], | |
| "metadata": { | |
| "id": "-u7iI63SFKzU" | |
| }, | |
| "id": "-u7iI63SFKzU", | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "My7R1U38eNOX" | |
| }, | |
| "source": [ | |
| "### Conjunto de Problemas 4: Mais Derivadas" | |
| ], | |
| "id": "My7R1U38eNOX" | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "oynEDx7zeNOY" | |
| }, | |
| "source": [ | |
| "Vamos brincar um pouco de derivadas dentro de funções. Dado dois números $x$ e $y$, implemente a função `log_exp`, que retorna:\n", | |
| "\n", | |
| "$$ f(x,y) = -\\log\\left(\\frac{e^x}{e^x+e^y}\\right)$$" | |
| ], | |
| "id": "oynEDx7zeNOY" | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "DfXmkIc7eNOY" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "def log_exp(x, y):\n", | |
| " # implemente aqui a sua solução\n", | |
| " pass" | |
| ], | |
| "id": "DfXmkIc7eNOY" | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "5oDhz-apeNOY" | |
| }, | |
| "source": [ | |
| "1. Abaixo vamos testar o seu código com algumas entradas simples." | |
| ], | |
| "id": "5oDhz-apeNOY" | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "1RNk8XUHeNOY" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "x, y = torch.tensor([2.0]), torch.tensor([3.0])\n", | |
| "z = log_exp(x, y)\n", | |
| "z" | |
| ], | |
| "id": "1RNk8XUHeNOY" | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "txUIVOQeeNOY" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "# Teste. Não apague\n", | |
| "assert_almost_equal(1.31326175, z.numpy())" | |
| ], | |
| "id": "txUIVOQeeNOY" | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "8s88vTx0eNOZ" | |
| }, | |
| "source": [ | |
| "2. A função a seguir computa $\\partial z/\\partial x$ e $\\partial z/\\partial y$ usando `autograd`." | |
| ], | |
| "id": "8s88vTx0eNOZ" | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "yu3HcNAKeNOZ" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "# O argumento funcao_forward é uma função python. Será a sua log_exp.\n", | |
| "# A ideia aqui é deixar claro a ideia de forward e backward propagation, depois\n", | |
| "# de avaliar a função chamamos backward e temos as derivadas.\n", | |
| "def grad(funcao_forward, x, y):\n", | |
| " x.requires_grad_(True)\n", | |
| " y.requires_grad_(True)\n", | |
| " z = funcao_forward(x, y)\n", | |
| " z.backward()\n", | |
| " return x.grad, y.grad" | |
| ], | |
| "id": "yu3HcNAKeNOZ" | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "zVqaKoq6eNOZ" | |
| }, | |
| "source": [ | |
| "Testando" | |
| ], | |
| "id": "zVqaKoq6eNOZ" | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "izUZIoBueNOZ" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "x, y = torch.tensor([2.0], dtype = torch.double) ,torch.tensor([3.0], dtype = torch.double)\n", | |
| "dx, dy = grad(log_exp, x, y)" | |
| ], | |
| "id": "izUZIoBueNOZ" | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "2kiIdjZMeNOa" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "assert_almost_equal(-0.7310586, dx.numpy())\n", | |
| "assert_almost_equal(0.7310586, dy.numpy())" | |
| ], | |
| "id": "2kiIdjZMeNOa" | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "9b-NWlkpeNOa" | |
| }, | |
| "source": [ | |
| "4. Agora teste com números maiores, algum problema?" | |
| ], | |
| "id": "9b-NWlkpeNOa" | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "lBKMNLureNOa" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "x, y = torch.tensor([400.0]).double() ,torch.tensor([800.0]).double()\n", | |
| "grad(log_exp, x, y)" | |
| ], | |
| "id": "lBKMNLureNOa" | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "qOG2yCPceNOa" | |
| }, | |
| "source": [ | |
| "5. Pense um pouco sobre o motivo do erro acima. Usando as propriedade de logaritmos, é possível fazer uma função mais estável. Abaixo segue a implementação da mesma. O problema aqui é que o exponencial \"explode\" quando $x$ ou $y$ são muito grandes. Este [link](http://www.wolframalpha.com/input/?i=log[e%5Ex+%2F+[e%5Ex+%2B+e%5Ey]]) pode ajudar." | |
| ], | |
| "id": "qOG2yCPceNOa" | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "gnas2cVQeNOa" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "x, y = torch.tensor([400.0], dtype = torch.double), torch.tensor([800.0], dtype = torch.double)\n", | |
| "def stable_log_exp(x, y):\n", | |
| " return torch.log(1 + torch.exp(y-x))\n", | |
| "\n", | |
| "dx, dy = grad(stable_log_exp, x, y)" | |
| ], | |
| "id": "gnas2cVQeNOa" | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "omq3r5Z5eNOa" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "stable_log_exp(x, y)" | |
| ], | |
| "id": "omq3r5Z5eNOa" | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "pdzogMVCeNOa" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "# Teste. Não apague\n", | |
| "assert_equal(-1, dx.numpy())\n", | |
| "assert_equal(1, dy.numpy())" | |
| ], | |
| "id": "pdzogMVCeNOa" | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "P2G3bY0deNOa" | |
| }, | |
| "source": [ | |
| "O exemplo acima mostra um pouco de problemas de estabilidade númerica. Às vezes é melhor usar versões alternativas de funções. Isto vai ocorrer quando você ver vários `nans` na sua frente :-) Claro, estamos assumindo que existe uma outra função equivalente que é mais estável para o computador." | |
| ], | |
| "id": "P2G3bY0deNOa" | |
| } | |
| ], | |
| "metadata": { | |
| "colab": { | |
| "provenance": [], | |
| "toc_visible": true, | |
| "include_colab_link": true | |
| }, | |
| "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.10.2" | |
| } | |
| }, | |
| "nbformat": 4, | |
| "nbformat_minor": 5 | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment