<jupyter_start><jupyter_text>**ENTREGA DO TRABALHO E06 - DEEP LEARNING**

**NOME:** <NAME>
**MATRICULA:** 201720094<jupyter_code>#@title
%%capture
!pip install git+https://github.com/grading/gradememaybe.git
!gdown --id '1edIyTQOkush3UHuidrX7zFWiQS9oLobW'
!gdown --id '18RoYdRXTnpPPkGBkK9DhsKeY8rcvbdiz'
!gdown --id '1UOA4_DMFNglsQ_1EezA--XJosCiolOLK'
!gdown --id '1EwribkNTDFLzQxDPuhYnUL4XDxsiyWHA'
from gofer import ok<jupyter_output><empty_output><jupyter_text>Parte 1: Leitura e Preparação dos Dados  Importação de BibliotecasNa célula de código abaixo importamos todas as principais bibliotecas (módulos do Python) que usaremos em nosso exercício.<jupyter_code>import numpy as np

import torch
import torch.nn as nn
import torch.functional as F
import torch.optim as optim

from matplotlib import pyplot as plt<jupyter_output><empty_output><jupyter_text>Leitura dos DadosO código abaixo faz a leitura das imagens de entrada e suas correspondentes categorias de saída desejada.<jupyter_code># Baixa as entradas X.npy
!gdown https://drive.google.com/uc?id=1oSRay8phFA91RJoGH0tMmj86LBovKj73

# Baixa as saídas desejadas Y.npy
!gdown https://drive.google.com/uc?id=1_BQLcsgcYYsubtv4M80BVm4BEknrTOr7
# Leitura dos dados
X = np.load('X.npy')
Y = np.load('Y.npy')

# Reordena as categorias na ordem correta
# (por motivo que desconheço, os dados
#  originais estavam com as classes fora
#  de ordem -- consistentes e organizadas,
#  mas fora de ordem)
cats = [9,0,7,6,1,8,4,3,2,5]
Y[:,cats] = Y[:,range(10)]<jupyter_output><empty_output><jupyter_text>Embaralhamento e Separação dos DadosEm seguida embaralhamos as amostras, mantendo os pares correspondentes entre entradas e suas respectivas saídas desejadas, e depois separamos uma parte das amostras para treinamento e outra parte para validação.<jupyter_code>def split_and_shuffle(X, Y, perc = 0.1):
  ''' Esta função embaralha os pares de entradas
      e saídas desejadas, e separa os dados de
      treinamento e validação
  '''
  # Total de amostras
  tot = len(X)
  # Emabaralhamento dos índices
  indexes = np.arange(tot)
  np.random.shuffle(indexes)
  # Calculo da quantidade de amostras de
  # treinamento
  n = int((1 - perc)*tot)
  Xt = X[indexes[:n]]
  Yt = Y[indexes[:n]]
  Xv = X[indexes[n:]]
  Yv = Y[indexes[n:]]
  return Xt, Yt, Xv, Yv
# Aqui efetivamente realizamos a separação
# e embaralhamento

Xt, Yt, Xv, Yv = split_and_shuffle(X, Y)
# Transforma os arrays do NumPy em
# tensores do PyTorch

Xt = torch.from_numpy(Xt)
Yt = torch.from_numpy(Yt)
Xv = torch.from_numpy(Xv)
Yv = torch.from_numpy(Yv)
# Adiciona dimensão dos canais
# (único canal, imagem monocromática)

Xt = Xt.unsqueeze(1)
Xv = Xv.unsqueeze(1)
print('Dados de treinamento:')
print('Xt', Xt.size(), 'Yt', Yt.size())
print()
print('Dados de validação:')
print('Xv', Xv.size(), 'Yv', Yv.size())<jupyter_output>Dados de treinamento:
Xt torch.Size([1855, 1, 64, 64]) Yt torch.Size([1855, 10])

Dados de validação:
Xv torch.Size([207, 1, 64, 64]) Yv torch.Size([207, 10])<jupyter_text>Inspeção dos DadosAgora mostramos algumas amostras dos dados para verificar se a preparação feita até aqui continua coerente.<jupyter_code>def show_sample(X, Y, n=3):
  ''' Essa função mostra algumas
      amostras aleatórias
  '''
  for i in range(n):
    k = np.random.randint(0,len(X))
    print('Mostrando', int(torch.argmax(Y[k,:])))
    plt.imshow(X[k,0,:,:], cmap='gray')
    plt.show()
show_sample(Xt, Yt)<jupyter_output>Mostrando 6<jupyter_text>Parte 2: Projeto da Rede NeuralPara esta primeira parte do exercício você irá implementar uma rede neural convolucional conforme a figura abaixo. Primeiro examine com calma a figura, tentando entender cada etapa da rede neural. Ela é muito semelhante à rede neural que implementamos em aula, disponível [aqui](https://colab.research.google.com/drive/1bT8jyS0qyScFLi_mA6c1Fbv9uixrNRO3?usp=sharing).Considere a fórmula abaixo, onde $w_i$ representa a largura da imagem de entrada, $p$ o tamanho do padding (se não houver padding, $p$=0), $k$ a largura do kernel, $s$ o tamanho do passo (stride). Essa fórmula calcula a largura $w_o$ do feature map de saída após a convolução. A mesma fórmula pode ser usada para calcular a altura também.$w_o = \frac{w_i + 2p - k}{s}+1$Na rede neural da figura acima, as camadas são:1.   `conv1`: Camada convolucional com kernel 6x6, 5 canais de saída, sem padding, stride 2 e ativação ReLU.2.   `pool1`: Camada _max-pooling_ 2x2, com stride 2.3.   `conv2`: Camada convolucional com kernel 3x3, 8 canais de saída, sem padding, stride 1 e ativação ReLU.4.   `drp1`: Dropout de 25%5.   `pool2`: Camada _max-pooling_ 2x2, com stride 2.6.   `lin1`: Camada feedforward que recebe os dados serializados e gera as saídas. A função de ativação final é _softmax_, mas ela é implementada no cálculo da função de custo, então não precisa ser considerada aqui.Com base nas informações e na figura acima, e usando a fórmula cima, considerando que a entrada é de 1 canal, largura 64 e altura 64 (1x64x64), defina os valores de `N1`, `N2`, `N3`, `N4`, `N5`, `N6`, `N7`, `N8`, `N9`, `N10`, `N11`, `N12` conforme apontados na figura.Preencha os valores no código abaixo.<jupyter_code># Para cada uma das variáveis abaixo
# substitua None pelo valor inteiro
# correto.

N1 = 5        # Valor do número de canais 
N2 = 30       # Linhas e colunas wo = (64 + 2.0 + 6)/2 + 1
N3 = 4500     # Canais*Linhas*Colunas 

N4 = 5        # Canais não mudam com Pool
N5 = 15       # 2x2 Stride 2 reduz pela metade 
N6 = 1125     # Canais*Linhas*Colunas 

N7 = 8        # Número de canais da saída da conv2 
N8 = 13       # Colunas e linhas
N9 = 1352     # Canais*Colunas*Linhas

N10 = 8       # Canal não muda com Pool
N11 = 6       # Metade para cima de 13

N12 = 288     # Saídas Coluna*Linhas*Canais

N13 = 10      # Saída de dados sum(0 -> 9) = 10<jupyter_output><empty_output><jupyter_text>Autovaliação do código até aqui<jupyter_code>ok.check('avalia01.py')<jupyter_output><empty_output><jupyter_text>Parte 3: Código da Rede NeuralCrie abaixo uma classe de nome `ConvNet`. Essa classe deve derivar da classe `nn.Module`. Se você estiver com dúvidas sobre como começar, revise o código desenvolvido em aula [aqui](https://colab.research.google.com/drive/1bT8jyS0qyScFLi_mA6c1Fbv9uixrNRO3?usp=sharing). Nesta classe, você vai definir uma rede convolucional com as seguintes camadas:1.   A primeira camada você vai chamar de `self.conv1`. Essa deve receber a imagem de entrada e aplicar uma convolução com um kernel de tamanho 6x6, com passo 2 (stride=2). A saída deve conter 5 canais.2.   A segunda camada deve ser uma camada de _max-pooling_ numa janela 2x2, com passo 2. Essa camada você vai chamar de `self.pool1`.3.   A terceira camada você vai chamar de `self.conv2`. Ela deve ser uma convolução com um kernel de tamanho 3x3, gerando 8 canais de saída.4.   Em seguida voce tomará a saída da terceira camada e aplicará _dropout_ com p=25%. Essa camada de _dropout_ você vai chamar de `self.drp1`.5.   Após o _dropout_, adicione mais uma camada de _max-pooling_ idêntica à usada na segunda camada, com janela 2x2 e passo 2. Essa camada você vai chamar de `self.pool2`6.   Agora os dados serão serializados. Adicione uma camada _feed-forward_ de nome `self.lin1` que receberá os dados serializados e gerará as saídas.<jupyter_code># Escreva aqui o código da classe que
# implementará sua rede neural

class ConvNet(nn.Module):
  
  def __init__(self):
    super(ConvNet, self).__init__()
    self.conv1 = nn.Conv2d(1, 5, 6, 2)  # Canais entrada, saída, kernel, stride
    self.pool1 = nn.MaxPool2d(2,2)      # Kernel, stride
    self.conv2 = nn.Conv2d(5, 8, 3, 1)  # Canais entrada, saída, kernel, stride
    self.drp1  = nn.Dropout2d(0.25)     # Percentagem de morte
    self.pool2 = nn.MaxPool2d(2,2)      # Kernel, stride 
    self.lin1  = nn.Linear(288, 10)     # Colunas*Linhas*Canais, saída 10 digitos 

  def forward(self, x):
    x = self.conv1(x)
    x = torch.relu(x)
    x = self.pool1(x)
    x = self.conv2(x)
    x = self.drp1(x)
    x = torch.relu(x)
    x = self.pool2(x)
    x = x.view(-1, 288)
    x = self.lin1(x)
    return x<jupyter_output><empty_output><jupyter_text>A célula de código abaixo vai criar um objeto da classe recém criada por você, e irá imprimir um sumário das camadas. Verifique se constam as camadas `conv1`, `pool1`, `conv2`, `drp1`, `pool2` e `lin1`, com os respectivos parâmetros pedidos no enunciado. Lembre que `conv1` e que as camadas de _max-pooling_ `pool1` e `pool2` devem possuir stride 2.<jupyter_code>cnn = ConvNet()
print(cnn)<jupyter_output>ConvNet(
  (conv1): Conv2d(1, 5, kernel_size=(6, 6), stride=(2, 2))
  (pool1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  (conv2): Conv2d(5, 8, kernel_size=(3, 3), stride=(1, 1))
  (drp1): Dropout2d(p=0.25, inplace=False)
  (pool2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  (lin1): Linear(in_features=288, out_features=10, bias=True)
)<jupyter_text>Autovaliação do código até aqui<jupyter_code>ok.check('avalia02.py')<jupyter_output><empty_output><jupyter_text>Parte 4: TreinamentoAgora você vai implementar o código para treinamento da rede neural. Para facilitar, já estão implementadas abaixo algumas partes desse código.A função `evaluate(x, y_hat)` vai servir para verificar a acurácia da rede neural para um par de entradas `x` e saídas desejadas correspondentes `y_hat`. Cuide que o nome de sua variável correspondente ao objeto da rede neural deve ser `cnn` para usar essa função.<jupyter_code>def evaluate(x, y_hat):
  ''' Calcula a acurácia da ConvNet (variável cnn)
      para o par de entradas e saídas desejadas
      x, y_hat. Aqui assume-se que y_hat está
      originalmente no formato one-hot. Tanto
      x quanto y_hat devem ser lotes, não amostras
      individuais.
  '''
  y = cnn(x).argmax(dim=1)
  y_hat = y_hat.argmax(dim=1)
  return 100*float((y == y_hat).sum()) / len(y)<jupyter_output><empty_output><jupyter_text>Abaixo criamos os objetos `opt` que será o otimizador Adam, com passo de aprendizagem 0,0001, e função de custo entropia cruzada no objeto `loss`.<jupyter_code>opt = optim.Adam(cnn.parameters(), lr=0.0001)
loss = nn.CrossEntropyLoss()<jupyter_output><empty_output><jupyter_text>Em seguida movemos os dados e a rede neural para a GPU, para que o treinamento seja um pouco mais ágil. Essa parte é opcional, depende de você ter GPU disponível e com memória suficiente para alocar todos os dados. Testando no Google Colab consegui alocar tudo normalmente.<jupyter_code># Movemos tudo para a GPU
# (essa parte é opcional)

gpu = torch.device("cuda:0")
cnn = cnn.to(gpu)
Xt = Xt.to(gpu, dtype=torch.float)
Yt = Yt.to(gpu, dtype=torch.long)
Xv = Xv.to(gpu, dtype=torch.float)
Yv = Yv.to(gpu, dtype=torch.long)<jupyter_output><empty_output><jupyter_text>Agora complete você mesmo o código abaixo, colocando os comandos que faltam nos espaços indicados conforme as instruções.<jupyter_code># Laço de treinamento para 2001
# épocas

for j in range(2001):

  # Faremos o treinamento em lotes de
  # tamanho igual a 128 amostras

  for i in range(0,len(Yt),128):

    # Separa o lote de entradas
    x = Xt[i:i+128,:,:,:]

    # Separa o lote de saídas desejadas
    # já transformando de one-hot para
    # índice das colunas.
    y_hat = Yt[i:i+128,:].argmax(dim=1)

    # Zera o gradiente do otimizador
    cnn.zero_grad()

    # Calcula a saída da rede neural
    y = cnn(x)

    # Calcula o erro
    e = loss(y, y_hat)

    # Calcula o gradiente usando backpropagation
    e.backward()

    # Realiza um passo de atualização dos parâmetros da rede neural usando o otimizador.
    opt.step()

  # A cada 200 épocas imprimimos o
  # erro do último lote e a acurácia
  # nos dados de treinamento
  if not (j % 200):
    print(float(e), evaluate(Xt, Yt))<jupyter_output>2.323652744293213 9.757412398921833
0.539328396320343 82.69541778975741
0.340692400932312 89.00269541778975
0.2506553828716278 91.53638814016172
0.1824968457221985 92.93800539083558
0.160583958029747 94.39353099730458
0.14417558908462524 95.90296495956873
0.10522658377885818 96.22641509433963
0.06388303637504578 96.81940700808626
0.08486971259117126 97.1967654986523
0.060186807066202164 97.30458221024259<jupyter_text>Depois de treinar a rede neural, podemos desligar a camada de _dropout_ e mostrar o resultado nos dados de validação.<jupyter_code>cnn.eval() # desliga dropout
# Não modifique essa célula.

ac = evaluate(Xv, Yv)
print('Acurácia de', ac, '%')<jupyter_output>Acurácia de 87.43961352657004 %<jupyter_text>Autovaliação do código até aqui<jupyter_code>ok.check('avalia03.py')<jupyter_output><empty_output><jupyter_text>Parte 5: Examinando os ResultadosPor fim, podemos agora avaliar a rede neural em funcionamento, nos dados de validação.A função abaixo escolhe 5 amostras aleatórias dos dados de validação e aplica sua rede neural nelas, mostrando a imagem, a saída calculada e a saída desejada.<jupyter_code>def random_sample_cnn(X, Y):
  ''' Essa função testa a rede convolucional
      mostrando a imagem de entrada, a saída
      calculada, e a saída esperada, para
      5 amostras aleatórias.
  '''
  for _ in range(5):
    idx = np.random.randint(0, len(Yv))
    x = Xv[idx:idx+1,:,:,:]
    y = int(cnn(x).argmax(dim=1))
    y_hat = int(Yv[idx:idx+1,:].argmax(dim=1))
    print('y =', y, 'y_hat =', y_hat)
    x = x.cpu()
    plt.imshow(x[0,0,:,:], cmap='gray')
    plt.show()<jupyter_output><empty_output><jupyter_text>Abaixo, finalmente, os resultados<jupyter_code># Aqui examinamos alguns exemplos
# aleatórios nos dados de validação

random_sample_cnn(Xv, Yv)<jupyter_output>y = 3 y_hat = 3
{'fraction_non_alphanumeric': 0.10104708208244856, 'fraction_numerical': 0.05762612579629494, 'mean_word_length': 4.496177062374246, 'pattern_counts': {'":': 0, '<': 68, '<?xml version=': 0, '>': 69, 'https://': 5, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '18', 'dir': 'jupyter-structured-clean-dedup', 'id': '18097', 'license': 'MIT', 'n_tokens_mistral': 5973, 'n_tokens_neox': 5457, 'n_words': 1585, 'path': 'DeepLearning/04-RedeConvolucional/Sinais/ConvSinais.ipynb', 'repo_name': 'iOsnaaente/Faculdade_ECA-UFSM', 'size': '13679'}