diff --git a/My images b/My images new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/My images @@ -0,0 +1 @@ + diff --git a/braised-steak-recipe-2.jpg b/braised-steak-recipe-2.jpg new file mode 100644 index 00000000..858fa61e Binary files /dev/null and b/braised-steak-recipe-2.jpg differ diff --git a/data/pizza_steak_sushi_40_percent.zip b/data/pizza_steak_sushi_40_percent.zip new file mode 100644 index 00000000..a580b10e Binary files /dev/null and b/data/pizza_steak_sushi_40_percent.zip differ diff --git a/extras/exercises/00_pytorch_fundamentals_exercises.ipynb b/extras/exercises/00_pytorch_fundamentals_exercises.ipynb index c7e0d959..d9a3c612 100644 --- a/extras/exercises/00_pytorch_fundamentals_exercises.ipynb +++ b/extras/exercises/00_pytorch_fundamentals_exercises.ipynb @@ -5,7 +5,7 @@ "colab": { "name": "00_pytorch_fundamentals_exercises.ipynb", "provenance": [], - "collapsed_sections": [] + "gpuType": "T4" }, "kernelspec": { "name": "python3", @@ -22,7 +22,7 @@ "source": [ "# 00. PyTorch Fundamentals Exercises\n", "\n", - "### 1. Documentation reading \n", + "### 1. Documentation reading\n", "\n", "A big part of deep learning (and learning to code in general) is getting familiar with the documentation of a certain framework you're using. We'll be using the PyTorch documentation a lot throughout the rest of this course. So I'd recommend spending 10-minutes reading the following (it's okay if you don't get some things for now, the focus is not yet full understanding, it's awareness):\n", " * The documentation on [`torch.Tensor`](https://pytorch.org/docs/stable/tensors.html#torch-tensor).\n", @@ -41,7 +41,7 @@ "metadata": { "id": "bGD0oD8Kizak" }, - "execution_count": 1, + "execution_count": null, "outputs": [] }, { @@ -57,15 +57,38 @@ "cell_type": "code", "source": [ "# Import torch\n", + "import torch\n", "\n", - "\n", - "# Create random tensor\n" + "# Create random tensor\n", + "tensor = torch.rand(7,7)\n", + "tensor" ], "metadata": { - "id": "6pUq9Dc8i2L7" + "id": "6pUq9Dc8i2L7", + "outputId": "a6081379-f7dd-4b2f-9809-eebec0ea5d42", + "colab": { + "base_uri": "https://localhost:8080/" + } }, - "execution_count": 2, - "outputs": [] + "execution_count": 1, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "tensor([[0.9473, 0.4796, 0.8238, 0.2162, 0.4292, 0.2930, 0.4013],\n", + " [0.0909, 0.9036, 0.1976, 0.7709, 0.3027, 0.3923, 0.5768],\n", + " [0.2172, 0.4720, 0.0324, 0.1137, 0.2709, 0.5308, 0.0708],\n", + " [0.4380, 0.2407, 0.6845, 0.4044, 0.4465, 0.0734, 0.6406],\n", + " [0.6649, 0.3130, 0.4927, 0.1371, 0.5819, 0.9410, 0.6062],\n", + " [0.3131, 0.9110, 0.0606, 0.9862, 0.9770, 0.1302, 0.8300],\n", + " [0.3633, 0.5432, 0.0048, 0.7753, 0.5374, 0.2534, 0.7470]])" + ] + }, + "metadata": {}, + "execution_count": 1 + } + ] }, { "cell_type": "markdown", @@ -80,14 +103,30 @@ "cell_type": "code", "source": [ "# Create another random tensor\n", - "\n", - "# Perform matrix multiplication \n" + "tensor2= torch.rand(1,7)\n", + "# Perform matrix multiplication\n", + "torch.mm(tensor2, tensor)" ], "metadata": { - "id": "NcLqR0Sbi_vT" + "id": "NcLqR0Sbi_vT", + "outputId": "1ca6c5b2-d54e-4847-8ae8-b4171ee48654", + "colab": { + "base_uri": "https://localhost:8080/" + } }, - "execution_count": 3, - "outputs": [] + "execution_count": 10, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "tensor([[1.5162, 1.4619, 1.2879, 0.9661, 1.1961, 1.2263, 1.2790]])" + ] + }, + "metadata": {}, + "execution_count": 10 + } + ] }, { "cell_type": "markdown", @@ -113,18 +152,44 @@ "cell_type": "code", "source": [ "# Set manual seed\n", - "\n", - "\n", + "random_seed=0\n", "# Create two random tensors\n", + "torch.manual_seed(random_seed)\n", + "tensor_a=torch.rand(7,7)\n", + "torch.manual_seed(random_seed)\n", + "tensor_b=torch.rand(1,7)\n", "\n", - "\n", - "# Matrix multiply tensors\n" + "# Matrix multiply tensors\n", + "mul= torch.mm(tensor_a,tensor_b.T)\n", + "mul, mul.shape" ], "metadata": { - "id": "D-lOWI_1jRMm" + "id": "D-lOWI_1jRMm", + "outputId": "d563b732-f901-40af-ab64-e34849ca543c", + "colab": { + "base_uri": "https://localhost:8080/" + } }, - "execution_count": 4, - "outputs": [] + "execution_count": 49, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "(tensor([[1.5985],\n", + " [1.1173],\n", + " [1.2741],\n", + " [1.6838],\n", + " [0.8279],\n", + " [1.0347],\n", + " [1.2498]]),\n", + " torch.Size([7, 1]))" + ] + }, + "metadata": {}, + "execution_count": 49 + } + ] }, { "cell_type": "markdown", @@ -144,7 +209,7 @@ "metadata": { "id": "_LKWcfSTjp00" }, - "execution_count": 5, + "execution_count": null, "outputs": [] }, { @@ -169,17 +234,48 @@ "cell_type": "code", "source": [ "# Set random seed\n", - "\n", - "\n", + "torch.cuda.manual_seed(1234)\n", + "torch.cuda.set_device(0)\n", "# Check for access to GPU\n", - "\n", - "# Create two random tensors on GPU\n" + "device= \"cuda\" if torch.cuda.is_available() else \"cpu\"\n", + "print(f\"Device: {device}\")\n", + "# Create two random tensors on GPU\n", + "torch.cuda.manual_seed(1234)\n", + "tensor_c = torch.rand(2,3).to(device)\n", + "torch.cuda.manual_seed(1234)\n", + "tensor_d = torch.rand(2,3).to(device)\n", + "tensor_c, tensor_d\n" ], "metadata": { - "id": "azXExiFZj5nm" + "id": "azXExiFZj5nm", + "outputId": "a2f6555a-d7be-4c8d-a4c5-e296f5a753aa", + "colab": { + "base_uri": "https://localhost:8080/" + } }, - "execution_count": 6, - "outputs": [] + "execution_count": 58, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Device: cuda\n" + ] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "(tensor([[0.7529, 0.8579, 0.6870],\n", + " [0.0051, 0.1757, 0.7497]], device='cuda:0'),\n", + " tensor([[0.6047, 0.1100, 0.2121],\n", + " [0.9704, 0.8369, 0.2820]], device='cuda:0'))" + ] + }, + "metadata": {}, + "execution_count": 58 + } + ] }, { "cell_type": "markdown", @@ -200,13 +296,32 @@ { "cell_type": "code", "source": [ - "# Perform matmul on tensor_A and tensor_B\n" + "# Perform matmul on tensor_A and tensor_B\n", + "mul2=torch.mm(tensor_c,tensor_d.T)\n", + "mul2,mul2.shape" ], "metadata": { - "id": "fAeG7ox0lHEO" + "id": "fAeG7ox0lHEO", + "outputId": "b8f0cf80-35bb-41d4-b94b-cf822b384024", + "colab": { + "base_uri": "https://localhost:8080/" + } }, - "execution_count": 7, - "outputs": [] + "execution_count": 47, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "(tensor([[1.1941, 0.6911],\n", + " [0.3175, 0.2087]], device='cuda:0'),\n", + " torch.Size([2, 2]))" + ] + }, + "metadata": {}, + "execution_count": 47 + } + ] }, { "cell_type": "markdown", @@ -221,14 +336,31 @@ "cell_type": "code", "source": [ "# Find max\n", - "\n", - "# Find min\n" + "max= torch.max(mul2)\n", + "# Find min\n", + "min= torch.min(mul2)\n", + "max,min" ], "metadata": { - "id": "Fu8_3mZpllOd" + "id": "Fu8_3mZpllOd", + "outputId": "479d6132-727d-4980-a819-82d0466ab10e", + "colab": { + "base_uri": "https://localhost:8080/" + } }, - "execution_count": 8, - "outputs": [] + "execution_count": 60, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "(tensor(1.1941, device='cuda:0'), tensor(0.2087, device='cuda:0'))" + ] + }, + "metadata": {}, + "execution_count": 60 + } + ] }, { "cell_type": "markdown", @@ -243,15 +375,34 @@ "cell_type": "code", "source": [ "# Find arg max\n", + "max2= torch.argmax(mul2)\n", "\n", - "\n", - "# Find arg min\n" + "# Find arg min\n", + "min2=torch.argmin(mul2)\n", + "max2,min2" ], "metadata": { - "id": "CCEKt4K2lsfQ" + "id": "CCEKt4K2lsfQ", + "outputId": "c8fb459d-2b91-4b80-a210-b46275657147", + "colab": { + "base_uri": "https://localhost:8080/" + } }, - "execution_count": 9, - "outputs": [] + "execution_count": 66, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "(tensor(0, device='cuda:0'),\n", + " tensor(3, device='cuda:0'),\n", + " tensor(0.2087, device='cuda:0'))" + ] + }, + "metadata": {}, + "execution_count": 66 + } + ] }, { "cell_type": "markdown", @@ -276,20 +427,50 @@ "cell_type": "code", "source": [ "# Set seed\n", - "\n", + "torch.cuda.manual_seed(7)\n", "\n", "# Create random tensor\n", - "\n", + "tensor_f = torch.rand(1,1,1,10)\n", "\n", "# Remove single dimensions\n", + "tensor_g=tensor_f.squeeze()\n", "\n", - "\n", - "# Print out tensors and their shapes\n" + "# Print out tensors and their shapes\n", + "print(f\"tensor_f: {tensor_f}\")\n", + "print(f\"tensfor_f_shape: {tensor_f.shape}\")\n", + "print(f\"tensor_g:{tensor_g}\")\n", + "print(f\"tensor_g_shape: {tensor_g.shape}\")" ], "metadata": { - "id": "TQ9zbRzVl1jV" + "id": "TQ9zbRzVl1jV", + "outputId": "a7ee7640-b73a-4874-8682-5b4e86b0c5c4", + "colab": { + "base_uri": "https://localhost:8080/" + } }, - "execution_count": 10, + "execution_count": 69, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "tensor_f: tensor([[[[0.9537, 0.8426, 0.0784, 0.3756, 0.5226, 0.5730, 0.6186, 0.6962,\n", + " 0.5300, 0.2560]]]])\n", + "tensfor_f_shape: torch.Size([1, 1, 1, 10])\n", + "tensor_g:tensor([0.9537, 0.8426, 0.0784, 0.3756, 0.5226, 0.5730, 0.6186, 0.6962, 0.5300,\n", + " 0.2560])\n", + "tensor_g_shape: torch.Size([10])\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [], + "metadata": { + "id": "0HaL7IBHbRVU" + }, + "execution_count": null, "outputs": [] } ] diff --git a/extras/exercises/03_pytorch_computer_vision_exercises.ipynb b/extras/exercises/03_pytorch_computer_vision_exercises.ipynb index 42418274..fd6e83cb 100644 --- a/extras/exercises/03_pytorch_computer_vision_exercises.ipynb +++ b/extras/exercises/03_pytorch_computer_vision_exercises.ipynb @@ -5,8 +5,6 @@ "colab": { "name": "03_pytorch_computer_vision_exercises.ipynb", "provenance": [], - "collapsed_sections": [], - "authorship_tag": "ABX9TyMUsDcN/+FAm9Pf7Ifqs6AZ", "include_colab_link": true }, "kernelspec": { @@ -15,8 +13,7 @@ }, "language_info": { "name": "python" - }, - "accelerator": "GPU" + } }, "cells": [ { @@ -26,7 +23,7 @@ "colab_type": "text" }, "source": [ - "\"Open" + "\"Open" ] }, { @@ -42,8 +39,8 @@ "\n", "## Resources\n", "\n", - "1. These exercises are based on [notebook 03 of the Learn PyTorch for Deep Learning course](https://www.learnpytorch.io/03_pytorch_computer_vision/). \n", - "2. See a live [walkthrough of the solutions (errors and all) on YouTube](https://youtu.be/_PibmqpEyhA). \n", + "1. These exercises are based on [notebook 03 of the Learn PyTorch for Deep Learning course](https://www.learnpytorch.io/03_pytorch_computer_vision/).\n", + "2. See a live [walkthrough of the solutions (errors and all) on YouTube](https://youtu.be/_PibmqpEyhA).\n", " * **Note:** Going through these exercises took me just over 3 hours of solid coding, so you should expect around the same.\n", "3. See [other solutions on the course GitHub](https://github.com/mrdbourke/pytorch-deep-learning/tree/main/extras/solutions)." ], @@ -62,34 +59,15 @@ "base_uri": "https://localhost:8080/" }, "id": "GaeYzOTLwWh2", - "outputId": "17dd5453-9639-4b01-aa18-7ddbfd5c3253" + "outputId": "4b2efed3-e8d8-440d-e7c8-2e4d4acc2a83" }, - "execution_count": null, + "execution_count": 1, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ - "Sat Apr 16 03:23:02 2022 \n", - "+-----------------------------------------------------------------------------+\n", - "| NVIDIA-SMI 460.32.03 Driver Version: 460.32.03 CUDA Version: 11.2 |\n", - "|-------------------------------+----------------------+----------------------+\n", - "| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |\n", - "| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |\n", - "| | | MIG M. |\n", - "|===============================+======================+======================|\n", - "| 0 Tesla P100-PCIE... Off | 00000000:00:04.0 Off | 0 |\n", - "| N/A 39C P0 29W / 250W | 0MiB / 16280MiB | 0% Default |\n", - "| | | N/A |\n", - "+-------------------------------+----------------------+----------------------+\n", - " \n", - "+-----------------------------------------------------------------------------+\n", - "| Processes: |\n", - "| GPU GI CI PID Type Process name GPU Memory |\n", - "| ID ID Usage |\n", - "|=============================================================================|\n", - "| No running processes found |\n", - "+-----------------------------------------------------------------------------+\n" + "/bin/bash: line 1: nvidia-smi: command not found\n" ] } ] @@ -103,23 +81,25 @@ "# Exercises require PyTorch > 1.10.0\n", "print(torch.__version__)\n", "\n", - "# TODO: Setup device agnostic code\n" + "# TODO: Setup device agnostic code\n", + "device = \"cuda\" if torch.cuda.is_available else \"cpu\"\n", + "device" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/", - "height": 53 + "height": 54 }, "id": "DNwZLMbCzJLk", - "outputId": "9c150c50-a092-4f34-9d33-b45247fb080d" + "outputId": "dbe40728-21ba-4f17-fb18-51ca5d8ffbfe" }, - "execution_count": null, + "execution_count": 2, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ - "1.10.0+cu111\n" + "2.6.0+cu124\n" ] }, { @@ -147,40 +127,36 @@ } }, { - "cell_type": "code", + "cell_type": "markdown", "source": [ - "" + "Face recognition, disease image recognition and self autonomous driving, image segmentation." ], "metadata": { "id": "VyWRkvWGbCXj" - }, - "execution_count": null, - "outputs": [] + } }, { "cell_type": "markdown", "source": [ - "## 2. Search \"what is overfitting in machine learning\" and write down a sentence about what you find. " + "## 2. Search \"what is overfitting in machine learning\" and write down a sentence about what you find." ], "metadata": { "id": "oBK-WI6YxDYa" } }, { - "cell_type": "code", + "cell_type": "markdown", "source": [ - "" + "Overfitting is the extremely adjusting of weights and bias od the model (the parameters of the model) in order to adapt the model in a excesive way to the traning data, loosing its capacity to generalize what learn during the training to new unseen data" ], "metadata": { "id": "d1rxD6GObCqh" - }, - "execution_count": null, - "outputs": [] + } }, { "cell_type": "markdown", "source": [ - "## 3. Search \"ways to prevent overfitting in machine learning\", write down 3 of the things you find and a sentence about each. \n", + "## 3. Search \"ways to prevent overfitting in machine learning\", write down 3 of the things you find and a sentence about each.\n", "> **Note:** there are lots of these, so don't worry too much about all of them, just pick 3 and start with those." ], "metadata": { @@ -188,15 +164,20 @@ } }, { - "cell_type": "code", + "cell_type": "markdown", "source": [ - "" + "There are ways to unsupervised models and supervised ones:\n", + "if we have NN: we can mention :\n", + " -the early stopping (where can be set an plot of the model loss, in order to see in which precise momento of the training, it stop to decrease, decreasing the capacity of the model's learning )\n", + " -the dropout (set the training process in order to avoid some neurons, in this way the model search for smart ways to adapt itself to unseen data)\n", + " \n", + "Then can be another ones out of NN model like :\n", + "LASSO, Rigde (where there are some parameter on the cost function in order to turn to zero o minimize the importance of some repetitive or high correlated variables with others)\n", + "Anyway even if Rigde techniche is very helpful and can addpat itself to complex models, respect LASSO. it's better to face off some complex problems through NN approach" ], "metadata": { "id": "ocvOdWKcbEKr" - }, - "execution_count": null, - "outputs": [] + } }, { "cell_type": "markdown", @@ -211,13 +192,11 @@ }, { "cell_type": "code", - "source": [ - "" - ], + "source": [], "metadata": { "id": "TqZaJIRMbFtS" }, - "execution_count": null, + "execution_count": 2, "outputs": [] }, { @@ -232,13 +211,261 @@ { "cell_type": "code", "source": [ - "" + "import torch\n", + "from torch import nn\n", + "\n", + "import torchvision\n", + "from torchvision import datasets\n", + "from torchvision.transforms import ToTensor\n", + "\n", + "from matplotlib import pyplot as plt\n", + "\n", + "print(f\"Torch version: {torch.__version__}\")\n" ], "metadata": { - "id": "SHjeuN81bHza" + "id": "SHjeuN81bHza", + "outputId": "c38bb71a-c39c-4277-da06-5d75edf636e3", + "colab": { + "base_uri": "https://localhost:8080/" + } }, - "execution_count": null, - "outputs": [] + "execution_count": 3, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Torch version: 2.6.0+cu124\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "train_data = torchvision.datasets.MNIST(root = \"data\",\n", + " train = True,\n", + " transform = ToTensor(),\n", + " download = True,\n", + " target_transform = None)\n", + "\n", + "test_data = torchvision.datasets.MNIST(root=\"data\",\n", + " train = False,\n", + " transform = ToTensor(),\n", + " download = True\n", + " )" + ], + "metadata": { + "id": "nm7XKJ2JyAFm", + "outputId": "2532261d-371b-45e5-ae6b-27c271dc9895", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "execution_count": 4, + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": [ + "100%|██████████| 9.91M/9.91M [00:00<00:00, 12.7MB/s]\n", + "100%|██████████| 28.9k/28.9k [00:00<00:00, 340kB/s]\n", + "100%|██████████| 1.65M/1.65M [00:00<00:00, 3.21MB/s]\n", + "100%|██████████| 4.54k/4.54k [00:00<00:00, 4.93MB/s]\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "image, label = train_data[0]\n", + "image, label" + ], + "metadata": { + "id": "QapGXclczTj6", + "outputId": "eb2d45a4-a7ba-4044-d732-e2e63fe313b8", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "execution_count": 5, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "(tensor([[[0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000],\n", + " [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000],\n", + " [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000],\n", + " [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000],\n", + " [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000],\n", + " [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000, 0.0118, 0.0706, 0.0706, 0.0706,\n", + " 0.4941, 0.5333, 0.6863, 0.1020, 0.6510, 1.0000, 0.9686, 0.4980,\n", + " 0.0000, 0.0000, 0.0000, 0.0000],\n", + " [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.1176, 0.1412, 0.3686, 0.6039, 0.6667, 0.9922, 0.9922, 0.9922,\n", + " 0.9922, 0.9922, 0.8824, 0.6745, 0.9922, 0.9490, 0.7647, 0.2510,\n", + " 0.0000, 0.0000, 0.0000, 0.0000],\n", + " [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.1922,\n", + " 0.9333, 0.9922, 0.9922, 0.9922, 0.9922, 0.9922, 0.9922, 0.9922,\n", + " 0.9922, 0.9843, 0.3647, 0.3216, 0.3216, 0.2196, 0.1529, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000],\n", + " [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0706,\n", + " 0.8588, 0.9922, 0.9922, 0.9922, 0.9922, 0.9922, 0.7765, 0.7137,\n", + " 0.9686, 0.9451, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000],\n", + " [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.3137, 0.6118, 0.4196, 0.9922, 0.9922, 0.8039, 0.0431, 0.0000,\n", + " 0.1686, 0.6039, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000],\n", + " [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0549, 0.0039, 0.6039, 0.9922, 0.3529, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000],\n", + " [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.5451, 0.9922, 0.7451, 0.0078, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000],\n", + " [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0431, 0.7451, 0.9922, 0.2745, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000],\n", + " [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000, 0.1373, 0.9451, 0.8824, 0.6275,\n", + " 0.4235, 0.0039, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000],\n", + " [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.3176, 0.9412, 0.9922,\n", + " 0.9922, 0.4667, 0.0980, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000],\n", + " [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.1765, 0.7294,\n", + " 0.9922, 0.9922, 0.5882, 0.1059, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000],\n", + " [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0627,\n", + " 0.3647, 0.9882, 0.9922, 0.7333, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000],\n", + " [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.9765, 0.9922, 0.9765, 0.2510, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000],\n", + " [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.1804, 0.5098,\n", + " 0.7176, 0.9922, 0.9922, 0.8118, 0.0078, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000],\n", + " [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000, 0.1529, 0.5804, 0.8980, 0.9922,\n", + " 0.9922, 0.9922, 0.9804, 0.7137, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000],\n", + " [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0941, 0.4471, 0.8667, 0.9922, 0.9922, 0.9922,\n", + " 0.9922, 0.7882, 0.3059, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000],\n", + " [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0902, 0.2588, 0.8353, 0.9922, 0.9922, 0.9922, 0.9922, 0.7765,\n", + " 0.3176, 0.0078, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000],\n", + " [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0706, 0.6706,\n", + " 0.8588, 0.9922, 0.9922, 0.9922, 0.9922, 0.7647, 0.3137, 0.0353,\n", + " 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000],\n", + " [0.0000, 0.0000, 0.0000, 0.0000, 0.2157, 0.6745, 0.8863, 0.9922,\n", + " 0.9922, 0.9922, 0.9922, 0.9569, 0.5216, 0.0431, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000],\n", + " [0.0000, 0.0000, 0.0000, 0.0000, 0.5333, 0.9922, 0.9922, 0.9922,\n", + " 0.8314, 0.5294, 0.5176, 0.0627, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000],\n", + " [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000],\n", + " [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000],\n", + " [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,\n", + " 0.0000, 0.0000, 0.0000, 0.0000]]]),\n", + " 5)" + ] + }, + "metadata": {}, + "execution_count": 5 + } + ] + }, + { + "cell_type": "code", + "source": [ + "image.shape" + ], + "metadata": { + "id": "rxnKYM4l2kjg", + "outputId": "e0a91059-857a-4c59-d5be-de0536190386", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "execution_count": 6, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "torch.Size([1, 28, 28])" + ] + }, + "metadata": {}, + "execution_count": 6 + } + ] + }, + { + "cell_type": "code", + "source": [ + "len(train_data.data), len(train_data.targets), len(test_data.data), len(test_data.targets)" + ], + "metadata": { + "id": "cyQZ-DLt2tFy", + "outputId": "71a8d0fd-89c1-4666-84af-6c2450cb2433", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "execution_count": 8, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "(60000, 60000, 10000, 10000)" + ] + }, + "metadata": {}, + "execution_count": 8 + } + ] }, { "cell_type": "markdown", @@ -251,13 +478,11 @@ }, { "cell_type": "code", - "source": [ - "" - ], + "source": [], "metadata": { "id": "QVFsYi1PbItE" }, - "execution_count": null, + "execution_count": 5, "outputs": [] }, { @@ -271,13 +496,11 @@ }, { "cell_type": "code", - "source": [ - "" - ], + "source": [], "metadata": { "id": "ALA6MPcFbJXQ" }, - "execution_count": null, + "execution_count": 5, "outputs": [] }, { @@ -291,13 +514,11 @@ }, { "cell_type": "code", - "source": [ - "" - ], + "source": [], "metadata": { "id": "5IKNF22XbKYS" }, - "execution_count": null, + "execution_count": 5, "outputs": [] }, { @@ -311,13 +532,11 @@ }, { "cell_type": "code", - "source": [ - "" - ], + "source": [], "metadata": { "id": "jSo6vVWFbNLD" }, - "execution_count": null, + "execution_count": 5, "outputs": [] }, { @@ -331,13 +550,11 @@ }, { "cell_type": "code", - "source": [ - "" - ], + "source": [], "metadata": { "id": "_YGgZvSobNxu" }, - "execution_count": null, + "execution_count": 5, "outputs": [] }, { @@ -351,13 +568,11 @@ }, { "cell_type": "code", - "source": [ - "" - ], + "source": [], "metadata": { "id": "vSrXiT_AbQ6e" }, - "execution_count": null, + "execution_count": 5, "outputs": [] }, { @@ -371,21 +586,19 @@ }, { "cell_type": "code", - "source": [ - "" - ], + "source": [], "metadata": { "id": "leCTsqtSbR5P" }, - "execution_count": null, + "execution_count": 5, "outputs": [] }, { "cell_type": "markdown", "source": [ - "## 13. Use a model similar to the trained `model_2` from notebook 03 to make predictions on the test [`torchvision.datasets.FashionMNIST`](https://pytorch.org/vision/main/generated/torchvision.datasets.FashionMNIST.html) dataset. \n", - "* Then plot some predictions where the model was wrong alongside what the label of the image should've been. \n", - "* After visualing these predictions do you think it's more of a modelling error or a data error? \n", + "## 13. Use a model similar to the trained `model_2` from notebook 03 to make predictions on the test [`torchvision.datasets.FashionMNIST`](https://pytorch.org/vision/main/generated/torchvision.datasets.FashionMNIST.html) dataset.\n", + "* Then plot some predictions where the model was wrong alongside what the label of the image should've been.\n", + "* After visualing these predictions do you think it's more of a modelling error or a data error?\n", "* As in, could the model do better or are the labels of the data too close to each other (e.g. a \"Shirt\" label is too close to \"T-shirt/top\")?" ], "metadata": { @@ -394,13 +607,11 @@ }, { "cell_type": "code", - "source": [ - "" - ], + "source": [], "metadata": { "id": "78a8LjtdbSZj" }, - "execution_count": null, + "execution_count": 5, "outputs": [] } ] diff --git a/extras/exercises/07_pytorch_experiment_tracking_exercise_solut.ipynb b/extras/exercises/07_pytorch_experiment_tracking_exercise_solut.ipynb new file mode 100644 index 00000000..75ce667e --- /dev/null +++ b/extras/exercises/07_pytorch_experiment_tracking_exercise_solut.ipynb @@ -0,0 +1,1397 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "name": "07_pytorch_experiment_tracking_exercise_template.ipynb", + "provenance": [], + "gpuType": "T4", + "include_colab_link": true + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + }, + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "view-in-github", + "colab_type": "text" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# 07. PyTorch Experiment Tracking Exercise Template\n", + "\n", + "Welcome to the 07. PyTorch Experiment Tracking exercise template notebook.\n", + "\n", + "> **Note:** There may be more than one solution to each of the exercises. This notebook only shows one possible example.\n", + "\n", + "## Resources\n", + "\n", + "1. These exercises/solutions are based on [section 07. PyTorch Transfer Learning](https://www.learnpytorch.io/07_pytorch_experiment_tracking/) of the Learn PyTorch for Deep Learning course by Zero to Mastery.\n", + "2. See a live [walkthrough of the solutions (errors and all) on YouTube](https://youtu.be/cO_r2FYcAjU).\n", + "3. See [other solutions on the course GitHub](https://github.com/mrdbourke/pytorch-deep-learning/tree/main/extras/solutions).\n", + "\n", + "> **Note:** The first section of this notebook is dedicated to getting various helper functions and datasets used for the exercises. The exercises start at the heading \"Exercise 1: ...\"." + ], + "metadata": { + "id": "zNqPNlYylluR" + } + }, + { + "cell_type": "markdown", + "source": [ + "### Get various imports and helper functions\n", + "\n", + "We'll need to make sure we have `torch` v.1.12+ and `torchvision` v0.13+." + ], + "metadata": { + "id": "sf8ab9cyHTzU" + } + }, + { + "cell_type": "code", + "source": [ + "# Import torch and torchvision\n", + "import torch\n", + "import torchvision" + ], + "metadata": { + "id": "dxehhNGjUDpF" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + " # Make sure we have a GPU\n", + " device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n", + " device" + ], + "metadata": { + "id": "Nf-DsrZipCE9" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Get regular imports\n", + "import matplotlib.pyplot as plt\n", + "import torch\n", + "import torchvision\n", + "\n", + "from torch import nn\n", + "from torchvision import transforms\n", + "\n", + "# Try to get torchinfo, install it if it doesn't work\n", + "try:\n", + " from torchinfo import summary\n", + "except:\n", + " print(\"[INFO] Couldn't find torchinfo... installing it.\")\n", + " !pip install -q torchinfo\n", + " from torchinfo import summary\n", + "\n", + "# Try to import the going_modular directory, download it from GitHub if it doesn't work\n", + "try:\n", + " from going_modular.going_modular import data_setup, engine\n", + "except:\n", + " # Get the going_modular scripts\n", + " print(\"[INFO] Couldn't find going_modular scripts... downloading them from GitHub.\")\n", + " !git clone https://github.com/mrdbourke/pytorch-deep-learning\n", + " !mv pytorch-deep-learning/going_modular .\n", + " !rm -rf pytorch-deep-learning\n", + " from going_modular.going_modular import data_setup, engine" + ], + "metadata": { + "id": "i_52puIeoab3" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Set seeds\n", + "def set_seeds(seed: int=42):\n", + " \"\"\"Sets random sets for torch operations.\n", + "\n", + " Args:\n", + " seed (int, optional): Random seed to set. Defaults to 42.\n", + " \"\"\"\n", + " # Set the seed for general torch operations\n", + " torch.manual_seed(seed)\n", + " # Set the seed for CUDA torch operations (ones that happen on the GPU)\n", + " torch.cuda.manual_seed(seed)" + ], + "metadata": { + "id": "DBj8I3P9pNK2" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "import os\n", + "import zipfile\n", + "\n", + "from pathlib import Path\n", + "\n", + "import requests\n", + "\n", + "def download_data(source: str,\n", + " destination: str,\n", + " remove_source: bool = True) -> Path:\n", + " \"\"\"Downloads a zipped dataset from source and unzips to destination.\n", + "\n", + " Args:\n", + " source (str): A link to a zipped file containing data.\n", + " destination (str): A target directory to unzip data to.\n", + " remove_source (bool): Whether to remove the source after downloading and extracting.\n", + "\n", + " Returns:\n", + " pathlib.Path to downloaded data.\n", + "\n", + " Example usage:\n", + " download_data(source=\"https://github.com/mrdbourke/pytorch-deep-learning/raw/main/data/pizza_steak_sushi.zip\",\n", + " destination=\"pizza_steak_sushi\")\n", + " \"\"\"\n", + " # Setup path to data folder\n", + " data_path = Path(\"data/\")\n", + " image_path = data_path / destination\n", + "\n", + " # If the image folder doesn't exist, download it and prepare it...\n", + " if image_path.is_dir():\n", + " print(f\"[INFO] {image_path} directory exists, skipping download.\")\n", + " else:\n", + " print(f\"[INFO] Did not find {image_path} directory, creating one...\")\n", + " image_path.mkdir(parents=True, exist_ok=True)\n", + "\n", + " # Download pizza, steak, sushi data\n", + " target_file = Path(source).name\n", + " with open(data_path / target_file, \"wb\") as f:\n", + " request = requests.get(source)\n", + " print(f\"[INFO] Downloading {target_file} from {source}...\")\n", + " f.write(request.content)\n", + "\n", + " # Unzip pizza, steak, sushi data\n", + " with zipfile.ZipFile(data_path / target_file, \"r\") as zip_ref:\n", + " print(f\"[INFO] Unzipping {target_file} data...\")\n", + " zip_ref.extractall(image_path)\n", + "\n", + " # Remove .zip file\n", + " if remove_source:\n", + " os.remove(data_path / target_file)\n", + "\n", + " return image_path\n", + "\n", + "image_path = download_data(source=\"https://github.com/mrdbourke/pytorch-deep-learning/raw/main/data/pizza_steak_sushi.zip\",\n", + " destination=\"pizza_steak_sushi\")\n", + "image_path" + ], + "metadata": { + "id": "m6R-CS53pTLS" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "from torch.utils.tensorboard import SummaryWriter\n", + "def create_writer(experiment_name: str,\n", + " model_name: str,\n", + " extra: str=None):\n", + " \"\"\"Creates a torch.utils.tensorboard.writer.SummaryWriter() instance saving to a specific log_dir.\n", + "\n", + " log_dir is a combination of runs/timestamp/experiment_name/model_name/extra.\n", + "\n", + " Where timestamp is the current date in YYYY-MM-DD format.\n", + "\n", + " Args:\n", + " experiment_name (str): Name of experiment.\n", + " model_name (str): Name of model.\n", + " extra (str, optional): Anything extra to add to the directory. Defaults to None.\n", + "\n", + " Returns:\n", + " torch.utils.tensorboard.writer.SummaryWriter(): Instance of a writer saving to log_dir.\n", + "\n", + " Example usage:\n", + " # Create a writer saving to \"runs/2022-06-04/data_10_percent/effnetb2/5_epochs/\"\n", + " writer = create_writer(experiment_name=\"data_10_percent\",\n", + " model_name=\"effnetb2\",\n", + " extra=\"5_epochs\")\n", + " # The above is the same as:\n", + " writer = SummaryWriter(log_dir=\"runs/2022-06-04/data_10_percent/effnetb2/5_epochs/\")\n", + " \"\"\"\n", + " from datetime import datetime\n", + " import os\n", + "\n", + " # Get timestamp of current date (all experiments on certain day live in same folder)\n", + " timestamp = datetime.now().strftime(\"%Y-%m-%d\") # returns current date in YYYY-MM-DD format\n", + "\n", + " if extra:\n", + " # Create log directory path\n", + " log_dir = os.path.join(\"runs\", timestamp, experiment_name, model_name, extra)\n", + " else:\n", + " log_dir = os.path.join(\"runs\", timestamp, experiment_name, model_name)\n", + "\n", + " print(f\"[INFO] Created SummaryWriter, saving to: {log_dir}...\")\n", + " return SummaryWriter(log_dir=log_dir)" + ], + "metadata": { + "id": "BE60IEEkr89l" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Create a test writer\n", + "writer = create_writer(experiment_name=\"test_experiment_name\",\n", + " model_name=\"this_is_the_model_name\",\n", + " extra=\"add_a_little_extra_if_you_want\")" + ], + "metadata": { + "id": "S0BH4ONGsgNB" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "eFZFsnDoR7K0" + }, + "source": [ + "from typing import Dict, List\n", + "from tqdm.auto import tqdm\n", + "\n", + "from going_modular.going_modular.engine import train_step, test_step\n", + "\n", + "# Add writer parameter to train()\n", + "def train(model: torch.nn.Module,\n", + " train_dataloader: torch.utils.data.DataLoader,\n", + " test_dataloader: torch.utils.data.DataLoader,\n", + " optimizer: torch.optim.Optimizer,\n", + " loss_fn: torch.nn.Module,\n", + " epochs: int,\n", + " device: torch.device,\n", + " writer: torch.utils.tensorboard.writer.SummaryWriter # new parameter to take in a writer\n", + " ) -> Dict[str, List]:\n", + " \"\"\"Trains and tests a PyTorch model.\n", + "\n", + " Passes a target PyTorch models through train_step() and test_step()\n", + " functions for a number of epochs, training and testing the model\n", + " in the same epoch loop.\n", + "\n", + " Calculates, prints and stores evaluation metrics throughout.\n", + "\n", + " Stores metrics to specified writer log_dir if present.\n", + "\n", + " Args:\n", + " model: A PyTorch model to be trained and tested.\n", + " train_dataloader: A DataLoader instance for the model to be trained on.\n", + " test_dataloader: A DataLoader instance for the model to be tested on.\n", + " optimizer: A PyTorch optimizer to help minimize the loss function.\n", + " loss_fn: A PyTorch loss function to calculate loss on both datasets.\n", + " epochs: An integer indicating how many epochs to train for.\n", + " device: A target device to compute on (e.g. \"cuda\" or \"cpu\").\n", + " writer: A SummaryWriter() instance to log model results to.\n", + "\n", + " Returns:\n", + " A dictionary of training and testing loss as well as training and\n", + " testing accuracy metrics. Each metric has a value in a list for\n", + " each epoch.\n", + " In the form: {train_loss: [...],\n", + " train_acc: [...],\n", + " test_loss: [...],\n", + " test_acc: [...]}\n", + " For example if training for epochs=2:\n", + " {train_loss: [2.0616, 1.0537],\n", + " train_acc: [0.3945, 0.3945],\n", + " test_loss: [1.2641, 1.5706],\n", + " test_acc: [0.3400, 0.2973]}\n", + " \"\"\"\n", + " # Create empty results dictionary\n", + " results = {\"train_loss\": [],\n", + " \"train_acc\": [],\n", + " \"test_loss\": [],\n", + " \"test_acc\": []\n", + " }\n", + "\n", + " # Loop through training and testing steps for a number of epochs\n", + " for epoch in tqdm(range(epochs)):\n", + " train_loss, train_acc = train_step(model=model,\n", + " dataloader=train_dataloader,\n", + " loss_fn=loss_fn,\n", + " optimizer=optimizer,\n", + " device=device)\n", + " test_loss, test_acc = test_step(model=model,\n", + " dataloader=test_dataloader,\n", + " loss_fn=loss_fn,\n", + " device=device)\n", + "\n", + " # Print out what's happening\n", + " print(\n", + " f\"Epoch: {epoch+1} | \"\n", + " f\"train_loss: {train_loss:.4f} | \"\n", + " f\"train_acc: {train_acc:.4f} | \"\n", + " f\"test_loss: {test_loss:.4f} | \"\n", + " f\"test_acc: {test_acc:.4f}\"\n", + " )\n", + "\n", + " # Update results dictionary\n", + " results[\"train_loss\"].append(train_loss)\n", + " results[\"train_acc\"].append(train_acc)\n", + " results[\"test_loss\"].append(test_loss)\n", + " results[\"test_acc\"].append(test_acc)\n", + "\n", + "\n", + " ### New: Use the writer parameter to track experiments ###\n", + " # See if there's a writer, if so, log to it\n", + " if writer:\n", + " # Add results to SummaryWriter\n", + " writer.add_scalars(main_tag=\"Loss\",\n", + " tag_scalar_dict={\"train_loss\": train_loss,\n", + " \"test_loss\": test_loss},\n", + " global_step=epoch)\n", + " writer.add_scalars(main_tag=\"Accuracy\",\n", + " tag_scalar_dict={\"train_acc\": train_acc,\n", + " \"test_acc\": test_acc},\n", + " global_step=epoch)\n", + "\n", + " # Close the writer AFTER the epoch loop completes\n", + " if writer:\n", + " writer.close()\n", + " else:\n", + " pass\n", + " ### End new ###\n", + "\n", + " # Return the filled results at the end of the epochs\n", + " return results" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "from typing import Dict, List\n", + "from tqdm.auto import tqdm\n", + "\n", + "from going_modular.going_modular.engine import train_step, test_step\n", + "\n", + "# Add writer parameter to train()\n", + "def train(model: torch.nn.Module,\n", + " train_dataloader: torch.utils.data.DataLoader,\n", + " test_dataloader: torch.utils.data.DataLoader,\n", + " optimizer: torch.optim.Optimizer,\n", + " loss_fn: torch.nn.Module,\n", + " epochs: int,\n", + " device: torch.device,\n", + " writer: torch.utils.tensorboard.writer.SummaryWriter # new parameter to take in a writer\n", + " ) -> Dict[str, List]:\n", + " \"\"\"Trains and tests a PyTorch model.\n", + "\n", + " Passes a target PyTorch models through train_step() and test_step()\n", + " functions for a number of epochs, training and testing the model\n", + " in the same epoch loop.\n", + "\n", + " Calculates, prints and stores evaluation metrics throughout.\n", + "\n", + " Stores metrics to specified writer log_dir if present.\n", + "\n", + " Args:\n", + " model: A PyTorch model to be trained and tested.\n", + " train_dataloader: A DataLoader instance for the model to be trained on.\n", + " test_dataloader: A DataLoader instance for the model to be tested on.\n", + " optimizer: A PyTorch optimizer to help minimize the loss function.\n", + " loss_fn: A PyTorch loss function to calculate loss on both datasets.\n", + " epochs: An integer indicating how many epochs to train for.\n", + " device: A target device to compute on (e.g. \"cuda\" or \"cpu\").\n", + " writer: A SummaryWriter() instance to log model results to.\n", + "\n", + " Returns:\n", + " A dictionary of training and testing loss as well as training and\n", + " testing accuracy metrics. Each metric has a value in a list for\n", + " each epoch.\n", + " In the form: {train_loss: [...],\n", + " train_acc: [...],\n", + " test_loss: [...],\n", + " test_acc: [...]}\n", + " For example if training for epochs=2:\n", + " {train_loss: [2.0616, 1.0537],\n", + " train_acc: [0.3945, 0.3945],\n", + " test_loss: [1.2641, 1.5706],\n", + " test_acc: [0.3400, 0.2973]}\n", + " \"\"\"\n", + " # Create empty results dictionary\n", + " results = {\"train_loss\": [],\n", + " \"train_acc\": [],\n", + " \"test_loss\": [],\n", + " \"test_acc\": []\n", + " }\n", + "\n", + " # Loop through training and testing steps for a number of epochs\n", + " for epoch in tqdm(range(epochs)):\n", + " train_loss, train_acc = train_step(model=model,\n", + " dataloader=train_dataloader,\n", + " loss_fn=loss_fn,\n", + " optimizer=optimizer,\n", + " device=device)\n", + " test_loss, test_acc = test_step(model=model,\n", + " dataloader=test_dataloader,\n", + " loss_fn=loss_fn,\n", + " device=device)\n", + "\n", + " # Print out what's happening\n", + " print(\n", + " f\"Epoch: {epoch+1} | \"\n", + " f\"train_loss: {train_loss:.4f} | \"\n", + " f\"train_acc: {train_acc:.4f} | \"\n", + " f\"test_loss: {test_loss:.4f} | \"\n", + " f\"test_acc: {test_acc:.4f}\"\n", + " )\n", + "\n", + " # Update results dictionary\n", + " results[\"train_loss\"].append(train_loss)\n", + " results[\"train_acc\"].append(train_acc)\n", + " results[\"test_loss\"].append(test_loss)\n", + " results[\"test_acc\"].append(test_acc)\n", + "\n", + "\n", + " ### New: Use the writer parameter to track experiments ###\n", + " # See if there's a writer, if so, log to it\n", + " if writer:\n", + " # Add results to SummaryWriter\n", + " writer.add_scalars(main_tag=\"Loss\",\n", + " tag_scalar_dict={\"train_loss\": train_loss,\n", + " \"test_loss\": test_loss},\n", + " global_step=epoch)\n", + " writer.add_scalars(main_tag=\"Accuracy\",\n", + " tag_scalar_dict={\"train_acc\": train_acc,\n", + " \"test_acc\": test_acc},\n", + " global_step=epoch)\n", + "\n", + " # Close the writer\n", + " writer.close()\n", + " else:\n", + " pass\n", + " ### End new ###\n", + "\n", + " # Return the filled results at the end of the epochs\n", + " return results" + ], + "metadata": { + "id": "VwO0Q1eFsusV" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "### Download data\n", + "\n", + "Using the same data from https://www.learnpytorch.io/07_pytorch_experiment_tracking/" + ], + "metadata": { + "id": "nh8jKzHYHYL3" + } + }, + { + "cell_type": "code", + "source": [ + "# Download 10 percent and 20 percent training data (if necessary)\n", + "data_10_percent_path = download_data(source=\"https://github.com/mrdbourke/pytorch-deep-learning/raw/main/data/pizza_steak_sushi.zip\",\n", + " destination=\"pizza_steak_sushi\")\n", + "\n", + "data_20_percent_path = download_data(source=\"https://github.com/mrdbourke/pytorch-deep-learning/raw/main/data/pizza_steak_sushi_20_percent.zip\",\n", + " destination=\"pizza_steak_sushi_20_percent\")" + ], + "metadata": { + "id": "68QGCR_1tzif" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Setup training directory paths\n", + "train_dir_10_percent = data_10_percent_path / \"train\"\n", + "train_dir_20_percent = data_20_percent_path / \"train\"\n", + "\n", + "# Setup testing directory paths (note: use the same test dataset for both to compare the results)\n", + "test_dir = data_10_percent_path / \"test\"\n", + "\n", + "# Check the directories\n", + "print(f\"Training directory 10%: {train_dir_10_percent}\")\n", + "print(f\"Training directory 20%: {train_dir_20_percent}\")\n", + "print(f\"Testing directory: {test_dir}\")" + ], + "metadata": { + "id": "9L2rCRxvt1ED" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "from torchvision import transforms\n", + "\n", + "# Create a transform to normalize data distribution to be inline with ImageNet\n", + "normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], # values per colour channel [red, green, blue]\n", + " std=[0.229, 0.224, 0.225])\n", + "\n", + "# Create a transform pipeline\n", + "simple_transform = transforms.Compose([\n", + " transforms.Resize((224, 224)),\n", + " transforms.ToTensor(), # get image values between 0 & 1\n", + " normalize\n", + "])" + ], + "metadata": { + "id": "K35q9wswt6NH" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "### Turn data into DataLoaders" + ], + "metadata": { + "id": "SBuEla8pHea9" + } + }, + { + "cell_type": "code", + "source": [ + "BATCH_SIZE = 32\n", + "\n", + "# Create 10% training and test DataLoaders\n", + "train_dataloader_10_percent, test_dataloader, class_names = data_setup.create_dataloaders(train_dir=train_dir_10_percent,\n", + " test_dir=test_dir,\n", + " transform=simple_transform,\n", + " batch_size=BATCH_SIZE)\n", + "\n", + "# Create 20% training and test DataLoaders\n", + "train_dataloader_20_percent, test_dataloader, class_names = data_setup.create_dataloaders(train_dir=train_dir_20_percent,\n", + " test_dir=test_dir,\n", + " transform=simple_transform,\n", + " batch_size=BATCH_SIZE)\n", + "\n", + "# Find the number of samples/batches per dataloader (using the same test_dataloader for both experiments)\n", + "print(f\"Number of batches of size {BATCH_SIZE} in 10 percent training data: {len(train_dataloader_10_percent)}\")\n", + "print(f\"Number of batches of size {BATCH_SIZE} in 20 percent training data: {len(train_dataloader_20_percent)}\")\n", + "print(f\"Number of batches of size {BATCH_SIZE} in testing data: {len(train_dataloader_10_percent)} (all experiments will use the same test set)\")\n", + "print(f\"Number of classes: {len(class_names)}, class names: {class_names}\")" + ], + "metadata": { + "id": "xlQU94HBuqOq" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Exercise 1: Pick a larger model from [`torchvision.models`](https://pytorch.org/vision/main/models.html) to add to the list of experiments (for example, EffNetB3 or higher)\n", + "\n", + "* How does it perform compared to our existing models?\n", + "* **Hint:** You'll need to set up an exerpiment similar to [07. PyTorch Experiment Tracking section 7.6](https://www.learnpytorch.io/07_pytorch_experiment_tracking/#76-create-experiments-and-set-up-training-code)." + ], + "metadata": { + "id": "nwmoMhW8IqSu" + } + }, + { + "cell_type": "code", + "source": [ + "# Models\n", + "weights_1 = torchvision.models.EfficientNet_B2_Weights.DEFAULT\n", + "model_1 = torchvision.models.efficientnet_b2(weights = weights_1)\n", + "\n", + "weights_2 = torchvision.models.EfficientNet_B3_Weights.DEFAULT\n", + "model_2 = torchvision.models.efficientnet_b3(weights = weights_2)\n" + ], + "metadata": { + "id": "iPVmRkAtbpcH" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Let's figure out how many out features do we need\n", + "import torchinfo\n", + "\n", + "summary(model = model_2,\n", + " input_size = (32,3,224,224),\n", + " col_names = [\"input_size\",\"output_size\", \"num_params\",\"trainable\"],\n", + " col_width = 20,\n", + " row_settings = [\"var_names\"])" + ], + "metadata": { + "id": "EBl09kmKbZAO" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Create some feature extractor model\n", + "import torchvision\n", + "import torch\n", + "\n", + "from torch import nn\n", + "OUT_FEATURES = len(class_names)\n", + "\n", + "# Create a helper function to instantiate effnet pretrained models and change the classifier to suit our problem\n", + "def create_effnetb2():\n", + " # Choose the model and get the pretrained weights\n", + " weights = torchvision.models.EfficientNet_B2_Weights.DEFAULT\n", + " model = torchvision.models.efficientnet_b2(weights = weights).to(device)\n", + "\n", + " # Freeze the base parameters for the train\n", + " for param in model.features.parameters():\n", + " param.requires_grad = False\n", + "\n", + " # Set the seeds\n", + " set_seeds()\n", + "\n", + " # Change the classifier head\n", + " model.classifier = nn.Sequential(\n", + " nn.Dropout(p=0.2),\n", + " nn.Linear(in_features=1408, out_features=OUT_FEATURES)\n", + " ).to(device)\n", + "\n", + " # Name the model\n", + " model.name = \"effnetb2\"\n", + " print(f\"Created {model.name} new model\")\n", + " return model\n", + "\n", + "def create_effnetb3():\n", + " # Choose the model\n", + " weights = torchvision.models.EfficientNet_B3_Weights.DEFAULT\n", + " model = torchvision.models.efficientnet_b3(weights = weights).to(device)\n", + "\n", + " # Freeze the base layers\n", + " for param in model.features.parameters():\n", + " param.requires_grad = False\n", + "\n", + " # Set the seed\n", + " set_seeds()\n", + "\n", + " # Change the model's head classifier\n", + " model.classifier = nn.Sequential(\n", + " nn.Dropout(p=0.2),\n", + " nn.Linear(in_features=1536, out_features=OUT_FEATURES)\n", + " ).to(device)\n", + "\n", + " # Change the model's name\n", + " model.name = \"effnetb3\"\n", + " print(f\"Created {model.name} new model\")\n", + " return model" + ], + "metadata": { + "id": "F-35y0uxJ8tg" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Let's instantiate our feature extractor models\n", + "effnetb2 = create_effnetb2().to(device)\n", + "effnetb3 = create_effnetb3().to(device)\n", + "# Let's use torchinfo\n", + "summary(model = effnetb3,\n", + " input_size = (32,3,224,224),\n", + " col_names = [\"input_size\",\"output_size\", \"num_params\",\"trainable\"],\n", + " col_width = 20,\n", + " row_settings = [\"var_names\"])" + ], + "metadata": { + "id": "1CHf4Zp9LQ--" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Let's create some list to iterate our experiment and save the ouputs on log_dir with SummaryWriter()¨\n", + "num_epochs = [5,10] # number of epochs\n", + "models = [\"effnetb2\",\"effnetb3\"] # model names\n", + "train_dataloaders = {\"data_10_percent\" : train_dataloader_10_percent, # a dictionary of our train_dataloaders\n", + " \"data_20_percent\": train_dataloader_20_percent}" + ], + "metadata": { + "id": "rxx5a6Y9NAym" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [], + "metadata": { + "id": "Dgg4aNiEQ-Ig" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "from going_modular.going_modular.utils import save_model\n", + "\n", + "# Set the random seeds\n", + "set_seeds(seed=42)\n", + "# Set experiment tracking for an asthetic printing\n", + "experiment_number = 0\n", + "# Loop through the dataloaders\n", + "for dataloader_name, train_dataloader in train_dataloaders.items(): #.items() returns both key and value\n", + " # Loop through the epochs\n", + " for epoch in num_epochs:\n", + " # Loop through the model\n", + " for model_name in models:\n", + " # Print out info for the experiments\n", + " experiment_number+=1\n", + " print(f\"[INFO] Number of experiment : {experiment_number}\")\n", + " print(f\"[INFO] Dataloader : {dataloader_name}\")\n", + " print(f\"[INFO] Number of epochs : {epoch}\")\n", + " print(f\"[INFO] Model's name : {model_name}\")\n", + "\n", + " # Check for the right model and create an instance of it\n", + " if model_name == \"effnetb2\":\n", + " model = create_effnetb2() # creates a new model each time (important because we want each experiment to start from scratch)\n", + " else:\n", + " model = create_effnetb3()\n", + "\n", + " # Create the loss function and the optimizer\n", + " loss_fn = nn.CrossEntropyLoss()\n", + " optimizer = torch.optim.Adam(params = model.parameters(), lr= 0.001)\n", + "\n", + " # Train the model\n", + " train(model = model,\n", + " train_dataloader = train_dataloader,\n", + " test_dataloader = test_dataloader,\n", + " optimizer = optimizer,\n", + " loss_fn = loss_fn,\n", + " epochs = epoch,\n", + " device = device,\n", + " writer = create_writer(experiment_name = dataloader_name,\n", + " model_name = model_name,\n", + " extra = f\"{epoch} epochs\"))\n", + " # Save the model\n", + " save_filepath = f\"07.{model_name}_{dataloader_name}_{epoch}_epochs.pth\"\n", + " save_model(model = model,\n", + " target_dir = \"models\",\n", + " model_name = save_filepath)\n", + " print(\"-\"*50 + \"\\n\")" + ], + "metadata": { + "id": "RvD1Uc1rOh8J" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Viewing TensorBoard in Jupyter and Google Colab Notebooks (uncomment to view full TensorBoard instance)\n", + "%load_ext tensorboard\n", + "%tensorboard --logdir runs" + ], + "metadata": { + "id": "l9G1Yez4rgyU" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Exercise 2. Introduce data augmentation to the list of experiments using the 20% pizza, steak, sushi training and test datasets, does this change anything?\n", + " \n", + "* For example, you could have one training DataLoader that uses data augmentation (e.g. `train_dataloader_20_percent_aug` and `train_dataloader_20_percent_no_aug`) and then compare the results of two of the same model types training on these two DataLoaders.\n", + "* **Note:** You may need to alter the `create_dataloaders()` function to be able to take a transform for the training data and the testing data (because you don't need to perform data augmentation on the test data). See [04. PyTorch Custom Datasets section 6](https://www.learnpytorch.io/04_pytorch_custom_datasets/#6-other-forms-of-transforms-data-augmentation) for examples of using data augmentation or the script below for an example:\n", + "\n", + "```python\n", + "# Note: Data augmentation transform like this should only be performed on training data\n", + "train_transform_data_aug = transforms.Compose([\n", + " transforms.Resize((224, 224)),\n", + " transforms.TrivialAugmentWide(),\n", + " transforms.ToTensor(),\n", + " normalize\n", + "])\n", + "\n", + "# Create a helper function to visualize different augmented (and not augmented) images\n", + "def view_dataloader_images(dataloader, n=10):\n", + " if n > 10:\n", + " print(f\"Having n higher than 10 will create messy plots, lowering to 10.\")\n", + " n = 10\n", + " imgs, labels = next(iter(dataloader))\n", + " plt.figure(figsize=(16, 8))\n", + " for i in range(n):\n", + " # Min max scale the image for display purposes\n", + " targ_image = imgs[i]\n", + " sample_min, sample_max = targ_image.min(), targ_image.max()\n", + " sample_scaled = (targ_image - sample_min)/(sample_max - sample_min)\n", + "\n", + " # Plot images with appropriate axes information\n", + " plt.subplot(1, 10, i+1)\n", + " plt.imshow(sample_scaled.permute(1, 2, 0)) # resize for Matplotlib requirements\n", + " plt.title(class_names[labels[i]])\n", + " plt.axis(False)\n", + "\n", + "# Have to update `create_dataloaders()` to handle different augmentations\n", + "import os\n", + "from torch.utils.data import DataLoader\n", + "from torchvision import datasets\n", + "\n", + "NUM_WORKERS = os.cpu_count() # use maximum number of CPUs for workers to load data\n", + "\n", + "# Note: this is an update version of data_setup.create_dataloaders to handle\n", + "# differnt train and test transforms.\n", + "def create_dataloaders(\n", + " train_dir,\n", + " test_dir,\n", + " train_transform, # add parameter for train transform (transforms on train dataset)\n", + " test_transform, # add parameter for test transform (transforms on test dataset)\n", + " batch_size=32, num_workers=NUM_WORKERS\n", + "):\n", + " # Use ImageFolder to create dataset(s)\n", + " train_data = datasets.ImageFolder(train_dir, transform=train_transform)\n", + " test_data = datasets.ImageFolder(test_dir, transform=test_transform)\n", + "\n", + " # Get class names\n", + " class_names = train_data.classes\n", + "\n", + " # Turn images into data loaders\n", + " train_dataloader = DataLoader(\n", + " train_data,\n", + " batch_size=batch_size,\n", + " shuffle=True,\n", + " num_workers=num_workers,\n", + " pin_memory=True,\n", + " )\n", + " test_dataloader = DataLoader(\n", + " test_data,\n", + " batch_size=batch_size,\n", + " shuffle=True,\n", + " num_workers=num_workers,\n", + " pin_memory=True,\n", + " )\n", + "\n", + " return train_dataloader, test_dataloader, class_names\n", + "```" + ], + "metadata": { + "id": "m_AG37-duz6O" + } + }, + { + "cell_type": "code", + "source": [ + "# Note: Data augmentation transform like this should only be performed on training data\n", + "train_transform_data_aug = transforms.Compose([\n", + " transforms.Resize((224, 224)),\n", + " transforms.TrivialAugmentWide(),\n", + " transforms.ToTensor(),\n", + " normalize\n", + "])\n", + "\n", + "# for the test_data we already have simple transform withouth any type of data augmentation" + ], + "metadata": { + "id": "E1N3yyDOoH2t" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Have to update `create_dataloaders()` to handle different augmentations\n", + "import os\n", + "from torch.utils.data import DataLoader\n", + "from torchvision import datasets\n", + "\n", + "NUM_WORKERS = os.cpu_count() # use maximum number of CPUs for workers to load data\n", + "\n", + "# Note: this is an update version of data_setup.create_dataloaders to handle\n", + "# differnt train and test transforms.\n", + "def create_dataloaders(\n", + " train_dir,\n", + " test_dir,\n", + " train_transform, # add parameter for train transform (transforms on train dataset)\n", + " test_transform, # add parameter for test transform (transforms on test dataset)\n", + " batch_size=32, num_workers=NUM_WORKERS\n", + "):\n", + " # Use ImageFolder to create dataset(s)\n", + " train_data = datasets.ImageFolder(train_dir, transform=train_transform)\n", + " test_data = datasets.ImageFolder(test_dir, transform=test_transform)\n", + "\n", + " # Get class names\n", + " class_names = train_data.classes\n", + "\n", + " # Turn images into data loaders\n", + " train_dataloader = DataLoader(\n", + " train_data,\n", + " batch_size=batch_size,\n", + " shuffle=True,\n", + " num_workers=num_workers,\n", + " pin_memory=True,\n", + " )\n", + " test_dataloader = DataLoader(\n", + " test_data,\n", + " batch_size=batch_size,\n", + " shuffle=True,\n", + " num_workers=num_workers,\n", + " pin_memory=True,\n", + " )\n", + "\n", + " return train_dataloader, test_dataloader, class_names" + ], + "metadata": { + "id": "L3oVw5qJwJcO" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Let's create the datalaoders with and withouth data augmentation\n", + "BATCH_SIZE = 32\n", + "\n", + "# Create 10% training and test DataLoaders\n", + "train_dataloader_20_percent_aug, test_dataloader, class_names = create_dataloaders(train_dir=train_dir_20_percent,\n", + " test_dir=test_dir,\n", + " train_transform= train_transform_data_aug,\n", + " test_transform = simple_transform,\n", + " batch_size=BATCH_SIZE)\n", + "\n", + "# Create 20% training and test DataLoaders\n", + "train_dataloader_20_percent_no_aug, test_dataloader, class_names = create_dataloaders(train_dir=train_dir_20_percent,\n", + " test_dir=test_dir,\n", + " train_transform = simple_transform,\n", + " test_transform =simple_transform,\n", + " batch_size=BATCH_SIZE)\n", + "\n", + "# Find the number of samples/batches per dataloader (using the same test_dataloader for both experiments)\n", + "print(f\"Number of batches of size {BATCH_SIZE} in 20 percent training data: {len(train_dataloader_20_percent_aug)}\")\n", + "print(f\"Number of batches of size {BATCH_SIZE} in 20 percent training data: {len(train_dataloader_20_percent_no_aug)}\")\n", + "print(f\"Number of batches of size {BATCH_SIZE} in testing data: {len(test_dataloader)} (all experiments will use the same test set)\")\n", + "print(f\"Number of classes: {len(class_names)}, class names: {class_names}\")" + ], + "metadata": { + "id": "UzWKObl6vwYV" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Create some feature extractor model\n", + "import torchvision\n", + "import torch\n", + "\n", + "from torch import nn\n", + "OUT_FEATURES = len(class_names)\n", + "\n", + "# Create a helper function to instantiate effnet pretrained models and change the classifier to suit our problem\n", + "def create_effnetb2():\n", + " # Choose the model and get the pretrained weights\n", + " weights = torchvision.models.EfficientNet_B2_Weights.DEFAULT\n", + " model = torchvision.models.efficientnet_b2(weights = weights).to(device)\n", + "\n", + " # Freeze the base parameters for the train\n", + " for param in model.features.parameters():\n", + " param.requires_grad = False\n", + "\n", + " # Set the seeds\n", + " set_seeds()\n", + "\n", + " # Change the classifier head\n", + " model.classifier = nn.Sequential(\n", + " nn.Dropout(p=0.2),\n", + " nn.Linear(in_features=1408, out_features=OUT_FEATURES)\n", + " ).to(device)\n", + "\n", + " # Name the model\n", + " model.name = \"effnetb2\"\n", + " print(f\"Created {model.name} new model\")\n", + " return model\n", + "\n", + "def create_effnetb3():\n", + " # Choose the model\n", + " weights = torchvision.models.EfficientNet_B3_Weights.DEFAULT\n", + " model = torchvision.models.efficientnet_b3(weights = weights).to(device)\n", + "\n", + " # Freeze the base layers\n", + " for param in model.features.parameters():\n", + " param.requires_grad = False\n", + "\n", + " # Set the seed\n", + " set_seeds()\n", + "\n", + " # Change the model's head classifier\n", + " model.classifier = nn.Sequential(\n", + " nn.Dropout(p=0.2),\n", + " nn.Linear(in_features=1536, out_features=OUT_FEATURES)\n", + " ).to(device)\n", + "\n", + " # Change the model's name\n", + " model.name = \"effnetb3\"\n", + " print(f\"Created {model.name} new model\")\n", + " return model" + ], + "metadata": { + "id": "4A9KVjxS0-ws" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Let's instantiate a feature extractor model\n", + "effnetb2 = create_effnetb2()" + ], + "metadata": { + "id": "lqO0WP2_yxZ0" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Let's create some lists\n", + "num_epochs = [5,10]\n", + "models = [\"effnetb2\"]\n", + "train_dataloaders = {\"data_20_percent_aug\" : train_dataloader_20_percent_aug,\n", + " \"data_20_percent_no_aug\" : train_dataloader_20_percent_no_aug}" + ], + "metadata": { + "id": "Ek7QxS8PzPb1" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "# Let's train our models and see if the data transformation improves its performance with the 20% of data\n", + "from going_modular.going_modular.utils import save_model\n", + "\n", + "# Set the random seeds\n", + "set_seeds(seed=42)\n", + "# Set experiment tracking for an asthetic printing\n", + "experiment_number = 0\n", + "# Loop through the dataloaders\n", + "for dataloader_name, train_dataloader in train_dataloaders.items(): #.items() returns both key and value\n", + " # Loop through the epochs\n", + " for epoch in num_epochs:\n", + " # Loop through the model\n", + " for model_name in models:\n", + " # Print out info for the experiments\n", + " experiment_number+=1\n", + " print(f\"[INFO] Number of experiment : {experiment_number}\")\n", + " print(f\"[INFO] Dataloader : {dataloader_name}\")\n", + " print(f\"[INFO] Number of epochs : {epoch}\")\n", + " print(f\"[INFO] Model's name : {model_name}\")\n", + "\n", + " # Check for the right model and create an instance of it\n", + " if model_name == \"effnetb2\":\n", + " model = create_effnetb2() # creates a new model each time (important because we want each experiment to start from scratch)\n", + " else:\n", + " model = create_effnetb3()\n", + "\n", + " # Create the loss function and the optimizer\n", + " loss_fn = nn.CrossEntropyLoss()\n", + " optimizer = torch.optim.Adam(params = model.parameters(), lr= 0.001)\n", + "\n", + " # Train the model\n", + " train(model = model,\n", + " train_dataloader = train_dataloader,\n", + " test_dataloader = test_dataloader,\n", + " optimizer = optimizer,\n", + " loss_fn = loss_fn,\n", + " epochs = epoch,\n", + " device = device,\n", + " writer = create_writer(experiment_name = dataloader_name,\n", + " model_name = model_name,\n", + " extra = f\"{epoch} epochs\"))\n", + " # Save the model\n", + " save_filepath = f\"07.{model_name}_{dataloader_name}_{epoch}_epochs.pth\"\n", + " save_model(model = model,\n", + " target_dir = \"models\",\n", + " model_name = save_filepath)\n", + " print(\"-\"*50 + \"\\n\")" + ], + "metadata": { + "id": "I99zYOVoz5Sy" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Viewing TensorBoard in Jupyter and Google Colab Notebooks (uncomment to view full TensorBoard instance)\n", + "%load_ext tensorboard\n", + "%tensorboard --logdir runs" + ], + "metadata": { + "id": "AhoKvHqU1BJG" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Exercise 3. Scale up the dataset to turn FoodVision Mini into FoodVision Big using the entire [Food101 dataset from `torchvision.models`](https://pytorch.org/vision/stable/generated/torchvision.datasets.Food101.html#torchvision.datasets.Food101)\n", + " \n", + "* You could take the best performing model from your various experiments or even the EffNetB2 feature extractor we created in this notebook and see how it goes fitting for 5 epochs on all of Food101.\n", + "* If you try more than one model, it would be good to have the model's results tracked.\n", + "* If you load the Food101 dataset from `torchvision.models`, you'll have to create PyTorch DataLoaders to use it in training.\n", + "* **Note:** Due to the larger amount of data in Food101 compared to our pizza, steak, sushi dataset, this model will take longer to train." + ], + "metadata": { + "id": "1IvuTskxgjaw" + } + }, + { + "cell_type": "code", + "source": [ + "# Load the data\n", + "from torchvision.datasets import Food101\n", + "from torchvision import transforms\n", + "# Define transformations\n", + "food_transform = transforms.Compose([\n", + " transforms.Resize((256, 256)),\n", + " transforms.ToTensor(),\n", + " transforms.Normalize(mean=[0.485, 0.456, 0.406],\n", + " std=[0.229, 0.224, 0.225])\n", + "])\n", + "# Load the dataset\n", + "train_data = Food101(root='data/food101', split='train', transform=food_transform, download=True)\n", + "test_data = Food101(root='data/food101', split='test', transform=food_transform, download=True)" + ], + "metadata": { + "id": "YehliYnYoP1x" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Use the going.modular functions to create the dataloaders\n", + "from torch.utils.data import DataLoader\n", + "\n", + "train_dataloader_food101 = DataLoader(train_data,\n", + " batch_size = 32,\n", + " shuffle = True,\n", + " num_workers = os.cpu_count())\n", + "test_dataloader_food101 = DataLoader(test_data,\n", + " batch_size = 32,\n", + " num_workers = os.cpu_count(),\n", + " shuffle = False)\n", + "\n", + "train_dataloader_food101, test_dataloader_food101\n", + "print(f\"Number of 32 batches for the train_datalaoder :{len(train_dataloader_food101)}\")\n", + "print(f\"Number of 32 batches for the test_datalaoder :{len(test_dataloader_food101)}\")" + ], + "metadata": { + "id": "QqqnEdDOWqxS" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Create some feature extractor model\n", + "import torchvision\n", + "import torch\n", + "\n", + "from torch import nn\n", + "OUT_FEATURES = 101\n", + "\n", + "# Create a helper function to instantiate effnet pretrained models and change the classifier to suit our problem\n", + "def create_effnetb2():\n", + " # Choose the model and get the pretrained weights\n", + " weights = torchvision.models.EfficientNet_B2_Weights.DEFAULT\n", + " model = torchvision.models.efficientnet_b2(weights = weights).to(device)\n", + "\n", + " # Freeze the base parameters for the train\n", + " for param in model.features.parameters():\n", + " param.requires_grad = False\n", + "\n", + " # Set the seeds\n", + " set_seeds()\n", + "\n", + " # Change the classifier head\n", + " model.classifier = nn.Sequential(\n", + " nn.Dropout(p=0.2),\n", + " nn.Linear(in_features=1408, out_features=OUT_FEATURES)\n", + " ).to(device)\n", + "\n", + " # Name the model\n", + " model.name = \"effnetb2\"\n", + " print(f\"Created {model.name} new model\")\n", + " return model\n", + "\n", + "def create_effnetb3():\n", + " # Choose the model\n", + " weights = torchvision.models.EfficientNet_B3_Weights.DEFAULT\n", + " model = torchvision.models.efficientnet_b3(weights = weights).to(device)\n", + "\n", + " # Freeze the base layers\n", + " for param in model.features.parameters():\n", + " param.requires_grad = False\n", + "\n", + " # Set the seed\n", + " set_seeds()\n", + "\n", + " # Change the model's head classifier\n", + " model.classifier = nn.Sequential(\n", + " nn.Dropout(p=0.2),\n", + " nn.Linear(in_features=1536, out_features=OUT_FEATURES)\n", + " ).to(device)\n", + "\n", + " # Change the model's name\n", + " model.name = \"effnetb3\"\n", + " print(f\"Created {model.name} new model\")\n", + " return model" + ], + "metadata": { + "id": "BhUvZCYje-rf" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Let's put together some lists\n", + "num_epochs = [3]\n", + "models = [\"effnetb2\", \"effnetb3\"]\n", + "train_dataloaders = {\"food_101\" : train_dataloader_food101}" + ], + "metadata": { + "id": "UWRa54tCfMvk" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "from going_modular.going_modular.utils import save_model\n", + "# Set the seeds\n", + "set_seeds(seed=42)\n", + "# Keep tracking the experiments\n", + "experiment_number = 0\n", + "# Loop through the datalaoders\n", + "for dataloader_name, train_dataloader in train_dataloaders.items():\n", + " for epochs in num_epochs:\n", + " for model_name in models:\n", + " # Print out how the training is going\n", + " experiment_number+=1\n", + " print(f\"[INFO] Number of experiment : {experiment_number}\")\n", + " print(f\"[INFO] Dataloader : {dataloader_name}\")\n", + " print(f\"[INFO] Model: {model_name}\")\n", + " print(f\"[INFO] Number of epochs:{epochs}\")\n", + "\n", + " # Choose for the right model\n", + " if model_name == \"effnetb2\":\n", + " model = create_effnetb2()\n", + " else:\n", + " model = create_effnetb3()\n", + "\n", + " # Create loss function and the optimizer\n", + " loss_fn = nn.CrossEntropyLoss()\n", + " optimizer = torch.optim.Adam(params = model.parameters(), lr =0.001)\n", + "\n", + " # Train the model with the engine function from going_modular\n", + " train(model=model,\n", + " train_dataloader=train_dataloader_food101,\n", + " test_dataloader = test_dataloader_food101,\n", + " optimizer = optimizer,\n", + " loss_fn = loss_fn,\n", + " epochs = epochs,\n", + " device = device,\n", + " writer = create_writer(experiment_name = dataloader_name,\n", + " model_name = model_name,\n", + " extra = f\"{epochs}_epochs\"))\n", + " # Save the model\n", + " save_filepath=f\"{model_name}_{dataloader_name}_{epochs}_epochs.pth\"\n", + " save_model(model = model,\n", + " target_dir = \"models\",\n", + " model_name = save_filepath)\n", + " print(\"-\"*50 + \"\\n\")\n" + ], + "metadata": { + "id": "IAHeTPf-fqrX" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Viewing TensorBoard in Jupyter and Google Colab Notebooks (uncomment to view full TensorBoard instance)\n", + "%load_ext tensorboard\n", + "%tensorboard --logdir runs" + ], + "metadata": { + "id": "JDROROB1hv1s" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [], + "metadata": { + "id": "958oibktIfvy" + }, + "execution_count": null, + "outputs": [] + } + ] +} \ No newline at end of file diff --git a/extras/exercises/07_pytorch_experiment_tracking_exercise_template.ipynb b/extras/exercises/07_pytorch_experiment_tracking_exercise_template.ipynb index bfb06187..03864d12 100644 --- a/extras/exercises/07_pytorch_experiment_tracking_exercise_template.ipynb +++ b/extras/exercises/07_pytorch_experiment_tracking_exercise_template.ipynb @@ -5,9 +5,7 @@ "colab": { "name": "07_pytorch_experiment_tracking_exercise_template.ipynb", "provenance": [], - "collapsed_sections": [], - "toc_visible": true, - "authorship_tag": "ABX9TyP4+Nwb43yrG43qNz11d5C4", + "gpuType": "T4", "include_colab_link": true }, "kernelspec": { @@ -18,7 +16,2746 @@ "name": "python" }, "accelerator": "GPU", - "gpuClass": "standard" + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "4695609ad07a442b96a09577e32c576d": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HBoxModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_e1a40b9ec98140558aa607029a0e3920", + "IPY_MODEL_0eec67604997435f9104400d419c08f2", + "IPY_MODEL_3ec79740118d4db5bf793787f2f1646f" + ], + "layout": "IPY_MODEL_ec839ee357584d6d9c953c3f71e55ff2" + } + }, + "e1a40b9ec98140558aa607029a0e3920": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_533961da467944e7909a07f7952b9999", + "placeholder": "​", + "style": "IPY_MODEL_1ad2abc210dd4ffbb0e3229f7f19ee16", + "value": "100%" + } + }, + "0eec67604997435f9104400d419c08f2": { + "model_module": "@jupyter-widgets/controls", + "model_name": "FloatProgressModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_a1e3e6242b454eaeb740c13b4da3e254", + "max": 5, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_e346dfda2eac423cb4ae77f65ad31a08", + "value": 5 + } + }, + "3ec79740118d4db5bf793787f2f1646f": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_9bad8b08a2d8454a9b6aad7594fc22e3", + "placeholder": "​", + "style": "IPY_MODEL_df62e0eaaa1e4e5e9d91d19c74c42a60", + "value": " 5/5 [00:09<00:00,  1.88s/it]" + } + }, + "ec839ee357584d6d9c953c3f71e55ff2": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "533961da467944e7909a07f7952b9999": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "1ad2abc210dd4ffbb0e3229f7f19ee16": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "a1e3e6242b454eaeb740c13b4da3e254": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "e346dfda2eac423cb4ae77f65ad31a08": { + "model_module": "@jupyter-widgets/controls", + "model_name": "ProgressStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "9bad8b08a2d8454a9b6aad7594fc22e3": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "df62e0eaaa1e4e5e9d91d19c74c42a60": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "eb560c9f9de240ccb7b8fd2701625bee": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HBoxModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_e4dd59e10a36408288cce249e2420d7d", + "IPY_MODEL_1bb254f3173046a49616ec08689d5176", + "IPY_MODEL_fe8aeb5708c44e36940b1f723c56363d" + ], + "layout": "IPY_MODEL_637756c4effd459584fece99686ac524" + } + }, + "e4dd59e10a36408288cce249e2420d7d": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_42e7efc2ca914950810c1839f4443879", + "placeholder": "​", + "style": "IPY_MODEL_a71f8ce6462d49da849e24b4886838a3", + "value": "100%" + } + }, + "1bb254f3173046a49616ec08689d5176": { + "model_module": "@jupyter-widgets/controls", + "model_name": "FloatProgressModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_d8e9f6b6d276408d82cdf1b2d2083a2a", + "max": 5, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_5cfd2d17bf7d444eb469fdbd06b8c9b0", + "value": 5 + } + }, + "fe8aeb5708c44e36940b1f723c56363d": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_d77b96fc404a4c1e9a9839f7db93fb3b", + "placeholder": "​", + "style": "IPY_MODEL_6cc3c059a8bb48b9b557463c27cf0872", + "value": " 5/5 [00:10<00:00,  2.06s/it]" + } + }, + "637756c4effd459584fece99686ac524": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "42e7efc2ca914950810c1839f4443879": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "a71f8ce6462d49da849e24b4886838a3": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "d8e9f6b6d276408d82cdf1b2d2083a2a": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "5cfd2d17bf7d444eb469fdbd06b8c9b0": { + "model_module": "@jupyter-widgets/controls", + "model_name": "ProgressStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "d77b96fc404a4c1e9a9839f7db93fb3b": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "6cc3c059a8bb48b9b557463c27cf0872": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "7008b323d16b4690b02819e022f1ed20": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HBoxModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_36159d2b66f04f8781c4458280bc10ee", + "IPY_MODEL_6a86f141a5754a10bba39565166073f8", + "IPY_MODEL_305c2a970cdf4a9eaafa20034d205e3c" + ], + "layout": "IPY_MODEL_ac4ad336491e4ceb89d565c98862419a" + } + }, + "36159d2b66f04f8781c4458280bc10ee": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_34e0c8b2549b4966bf222f1e288b5ee5", + "placeholder": "​", + "style": "IPY_MODEL_a0402a185c884e9f87fec59f706269d2", + "value": "100%" + } + }, + "6a86f141a5754a10bba39565166073f8": { + "model_module": "@jupyter-widgets/controls", + "model_name": "FloatProgressModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_0d3e4120af494134837c8523aff70a90", + "max": 10, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_5295a86e03a44bf8ab88203545c69068", + "value": 10 + } + }, + "305c2a970cdf4a9eaafa20034d205e3c": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_c3b07a6ebdb142dbb82273d5160fb0a3", + "placeholder": "​", + "style": "IPY_MODEL_433b4b71634b4ac38f12cd6f0ec8c8c5", + "value": " 10/10 [00:18<00:00,  1.81s/it]" + } + }, + "ac4ad336491e4ceb89d565c98862419a": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "34e0c8b2549b4966bf222f1e288b5ee5": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "a0402a185c884e9f87fec59f706269d2": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "0d3e4120af494134837c8523aff70a90": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "5295a86e03a44bf8ab88203545c69068": { + "model_module": "@jupyter-widgets/controls", + "model_name": "ProgressStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "c3b07a6ebdb142dbb82273d5160fb0a3": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "433b4b71634b4ac38f12cd6f0ec8c8c5": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "e01bd22ca11a45c692851ce68496a450": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HBoxModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_55aa8e0bd1c445dea251482dbdf5de0b", + "IPY_MODEL_32bd9183080f4f0ab9e37c7b69e58d07", + "IPY_MODEL_0712ca20c200409187a29b1499f4cb1b" + ], + "layout": "IPY_MODEL_7b5b02a335c641f09d1e5d0119ee732f" + } + }, + "55aa8e0bd1c445dea251482dbdf5de0b": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_112e7a336d114977b7845a10f7814abf", + "placeholder": "​", + "style": "IPY_MODEL_d6529689e2af453496967c966139101f", + "value": "100%" + } + }, + "32bd9183080f4f0ab9e37c7b69e58d07": { + "model_module": "@jupyter-widgets/controls", + "model_name": "FloatProgressModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_f904c858da4649a687970cefae9e191a", + "max": 10, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_7aa0a083f5214105b7f911821c7c4890", + "value": 10 + } + }, + "0712ca20c200409187a29b1499f4cb1b": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_2214a242b2e84d65a3b96d77a6438a59", + "placeholder": "​", + "style": "IPY_MODEL_f36600c7a7cd4f3c974d472d2ca93039", + "value": " 10/10 [00:19<00:00,  2.02s/it]" + } + }, + "7b5b02a335c641f09d1e5d0119ee732f": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "112e7a336d114977b7845a10f7814abf": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "d6529689e2af453496967c966139101f": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "f904c858da4649a687970cefae9e191a": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "7aa0a083f5214105b7f911821c7c4890": { + "model_module": "@jupyter-widgets/controls", + "model_name": "ProgressStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "2214a242b2e84d65a3b96d77a6438a59": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "f36600c7a7cd4f3c974d472d2ca93039": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "0a13ad9692ad4d29b5e33a6ed6a6aeb8": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HBoxModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_a82ea609965445f487b741df5577f2e0", + "IPY_MODEL_d06a916787f048bc8021b450b94713b2", + "IPY_MODEL_b3e255d026994a11b1815baf627b5476" + ], + "layout": "IPY_MODEL_672a7882ab6c463fa1f8ec930d1344f0" + } + }, + "a82ea609965445f487b741df5577f2e0": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_1a48e0c29363483aaedac262dc7e9d6f", + "placeholder": "​", + "style": "IPY_MODEL_8ed45f20a68b424486e4abd9be970c3a", + "value": "100%" + } + }, + "d06a916787f048bc8021b450b94713b2": { + "model_module": "@jupyter-widgets/controls", + "model_name": "FloatProgressModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_f41d11bb53aa4278b4f43d4f681eeb0b", + "max": 5, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_6866057cfd0a44a9803044d15bc3e452", + "value": 5 + } + }, + "b3e255d026994a11b1815baf627b5476": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_dfb3b2c3d09647e0b735aa59d5e3eb61", + "placeholder": "​", + "style": "IPY_MODEL_e989ccc5acb84feaade094cbd0ede1ec", + "value": " 5/5 [00:14<00:00,  2.87s/it]" + } + }, + "672a7882ab6c463fa1f8ec930d1344f0": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "1a48e0c29363483aaedac262dc7e9d6f": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "8ed45f20a68b424486e4abd9be970c3a": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "f41d11bb53aa4278b4f43d4f681eeb0b": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "6866057cfd0a44a9803044d15bc3e452": { + "model_module": "@jupyter-widgets/controls", + "model_name": "ProgressStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "dfb3b2c3d09647e0b735aa59d5e3eb61": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "e989ccc5acb84feaade094cbd0ede1ec": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "a00ac6d276d543cc89803a63b2b84195": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HBoxModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_67280e5aa4e94d28aaac00400cf45daa", + "IPY_MODEL_2b4819ea01d24207a2111b7d61562a2d", + "IPY_MODEL_adbc017d4327408d92f6846f9d70f10d" + ], + "layout": "IPY_MODEL_2b32a2e0281a419c89fb55c642bb9769" + } + }, + "67280e5aa4e94d28aaac00400cf45daa": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_bb35864a5e204e35a3526a9212745cee", + "placeholder": "​", + "style": "IPY_MODEL_75fa7f5d0659455ea3fb180dcf354a77", + "value": "100%" + } + }, + "2b4819ea01d24207a2111b7d61562a2d": { + "model_module": "@jupyter-widgets/controls", + "model_name": "FloatProgressModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_dee470215a32432780296392ea4501c4", + "max": 5, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_e353be5c48d04d36972fe4b26b699482", + "value": 5 + } + }, + "adbc017d4327408d92f6846f9d70f10d": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_344c9c10cc6a4228a95ac1ae9799d2ea", + "placeholder": "​", + "style": "IPY_MODEL_7b92aa0f701345c8835a1b4389d5ea06", + "value": " 5/5 [00:14<00:00,  2.96s/it]" + } + }, + "2b32a2e0281a419c89fb55c642bb9769": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "bb35864a5e204e35a3526a9212745cee": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "75fa7f5d0659455ea3fb180dcf354a77": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "dee470215a32432780296392ea4501c4": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "e353be5c48d04d36972fe4b26b699482": { + "model_module": "@jupyter-widgets/controls", + "model_name": "ProgressStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "344c9c10cc6a4228a95ac1ae9799d2ea": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "7b92aa0f701345c8835a1b4389d5ea06": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "8d23b172d32d45fc8b33e4b980fb3bd0": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HBoxModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_5f8b7b2fcada4663b3a6d1e427924455", + "IPY_MODEL_9b60e284166b479bac1bac360e79036a", + "IPY_MODEL_1f7da29d17d64e35823d69aa99e97c00" + ], + "layout": "IPY_MODEL_323d7e91a99d426cb5629a8afe4da36a" + } + }, + "5f8b7b2fcada4663b3a6d1e427924455": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_a08733bf7bdf4c1aa700cd7b85711dac", + "placeholder": "​", + "style": "IPY_MODEL_324294520c824ad28205bb0541cb5b02", + "value": "100%" + } + }, + "9b60e284166b479bac1bac360e79036a": { + "model_module": "@jupyter-widgets/controls", + "model_name": "FloatProgressModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_adc9acaddb0146f6acb96273dbddc74f", + "max": 10, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_ba585aa142574916a34609a411bcbfc5", + "value": 10 + } + }, + "1f7da29d17d64e35823d69aa99e97c00": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_e3ef4d2d0199420aa6af10c27a1ba536", + "placeholder": "​", + "style": "IPY_MODEL_f579df9d3b714c9384308d1b409b5de5", + "value": " 10/10 [00:28<00:00,  2.90s/it]" + } + }, + "323d7e91a99d426cb5629a8afe4da36a": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "a08733bf7bdf4c1aa700cd7b85711dac": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "324294520c824ad28205bb0541cb5b02": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "adc9acaddb0146f6acb96273dbddc74f": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "ba585aa142574916a34609a411bcbfc5": { + "model_module": "@jupyter-widgets/controls", + "model_name": "ProgressStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "e3ef4d2d0199420aa6af10c27a1ba536": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "f579df9d3b714c9384308d1b409b5de5": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "394b9195bc124d1bbdda0521fa4c43d7": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HBoxModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_ebb16abfe0d64e358b9aeccc63b941c0", + "IPY_MODEL_27a6ed57c064474194130ee3820ed7fd", + "IPY_MODEL_ffabd25c178d41529ca8d4fda5f8fb29" + ], + "layout": "IPY_MODEL_a4180b21afc845eab9cc35e9ea028ace" + } + }, + "ebb16abfe0d64e358b9aeccc63b941c0": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_3dac7f84fb3e41f0916fd9ace98aaaf0", + "placeholder": "​", + "style": "IPY_MODEL_b7e5553d34794ccba05714cb17a77b81", + "value": "100%" + } + }, + "27a6ed57c064474194130ee3820ed7fd": { + "model_module": "@jupyter-widgets/controls", + "model_name": "FloatProgressModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_1dbbe2df39e946cfa87bf38d216c8bbc", + "max": 10, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_e14babfd7d92449ea998ac3f164a3eaa", + "value": 10 + } + }, + "ffabd25c178d41529ca8d4fda5f8fb29": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_1b4dca71eb5f4651bc95a5173cdb633c", + "placeholder": "​", + "style": "IPY_MODEL_2c93a2ef16024cef862380b7ad59c218", + "value": " 10/10 [00:30<00:00,  2.99s/it]" + } + }, + "a4180b21afc845eab9cc35e9ea028ace": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "3dac7f84fb3e41f0916fd9ace98aaaf0": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "b7e5553d34794ccba05714cb17a77b81": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "1dbbe2df39e946cfa87bf38d216c8bbc": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "e14babfd7d92449ea998ac3f164a3eaa": { + "model_module": "@jupyter-widgets/controls", + "model_name": "ProgressStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "1b4dca71eb5f4651bc95a5173cdb633c": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "2c93a2ef16024cef862380b7ad59c218": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + } + } + } }, "cells": [ { @@ -28,7 +2765,7 @@ "colab_type": "text" }, "source": [ - "\"Open" + "\"Open" ] }, { @@ -66,40 +2803,15 @@ { "cell_type": "code", "source": [ - "# For this notebook to run with updated APIs, we need torch 1.12+ and torchvision 0.13+\n", - "try:\n", - " import torch\n", - " import torchvision\n", - " assert int(torch.__version__.split(\".\")[1]) >= 12, \"torch version should be 1.12+\"\n", - " assert int(torchvision.__version__.split(\".\")[1]) >= 13, \"torchvision version should be 0.13+\"\n", - " print(f\"torch version: {torch.__version__}\")\n", - " print(f\"torchvision version: {torchvision.__version__}\")\n", - "except:\n", - " print(f\"[INFO] torch/torchvision versions not as required, installing nightly versions.\")\n", - " !pip3 install -U --pre torch torchvision --extra-index-url https://download.pytorch.org/whl/nightly/cu113\n", - " import torch\n", - " import torchvision\n", - " print(f\"torch version: {torch.__version__}\")\n", - " print(f\"torchvision version: {torchvision.__version__}\")" + "# Import torch and torchvision\n", + "import torch\n", + "import torchvision" ], "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "5MOv1De4mxeL", - "outputId": "a4f9fdd9-f225-4861-e204-e26186365bea" + "id": "dxehhNGjUDpF" }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "torch version: 1.13.0.dev20220622+cu113\n", - "torchvision version: 0.14.0.dev20220622+cu113\n" - ] - } - ] + "execution_count": 4, + "outputs": [] }, { "cell_type": "code", @@ -111,12 +2823,12 @@ "metadata": { "colab": { "base_uri": "https://localhost:8080/", - "height": 35 + "height": 36 }, "id": "Nf-DsrZipCE9", - "outputId": "f4cc7d1c-da78-4eb4-c753-4e135771650c" + "outputId": "a3c88610-7c79-4091-a33a-e114f2f40d7d" }, - "execution_count": null, + "execution_count": 5, "outputs": [ { "output_type": "execute_result", @@ -129,14 +2841,14 @@ } }, "metadata": {}, - "execution_count": 2 + "execution_count": 5 } ] }, { "cell_type": "code", "source": [ - "# Get regular imports \n", + "# Get regular imports\n", "import matplotlib.pyplot as plt\n", "import torch\n", "import torchvision\n", @@ -164,10 +2876,29 @@ " from going_modular.going_modular import data_setup, engine" ], "metadata": { - "id": "i_52puIeoab3" + "id": "i_52puIeoab3", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "5fb0976b-7c3b-4148-984a-629ace0c4fe1" }, - "execution_count": null, - "outputs": [] + "execution_count": 6, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "[INFO] Couldn't find torchinfo... installing it.\n", + "[INFO] Couldn't find going_modular scripts... downloading them from GitHub.\n", + "Cloning into 'pytorch-deep-learning'...\n", + "remote: Enumerating objects: 4393, done.\u001b[K\n", + "remote: Total 4393 (delta 0), reused 0 (delta 0), pack-reused 4393 (from 1)\u001b[K\n", + "Receiving objects: 100% (4393/4393), 764.14 MiB | 17.15 MiB/s, done.\n", + "Resolving deltas: 100% (2657/2657), done.\n", + "Updating files: 100% (248/248), done.\n" + ] + } + ] }, { "cell_type": "code", @@ -187,7 +2918,7 @@ "metadata": { "id": "DBj8I3P9pNK2" }, - "execution_count": null, + "execution_count": 7, "outputs": [] }, { @@ -200,7 +2931,7 @@ "\n", "import requests\n", "\n", - "def download_data(source: str, \n", + "def download_data(source: str,\n", " destination: str,\n", " remove_source: bool = True) -> Path:\n", " \"\"\"Downloads a zipped dataset from source and unzips to destination.\n", @@ -209,10 +2940,10 @@ " source (str): A link to a zipped file containing data.\n", " destination (str): A target directory to unzip data to.\n", " remove_source (bool): Whether to remove the source after downloading and extracting.\n", - " \n", + "\n", " Returns:\n", " pathlib.Path to downloaded data.\n", - " \n", + "\n", " Example usage:\n", " download_data(source=\"https://github.com/mrdbourke/pytorch-deep-learning/raw/main/data/pizza_steak_sushi.zip\",\n", " destination=\"pizza_steak_sushi\")\n", @@ -221,13 +2952,13 @@ " data_path = Path(\"data/\")\n", " image_path = data_path / destination\n", "\n", - " # If the image folder doesn't exist, download it and prepare it... \n", + " # If the image folder doesn't exist, download it and prepare it...\n", " if image_path.is_dir():\n", " print(f\"[INFO] {image_path} directory exists, skipping download.\")\n", " else:\n", " print(f\"[INFO] Did not find {image_path} directory, creating one...\")\n", " image_path.mkdir(parents=True, exist_ok=True)\n", - " \n", + "\n", " # Download pizza, steak, sushi data\n", " target_file = Path(source).name\n", " with open(data_path / target_file, \"wb\") as f:\n", @@ -237,13 +2968,13 @@ "\n", " # Unzip pizza, steak, sushi data\n", " with zipfile.ZipFile(data_path / target_file, \"r\") as zip_ref:\n", - " print(f\"[INFO] Unzipping {target_file} data...\") \n", + " print(f\"[INFO] Unzipping {target_file} data...\")\n", " zip_ref.extractall(image_path)\n", "\n", " # Remove .zip file\n", " if remove_source:\n", " os.remove(data_path / target_file)\n", - " \n", + "\n", " return image_path\n", "\n", "image_path = download_data(source=\"https://github.com/mrdbourke/pytorch-deep-learning/raw/main/data/pizza_steak_sushi.zip\",\n", @@ -255,15 +2986,17 @@ "base_uri": "https://localhost:8080/" }, "id": "m6R-CS53pTLS", - "outputId": "3f7688b7-0b86-4cd8-bb11-4e71f8e7270f" + "outputId": "4158233e-8e00-44fc-9e8f-a2c4b0a25804" }, - "execution_count": null, + "execution_count": 8, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ - "[INFO] data/pizza_steak_sushi directory exists, skipping download.\n" + "[INFO] Did not find data/pizza_steak_sushi directory, creating one...\n", + "[INFO] Downloading pizza_steak_sushi.zip from https://github.com/mrdbourke/pytorch-deep-learning/raw/main/data/pizza_steak_sushi.zip...\n", + "[INFO] Unzipping pizza_steak_sushi.zip data...\n" ] }, { @@ -274,7 +3007,7 @@ ] }, "metadata": {}, - "execution_count": 6 + "execution_count": 8 } ] }, @@ -282,8 +3015,8 @@ "cell_type": "code", "source": [ "from torch.utils.tensorboard import SummaryWriter\n", - "def create_writer(experiment_name: str, \n", - " model_name: str, \n", + "def create_writer(experiment_name: str,\n", + " model_name: str,\n", " extra: str=None):\n", " \"\"\"Creates a torch.utils.tensorboard.writer.SummaryWriter() instance saving to a specific log_dir.\n", "\n", @@ -318,14 +3051,14 @@ " log_dir = os.path.join(\"runs\", timestamp, experiment_name, model_name, extra)\n", " else:\n", " log_dir = os.path.join(\"runs\", timestamp, experiment_name, model_name)\n", - " \n", + "\n", " print(f\"[INFO] Created SummaryWriter, saving to: {log_dir}...\")\n", " return SummaryWriter(log_dir=log_dir)" ], "metadata": { "id": "BE60IEEkr89l" }, - "execution_count": null, + "execution_count": 9, "outputs": [] }, { @@ -341,15 +3074,15 @@ "base_uri": "https://localhost:8080/" }, "id": "S0BH4ONGsgNB", - "outputId": "077f7b39-f4d0-44dd-b74f-8ead04fb7add" + "outputId": "f092222c-7a05-4a4c-e2c5-fa12ce23af66" }, - "execution_count": null, + "execution_count": 10, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ - "[INFO] Created SummaryWriter, saving to: runs/2022-06-23/test_experiment_name/this_is_the_model_name/add_a_little_extra_if_you_want...\n" + "[INFO] Created SummaryWriter, saving to: runs/2025-11-17/test_experiment_name/this_is_the_model_name/add_a_little_extra_if_you_want...\n" ] } ] @@ -363,13 +3096,13 @@ "from going_modular.going_modular.engine import train_step, test_step\n", "\n", "# Add writer parameter to train()\n", - "def train(model: torch.nn.Module, \n", - " train_dataloader: torch.utils.data.DataLoader, \n", - " test_dataloader: torch.utils.data.DataLoader, \n", + "def train(model: torch.nn.Module,\n", + " train_dataloader: torch.utils.data.DataLoader,\n", + " test_dataloader: torch.utils.data.DataLoader,\n", " optimizer: torch.optim.Optimizer,\n", " loss_fn: torch.nn.Module,\n", " epochs: int,\n", - " device: torch.device, \n", + " device: torch.device,\n", " writer: torch.utils.tensorboard.writer.SummaryWriter # new parameter to take in a writer\n", " ) -> Dict[str, List]:\n", " \"\"\"Trains and tests a PyTorch model.\n", @@ -394,17 +3127,17 @@ "\n", " Returns:\n", " A dictionary of training and testing loss as well as training and\n", - " testing accuracy metrics. Each metric has a value in a list for \n", + " testing accuracy metrics. Each metric has a value in a list for\n", " each epoch.\n", " In the form: {train_loss: [...],\n", " train_acc: [...],\n", " test_loss: [...],\n", - " test_acc: [...]} \n", - " For example if training for epochs=2: \n", + " test_acc: [...]}\n", + " For example if training for epochs=2:\n", " {train_loss: [2.0616, 1.0537],\n", " train_acc: [0.3945, 0.3945],\n", " test_loss: [1.2641, 1.5706],\n", - " test_acc: [0.3400, 0.2973]} \n", + " test_acc: [0.3400, 0.2973]}\n", " \"\"\"\n", " # Create empty results dictionary\n", " results = {\"train_loss\": [],\n", @@ -445,13 +3178,13 @@ " # See if there's a writer, if so, log to it\n", " if writer:\n", " # Add results to SummaryWriter\n", - " writer.add_scalars(main_tag=\"Loss\", \n", + " writer.add_scalars(main_tag=\"Loss\",\n", " tag_scalar_dict={\"train_loss\": train_loss,\n", " \"test_loss\": test_loss},\n", " global_step=epoch)\n", - " writer.add_scalars(main_tag=\"Accuracy\", \n", + " writer.add_scalars(main_tag=\"Accuracy\",\n", " tag_scalar_dict={\"train_acc\": train_acc,\n", - " \"test_acc\": test_acc}, \n", + " \"test_acc\": test_acc},\n", " global_step=epoch)\n", "\n", " # Close the writer\n", @@ -466,7 +3199,7 @@ "metadata": { "id": "VwO0Q1eFsusV" }, - "execution_count": null, + "execution_count": 11, "outputs": [] }, { @@ -495,16 +3228,18 @@ "base_uri": "https://localhost:8080/" }, "id": "68QGCR_1tzif", - "outputId": "a5073b19-1463-4d8a-ec08-5399945196a4" + "outputId": "48e839a9-b530-40d8-8c77-1673a5ab5c8a" }, - "execution_count": null, + "execution_count": 12, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "[INFO] data/pizza_steak_sushi directory exists, skipping download.\n", - "[INFO] data/pizza_steak_sushi_20_percent directory exists, skipping download.\n" + "[INFO] Did not find data/pizza_steak_sushi_20_percent directory, creating one...\n", + "[INFO] Downloading pizza_steak_sushi_20_percent.zip from https://github.com/mrdbourke/pytorch-deep-learning/raw/main/data/pizza_steak_sushi_20_percent.zip...\n", + "[INFO] Unzipping pizza_steak_sushi_20_percent.zip data...\n" ] } ] @@ -529,9 +3264,9 @@ "base_uri": "https://localhost:8080/" }, "id": "9L2rCRxvt1ED", - "outputId": "30b8202e-96f3-443f-e89e-2a83e11b1c85" + "outputId": "eb1e081e-c684-4b74-88db-b53e079a8979" }, - "execution_count": null, + "execution_count": 13, "outputs": [ { "output_type": "stream", @@ -563,13 +3298,13 @@ "metadata": { "id": "K35q9wswt6NH" }, - "execution_count": null, + "execution_count": 14, "outputs": [] }, { "cell_type": "markdown", "source": [ - "### Turn data into DataLoaders " + "### Turn data into DataLoaders" ], "metadata": { "id": "SBuEla8pHea9" @@ -603,9 +3338,9 @@ "base_uri": "https://localhost:8080/" }, "id": "xlQU94HBuqOq", - "outputId": "db4b9144-fd3b-4f31-e0c1-ebd60f906232" + "outputId": "4181d84b-d066-4aa0-f13a-296688053264" }, - "execution_count": null, + "execution_count": 15, "outputs": [ { "output_type": "stream", @@ -634,14 +3369,1094 @@ { "cell_type": "code", "source": [ - "# TODO: your code" + "# Models\n", + "weights_1 = torchvision.models.EfficientNet_B2_Weights.DEFAULT\n", + "model_1 = torchvision.models.efficientnet_b2(weights = weights_1)\n", + "\n", + "weights_2 = torchvision.models.EfficientNet_B3_Weights.DEFAULT\n", + "model_2 = torchvision.models.efficientnet_b3(weights = weights_2)\n" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "iPVmRkAtbpcH", + "outputId": "30490721-e0d4-462b-eea9-e065e54dc0a2" + }, + "execution_count": 16, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Downloading: \"https://download.pytorch.org/models/efficientnet_b2_rwightman-c35c1473.pth\" to /root/.cache/torch/hub/checkpoints/efficientnet_b2_rwightman-c35c1473.pth\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "100%|██████████| 35.2M/35.2M [00:00<00:00, 86.6MB/s]\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Downloading: \"https://download.pytorch.org/models/efficientnet_b3_rwightman-b3899882.pth\" to /root/.cache/torch/hub/checkpoints/efficientnet_b3_rwightman-b3899882.pth\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "100%|██████████| 47.2M/47.2M [00:00<00:00, 99.9MB/s]\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "# Let's figure out how many out features do we need\n", + "import torchinfo\n", + "\n", + "summary(model = model_2,\n", + " input_size = (32,3,224,224),\n", + " col_names = [\"input_size\",\"output_size\", \"num_params\",\"trainable\"],\n", + " col_width = 20,\n", + " row_settings = [\"var_names\"])" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "EBl09kmKbZAO", + "outputId": "82823d6f-7a84-4930-ecbf-f788a7f8ed9a" + }, + "execution_count": 17, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "============================================================================================================================================\n", + "Layer (type (var_name)) Input Shape Output Shape Param # Trainable\n", + "============================================================================================================================================\n", + "EfficientNet (EfficientNet) [32, 3, 224, 224] [32, 1000] -- True\n", + "├─Sequential (features) [32, 3, 224, 224] [32, 1536, 7, 7] -- True\n", + "│ └─Conv2dNormActivation (0) [32, 3, 224, 224] [32, 40, 112, 112] -- True\n", + "│ │ └─Conv2d (0) [32, 3, 224, 224] [32, 40, 112, 112] 1,080 True\n", + "│ │ └─BatchNorm2d (1) [32, 40, 112, 112] [32, 40, 112, 112] 80 True\n", + "│ │ └─SiLU (2) [32, 40, 112, 112] [32, 40, 112, 112] -- --\n", + "│ └─Sequential (1) [32, 40, 112, 112] [32, 24, 112, 112] -- True\n", + "│ │ └─MBConv (0) [32, 40, 112, 112] [32, 24, 112, 112] 2,298 True\n", + "│ │ └─MBConv (1) [32, 24, 112, 112] [32, 24, 112, 112] 1,206 True\n", + "│ └─Sequential (2) [32, 24, 112, 112] [32, 32, 56, 56] -- True\n", + "│ │ └─MBConv (0) [32, 24, 112, 112] [32, 32, 56, 56] 11,878 True\n", + "│ │ └─MBConv (1) [32, 32, 56, 56] [32, 32, 56, 56] 18,120 True\n", + "│ │ └─MBConv (2) [32, 32, 56, 56] [32, 32, 56, 56] 18,120 True\n", + "│ └─Sequential (3) [32, 32, 56, 56] [32, 48, 28, 28] -- True\n", + "│ │ └─MBConv (0) [32, 32, 56, 56] [32, 48, 28, 28] 24,296 True\n", + "│ │ └─MBConv (1) [32, 48, 28, 28] [32, 48, 28, 28] 43,308 True\n", + "│ │ └─MBConv (2) [32, 48, 28, 28] [32, 48, 28, 28] 43,308 True\n", + "│ └─Sequential (4) [32, 48, 28, 28] [32, 96, 14, 14] -- True\n", + "│ │ └─MBConv (0) [32, 48, 28, 28] [32, 96, 14, 14] 52,620 True\n", + "│ │ └─MBConv (1) [32, 96, 14, 14] [32, 96, 14, 14] 146,520 True\n", + "│ │ └─MBConv (2) [32, 96, 14, 14] [32, 96, 14, 14] 146,520 True\n", + "│ │ └─MBConv (3) [32, 96, 14, 14] [32, 96, 14, 14] 146,520 True\n", + "│ │ └─MBConv (4) [32, 96, 14, 14] [32, 96, 14, 14] 146,520 True\n", + "│ └─Sequential (5) [32, 96, 14, 14] [32, 136, 14, 14] -- True\n", + "│ │ └─MBConv (0) [32, 96, 14, 14] [32, 136, 14, 14] 178,856 True\n", + "│ │ └─MBConv (1) [32, 136, 14, 14] [32, 136, 14, 14] 302,226 True\n", + "│ │ └─MBConv (2) [32, 136, 14, 14] [32, 136, 14, 14] 302,226 True\n", + "│ │ └─MBConv (3) [32, 136, 14, 14] [32, 136, 14, 14] 302,226 True\n", + "│ │ └─MBConv (4) [32, 136, 14, 14] [32, 136, 14, 14] 302,226 True\n", + "│ └─Sequential (6) [32, 136, 14, 14] [32, 232, 7, 7] -- True\n", + "│ │ └─MBConv (0) [32, 136, 14, 14] [32, 232, 7, 7] 380,754 True\n", + "│ │ └─MBConv (1) [32, 232, 7, 7] [32, 232, 7, 7] 849,642 True\n", + "│ │ └─MBConv (2) [32, 232, 7, 7] [32, 232, 7, 7] 849,642 True\n", + "│ │ └─MBConv (3) [32, 232, 7, 7] [32, 232, 7, 7] 849,642 True\n", + "│ │ └─MBConv (4) [32, 232, 7, 7] [32, 232, 7, 7] 849,642 True\n", + "│ │ └─MBConv (5) [32, 232, 7, 7] [32, 232, 7, 7] 849,642 True\n", + "│ └─Sequential (7) [32, 232, 7, 7] [32, 384, 7, 7] -- True\n", + "│ │ └─MBConv (0) [32, 232, 7, 7] [32, 384, 7, 7] 1,039,258 True\n", + "│ │ └─MBConv (1) [32, 384, 7, 7] [32, 384, 7, 7] 2,244,960 True\n", + "│ └─Conv2dNormActivation (8) [32, 384, 7, 7] [32, 1536, 7, 7] -- True\n", + "│ │ └─Conv2d (0) [32, 384, 7, 7] [32, 1536, 7, 7] 589,824 True\n", + "│ │ └─BatchNorm2d (1) [32, 1536, 7, 7] [32, 1536, 7, 7] 3,072 True\n", + "│ │ └─SiLU (2) [32, 1536, 7, 7] [32, 1536, 7, 7] -- --\n", + "├─AdaptiveAvgPool2d (avgpool) [32, 1536, 7, 7] [32, 1536, 1, 1] -- --\n", + "├─Sequential (classifier) [32, 1536] [32, 1000] -- True\n", + "│ └─Dropout (0) [32, 1536] [32, 1536] -- --\n", + "│ └─Linear (1) [32, 1536] [32, 1000] 1,537,000 True\n", + "============================================================================================================================================\n", + "Total params: 12,233,232\n", + "Trainable params: 12,233,232\n", + "Non-trainable params: 0\n", + "Total mult-adds (Units.GIGABYTES): 30.81\n", + "============================================================================================================================================\n", + "Input size (MB): 19.27\n", + "Forward/backward pass size (MB): 6654.32\n", + "Params size (MB): 48.93\n", + "Estimated Total Size (MB): 6722.52\n", + "============================================================================================================================================" + ] + }, + "metadata": {}, + "execution_count": 17 + } + ] + }, + { + "cell_type": "code", + "source": [ + "# Create some feature extractor model\n", + "import torchvision\n", + "import torch\n", + "\n", + "from torch import nn\n", + "OUT_FEATURES = len(class_names)\n", + "\n", + "# Create a helper function to instantiate effnet pretrained models and change the classifier to suit our problem\n", + "def create_effnetb2():\n", + " # Choose the model and get the pretrained weights\n", + " weights = torchvision.models.EfficientNet_B2_Weights.DEFAULT\n", + " model = torchvision.models.efficientnet_b2(weights = weights).to(device)\n", + "\n", + " # Freeze the base parameters for the train\n", + " for param in model.features.parameters():\n", + " param.requires_grad = False\n", + "\n", + " # Set the seeds\n", + " set_seeds()\n", + "\n", + " # Change the classifier head\n", + " model.classifier = nn.Sequential(\n", + " nn.Dropout(p=0.2),\n", + " nn.Linear(in_features=1408, out_features=OUT_FEATURES)\n", + " ).to(device)\n", + "\n", + " # Name the model\n", + " model.name = \"effnetb2\"\n", + " print(f\"Created {model.name} new model\")\n", + " return model\n", + "\n", + "def create_effnetb3():\n", + " # Choose the model\n", + " weights = torchvision.models.EfficientNet_B3_Weights.DEFAULT\n", + " model = torchvision.models.efficientnet_b3(weights = weights).to(device)\n", + "\n", + " # Freeze the base layers\n", + " for param in model.features.parameters():\n", + " param.requires_grad = False\n", + "\n", + " # Set the seed\n", + " set_seeds()\n", + "\n", + " # Change the model's head classifier\n", + " model.classifier = nn.Sequential(\n", + " nn.Dropout(p=0.2),\n", + " nn.Linear(in_features=1536, out_features=OUT_FEATURES)\n", + " ).to(device)\n", + "\n", + " # Change the model's name\n", + " model.name = \"effnetb3\"\n", + " print(f\"Created {model.name} new model\")\n", + " return model" ], "metadata": { "id": "F-35y0uxJ8tg" }, - "execution_count": null, + "execution_count": 27, "outputs": [] }, + { + "cell_type": "code", + "source": [ + "# Let's instantiate our feature extractor models\n", + "effnetb2 = create_effnetb2().to(device)\n", + "effnetb3 = create_effnetb3().to(device)\n", + "# Let's use torchinfo\n", + "summary(model = effnetb3,\n", + " input_size = (32,3,224,224),\n", + " col_names = [\"input_size\",\"output_size\", \"num_params\",\"trainable\"],\n", + " col_width = 20,\n", + " row_settings = [\"var_names\"])" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "1CHf4Zp9LQ--", + "outputId": "aee2ca19-7cd6-4689-8664-cd112cfae0e2" + }, + "execution_count": 28, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Created effnetb2 new model\n", + "Created effnetb3 new model\n" + ] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "============================================================================================================================================\n", + "Layer (type (var_name)) Input Shape Output Shape Param # Trainable\n", + "============================================================================================================================================\n", + "EfficientNet (EfficientNet) [32, 3, 224, 224] [32, 3] -- Partial\n", + "├─Sequential (features) [32, 3, 224, 224] [32, 1536, 7, 7] -- False\n", + "│ └─Conv2dNormActivation (0) [32, 3, 224, 224] [32, 40, 112, 112] -- False\n", + "│ │ └─Conv2d (0) [32, 3, 224, 224] [32, 40, 112, 112] (1,080) False\n", + "│ │ └─BatchNorm2d (1) [32, 40, 112, 112] [32, 40, 112, 112] (80) False\n", + "│ │ └─SiLU (2) [32, 40, 112, 112] [32, 40, 112, 112] -- --\n", + "│ └─Sequential (1) [32, 40, 112, 112] [32, 24, 112, 112] -- False\n", + "│ │ └─MBConv (0) [32, 40, 112, 112] [32, 24, 112, 112] (2,298) False\n", + "│ │ └─MBConv (1) [32, 24, 112, 112] [32, 24, 112, 112] (1,206) False\n", + "│ └─Sequential (2) [32, 24, 112, 112] [32, 32, 56, 56] -- False\n", + "│ │ └─MBConv (0) [32, 24, 112, 112] [32, 32, 56, 56] (11,878) False\n", + "│ │ └─MBConv (1) [32, 32, 56, 56] [32, 32, 56, 56] (18,120) False\n", + "│ │ └─MBConv (2) [32, 32, 56, 56] [32, 32, 56, 56] (18,120) False\n", + "│ └─Sequential (3) [32, 32, 56, 56] [32, 48, 28, 28] -- False\n", + "│ │ └─MBConv (0) [32, 32, 56, 56] [32, 48, 28, 28] (24,296) False\n", + "│ │ └─MBConv (1) [32, 48, 28, 28] [32, 48, 28, 28] (43,308) False\n", + "│ │ └─MBConv (2) [32, 48, 28, 28] [32, 48, 28, 28] (43,308) False\n", + "│ └─Sequential (4) [32, 48, 28, 28] [32, 96, 14, 14] -- False\n", + "│ │ └─MBConv (0) [32, 48, 28, 28] [32, 96, 14, 14] (52,620) False\n", + "│ │ └─MBConv (1) [32, 96, 14, 14] [32, 96, 14, 14] (146,520) False\n", + "│ │ └─MBConv (2) [32, 96, 14, 14] [32, 96, 14, 14] (146,520) False\n", + "│ │ └─MBConv (3) [32, 96, 14, 14] [32, 96, 14, 14] (146,520) False\n", + "│ │ └─MBConv (4) [32, 96, 14, 14] [32, 96, 14, 14] (146,520) False\n", + "│ └─Sequential (5) [32, 96, 14, 14] [32, 136, 14, 14] -- False\n", + "│ │ └─MBConv (0) [32, 96, 14, 14] [32, 136, 14, 14] (178,856) False\n", + "│ │ └─MBConv (1) [32, 136, 14, 14] [32, 136, 14, 14] (302,226) False\n", + "│ │ └─MBConv (2) [32, 136, 14, 14] [32, 136, 14, 14] (302,226) False\n", + "│ │ └─MBConv (3) [32, 136, 14, 14] [32, 136, 14, 14] (302,226) False\n", + "│ │ └─MBConv (4) [32, 136, 14, 14] [32, 136, 14, 14] (302,226) False\n", + "│ └─Sequential (6) [32, 136, 14, 14] [32, 232, 7, 7] -- False\n", + "│ │ └─MBConv (0) [32, 136, 14, 14] [32, 232, 7, 7] (380,754) False\n", + "│ │ └─MBConv (1) [32, 232, 7, 7] [32, 232, 7, 7] (849,642) False\n", + "│ │ └─MBConv (2) [32, 232, 7, 7] [32, 232, 7, 7] (849,642) False\n", + "│ │ └─MBConv (3) [32, 232, 7, 7] [32, 232, 7, 7] (849,642) False\n", + "│ │ └─MBConv (4) [32, 232, 7, 7] [32, 232, 7, 7] (849,642) False\n", + "│ │ └─MBConv (5) [32, 232, 7, 7] [32, 232, 7, 7] (849,642) False\n", + "│ └─Sequential (7) [32, 232, 7, 7] [32, 384, 7, 7] -- False\n", + "│ │ └─MBConv (0) [32, 232, 7, 7] [32, 384, 7, 7] (1,039,258) False\n", + "│ │ └─MBConv (1) [32, 384, 7, 7] [32, 384, 7, 7] (2,244,960) False\n", + "│ └─Conv2dNormActivation (8) [32, 384, 7, 7] [32, 1536, 7, 7] -- False\n", + "│ │ └─Conv2d (0) [32, 384, 7, 7] [32, 1536, 7, 7] (589,824) False\n", + "│ │ └─BatchNorm2d (1) [32, 1536, 7, 7] [32, 1536, 7, 7] (3,072) False\n", + "│ │ └─SiLU (2) [32, 1536, 7, 7] [32, 1536, 7, 7] -- --\n", + "├─AdaptiveAvgPool2d (avgpool) [32, 1536, 7, 7] [32, 1536, 1, 1] -- --\n", + "├─Sequential (classifier) [32, 1536] [32, 3] -- True\n", + "│ └─Dropout (0) [32, 1536] [32, 1536] -- --\n", + "│ └─Linear (1) [32, 1536] [32, 3] 4,611 True\n", + "============================================================================================================================================\n", + "Total params: 10,700,843\n", + "Trainable params: 4,611\n", + "Non-trainable params: 10,696,232\n", + "Total mult-adds (Units.GIGABYTES): 30.76\n", + "============================================================================================================================================\n", + "Input size (MB): 19.27\n", + "Forward/backward pass size (MB): 6654.06\n", + "Params size (MB): 42.80\n", + "Estimated Total Size (MB): 6716.13\n", + "============================================================================================================================================" + ] + }, + "metadata": {}, + "execution_count": 28 + } + ] + }, + { + "cell_type": "code", + "source": [ + "# Let's create some list to iterate our experiment and save the ouputs on log_dir with SummaryWriter()¨\n", + "num_epochs = [5,10] # number of epochs\n", + "models = [\"effnetb2\",\"effnetb3\"] # model names\n", + "train_dataloaders = {\"data_10_percent\" : train_dataloader_10_percent, # a dictionary of our train_dataloaders\n", + " \"data_20_percent\": train_dataloader_20_percent}" + ], + "metadata": { + "id": "rxx5a6Y9NAym" + }, + "execution_count": 29, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "from going_modular.going_modular.utils import save_model\n", + "\n", + "# Set the random seeds\n", + "set_seeds(seed=42)\n", + "# Set experiment tracking for an asthetic printing\n", + "experiment_number = 0\n", + "# Loop through the dataloaders\n", + "for dataloader_name, train_dataloader in train_dataloaders.items(): #.items() returns both key and value\n", + " # Loop through the epochs\n", + " for epoch in num_epochs:\n", + " # Loop through the model\n", + " for model_name in models:\n", + " # Print out info for the experiments\n", + " experiment_number+=1\n", + " print(f\"[INFO] Number of experiment : {experiment_number}\")\n", + " print(f\"[INFO] Dataloader : {dataloader_name}\")\n", + " print(f\"[INFO] Number of epochs : {epoch}\")\n", + " print(f\"[INFO] Model's name : {model_name}\")\n", + "\n", + " # Check for the right model and create an instance of it\n", + " if model_name == \"effnetb2\":\n", + " model = create_effnetb2() # creates a new model each time (important because we want each experiment to start from scratch)\n", + " else:\n", + " model = create_effnetb3()\n", + "\n", + " # Create the loss function and the optimizer\n", + " loss_fn = nn.CrossEntropyLoss()\n", + " optimizer = torch.optim.Adam(params = model.parameters(), lr= 0.001)\n", + "\n", + " # Train the model\n", + " train(model = model,\n", + " train_dataloader = train_dataloader,\n", + " test_dataloader = test_dataloader,\n", + " optimizer = optimizer,\n", + " loss_fn = loss_fn,\n", + " epochs = epoch,\n", + " device = device,\n", + " writer = create_writer(experiment_name = dataloader_name,\n", + " model_name = model_name,\n", + " extra = f\"{epoch} epochs\"))\n", + " # Save the model\n", + " save_filepath = f\"07.{model_name}_{dataloader_name}_{epoch}_epochs.pth\"\n", + " save_model(model = model,\n", + " target_dir = \"models\",\n", + " model_name = save_filepath)\n", + " print(\"-\"*50 + \"\\n\")" + ], + "metadata": { + "id": "RvD1Uc1rOh8J", + "outputId": "8a3fbd16-fced-47ac-e0f6-41eeedb73085", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000, + "referenced_widgets": [ + "4695609ad07a442b96a09577e32c576d", + "e1a40b9ec98140558aa607029a0e3920", + "0eec67604997435f9104400d419c08f2", + "3ec79740118d4db5bf793787f2f1646f", + "ec839ee357584d6d9c953c3f71e55ff2", + "533961da467944e7909a07f7952b9999", + "1ad2abc210dd4ffbb0e3229f7f19ee16", + "a1e3e6242b454eaeb740c13b4da3e254", + "e346dfda2eac423cb4ae77f65ad31a08", + "9bad8b08a2d8454a9b6aad7594fc22e3", + "df62e0eaaa1e4e5e9d91d19c74c42a60", + "eb560c9f9de240ccb7b8fd2701625bee", + "e4dd59e10a36408288cce249e2420d7d", + "1bb254f3173046a49616ec08689d5176", + "fe8aeb5708c44e36940b1f723c56363d", + "637756c4effd459584fece99686ac524", + "42e7efc2ca914950810c1839f4443879", + "a71f8ce6462d49da849e24b4886838a3", + "d8e9f6b6d276408d82cdf1b2d2083a2a", + "5cfd2d17bf7d444eb469fdbd06b8c9b0", + "d77b96fc404a4c1e9a9839f7db93fb3b", + "6cc3c059a8bb48b9b557463c27cf0872", + "7008b323d16b4690b02819e022f1ed20", + "36159d2b66f04f8781c4458280bc10ee", + "6a86f141a5754a10bba39565166073f8", + "305c2a970cdf4a9eaafa20034d205e3c", + "ac4ad336491e4ceb89d565c98862419a", + "34e0c8b2549b4966bf222f1e288b5ee5", + "a0402a185c884e9f87fec59f706269d2", + "0d3e4120af494134837c8523aff70a90", + "5295a86e03a44bf8ab88203545c69068", + "c3b07a6ebdb142dbb82273d5160fb0a3", + "433b4b71634b4ac38f12cd6f0ec8c8c5", + "e01bd22ca11a45c692851ce68496a450", + "55aa8e0bd1c445dea251482dbdf5de0b", + "32bd9183080f4f0ab9e37c7b69e58d07", + "0712ca20c200409187a29b1499f4cb1b", + "7b5b02a335c641f09d1e5d0119ee732f", + "112e7a336d114977b7845a10f7814abf", + "d6529689e2af453496967c966139101f", + "f904c858da4649a687970cefae9e191a", + "7aa0a083f5214105b7f911821c7c4890", + "2214a242b2e84d65a3b96d77a6438a59", + "f36600c7a7cd4f3c974d472d2ca93039", + "0a13ad9692ad4d29b5e33a6ed6a6aeb8", + "a82ea609965445f487b741df5577f2e0", + "d06a916787f048bc8021b450b94713b2", + "b3e255d026994a11b1815baf627b5476", + "672a7882ab6c463fa1f8ec930d1344f0", + "1a48e0c29363483aaedac262dc7e9d6f", + "8ed45f20a68b424486e4abd9be970c3a", + "f41d11bb53aa4278b4f43d4f681eeb0b", + "6866057cfd0a44a9803044d15bc3e452", + "dfb3b2c3d09647e0b735aa59d5e3eb61", + "e989ccc5acb84feaade094cbd0ede1ec", + "a00ac6d276d543cc89803a63b2b84195", + "67280e5aa4e94d28aaac00400cf45daa", + "2b4819ea01d24207a2111b7d61562a2d", + "adbc017d4327408d92f6846f9d70f10d", + "2b32a2e0281a419c89fb55c642bb9769", + "bb35864a5e204e35a3526a9212745cee", + "75fa7f5d0659455ea3fb180dcf354a77", + "dee470215a32432780296392ea4501c4", + "e353be5c48d04d36972fe4b26b699482", + "344c9c10cc6a4228a95ac1ae9799d2ea", + "7b92aa0f701345c8835a1b4389d5ea06", + "8d23b172d32d45fc8b33e4b980fb3bd0", + "5f8b7b2fcada4663b3a6d1e427924455", + "9b60e284166b479bac1bac360e79036a", + "1f7da29d17d64e35823d69aa99e97c00", + "323d7e91a99d426cb5629a8afe4da36a", + "a08733bf7bdf4c1aa700cd7b85711dac", + "324294520c824ad28205bb0541cb5b02", + "adc9acaddb0146f6acb96273dbddc74f", + "ba585aa142574916a34609a411bcbfc5", + "e3ef4d2d0199420aa6af10c27a1ba536", + "f579df9d3b714c9384308d1b409b5de5", + "394b9195bc124d1bbdda0521fa4c43d7", + "ebb16abfe0d64e358b9aeccc63b941c0", + "27a6ed57c064474194130ee3820ed7fd", + "ffabd25c178d41529ca8d4fda5f8fb29", + "a4180b21afc845eab9cc35e9ea028ace", + "3dac7f84fb3e41f0916fd9ace98aaaf0", + "b7e5553d34794ccba05714cb17a77b81", + "1dbbe2df39e946cfa87bf38d216c8bbc", + "e14babfd7d92449ea998ac3f164a3eaa", + "1b4dca71eb5f4651bc95a5173cdb633c", + "2c93a2ef16024cef862380b7ad59c218" + ] + } + }, + "execution_count": 30, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "[INFO] Number of experiment : 1\n", + "[INFO] Dataloader : data_10_percent\n", + "[INFO] Number of epochs : 5\n", + "[INFO] Model's name : effnetb2\n", + "Created effnetb2 new model\n", + "[INFO] Created SummaryWriter, saving to: runs/2025-11-17/data_10_percent/effnetb2/5 epochs...\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + " 0%| | 0/5 [00:00" + ], + "application/javascript": [ + "\n", + " (async () => {\n", + " const url = new URL(await google.colab.kernel.proxyPort(6006, {'cache': true}));\n", + " url.searchParams.set('tensorboardColab', 'true');\n", + " const iframe = document.createElement('iframe');\n", + " iframe.src = url;\n", + " iframe.setAttribute('width', '100%');\n", + " iframe.setAttribute('height', '800');\n", + " iframe.setAttribute('frameborder', 0);\n", + " document.body.appendChild(iframe);\n", + " })();\n", + " " + ] + }, + "metadata": {} + } + ] + }, { "cell_type": "markdown", "source": [ @@ -683,13 +4498,13 @@ "from torch.utils.data import DataLoader\n", "from torchvision import datasets\n", "\n", - "NUM_WORKERS = os.cpu_count() # use maximum number of CPUs for workers to load data \n", + "NUM_WORKERS = os.cpu_count() # use maximum number of CPUs for workers to load data\n", "\n", "# Note: this is an update version of data_setup.create_dataloaders to handle\n", "# differnt train and test transforms.\n", "def create_dataloaders(\n", - " train_dir, \n", - " test_dir, \n", + " train_dir,\n", + " test_dir,\n", " train_transform, # add parameter for train transform (transforms on train dataset)\n", " test_transform, # add parameter for test transform (transforms on test dataset)\n", " batch_size=32, num_workers=NUM_WORKERS\n", @@ -732,7 +4547,7 @@ "metadata": { "id": "E1N3yyDOoH2t" }, - "execution_count": null, + "execution_count": 2, "outputs": [] }, { @@ -757,8 +4572,8 @@ "metadata": { "id": "YehliYnYoP1x" }, - "execution_count": null, + "execution_count": 3, "outputs": [] } ] -} +} \ No newline at end of file diff --git a/going_modular/05_pytorch_going_modular_script_mode.ipynb b/going_modular/05_pytorch_going_modular_script_mode.ipynb index 6b7ac2d9..86fd1a79 100644 --- a/going_modular/05_pytorch_going_modular_script_mode.ipynb +++ b/going_modular/05_pytorch_going_modular_script_mode.ipynb @@ -1,2376 +1,2377 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "view-in-github" - }, - "source": [ - "\"Open" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "1M5tsObEa17-", - "tags": [] - }, - "source": [ - "# 05. Going Modular: Part 2 (script mode)\n", - "\n", - "This notebook is part 2/2 of section [05. Going Modular](https://www.learnpytorch.io/05_pytorch_going_modular/).\n", - "\n", - "For reference, the two parts are: \n", - "1. [**05. Going Modular: Part 1 (cell mode)**](https://github.com/mrdbourke/pytorch-deep-learning/blob/main/going_modular/05_pytorch_going_modular_cell_mode.ipynb) - this notebook is run as a traditional Jupyter Notebook/Google Colab notebook and is a condensed version of [notebook 04](https://www.learnpytorch.io/04_pytorch_custom_datasets/).\n", - "2. [**05. Going Modular: Part 2 (script mode)**](https://github.com/mrdbourke/pytorch-deep-learning/blob/main/going_modular/05_pytorch_going_modular_script_mode.ipynb) - this notebook is the same as number 1 but with added functionality to turn each of the major sections into Python scripts, such as, `data_setup.py` and `train.py`. \n", - "\n", - "Why two parts?\n", - "\n", - "Because sometimes the best way to learn something is to see how it *differs* from something else.\n", - "\n", - "If you run each notebook side-by-side you'll see how they differ and that's where the key learnings are." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "1M5tsObEa17-", - "tags": [] - }, - "source": [ - "## What is script mode?\n", - "\n", - "**Script mode** uses [Jupyter Notebook cell magic](https://ipython.readthedocs.io/en/stable/interactive/magics.html) (special commands) to turn specific cells into Python scripts.\n", - "\n", - "For example if you run the following code in a cell, you'll create a Python file called `hello_world.py`:\n", - "\n", - "```\n", - "%%writefile hello_world.py\n", - "print(\"hello world, machine learning is fun!\")\n", - "```\n", - "\n", - "You could then run this Python file on the command line with:\n", - "\n", - "```\n", - "python hello_world.py\n", - "\n", - ">>> hello world, machine learning is fun!\n", - "```\n", - "\n", - "The main cell magic we're interested in using is `%%writefile`.\n", - "\n", - "Putting `%%writefile filename` at the top of a cell in Jupyter or Google Colab will write the contents of that cell to a specified `filename`.\n", - "\n", - "> **Question:** Do I have to create Python files like this? Can't I just start directly with a Python file and skip using a Google Colab notebook?\n", - ">\n", - "> **Answer:** Yes. This is only *one* way of creating Python scripts. If you know the kind of script you'd like to write, you could start writing it straight away. But since using Jupyter/Google Colab notebooks is a popular way of starting off data science and machine learning projects, knowing about the `%%writefile` magic command is a handy tip. " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "1M5tsObEa17-", - "tags": [] - }, - "source": [ - "## What has script mode got to do with PyTorch?\n", - "\n", - "If you've written some useful code in a Jupyter Notebook or Google Colab notebook, chances are you'll want to use that code again.\n", - "\n", - "And turning your useful cells into Python scripts (`.py` files) means you can use specific pieces of your code in other projects.\n", - "\n", - "This practice is not PyTorch specific.\n", - "\n", - "But it's how you'll see many different online PyTorch repositories structured." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "1M5tsObEa17-", - "tags": [] - }, - "source": [ - "### PyTorch in the wild\n", - "\n", - "For example, if you find a PyTorch project on GitHub, it may be structured in the following way:\n", - "\n", - "```\n", - "pytorch_project/\n", - "├── pytorch_project/\n", - "│ ├── data_setup.py\n", - "│ ├── engine.py\n", - "│ ├── model.py\n", - "│ ├── train.py\n", - "│ └── utils.py\n", - "├── models/\n", - "│ ├── model_1.pth\n", - "│ └── model_2.pth\n", - "└── data/\n", - " ├── data_folder_1/\n", - " └── data_folder_2/\n", - "```\n", - "\n", - "Here, the top level directory is called `pytorch_project` but you could call it whatever you want.\n", - "\n", - "Inside there's another directory called `pytorch_project` which contains several `.py` files, the purposes of these may be:\n", - "* `data_setup.py` - a file to prepare data (and download data if needed).\n", - "* `engine.py` - a file containing various training functions.\n", - "* `model_builder.py` or `model.py` - a file to create a PyTorch model.\n", - "* `train.py` - a file to leverage all other files and train a target PyTorch model.\n", - "* `utils.py` - a file dedicated to helpful utility functions.\n", - "\n", - "And the `models` and `data` directories could hold PyTorch models and data files respectively (though due to the size of models and data files, it's unlikely you'll find the *full* versions of these on GitHub, these directories are present above mainly for demonstration purposes).\n", - "\n", - "> **Note:** There are many different ways to structure a Python project and subsequently a PyTorch project. This isn't a guide on *how* to structure your projects, only an example of how you *might* come across PyTorch projects in the wild. For more on structuring Python projects, see Real Python's [*Python Application Layouts: A Reference*](https://realpython.com/python-application-layouts/) guide." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "1M5tsObEa17-", - "tags": [] - }, - "source": [ - "## What's the difference between this notebook (Part 2) and the cell mode notebook (Part 1)?\n", - "\n", - "This notebook, 05 Going Modular: Part 2 (script mode), creates Python scripts out of the cells created in part 1.\n", - "\n", - "Running this notebook end-to-end will result in having a directory structure very similar to the `pytorch_project` structure above.\n", - "\n", - "You'll notice each section in Part 2 (script mode) has an extra subsection (e.g. 2.1, 3.1, 4.1) for turning cell code into script code." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "1M5tsObEa17-", - "tags": [] - }, - "source": [ - "## What we're going to cover\n", - "\n", - "By the end of this notebook you should finish with a directory structure of: \n", - "\n", - "```\n", - "going_modular/\n", - "├── going_modular/\n", - "│ ├── data_setup.py\n", - "│ ├── engine.py\n", - "│ ├── model_builder.py\n", - "│ ├── train.py\n", - "│ └── utils.py\n", - "├── models/\n", - "│ ├── 05_going_modular_cell_mode_tinyvgg_model.pth\n", - "│ └── 05_going_modular_script_mode_tinyvgg_model.pth\n", - "└── data/\n", - " └── pizza_steak_sushi/\n", - " ├── train/\n", - " │ ├── pizza/\n", - " │ │ ├── image01.jpeg\n", - " │ │ └── ...\n", - " │ ├── steak/\n", - " │ └── sushi/\n", - " └── test/\n", - " ├── pizza/\n", - " ├── steak/\n", - " └── sushi/\n", - "```\n", - "\n", - "Using this directory structure, you should be able to train a model from within a notebook with the command:\n", - "\n", - "```\n", - "!python going_modular/train.py\n", - "```\n", - "\n", - "Or from the command line with:\n", - "\n", - "```\n", - "python going_modular/train.py\n", - "```\n", - "\n", - "In essence, we will have turned our helpful notebook code into **reusable modular code**." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "1M5tsObEa17-", - "tags": [] - }, - "source": [ - "## Where can you get help?\n", - "\n", - "You can find the book version of this section [05. PyTorch Going Modular on learnpytorch.io](https://www.learnpytorch.io/05_pytorch_going_modular/).\n", - "\n", - "The rest of the materials for this course [are available on GitHub](https://github.com/mrdbourke/pytorch-deep-learning).\n", - "\n", - "If you run into trouble, you can ask a question on the course [GitHub Discussions page](https://github.com/mrdbourke/pytorch-deep-learning/discussions).\n", - "\n", - "And of course, there's the [PyTorch documentation](https://pytorch.org/docs/stable/index.html) and [PyTorch developer forums](https://discuss.pytorch.org/), a very helpful place for all things PyTorch. " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "OW_3D_fOap2I" - }, - "source": [ - "## 0. Creating a folder for storing Python scripts\n", - "\n", - "Since we're going to be creating Python scripts out of our most useful code cells, let's create a folder for storing those scripts.\n", - "\n", - "We'll call the folder `going_modular` and create it using Python's [`os.makedirs()`](https://docs.python.org/3/library/os.html) method." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "id": "K29qwItveNm9" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "os.makedirs(\"going_modular\", exist_ok=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "1XgXFr97bCsI" - }, - "source": [ - "## 1. Get data\n", - "\n", - "We're going to start by downloading the same data we used in [notebook 04](https://www.learnpytorch.io/04_pytorch_custom_datasets/#1-get-data), the `pizza_steak_sushi` dataset with images of pizza, steak and sushi." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "view-in-github", + "colab_type": "text" + }, + "source": [ + "\"Open" + ] }, - "id": "wl_jfPzVbGDS", - "outputId": "51a3e5e8-991c-4309-b1ba-b24d32bbdb61" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "data/pizza_steak_sushi directory exists.\n", - "Downloading pizza, steak, sushi data...\n", - "Unzipping pizza, steak, sushi data...\n" - ] - } - ], - "source": [ - "import os\n", - "import zipfile\n", - "\n", - "from pathlib import Path\n", - "\n", - "import requests\n", - "\n", - "# Setup path to data folder\n", - "data_path = Path(\"data/\")\n", - "image_path = data_path / \"pizza_steak_sushi\"\n", - "\n", - "# If the image folder doesn't exist, download it and prepare it... \n", - "if image_path.is_dir():\n", - " print(f\"{image_path} directory exists.\")\n", - "else:\n", - " print(f\"Did not find {image_path} directory, creating one...\")\n", - " image_path.mkdir(parents=True, exist_ok=True)\n", - " \n", - "# Download pizza, steak, sushi data\n", - "with open(data_path / \"pizza_steak_sushi.zip\", \"wb\") as f:\n", - " request = requests.get(\"https://github.com/mrdbourke/pytorch-deep-learning/raw/main/data/pizza_steak_sushi.zip\")\n", - " print(\"Downloading pizza, steak, sushi data...\")\n", - " f.write(request.content)\n", - "\n", - "# Unzip pizza, steak, sushi data\n", - "with zipfile.ZipFile(data_path / \"pizza_steak_sushi.zip\", \"r\") as zip_ref:\n", - " print(\"Unzipping pizza, steak, sushi data...\") \n", - " zip_ref.extractall(image_path)\n", - "\n", - "# Remove zip file\n", - "os.remove(data_path / \"pizza_steak_sushi.zip\")" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" + "cell_type": "markdown", + "metadata": { + "id": "1M5tsObEa17-", + "tags": [] + }, + "source": [ + "# 05. Going Modular: Part 2 (script mode)\n", + "\n", + "This notebook is part 2/2 of section [05. Going Modular](https://www.learnpytorch.io/05_pytorch_going_modular/).\n", + "\n", + "For reference, the two parts are:\n", + "1. [**05. Going Modular: Part 1 (cell mode)**](https://github.com/mrdbourke/pytorch-deep-learning/blob/main/going_modular/05_pytorch_going_modular_cell_mode.ipynb) - this notebook is run as a traditional Jupyter Notebook/Google Colab notebook and is a condensed version of [notebook 04](https://www.learnpytorch.io/04_pytorch_custom_datasets/).\n", + "2. [**05. Going Modular: Part 2 (script mode)**](https://github.com/mrdbourke/pytorch-deep-learning/blob/main/going_modular/05_pytorch_going_modular_script_mode.ipynb) - this notebook is the same as number 1 but with added functionality to turn each of the major sections into Python scripts, such as, `data_setup.py` and `train.py`.\n", + "\n", + "Why two parts?\n", + "\n", + "Because sometimes the best way to learn something is to see how it *differs* from something else.\n", + "\n", + "If you run each notebook side-by-side you'll see how they differ and that's where the key learnings are." + ] }, - "id": "8qYMdiBMbde0", - "outputId": "a5093c72-455f-471c-9e95-5b391eea9ebf" - }, - "outputs": [ { - "data": { - "text/plain": [ - "(PosixPath('data/pizza_steak_sushi/train'),\n", - " PosixPath('data/pizza_steak_sushi/test'))" + "cell_type": "markdown", + "metadata": { + "tags": [], + "id": "z6UnNOfkXoI-" + }, + "source": [ + "## What is script mode?\n", + "\n", + "**Script mode** uses [Jupyter Notebook cell magic](https://ipython.readthedocs.io/en/stable/interactive/magics.html) (special commands) to turn specific cells into Python scripts.\n", + "\n", + "For example if you run the following code in a cell, you'll create a Python file called `hello_world.py`:\n", + "\n", + "```\n", + "%%writefile hello_world.py\n", + "print(\"hello world, machine learning is fun!\")\n", + "```\n", + "\n", + "You could then run this Python file on the command line with:\n", + "\n", + "```\n", + "python hello_world.py\n", + "\n", + ">>> hello world, machine learning is fun!\n", + "```\n", + "\n", + "The main cell magic we're interested in using is `%%writefile`.\n", + "\n", + "Putting `%%writefile filename` at the top of a cell in Jupyter or Google Colab will write the contents of that cell to a specified `filename`.\n", + "\n", + "> **Question:** Do I have to create Python files like this? Can't I just start directly with a Python file and skip using a Google Colab notebook?\n", + ">\n", + "> **Answer:** Yes. This is only *one* way of creating Python scripts. If you know the kind of script you'd like to write, you could start writing it straight away. But since using Jupyter/Google Colab notebooks is a popular way of starting off data science and machine learning projects, knowing about the `%%writefile` magic command is a handy tip." ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Setup train and testing paths\n", - "train_dir = image_path / \"train\"\n", - "test_dir = image_path / \"test\"\n", - "\n", - "train_dir, test_dir" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "eYL6NynybU9Z" - }, - "source": [ - "## 2. Create Datasets and DataLoaders\n", - "\n", - "Let's turn our data into PyTorch `Dataset`'s and `DataLoader`'s and find out a few useful attributes from them such as `classes` and their lengths. " - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" }, - "id": "DaOJOd-QbOQk", - "outputId": "dc7168b3-8bee-40c4-867e-58aa058175b5" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Train data:\n", - "Dataset ImageFolder\n", - " Number of datapoints: 225\n", - " Root location: data/pizza_steak_sushi/train\n", - " StandardTransform\n", - "Transform: Compose(\n", - " Resize(size=(64, 64), interpolation=bilinear, max_size=None, antialias=None)\n", - " ToTensor()\n", - " )\n", - "Test data:\n", - "Dataset ImageFolder\n", - " Number of datapoints: 75\n", - " Root location: data/pizza_steak_sushi/test\n", - " StandardTransform\n", - "Transform: Compose(\n", - " Resize(size=(64, 64), interpolation=bilinear, max_size=None, antialias=None)\n", - " ToTensor()\n", - " )\n" - ] - } - ], - "source": [ - "from torchvision import datasets, transforms\n", - "\n", - "# Create simple transform\n", - "data_transform = transforms.Compose([ \n", - " transforms.Resize((64, 64)),\n", - " transforms.ToTensor(),\n", - "])\n", - "\n", - "# Use ImageFolder to create dataset(s)\n", - "train_data = datasets.ImageFolder(root=train_dir, # target folder of images\n", - " transform=data_transform, # transforms to perform on data (images)\n", - " target_transform=None) # transforms to perform on labels (if necessary)\n", - "\n", - "test_data = datasets.ImageFolder(root=test_dir, \n", - " transform=data_transform)\n", - "\n", - "print(f\"Train data:\\n{train_data}\\nTest data:\\n{test_data}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" + "cell_type": "markdown", + "metadata": { + "tags": [], + "id": "4t1COKV2XoI_" + }, + "source": [ + "## What has script mode got to do with PyTorch?\n", + "\n", + "If you've written some useful code in a Jupyter Notebook or Google Colab notebook, chances are you'll want to use that code again.\n", + "\n", + "And turning your useful cells into Python scripts (`.py` files) means you can use specific pieces of your code in other projects.\n", + "\n", + "This practice is not PyTorch specific.\n", + "\n", + "But it's how you'll see many different online PyTorch repositories structured." + ] }, - "id": "eEln0VFmbfyY", - "outputId": "c514574e-e3ab-4689-a5ee-6a2aef6a11d2" - }, - "outputs": [ { - "data": { - "text/plain": [ - "['pizza', 'steak', 'sushi']" + "cell_type": "markdown", + "metadata": { + "tags": [], + "id": "WJ-psm3qXoI_" + }, + "source": [ + "### PyTorch in the wild\n", + "\n", + "For example, if you find a PyTorch project on GitHub, it may be structured in the following way:\n", + "\n", + "```\n", + "pytorch_project/\n", + "├── pytorch_project/\n", + "│ ├── data_setup.py\n", + "│ ├── engine.py\n", + "│ ├── model.py\n", + "│ ├── train.py\n", + "│ └── utils.py\n", + "├── models/\n", + "│ ├── model_1.pth\n", + "│ └── model_2.pth\n", + "└── data/\n", + " ├── data_folder_1/\n", + " └── data_folder_2/\n", + "```\n", + "\n", + "Here, the top level directory is called `pytorch_project` but you could call it whatever you want.\n", + "\n", + "Inside there's another directory called `pytorch_project` which contains several `.py` files, the purposes of these may be:\n", + "* `data_setup.py` - a file to prepare data (and download data if needed).\n", + "* `engine.py` - a file containing various training functions.\n", + "* `model_builder.py` or `model.py` - a file to create a PyTorch model.\n", + "* `train.py` - a file to leverage all other files and train a target PyTorch model.\n", + "* `utils.py` - a file dedicated to helpful utility functions.\n", + "\n", + "And the `models` and `data` directories could hold PyTorch models and data files respectively (though due to the size of models and data files, it's unlikely you'll find the *full* versions of these on GitHub, these directories are present above mainly for demonstration purposes).\n", + "\n", + "> **Note:** There are many different ways to structure a Python project and subsequently a PyTorch project. This isn't a guide on *how* to structure your projects, only an example of how you *might* come across PyTorch projects in the wild. For more on structuring Python projects, see Real Python's [*Python Application Layouts: A Reference*](https://realpython.com/python-application-layouts/) guide." ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Get class names as a list\n", - "class_names = train_data.classes\n", - "class_names" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" }, - "id": "IMGTFVr8bgwE", - "outputId": "4268d11f-8899-447a-baba-7e91ade01f13" - }, - "outputs": [ { - "data": { - "text/plain": [ - "{'pizza': 0, 'steak': 1, 'sushi': 2}" + "cell_type": "markdown", + "metadata": { + "tags": [], + "id": "2ryUvCbKXoJA" + }, + "source": [ + "## What's the difference between this notebook (Part 2) and the cell mode notebook (Part 1)?\n", + "\n", + "This notebook, 05 Going Modular: Part 2 (script mode), creates Python scripts out of the cells created in part 1.\n", + "\n", + "Running this notebook end-to-end will result in having a directory structure very similar to the `pytorch_project` structure above.\n", + "\n", + "You'll notice each section in Part 2 (script mode) has an extra subsection (e.g. 2.1, 3.1, 4.1) for turning cell code into script code." ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Can also get class names as a dict\n", - "class_dict = train_data.class_to_idx\n", - "class_dict" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" }, - "id": "6a8IW-IYbhGX", - "outputId": "87bc60ac-3be3-499f-e0a8-aed5964a3075" - }, - "outputs": [ { - "data": { - "text/plain": [ - "(225, 75)" + "cell_type": "markdown", + "metadata": { + "tags": [], + "id": "qeol24g7XoJB" + }, + "source": [ + "## What we're going to cover\n", + "\n", + "By the end of this notebook you should finish with a directory structure of:\n", + "\n", + "```\n", + "going_modular/\n", + "├── going_modular/\n", + "│ ├── data_setup.py\n", + "│ ├── engine.py\n", + "│ ├── model_builder.py\n", + "│ ├── train.py\n", + "│ └── utils.py\n", + "├── models/\n", + "│ ├── 05_going_modular_cell_mode_tinyvgg_model.pth\n", + "│ └── 05_going_modular_script_mode_tinyvgg_model.pth\n", + "└── data/\n", + " └── pizza_steak_sushi/\n", + " ├── train/\n", + " │ ├── pizza/\n", + " │ │ ├── image01.jpeg\n", + " │ │ └── ...\n", + " │ ├── steak/\n", + " │ └── sushi/\n", + " └── test/\n", + " ├── pizza/\n", + " ├── steak/\n", + " └── sushi/\n", + "```\n", + "\n", + "Using this directory structure, you should be able to train a model from within a notebook with the command:\n", + "\n", + "```\n", + "!python going_modular/train.py\n", + "```\n", + "\n", + "Or from the command line with:\n", + "\n", + "```\n", + "python going_modular/train.py\n", + "```\n", + "\n", + "In essence, we will have turned our helpful notebook code into **reusable modular code**." ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Check the lengths\n", - "len(train_data), len(test_data)" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" }, - "id": "f1UxnvvmblUj", - "outputId": "1dd0c011-912e-498b-ea34-ee4bf05c7b87" - }, - "outputs": [ { - "data": { - "text/plain": [ - "(,\n", - " )" + "cell_type": "markdown", + "metadata": { + "tags": [], + "id": "fVjF3MLOXoJC" + }, + "source": [ + "## Where can you get help?\n", + "\n", + "You can find the book version of this section [05. PyTorch Going Modular on learnpytorch.io](https://www.learnpytorch.io/05_pytorch_going_modular/).\n", + "\n", + "The rest of the materials for this course [are available on GitHub](https://github.com/mrdbourke/pytorch-deep-learning).\n", + "\n", + "If you run into trouble, you can ask a question on the course [GitHub Discussions page](https://github.com/mrdbourke/pytorch-deep-learning/discussions).\n", + "\n", + "And of course, there's the [PyTorch documentation](https://pytorch.org/docs/stable/index.html) and [PyTorch developer forums](https://discuss.pytorch.org/), a very helpful place for all things PyTorch." ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Turn train and test Datasets into DataLoaders\n", - "from torch.utils.data import DataLoader\n", - "\n", - "train_dataloader = DataLoader(dataset=train_data, \n", - " batch_size=1, # how many samples per batch?\n", - " num_workers=1, # how many subprocesses to use for data loading? (higher = more)\n", - " shuffle=True) # shuffle the data?\n", - "\n", - "test_dataloader = DataLoader(dataset=test_data, \n", - " batch_size=1, \n", - " num_workers=1, \n", - " shuffle=False) # don't usually need to shuffle testing data\n", - "\n", - "train_dataloader, test_dataloader" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" }, - "id": "JJKRqXNGbnI5", - "outputId": "2d5320fc-ac72-4ef8-94f8-0ef283fc3a30" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Image shape: torch.Size([1, 3, 64, 64]) -> [batch_size, color_channels, height, width]\n", - "Label shape: torch.Size([1])\n" - ] - } - ], - "source": [ - "# Check out single image size/shape\n", - "img, label = next(iter(train_dataloader))\n", - "\n", - "# Batch size will now be 1, try changing the batch_size parameter above and see what happens\n", - "print(f\"Image shape: {img.shape} -> [batch_size, color_channels, height, width]\")\n", - "print(f\"Label shape: {label.shape}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "RjINjcyGdwL9" - }, - "source": [ - "### 2.1 Create Datasets and DataLoaders (script mode)\n", - "\n", - "Rather than rewriting all of the code above everytime we wanted to load data, we can turn it into a script called `data_setup.py`.\n", - "\n", - "Let's capture all of the above functionality into a function called `create_dataloaders()`." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" + "cell_type": "markdown", + "metadata": { + "id": "OW_3D_fOap2I" + }, + "source": [ + "## 0. Creating a folder for storing Python scripts\n", + "\n", + "Since we're going to be creating Python scripts out of our most useful code cells, let's create a folder for storing those scripts.\n", + "\n", + "We'll call the folder `going_modular` and create it using Python's [`os.makedirs()`](https://docs.python.org/3/library/os.html) method." + ] }, - "id": "CmjK-b04du_-", - "outputId": "07d3450d-bc24-4aa4-a6d6-57d60bf11416" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Overwriting going_modular/data_setup.py\n" - ] - } - ], - "source": [ - "%%writefile going_modular/data_setup.py\n", - "\"\"\"\n", - "Contains functionality for creating PyTorch DataLoaders for \n", - "image classification data.\n", - "\"\"\"\n", - "import os\n", - "\n", - "from torch.utils.data import DataLoader\n", - "from torchvision import datasets, transforms\n", - "\n", - "NUM_WORKERS = os.cpu_count()\n", - "\n", - "def create_dataloaders(\n", - " train_dir: str, \n", - " test_dir: str, \n", - " transform: transforms.Compose, \n", - " batch_size: int, \n", - " num_workers: int=NUM_WORKERS\n", - "):\n", - " \"\"\"Creates training and testing DataLoaders.\n", - "\n", - " Takes in a training directory and testing directory path and turns\n", - " them into PyTorch Datasets and then into PyTorch DataLoaders.\n", - "\n", - " Args:\n", - " train_dir: Path to training directory.\n", - " test_dir: Path to testing directory.\n", - " transform: torchvision transforms to perform on training and testing data.\n", - " batch_size: Number of samples per batch in each of the DataLoaders.\n", - " num_workers: An integer for number of workers per DataLoader.\n", - "\n", - " Returns:\n", - " A tuple of (train_dataloader, test_dataloader, class_names).\n", - " Where class_names is a list of the target classes.\n", - " Example usage:\n", - " train_dataloader, test_dataloader, class_names = \\\n", - " = create_dataloaders(train_dir=path/to/train_dir,\n", - " test_dir=path/to/test_dir,\n", - " transform=some_transform,\n", - " batch_size=32,\n", - " num_workers=4)\n", - " \"\"\"\n", - " # Use ImageFolder to create dataset(s)\n", - " train_data = datasets.ImageFolder(train_dir, transform=transform)\n", - " test_data = datasets.ImageFolder(test_dir, transform=transform)\n", - "\n", - " # Get class names\n", - " class_names = train_data.classes\n", - "\n", - " # Turn images into data loaders\n", - " train_dataloader = DataLoader(\n", - " train_data,\n", - " batch_size=batch_size,\n", - " shuffle=True,\n", - " num_workers=num_workers,\n", - " pin_memory=True,\n", - " )\n", - " test_dataloader = DataLoader(\n", - " test_data,\n", - " batch_size=batch_size,\n", - " shuffle=False,\n", - " num_workers=num_workers,\n", - " pin_memory=True,\n", - " )\n", - "\n", - " return train_dataloader, test_dataloader, class_names" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "gAQLp1dCbrY0" - }, - "source": [ - "## 3. Making a model (TinyVGG) \n", - "\n", - "We're going to use the same model we used in notebook 04: TinyVGG from the CNN Explainer website.\n", - "\n", - "The only change here from notebook 04 is that a docstring has been added using [Google's Style Guide for Python](https://google.github.io/styleguide/pyguide.html#384-classes). " - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "id": "Z6i2xWcwbvom" - }, - "outputs": [], - "source": [ - "import torch\n", - "\n", - "from torch import nn \n", - "\n", - "class TinyVGG(nn.Module):\n", - " \"\"\"Creates the TinyVGG architecture.\n", - "\n", - " Replicates the TinyVGG architecture from the CNN explainer website in PyTorch.\n", - " See the original architecture here: https://poloclub.github.io/cnn-explainer/\n", - "\n", - " Args:\n", - " input_shape: An integer indicating number of input channels.\n", - " hidden_units: An integer indicating number of hidden units between layers.\n", - " output_shape: An integer indicating number of output units.\n", - " \"\"\"\n", - " def __init__(self, input_shape: int, hidden_units: int, output_shape: int) -> None:\n", - " super().__init__()\n", - " self.conv_block_1 = nn.Sequential(\n", - " nn.Conv2d(in_channels=input_shape, \n", - " out_channels=hidden_units, \n", - " kernel_size=3, \n", - " stride=1, \n", - " padding=0), \n", - " nn.ReLU(),\n", - " nn.Conv2d(in_channels=hidden_units, \n", - " out_channels=hidden_units,\n", - " kernel_size=3,\n", - " stride=1,\n", - " padding=0),\n", - " nn.ReLU(),\n", - " nn.MaxPool2d(kernel_size=2,\n", - " stride=2)\n", - " )\n", - " self.conv_block_2 = nn.Sequential(\n", - " nn.Conv2d(hidden_units, hidden_units, kernel_size=3, padding=0),\n", - " nn.ReLU(),\n", - " nn.Conv2d(hidden_units, hidden_units, kernel_size=3, padding=0),\n", - " nn.ReLU(),\n", - " nn.MaxPool2d(2)\n", - " )\n", - " self.classifier = nn.Sequential(\n", - " nn.Flatten(),\n", - " # Where did this in_features shape come from? \n", - " # It's because each layer of our network compresses and changes the shape of our inputs data.\n", - " nn.Linear(in_features=hidden_units*13*13,\n", - " out_features=output_shape)\n", - " )\n", - " \n", - " def forward(self, x: torch.Tensor):\n", - " x = self.conv_block_1(x)\n", - " x = self.conv_block_2(x)\n", - " x = self.classifier(x)\n", - " return x\n", - " # return self.classifier(self.block_2(self.block_1(x))) # <- leverage the benefits of operator fusion" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "ROZsju1d9Cbu" - }, - "source": [ - "Now let's create an instance of `TinyVGG` and put it on the target device.\n", - "\n", - "> **Note:** If you're using Google Colab, and you'd like to use a GPU (recommended), you can turn one on via going to Runtime -> Change runtime type -> Hardware accelerator -> GPU." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "K29qwItveNm9" + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "os.makedirs(\"going_modular\", exist_ok=True)" + ] }, - "id": "tXOOAEHHc6-L", - "outputId": "3207fda6-896b-4cb5-9f08-f1e46879bf75" - }, - "outputs": [ { - "data": { - "text/plain": [ - "TinyVGG(\n", - " (conv_block_1): Sequential(\n", - " (0): Conv2d(3, 10, kernel_size=(3, 3), stride=(1, 1))\n", - " (1): ReLU()\n", - " (2): Conv2d(10, 10, kernel_size=(3, 3), stride=(1, 1))\n", - " (3): ReLU()\n", - " (4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n", - " )\n", - " (conv_block_2): Sequential(\n", - " (0): Conv2d(10, 10, kernel_size=(3, 3), stride=(1, 1))\n", - " (1): ReLU()\n", - " (2): Conv2d(10, 10, kernel_size=(3, 3), stride=(1, 1))\n", - " (3): ReLU()\n", - " (4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n", - " )\n", - " (classifier): Sequential(\n", - " (0): Flatten(start_dim=1, end_dim=-1)\n", - " (1): Linear(in_features=1690, out_features=3, bias=True)\n", - " )\n", - ")" + "cell_type": "markdown", + "metadata": { + "id": "1XgXFr97bCsI" + }, + "source": [ + "## 1. Get data\n", + "\n", + "We're going to start by downloading the same data we used in [notebook 04](https://www.learnpytorch.io/04_pytorch_custom_datasets/#1-get-data), the `pizza_steak_sushi` dataset with images of pizza, steak and sushi." ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import torch\n", - "\n", - "device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n", - "\n", - "# Instantiate an instance of the model\n", - "torch.manual_seed(42)\n", - "model_0 = TinyVGG(input_shape=3, # number of color channels (3 for RGB) \n", - " hidden_units=10, \n", - " output_shape=len(train_data.classes)).to(device)\n", - "model_0" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "C1CwvPiMcEfZ" - }, - "source": [ - "Let's check out our model by doing a dummy forward pass." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" }, - "id": "__p5G2ArcFko", - "outputId": "341f2b73-a0fa-4fdb-f458-142510dcfcca" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Single image shape: torch.Size([1, 3, 64, 64])\n", - "\n", - "Output logits:\n", - "tensor([[ 0.0208, -0.0019, 0.0095]], device='cuda:0')\n", - "\n", - "Output prediction probabilities:\n", - "tensor([[0.3371, 0.3295, 0.3333]], device='cuda:0')\n", - "\n", - "Output prediction label:\n", - "tensor([0], device='cuda:0')\n", - "\n", - "Actual label:\n", - "0\n" - ] - } - ], - "source": [ - "# 1. Get a batch of images and labels from the DataLoader\n", - "img_batch, label_batch = next(iter(train_dataloader))\n", - "\n", - "# 2. Get a single image from the batch and unsqueeze the image so its shape fits the model\n", - "img_single, label_single = img_batch[0].unsqueeze(dim=0), label_batch[0]\n", - "print(f\"Single image shape: {img_single.shape}\\n\")\n", - "\n", - "# 3. Perform a forward pass on a single image\n", - "model_0.eval()\n", - "with torch.inference_mode():\n", - " pred = model_0(img_single.to(device))\n", - " \n", - "# 4. Print out what's happening and convert model logits -> pred probs -> pred label\n", - "print(f\"Output logits:\\n{pred}\\n\")\n", - "print(f\"Output prediction probabilities:\\n{torch.softmax(pred, dim=1)}\\n\")\n", - "print(f\"Output prediction label:\\n{torch.argmax(torch.softmax(pred, dim=1), dim=1)}\\n\")\n", - "print(f\"Actual label:\\n{label_single}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "wSaDm_W7bc3y" - }, - "source": [ - "### 3.1 Making a model (TinyVGG) (script mode)\n", - "\n", - "Over the past few notebooks (notebook 03 and notebook 04), we've built the TinyVGG model a few times.\n", - "\n", - "So it makes sense to put the model into its file so we can reuse it again and again.\n", - "\n", - "Let's put our `TinyVGG()` model class into a script called `model_builder.py` with the line `%%writefile going_modular/model_builder.py`. " - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "wl_jfPzVbGDS", + "outputId": "51a3e5e8-991c-4309-b1ba-b24d32bbdb61" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "data/pizza_steak_sushi directory exists.\n", + "Downloading pizza, steak, sushi data...\n", + "Unzipping pizza, steak, sushi data...\n" + ] + } + ], + "source": [ + "import os\n", + "import zipfile\n", + "\n", + "from pathlib import Path\n", + "\n", + "import requests\n", + "\n", + "# Setup path to data folder\n", + "data_path = Path(\"data/\")\n", + "image_path = data_path / \"pizza_steak_sushi\"\n", + "\n", + "# If the image folder doesn't exist, download it and prepare it...\n", + "if image_path.is_dir():\n", + " print(f\"{image_path} directory exists.\")\n", + "else:\n", + " print(f\"Did not find {image_path} directory, creating one...\")\n", + " image_path.mkdir(parents=True, exist_ok=True)\n", + "\n", + "# Download pizza, steak, sushi data\n", + "with open(data_path / \"pizza_steak_sushi.zip\", \"wb\") as f:\n", + " request = requests.get(\"https://github.com/mrdbourke/pytorch-deep-learning/raw/main/data/pizza_steak_sushi.zip\")\n", + " print(\"Downloading pizza, steak, sushi data...\")\n", + " f.write(request.content)\n", + "\n", + "# Unzip pizza, steak, sushi data\n", + "with zipfile.ZipFile(data_path / \"pizza_steak_sushi.zip\", \"r\") as zip_ref:\n", + " print(\"Unzipping pizza, steak, sushi data...\")\n", + " zip_ref.extractall(image_path)\n", + "\n", + "# Remove zip file\n", + "os.remove(data_path / \"pizza_steak_sushi.zip\")" + ] }, - "id": "oEH3bklL86hF", - "outputId": "b3f7e983-e51c-4e35-a991-df5ff99d63e6" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Overwriting going_modular/model_builder.py\n" - ] - } - ], - "source": [ - "%%writefile going_modular/model_builder.py\n", - "\"\"\"\n", - "Contains PyTorch model code to instantiate a TinyVGG model.\n", - "\"\"\"\n", - "import torch\n", - "\n", - "from torch import nn\n", - "\n", - "class TinyVGG(nn.Module):\n", - " \"\"\"Creates the TinyVGG architecture.\n", - "\n", - " Replicates the TinyVGG architecture from the CNN explainer website in PyTorch.\n", - " See the original architecture here: https://poloclub.github.io/cnn-explainer/\n", - "\n", - " Args:\n", - " input_shape: An integer indicating number of input channels.\n", - " hidden_units: An integer indicating number of hidden units between layers.\n", - " output_shape: An integer indicating number of output units.\n", - " \"\"\"\n", - " def __init__(self, input_shape: int, hidden_units: int, output_shape: int) -> None:\n", - " super().__init__()\n", - " self.conv_block_1 = nn.Sequential(\n", - " nn.Conv2d(in_channels=input_shape, \n", - " out_channels=hidden_units, \n", - " kernel_size=3, \n", - " stride=1, \n", - " padding=0), \n", - " nn.ReLU(),\n", - " nn.Conv2d(in_channels=hidden_units, \n", - " out_channels=hidden_units,\n", - " kernel_size=3,\n", - " stride=1,\n", - " padding=0),\n", - " nn.ReLU(),\n", - " nn.MaxPool2d(kernel_size=2,\n", - " stride=2)\n", - " )\n", - " self.conv_block_2 = nn.Sequential(\n", - " nn.Conv2d(hidden_units, hidden_units, kernel_size=3, padding=0),\n", - " nn.ReLU(),\n", - " nn.Conv2d(hidden_units, hidden_units, kernel_size=3, padding=0),\n", - " nn.ReLU(),\n", - " nn.MaxPool2d(2)\n", - " )\n", - " self.classifier = nn.Sequential(\n", - " nn.Flatten(),\n", - " # Where did this in_features shape come from? \n", - " # It's because each layer of our network compresses and changes the shape of our inputs data.\n", - " nn.Linear(in_features=hidden_units*13*13,\n", - " out_features=output_shape)\n", - " )\n", - " \n", - " def forward(self, x: torch.Tensor):\n", - " x = self.conv_block_1(x)\n", - " x = self.conv_block_2(x)\n", - " x = self.classifier(x)\n", - " return x\n", - " # return self.classifier(self.block_2(self.block_1(x))) # <- leverage the benefits of operator fusion" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "3-WijBWM9K2c" - }, - "source": [ - "Create an instance of `TinyVGG` (from the script)." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "8qYMdiBMbde0", + "outputId": "a5093c72-455f-471c-9e95-5b391eea9ebf" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(PosixPath('data/pizza_steak_sushi/train'),\n", + " PosixPath('data/pizza_steak_sushi/test'))" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Setup train and testing paths\n", + "train_dir = image_path / \"train\"\n", + "test_dir = image_path / \"test\"\n", + "\n", + "train_dir, test_dir" + ] }, - "id": "F__hbQwZ9A56", - "outputId": "4e8abd04-cbc4-4aa8-b71c-3a8ff8617407" - }, - "outputs": [ { - "data": { - "text/plain": [ - "TinyVGG(\n", - " (conv_block_1): Sequential(\n", - " (0): Conv2d(3, 10, kernel_size=(3, 3), stride=(1, 1))\n", - " (1): ReLU()\n", - " (2): Conv2d(10, 10, kernel_size=(3, 3), stride=(1, 1))\n", - " (3): ReLU()\n", - " (4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n", - " )\n", - " (conv_block_2): Sequential(\n", - " (0): Conv2d(10, 10, kernel_size=(3, 3), stride=(1, 1))\n", - " (1): ReLU()\n", - " (2): Conv2d(10, 10, kernel_size=(3, 3), stride=(1, 1))\n", - " (3): ReLU()\n", - " (4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n", - " )\n", - " (classifier): Sequential(\n", - " (0): Flatten(start_dim=1, end_dim=-1)\n", - " (1): Linear(in_features=1690, out_features=3, bias=True)\n", - " )\n", - ")" + "cell_type": "markdown", + "metadata": { + "id": "eYL6NynybU9Z" + }, + "source": [ + "## 2. Create Datasets and DataLoaders\n", + "\n", + "Let's turn our data into PyTorch `Dataset`'s and `DataLoader`'s and find out a few useful attributes from them such as `classes` and their lengths." ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import torch\n", - "\n", - "from going_modular import model_builder\n", - "\n", - "device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n", - "\n", - "# Instantiate an instance of the model from the \"model_builder.py\" script\n", - "torch.manual_seed(42)\n", - "model_1 = model_builder.TinyVGG(input_shape=3, # number of color channels (3 for RGB) \n", - " hidden_units=10, \n", - " output_shape=len(class_names)).to(device)\n", - "model_1" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "vBD-yWcp9QVT" - }, - "source": [ - "Do a dummy forward pass on `model_1`." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" }, - "id": "NCQWeeMj9kHE", - "outputId": "cb33c4ec-4848-423c-aea3-89a131e07cfd" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Single image shape: torch.Size([1, 3, 64, 64])\n", - "\n", - "Output logits:\n", - "tensor([[ 0.0208, -0.0019, 0.0095]], device='cuda:0')\n", - "\n", - "Output prediction probabilities:\n", - "tensor([[0.3371, 0.3295, 0.3333]], device='cuda:0')\n", - "\n", - "Output prediction label:\n", - "tensor([0], device='cuda:0')\n", - "\n", - "Actual label:\n", - "0\n" - ] - } - ], - "source": [ - "# 1. Get a batch of images and labels from the DataLoader\n", - "img_batch, label_batch = next(iter(train_dataloader))\n", - "\n", - "# 2. Get a single image from the batch and unsqueeze the image so its shape fits the model\n", - "img_single, label_single = img_batch[0].unsqueeze(dim=0), label_batch[0]\n", - "print(f\"Single image shape: {img_single.shape}\\n\")\n", - "\n", - "# 3. Perform a forward pass on a single image\n", - "model_1.eval()\n", - "with torch.inference_mode():\n", - " pred = model_1(img_single.to(device))\n", - " \n", - "# 4. Print out what's happening and convert model logits -> pred probs -> pred label\n", - "print(f\"Output logits:\\n{pred}\\n\")\n", - "print(f\"Output prediction probabilities:\\n{torch.softmax(pred, dim=1)}\\n\")\n", - "print(f\"Output prediction label:\\n{torch.argmax(torch.softmax(pred, dim=1), dim=1)}\\n\")\n", - "print(f\"Actual label:\\n{label_single}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "8OB4xIsXcHrp" - }, - "source": [ - "## 4. Creating `train_step()` and `test_step()` functions and `train()` to combine them \n", - "\n", - "Rather than writing them again, we can reuse the `train_step()` and `test_step()` functions from [notebook 04](https://www.learnpytorch.io/04_pytorch_custom_datasets/#75-create-train-test-loop-functions).\n", - "\n", - "The same goes for the `train()` function we created.\n", - "\n", - "The only difference here is that these functions have had docstrings added to them in [Google's Python Functions and Methods Style Guide](https://google.github.io/styleguide/pyguide.html#383-functions-and-methods).\n", - "\n", - "Let's start by making `train_step()`." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": { - "id": "t_0zRYZecJZ6" - }, - "outputs": [], - "source": [ - "from typing import Tuple\n", - "\n", - "def train_step(model: torch.nn.Module, \n", - " dataloader: torch.utils.data.DataLoader, \n", - " loss_fn: torch.nn.Module, \n", - " optimizer: torch.optim.Optimizer,\n", - " device: torch.device) -> Tuple[float, float]:\n", - " \"\"\"Trains a PyTorch model for a single epoch.\n", - "\n", - " Turns a target PyTorch model to training mode and then\n", - " runs through all of the required training steps (forward\n", - " pass, loss calculation, optimizer step).\n", - "\n", - " Args:\n", - " model: A PyTorch model to be trained.\n", - " dataloader: A DataLoader instance for the model to be trained on.\n", - " loss_fn: A PyTorch loss function to minimize.\n", - " optimizer: A PyTorch optimizer to help minimize the loss function.\n", - " device: A target device to compute on (e.g. \"cuda\" or \"cpu\").\n", - "\n", - " Returns:\n", - " A tuple of training loss and training accuracy metrics.\n", - " In the form (train_loss, train_accuracy). For example:\n", - "\n", - " (0.1112, 0.8743)\n", - " \"\"\"\n", - " # Put model in train mode\n", - " model.train()\n", - "\n", - " # Setup train loss and train accuracy values\n", - " train_loss, train_acc = 0, 0\n", - "\n", - " # Loop through data loader data batches\n", - " for batch, (X, y) in enumerate(dataloader):\n", - " # Send data to target device\n", - " X, y = X.to(device), y.to(device)\n", - "\n", - " # 1. Forward pass\n", - " y_pred = model(X)\n", - "\n", - " # 2. Calculate and accumulate loss\n", - " loss = loss_fn(y_pred, y)\n", - " train_loss += loss.item() \n", - "\n", - " # 3. Optimizer zero grad\n", - " optimizer.zero_grad()\n", - "\n", - " # 4. Loss backward\n", - " loss.backward()\n", - "\n", - " # 5. Optimizer step\n", - " optimizer.step()\n", - "\n", - " # Calculate and accumulate accuracy metric across all batches\n", - " y_pred_class = torch.argmax(torch.softmax(y_pred, dim=1), dim=1)\n", - " train_acc += (y_pred_class == y).sum().item()/len(y_pred)\n", - "\n", - " # Adjust metrics to get average loss and accuracy per batch \n", - " train_loss = train_loss / len(dataloader)\n", - " train_acc = train_acc / len(dataloader)\n", - " return train_loss, train_acc" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "1nwAPwNh-f3k" - }, - "source": [ - "Now we'll do `test_step()`." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": { - "id": "sr_9AspYcKkW" - }, - "outputs": [], - "source": [ - "def test_step(model: torch.nn.Module, \n", - " dataloader: torch.utils.data.DataLoader, \n", - " loss_fn: torch.nn.Module,\n", - " device: torch.device) -> Tuple[float, float]:\n", - " \"\"\"Tests a PyTorch model for a single epoch.\n", - "\n", - " Turns a target PyTorch model to \"eval\" mode and then performs\n", - " a forward pass on a testing dataset.\n", - "\n", - " Args:\n", - " model: A PyTorch model to be tested.\n", - " dataloader: A DataLoader instance for the model to be tested on.\n", - " loss_fn: A PyTorch loss function to calculate loss on the test data.\n", - " device: A target device to compute on (e.g. \"cuda\" or \"cpu\").\n", - "\n", - " Returns:\n", - " A tuple of testing loss and testing accuracy metrics.\n", - " In the form (test_loss, test_accuracy). For example:\n", - "\n", - " (0.0223, 0.8985)\n", - " \"\"\"\n", - " # Put model in eval mode\n", - " model.eval() \n", - "\n", - " # Setup test loss and test accuracy values\n", - " test_loss, test_acc = 0, 0\n", - "\n", - " # Turn on inference context manager\n", - " with torch.inference_mode():\n", - " # Loop through DataLoader batches\n", - " for batch, (X, y) in enumerate(dataloader):\n", - " # Send data to target device\n", - " X, y = X.to(device), y.to(device)\n", - "\n", - " # 1. Forward pass\n", - " test_pred_logits = model(X)\n", - "\n", - " # 2. Calculate and accumulate loss\n", - " loss = loss_fn(test_pred_logits, y)\n", - " test_loss += loss.item()\n", - "\n", - " # Calculate and accumulate accuracy\n", - " test_pred_labels = test_pred_logits.argmax(dim=1)\n", - " test_acc += ((test_pred_labels == y).sum().item()/len(test_pred_labels))\n", - "\n", - " # Adjust metrics to get average loss and accuracy per batch \n", - " test_loss = test_loss / len(dataloader)\n", - " test_acc = test_acc / len(dataloader)\n", - " return test_loss, test_acc" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "weqHDd5b-iGz" - }, - "source": [ - "And we'll combine `train_step()` and `test_step()` into `train()`." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": { - "id": "-oze8b6icWE7" - }, - "outputs": [], - "source": [ - "from typing import Dict, List\n", - "\n", - "from tqdm.auto import tqdm\n", - "\n", - "def train(model: torch.nn.Module, \n", - " train_dataloader: torch.utils.data.DataLoader, \n", - " test_dataloader: torch.utils.data.DataLoader, \n", - " optimizer: torch.optim.Optimizer,\n", - " loss_fn: torch.nn.Module,\n", - " epochs: int,\n", - " device: torch.device) -> Dict[str, List[float]]:\n", - " \"\"\"Trains and tests a PyTorch model.\n", - "\n", - " Passes a target PyTorch models through train_step() and test_step()\n", - " functions for a number of epochs, training and testing the model\n", - " in the same epoch loop.\n", - "\n", - " Calculates, prints and stores evaluation metrics throughout.\n", - "\n", - " Args:\n", - " model: A PyTorch model to be trained and tested.\n", - " train_dataloader: A DataLoader instance for the model to be trained on.\n", - " test_dataloader: A DataLoader instance for the model to be tested on.\n", - " optimizer: A PyTorch optimizer to help minimize the loss function.\n", - " loss_fn: A PyTorch loss function to calculate loss on both datasets.\n", - " epochs: An integer indicating how many epochs to train for.\n", - " device: A target device to compute on (e.g. \"cuda\" or \"cpu\").\n", - "\n", - " Returns:\n", - " A dictionary of training and testing loss as well as training and\n", - " testing accuracy metrics. Each metric has a value in a list for \n", - " each epoch.\n", - " In the form: {train_loss: [...],\n", - " train_acc: [...],\n", - " test_loss: [...],\n", - " test_acc: [...]} \n", - " For example if training for epochs=2: \n", - " {train_loss: [2.0616, 1.0537],\n", - " train_acc: [0.3945, 0.3945],\n", - " test_loss: [1.2641, 1.5706],\n", - " test_acc: [0.3400, 0.2973]} \n", - " \"\"\"\n", - " # Create empty results dictionary\n", - " results = {\"train_loss\": [],\n", - " \"train_acc\": [],\n", - " \"test_loss\": [],\n", - " \"test_acc\": []\n", - " }\n", - "\n", - " # Loop through training and testing steps for a number of epochs\n", - " for epoch in tqdm(range(epochs)):\n", - " train_loss, train_acc = train_step(model=model,\n", - " dataloader=train_dataloader,\n", - " loss_fn=loss_fn,\n", - " optimizer=optimizer,\n", - " device=device)\n", - " test_loss, test_acc = test_step(model=model,\n", - " dataloader=test_dataloader,\n", - " loss_fn=loss_fn,\n", - " device=device)\n", - "\n", - " # Print out what's happening\n", - " print(\n", - " f\"Epoch: {epoch+1} | \"\n", - " f\"train_loss: {train_loss:.4f} | \"\n", - " f\"train_acc: {train_acc:.4f} | \"\n", - " f\"test_loss: {test_loss:.4f} | \"\n", - " f\"test_acc: {test_acc:.4f}\"\n", - " )\n", - "\n", - " # Update results dictionary\n", - " results[\"train_loss\"].append(train_loss)\n", - " results[\"train_acc\"].append(train_acc)\n", - " results[\"test_loss\"].append(test_loss)\n", - " results[\"test_acc\"].append(test_acc)\n", - "\n", - " # Return the filled results at the end of the epochs\n", - " return results" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "tw_nYa4kfQeI" - }, - "source": [ - "### 4.1 Creating `train_step()` and `test_step()` functions and `train()` to combine them (script mode) \n", - "\n", - "To create a script for `train_step()`, `test_step()` and `train()`, we'll combine their code all into a single cell.\n", - "\n", - "We'll then write that cell to a file called `engine.py` because these functions will be the \"engine\" of our training pipeline.\n", - "\n", - "We can do so with the magic line `%%writefile going_modular/engine.py`.\n", - "\n", - "We'll also make sure to put all the imports we need (`torch`, `typing`, and `tqdm`) at the top of the cell." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "DaOJOd-QbOQk", + "outputId": "dc7168b3-8bee-40c4-867e-58aa058175b5" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train data:\n", + "Dataset ImageFolder\n", + " Number of datapoints: 225\n", + " Root location: data/pizza_steak_sushi/train\n", + " StandardTransform\n", + "Transform: Compose(\n", + " Resize(size=(64, 64), interpolation=bilinear, max_size=None, antialias=None)\n", + " ToTensor()\n", + " )\n", + "Test data:\n", + "Dataset ImageFolder\n", + " Number of datapoints: 75\n", + " Root location: data/pizza_steak_sushi/test\n", + " StandardTransform\n", + "Transform: Compose(\n", + " Resize(size=(64, 64), interpolation=bilinear, max_size=None, antialias=None)\n", + " ToTensor()\n", + " )\n" + ] + } + ], + "source": [ + "from torchvision import datasets, transforms\n", + "\n", + "# Create simple transform\n", + "data_transform = transforms.Compose([\n", + " transforms.Resize((64, 64)),\n", + " transforms.ToTensor(),\n", + "])\n", + "\n", + "# Use ImageFolder to create dataset(s)\n", + "train_data = datasets.ImageFolder(root=train_dir, # target folder of images\n", + " transform=data_transform, # transforms to perform on data (images)\n", + " target_transform=None) # transforms to perform on labels (if necessary)\n", + "\n", + "test_data = datasets.ImageFolder(root=test_dir,\n", + " transform=data_transform)\n", + "\n", + "print(f\"Train data:\\n{train_data}\\nTest data:\\n{test_data}\")" + ] }, - "id": "d47bhfY3fVq5", - "outputId": "41053838-2e28-4702-d833-d8c97eed119b" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Overwriting going_modular/engine.py\n" - ] - } - ], - "source": [ - "%%writefile going_modular/engine.py\n", - "\"\"\"\n", - "Contains functions for training and testing a PyTorch model.\n", - "\"\"\"\n", - "from typing import Dict, List, Tuple\n", - "\n", - "import torch\n", - "\n", - "from tqdm.auto import tqdm\n", - "\n", - "def train_step(model: torch.nn.Module, \n", - " dataloader: torch.utils.data.DataLoader, \n", - " loss_fn: torch.nn.Module, \n", - " optimizer: torch.optim.Optimizer,\n", - " device: torch.device) -> Tuple[float, float]:\n", - " \"\"\"Trains a PyTorch model for a single epoch.\n", - "\n", - " Turns a target PyTorch model to training mode and then\n", - " runs through all of the required training steps (forward\n", - " pass, loss calculation, optimizer step).\n", - "\n", - " Args:\n", - " model: A PyTorch model to be trained.\n", - " dataloader: A DataLoader instance for the model to be trained on.\n", - " loss_fn: A PyTorch loss function to minimize.\n", - " optimizer: A PyTorch optimizer to help minimize the loss function.\n", - " device: A target device to compute on (e.g. \"cuda\" or \"cpu\").\n", - "\n", - " Returns:\n", - " A tuple of training loss and training accuracy metrics.\n", - " In the form (train_loss, train_accuracy). For example:\n", - "\n", - " (0.1112, 0.8743)\n", - " \"\"\"\n", - " # Put model in train mode\n", - " model.train()\n", - "\n", - " # Setup train loss and train accuracy values\n", - " train_loss, train_acc = 0, 0\n", - "\n", - " # Loop through data loader data batches\n", - " for batch, (X, y) in enumerate(dataloader):\n", - " # Send data to target device\n", - " X, y = X.to(device), y.to(device)\n", - "\n", - " # 1. Forward pass\n", - " y_pred = model(X)\n", - "\n", - " # 2. Calculate and accumulate loss\n", - " loss = loss_fn(y_pred, y)\n", - " train_loss += loss.item() \n", - "\n", - " # 3. Optimizer zero grad\n", - " optimizer.zero_grad()\n", - "\n", - " # 4. Loss backward\n", - " loss.backward()\n", - "\n", - " # 5. Optimizer step\n", - " optimizer.step()\n", - "\n", - " # Calculate and accumulate accuracy metric across all batches\n", - " y_pred_class = torch.argmax(torch.softmax(y_pred, dim=1), dim=1)\n", - " train_acc += (y_pred_class == y).sum().item()/len(y_pred)\n", - "\n", - " # Adjust metrics to get average loss and accuracy per batch \n", - " train_loss = train_loss / len(dataloader)\n", - " train_acc = train_acc / len(dataloader)\n", - " return train_loss, train_acc\n", - "\n", - "def test_step(model: torch.nn.Module, \n", - " dataloader: torch.utils.data.DataLoader, \n", - " loss_fn: torch.nn.Module,\n", - " device: torch.device) -> Tuple[float, float]:\n", - " \"\"\"Tests a PyTorch model for a single epoch.\n", - "\n", - " Turns a target PyTorch model to \"eval\" mode and then performs\n", - " a forward pass on a testing dataset.\n", - "\n", - " Args:\n", - " model: A PyTorch model to be tested.\n", - " dataloader: A DataLoader instance for the model to be tested on.\n", - " loss_fn: A PyTorch loss function to calculate loss on the test data.\n", - " device: A target device to compute on (e.g. \"cuda\" or \"cpu\").\n", - "\n", - " Returns:\n", - " A tuple of testing loss and testing accuracy metrics.\n", - " In the form (test_loss, test_accuracy). For example:\n", - "\n", - " (0.0223, 0.8985)\n", - " \"\"\"\n", - " # Put model in eval mode\n", - " model.eval() \n", - "\n", - " # Setup test loss and test accuracy values\n", - " test_loss, test_acc = 0, 0\n", - "\n", - " # Turn on inference context manager\n", - " with torch.inference_mode():\n", - " # Loop through DataLoader batches\n", - " for batch, (X, y) in enumerate(dataloader):\n", - " # Send data to target device\n", - " X, y = X.to(device), y.to(device)\n", - "\n", - " # 1. Forward pass\n", - " test_pred_logits = model(X)\n", - "\n", - " # 2. Calculate and accumulate loss\n", - " loss = loss_fn(test_pred_logits, y)\n", - " test_loss += loss.item()\n", - "\n", - " # Calculate and accumulate accuracy\n", - " test_pred_labels = test_pred_logits.argmax(dim=1)\n", - " test_acc += ((test_pred_labels == y).sum().item()/len(test_pred_labels))\n", - "\n", - " # Adjust metrics to get average loss and accuracy per batch \n", - " test_loss = test_loss / len(dataloader)\n", - " test_acc = test_acc / len(dataloader)\n", - " return test_loss, test_acc\n", - "\n", - "def train(model: torch.nn.Module, \n", - " train_dataloader: torch.utils.data.DataLoader, \n", - " test_dataloader: torch.utils.data.DataLoader, \n", - " optimizer: torch.optim.Optimizer,\n", - " loss_fn: torch.nn.Module,\n", - " epochs: int,\n", - " device: torch.device) -> Dict[str, List[float]]:\n", - " \"\"\"Trains and tests a PyTorch model.\n", - "\n", - " Passes a target PyTorch models through train_step() and test_step()\n", - " functions for a number of epochs, training and testing the model\n", - " in the same epoch loop.\n", - "\n", - " Calculates, prints and stores evaluation metrics throughout.\n", - "\n", - " Args:\n", - " model: A PyTorch model to be trained and tested.\n", - " train_dataloader: A DataLoader instance for the model to be trained on.\n", - " test_dataloader: A DataLoader instance for the model to be tested on.\n", - " optimizer: A PyTorch optimizer to help minimize the loss function.\n", - " loss_fn: A PyTorch loss function to calculate loss on both datasets.\n", - " epochs: An integer indicating how many epochs to train for.\n", - " device: A target device to compute on (e.g. \"cuda\" or \"cpu\").\n", - "\n", - " Returns:\n", - " A dictionary of training and testing loss as well as training and\n", - " testing accuracy metrics. Each metric has a value in a list for \n", - " each epoch.\n", - " In the form: {train_loss: [...],\n", - " train_acc: [...],\n", - " test_loss: [...],\n", - " test_acc: [...]} \n", - " For example if training for epochs=2: \n", - " {train_loss: [2.0616, 1.0537],\n", - " train_acc: [0.3945, 0.3945],\n", - " test_loss: [1.2641, 1.5706],\n", - " test_acc: [0.3400, 0.2973]} \n", - " \"\"\"\n", - " # Create empty results dictionary\n", - " results = {\"train_loss\": [],\n", - " \"train_acc\": [],\n", - " \"test_loss\": [],\n", - " \"test_acc\": []\n", - " }\n", - "\n", - " # Loop through training and testing steps for a number of epochs\n", - " for epoch in tqdm(range(epochs)):\n", - " train_loss, train_acc = train_step(model=model,\n", - " dataloader=train_dataloader,\n", - " loss_fn=loss_fn,\n", - " optimizer=optimizer,\n", - " device=device)\n", - " test_loss, test_acc = test_step(model=model,\n", - " dataloader=test_dataloader,\n", - " loss_fn=loss_fn,\n", - " device=device)\n", - "\n", - " # Print out what's happening\n", - " print(\n", - " f\"Epoch: {epoch+1} | \"\n", - " f\"train_loss: {train_loss:.4f} | \"\n", - " f\"train_acc: {train_acc:.4f} | \"\n", - " f\"test_loss: {test_loss:.4f} | \"\n", - " f\"test_acc: {test_acc:.4f}\"\n", - " )\n", - "\n", - " # Update results dictionary\n", - " results[\"train_loss\"].append(train_loss)\n", - " results[\"train_acc\"].append(train_acc)\n", - " results[\"test_loss\"].append(test_loss)\n", - " results[\"test_acc\"].append(test_acc)\n", - "\n", - " # Return the filled results at the end of the epochs\n", - " return results" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "A2nXumITc9DL" - }, - "source": [ - "## 5. Creating a function to save the model\n", - "\n", - "Let's setup a function to save our model to a directory." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": { - "id": "lu_vyjq-c933" - }, - "outputs": [], - "source": [ - "from pathlib import Path\n", - "\n", - "def save_model(model: torch.nn.Module,\n", - " target_dir: str,\n", - " model_name: str):\n", - " \"\"\"Saves a PyTorch model to a target directory.\n", - "\n", - " Args:\n", - " model: A target PyTorch model to save.\n", - " target_dir: A directory for saving the model to.\n", - " model_name: A filename for the saved model. Should include\n", - " either \".pth\" or \".pt\" as the file extension.\n", - "\n", - " Example usage:\n", - " save_model(model=model_0,\n", - " target_dir=\"models\",\n", - " model_name=\"05_going_modular_tingvgg_model.pth\")\n", - " \"\"\"\n", - " # Create target directory\n", - " target_dir_path = Path(target_dir)\n", - " target_dir_path.mkdir(parents=True,\n", - " exist_ok=True)\n", - "\n", - " # Create model save path\n", - " assert model_name.endswith(\".pth\") or model_name.endswith(\".pt\"), \"model_name should end with '.pt' or '.pth'\"\n", - " model_save_path = target_dir_path / model_name\n", - "\n", - " # Save the model state_dict()\n", - " print(f\"[INFO] Saving model to: {model_save_path}\")\n", - " torch.save(obj=model.state_dict(),\n", - " f=model_save_path)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "F7SnPGOfc_Wc" - }, - "source": [ - "### 5.1 Creating a function to save the model (script mode)\n", - "\n", - "How about we add our `save_model()` function to a script called `utils.py` which is short for \"utilities\".\n", - "\n", - "We can do so with the magic line `%%writefile going_modular/utils.py`." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "eEln0VFmbfyY", + "outputId": "c514574e-e3ab-4689-a5ee-6a2aef6a11d2" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['pizza', 'steak', 'sushi']" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Get class names as a list\n", + "class_names = train_data.classes\n", + "class_names" + ] }, - "id": "20LC4xwN_2ZT", - "outputId": "30852196-8138-4f9e-9f43-578dc6277682" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Overwriting going_modular/utils.py\n" - ] - } - ], - "source": [ - "%%writefile going_modular/utils.py\n", - "\"\"\"\n", - "Contains various utility functions for PyTorch model training and saving.\n", - "\"\"\"\n", - "from pathlib import Path\n", - "\n", - "import torch\n", - "\n", - "def save_model(model: torch.nn.Module,\n", - " target_dir: str,\n", - " model_name: str):\n", - " \"\"\"Saves a PyTorch model to a target directory.\n", - "\n", - " Args:\n", - " model: A target PyTorch model to save.\n", - " target_dir: A directory for saving the model to.\n", - " model_name: A filename for the saved model. Should include\n", - " either \".pth\" or \".pt\" as the file extension.\n", - "\n", - " Example usage:\n", - " save_model(model=model_0,\n", - " target_dir=\"models\",\n", - " model_name=\"05_going_modular_tingvgg_model.pth\")\n", - " \"\"\"\n", - " # Create target directory\n", - " target_dir_path = Path(target_dir)\n", - " target_dir_path.mkdir(parents=True,\n", - " exist_ok=True)\n", - "\n", - " # Create model save path\n", - " assert model_name.endswith(\".pth\") or model_name.endswith(\".pt\"), \"model_name should end with '.pt' or '.pth'\"\n", - " model_save_path = target_dir_path / model_name\n", - "\n", - " # Save the model state_dict()\n", - " print(f\"[INFO] Saving model to: {model_save_path}\")\n", - " torch.save(obj=model.state_dict(),\n", - " f=model_save_path)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "20ZO64U2cZY_" - }, - "source": [ - "## 6. Train, evaluate and save the model\n", - "\n", - "Let's leverage the functions we've got above to train, test and save a model to file." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 173, - "referenced_widgets": [ - "84317b054c244ee4b1bed7b2f86660b7", - "a0a72aed7e2743f8a95c6af3b6e4a457", - "b517f855551f4c53969677161952a1ac", - "4d780e75e5d24241b1f6c0203a8bd73e", - "a0caf8f4760c4935b5b9be86566ee8e0", - "83cf71b79abd4fc5ae9daaac8f586841", - "3402f79d3df643efbed25c72aac94642", - "1e79615e722f456189e07874d13564d2", - "bac27d87b4d54b6cbaf85dd7b7ecdc64", - "f9b7fbae53e84883ac281343bb6a9da9", - "4bc100911d7d4303b6ab7efb59d3420c" - ] + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "IMGTFVr8bgwE", + "outputId": "4268d11f-8899-447a-baba-7e91ade01f13" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'pizza': 0, 'steak': 1, 'sushi': 2}" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Can also get class names as a dict\n", + "class_dict = train_data.class_to_idx\n", + "class_dict" + ] }, - "id": "FjeRMiLjccVc", - "outputId": "2d9005a4-afce-4230-c758-1c6efa01a5e8" - }, - "outputs": [ { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "205f478a42ca42f2ab32113d597dc388", - "version_major": 2, - "version_minor": 0 + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "6a8IW-IYbhGX", + "outputId": "87bc60ac-3be3-499f-e0a8-aed5964a3075" }, - "text/plain": [ - " 0%| | 0/5 [00:00,\n", + " )" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Turn train and test Datasets into DataLoaders\n", + "from torch.utils.data import DataLoader\n", + "\n", + "train_dataloader = DataLoader(dataset=train_data,\n", + " batch_size=1, # how many samples per batch?\n", + " num_workers=1, # how many subprocesses to use for data loading? (higher = more)\n", + " shuffle=True) # shuffle the data?\n", + "\n", + "test_dataloader = DataLoader(dataset=test_data,\n", + " batch_size=1,\n", + " num_workers=1,\n", + " shuffle=False) # don't usually need to shuffle testing data\n", + "\n", + "train_dataloader, test_dataloader" + ] }, - "id": "8XnG2l4zf2Ei", - "outputId": "84f67ba0-ddaf-45a2-9a71-a37cf3d1ca88" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Overwriting going_modular/train.py\n" - ] - } - ], - "source": [ - "%%writefile going_modular/train.py\n", - "\"\"\"\n", - "Trains a PyTorch image classification model using device-agnostic code.\n", - "\"\"\"\n", - "\n", - "import os\n", - "\n", - "import torch\n", - "\n", - "from torchvision import transforms\n", - "\n", - "import data_setup, engine, model_builder, utils\n", - "\n", - "\n", - "# Setup hyperparameters\n", - "NUM_EPOCHS = 5\n", - "BATCH_SIZE = 32\n", - "HIDDEN_UNITS = 10\n", - "LEARNING_RATE = 0.001\n", - "\n", - "# Setup directories\n", - "train_dir = \"data/pizza_steak_sushi/train\"\n", - "test_dir = \"data/pizza_steak_sushi/test\"\n", - "\n", - "# Setup target device\n", - "device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n", - "\n", - "# Create transforms\n", - "data_transform = transforms.Compose([\n", - " transforms.Resize((64, 64)),\n", - " transforms.ToTensor()\n", - "])\n", - "\n", - "# Create DataLoaders with help from data_setup.py\n", - "train_dataloader, test_dataloader, class_names = data_setup.create_dataloaders(\n", - " train_dir=train_dir,\n", - " test_dir=test_dir,\n", - " transform=data_transform,\n", - " batch_size=BATCH_SIZE\n", - ")\n", - "\n", - "# Create model with help from model_builder.py\n", - "model = model_builder.TinyVGG(\n", - " input_shape=3,\n", - " hidden_units=HIDDEN_UNITS,\n", - " output_shape=len(class_names)\n", - ").to(device)\n", - "\n", - "# Set loss and optimizer\n", - "loss_fn = torch.nn.CrossEntropyLoss()\n", - "optimizer = torch.optim.Adam(model.parameters(),\n", - " lr=LEARNING_RATE)\n", - "\n", - "# Start training with help from engine.py\n", - "engine.train(model=model,\n", - " train_dataloader=train_dataloader,\n", - " test_dataloader=test_dataloader,\n", - " loss_fn=loss_fn,\n", - " optimizer=optimizer,\n", - " epochs=NUM_EPOCHS,\n", - " device=device)\n", - "\n", - "# Save the model with help from utils.py\n", - "utils.save_model(model=model,\n", - " target_dir=\"models\",\n", - " model_name=\"05_going_modular_script_mode_tinyvgg_model.pth\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "HHnPm-w7CjXT" - }, - "source": [ - "Now our final directory structure looks like:\n", - "```\n", - "data/\n", - " pizza_steak_sushi/\n", - " train/\n", - " pizza/\n", - " train_image_01.jpeg\n", - " train_image_02.jpeg\n", - " ...\n", - " steak/\n", - " sushi/\n", - " test/\n", - " pizza/\n", - " test_image_01.jpeg\n", - " test_image_02.jpeg\n", - " ...\n", - " steak/\n", - " sushi/\n", - "going_modular/\n", - " data_setup.py\n", - " engine.py\n", - " model_builder.py\n", - " train.py\n", - " utils.py\n", - "models/\n", - " saved_model.pth\n", - "```\n", - "\n", - "Now to put it all together!\n", - "\n", - "Let's run our `train.py` file from the command line with:\n", - "\n", - "```\n", - "!python going_modular/train.py\n", - "```\n" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "JJKRqXNGbnI5", + "outputId": "2d5320fc-ac72-4ef8-94f8-0ef283fc3a30" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Image shape: torch.Size([1, 3, 64, 64]) -> [batch_size, color_channels, height, width]\n", + "Label shape: torch.Size([1])\n" + ] + } + ], + "source": [ + "# Check out single image size/shape\n", + "img, label = next(iter(train_dataloader))\n", + "\n", + "# Batch size will now be 1, try changing the batch_size parameter above and see what happens\n", + "print(f\"Image shape: {img.shape} -> [batch_size, color_channels, height, width]\")\n", + "print(f\"Label shape: {label.shape}\")" + ] }, - "id": "6eVtanbSCjFj", - "outputId": "9c90d0d8-9b44-4558-b74d-292729eca1e4" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - " 0%| | 0/5 [00:00 None:\n", + " super().__init__()\n", + " self.conv_block_1 = nn.Sequential(\n", + " nn.Conv2d(in_channels=input_shape,\n", + " out_channels=hidden_units,\n", + " kernel_size=3,\n", + " stride=1,\n", + " padding=0),\n", + " nn.ReLU(),\n", + " nn.Conv2d(in_channels=hidden_units,\n", + " out_channels=hidden_units,\n", + " kernel_size=3,\n", + " stride=1,\n", + " padding=0),\n", + " nn.ReLU(),\n", + " nn.MaxPool2d(kernel_size=2,\n", + " stride=2)\n", + " )\n", + " self.conv_block_2 = nn.Sequential(\n", + " nn.Conv2d(hidden_units, hidden_units, kernel_size=3, padding=0),\n", + " nn.ReLU(),\n", + " nn.Conv2d(hidden_units, hidden_units, kernel_size=3, padding=0),\n", + " nn.ReLU(),\n", + " nn.MaxPool2d(2)\n", + " )\n", + " self.classifier = nn.Sequential(\n", + " nn.Flatten(),\n", + " # Where did this in_features shape come from?\n", + " # It's because each layer of our network compresses and changes the shape of our inputs data.\n", + " nn.Linear(in_features=hidden_units*13*13,\n", + " out_features=output_shape)\n", + " )\n", + "\n", + " def forward(self, x: torch.Tensor):\n", + " x = self.conv_block_1(x)\n", + " x = self.conv_block_2(x)\n", + " x = self.classifier(x)\n", + " return x\n", + " # return self.classifier(self.block_2(self.block_1(x))) # <- leverage the benefits of operator fusion" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ROZsju1d9Cbu" + }, + "source": [ + "Now let's create an instance of `TinyVGG` and put it on the target device.\n", + "\n", + "> **Note:** If you're using Google Colab, and you'd like to use a GPU (recommended), you can turn one on via going to Runtime -> Change runtime type -> Hardware accelerator -> GPU." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "tXOOAEHHc6-L", + "outputId": "3207fda6-896b-4cb5-9f08-f1e46879bf75" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "TinyVGG(\n", + " (conv_block_1): Sequential(\n", + " (0): Conv2d(3, 10, kernel_size=(3, 3), stride=(1, 1))\n", + " (1): ReLU()\n", + " (2): Conv2d(10, 10, kernel_size=(3, 3), stride=(1, 1))\n", + " (3): ReLU()\n", + " (4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n", + " )\n", + " (conv_block_2): Sequential(\n", + " (0): Conv2d(10, 10, kernel_size=(3, 3), stride=(1, 1))\n", + " (1): ReLU()\n", + " (2): Conv2d(10, 10, kernel_size=(3, 3), stride=(1, 1))\n", + " (3): ReLU()\n", + " (4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n", + " )\n", + " (classifier): Sequential(\n", + " (0): Flatten(start_dim=1, end_dim=-1)\n", + " (1): Linear(in_features=1690, out_features=3, bias=True)\n", + " )\n", + ")" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import torch\n", + "\n", + "device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n", + "\n", + "# Instantiate an instance of the model\n", + "torch.manual_seed(42)\n", + "model_0 = TinyVGG(input_shape=3, # number of color channels (3 for RGB)\n", + " hidden_units=10,\n", + " output_shape=len(train_data.classes)).to(device)\n", + "model_0" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "C1CwvPiMcEfZ" + }, + "source": [ + "Let's check out our model by doing a dummy forward pass." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "__p5G2ArcFko", + "outputId": "341f2b73-a0fa-4fdb-f458-142510dcfcca" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Single image shape: torch.Size([1, 3, 64, 64])\n", + "\n", + "Output logits:\n", + "tensor([[ 0.0208, -0.0019, 0.0095]], device='cuda:0')\n", + "\n", + "Output prediction probabilities:\n", + "tensor([[0.3371, 0.3295, 0.3333]], device='cuda:0')\n", + "\n", + "Output prediction label:\n", + "tensor([0], device='cuda:0')\n", + "\n", + "Actual label:\n", + "0\n" + ] + } + ], + "source": [ + "# 1. Get a batch of images and labels from the DataLoader\n", + "img_batch, label_batch = next(iter(train_dataloader))\n", + "\n", + "# 2. Get a single image from the batch and unsqueeze the image so its shape fits the model\n", + "img_single, label_single = img_batch[0].unsqueeze(dim=0), label_batch[0]\n", + "print(f\"Single image shape: {img_single.shape}\\n\")\n", + "\n", + "# 3. Perform a forward pass on a single image\n", + "model_0.eval()\n", + "with torch.inference_mode():\n", + " pred = model_0(img_single.to(device))\n", + "\n", + "# 4. Print out what's happening and convert model logits -> pred probs -> pred label\n", + "print(f\"Output logits:\\n{pred}\\n\")\n", + "print(f\"Output prediction probabilities:\\n{torch.softmax(pred, dim=1)}\\n\")\n", + "print(f\"Output prediction label:\\n{torch.argmax(torch.softmax(pred, dim=1), dim=1)}\\n\")\n", + "print(f\"Actual label:\\n{label_single}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "wSaDm_W7bc3y" + }, + "source": [ + "### 3.1 Making a model (TinyVGG) (script mode)\n", + "\n", + "Over the past few notebooks (notebook 03 and notebook 04), we've built the TinyVGG model a few times.\n", + "\n", + "So it makes sense to put the model into its file so we can reuse it again and again.\n", + "\n", + "Let's put our `TinyVGG()` model class into a script called `model_builder.py` with the line `%%writefile going_modular/model_builder.py`." + ] }, - "4bc100911d7d4303b6ab7efb59d3420c": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "oEH3bklL86hF", + "outputId": "b3f7e983-e51c-4e35-a991-df5ff99d63e6" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting going_modular/model_builder.py\n" + ] + } + ], + "source": [ + "%%writefile going_modular/model_builder.py\n", + "\"\"\"\n", + "Contains PyTorch model code to instantiate a TinyVGG model.\n", + "\"\"\"\n", + "import torch\n", + "\n", + "from torch import nn\n", + "\n", + "class TinyVGG(nn.Module):\n", + " \"\"\"Creates the TinyVGG architecture.\n", + "\n", + " Replicates the TinyVGG architecture from the CNN explainer website in PyTorch.\n", + " See the original architecture here: https://poloclub.github.io/cnn-explainer/\n", + "\n", + " Args:\n", + " input_shape: An integer indicating number of input channels.\n", + " hidden_units: An integer indicating number of hidden units between layers.\n", + " output_shape: An integer indicating number of output units.\n", + " \"\"\"\n", + " def __init__(self, input_shape: int, hidden_units: int, output_shape: int) -> None:\n", + " super().__init__()\n", + " self.conv_block_1 = nn.Sequential(\n", + " nn.Conv2d(in_channels=input_shape,\n", + " out_channels=hidden_units,\n", + " kernel_size=3,\n", + " stride=1,\n", + " padding=0),\n", + " nn.ReLU(),\n", + " nn.Conv2d(in_channels=hidden_units,\n", + " out_channels=hidden_units,\n", + " kernel_size=3,\n", + " stride=1,\n", + " padding=0),\n", + " nn.ReLU(),\n", + " nn.MaxPool2d(kernel_size=2,\n", + " stride=2)\n", + " )\n", + " self.conv_block_2 = nn.Sequential(\n", + " nn.Conv2d(hidden_units, hidden_units, kernel_size=3, padding=0),\n", + " nn.ReLU(),\n", + " nn.Conv2d(hidden_units, hidden_units, kernel_size=3, padding=0),\n", + " nn.ReLU(),\n", + " nn.MaxPool2d(2)\n", + " )\n", + " self.classifier = nn.Sequential(\n", + " nn.Flatten(),\n", + " # Where did this in_features shape come from?\n", + " # It's because each layer of our network compresses and changes the shape of our inputs data.\n", + " nn.Linear(in_features=hidden_units*13*13,\n", + " out_features=output_shape)\n", + " )\n", + "\n", + " def forward(self, x: torch.Tensor):\n", + " x = self.conv_block_1(x)\n", + " x = self.conv_block_2(x)\n", + " x = self.classifier(x)\n", + " return x\n", + " # return self.classifier(self.block_2(self.block_1(x))) # <- leverage the benefits of operator fusion" + ] }, - "4d780e75e5d24241b1f6c0203a8bd73e": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_f9b7fbae53e84883ac281343bb6a9da9", - "placeholder": "​", - "style": "IPY_MODEL_4bc100911d7d4303b6ab7efb59d3420c", - "value": " 5/5 [00:11<00:00, 2.23s/it]" - } + { + "cell_type": "markdown", + "metadata": { + "id": "3-WijBWM9K2c" + }, + "source": [ + "Create an instance of `TinyVGG` (from the script)." + ] }, - "83cf71b79abd4fc5ae9daaac8f586841": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "F__hbQwZ9A56", + "outputId": "4e8abd04-cbc4-4aa8-b71c-3a8ff8617407" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "TinyVGG(\n", + " (conv_block_1): Sequential(\n", + " (0): Conv2d(3, 10, kernel_size=(3, 3), stride=(1, 1))\n", + " (1): ReLU()\n", + " (2): Conv2d(10, 10, kernel_size=(3, 3), stride=(1, 1))\n", + " (3): ReLU()\n", + " (4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n", + " )\n", + " (conv_block_2): Sequential(\n", + " (0): Conv2d(10, 10, kernel_size=(3, 3), stride=(1, 1))\n", + " (1): ReLU()\n", + " (2): Conv2d(10, 10, kernel_size=(3, 3), stride=(1, 1))\n", + " (3): ReLU()\n", + " (4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n", + " )\n", + " (classifier): Sequential(\n", + " (0): Flatten(start_dim=1, end_dim=-1)\n", + " (1): Linear(in_features=1690, out_features=3, bias=True)\n", + " )\n", + ")" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import torch\n", + "\n", + "from going_modular import model_builder\n", + "\n", + "device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n", + "\n", + "# Instantiate an instance of the model from the \"model_builder.py\" script\n", + "torch.manual_seed(42)\n", + "model_1 = model_builder.TinyVGG(input_shape=3, # number of color channels (3 for RGB)\n", + " hidden_units=10,\n", + " output_shape=len(class_names)).to(device)\n", + "model_1" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "vBD-yWcp9QVT" + }, + "source": [ + "Do a dummy forward pass on `model_1`." + ] }, - "84317b054c244ee4b1bed7b2f86660b7": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HBoxModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_a0a72aed7e2743f8a95c6af3b6e4a457", - "IPY_MODEL_b517f855551f4c53969677161952a1ac", - "IPY_MODEL_4d780e75e5d24241b1f6c0203a8bd73e" + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "NCQWeeMj9kHE", + "outputId": "cb33c4ec-4848-423c-aea3-89a131e07cfd" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Single image shape: torch.Size([1, 3, 64, 64])\n", + "\n", + "Output logits:\n", + "tensor([[ 0.0208, -0.0019, 0.0095]], device='cuda:0')\n", + "\n", + "Output prediction probabilities:\n", + "tensor([[0.3371, 0.3295, 0.3333]], device='cuda:0')\n", + "\n", + "Output prediction label:\n", + "tensor([0], device='cuda:0')\n", + "\n", + "Actual label:\n", + "0\n" + ] + } ], - "layout": "IPY_MODEL_a0caf8f4760c4935b5b9be86566ee8e0" - } + "source": [ + "# 1. Get a batch of images and labels from the DataLoader\n", + "img_batch, label_batch = next(iter(train_dataloader))\n", + "\n", + "# 2. Get a single image from the batch and unsqueeze the image so its shape fits the model\n", + "img_single, label_single = img_batch[0].unsqueeze(dim=0), label_batch[0]\n", + "print(f\"Single image shape: {img_single.shape}\\n\")\n", + "\n", + "# 3. Perform a forward pass on a single image\n", + "model_1.eval()\n", + "with torch.inference_mode():\n", + " pred = model_1(img_single.to(device))\n", + "\n", + "# 4. Print out what's happening and convert model logits -> pred probs -> pred label\n", + "print(f\"Output logits:\\n{pred}\\n\")\n", + "print(f\"Output prediction probabilities:\\n{torch.softmax(pred, dim=1)}\\n\")\n", + "print(f\"Output prediction label:\\n{torch.argmax(torch.softmax(pred, dim=1), dim=1)}\\n\")\n", + "print(f\"Actual label:\\n{label_single}\")" + ] }, - "a0a72aed7e2743f8a95c6af3b6e4a457": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_83cf71b79abd4fc5ae9daaac8f586841", - "placeholder": "​", - "style": "IPY_MODEL_3402f79d3df643efbed25c72aac94642", - "value": "100%" - } + { + "cell_type": "markdown", + "metadata": { + "id": "8OB4xIsXcHrp" + }, + "source": [ + "## 4. Creating `train_step()` and `test_step()` functions and `train()` to combine them \n", + "\n", + "Rather than writing them again, we can reuse the `train_step()` and `test_step()` functions from [notebook 04](https://www.learnpytorch.io/04_pytorch_custom_datasets/#75-create-train-test-loop-functions).\n", + "\n", + "The same goes for the `train()` function we created.\n", + "\n", + "The only difference here is that these functions have had docstrings added to them in [Google's Python Functions and Methods Style Guide](https://google.github.io/styleguide/pyguide.html#383-functions-and-methods).\n", + "\n", + "Let's start by making `train_step()`." + ] }, - "a0caf8f4760c4935b5b9be86566ee8e0": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "t_0zRYZecJZ6" + }, + "outputs": [], + "source": [ + "from typing import Tuple\n", + "\n", + "def train_step(model: torch.nn.Module,\n", + " dataloader: torch.utils.data.DataLoader,\n", + " loss_fn: torch.nn.Module,\n", + " optimizer: torch.optim.Optimizer,\n", + " device: torch.device) -> Tuple[float, float]:\n", + " \"\"\"Trains a PyTorch model for a single epoch.\n", + "\n", + " Turns a target PyTorch model to training mode and then\n", + " runs through all of the required training steps (forward\n", + " pass, loss calculation, optimizer step).\n", + "\n", + " Args:\n", + " model: A PyTorch model to be trained.\n", + " dataloader: A DataLoader instance for the model to be trained on.\n", + " loss_fn: A PyTorch loss function to minimize.\n", + " optimizer: A PyTorch optimizer to help minimize the loss function.\n", + " device: A target device to compute on (e.g. \"cuda\" or \"cpu\").\n", + "\n", + " Returns:\n", + " A tuple of training loss and training accuracy metrics.\n", + " In the form (train_loss, train_accuracy). For example:\n", + "\n", + " (0.1112, 0.8743)\n", + " \"\"\"\n", + " # Put model in train mode\n", + " model.train()\n", + "\n", + " # Setup train loss and train accuracy values\n", + " train_loss, train_acc = 0, 0\n", + "\n", + " # Loop through data loader data batches\n", + " for batch, (X, y) in enumerate(dataloader):\n", + " # Send data to target device\n", + " X, y = X.to(device), y.to(device)\n", + "\n", + " # 1. Forward pass\n", + " y_pred = model(X)\n", + "\n", + " # 2. Calculate and accumulate loss\n", + " loss = loss_fn(y_pred, y)\n", + " train_loss += loss.item()\n", + "\n", + " # 3. Optimizer zero grad\n", + " optimizer.zero_grad()\n", + "\n", + " # 4. Loss backward\n", + " loss.backward()\n", + "\n", + " # 5. Optimizer step\n", + " optimizer.step()\n", + "\n", + " # Calculate and accumulate accuracy metric across all batches\n", + " y_pred_class = torch.argmax(torch.softmax(y_pred, dim=1), dim=1)\n", + " train_acc += (y_pred_class == y).sum().item()/len(y_pred)\n", + "\n", + " # Adjust metrics to get average loss and accuracy per batch\n", + " train_loss = train_loss / len(dataloader)\n", + " train_acc = train_acc / len(dataloader)\n", + " return train_loss, train_acc" + ] }, - "b517f855551f4c53969677161952a1ac": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "FloatProgressModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_1e79615e722f456189e07874d13564d2", - "max": 5, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_bac27d87b4d54b6cbaf85dd7b7ecdc64", - "value": 5 - } + { + "cell_type": "markdown", + "metadata": { + "id": "1nwAPwNh-f3k" + }, + "source": [ + "Now we'll do `test_step()`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "sr_9AspYcKkW" + }, + "outputs": [], + "source": [ + "def test_step(model: torch.nn.Module,\n", + " dataloader: torch.utils.data.DataLoader,\n", + " loss_fn: torch.nn.Module,\n", + " device: torch.device) -> Tuple[float, float]:\n", + " \"\"\"Tests a PyTorch model for a single epoch.\n", + "\n", + " Turns a target PyTorch model to \"eval\" mode and then performs\n", + " a forward pass on a testing dataset.\n", + "\n", + " Args:\n", + " model: A PyTorch model to be tested.\n", + " dataloader: A DataLoader instance for the model to be tested on.\n", + " loss_fn: A PyTorch loss function to calculate loss on the test data.\n", + " device: A target device to compute on (e.g. \"cuda\" or \"cpu\").\n", + "\n", + " Returns:\n", + " A tuple of testing loss and testing accuracy metrics.\n", + " In the form (test_loss, test_accuracy). For example:\n", + "\n", + " (0.0223, 0.8985)\n", + " \"\"\"\n", + " # Put model in eval mode\n", + " model.eval()\n", + "\n", + " # Setup test loss and test accuracy values\n", + " test_loss, test_acc = 0, 0\n", + "\n", + " # Turn on inference context manager\n", + " with torch.inference_mode():\n", + " # Loop through DataLoader batches\n", + " for batch, (X, y) in enumerate(dataloader):\n", + " # Send data to target device\n", + " X, y = X.to(device), y.to(device)\n", + "\n", + " # 1. Forward pass\n", + " test_pred_logits = model(X)\n", + "\n", + " # 2. Calculate and accumulate loss\n", + " loss = loss_fn(test_pred_logits, y)\n", + " test_loss += loss.item()\n", + "\n", + " # Calculate and accumulate accuracy\n", + " test_pred_labels = test_pred_logits.argmax(dim=1)\n", + " test_acc += ((test_pred_labels == y).sum().item()/len(test_pred_labels))\n", + "\n", + " # Adjust metrics to get average loss and accuracy per batch\n", + " test_loss = test_loss / len(dataloader)\n", + " test_acc = test_acc / len(dataloader)\n", + " return test_loss, test_acc" + ] }, - "bac27d87b4d54b6cbaf85dd7b7ecdc64": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "ProgressStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } + { + "cell_type": "markdown", + "metadata": { + "id": "weqHDd5b-iGz" + }, + "source": [ + "And we'll combine `train_step()` and `test_step()` into `train()`." + ] }, - "f9b7fbae53e84883ac281343bb6a9da9": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "-oze8b6icWE7" + }, + "outputs": [], + "source": [ + "from typing import Dict, List\n", + "\n", + "from tqdm.auto import tqdm\n", + "\n", + "def train(model: torch.nn.Module,\n", + " train_dataloader: torch.utils.data.DataLoader,\n", + " test_dataloader: torch.utils.data.DataLoader,\n", + " optimizer: torch.optim.Optimizer,\n", + " loss_fn: torch.nn.Module,\n", + " epochs: int,\n", + " device: torch.device) -> Dict[str, List[float]]:\n", + " \"\"\"Trains and tests a PyTorch model.\n", + "\n", + " Passes a target PyTorch models through train_step() and test_step()\n", + " functions for a number of epochs, training and testing the model\n", + " in the same epoch loop.\n", + "\n", + " Calculates, prints and stores evaluation metrics throughout.\n", + "\n", + " Args:\n", + " model: A PyTorch model to be trained and tested.\n", + " train_dataloader: A DataLoader instance for the model to be trained on.\n", + " test_dataloader: A DataLoader instance for the model to be tested on.\n", + " optimizer: A PyTorch optimizer to help minimize the loss function.\n", + " loss_fn: A PyTorch loss function to calculate loss on both datasets.\n", + " epochs: An integer indicating how many epochs to train for.\n", + " device: A target device to compute on (e.g. \"cuda\" or \"cpu\").\n", + "\n", + " Returns:\n", + " A dictionary of training and testing loss as well as training and\n", + " testing accuracy metrics. Each metric has a value in a list for\n", + " each epoch.\n", + " In the form: {train_loss: [...],\n", + " train_acc: [...],\n", + " test_loss: [...],\n", + " test_acc: [...]}\n", + " For example if training for epochs=2:\n", + " {train_loss: [2.0616, 1.0537],\n", + " train_acc: [0.3945, 0.3945],\n", + " test_loss: [1.2641, 1.5706],\n", + " test_acc: [0.3400, 0.2973]}\n", + " \"\"\"\n", + " # Create empty results dictionary\n", + " results = {\"train_loss\": [],\n", + " \"train_acc\": [],\n", + " \"test_loss\": [],\n", + " \"test_acc\": []\n", + " }\n", + "\n", + " # Loop through training and testing steps for a number of epochs\n", + " for epoch in tqdm(range(epochs)):\n", + " train_loss, train_acc = train_step(model=model,\n", + " dataloader=train_dataloader,\n", + " loss_fn=loss_fn,\n", + " optimizer=optimizer,\n", + " device=device)\n", + " test_loss, test_acc = test_step(model=model,\n", + " dataloader=test_dataloader,\n", + " loss_fn=loss_fn,\n", + " device=device)\n", + "\n", + " # Print out what's happening\n", + " print(\n", + " f\"Epoch: {epoch+1} | \"\n", + " f\"train_loss: {train_loss:.4f} | \"\n", + " f\"train_acc: {train_acc:.4f} | \"\n", + " f\"test_loss: {test_loss:.4f} | \"\n", + " f\"test_acc: {test_acc:.4f}\"\n", + " )\n", + "\n", + " # Update results dictionary\n", + " results[\"train_loss\"].append(train_loss)\n", + " results[\"train_acc\"].append(train_acc)\n", + " results[\"test_loss\"].append(test_loss)\n", + " results[\"test_acc\"].append(test_acc)\n", + "\n", + " # Return the filled results at the end of the epochs\n", + " return results" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "tw_nYa4kfQeI" + }, + "source": [ + "### 4.1 Creating `train_step()` and `test_step()` functions and `train()` to combine them (script mode) \n", + "\n", + "To create a script for `train_step()`, `test_step()` and `train()`, we'll combine their code all into a single cell.\n", + "\n", + "We'll then write that cell to a file called `engine.py` because these functions will be the \"engine\" of our training pipeline.\n", + "\n", + "We can do so with the magic line `%%writefile going_modular/engine.py`.\n", + "\n", + "We'll also make sure to put all the imports we need (`torch`, `typing`, and `tqdm`) at the top of the cell." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "d47bhfY3fVq5", + "outputId": "41053838-2e28-4702-d833-d8c97eed119b" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting going_modular/engine.py\n" + ] + } + ], + "source": [ + "%%writefile going_modular/engine.py\n", + "\"\"\"\n", + "Contains functions for training and testing a PyTorch model.\n", + "\"\"\"\n", + "from typing import Dict, List, Tuple\n", + "\n", + "import torch\n", + "\n", + "from tqdm.auto import tqdm\n", + "\n", + "def train_step(model: torch.nn.Module,\n", + " dataloader: torch.utils.data.DataLoader,\n", + " loss_fn: torch.nn.Module,\n", + " optimizer: torch.optim.Optimizer,\n", + " device: torch.device) -> Tuple[float, float]:\n", + " \"\"\"Trains a PyTorch model for a single epoch.\n", + "\n", + " Turns a target PyTorch model to training mode and then\n", + " runs through all of the required training steps (forward\n", + " pass, loss calculation, optimizer step).\n", + "\n", + " Args:\n", + " model: A PyTorch model to be trained.\n", + " dataloader: A DataLoader instance for the model to be trained on.\n", + " loss_fn: A PyTorch loss function to minimize.\n", + " optimizer: A PyTorch optimizer to help minimize the loss function.\n", + " device: A target device to compute on (e.g. \"cuda\" or \"cpu\").\n", + "\n", + " Returns:\n", + " A tuple of training loss and training accuracy metrics.\n", + " In the form (train_loss, train_accuracy). For example:\n", + "\n", + " (0.1112, 0.8743)\n", + " \"\"\"\n", + " # Put model in train mode\n", + " model.train()\n", + "\n", + " # Setup train loss and train accuracy values\n", + " train_loss, train_acc = 0, 0\n", + "\n", + " # Loop through data loader data batches\n", + " for batch, (X, y) in enumerate(dataloader):\n", + " # Send data to target device\n", + " X, y = X.to(device), y.to(device)\n", + "\n", + " # 1. Forward pass\n", + " y_pred = model(X)\n", + "\n", + " # 2. Calculate and accumulate loss\n", + " loss = loss_fn(y_pred, y)\n", + " train_loss += loss.item()\n", + "\n", + " # 3. Optimizer zero grad\n", + " optimizer.zero_grad()\n", + "\n", + " # 4. Loss backward\n", + " loss.backward()\n", + "\n", + " # 5. Optimizer step\n", + " optimizer.step()\n", + "\n", + " # Calculate and accumulate accuracy metric across all batches\n", + " y_pred_class = torch.argmax(torch.softmax(y_pred, dim=1), dim=1)\n", + " train_acc += (y_pred_class == y).sum().item()/len(y_pred)\n", + "\n", + " # Adjust metrics to get average loss and accuracy per batch\n", + " train_loss = train_loss / len(dataloader)\n", + " train_acc = train_acc / len(dataloader)\n", + " return train_loss, train_acc\n", + "\n", + "def test_step(model: torch.nn.Module,\n", + " dataloader: torch.utils.data.DataLoader,\n", + " loss_fn: torch.nn.Module,\n", + " device: torch.device) -> Tuple[float, float]:\n", + " \"\"\"Tests a PyTorch model for a single epoch.\n", + "\n", + " Turns a target PyTorch model to \"eval\" mode and then performs\n", + " a forward pass on a testing dataset.\n", + "\n", + " Args:\n", + " model: A PyTorch model to be tested.\n", + " dataloader: A DataLoader instance for the model to be tested on.\n", + " loss_fn: A PyTorch loss function to calculate loss on the test data.\n", + " device: A target device to compute on (e.g. \"cuda\" or \"cpu\").\n", + "\n", + " Returns:\n", + " A tuple of testing loss and testing accuracy metrics.\n", + " In the form (test_loss, test_accuracy). For example:\n", + "\n", + " (0.0223, 0.8985)\n", + " \"\"\"\n", + " # Put model in eval mode\n", + " model.eval()\n", + "\n", + " # Setup test loss and test accuracy values\n", + " test_loss, test_acc = 0, 0\n", + "\n", + " # Turn on inference context manager\n", + " with torch.inference_mode():\n", + " # Loop through DataLoader batches\n", + " for batch, (X, y) in enumerate(dataloader):\n", + " # Send data to target device\n", + " X, y = X.to(device), y.to(device)\n", + "\n", + " # 1. Forward pass\n", + " test_pred_logits = model(X)\n", + "\n", + " # 2. Calculate and accumulate loss\n", + " loss = loss_fn(test_pred_logits, y)\n", + " test_loss += loss.item()\n", + "\n", + " # Calculate and accumulate accuracy\n", + " test_pred_labels = test_pred_logits.argmax(dim=1)\n", + " test_acc += ((test_pred_labels == y).sum().item()/len(test_pred_labels))\n", + "\n", + " # Adjust metrics to get average loss and accuracy per batch\n", + " test_loss = test_loss / len(dataloader)\n", + " test_acc = test_acc / len(dataloader)\n", + " return test_loss, test_acc\n", + "\n", + "def train(model: torch.nn.Module,\n", + " train_dataloader: torch.utils.data.DataLoader,\n", + " test_dataloader: torch.utils.data.DataLoader,\n", + " optimizer: torch.optim.Optimizer,\n", + " loss_fn: torch.nn.Module,\n", + " epochs: int,\n", + " device: torch.device) -> Dict[str, List[float]]:\n", + " \"\"\"Trains and tests a PyTorch model.\n", + "\n", + " Passes a target PyTorch models through train_step() and test_step()\n", + " functions for a number of epochs, training and testing the model\n", + " in the same epoch loop.\n", + "\n", + " Calculates, prints and stores evaluation metrics throughout.\n", + "\n", + " Args:\n", + " model: A PyTorch model to be trained and tested.\n", + " train_dataloader: A DataLoader instance for the model to be trained on.\n", + " test_dataloader: A DataLoader instance for the model to be tested on.\n", + " optimizer: A PyTorch optimizer to help minimize the loss function.\n", + " loss_fn: A PyTorch loss function to calculate loss on both datasets.\n", + " epochs: An integer indicating how many epochs to train for.\n", + " device: A target device to compute on (e.g. \"cuda\" or \"cpu\").\n", + "\n", + " Returns:\n", + " A dictionary of training and testing loss as well as training and\n", + " testing accuracy metrics. Each metric has a value in a list for\n", + " each epoch.\n", + " In the form: {train_loss: [...],\n", + " train_acc: [...],\n", + " test_loss: [...],\n", + " test_acc: [...]}\n", + " For example if training for epochs=2:\n", + " {train_loss: [2.0616, 1.0537],\n", + " train_acc: [0.3945, 0.3945],\n", + " test_loss: [1.2641, 1.5706],\n", + " test_acc: [0.3400, 0.2973]}\n", + " \"\"\"\n", + " # Create empty results dictionary\n", + " results = {\"train_loss\": [],\n", + " \"train_acc\": [],\n", + " \"test_loss\": [],\n", + " \"test_acc\": []\n", + " }\n", + "\n", + " # Loop through training and testing steps for a number of epochs\n", + " for epoch in tqdm(range(epochs)):\n", + " train_loss, train_acc = train_step(model=model,\n", + " dataloader=train_dataloader,\n", + " loss_fn=loss_fn,\n", + " optimizer=optimizer,\n", + " device=device)\n", + " test_loss, test_acc = test_step(model=model,\n", + " dataloader=test_dataloader,\n", + " loss_fn=loss_fn,\n", + " device=device)\n", + "\n", + " # Print out what's happening\n", + " print(\n", + " f\"Epoch: {epoch+1} | \"\n", + " f\"train_loss: {train_loss:.4f} | \"\n", + " f\"train_acc: {train_acc:.4f} | \"\n", + " f\"test_loss: {test_loss:.4f} | \"\n", + " f\"test_acc: {test_acc:.4f}\"\n", + " )\n", + "\n", + " # Update results dictionary\n", + " results[\"train_loss\"].append(train_loss)\n", + " results[\"train_acc\"].append(train_acc)\n", + " results[\"test_loss\"].append(test_loss)\n", + " results[\"test_acc\"].append(test_acc)\n", + "\n", + " # Return the filled results at the end of the epochs\n", + " return results" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "A2nXumITc9DL" + }, + "source": [ + "## 5. Creating a function to save the model\n", + "\n", + "Let's setup a function to save our model to a directory." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "lu_vyjq-c933" + }, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "\n", + "def save_model(model: torch.nn.Module,\n", + " target_dir: str,\n", + " model_name: str):\n", + " \"\"\"Saves a PyTorch model to a target directory.\n", + "\n", + " Args:\n", + " model: A target PyTorch model to save.\n", + " target_dir: A directory for saving the model to.\n", + " model_name: A filename for the saved model. Should include\n", + " either \".pth\" or \".pt\" as the file extension.\n", + "\n", + " Example usage:\n", + " save_model(model=model_0,\n", + " target_dir=\"models\",\n", + " model_name=\"05_going_modular_tingvgg_model.pth\")\n", + " \"\"\"\n", + " # Create target directory\n", + " target_dir_path = Path(target_dir)\n", + " target_dir_path.mkdir(parents=True,\n", + " exist_ok=True)\n", + "\n", + " # Create model save path\n", + " assert model_name.endswith(\".pth\") or model_name.endswith(\".pt\"), \"model_name should end with '.pt' or '.pth'\"\n", + " model_save_path = target_dir_path / model_name\n", + "\n", + " # Save the model state_dict()\n", + " print(f\"[INFO] Saving model to: {model_save_path}\")\n", + " torch.save(obj=model.state_dict(),\n", + " f=model_save_path)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "F7SnPGOfc_Wc" + }, + "source": [ + "### 5.1 Creating a function to save the model (script mode)\n", + "\n", + "How about we add our `save_model()` function to a script called `utils.py` which is short for \"utilities\".\n", + "\n", + "We can do so with the magic line `%%writefile going_modular/utils.py`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "20LC4xwN_2ZT", + "outputId": "30852196-8138-4f9e-9f43-578dc6277682" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting going_modular/utils.py\n" + ] + } + ], + "source": [ + "%%writefile going_modular/utils.py\n", + "\"\"\"\n", + "Contains various utility functions for PyTorch model training and saving.\n", + "\"\"\"\n", + "from pathlib import Path\n", + "\n", + "import torch\n", + "\n", + "def save_model(model: torch.nn.Module,\n", + " target_dir: str,\n", + " model_name: str):\n", + " \"\"\"Saves a PyTorch model to a target directory.\n", + "\n", + " Args:\n", + " model: A target PyTorch model to save.\n", + " target_dir: A directory for saving the model to.\n", + " model_name: A filename for the saved model. Should include\n", + " either \".pth\" or \".pt\" as the file extension.\n", + "\n", + " Example usage:\n", + " save_model(model=model_0,\n", + " target_dir=\"models\",\n", + " model_name=\"05_going_modular_tingvgg_model.pth\")\n", + " \"\"\"\n", + " # Create target directory\n", + " target_dir_path = Path(target_dir)\n", + " target_dir_path.mkdir(parents=True,\n", + " exist_ok=True)\n", + "\n", + " # Create model save path\n", + " assert model_name.endswith(\".pth\") or model_name.endswith(\".pt\"), \"model_name should end with '.pt' or '.pth'\"\n", + " model_save_path = target_dir_path / model_name\n", + "\n", + " # Save the model state_dict()\n", + " print(f\"[INFO] Saving model to: {model_save_path}\")\n", + " torch.save(obj=model.state_dict(),\n", + " f=model_save_path)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "20ZO64U2cZY_" + }, + "source": [ + "## 6. Train, evaluate and save the model\n", + "\n", + "Let's leverage the functions we've got above to train, test and save a model to file." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 173, + "referenced_widgets": [ + "84317b054c244ee4b1bed7b2f86660b7", + "a0a72aed7e2743f8a95c6af3b6e4a457", + "b517f855551f4c53969677161952a1ac", + "4d780e75e5d24241b1f6c0203a8bd73e", + "a0caf8f4760c4935b5b9be86566ee8e0", + "83cf71b79abd4fc5ae9daaac8f586841", + "3402f79d3df643efbed25c72aac94642", + "1e79615e722f456189e07874d13564d2", + "bac27d87b4d54b6cbaf85dd7b7ecdc64", + "f9b7fbae53e84883ac281343bb6a9da9", + "4bc100911d7d4303b6ab7efb59d3420c", + "205f478a42ca42f2ab32113d597dc388" + ] + }, + "id": "FjeRMiLjccVc", + "outputId": "2d9005a4-afce-4230-c758-1c6efa01a5e8" + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "205f478a42ca42f2ab32113d597dc388", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + " 0%| | 0/5 [00:00\"Open" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# 05. PyTorch Going Modular Exercises\n", + "\n", + "Welcome to the 05. PyTorch Going Modular exercise template notebook.\n", + "\n", + "There are several questions in this notebook and it's your goal to answer them by writing Python and PyTorch code.\n", + "\n", + "> **Note:** There may be more than one solution to each of the exercises, don't worry too much about the *exact* right answer. Try to write some code that works first and then improve it if you can.\n", + "\n", + "## Resources and solutions\n", + "\n", + "* These exercises/solutions are based on [section 05. PyTorch Going Modular](https://www.learnpytorch.io/05_pytorch_going_modular/) of the Learn PyTorch for Deep Learning course by Zero to Mastery.\n", + "\n", + "**Solutions:**\n", + "\n", + "Try to complete the code below *before* looking at these.\n", + "\n", + "* See a live [walkthrough of the solutions (errors and all) on YouTube](https://youtu.be/ijgFhMK3pp4).\n", + "* See an example [solutions notebook for these exercises on GitHub](https://github.com/mrdbourke/pytorch-deep-learning/blob/main/extras/solutions/05_pytorch_going_modular_exercise_solutions.ipynb)." + ], + "metadata": { + "id": "zNqPNlYylluR" + } + }, + { + "cell_type": "markdown", + "source": [ + "## 1. Turn the code to get the data (from section 1. Get Data) into a Python script, such as `get_data.py`.\n", + "\n", + "* When you run the script using `python get_data.py` it should check if the data already exists and skip downloading if it does.\n", + "* If the data download is successful, you should be able to access the `pizza_steak_sushi` images from the `data` directory." + ], + "metadata": { + "id": "bicbWSrPmfTU" + } + }, + { + "cell_type": "code", + "source": [ + "%%writefile get_data.py\n", + "import os\n", + "import requests\n", + "import zipfile\n", + "\n", + "from pathlib import Path\n", + "\n", + "# Set the data directory\n", + "data_path = Path(\"data/\")\n", + "image_path = data_path/\"pizza, steak and sushi\"\n", + "\n", + "# Create the directory if it doesn't exist\n", + "if image_path.is_dir():\n", + " print(f\"{image_path} directory exist\")\n", + "else:\n", + " print(f\"{image_path} doesn't exist, creating one...\")\n", + " image_path.mkdir(parents=True, exist_ok=True)\n", + "\n", + "# Download pizza, steak and sushi images\n", + "with open(data_path/\"pizza, steak and sushi.zip\", \"wb\") as f:\n", + " request = requests.get(\"https://github.com/mrdbourke/pytorch-deep-learning/raw/main/data/pizza_steak_sushi.zip\")\n", + " print(f\"Downloading pizza, steak and sushi data\")\n", + " f.write(request.content)\n", + "\n", + "# Unzipping the image data downloaded\n", + "with zipfile.ZipFile(data_path/\"pizza, steak and sushi.zip\", \"r\") as zip_ref:\n", + " print(f\"Unzipping the pizza, steak and sushi images\")\n", + " zip_ref.extractall(image_path)\n", + "\n", + "# Remove the zip module\n", + "os.remove(data_path/\"pizza, steak and sushi.zip\")" + ], + "metadata": { + "id": "r0BCn1XIYZ8c", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "f17d3302-7131-40a5-b126-f604d9c8a6f7" + }, + "execution_count": 21, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Overwriting get_data.py\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "# Example running of get_data.py\n", + "!python get_data.py" + ], + "metadata": { + "id": "_LrUOIC-YOP9", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "d1c43bab-741d-429a-e594-74585137083c" + }, + "execution_count": 22, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "data/pizza, steak and sushi directory exist\n", + "Downloading pizza, steak and sushi data\n", + "Unzipping the pizza, steak and sushi images\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "## 2. Use [Python's `argparse` module](https://docs.python.org/3/library/argparse.html) to be able to send the `train.py` custom hyperparameter values for training procedures.\n", + "* Add an argument flag for using a different:\n", + " * Training/testing directory\n", + " * Learning rate\n", + " * Batch size\n", + " * Number of epochs to train for\n", + " * Number of hidden units in the TinyVGG model\n", + " * Keep the default values for each of the above arguments as what they already are (as in notebook 05).\n", + "* For example, you should be able to run something similar to the following line to train a TinyVGG model with a learning rate of 0.003 and a batch size of 64 for 20 epochs: `python train.py --learning_rate 0.003 batch_size 64 num_epochs 20`.\n", + "* **Note:** Since `train.py` leverages the other scripts we created in section 05, such as, `model_builder.py`, `utils.py` and `engine.py`, you'll have to make sure they're available to use too. You can find these in the [`going_modular` folder on the course GitHub](https://github.com/mrdbourke/pytorch-deep-learning/tree/main/going_modular/going_modular)." + ], + "metadata": { + "id": "zjyn7LU3mvkR" + } + }, + { + "cell_type": "code", + "source": [ + "%%writefile data_setup.py\n", + "\"\"\"\n", + "Contains functionality for creating PyTorch DataLoaders for\n", + "image classification data.\n", + "\"\"\"\n", + "import os\n", + "\n", + "from torchvision import datasets, transforms\n", + "from torch.utils.data import DataLoader\n", + "\n", + "NUM_WORKERS = os.cpu_count()\n", + "\n", + "def create_dataloaders(\n", + " train_dir: str,\n", + " test_dir: str,\n", + " transform: transforms.Compose,\n", + " batch_size: int,\n", + " num_workers: int=NUM_WORKERS\n", + "):\n", + " \"\"\"Creates training and testing DataLoaders.\n", + "\n", + " Takes in a training directory and testing directory path and turns\n", + " them into PyTorch Datasets and then into PyTorch DataLoaders.\n", + "\n", + " Args:\n", + " train_dir: Path to training directory.\n", + " test_dir: Path to testing directory.\n", + " transform: torchvision transforms to perform on training and testing data.\n", + " batch_size: Number of samples per batch in each of the DataLoaders.\n", + " num_workers: An integer for number of workers per DataLoader.\n", + "\n", + " Returns:\n", + " A tuple of (train_dataloader, test_dataloader, class_names).\n", + " Where class_names is a list of the target classes.\n", + " Example usage:\n", + " train_dataloader, test_dataloader, class_names = \\\n", + " = create_dataloaders(train_dir=path/to/train_dir,\n", + " test_dir=path/to/test_dir,\n", + " transform=some_transform,\n", + " batch_size=32,\n", + " num_workers=4)\n", + " \"\"\"\n", + " # Use ImageFolder to create dataset(s)\n", + " train_data = datasets.ImageFolder(train_dir, transform=transform)\n", + " test_data = datasets.ImageFolder(test_dir, transform=transform)\n", + "\n", + " # Get class names\n", + " class_names = train_data.classes\n", + "\n", + " # Turn images into data loaders\n", + " train_dataloader = DataLoader(\n", + " train_data,\n", + " batch_size=batch_size,\n", + " shuffle=True,\n", + " num_workers=num_workers,\n", + " pin_memory=True,\n", + " )\n", + " test_dataloader = DataLoader(\n", + " test_data,\n", + " batch_size=batch_size,\n", + " shuffle=False,\n", + " num_workers=num_workers,\n", + " pin_memory=True,\n", + " )\n", + "\n", + " return train_dataloader, test_dataloader, class_names" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "pp2Y27oOzHRn", + "outputId": "1ebc0ce0-1ad5-4641-fc8d-fddf1611859c" + }, + "execution_count": 23, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Overwriting data_setup.py\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%writefile engine.py\n", + "\"\"\"\n", + "Contains functions for training and testing a PyTorch model.\n", + "\"\"\"\n", + "import torch\n", + "\n", + "from tqdm.auto import tqdm\n", + "from typing import Dict, List, Tuple\n", + "\n", + "def train_step(model: torch.nn.Module,\n", + " dataloader: torch.utils.data.DataLoader,\n", + " loss_fn: torch.nn.Module,\n", + " optimizer: torch.optim.Optimizer,\n", + " device: torch.device) -> Tuple[float, float]:\n", + " \"\"\"Trains a PyTorch model for a single epoch.\n", + "\n", + " Turns a target PyTorch model to training mode and then\n", + " runs through all of the required training steps (forward\n", + " pass, loss calculation, optimizer step).\n", + "\n", + " Args:\n", + " model: A PyTorch model to be trained.\n", + " dataloader: A DataLoader instance for the model to be trained on.\n", + " loss_fn: A PyTorch loss function to minimize.\n", + " optimizer: A PyTorch optimizer to help minimize the loss function.\n", + " device: A target device to compute on (e.g. \"cuda\" or \"cpu\").\n", + "\n", + " Returns:\n", + " A tuple of training loss and training accuracy metrics.\n", + " In the form (train_loss, train_accuracy). For example:\n", + "\n", + " (0.1112, 0.8743)\n", + " \"\"\"\n", + " # Put model in train mode\n", + " model.train()\n", + "\n", + " # Setup train loss and train accuracy values\n", + " train_loss, train_acc = 0, 0\n", + "\n", + " # Loop through data loader data batches\n", + " for batch, (X, y) in enumerate(dataloader):\n", + " # Send data to target device\n", + " X, y = X.to(device), y.to(device)\n", + "\n", + " # 1. Forward pass\n", + " y_pred = model(X)\n", + "\n", + " # 2. Calculate and accumulate loss\n", + " loss = loss_fn(y_pred, y)\n", + " train_loss += loss.item()\n", + "\n", + " # 3. Optimizer zero grad\n", + " optimizer.zero_grad()\n", + "\n", + " # 4. Loss backward\n", + " loss.backward()\n", + "\n", + " # 5. Optimizer step\n", + " optimizer.step()\n", + "\n", + " # Calculate and accumulate accuracy metric across all batches\n", + " y_pred_class = torch.argmax(torch.softmax(y_pred, dim=1), dim=1)\n", + " train_acc += (y_pred_class == y).sum().item()/len(y_pred)\n", + "\n", + " # Adjust metrics to get average loss and accuracy per batch\n", + " train_loss = train_loss / len(dataloader)\n", + " train_acc = train_acc / len(dataloader)\n", + " return train_loss, train_acc\n", + "\n", + "def test_step(model: torch.nn.Module,\n", + " dataloader: torch.utils.data.DataLoader,\n", + " loss_fn: torch.nn.Module,\n", + " device: torch.device) -> Tuple[float, float]:\n", + " \"\"\"Tests a PyTorch model for a single epoch.\n", + "\n", + " Turns a target PyTorch model to \"eval\" mode and then performs\n", + " a forward pass on a testing dataset.\n", + "\n", + " Args:\n", + " model: A PyTorch model to be tested.\n", + " dataloader: A DataLoader instance for the model to be tested on.\n", + " loss_fn: A PyTorch loss function to calculate loss on the test data.\n", + " device: A target device to compute on (e.g. \"cuda\" or \"cpu\").\n", + "\n", + " Returns:\n", + " A tuple of testing loss and testing accuracy metrics.\n", + " In the form (test_loss, test_accuracy). For example:\n", + "\n", + " (0.0223, 0.8985)\n", + " \"\"\"\n", + " # Put model in eval mode\n", + " model.eval()\n", + "\n", + " # Setup test loss and test accuracy values\n", + " test_loss, test_acc = 0, 0\n", + "\n", + " # Turn on inference context manager\n", + " with torch.inference_mode():\n", + " # Loop through DataLoader batches\n", + " for batch, (X, y) in enumerate(dataloader):\n", + " # Send data to target device\n", + " X, y = X.to(device), y.to(device)\n", + "\n", + " # 1. Forward pass\n", + " test_pred_logits = model(X)\n", + "\n", + " # 2. Calculate and accumulate loss\n", + " loss = loss_fn(test_pred_logits, y)\n", + " test_loss += loss.item()\n", + "\n", + " # Calculate and accumulate accuracy\n", + " test_pred_labels = test_pred_logits.argmax(dim=1)\n", + " test_acc += ((test_pred_labels == y).sum().item()/len(test_pred_labels))\n", + "\n", + " # Adjust metrics to get average loss and accuracy per batch\n", + " test_loss = test_loss / len(dataloader)\n", + " test_acc = test_acc / len(dataloader)\n", + " return test_loss, test_acc\n", + "\n", + "def train(model: torch.nn.Module,\n", + " train_dataloader: torch.utils.data.DataLoader,\n", + " test_dataloader: torch.utils.data.DataLoader,\n", + " optimizer: torch.optim.Optimizer,\n", + " loss_fn: torch.nn.Module,\n", + " epochs: int,\n", + " device: torch.device) -> Dict[str, List]:\n", + " \"\"\"Trains and tests a PyTorch model.\n", + "\n", + " Passes a target PyTorch models through train_step() and test_step()\n", + " functions for a number of epochs, training and testing the model\n", + " in the same epoch loop.\n", + "\n", + " Calculates, prints and stores evaluation metrics throughout.\n", + "\n", + " Args:\n", + " model: A PyTorch model to be trained and tested.\n", + " train_dataloader: A DataLoader instance for the model to be trained on.\n", + " test_dataloader: A DataLoader instance for the model to be tested on.\n", + " optimizer: A PyTorch optimizer to help minimize the loss function.\n", + " loss_fn: A PyTorch loss function to calculate loss on both datasets.\n", + " epochs: An integer indicating how many epochs to train for.\n", + " device: A target device to compute on (e.g. \"cuda\" or \"cpu\").\n", + "\n", + " Returns:\n", + " A dictionary of training and testing loss as well as training and\n", + " testing accuracy metrics. Each metric has a value in a list for\n", + " each epoch.\n", + " In the form: {train_loss: [...],\n", + " train_acc: [...],\n", + " test_loss: [...],\n", + " test_acc: [...]}\n", + " For example if training for epochs=2:\n", + " {train_loss: [2.0616, 1.0537],\n", + " train_acc: [0.3945, 0.3945],\n", + " test_loss: [1.2641, 1.5706],\n", + " test_acc: [0.3400, 0.2973]}\n", + " \"\"\"\n", + " # Create empty results dictionary\n", + " results = {\"train_loss\": [],\n", + " \"train_acc\": [],\n", + " \"test_loss\": [],\n", + " \"test_acc\": []\n", + " }\n", + "\n", + " # Make sure model on target device\n", + " model.to(device)\n", + "\n", + " # Loop through training and testing steps for a number of epochs\n", + " for epoch in tqdm(range(epochs)):\n", + " train_loss, train_acc = train_step(model=model,\n", + " dataloader=train_dataloader,\n", + " loss_fn=loss_fn,\n", + " optimizer=optimizer,\n", + " device=device)\n", + " test_loss, test_acc = test_step(model=model,\n", + " dataloader=test_dataloader,\n", + " loss_fn=loss_fn,\n", + " device=device)\n", + "\n", + " # Print out what's happening\n", + " print(\n", + " f\"Epoch: {epoch+1} | \"\n", + " f\"train_loss: {train_loss:.4f} | \"\n", + " f\"train_acc: {train_acc:.4f} | \"\n", + " f\"test_loss: {test_loss:.4f} | \"\n", + " f\"test_acc: {test_acc:.4f}\"\n", + " )\n", + "\n", + " # Update results dictionary\n", + " results[\"train_loss\"].append(train_loss)\n", + " results[\"train_acc\"].append(train_acc)\n", + " results[\"test_loss\"].append(test_loss)\n", + " results[\"test_acc\"].append(test_acc)\n", + "\n", + " # Return the filled results at the end of the epochs\n", + " return results" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "tbgZp6KJzVfI", + "outputId": "123c3251-0a30-434b-84fe-3123116de38e" + }, + "execution_count": 24, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Overwriting engine.py\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%writefile model_builder.py\n", + "\"\"\"\n", + "Contains PyTorch model code to instantiate a TinyVGG model.\n", + "\"\"\"\n", + "import torch\n", + "from torch import nn\n", + "\n", + "class TinyVGG(nn.Module):\n", + " \"\"\"Creates the TinyVGG architecture.\n", + "\n", + " Replicates the TinyVGG architecture from the CNN explainer website in PyTorch.\n", + " See the original architecture here: https://poloclub.github.io/cnn-explainer/\n", + "\n", + " Args:\n", + " input_shape: An integer indicating number of input channels.\n", + " hidden_units: An integer indicating number of hidden units between layers.\n", + " output_shape: An integer indicating number of output units.\n", + " \"\"\"\n", + " def __init__(self, input_shape: int, hidden_units: int, output_shape: int) -> None:\n", + " super().__init__()\n", + " self.conv_block_1 = nn.Sequential(\n", + " nn.Conv2d(in_channels=input_shape,\n", + " out_channels=hidden_units,\n", + " kernel_size=3,\n", + " stride=1,\n", + " padding=0),\n", + " nn.ReLU(),\n", + " nn.Conv2d(in_channels=hidden_units,\n", + " out_channels=hidden_units,\n", + " kernel_size=3,\n", + " stride=1,\n", + " padding=0),\n", + " nn.ReLU(),\n", + " nn.MaxPool2d(kernel_size=2,\n", + " stride=2)\n", + " )\n", + " self.conv_block_2 = nn.Sequential(\n", + " nn.Conv2d(hidden_units, hidden_units, kernel_size=3, padding=0),\n", + " nn.ReLU(),\n", + " nn.Conv2d(hidden_units, hidden_units, kernel_size=3, padding=0),\n", + " nn.ReLU(),\n", + " nn.MaxPool2d(2)\n", + " )\n", + " self.classifier = nn.Sequential(\n", + " nn.Flatten(),\n", + " # Where did this in_features shape come from?\n", + " # It's because each layer of our network compresses and changes the shape of our inputs data.\n", + " nn.Linear(in_features=hidden_units*13*13,\n", + " out_features=output_shape)\n", + " )\n", + "\n", + " def forward(self, x: torch.Tensor):\n", + " x = self.conv_block_1(x)\n", + " x = self.conv_block_2(x)\n", + " x = self.classifier(x)\n", + " return x\n", + " # return self.classifier(self.block_2(self.block_1(x))) # <- leverage the benefits of operator fusion" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "yrJKqM-_zhA1", + "outputId": "7e0e1e6b-22a8-43ed-e023-52ffa9e72db9" + }, + "execution_count": 25, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Overwriting model_builder.py\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%writefile utils.py\n", + "\"\"\"\n", + "Contains various utility functions for PyTorch model training and saving.\n", + "\"\"\"\n", + "import torch\n", + "from pathlib import Path\n", + "\n", + "def save_model(model: torch.nn.Module,\n", + " target_dir: str,\n", + " model_name: str):\n", + " \"\"\"Saves a PyTorch model to a target directory.\n", + "\n", + " Args:\n", + " model: A target PyTorch model to save.\n", + " target_dir: A directory for saving the model to.\n", + " model_name: A filename for the saved model. Should include\n", + " either \".pth\" or \".pt\" as the file extension.\n", + "\n", + " Example usage:\n", + " save_model(model=model_0,\n", + " target_dir=\"models\",\n", + " model_name=\"05_going_modular_tingvgg_model.pth\")\n", + " \"\"\"\n", + " # Create target directory\n", + " target_dir_path = Path(target_dir)\n", + " target_dir_path.mkdir(parents=True,\n", + " exist_ok=True)\n", + "\n", + " # Create model save path\n", + " assert model_name.endswith(\".pth\") or model_name.endswith(\".pt\"), \"model_name should end with '.pt' or '.pth'\"\n", + " model_save_path = target_dir_path / model_name\n", + "\n", + " # Save the model state_dict()\n", + " print(f\"[INFO] Saving model to: {model_save_path}\")\n", + " torch.save(obj=model.state_dict(),\n", + " f=model_save_path)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "h3SYtoJJ1rPS", + "outputId": "0c8218f7-046d-4745-9b66-d20451b5c955" + }, + "execution_count": 26, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Overwriting utils.py\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%writefile train.py\n", + "\n", + "\"\"\"\n", + "Trains a PyTorch image classification model using device-agnostic code.\n", + "\"\"\"\n", + "\n", + "import os\n", + "import torch\n", + "import argparse\n", + "import data_setup, engine, model_builder, utils\n", + "\n", + "from torchvision import transforms\n", + "\n", + "parser = argparse.ArgumentParser(description=\"Get some hyperparameters\")\n", + "\n", + "# Get an arg for num of epochs\n", + "parser.add_argument(\"--num_epochs\",\n", + " type = int,\n", + " default = 10,\n", + " help =\"set up the number of epochs for the training\")\n", + "\n", + "parser.add_argument(\"--batch_size\",\n", + " type= int,\n", + " default = 16,\n", + " help=\"set up the size of the batch for the dataloader\")\n", + "\n", + "parser.add_argument(\"--hidden_units\",\n", + " type = int,\n", + " default = 10,\n", + " help = \"number of hidden units in the hidden layers\")\n", + "\n", + "parser.add_argument(\"--learning_rate\",\n", + " type = float,\n", + " default = 0.001,\n", + " help=\"set up the learning rate for the choosen loss function\")\n", + "\n", + "args = parser.parse_args()\n", + "\n", + "\n", + "# Get the hyperparameters\n", + "NUM_EPOCHS = args.num_epochs\n", + "BATCH_SIZE = args.batch_size\n", + "HIDDEN_UNITS = args.hidden_units\n", + "LEARNING_RATE = args.learning_rate\n", + "print(f\"[INFO] Training a model for {NUM_EPOCHS} with batch size {BATCH_SIZE} using hidden units {HIDDEN_UNITS} and lenaing rate {LEARNING_RATE}\")\n", + "\n", + "# Setup directories\n", + "train_dir = \"data/pizza, steak and sushi/train\"\n", + "test_dir = \"data/pizza, steak and sushi/test\"\n", + "\n", + "# Setup target device\n", + "device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n", + "\n", + "# Create transforms\n", + "data_transform = transforms.Compose([\n", + " transforms.Resize((64, 64)),\n", + " transforms.ToTensor()\n", + "])\n", + "\n", + "# Create DataLoaders with help from data_setup.py\n", + "train_dataloader, test_dataloader, class_names = data_setup.create_dataloaders(\n", + " train_dir=train_dir,\n", + " test_dir=test_dir,\n", + " transform=data_transform,\n", + " batch_size=BATCH_SIZE\n", + ")\n", + "\n", + "# Create model with help from model_builder.py\n", + "model = model_builder.TinyVGG(\n", + " input_shape=3,\n", + " hidden_units=HIDDEN_UNITS,\n", + " output_shape=len(class_names)\n", + ").to(device)\n", + "\n", + "# Set loss and optimizer\n", + "loss_fn = torch.nn.CrossEntropyLoss()\n", + "optimizer = torch.optim.Adam(model.parameters(),\n", + " lr=LEARNING_RATE)\n", + "\n", + "# Start training with help from engine.py\n", + "engine.train(model=model,\n", + " train_dataloader=train_dataloader,\n", + " test_dataloader=test_dataloader,\n", + " loss_fn=loss_fn,\n", + " optimizer=optimizer,\n", + " epochs=NUM_EPOCHS,\n", + " device=device)\n", + "\n", + "# Save the model with help from utils.py\n", + "utils.save_model(model=model,\n", + " target_dir=\"models\",\n", + " model_name=\"05_going_modular_script_mode_tinyvgg_model.pth\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "OVyIjxIT2AC0", + "outputId": "724086a4-c7aa-4f02-b585-156896862881" + }, + "execution_count": 27, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Overwriting train.py\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "! python train.py" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "eK55lfhy_u1p", + "outputId": "6ccb57a0-201d-4745-d44d-6fc36bda350d" + }, + "execution_count": 28, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "[INFO] Training a model for 10 with batch size 16 using hidden units 10 and lenaing rate 0.001\n", + " 0% 0/10 [00:00 prediction probabilities (using torch.softmax() for multi-class classification)\n", + " pred_probs = torch.softmax(pred_logits, dim=1)\n", + "\n", + " # Convert prediction probabilities -> prediction labels\n", + " pred_label = torch.argmax(pred_probs, dim=1)\n", + " pred_label_class = class_names[pred_label]\n", + "\n", + " print(f\"[INFO] Pred label : {pred_label_class}| Predictions porbabilities: {pred_probs.max():.3f}\")\n", + "\n", + "if __name__ == \"__main__\":\n", + " predict_on_image()" + ], + "metadata": { + "id": "HU7W6VZfYawP", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "1b721124-01dc-4f38-e05e-cba8cf0f24aa" + }, + "execution_count": 32, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Overwriting predict.py\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "# Example running of predict.py\n", + "!python predict.py --image \"data/pizza, steak and sushi/test/pizza/2111981.jpg\"" + ], + "metadata": { + "id": "Zcvw9sitIn6r", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "bb464eb9-b9b4-48f2-b94f-b417b7232d56" + }, + "execution_count": 37, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "[INFO] Predicting on data/pizza, steak and sushi/test/pizza/2111981.jpg\n", + "[INFO] Loading a model from models/05_going_modular_script_mode_tinyvgg_model.pth\n", + "[INFO] Pred label : pizza| Predictions porbabilities: 0.638\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [], + "metadata": { + "id": "uz8EZKAMm2gT" + }, + "execution_count": 31, + "outputs": [] + } + ] +} \ No newline at end of file diff --git a/going_modular/going_modular/utils.py b/going_modular/going_modular/utils.py index da1ce207..d0e1ccbc 100644 --- a/going_modular/going_modular/utils.py +++ b/going_modular/going_modular/utils.py @@ -1,8 +1,21 @@ """ Contains various utility functions for PyTorch model training and saving. + +If a function gets defined once and could be used over and over, it'll go in here """ import torch +import matplotlib.pyplot as plt +import numpy as np +import os +import zipfile +import requests + from pathlib import Path +from torch import nn + +# Walk through an image classification directory and find out how many files (images) +# are in each subdirectory. +import os def save_model(model: torch.nn.Module, target_dir: str, @@ -33,3 +46,280 @@ def save_model(model: torch.nn.Module, print(f"[INFO] Saving model to: {model_save_path}") torch.save(obj=model.state_dict(), f=model_save_path) + +def walk_through_dir(dir_path): + """ + Walks through dir_path returning its contents. + Args: + dir_path (str): target directory + + Returns: + A print out of: + number of subdiretories in dir_path + number of images (files) in each subdirectory + name of each subdirectory + """ + for dirpath, dirnames, filenames in os.walk(dir_path): + print(f"There are {len(dirnames)} directories and {len(filenames)} images in '{dirpath}'.") + +def plot_decision_boundary(model: torch.nn.Module, X: torch.Tensor, y: torch.Tensor): + """Plots decision boundaries of model predicting on X in comparison to y. + + Source - https://madewithml.com/courses/foundations/neural-networks/ (with modifications) + """ + # Put everything to CPU (works better with NumPy + Matplotlib) + model.to("cpu") + X, y = X.to("cpu"), y.to("cpu") + + # Setup prediction boundaries and grid + x_min, x_max = X[:, 0].min() - 0.1, X[:, 0].max() + 0.1 + y_min, y_max = X[:, 1].min() - 0.1, X[:, 1].max() + 0.1 + xx, yy = np.meshgrid(np.linspace(x_min, x_max, 101), np.linspace(y_min, y_max, 101)) + + # Make features + X_to_pred_on = torch.from_numpy(np.column_stack((xx.ravel(), yy.ravel()))).float() + + # Make predictions + model.eval() + with torch.inference_mode(): + y_logits = model(X_to_pred_on) + + # Test for multi-class or binary and adjust logits to prediction labels + if len(torch.unique(y)) > 2: + y_pred = torch.softmax(y_logits, dim=1).argmax(dim=1) # mutli-class + else: + y_pred = torch.round(torch.sigmoid(y_logits)) # binary + + # Reshape preds and plot + y_pred = y_pred.reshape(xx.shape).detach().numpy() + plt.contourf(xx, yy, y_pred, cmap=plt.cm.RdYlBu, alpha=0.7) + plt.scatter(X[:, 0], X[:, 1], c=y, s=40, cmap=plt.cm.RdYlBu) + plt.xlim(xx.min(), xx.max()) + plt.ylim(yy.min(), yy.max()) + + +# Plot linear data or training and test and predictions (optional) +def plot_predictions( + train_data, train_labels, test_data, test_labels, predictions=None +): + """ + Plots linear training data and test data and compares predictions. + """ + plt.figure(figsize=(6, 4)) + + # Plot training data in blue + plt.scatter(train_data, train_labels, c="b", s=4, label="Training data") + + # Plot test data in green + plt.scatter(test_data, test_labels, c="g", s=4, label="Testing data") + + if predictions is not None: + # Plot the predictions in red (predictions were made on the test data) + plt.scatter(test_data, predictions, c="r", s=4, label="Predictions") + + # Show the legend + plt.legend(prop={"size": 14}) + + +# Calculate accuracy (a classification metric) +def accuracy_fn(y_true, y_pred): + """Calculates accuracy between truth labels and predictions. + + Args: + y_true (torch.Tensor): Truth labels for predictions. + y_pred (torch.Tensor): Predictions to be compared to predictions. + + Returns: + [torch.float]: Accuracy value between y_true and y_pred, e.g. 78.45 + """ + correct = torch.eq(y_true, y_pred).sum().item() + acc = (correct / len(y_pred)) * 100 + return acc + + +def print_train_time(start, end, device=None): + """Prints difference between start and end time. + + Args: + start (float): Start time of computation (preferred in timeit format). + end (float): End time of computation. + device ([type], optional): Device that compute is running on. Defaults to None. + + Returns: + float: time between start and end in seconds (higher is longer). + """ + total_time = end - start + print(f"\nTrain time on {device}: {total_time:.3f} seconds") + return total_time + + +# Plot loss curves of a model +def plot_loss_curves(results): + """Plots training curves of a results dictionary. + + Args: + results (dict): dictionary containing list of values, e.g. + {"train_loss": [...], + "train_acc": [...], + "test_loss": [...], + "test_acc": [...]} + """ + loss = results["train_loss"] + test_loss = results["test_loss"] + + accuracy = results["train_acc"] + test_accuracy = results["test_acc"] + + epochs = range(len(results["train_loss"])) + + plt.figure(figsize=(9, 3)) + + # Plot loss + plt.subplot(1, 2, 1) + plt.plot(epochs, loss, label="train_loss") + plt.plot(epochs, test_loss, label="test_loss") + plt.title("Loss") + plt.xlabel("Epochs") + plt.legend() + + # Plot accuracy + plt.subplot(1, 2, 2) + plt.plot(epochs, accuracy, label="train_accuracy") + plt.plot(epochs, test_accuracy, label="test_accuracy") + plt.title("Accuracy") + plt.xlabel("Epochs") + plt.legend() + + +# Pred and plot image function from notebook 04 +# See creation: https://www.learnpytorch.io/04_pytorch_custom_datasets/#113-putting-custom-image-prediction-together-building-a-function +from typing import List +import torchvision + + +def pred_and_plot_image( + model: torch.nn.Module, + image_path: str, + class_names: List[str] = None, + transform=None, + device: torch.device = "cuda" if torch.cuda.is_available() else "cpu", +): + """Makes a prediction on a target image with a trained model and plots the image. + + Args: + model (torch.nn.Module): trained PyTorch image classification model. + image_path (str): filepath to target image. + class_names (List[str], optional): different class names for target image. Defaults to None. + transform (_type_, optional): transform of target image. Defaults to None. + device (torch.device, optional): target device to compute on. Defaults to "cuda" if torch.cuda.is_available() else "cpu". + + Returns: + Matplotlib plot of target image and model prediction as title. + + Example usage: + pred_and_plot_image(model=model, + image="some_image.jpeg", + class_names=["class_1", "class_2", "class_3"], + transform=torchvision.transforms.ToTensor(), + device=device) + """ + + # 1. Load in image and convert the tensor values to float32 + target_image = torchvision.io.read_image(str(image_path)).type(torch.float32) + + # 2. Divide the image pixel values by 255 to get them between [0, 1] + target_image = target_image / 255.0 + + # 3. Transform if necessary + if transform: + target_image = transform(target_image) + + # 4. Make sure the model is on the target device + model.to(device) + + # 5. Turn on model evaluation mode and inference mode + model.eval() + with torch.inference_mode(): + # Add an extra dimension to the image + target_image = target_image.unsqueeze(dim=0) + + # Make a prediction on image with an extra dimension and send it to the target device + target_image_pred = model(target_image.to(device)) + + # 6. Convert logits -> prediction probabilities (using torch.softmax() for multi-class classification) + target_image_pred_probs = torch.softmax(target_image_pred, dim=1) + + # 7. Convert prediction probabilities -> prediction labels + target_image_pred_label = torch.argmax(target_image_pred_probs, dim=1) + + # 8. Plot the image alongside the prediction and prediction probability + plt.figure(figsize=(4,4)) + plt.imshow( + target_image.squeeze().permute(1, 2, 0) + ) # make sure it's the right size for matplotlib + if class_names: + title = f"Pred: {class_names[target_image_pred_label.cpu()]} | Prob: {target_image_pred_probs.max().cpu():.3f}" + else: + title = f"Pred: {target_image_pred_label} | Prob: {target_image_pred_probs.max().cpu():.3f}" + plt.title(title) + plt.axis(False) + +def set_seeds(seed: int=42): + """Sets random sets for torch operations. + + Args: + seed (int, optional): Random seed to set. Defaults to 42. + """ + # Set the seed for general torch operations + torch.manual_seed(seed) + # Set the seed for CUDA torch operations (ones that happen on the GPU) + torch.cuda.manual_seed(seed) + +def download_data(source: str, + destination: str, + remove_source: bool = True) -> Path: + """Downloads a zipped dataset from source and unzips to destination. + + Args: + source (str): A link to a zipped file containing data. + destination (str): A target directory to unzip data to. + remove_source (bool): Whether to remove the source after downloading and extracting. + + Returns: + pathlib.Path to downloaded data. + + Example usage: + download_data(source="https://github.com/mrdbourke/pytorch-deep-learning/raw/main/data/pizza_steak_sushi.zip", + destination="pizza_steak_sushi") + """ + # Setup path to data folder + data_path = Path("data/") + image_path = data_path / destination + + # If the image folder doesn't exist, download it and prepare it... + if image_path.is_dir(): + print(f"[INFO] {image_path} directory exists, skipping download.") + else: + print(f"[INFO] Did not find {image_path} directory, creating one...") + image_path.mkdir(parents=True, exist_ok=True) + + # Download pizza, steak, sushi data + target_file = Path(source).name + with open(data_path / target_file, "wb") as f: + request = requests.get(source) + print(f"[INFO] Downloading {target_file} from {source}...") + f.write(request.content) + + # Unzip pizza, steak, sushi data + with zipfile.ZipFile(data_path / target_file, "r") as zip_ref: + print(f"[INFO] Unzipping {target_file} data...") + zip_ref.extractall(image_path) + + # Remove .zip file + if remove_source: + os.remove(data_path / target_file) + + return image_path + + +