{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "source": [
        "# Réseaux de neurones et classification d'images\n",
        "\n",
        "Le but de ce TP est de comparer l'architecture *Perceptron multi-couche* et *Réseau à convolution* pour la classification d'images, d'abord sur un corpus d'images simples en noir et blanc, puis sur un corpus plus complexe d'images en couleur.\n",
        "\n",
        "\n",
        "Le but du TP est de remplir les zones de code vides pour implémenter un programme qui peut classer correctement les images (avec un taux d'erreur de 10% maximum)"
      ],
      "metadata": {
        "id": "Jo33NxikiCs7"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "Remplacer ci-dessous par les noms, prénoms numéros d'étudiants pour le binôme\n",
        "\n",
        "NOM1 PRENOM1 NUM-ETU1\n",
        "\n",
        "NOM2 PRENOM2 NUM-ETU2\n"
      ],
      "metadata": {
        "id": "zH2qpTiHiRx9"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Imports"
      ],
      "metadata": {
        "id": "oXTMU9JtkI2K"
      }
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "SzPPPLu3h9_S"
      },
      "outputs": [],
      "source": [
        "import torch\n",
        "from torch.utils.data import Dataset, DataLoader\n",
        "\n",
        "\n",
        "from torchvision import datasets\n",
        "from torchvision.transforms import ToTensor, Compose, Resize\n",
        "import matplotlib.pyplot as plt\n"
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Le corpus Fashion MNIST\n",
        "\n",
        "Ce corpus est constitué d'images en noir et blanc de taille $28\\times28$.\n",
        "Ces images représentent des vêtements réparties en 10 classes différentes.\n",
        "\n",
        "Notez bien que même si ces images peuvent être représentées par une matrice  $28\\times28$, elles sont en faites stockées comme des tenseurs $1\\times28\\times28$ de façon à expliciter qu'au lieu des habituels 3 canaux RGB, on n'a ici qu'un seul canal qui indique le niveau de gris."
      ],
      "metadata": {
        "id": "rzf7C-0wkU0i"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "\n",
        "training_data = datasets.FashionMNIST(\n",
        "    root=\"data\",\n",
        "    train=True,\n",
        "    download=True,\n",
        "    transform=ToTensor()\n",
        ")\n",
        "\n",
        "\n",
        "train_val_ratio = 0.8\n",
        "train_size = int(train_val_ratio * len(training_data))\n",
        "val_size = len(training_data) - train_size\n",
        "training_data, val_data = torch.utils.data.random_split(training_data, [train_size, val_size])\n",
        "\n",
        "\n",
        "test_data = datasets.FashionMNIST(\n",
        "    root=\"data\",\n",
        "    train=False,\n",
        "    download=True,\n",
        "    transform=ToTensor()\n",
        ")\n",
        "\n",
        "NB_CLASSES=10\n",
        "\n",
        "labels_map = {\n",
        "    0: \"T-Shirt\",\n",
        "    1: \"Trouser\",\n",
        "    2: \"Pullover\",\n",
        "    3: \"Dress\",\n",
        "    4: \"Coat\",\n",
        "    5: \"Sandal\",\n",
        "    6: \"Shirt\",\n",
        "    7: \"Sneaker\",\n",
        "    8: \"Bag\",\n",
        "    9: \"Ankle Boot\",\n",
        "}\n",
        "\n",
        "figure = plt.figure(figsize=(8, 8))\n",
        "cols, rows = 5,2\n",
        "for i in range(1, cols * rows + 1):\n",
        "    sample_idx = torch.randint(len(training_data), size=(1,)).item()\n",
        "    img, label = training_data[sample_idx]\n",
        "    figure.add_subplot(rows, cols, i)\n",
        "    plt.title(labels_map[label])\n",
        "    plt.axis(\"off\")\n",
        "    plt.imshow(img.squeeze(), cmap=\"gray\")\n",
        "plt.show()\n"
      ],
      "metadata": {
        "id": "JaoC-LUpkSzo"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## On charge les données"
      ],
      "metadata": {
        "id": "lJMakfDelutS"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "BATCH_SIZE=512\n",
        "\n",
        "train_dataloader = DataLoader(training_data, batch_size=BATCH_SIZE, shuffle=True, pin_memory=True)\n",
        "val_dataloader = DataLoader(val_data, batch_size=BATCH_SIZE, shuffle=False, pin_memory=True)\n",
        "test_dataloader = DataLoader(test_data, batch_size=BATCH_SIZE, shuffle=False, pin_memory=True)\n"
      ],
      "metadata": {
        "id": "1PXOj23Yl0DM"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "# Display image and label.\n",
        "train_iter = iter(train_dataloader) #get an iterator over data\n",
        "train_features, train_labels = next(train_iter) # get a batch of data\n",
        "print(f\"Feature batch shape: {train_features.size()}\")\n",
        "print(f\"Labels batch shape: {train_labels.size()}\")\n",
        "img = train_features[0].squeeze() # reformat the first pic as 28x28\n",
        "label = train_labels[0]\n",
        "plt.imshow(img, cmap=\"gray\") #it's B&W\n",
        "plt.show()\n",
        "print(f\"Label: {label}\")"
      ],
      "metadata": {
        "id": "0GseLj8xl3B0"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "Que contient notre image ?"
      ],
      "metadata": {
        "id": "aj4jcMslmoma"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "print(img)"
      ],
      "metadata": {
        "id": "obY_b5JFmmIn"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Évaluation\n",
        "\n"
      ],
      "metadata": {
        "id": "I4_15CE-qZI9"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "def eval(net, dataloader, device, epoch):\n",
        "\n",
        "  total_test = 0.0\n",
        "  correct_test = 0.0\n",
        "\n",
        "  net.eval()\n",
        "\n",
        "  #iterate over data\n",
        "  for local_batch, local_labels in dataloader:\n",
        "    # Transfer to GPU\n",
        "    local_batch, local_labels = local_batch.to(device), local_labels.to(device)\n",
        "\n",
        "    preds = net(local_batch)\n",
        "    total_test += preds.size(0)\n",
        "    correct_test += (torch.argmax(preds, dim=1) == local_labels).sum()\n",
        "\n",
        "  print(f\"Epoch: {epoch}, Test accuracy: {correct_test/total_test}\")\n"
      ],
      "metadata": {
        "id": "ZDPphYp9qmKh"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Implémentation d'un Perceptron multicouche\n",
        "\n",
        "Implementer la class MLP pour les perceptrons multicouches.\n",
        "\n",
        "Le constructeur prend en entrée:\n",
        "- *input_size* la taille du vecteur d'entrée\n",
        "- *hidden_sizes* la liste des tailles des couches internes\n",
        "- *output_size* la taille de la couche de sortie\n",
        "- *activation* la classe qui implémente la fonction à appliquer après chaque transformation linéaire\n",
        "\n",
        "Aide: vous pouvez utiliser la classe **Sequential** pour modéliser la liste des transformations de votre MLP\n",
        "\n",
        "La fonction forward prend entrée une image. Il faut d'abord transformer l'image (le tenseur à 3 dimensions) en un vecteur, puis on applique successivement chaque transformation linéaire, suivie d'une activation si on n'est pas sur la dernière couche."
      ],
      "metadata": {
        "id": "mzPlBXQPm0Sk"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "class MLP(torch.nn.Module):\n",
        "\n",
        "  def __init__(self, input_size, hidden_sizes, output_size, activation):\n",
        "    super().__init__()\n",
        "\n",
        "    #your code begins here\n",
        "    pass\n",
        "    #your code ends here\n",
        "\n",
        "  def forward(self,x):\n",
        "    #your code begins here\n",
        "    pass\n",
        "    #your code ends here\n"
      ],
      "metadata": {
        "id": "hy5AHLqmscwE"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Boucle d'entraînement"
      ],
      "metadata": {
        "id": "3QnVpiWQmunT"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "def train(net, optimizer, epochs, trainloader, valloader, testloader, batch_size, device, eval_frq=10):\n",
        "\n",
        "  loss_fn = torch.nn.CrossEntropyLoss()\n",
        "\n",
        "  total_loss = 0.0\n",
        "\n",
        "  n_batches = 0\n",
        "  for epoch in range(epochs):\n",
        "\n",
        "    net.train()\n",
        "    # iterate through train data\n",
        "    #   -> get new batch\n",
        "    #   -> send it to device\n",
        "    #   -> compute class scores with net\n",
        "    #   -> compute loss and compute the gradient of the loss\n",
        "    #   -> call step() on optimizer to perform SGD, reset gradients to zero\n",
        "\n",
        "    #your code begins here\n",
        "    pass\n",
        "    #your code ends here\n",
        "\n",
        "    # eval if it's time...\n",
        "    if epoch % eval_frq == 0:\n",
        "      print(f\"Epoch: {epoch}, Mean loss: {total_loss/n_batches}\")\n",
        "      eval(net, valloader, device, epoch)\n"
      ],
      "metadata": {
        "id": "yndf6qwkm4-E"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "### Entraînement d'un modèle MLP\n",
        "\n",
        "Si tout est correct, voud devriez avoir un MLP qui arrive à 90% de classifications correctes."
      ],
      "metadata": {
        "id": "401Go9Xguhct"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "net = MLP(28*28, [512, 256], NB_CLASSES, torch.nn.ReLU)\n",
        "opt = torch.optim.Adam(net.parameters(),lr=0.001)\n",
        "\n",
        "use_cuda = torch.cuda.is_available()\n",
        "print(use_cuda)\n",
        "device = torch.device(\"cuda:0\" if use_cuda else \"cpu\")\n",
        "net = net.to(device)\n",
        "\n",
        "train(net, opt, epochs=50, trainloader=train_dataloader, valloader=val_dataloader, testloader=test_dataloader, batch_size=BATCH_SIZE, device=device, eval_frq=5)\n"
      ],
      "metadata": {
        "id": "LAfIuWrnuhEf"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Un premier réseau à convolutions\n",
        "\n",
        "Implémenter la classe pour le CNN qui implémente les étapes suivantes:\n",
        "- *convolution 1* qui transforme chaque point (1 canal $\\to$ 10 canaux) en regardant son voisinage de taille $5\\times5$. Régler le *padding* pour que l'image de sortie ait la même taille ($28\\times28$) que l'entrée\n",
        "- *max-pooling 1* qui prend pour chaque canal la plus grande valeur dans un carré de taille $2\\times2$. Essayer ensuite de changer la valeur du paramètre  *stride* à $2$.\n",
        "- *convolution 2* qui transforme chaque point (10 canaux $\\to$ 20 canaux) en regardant son voisinage de taille $5\\times5$. Régler le *padding* pour que l'image de sortie ait la taille $10\\times10$\n",
        "- *max-pooling 2* qui prend pour chaque canal la plus grande valeur dans un carré de taille $2\\times2$. Essayer ensuite de changer la valeur du paramètre  *stride* à $2$.\n",
        "- *convolution 3* qui transforme chaque point (20 canaux $\\to$ 40 canaux) en regardant son voisinage de taille $3\\times3$. Régler le *padding* pour que l'image de sortie ait la taille $3\\times3$\n",
        "- *max-pooling 3* qui prend pour chaque canal la plus grande valeur dans un carré de taille $3\\times3$.\n",
        "- un MLP qui a pour taille de couches internes (en plus de la couche d'entrée et la taille de sortie) $128$ et $64$.\n",
        "\n",
        "\n",
        "\n",
        "\n"
      ],
      "metadata": {
        "id": "_7BB6B4xu3YF"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "class ConvNetFM(torch.nn.Module):\n",
        "  def __init__(self,):\n",
        "    super().__init__()\n",
        "\n",
        "    #your code begins here\n",
        "    self.c1 = None\n",
        "    self.m1 = None\n",
        "    self.c2 = None\n",
        "    self.m2 = None\n",
        "    self.c3 = None\n",
        "    self.m3 = None\n",
        "    self.mlp = None\n",
        "    #your code ends here\n",
        "\n",
        "  def forward(self,x):\n",
        "    #print(x.size())\n",
        "    x = self.c1(x)\n",
        "    x = self.m1(x)\n",
        "    x = self.c2(x)\n",
        "    x = self.m2(x)\n",
        "    x = self.c3(x)\n",
        "    x = self.m3(x)\n",
        "    x = self.mlp(x)\n",
        "    return x"
      ],
      "metadata": {
        "id": "S6zyxg9NvHg5"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "Vous devriez obtenir aussi une exactitude à 90% avec ce réseau.\n",
        "Sur des petites images sans couleur, MLP et ConvNet se valent."
      ],
      "metadata": {
        "id": "pkJ8ERfj8_BL"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "net = ConvNetFM()\n",
        "\n",
        "opt = torch.optim.Adam(net.parameters(),lr=0.001)\n",
        "\n",
        "use_cuda = torch.cuda.is_available()\n",
        "print(use_cuda)\n",
        "device = torch.device(\"cuda:0\" if use_cuda else \"cpu\")\n",
        "net = net.to(device)\n",
        "\n",
        "train(net, opt, epochs=50, trainloader=train_dataloader, valloader= val_dataloader, testloader=test_dataloader, batch_size=BATCH_SIZE, device=device, eval_frq=5)\n"
      ],
      "metadata": {
        "id": "rST9hFYh82pw"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Le corpus CIFAR10\n",
        "\n",
        "On va maintenant appliquer et adapter nos classificateurs à des images couleurs de taille $32\\times32$ où chaque point a pour commencer 3 canaux d'information qui correspondent aux intensités RGB (rouge,vert,bleu)"
      ],
      "metadata": {
        "id": "2u-d36c46qG_"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "\n",
        "training_data = datasets.CIFAR10(\n",
        "    root=\"data\",\n",
        "    train=True,\n",
        "    download=True,\n",
        "    transform=ToTensor()\n",
        ")\n",
        "\n",
        "test_data = datasets.CIFAR10(\n",
        "    root=\"data\",\n",
        "    train=False,\n",
        "    download=True,\n",
        "    transform=ToTensor()\n",
        ")"
      ],
      "metadata": {
        "id": "vX3xpo3y7L1Q"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "NB_CLASSES=10\n",
        "\n",
        "labels_map ={\n",
        "    0:\"airplane\",\n",
        "    1:\"automobile\",\n",
        "    2:\"bird\",\n",
        "    3:\"cat\",\n",
        "    4: \"deer\",\n",
        "    5: \"dog\",\n",
        "    6: \"frog\",\n",
        "    7: \"horse\",\n",
        "    8: \"ship\",\n",
        "    9: \"truck\"\n",
        "}\n",
        "\n",
        "figure = plt.figure(figsize=(8, 8))\n",
        "cols, rows = 5,2\n",
        "for i in range(1, cols * rows + 1):\n",
        "    sample_idx = torch.randint(len(training_data), size=(1,)).item()\n",
        "    img, label = training_data[sample_idx]\n",
        "    figure.add_subplot(rows, cols, i)\n",
        "    plt.title(labels_map[label])\n",
        "    plt.axis(\"off\")\n",
        "    plt.imshow(img.permute(1,2,0)) #quizz question: why permute ?\n",
        "plt.show()\n"
      ],
      "metadata": {
        "id": "M-MtP6zJ7PsJ"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "\n",
        "train_dataloader = DataLoader(training_data, batch_size=BATCH_SIZE, shuffle=True, pin_memory=True)\n",
        "test_dataloader = DataLoader(test_data, batch_size=BATCH_SIZE, shuffle=False, pin_memory=True)\n"
      ],
      "metadata": {
        "id": "iVLuJQX17Wit"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "# Display image and label.\n",
        "train_iter = iter(train_dataloader)\n",
        "train_features, train_labels = next(train_iter)\n",
        "print(f\"Feature batch shape: {train_features.size()}\")\n",
        "print(f\"Labels batch shape: {train_labels.size()}\")\n",
        "img = train_features[0].squeeze()\n",
        "label = train_labels[0]\n",
        "plt.imshow(img.permute(1,2,0))\n",
        "plt.show()\n",
        "print(f\"Label: {label}\")"
      ],
      "metadata": {
        "id": "z5DMIc7m7mJp"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "train_features, train_labels = next(train_iter)\n",
        "print(f\"Feature batch shape: {train_features.size()}\")\n",
        "print(f\"Labels batch shape: {train_labels.size()}\")\n",
        "img = train_features[0].squeeze()\n",
        "label = train_labels[0]\n",
        "plt.imshow(img.permute(1,2,0))\n",
        "plt.show()\n",
        "print(f\"Label: {label}\")"
      ],
      "metadata": {
        "id": "SydtRF9L7pxH"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "print(img)"
      ],
      "metadata": {
        "id": "qyejDJmT7t10"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Classification avec un MLP\n",
        "\n",
        "Créer l'objet MLP (tailles intermédiaires $1024,512,256$)"
      ],
      "metadata": {
        "id": "s7jN_5EE73TT"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "#your code begins here\n",
        "net = MLP(?????, [1024, 512, 256], ????, torch.nn.ReLU)\n",
        "#your code ends here\n",
        "\n",
        "opt = torch.optim.Adam(net.parameters(),lr=0.001)\n",
        "\n",
        "use_cuda = torch.cuda.is_available()\n",
        "print(use_cuda)\n",
        "device = torch.device(\"cuda:0\" if use_cuda else \"cpu\")\n",
        "net = net.to(device)\n",
        "\n",
        "train(net, opt, epochs=50, trainloader=train_dataloader, valloader= val_dataloader, testloader=test_dataloader, batch_size=BATCH_SIZE, device=device, eval_frq=5)\n"
      ],
      "metadata": {
        "id": "9sicbro778e2"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Classification avec convolutions\n",
        "\n",
        "\n",
        "Adapter la classe pour le CNN qui implémente les étapes suivantes:\n",
        "- *convolution 1* qui transforme chaque point (3 canaux $\\to$ 10 canaux) en regardant son voisinage de taille $5\\times5$. Régler le *padding* pour que l'image de sortie ait la même taille ($32\\times32$) que l'entrée\n",
        "- *max-pooling 1* qui prend pour chaque canal la plus grande valeur dans un carré de taille $2\\times2$. Mettre la valeur du paramètre  *stride* à $2$.\n",
        "- *convolution 2* qui transforme chaque point (10 canaux $\\to$ 20 canaux) en regardant son voisinage de taille $5\\times5$. Régler le *padding* pour que l'image de sortie ait la taille $12\\times12$\n",
        "- *max-pooling 2* qui prend pour chaque canal la plus grande valeur dans un carré de taille $2\\times2$. Mettre la valeur du paramètre  *stride* à $2$.\n",
        "- *convolution 3* qui transforme chaque point (20 canaux $\\to$ 40 canaux) en regardant son voisinage de taille $3\\times3$. Régler le *padding* pour que l'image de sortie ait la taille $4\\times4$\n",
        "- *max-pooling 3* qui prend pour chaque canal la plus grande valeur dans un carré de taille $4\\times4$.\n",
        "- un MLP qui a pour taille de couches internes (en plus de la couche d'entrée et la taille de sortie) $128$ et $64$.\n"
      ],
      "metadata": {
        "id": "nGgO6iis9U49"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "class ConvNet(torch.nn.Module):\n",
        "  def __init__(self,):\n",
        "    super().__init__()\n",
        "\n",
        "    #your code begins here\n",
        "    self.c1 = None\n",
        "    self.m1 = None\n",
        "    self.c2 = None\n",
        "    self.m2 = None\n",
        "    self.c3 = None\n",
        "    self.m3 = None\n",
        "    self.mlp = None\n",
        "    #your code ends here\n",
        "\n",
        "  def forward(self,x):\n",
        "    #print(x.size())\n",
        "    x = self.c1(x)\n",
        "    x = self.m1(x)\n",
        "    x = self.c2(x)\n",
        "    x = self.m2(x)\n",
        "    x = self.c3(x)\n",
        "    x = self.m3(x)\n",
        "    x = self.mlp(x)\n",
        "    return x"
      ],
      "metadata": {
        "id": "kx1OT8yX9x6v"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "net = ConvNet()\n",
        "\n",
        "opt = torch.optim.Adam(net.parameters(),lr=0.001)\n",
        "\n",
        "use_cuda = torch.cuda.is_available()\n",
        "print(use_cuda)\n",
        "device = torch.device(\"cuda:0\" if use_cuda else \"cpu\")\n",
        "net = net.to(device)\n",
        "\n",
        "train(net, opt, epochs=50, trainloader=train_dataloader, valloader= val_dataloader, testloader=test_dataloader, batch_size=BATCH_SIZE, device=device, eval_frq=5)\n"
      ],
      "metadata": {
        "id": "2WAXiYf7-s5v"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Une autre architecture pour les convolutions\n",
        "\n",
        "On va s'inspirer d'une architecture plus récente (VGG, https://arxiv.org/pdf/1409.1556.pdf) pour les réseaux à convolutions.\n",
        "\n",
        "L'idée est de construire le réseaux à partir de *blocs* constitués de deux convolutions successives, suivies d'un *pooling*.\n",
        "Chaque nouveau bloc double le nombre de canaux par rapport au bloc précédent. Finalement un MLP transforme l'image obtenu à la fin du dernier bloc en un vecteur de scores pour les classes.\n",
        "\n",
        "\n",
        "Nous allons implémenter cette architecture pour 1,2,3 blocs.\n",
        "\n",
        "\n",
        "\n",
        "\n"
      ],
      "metadata": {
        "id": "8MK4_84r-1i2"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "### VGG \"1 bloc\"\n",
        "\n",
        "Implémenter la classe VGG1.\n",
        "Le module est constitué de:\n",
        "- *conv 1-1* une convolution $3$ canaux $\\to$ $32$ canaux, avec un voisinage de taille $3\\times3$ avec un padding réglé pour que l'image de sortie ait la même taille que la l'image d'entrée ($32\\times32$)\n",
        "- *activ-1* une activation élémentaire, ici ReLU\n",
        "- *conv 1-2* une convolution $32$ canaux $\\to$ $32$ canaux, avec un voisinage de taille $3\\times3$ avec un padding réglé pour que l'image de sortie ait la même taille que la l'image d'entrée ($32\\times32$)\n",
        "- *pooling 1*  un *pooling* $\\max$ sur une fenêtre de $2\\times2$\n",
        "- un MLP avec couches internes de tailles $128,64$.\n",
        "\n"
      ],
      "metadata": {
        "id": "9Et1F8SHAbAX"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "class VGG1(torch.nn.Module):\n",
        "  def __init__(self,):\n",
        "    super().__init__()\n",
        "\n",
        "    #your code begins here\n",
        "    self.c11 = None\n",
        "    self.c12 = None\n",
        "    self.m1 = None\n",
        "    self.mlp = None\n",
        "    #your code ends here\n",
        "\n",
        "  def forward(self,x):\n",
        "    #print(x.size())\n",
        "    x = self.c11(x)\n",
        "    x = torch.relu(x)\n",
        "    x = self.c12(x)\n",
        "    x = self.m1(x)\n",
        "    x = self.mlp(x)\n",
        "    return x\n"
      ],
      "metadata": {
        "id": "viGSUuTbAyR8"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "net = VGG1()\n",
        "\n",
        "opt = torch.optim.Adam(net.parameters(),lr=0.001)\n",
        "\n",
        "use_cuda = torch.cuda.is_available()\n",
        "print(use_cuda)\n",
        "device = torch.device(\"cuda:0\" if use_cuda else \"cpu\")\n",
        "net = net.to(device)\n",
        "\n",
        "train(net, opt, epochs=50, trainloader=train_dataloader, valloader= val_dataloader, testloader=test_dataloader, batch_size=BATCH_SIZE, device=device, eval_frq=5)\n"
      ],
      "metadata": {
        "id": "jIxgOgZ3Ch4_"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "### VGG \"2 blocs\"\n",
        "\n",
        "Implémenter la classe VGG2.\n",
        "Le module est constitué de:\n",
        "- de *conv 1-1*, *conv 1-2* et *pooling 1* comme dans VGG1\n",
        "- *conv 2-1* une convolution $32$ canaux $\\to$ $64$ canaux, avec un voisinage de taille $3\\times3$ avec un padding réglé pour que l'image de sortie ait la même taille que la l'image en entrée du 2e bloc\n",
        "- *activ-2* une activation\n",
        "- *conv 2-2* une convolution $64$ canaux $\\to$ $64$ canaux, avec un voisinage de taille $3\\times3$ avec un padding réglé pour que l'image de sortie ait la même taille\n",
        "- *pooling 2*  un *pooling* $\\max$ sur une fenêtre de $2\\times2$\n",
        "- un MLP avec couches internes de tailles $128,64$.\n"
      ],
      "metadata": {
        "id": "dDt9zrdMCllp"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "class VGG2(torch.nn.Module):\n",
        "  def __init__(self,):\n",
        "    super().__init__()\n",
        "\n",
        "    #your code begins here\n",
        "    self.c11 = None\n",
        "    self.c12 = None\n",
        "    self.m1 = None\n",
        "\n",
        "    self.c21 = None\n",
        "    self.c22 = None\n",
        "    self.m2 = None\n",
        "\n",
        "    self.mlp = None\n",
        "    #your code ends here\n",
        "\n",
        "  def forward(self,x):\n",
        "    #print(x.size())\n",
        "    x = self.c11(x)\n",
        "    x = torch.relu(x)\n",
        "    x = self.c12(x)\n",
        "    x = self.m1(x)\n",
        "    x = self.c21(x)\n",
        "    x = torch.relu(x)\n",
        "    x = self.c22(x)\n",
        "    x = self.m2(x)\n",
        "    x = self.mlp(x)\n",
        "    return x"
      ],
      "metadata": {
        "id": "ewiRN32uDQZe"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "net = VGG2()\n",
        "\n",
        "opt = torch.optim.Adam(net.parameters(),lr=0.001)\n",
        "\n",
        "use_cuda = torch.cuda.is_available()\n",
        "print(use_cuda)\n",
        "device = torch.device(\"cuda:0\" if use_cuda else \"cpu\")\n",
        "net = net.to(device)\n",
        "\n",
        "train(net, opt, epochs=50, trainloader=train_dataloader, valloader= val_dataloader, testloader=test_dataloader, batch_size=BATCH_SIZE, device=device, eval_frq=5)\n"
      ],
      "metadata": {
        "id": "lym1H1zNDcTP"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "### VGG \"3 blocs\"\n",
        "\n",
        "Implémenter la classe VGG3.\n",
        "Le module est constitué de:\n",
        "- de *conv 1-1*, *conv 1-2*, *pooling 1*, *conv 2-1*, *conv 2-2*, *pooling 2*comme dans VGG2\n",
        "- *conv 3-1* une convolution $64$ canaux $\\to$ $128$ canaux, avec un voisinage de taille $3\\times3$ avec un padding réglé pour que l'image de sortie ait la même taille que la l'image en entrée du 2e bloc\n",
        "- *activ-3* l'activation\n",
        "- *conv 3-2* une convolution $128$ canaux $\\to$ $128$ canaux, avec un voisinage de taille $3\\times3$ avec un padding réglé pour que l'image de sortie ait la même taille\n",
        "- *pooling 3*  un *pooling* $\\max$ sur une fenêtre de $2\\times2$\n",
        "- un MLP avec couches internes de tailles $128,64$.\n"
      ],
      "metadata": {
        "id": "aUj6cofiDizr"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "class VGG2(torch.nn.Module):\n",
        "  def __init__(self,):\n",
        "    super().__init__()\n",
        "\n",
        "    #your code begins here\n",
        "    self.c11 = None\n",
        "    self.c12 = None\n",
        "    self.m1 = None\n",
        "\n",
        "    self.c21 = None\n",
        "    self.c22 = None\n",
        "    self.m2 = None\n",
        "\n",
        "    self.c31 = None\n",
        "    self.c32 = None\n",
        "    self.m3 = None\n",
        "\n",
        "    self.mlp = None\n",
        "    #your code ends here\n",
        "\n",
        "  def forward(self,x):\n",
        "    #print(x.size())\n",
        "    x = self.c11(x)\n",
        "    x = torch.relu(x)\n",
        "    x = self.c12(x)\n",
        "    x = self.m1(x)\n",
        "    x = self.c21(x)\n",
        "    x = torch.relu(x)\n",
        "    x = self.c22(x)\n",
        "    x = self.m2(x)\n",
        "    x = self.c31(x)\n",
        "    x = torch.relu(x)\n",
        "    x = self.c32(x)\n",
        "    x = self.m3(x)\n",
        "    x = self.mlp(x)\n",
        "    return x"
      ],
      "metadata": {
        "id": "Zg3B7wtJDhLM"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "Avec ce dernier modèle, vous devriez atteindre 75% d'exactitude"
      ],
      "metadata": {
        "id": "1mmFDq7JEET8"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "net = VGG3()\n",
        "\n",
        "opt = torch.optim.Adam(net.parameters(),lr=0.001)\n",
        "\n",
        "use_cuda = torch.cuda.is_available()\n",
        "print(use_cuda)\n",
        "device = torch.device(\"cuda:0\" if use_cuda else \"cpu\")\n",
        "net = net.to(device)\n",
        "\n",
        "train(net, opt, epochs=50, trainloader=train_dataloader, valloader= val_dataloader, testloader=test_dataloader, batch_size=BATCH_SIZE, device=device, eval_frq=5)\n"
      ],
      "metadata": {
        "id": "dbtfX6rrECbD"
      },
      "execution_count": null,
      "outputs": []
    }
  ]
}