{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "waK78bpJHqYr" }, "source": [ "**Name:** \\_\\_\\_\\_\\_\n", "\n", "**EID:** \\_\\_\\_\\_\\_" ] }, { "cell_type": "markdown", "metadata": { "id": "KzrZ4qYfHqYt" }, "source": [ "# Tutorial 7: Build a Fashion-MNIST CNN in PyTorch\n", "\n", "In this tutorial, we will use [PyTorch](https://pytorch.org/) to build a convolutional neural network (CNN) for Fashion-MNIST classification. This tutorial is by courtesy of Michael Li." ] }, { "cell_type": "markdown", "metadata": { "id": "MnxcsjlrHqYt" }, "source": [ "## Installing PyTorch\n", "PyTorch is a Python-based scientific computing package target to use the power of GPUs and to provide maximum flexibility and speed. Thus, we default that you have installed Python (version > 3.0) with [Anaconda](https://www.anaconda.com/). Here, we only introduce how to install PyTorch with CPU.\n", "\n", "### Windows\n", "It's straightforward to install it in Windows. You can choose to use a virtual environment or install it directly with root access. Type this command in the terminal\n", "\n", "> pip3 install --upgrade torch==1.3.0+cpu torchvision==0.4.1+cpu -f https://download.pytorch.org/whl/torch_stable.html\n", "\n", "### Mac\n", "Type this command in the terminal\n", "> pip3 install --upgrade torch torchvison" ] }, { "cell_type": "markdown", "metadata": { "id": "n7rXj6oTHqYt" }, "source": [ "## Import\n", "First, let’s import the necessary modules." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "executionInfo": { "elapsed": 10249, "status": "ok", "timestamp": 1698825573763, "user": { "displayName": "Kede Ma", "userId": "03984196065012193056" }, "user_tz": -480 }, "id": "M0fJM0zJHqYu", "outputId": "e733db15-46d8-4072-c2ec-aa998baf378c", "pycharm": { "is_executing": false, "name": "#%%\n" } }, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# import standard PyTorch modules\n", "import torch\n", "import torch.nn as nn\n", "import torch.nn.functional as F\n", "import torch.optim as optim\n", "from torch.utils.tensorboard import SummaryWriter # TensorBoard support\n", "\n", "# import torchvision module to handle image manipulation\n", "import torchvision\n", "import torchvision.transforms as transforms\n", "\n", "# calculate train time, writing train data to files etc.\n", "import time\n", "import pandas as pd\n", "import json\n", "from IPython.display import clear_output\n", "\n", "torch.set_printoptions(linewidth=120)\n", "torch.set_grad_enabled(True) # On by default, leave it here for clarity" ] }, { "cell_type": "markdown", "metadata": { "id": "_ON0gicZHqYv" }, "source": [ "PyTorch modules are quite straight forward." ] }, { "cell_type": "markdown", "metadata": { "id": "iBlVbGlMHqYv" }, "source": [ "### `torch`" ] }, { "cell_type": "markdown", "metadata": { "id": "VKEvM9T_HqYv", "pycharm": { "name": "#%% md\n" } }, "source": [ "`torch` is the main module that holds all the things you need for Tensor computation. You can build a fully functional neural network using Tensor computation alone, but this is not what this tutorial is about. We’ll make use of the more powerful and convenient `torch.nn`, `torch.optim` and `torchvision` classes to quickly build our CNN." ] }, { "cell_type": "markdown", "metadata": { "id": "L-_4DbvTHqYv", "pycharm": { "name": "#%% md\n" } }, "source": [ "### `torch.nn` and `torch.nn.functional`" ] }, { "cell_type": "markdown", "metadata": { "id": "oFagqjEqHqYv" }, "source": [ "The `torch.nn` module provides many classes and functions to build neural networks. You can think of it as the fundamental building blocks of neural networks: models, all kinds of layers, activation functions, parameter classes, etc. It allows us to build the model like putting some LEGO set together." ] }, { "cell_type": "markdown", "metadata": { "id": "_HPKBmU6HqYv", "pycharm": { "name": "#%% md\n" } }, "source": [ "### `torch.optim`" ] }, { "cell_type": "markdown", "metadata": { "id": "RXMwg1OwHqYw" }, "source": [ "`torch.optim` offers all the optimizers like SGD, ADAM, etc., so you don’t have to write it from scratch." ] }, { "cell_type": "markdown", "metadata": { "id": "BvjQB2kPHqYw" }, "source": [ "### `torchvision`" ] }, { "cell_type": "markdown", "metadata": { "id": "Mj8trRRfHqYw" }, "source": [ "`torchvision` contains a lot of popular datasets, model architectures, and common image transformations for computer vision. We get our Fashion MNIST dataset from it and also use its transforms.\n", "\n", "`torchvision` already has the Fashion MNIST dataset. If you’re not familiar with Fashion MNIST dataset:\n", "> Fashion-MNIST is a dataset of Zalando's article images—consisting of a training set of $60,000$ examples and a test set of $10,000$ examples. Each example is a $28\\times28$ grayscale image, associated with a label from $10$ classes. We intend Fashion-MNIST to serve as a direct drop-in replacement for the original [MNIST dataset](http://yann.lecun.com/exdb/mnist/) for benchmarking machine learning algorithms. It shares the same image size and structure of training and testing splits. — [From Github](https://github.com/zalandoresearch/fashion-mnist)" ] }, { "cell_type": "markdown", "metadata": { "id": "AMK6RrhEHqYw" }, "source": [ "![./1.png](1.png)" ] }, { "cell_type": "markdown", "metadata": { "id": "MRNgig4EHqYw", "pycharm": { "name": "#%% md\n" } }, "source": [ "## Dataset" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "executionInfo": { "elapsed": 5565, "status": "ok", "timestamp": 1698825595342, "user": { "displayName": "Kede Ma", "userId": "03984196065012193056" }, "user_tz": -480 }, "id": "zAN7m64GHqYw", "outputId": "e2c0d22b-bd31-4341-8231-d9af9c59e236" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-images-idx3-ubyte.gz\n", "Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-images-idx3-ubyte.gz to ./data/FashionMNIST\\FashionMNIST\\raw\\train-images-idx3-ubyte.gz\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "2daff0ee3f564601bf365889fa53da0a", "version_major": 2, "version_minor": 0 }, "text/plain": [ " 0%| | 0/26421880 [00:00\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
runepochlossaccuracyepoch durationrun durationlrbatch_sizeshuffle
0110.6043850.7696839.43902212.6655760.010100True
1120.4163350.8451509.94507522.6674640.010100True
2130.3756950.8588679.71841932.4418260.010100True
3210.5535250.79336710.07378210.1804950.010100False
4220.3839370.8561509.96594520.2102700.010100False
5230.3553840.86735010.17879430.4511020.010100False
6310.9660710.6324179.0163969.5954260.0101000True
7320.5049120.8081508.77096218.4302200.0101000True
8330.4136160.8467008.65435827.1434200.0101000True
9410.9888110.6211508.9135999.4865700.0101000False
10420.4942450.8116838.79891918.3575340.0101000False
11430.4119370.8492008.63389027.0494030.0101000False
12510.7773930.7016509.7269359.8208930.001100True
13520.4944400.8141509.83889419.7177870.001100True
14530.4289380.84161711.17818730.9599780.001100True
15610.7745330.70403310.19584010.3047690.001100False
16620.5056170.8097179.79091420.1577230.001100False
17630.4392040.8399179.72301929.9386660.001100False
18711.4845130.5143839.3240709.8799090.0011000True
\n", "" ], "text/plain": [ " run epoch loss accuracy epoch duration run duration lr \\\n", "0 1 1 0.604385 0.769683 9.439022 12.665576 0.010 \n", "1 1 2 0.416335 0.845150 9.945075 22.667464 0.010 \n", "2 1 3 0.375695 0.858867 9.718419 32.441826 0.010 \n", "3 2 1 0.553525 0.793367 10.073782 10.180495 0.010 \n", "4 2 2 0.383937 0.856150 9.965945 20.210270 0.010 \n", "5 2 3 0.355384 0.867350 10.178794 30.451102 0.010 \n", "6 3 1 0.966071 0.632417 9.016396 9.595426 0.010 \n", "7 3 2 0.504912 0.808150 8.770962 18.430220 0.010 \n", "8 3 3 0.413616 0.846700 8.654358 27.143420 0.010 \n", "9 4 1 0.988811 0.621150 8.913599 9.486570 0.010 \n", "10 4 2 0.494245 0.811683 8.798919 18.357534 0.010 \n", "11 4 3 0.411937 0.849200 8.633890 27.049403 0.010 \n", "12 5 1 0.777393 0.701650 9.726935 9.820893 0.001 \n", "13 5 2 0.494440 0.814150 9.838894 19.717787 0.001 \n", "14 5 3 0.428938 0.841617 11.178187 30.959978 0.001 \n", "15 6 1 0.774533 0.704033 10.195840 10.304769 0.001 \n", "16 6 2 0.505617 0.809717 9.790914 20.157723 0.001 \n", "17 6 3 0.439204 0.839917 9.723019 29.938666 0.001 \n", "18 7 1 1.484513 0.514383 9.324070 9.879909 0.001 \n", "\n", " batch_size shuffle \n", "0 100 True \n", "1 100 True \n", "2 100 True \n", "3 100 False \n", "4 100 False \n", "5 100 False \n", "6 1000 True \n", "7 1000 True \n", "8 1000 True \n", "9 1000 False \n", "10 1000 False \n", "11 1000 False \n", "12 100 True \n", "13 100 True \n", "14 100 True \n", "15 100 False \n", "16 100 False \n", "17 100 False \n", "18 1000 True " ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "m = RunManager()\n", "\n", "# get all runs from params using RunBuilder class\n", "for run in RunBuilder.get_runs(params):\n", "\n", " # if params changes, following line of code should reflect the changes too\n", " network = Network()\n", " loader = torch.utils.data.DataLoader(train_set, batch_size = run.batch_size)\n", " optimizer = optim.Adam(network.parameters(), lr=run.lr)\n", "\n", " m.begin_run(run, network, loader)\n", " for epoch in range(epochs):\n", "\n", " m.begin_epoch()\n", " for batch in loader:\n", "\n", " images = batch[0]\n", " labels = batch[1]\n", " preds = network(images)\n", " loss = F.cross_entropy(preds, labels)\n", "\n", " optimizer.zero_grad()\n", " loss.backward()\n", " optimizer.step()\n", "\n", " m.track_loss(loss)\n", " m.track_num_correct(preds, labels)\n", "\n", " m.end_epoch()\n", " m.end_run()\n", "\n", "# when all runs are done, save results to files\n", "m.save('results')" ] }, { "cell_type": "markdown", "metadata": { "id": "Gi02QZFLHqY6" }, "source": [ "The above code is where real training happens. We read in the images and labels from the batch, use network class to do the forward propagation (remember the forward method above?) and get the predictions. With predictions, we can calculate the loss of this batch using `cross_entropy` function. Once the loss is calculated, we reset the gradients (otherwise PyTorch will accumulate the gradients which is not what we want) with `.zero_grad()`, do one back propagation use `loss.backward()` method to calculate all the gradients of the weights/biases. Then, we use the optimizer defined above to update the weights/biases. Now that the network is updated for the current batch, we’ll calculate the loss and number of correct predictions and accumulate/track them using `track_loss` and `track_num_correct` methods of our `RunManager` class.\n", "\n", "Once all is finished, we’ll save the results in files using `m.save('results')`.\n", "\n", "The output of the runs in the notebook looks like this:\n", "![./3.png](3.png)\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "Ly3IdhQJHqY6" }, "outputs": [], "source": [] } ], "metadata": { "anaconda-cloud": {}, "colab": { "provenance": [] }, "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.7" } }, "nbformat": 4, "nbformat_minor": 1 }