├── .gitignore ├── LICENSE ├── README.md ├── datasets ├── banner.png └── nab.zip ├── requirements.txt ├── section00_python_basics ├── Python_Basics_Exercises.ipynb ├── Tutorial1_Python_Basics.ipynb ├── Tutorial2_Python_Libraries.ipynb └── Tutorial3_Numpy.ipynb ├── section01_numpy_ml ├── data │ ├── K_means.gif │ ├── Linear_Regression.gif │ ├── faces.npy │ └── toy_data_two_moon.npz ├── notebooks │ ├── Averaged_Perceptron.ipynb │ ├── Linear_Regression_For_Classification.ipynb │ ├── Numpy_KMeans.ipynb │ └── load.py └── solutions │ ├── Averaged_Perceptron_Solutions.ipynb │ ├── Eigen_Faces.ipynb │ ├── KMeans_Basic_Solution.ipynb │ ├── KMeans_Broadcasting_Solution.ipynb │ ├── Linear_Regression_For_Classification_Solutions.ipynb │ ├── Non-Linear_Regression_For_Classification_Solutions.ipynb │ └── load.py ├── section02_pytorch_basics ├── data │ ├── K_means.gif │ ├── Linear_Regression.gif │ └── toy_data_two_moon.npz ├── notebooks │ ├── Pytorch1_KMeans.ipynb │ ├── Pytorch2_Linear_Logistic_Regression_For_Classification.ipynb │ ├── Tutorial1_Pytorch_Basics.ipynb │ └── load.py └── solutions │ ├── Pytorch1_KMeans_Solution.ipynb │ ├── Pytorch2_Linear_Logistic_Regression_For_Classification_Solution.ipynb │ └── load.py ├── section03_pytorch_mlp ├── data │ ├── MNIST.gif │ └── sine_wave.gif ├── notebooks │ ├── Pytorch0_Activation_Functions.ipynb │ ├── Pytorch1_MLP_Function_Approximation.ipynb │ └── Pytorch2_MLP_MNIST_Classification.ipynb └── solutions │ ├── Pytorch1_MLP_Function_Approximation_Solution.ipynb │ └── Pytorch2_MLP_MNIST_Classification_Solution.ipynb ├── section04_pytorch_cnn ├── data │ └── puppy.jpg ├── notebooks │ ├── Pytorch1_CNN_LeNet5.ipynb │ └── Tutorial_Convolutions.ipynb └── solutions │ ├── Network.py │ ├── Pytorch1_CNN_LeNet5_Solution.ipynb │ └── Pytorch1_CNN_LeNet5_Solution_With_Modules.ipynb ├── section05_transfer_learning ├── notebooks │ ├── Pytorch1_Transfer_Learning.ipynb │ ├── Trainer.py │ └── Tutorial_Residual_and_Skip_Connections.ipynb └── solutions │ ├── Pytorch1_Transfer_Learning_Solutions.ipynb │ └── Trainer.py ├── section06_pretraining_augmentations ├── notebooks │ ├── Pytorch1_Augmentations_and_Learning_Rates.ipynb │ ├── Pytorch2_Unsupervised_PreTraining.ipynb │ └── Trainer.py └── solutions │ ├── Pytorch1_Augmentations_and_Learning_Rates_Solutions.ipynb │ ├── Pytorch2_Unsupervised_PreTraining_Solutions.ipynb │ └── Trainer.py ├── section07_autoencoders ├── data │ └── VAE_3d.gif ├── notebooks │ ├── Pytorch1_Autoencoders.ipynb │ └── Pytorch2_Variational_Autoencoders.ipynb └── solutions │ ├── Pytorch1_Autoencoders_Solutions.ipynb │ ├── Pytorch2_Variational_Autoencoders_Solutions.ipynb │ └── Pytorch3_VQVAE.ipynb ├── section08_detection └── solutions │ ├── Datasets.py │ ├── Pytorch1_Bounding_Box_Detection_Solutions.ipynb │ ├── Pytorch2_Segmentation.ipynb │ ├── Trainer.py │ └── test_train_split.txt ├── section09_generation ├── data │ ├── GAN.jpg │ ├── MNIST_GAN_DEMO.gif │ ├── ob_bg_mask.png │ ├── ob_face_mask.png │ └── obama.png └── solutions │ ├── Extract_Features.ipynb │ ├── Pytorch1_DCGAN.ipynb │ ├── Pytorch2_Diffusion.ipynb │ ├── Pytorch3_Latent_Diffusion.ipynb │ ├── Pytorch4_Latent_Transformer_Diffusion.ipynb │ └── Unet.py ├── section10_interpretation ├── data │ ├── Breast-Cancer-Ribbon.png │ ├── breast-cancer.csv │ ├── fruit.jpg │ ├── husky_wolf1.png │ ├── husky_wolf2.png │ ├── imagenet_class_index.json │ ├── lime.png │ ├── lime_logo.jpg │ ├── penguin.jpg │ ├── puppy_kitten.jpg │ └── super_pixels.jpg └── solutions │ ├── Pytorch1_Interpretable_Explanations_Tabular.ipynb │ └── Pytorch2_Interpretable_Explanations_Images.ipynb ├── section11_rl ├── data │ ├── PPO_Clipping.png │ ├── PROCGEN.png │ └── coinrun_easy_rollout.gif ├── notebooks │ ├── Cartpole │ │ ├── Cartpole_DQN.ipynb │ │ ├── Cartpole_REINFORCE.ipynb │ │ └── Cartpole_actor_critic.ipynb │ ├── Deep_Reinforcement_Learning.pdf │ ├── Gridworlds │ │ ├── Actor_Critic_Learning.ipynb │ │ ├── Average_Returns.ipynb │ │ ├── Monte_Carlo.ipynb │ │ ├── Q_Learning_e_greedy.ipynb │ │ ├── REINFORCE_Grid_World.ipynb │ │ ├── TD_Learning.ipynb │ │ ├── TD_Learning_e_greedy.ipynb │ │ └── state_transitions.csv │ ├── PPO.pdf │ └── Reinforcement_Learning.pdf └── solutions │ └── Procgen_PPO.ipynb ├── section12_sequential ├── data │ ├── IDCJAC0009_086338_1800_Data.csv │ ├── IDCJAC0010_086338_1800_Data.csv │ └── weather.csv └── solutions │ ├── Dataset.py │ ├── Pytorch1_Autoregressive_MLP.ipynb │ ├── Pytorch2_Autoregressive_RNN.ipynb │ ├── Pytorch2_Autoregressive_RNN_NoFeedback.ipynb │ ├── Pytorch3_Autoregressive_LSTM.ipynb │ ├── Pytorch4_Autoregressive_LSTM_Seqence.ipynb │ ├── Pytorch5_LSTM_Many_To_One.ipynb │ ├── Pytorch6_LSTM_Text_Classification.ipynb │ ├── Pytorch7_LSTM_Text_Generation.ipynb │ └── Pytorch8_LSTM_Question_Answering.ipynb ├── section13_attention ├── data │ └── LSTM_Attention.jpg └── solutions │ ├── Pytorch1_Attention_Basics.ipynb │ ├── Pytorch2_LSTM_Attention_Text_Generation.ipynb │ └── Pytorch3_CNN_Self_Attention.ipynb ├── section14_transformers ├── data │ └── llm_architecture_comparison.png └── solutions │ ├── Pytorch1_Simple_Transformer_Text_Classification.ipynb │ ├── Pytorch1_Transformer_Text_Classification_Multi_Block.ipynb │ ├── Pytorch1_Transformer_Text_Classification_Multi_Block_IMDB.ipynb │ ├── Pytorch2_Transformer_Text_Generation.ipynb │ ├── Pytorch3_Transformer_Question_Answering.ipynb │ ├── Pytorch4_Vision_Transformers.ipynb │ ├── Pytorch5_Transformer_Image_Captioning.ipynb │ └── Pytorch5_Transformer_Image_Captioning_Pytorch_Layers.ipynb ├── section15_deploying_models ├── data │ ├── dog.jpg │ └── imagenet_classes.json └── solutions │ └── ONNX_Basics │ ├── Exporting_Pytorch_as_ONNX.ipynb │ ├── Running_ONNX_Model.ipynb │ └── onnx_inference.py └── section16_advanced_applications └── notebooks ├── Semantic_Clustering.ipynb └── Stock_Sentiment.ipynb /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | section/ 3 | Models/ 4 | .ipynb_checkpoints/ 5 | .idea/ 6 | .gz 7 | .pt 8 | .vscode/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 LukeDitria 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Pytorch Tutorials 2 | 3 | # Beginner Level Deep Learning Tutorials in Pytorch!
4 | Note that these tutorials expect some knowledge of deep learning concepts. While some of the concepts are explained we are mainly focusing on (in detail) how to implement them in python with Pytorch.
5 | I have compiled a list of additional resources that cover many of the concepts we look at, the YouTube series section are incredibly valuable!
6 | 7 | [Deep learning google sheets](https://docs.google.com/spreadsheets/d/1WNJmgsVrLqH522yQ47euqAuO83a4WvJe/edit?usp=sharing&ouid=115240163501200760663&rtpof=true&sd=true)
8 | If you have any good resources let me know and I can add them!
9 | If you can't find an explaination on something you want to know let me know and i'll try to find it!
10 |
11 | Some level of basic Python programming knowledge is expected.
12 | More sections to come!
13 | Let me know if you want to see anything else!
14 | 15 | ## Help support this work! 16 | Donate here!
17 | 18 | https://www.buymeacoffee.com/lukeditria 19 |
20 | 21 | ## Corresponding Videos 22 | [Pytorch Youtube Playlist](https://youtube.com/playlist?list=PLN8j_qfCJpNhhY26TQpXC5VeK-_q3YLPa&si=bMjdMvuVIX8X0yTz)
23 | [Reinforcement Learning Youtube Playlist](https://youtube.com/playlist?list=PLN8j_qfCJpNg5-6LcqGn_LZMyB99GoYba&si=1HVWNHNQOhw2GrYq)
24 | 25 | Let me know if you want to see a video on any particular section! 26 | 27 | ## Discord Server 28 | Get help in my [Discord Server](https://discord.gg/8g92X5hjYF)
29 | 30 | ## Contents (So Far!) 31 | Section 0 -> Python basics that will be expected knowledge
32 | Section 1 -> Implementing some basic Machine Learning Algorithms in Python with Numpy
33 | Section 2 -> Pytorch intro and basics, basic Machine Learning Algorithms with Pytorch
34 | Section 3 -> Multi-Layer Perceptron (MLP) for Classification and Non-Linear Regression
35 | Section 4 -> Pytorch Convolutions and CNNs
36 | Section 5 -> Pytorch Transfer Learning
37 | Section 6 -> Pytorch Tools and Training Techniques
38 | #### Applications + Advanced 39 | Section 7 -> Pytorch Autoencoders and Representation Learning
40 | Section 8 -> Pytorch Bounding Box Detection and Image Segmentation
41 | Section 9 -> Pytorch Image Generation
42 | Section 10 -> Pytorch Trained Model Interpretation
43 | Section 11 -> Pytorch Reinforcement Learning
44 | 45 | #### Sequential Data 46 | Section 12 -> Using Sequential Data
47 | Section 13 -> All about Attention
48 | Section 14 -> Transformer Time
49 | 50 | ## Contents (In Progress!) 51 | Section 15 -> Deploying Models
52 | Section 16 -> Advanced Applications
53 | 54 | ## Contents (To Come!) 55 | 56 | ## Folder layout: 57 | notebooks -> Tutorials and Skeleton code (Start here)
58 | solutions -> Skeleton code Solutions
59 | data -> Data and Images
60 | -------------------------------------------------------------------------------- /datasets/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/datasets/banner.png -------------------------------------------------------------------------------- /datasets/nab.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/datasets/nab.zip -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | albumentations==1.3.1 2 | einops==0.6.1 3 | gym==0.26.2 4 | imageio==2.28.1 5 | ipython==8.18.1 6 | lime==0.2.0.1 7 | matplotlib==3.7.1 8 | numpy==1.23.5 9 | opencv-python==4.8.0.74 10 | pandas==2.0.1 11 | Pillow==9.5.0 12 | scikit-image==0.20.0 13 | scikit-learn==1.2.2 14 | scipy==1.9.1 15 | torch==2.1.0+cu118 16 | torchtext==0.16.0 17 | torchvision==0.16.0+cu118 18 | tqdm==4.66.3 19 | -------------------------------------------------------------------------------- /section00_python_basics/Python_Basics_Exercises.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "### Exercise 1: \n", 8 | "Write a Python program to find the numbers between 1800 and 2400 (inclusive) that are multiples of both 5 and 8." 9 | ] 10 | }, 11 | { 12 | "cell_type": "code", 13 | "execution_count": null, 14 | "metadata": {}, 15 | "outputs": [], 16 | "source": [] 17 | }, 18 | { 19 | "cell_type": "markdown", 20 | "metadata": {}, 21 | "source": [ 22 | "### Exercise2:\n", 23 | "Write a Python program to construct the following pattern, using a nested for loop.\n", 24 | "\n", 25 | " * \n", 26 | " * * \n", 27 | " * * * \n", 28 | " * * * * \n", 29 | " * * * * * \n", 30 | " * * * * \n", 31 | " * * * \n", 32 | " * * \n", 33 | " *\n", 34 | "\n", 35 | "Note: to print without a newline after the text write print(\"Text to print\", end=\"\")" 36 | ] 37 | }, 38 | { 39 | "cell_type": "code", 40 | "execution_count": null, 41 | "metadata": {}, 42 | "outputs": [], 43 | "source": [] 44 | }, 45 | { 46 | "cell_type": "markdown", 47 | "metadata": {}, 48 | "source": [ 49 | "### Exercise3:\n", 50 | "Create a function that sorts the following lists of numbers from ascending or decending order" 51 | ] 52 | }, 53 | { 54 | "cell_type": "code", 55 | "execution_count": null, 56 | "metadata": {}, 57 | "outputs": [], 58 | "source": [ 59 | "def sort_list(list_of_numbers, ascending = True):\n", 60 | " #####Complete the function####\n", 61 | "\n", 62 | "print(sort_list([16, 2, 28, 19, 8, 5, 21, 12, 7]))\n", 63 | "print(sort_list([32, 8, 5, 21, 100, 21], ascending = False))\n", 64 | "print(sort_list([29]))" 65 | ] 66 | }, 67 | { 68 | "cell_type": "markdown", 69 | "metadata": {}, 70 | "source": [ 71 | "#### Exercise 4:\n", 72 | "Write a Python function to calculate the factoral of a number. Pass the number as an arguement." 73 | ] 74 | }, 75 | { 76 | "cell_type": "code", 77 | "execution_count": null, 78 | "metadata": {}, 79 | "outputs": [], 80 | "source": [ 81 | "def factorial(number):\n", 82 | " #####Complete the function####\n", 83 | "\n", 84 | " \n", 85 | " \n", 86 | "print(factorial(5))" 87 | ] 88 | }, 89 | { 90 | "cell_type": "markdown", 91 | "metadata": {}, 92 | "source": [ 93 | "### Exercise 5:\n", 94 | "Write a Python class named Circle constructed by a radius and two methods which will compute the area and the perimeter of a circle. (Use 3.14 as the value of pi)" 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": null, 100 | "metadata": {}, 101 | "outputs": [], 102 | "source": [ 103 | "class Circle():\n", 104 | " ##### Complete __init__() function here\n", 105 | " \n", 106 | " \n", 107 | " ##### Complete area method here\n", 108 | " \n", 109 | " \n", 110 | " ##### Complete perimeter method here\n", 111 | "\n", 112 | "new_circle = Circle(8)\n", 113 | "print(\"Circle area %.2f\" % new_circle.area())\n", 114 | "print(\"Circle perimeter %.2f\" % new_circle.perimeter())" 115 | ] 116 | }, 117 | { 118 | "cell_type": "markdown", 119 | "metadata": {}, 120 | "source": [ 121 | "### Exercise 6:\n", 122 | "Using the previous class create a new class that computes the volume and surface area of a cylinder by inheriting properties of the Circle class" 123 | ] 124 | }, 125 | { 126 | "cell_type": "code", 127 | "execution_count": null, 128 | "metadata": {}, 129 | "outputs": [], 130 | "source": [ 131 | "class Cylinder(Circle):\n", 132 | " ##### Complete __init__() function here\n", 133 | "\n", 134 | " ##### Complete surface area method here\n", 135 | "\n", 136 | " ##### Complete volume method here\n", 137 | "\n", 138 | "new_cylinder = Cylinder(8, 10)\n", 139 | "print(\"Cylinder surface area %.2f\" % new_cylinder.surface_area())\n", 140 | "print(\"Cylinder volume %.2f\" % new_cylinder.volume())" 141 | ] 142 | } 143 | ], 144 | "metadata": { 145 | "kernelspec": { 146 | "display_name": "Python 3", 147 | "language": "python", 148 | "name": "python3" 149 | }, 150 | "language_info": { 151 | "codemirror_mode": { 152 | "name": "ipython", 153 | "version": 3 154 | }, 155 | "file_extension": ".py", 156 | "mimetype": "text/x-python", 157 | "name": "python", 158 | "nbconvert_exporter": "python", 159 | "pygments_lexer": "ipython3", 160 | "version": "3.6.9" 161 | } 162 | }, 163 | "nbformat": 4, 164 | "nbformat_minor": 2 165 | } 166 | -------------------------------------------------------------------------------- /section01_numpy_ml/data/K_means.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section01_numpy_ml/data/K_means.gif -------------------------------------------------------------------------------- /section01_numpy_ml/data/Linear_Regression.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section01_numpy_ml/data/Linear_Regression.gif -------------------------------------------------------------------------------- /section01_numpy_ml/data/faces.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section01_numpy_ml/data/faces.npy -------------------------------------------------------------------------------- /section01_numpy_ml/data/toy_data_two_moon.npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section01_numpy_ml/data/toy_data_two_moon.npz -------------------------------------------------------------------------------- /section01_numpy_ml/notebooks/load.py: -------------------------------------------------------------------------------- 1 | 2 | import urllib.request 3 | import gzip 4 | import pickle 5 | import os 6 | import numpy as np 7 | 8 | from PIL import Image 9 | 10 | def _download(file_name): 11 | file_path = dataset_dir + "/" + file_name 12 | 13 | if os.path.exists(file_path): 14 | return 15 | 16 | print("Downloading " + file_name + " ... ") 17 | urllib.request.urlretrieve(url_base + file_name, file_path) 18 | print("Done") 19 | 20 | def download_mnist(): 21 | for v in key_file.values(): 22 | _download(v) 23 | 24 | def _load_label(file_name): 25 | file_path = dataset_dir + "/" + file_name 26 | 27 | print("Converting " + file_name + " to NumPy Array ...") 28 | with gzip.open(file_path, 'rb') as f: 29 | labels = np.frombuffer(f.read(), np.uint8, offset=8) 30 | print("Done") 31 | 32 | return labels 33 | 34 | def _load_img(file_name): 35 | file_path = dataset_dir + "/" + file_name 36 | 37 | print("Converting " + file_name + " to NumPy Array ...") 38 | with gzip.open(file_path, 'rb') as f: 39 | data = np.frombuffer(f.read(), np.uint8, offset=16) 40 | data = data.reshape(-1, img_size) 41 | print("Done") 42 | 43 | return data 44 | 45 | def _convert_numpy(): 46 | dataset = {} 47 | dataset['train_img'] = _load_img(key_file['train_img']) 48 | dataset['train_label'] = _load_label(key_file['train_label']) 49 | dataset['test_img'] = _load_img(key_file['test_img']) 50 | dataset['test_label'] = _load_label(key_file['test_label']) 51 | 52 | return dataset 53 | 54 | def init_mnist(): 55 | download_mnist() 56 | dataset = _convert_numpy() 57 | print("Creating pickle file ...") 58 | with open(save_file, 'wb') as f: 59 | pickle.dump(dataset, f, -1) 60 | print("Done") 61 | 62 | def _change_ont_hot_label(X): 63 | T = np.zeros((X.size, 10)) 64 | for idx, row in enumerate(T): 65 | row[X[idx]] = 1 66 | 67 | return T 68 | 69 | def load_mnist(normalize=True, flatten=True, one_hot_label=False): 70 | """ 71 | Parameters 72 | ---------- 73 | normalize : Normalize the pixel values 74 | flatten : Flatten the images as one array 75 | one_hot_label : Encode the labels as a one-hot array 76 | 77 | Returns 78 | ------- 79 | (Trainig Image, Training Label), (Test Image, Test Label) 80 | """ 81 | if not os.path.exists(save_file): 82 | init_mnist() 83 | 84 | with open(save_file, 'rb') as f: 85 | dataset = pickle.load(f) 86 | 87 | if normalize: 88 | for key in ('train_img', 'test_img'): 89 | dataset[key] = dataset[key].astype(np.float32) 90 | dataset[key] /= 255.0 91 | 92 | if not flatten: 93 | for key in ('train_img', 'test_img'): 94 | dataset[key] = dataset[key].reshape(-1, 1, 28, 28) 95 | 96 | if one_hot_label: 97 | dataset['train_label'] = _change_ont_hot_label(dataset['train_label']) 98 | dataset['test_label'] = _change_ont_hot_label(dataset['test_label']) 99 | 100 | return (dataset['train_img'], dataset['train_label']), (dataset['test_img'], dataset['test_label']) 101 | 102 | def img_show(img): 103 | pil_img = Image.fromarray(np.uint8(img)) 104 | pil_img.show() 105 | 106 | 107 | # Load the MNIST dataset 108 | url_base = 'https://ossci-datasets.s3.amazonaws.com/mnist/' 109 | key_file = { 110 | 'train_img':'train-images-idx3-ubyte.gz', 111 | 'train_label':'train-labels-idx1-ubyte.gz', 112 | 'test_img':'t10k-images-idx3-ubyte.gz', 113 | 'test_label':'t10k-labels-idx1-ubyte.gz' 114 | } 115 | 116 | dataset_dir = "../../datasets/MNIST/raw" 117 | save_file = dataset_dir + "/mnist.pkl" 118 | 119 | train_num = 60000 120 | test_num = 10000 121 | img_dim = (1, 28, 28) 122 | img_size = 784 123 | 124 | (train_x, train_y), (test_x, test_y) = load_mnist(normalize=False, flatten=True) 125 | 126 | 127 | -------------------------------------------------------------------------------- /section01_numpy_ml/solutions/Eigen_Faces.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Eigenfaces\n", 8 | "This notebook goes over a quick example of how we can decompose a dataset of images into a set of simple components.
\n", 9 | "[Eigenface](https://en.wikipedia.org/wiki/Eigenface)" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": null, 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "import numpy as np\n", 19 | "import matplotlib.pyplot as plt\n", 20 | "import os" 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": null, 26 | "metadata": {}, 27 | "outputs": [], 28 | "source": [ 29 | "# Define image size\n", 30 | "image_size = 48\n", 31 | "\n", 32 | "# Load image matrix\n", 33 | "img_matrix = np.load(\"../data/faces.npy\")\n", 34 | "print(img_matrix.shape)" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": null, 40 | "metadata": {}, 41 | "outputs": [], 42 | "source": [ 43 | "print(\"There are %d greyscale images\" % img_matrix.shape[0])" 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": null, 49 | "metadata": {}, 50 | "outputs": [], 51 | "source": [ 52 | "# Plot out an example image\n", 53 | "_ = plt.imshow(img_matrix[20].reshape(image_size, image_size), cmap='gray')" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": null, 59 | "metadata": {}, 60 | "outputs": [], 61 | "source": [ 62 | "# Use SVD to decompose the Image matrix\n", 63 | "# This will take a bit of time\n", 64 | "U, S, Vh = np.linalg.svd(img_matrix, full_matrices=False)" 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": null, 70 | "metadata": {}, 71 | "outputs": [], 72 | "source": [ 73 | "print(Vh.shape)" 74 | ] 75 | }, 76 | { 77 | "cell_type": "code", 78 | "execution_count": null, 79 | "metadata": {}, 80 | "outputs": [], 81 | "source": [ 82 | "print(\"There are a total of %d components\" % Vh.shape[0])" 83 | ] 84 | }, 85 | { 86 | "cell_type": "markdown", 87 | "metadata": {}, 88 | "source": [ 89 | "## Visualize the Eigenfaces\n", 90 | "Lets have a look at some of the components that we can decompose our faces into" 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": null, 96 | "metadata": {}, 97 | "outputs": [], 98 | "source": [ 99 | "# Select a few of the components\n", 100 | "imgs_indx = [0, 10, 50, 100, 200, 500, 1000, 2000]\n", 101 | "\n", 102 | "# The components of the dataset is contained in the Vh matrix\n", 103 | "# Reshape image stack to be one long row\n", 104 | "img_to_view = Vh[imgs_indx].reshape(-1, image_size, image_size).transpose(1, 0, 2).reshape(image_size, -1)\n", 105 | "\n", 106 | "# Plot images\n", 107 | "_ = plt.figure(figsize=(20, 10))\n", 108 | "_ = plt.imshow(img_to_view, cmap='gray')" 109 | ] 110 | }, 111 | { 112 | "cell_type": "markdown", 113 | "metadata": {}, 114 | "source": [ 115 | "## Reconstruct a face\n", 116 | "Every face in the dataset can be constructed by adding a certain proportion of these components together" 117 | ] 118 | }, 119 | { 120 | "cell_type": "code", 121 | "execution_count": null, 122 | "metadata": {}, 123 | "outputs": [], 124 | "source": [ 125 | "# Set the number of componets to use\n", 126 | "# max number is image_size * image_size\n", 127 | "components = min(10, image_size ** 2)\n", 128 | "\n", 129 | "# Select an image to use\n", 130 | "img_index = 1205\n", 131 | "\n", 132 | "# Reconstruct the image using a set of components\n", 133 | "# The proportion of each component that must be added together is contain in the U matrix\n", 134 | "img_eg = np.expand_dims(U[img_index, :components], 0) @ np.diag(S)[:components, :] @ Vh" 135 | ] 136 | }, 137 | { 138 | "cell_type": "code", 139 | "execution_count": null, 140 | "metadata": {}, 141 | "outputs": [], 142 | "source": [ 143 | "fig, axs = plt.subplots(1, 2)\n", 144 | "_ = fig.set_size_inches(10, 20)\n", 145 | "\n", 146 | "_ = axs[0].imshow(img_matrix[img_index].reshape(image_size, image_size), cmap='gray')\n", 147 | "_ = axs[0].set_title(\"Origional Image\")\n", 148 | "\n", 149 | "_ = axs[1].imshow(img_eg.reshape(image_size, image_size), cmap='gray')\n", 150 | "_ = axs[1].set_title(\"Reconstructed Image\")\n" 151 | ] 152 | }, 153 | { 154 | "cell_type": "code", 155 | "execution_count": null, 156 | "metadata": {}, 157 | "outputs": [], 158 | "source": [ 159 | "# Reconstruct image multiple times with different number of components \n", 160 | "img_all = np.tril(U[img_index]) @ np.diag(S) @ Vh" 161 | ] 162 | }, 163 | { 164 | "cell_type": "code", 165 | "execution_count": null, 166 | "metadata": {}, 167 | "outputs": [], 168 | "source": [ 169 | "# Select a few examples to view\n", 170 | "# Each value is the number of components used in the recostruction\n", 171 | "imgs_indx = [10, 50, 100, 500, 1000, 2000]\n", 172 | "\n", 173 | "# Reshape image stack to be one long row\n", 174 | "img_to_view = img_all[imgs_indx].reshape(-1, image_size, image_size).transpose(1, 0, 2).reshape(image_size, -1)\n", 175 | "\n", 176 | "# Plot images\n", 177 | "_ = plt.figure(figsize=(20, 10))\n", 178 | "_ = plt.imshow(img_to_view, cmap='gray')" 179 | ] 180 | }, 181 | { 182 | "cell_type": "markdown", 183 | "metadata": {}, 184 | "source": [ 185 | "## Generate Faces\n", 186 | "What happens when we randomly sample the vector that defines the scale of the components of the face?" 187 | ] 188 | }, 189 | { 190 | "cell_type": "code", 191 | "execution_count": null, 192 | "metadata": {}, 193 | "outputs": [], 194 | "source": [ 195 | "# Randomly sample a vector of values from a standard normal distribution \n", 196 | "random_sample = np.random.randn(1, image_size * image_size)\n", 197 | "\n", 198 | "# Re-scale the values based on the values from the real faces\n", 199 | "random_sample = (random_sample * U.std(0, keepdims=True)) + U.mean(0, keepdims=True)\n", 200 | "\n", 201 | "# Construct a face with these values\n", 202 | "img_eg = random_sample @ np.diag(S) @ Vh" 203 | ] 204 | }, 205 | { 206 | "cell_type": "code", 207 | "execution_count": null, 208 | "metadata": {}, 209 | "outputs": [], 210 | "source": [ 211 | "_ = plt.imshow(img_eg.reshape(image_size, image_size), cmap='gray')" 212 | ] 213 | } 214 | ], 215 | "metadata": { 216 | "kernelspec": { 217 | "display_name": "Python 3", 218 | "language": "python", 219 | "name": "python3" 220 | }, 221 | "language_info": { 222 | "codemirror_mode": { 223 | "name": "ipython", 224 | "version": 3 225 | }, 226 | "file_extension": ".py", 227 | "mimetype": "text/x-python", 228 | "name": "python", 229 | "nbconvert_exporter": "python", 230 | "pygments_lexer": "ipython3", 231 | "version": "3.6.9" 232 | } 233 | }, 234 | "nbformat": 4, 235 | "nbformat_minor": 4 236 | } 237 | -------------------------------------------------------------------------------- /section01_numpy_ml/solutions/KMeans_Basic_Solution.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "

Kmeans Clustering

\n", 8 | "\n", 9 | "With our knowledge of Python and now Numpy lets create an implementation of a famous machine learning algorithm \"K-Means Clustering\". The job of a clustering algorithm is to break a dataset into some number of \"clusters\" (groups), the number of clusters usually defined by the user. K-Means clustering works by iteratively updating a pre-defined number of cluster centers. It does this by finding the distance between each datapoint and every cluster center. Datapoints are then assigned to the cluster center they are closest to and each cluster center is updated to be the mean of the new cluster. These steps are repeated for some number of steps or until the cluster centers converge (they stop moving so much).
\n", 10 | "\n", 11 | "[For more Information on K-means](https://en.wikipedia.org/wiki/K-means_clustering)
\n", 12 | "\n", 13 | "Lets have a look at the steps of K-means clustering
\n", 14 | "1. Define the number of clusters \"k\" you want to group your data into
\n", 15 | "2. Randomly initialise k vectors with the same size as each datapoint, this is the initialisation of our cluster centers
\n", 16 | "3. Calculate the distance between each datapoint and each cluster center (using MSE or equivalent)
\n", 17 | "4. For every datapoint find the cluster center they are closest to
\n", 18 | "5. Re-calculate the cluster centers by finding the mean of every new cluster
\n", 19 | "6. Repeat steps 3-5 for n steps or until convergence" 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": null, 25 | "metadata": {}, 26 | "outputs": [], 27 | "source": [ 28 | "import matplotlib.pyplot as plt\n", 29 | "import numpy as np \n", 30 | "import time\n", 31 | "\n", 32 | "# Custom module to deal with downloading the dataset\n", 33 | "from load import test_x\n", 34 | "from tqdm.notebook import trange, tqdm" 35 | ] 36 | }, 37 | { 38 | "cell_type": "markdown", 39 | "metadata": {}, 40 | "source": [ 41 | "Using the module \"load\" that comes with this notebook, lets load our dataset
\n", 42 | "The dataset we'll be using is the MNIST dataset, a dataset of small, low-res handwritten digits. There are 60000 training images and 10000 test images divided up into 10 classes (digits 0-9). Here we will be using the test set (as it's a smaller set)" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": null, 48 | "metadata": {}, 49 | "outputs": [], 50 | "source": [ 51 | "# Number of datapoint\n", 52 | "num_img = 10000 \n", 53 | "# Number of cluster centers, 10 because the dataset contains 10 classes eg: digit 0 to 9\n", 54 | "num_means = 10 \n", 55 | "# We'll perform this many iterations of the algorithm\n", 56 | "iterations = 100\n", 57 | "# Each image is 28*28 pixels, which has been flattened to a vector 0f 784 values\n", 58 | "data_size = 28*28\n", 59 | "# The images are 8 bit greyscale images (values range from 0-255)\n", 60 | "# We'll rescale the pixel values to be between 0-1 (We don't REALLY need to do this for k-means)\n", 61 | "test_x = (test_x.astype(float) / 255)" 62 | ] 63 | }, 64 | { 65 | "cell_type": "markdown", 66 | "metadata": {}, 67 | "source": [ 68 | "

Kmeans Initialization

\n", 69 | "Here we'll initialise the cluster centers to random values by randomly sampling 10 points from the dataset" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": null, 75 | "metadata": {}, 76 | "outputs": [], 77 | "source": [ 78 | "# Randomly generate K indicies for k datapoints from the dataset (indicies need to be int)\n", 79 | "means = test_x[np.random.randint(0, num_img , num_means)]" 80 | ] 81 | }, 82 | { 83 | "cell_type": "markdown", 84 | "metadata": {}, 85 | "source": [ 86 | "Lets visualise the random means!" 87 | ] 88 | }, 89 | { 90 | "cell_type": "code", 91 | "execution_count": null, 92 | "metadata": {}, 93 | "outputs": [], 94 | "source": [ 95 | "plt.figure(1, figsize=(10, 20))\n", 96 | "_ = plt.imshow(means.reshape(num_means, 28, 28).transpose((1, 0, 2)).reshape(28, num_means*28))" 97 | ] 98 | }, 99 | { 100 | "cell_type": "markdown", 101 | "metadata": {}, 102 | "source": [ 103 | "

Kmeans Algorithm

\n", 104 | "Now implement the main steps of the K-Means clustering algorithm! Try and make it as efficient as possible and minimise the time/iteration" 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": null, 110 | "metadata": {}, 111 | "outputs": [], 112 | "source": [ 113 | "start_time = time.time()\n", 114 | "# Initialise a vector that wil contain the the cluster index for each datapoint\n", 115 | "cluster_index = np.zeros(num_img)\n", 116 | "\n", 117 | "for i in trange(iterations, leave=False): \n", 118 | "# Implement a step of k-means clustering\n", 119 | " # For every datapoint find the cluster center that it is closest to and log it in cluster_index\n", 120 | " for j in range(num_img):\n", 121 | " # Init a list to store the distance from the datapoint to each cluster center\n", 122 | " dist_to_center = []\n", 123 | " # Calculate the distance from the datapoint to each cluster center and store it in the list\n", 124 | " for k in range(num_means):\n", 125 | " dist = np.mean((test_x[j, :] - means[k, :])**2)\n", 126 | " dist_to_center.append(dist)\n", 127 | " # Find the index of the cluster center with the smallest distance to the datapoint and store it\n", 128 | " cluster_index[j] = np.argmin(dist_to_center)\n", 129 | "\n", 130 | "# Updating the cluster center positions\n", 131 | " # For every cluster find the new mean (new cluster center)\n", 132 | " for o in trange(num_means, leave=False):\n", 133 | " # initialise a counter of the number of points in the cluster\n", 134 | " count = 0\n", 135 | " # init a sum of the datapoints in the cluster\n", 136 | " cluster_sum = 0\n", 137 | " # Find all the datapoints in the cluster and sum them together \n", 138 | " # and keep track of the number of datapoints in the cluster\n", 139 | " for p in range(num_img):\n", 140 | " if cluster_index[p] == o:\n", 141 | " count+=1\n", 142 | " cluster_sum+=test_x[p,:]\n", 143 | " # Find the new mean of the cluster\n", 144 | " if count > 0:\n", 145 | " means[o, :] = cluster_sum/count\n", 146 | " \n", 147 | "end_time = time.time()\n", 148 | "print(\"%d iterations took %.2f seconds, which corresponds to %.2fs/iteration\" % (iterations, end_time - start_time, (end_time - start_time)/iterations))" 149 | ] 150 | }, 151 | { 152 | "cell_type": "markdown", 153 | "metadata": {}, 154 | "source": [ 155 | "

Lets visualise the the cluster centers!

" 156 | ] 157 | }, 158 | { 159 | "cell_type": "code", 160 | "execution_count": null, 161 | "metadata": {}, 162 | "outputs": [], 163 | "source": [ 164 | "plt.figure(1, figsize=(20, 10))\n", 165 | "img = means.reshape(num_means, 28, 28).transpose((1, 0, 2)).reshape(28, num_means*28)\n", 166 | "_ = plt.imshow(img)" 167 | ] 168 | } 169 | ], 170 | "metadata": { 171 | "kernelspec": { 172 | "display_name": "Python 3 (ipykernel)", 173 | "language": "python", 174 | "name": "python3" 175 | }, 176 | "language_info": { 177 | "codemirror_mode": { 178 | "name": "ipython", 179 | "version": 3 180 | }, 181 | "file_extension": ".py", 182 | "mimetype": "text/x-python", 183 | "name": "python", 184 | "nbconvert_exporter": "python", 185 | "pygments_lexer": "ipython3", 186 | "version": "3.9.16" 187 | } 188 | }, 189 | "nbformat": 4, 190 | "nbformat_minor": 2 191 | } 192 | -------------------------------------------------------------------------------- /section01_numpy_ml/solutions/KMeans_Broadcasting_Solution.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "

Kmeans Clustering

\n", 8 | "\n", 9 | "With our knowledge of Python and now Numpy lets create an implementation of a famous machine learning algorithm \"K-Means Clustering\". The job of a clustering algorithm is to break a dataset into some number of \"clusters\" (groups), the number of clusters usually defined by the user. K-Means clustering working by iteratively updating a pre-defined number of cluster centers. It does this by finding the distance between each datapoint and every cluster center. Datapoints are then assigned to the cluster center they are closest to and each cluster center is updated to be the mean of the new cluster. These steps are updated for some number of steps or until the cluster centers converge (they stop moving so much)
\n", 10 | "Lets have a look at the steps of K-means clustering
\n", 11 | "1. Define the number of clusters \"k\" you want to group your data into
\n", 12 | "2. Randomly initialise k vectors with the same size as each datapoint, this is the initialisation of our cluster centers
\n", 13 | "3. Calculate the distance between each datapoint and each cluster center (using MSE or equivalent)
\n", 14 | "4. For every datapoint find the cluster center they are closest to
\n", 15 | "5. Re-calculate the cluster centers by finding the mean of every new cluster
\n", 16 | "6. Repeat steps 3-6 for n steps or until convergence" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": null, 22 | "metadata": {}, 23 | "outputs": [], 24 | "source": [ 25 | "import matplotlib.pyplot as plt # This package basically adds all the matlabplotfeatures to the python\n", 26 | "import numpy as np # This package essentially make python the same as matlab\n", 27 | "from load import test_x # Load the Mnist dataset\n", 28 | "import time\n", 29 | "from tqdm.notebook import trange, tqdm" 30 | ] 31 | }, 32 | { 33 | "cell_type": "markdown", 34 | "metadata": {}, 35 | "source": [ 36 | "Using the module \"load\" that comes with this notebook, lets load our dataset
\n", 37 | "The dataset we'll be using is the MNIST dataset, a dataset of small, low-res handwritten digits. There are 60000 training images and 10000 test images divided up into 10 classes (digits 0-9). Here we will be using the test set (as it's a smaller set)" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": null, 43 | "metadata": {}, 44 | "outputs": [], 45 | "source": [ 46 | "# Number of datapoint\n", 47 | "num_img = 10000 \n", 48 | "# Number of cluster centers, 10 because the dataset contains 10 classes eg: digit 0 to 9\n", 49 | "num_means = 10\n", 50 | "# We'll perform this many iterations of the algorithm\n", 51 | "iterations = 100\n", 52 | "# Each image is 28*28 pixels, which has been flattened to a vector 0f 784 values\n", 53 | "data_size = 28*28\n", 54 | "# The images are 8 bit greyscale images (values range from 0-255)\n", 55 | "# We'll rescale the pixel values to be between 0-1 (We don't REALLY need to do this for k-means)\n", 56 | "test_x = (test_x.astype(float) / 255)" 57 | ] 58 | }, 59 | { 60 | "cell_type": "markdown", 61 | "metadata": {}, 62 | "source": [ 63 | "

Kmeans Initialization

\n", 64 | "Here we'll initialise the cluster centers to random values by creating a 10*784 matrix (2D Tensor) by randomly sampling 10 points from the dataset" 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": null, 70 | "metadata": {}, 71 | "outputs": [], 72 | "source": [ 73 | "# Randomly generate K indicies for k datapoints from the dataset (indicies need to be int)\n", 74 | "means = test_x[np.random.randint(0, num_img , num_means)]" 75 | ] 76 | }, 77 | { 78 | "cell_type": "markdown", 79 | "metadata": {}, 80 | "source": [ 81 | "

Kmeans Algorithm

\n", 82 | "Now implement the main steps of the K-Means clustering algorithm! Try and make it as efficient as possible and minimise the time/iteration" 83 | ] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": null, 88 | "metadata": {}, 89 | "outputs": [], 90 | "source": [ 91 | "start_time = time.time()\n", 92 | "for i in trange(iterations):\n", 93 | " # Add on a dimension in the right place and use broadcasting to find the differences\n", 94 | " diff_from_means = np.expand_dims(means, 0) - np.expand_dims(test_x, 1)\n", 95 | " # Using mean square of differences here\n", 96 | " dist_to_means = np.mean(diff_from_means ** 2, 2)\n", 97 | " # Expand dims is anther way to add a dimension\n", 98 | " indx_of_means = np.argmin(dist_to_means, 1)\n", 99 | " # Create a one hot coded vector per datapoint\n", 100 | " a = np.eye(num_means)[indx_of_means].T\n", 101 | " # Multiply to get the sums of each cluster then divide by elements per cluster to get means\n", 102 | " means = np.matmul(a, test_x) / np.expand_dims(np.sum(a,1),1) \n", 103 | "\n", 104 | "end_time = time.time()\n", 105 | "print(\"%d iterations took %.2f seconds, which corresponds to %.2fs/iteration\" % (iterations, end_time - start_time, (end_time - start_time)/iterations))" 106 | ] 107 | }, 108 | { 109 | "cell_type": "markdown", 110 | "metadata": {}, 111 | "source": [ 112 | "

Lets visualise the the cluster centers!

" 113 | ] 114 | }, 115 | { 116 | "cell_type": "code", 117 | "execution_count": null, 118 | "metadata": {}, 119 | "outputs": [], 120 | "source": [ 121 | "plt.figure(1, figsize=(20, 10))\n", 122 | "img = means.reshape(num_means, 28, 28).transpose((1, 0, 2)).reshape(28, num_means*28)\n", 123 | "_ = plt.imshow(img)" 124 | ] 125 | } 126 | ], 127 | "metadata": { 128 | "kernelspec": { 129 | "display_name": "Python 3 (ipykernel)", 130 | "language": "python", 131 | "name": "python3" 132 | }, 133 | "language_info": { 134 | "codemirror_mode": { 135 | "name": "ipython", 136 | "version": 3 137 | }, 138 | "file_extension": ".py", 139 | "mimetype": "text/x-python", 140 | "name": "python", 141 | "nbconvert_exporter": "python", 142 | "pygments_lexer": "ipython3", 143 | "version": "3.9.16" 144 | } 145 | }, 146 | "nbformat": 4, 147 | "nbformat_minor": 2 148 | } 149 | -------------------------------------------------------------------------------- /section01_numpy_ml/solutions/Non-Linear_Regression_For_Classification_Solutions.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "

Non-Linear Regression For Classification


\n", 8 | "Example of how to implement an MLP with 1 hidden layer using numpy" 9 | ] 10 | }, 11 | { 12 | "cell_type": "code", 13 | "execution_count": null, 14 | "metadata": {}, 15 | "outputs": [], 16 | "source": [ 17 | "import numpy as np\n", 18 | "import matplotlib.pyplot as plt\n", 19 | "from tqdm.notebook import trange, tqdm" 20 | ] 21 | }, 22 | { 23 | "cell_type": "markdown", 24 | "metadata": {}, 25 | "source": [ 26 | "

Loading the data

\n", 27 | "Lets load some \"toy\" data that we can use" 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": null, 33 | "metadata": {}, 34 | "outputs": [], 35 | "source": [ 36 | "# you can load your data using this cell\n", 37 | "npzfile = np.load(\"../data/toy_data_two_moon.npz\") # toy_data.npz or toy_data_two_circles.npz\n", 38 | "\n", 39 | "#The compressed Numpy file is split up into 4 parts\n", 40 | "#Train inputs and target outputs\n", 41 | "#Test inputs and target outputs\n", 42 | "x_train = npzfile['arr_0']\n", 43 | "x_test = npzfile['arr_1']\n", 44 | "y_train = npzfile['arr_2']\n", 45 | "y_test = npzfile['arr_3']\n", 46 | "\n", 47 | "# remember that each row in x_train and X_test is a sample. so x_train[1,:] is the first training sample" 48 | ] 49 | }, 50 | { 51 | "cell_type": "markdown", 52 | "metadata": {}, 53 | "source": [ 54 | "

Let's plot our data

" 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": null, 60 | "metadata": {}, 61 | "outputs": [], 62 | "source": [ 63 | "#Lets see what the data looks like\n", 64 | "fig = plt.figure(figsize = (10, 5))\n", 65 | "plt.subplot(121)\n", 66 | "plt.scatter(x_train[:, 0], x_train[:, 1], marker='o', c=y_train[:,0], s=20, edgecolor='k')\n", 67 | "plt.title(\"Train data\")\n", 68 | "plt.xlabel(\"X0\")\n", 69 | "plt.ylabel(\"X1\")\n", 70 | "plt.subplot(122)\n", 71 | "plt.scatter(x_test[:, 0], x_test[:, 1], marker='o', c=y_test[:,0], s=20, edgecolor='k')\n", 72 | "plt.title(\"Test data\")\n", 73 | "plt.xlabel(\"X0\")\n", 74 | "plt.ylabel(\"X1\")" 75 | ] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "execution_count": null, 80 | "metadata": {}, 81 | "outputs": [], 82 | "source": [ 83 | "def sigmoid(x):\n", 84 | " return 1/(1 + np.exp(-x))\n", 85 | "\n", 86 | "def tanh(x):\n", 87 | " return (np.exp(x) - np.exp(-x))/(np.exp(x) + np.exp(-x) + 1e-4)\n", 88 | "\n", 89 | "def relu(x):\n", 90 | " return (x > 0) * x" 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": null, 96 | "metadata": {}, 97 | "outputs": [], 98 | "source": [ 99 | "class MLP():\n", 100 | " def __init__(self, input_size, output_size, hidden_size):\n", 101 | " self.theta0 = np.random.randn(input_size, hidden_size)/(input_size)\n", 102 | " self.bias0 = np.zeros((1, hidden_size))\n", 103 | " self.theta1 = np.random.randn(hidden_size, 1)/(hidden_size)\n", 104 | " self.bias1 = np.zeros((1, 1))\n", 105 | "\n", 106 | " def predict(self, x):\n", 107 | " h1 = np.matmul(x , self.theta0)\n", 108 | " h1_ = h1 + self.bias0\n", 109 | " h2 = relu(h1_)\n", 110 | " output = np.matmul(h2 , self.theta1) + self.bias1\n", 111 | " pred = (output >= 0.5).astype(int)\n", 112 | " return pred, (h1, h1_, h2, output)\n", 113 | " \n", 114 | " def compute_grad(self, x, y):\n", 115 | " _, layers = self.predict(x)\n", 116 | " h1, h1_, h2, output = layers\n", 117 | "\n", 118 | " dl_dtheta1 = np.matmul(h2.T , 2 * (output - y))/y.shape[0]\n", 119 | " dl_dbias1 = np.matmul(np.ones(output.shape).T, 2 * (output - y))/y.shape[0]\n", 120 | "\n", 121 | " dl_dh2 = np.matmul(2 * (output - y), self.theta1.T)\n", 122 | " \n", 123 | " # If using tanh\n", 124 | "# dl_dh1 = dl_dh2 * (1 - (tanh(h1) ** 2))\n", 125 | "\n", 126 | " # If using sigmoid\n", 127 | "# dl_dh1 = dl_dh2 * (sigmoid(h1)*(1 - sigmoid(h1)))\n", 128 | "\n", 129 | " # If using relu\n", 130 | " dl_dh1_ = dl_dh2 * (h1_ > 0)\n", 131 | "\n", 132 | " dl_dtheta0 = np.matmul(x.T , dl_dh1_)/y.shape[0]\n", 133 | " dl_dbias0 = np.matmul(np.ones(output.shape).T , dl_dh1_)/y.shape[0]\n", 134 | "\n", 135 | " return dl_dtheta0, dl_dbias0, dl_dtheta1, dl_dbias1\n", 136 | " \n", 137 | " def update_params(self, x, y, lr):\n", 138 | " dl_dtheta0, dl_dbias0, dl_dtheta1, dl_dbias1 = self.compute_grad(x, y)\n", 139 | " self.theta0 -= lr * dl_dtheta0 \n", 140 | " self.bias0 -= lr * dl_dbias0 \n", 141 | " \n", 142 | " self.theta1 -= lr * dl_dtheta1\n", 143 | " self.bias1 -= lr * dl_dbias1\n", 144 | " \n", 145 | " def compute_loss(self, x, y):\n", 146 | " _, layers = self.predict(x)\n", 147 | " _, _, _, output = layers\n", 148 | " return np.mean((output - y)**2)\n" 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": null, 154 | "metadata": {}, 155 | "outputs": [], 156 | "source": [ 157 | "mlp = MLP(input_size=2, output_size=1, hidden_size=64)" 158 | ] 159 | }, 160 | { 161 | "cell_type": "code", 162 | "execution_count": null, 163 | "metadata": {}, 164 | "outputs": [], 165 | "source": [ 166 | "loss_log = [] # keep track of the loss values\n", 167 | "acc = [] # keep track of the accuracy " 168 | ] 169 | }, 170 | { 171 | "cell_type": "code", 172 | "execution_count": null, 173 | "metadata": {}, 174 | "outputs": [], 175 | "source": [ 176 | "lr = 0.05\n", 177 | "\n", 178 | "# number of times we itterate over the dataset\n", 179 | "max_epoch = 10000\n", 180 | "\n", 181 | "for epoch in trange(max_epoch):\n", 182 | " y_test_hat, _ = mlp.predict(x_test)\n", 183 | "\n", 184 | " acc.append(float(sum(y_test_hat == y_test))/ float(len(y_test)))\n", 185 | "\n", 186 | " # call the compute_grad_loss that is implemented above to \n", 187 | " # measure the loss and the gradient\n", 188 | " loss = mlp.compute_loss(x_train, y_train)\n", 189 | " mlp.update_params(x_train, y_train, lr)\n", 190 | "\n", 191 | " loss_log.append(loss)\n", 192 | "\n", 193 | "print(\"Test Accuracy of linear model(GD): %.2f%% \" %(acc[-1]*100))" 194 | ] 195 | }, 196 | { 197 | "cell_type": "code", 198 | "execution_count": null, 199 | "metadata": {}, 200 | "outputs": [], 201 | "source": [ 202 | "_ = plt.plot(acc)\n", 203 | "_ = plt.title(\"Model accuracy per itteration\")" 204 | ] 205 | }, 206 | { 207 | "cell_type": "code", 208 | "execution_count": null, 209 | "metadata": {}, 210 | "outputs": [], 211 | "source": [ 212 | "_ = plt.plot(loss_log)\n", 213 | "_ = plt.title(\"Model loss per itteration\")" 214 | ] 215 | }, 216 | { 217 | "cell_type": "code", 218 | "execution_count": null, 219 | "metadata": {}, 220 | "outputs": [], 221 | "source": [ 222 | "y_test_hat, _ = mlp.predict(x_test)\n", 223 | "_ = plt.scatter(x_test[:, 0], x_test[:, 1], marker='o', c=y_test_hat[:,0], s=25, edgecolor='k')\n", 224 | "_ = plt.title(\"Model Test Prediction\")" 225 | ] 226 | } 227 | ], 228 | "metadata": { 229 | "kernelspec": { 230 | "display_name": "Python 3", 231 | "language": "python", 232 | "name": "python3" 233 | }, 234 | "language_info": { 235 | "codemirror_mode": { 236 | "name": "ipython", 237 | "version": 3 238 | }, 239 | "file_extension": ".py", 240 | "mimetype": "text/x-python", 241 | "name": "python", 242 | "nbconvert_exporter": "python", 243 | "pygments_lexer": "ipython3", 244 | "version": "3.6.9" 245 | } 246 | }, 247 | "nbformat": 4, 248 | "nbformat_minor": 2 249 | } 250 | -------------------------------------------------------------------------------- /section01_numpy_ml/solutions/load.py: -------------------------------------------------------------------------------- 1 | 2 | import urllib.request 3 | import gzip 4 | import pickle 5 | import os 6 | import numpy as np 7 | 8 | from PIL import Image 9 | 10 | def _download(file_name): 11 | file_path = dataset_dir + "/" + file_name 12 | 13 | if os.path.exists(file_path): 14 | return 15 | 16 | print("Downloading " + file_name + " ... ") 17 | urllib.request.urlretrieve(url_base + file_name, file_path) 18 | print("Done") 19 | 20 | def download_mnist(): 21 | for v in key_file.values(): 22 | _download(v) 23 | 24 | def _load_label(file_name): 25 | file_path = dataset_dir + "/" + file_name 26 | 27 | print("Converting " + file_name + " to NumPy Array ...") 28 | with gzip.open(file_path, 'rb') as f: 29 | labels = np.frombuffer(f.read(), np.uint8, offset=8) 30 | print("Done") 31 | 32 | return labels 33 | 34 | def _load_img(file_name): 35 | file_path = dataset_dir + "/" + file_name 36 | 37 | print("Converting " + file_name + " to NumPy Array ...") 38 | with gzip.open(file_path, 'rb') as f: 39 | data = np.frombuffer(f.read(), np.uint8, offset=16) 40 | data = data.reshape(-1, img_size) 41 | print("Done") 42 | 43 | return data 44 | 45 | def _convert_numpy(): 46 | dataset = {} 47 | dataset['train_img'] = _load_img(key_file['train_img']) 48 | dataset['train_label'] = _load_label(key_file['train_label']) 49 | dataset['test_img'] = _load_img(key_file['test_img']) 50 | dataset['test_label'] = _load_label(key_file['test_label']) 51 | 52 | return dataset 53 | 54 | def init_mnist(): 55 | download_mnist() 56 | dataset = _convert_numpy() 57 | print("Creating pickle file ...") 58 | with open(save_file, 'wb') as f: 59 | pickle.dump(dataset, f, -1) 60 | print("Done") 61 | 62 | def _change_ont_hot_label(X): 63 | T = np.zeros((X.size, 10)) 64 | for idx, row in enumerate(T): 65 | row[X[idx]] = 1 66 | 67 | return T 68 | 69 | def load_mnist(normalize=True, flatten=True, one_hot_label=False): 70 | """ 71 | Parameters 72 | ---------- 73 | normalize : Normalize the pixel values 74 | flatten : Flatten the images as one array 75 | one_hot_label : Encode the labels as a one-hot array 76 | 77 | Returns 78 | ------- 79 | (Trainig Image, Training Label), (Test Image, Test Label) 80 | """ 81 | if not os.path.exists(save_file): 82 | init_mnist() 83 | 84 | with open(save_file, 'rb') as f: 85 | dataset = pickle.load(f) 86 | 87 | if normalize: 88 | for key in ('train_img', 'test_img'): 89 | dataset[key] = dataset[key].astype(np.float32) 90 | dataset[key] /= 255.0 91 | 92 | if not flatten: 93 | for key in ('train_img', 'test_img'): 94 | dataset[key] = dataset[key].reshape(-1, 1, 28, 28) 95 | 96 | if one_hot_label: 97 | dataset['train_label'] = _change_ont_hot_label(dataset['train_label']) 98 | dataset['test_label'] = _change_ont_hot_label(dataset['test_label']) 99 | 100 | return (dataset['train_img'], dataset['train_label']), (dataset['test_img'], dataset['test_label']) 101 | 102 | def img_show(img): 103 | pil_img = Image.fromarray(np.uint8(img)) 104 | pil_img.show() 105 | 106 | 107 | # Load the MNIST dataset 108 | url_base = 'https://ossci-datasets.s3.amazonaws.com/mnist/' 109 | key_file = { 110 | 'train_img':'train-images-idx3-ubyte.gz', 111 | 'train_label':'train-labels-idx1-ubyte.gz', 112 | 'test_img':'t10k-images-idx3-ubyte.gz', 113 | 'test_label':'t10k-labels-idx1-ubyte.gz' 114 | } 115 | 116 | dataset_dir = "../../datasets/MNIST/raw" 117 | save_file = dataset_dir + "/mnist.pkl" 118 | 119 | train_num = 60000 120 | test_num = 10000 121 | img_dim = (1, 28, 28) 122 | img_size = 784 123 | 124 | (train_x, train_y), (test_x, test_y) = load_mnist(normalize=False, flatten=True) 125 | 126 | 127 | -------------------------------------------------------------------------------- /section02_pytorch_basics/data/K_means.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section02_pytorch_basics/data/K_means.gif -------------------------------------------------------------------------------- /section02_pytorch_basics/data/Linear_Regression.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section02_pytorch_basics/data/Linear_Regression.gif -------------------------------------------------------------------------------- /section02_pytorch_basics/data/toy_data_two_moon.npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section02_pytorch_basics/data/toy_data_two_moon.npz -------------------------------------------------------------------------------- /section02_pytorch_basics/notebooks/Pytorch1_KMeans.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "

Kmeans Clustering

\n", 8 | "\n", 9 | "With our knowledge of Python and now Numpy lets create an implementation of a famous machine learning algorithm \"K-Means Clustering\". The job of a clustering algorithm is to break a dataset into some number of \"clusters\" (groups), the number of clusters usually defined by the user. K-Means clustering working by iteratively updating a pre-defined number of cluster centers. It does this by finding the distance between each datapoint and every cluster center. Datapoints are then assigned to the cluster center they are closest to and each cluster center is updated to be the mean of the new cluster. These steps are updated for some number of steps or until the cluster centers converge (they stop moving so much)
\n", 10 | "Lets have a look at the steps of K-means clustering
\n", 11 | "1. Define the number of clusters \"k\" you want to group your data into
\n", 12 | "2. Randomly initialise k vectors with the same size as each datapoint, this is the initialisation of our cluster centers
\n", 13 | "3. Calculate the distance between each datapoint and each cluster center (using MSE or equivalent)
\n", 14 | "4. For every datapoint find the cluster center they are closest to
\n", 15 | "5. Re-calculate the cluster centers by finding the mean of every new cluster
\n", 16 | "6. Repeat steps 3-6 for n steps or until convergence" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": null, 22 | "metadata": {}, 23 | "outputs": [], 24 | "source": [ 25 | "import matplotlib.pyplot as plt \n", 26 | "import numpy as np \n", 27 | "import torch\n", 28 | "from load import test_x\n", 29 | "import time\n", 30 | "from IPython.display import clear_output" 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "Using the module \"load\" that comes with this notebook, lets load our dataset
\n", 38 | "The dataset we'll be using is the MNIST dataset, a dataset of small, low-res handwritten digits. There are 60000 training images and 10000 test images divided up into 10 classes (digits 0-9). Here we will be using the test set (as it's a smaller set)" 39 | ] 40 | }, 41 | { 42 | "cell_type": "code", 43 | "execution_count": null, 44 | "metadata": {}, 45 | "outputs": [], 46 | "source": [ 47 | "#Number of datapoint\n", 48 | "num_img = 10000 \n", 49 | "#Number of cluster centers, 10 because the dataset contains 10 classes eg: digit 0 to 9\n", 50 | "num_means = 10 \n", 51 | "#We'll perform this many iterations of the algorithm\n", 52 | "iterations = 20 \n", 53 | "#Each image is 28*28 pixels, which has been flattened to a vector 0f 784 values\n", 54 | "data_size = 28*28\n", 55 | "# The images are 8 bit greyscale images (values range from 0-255)\n", 56 | "# We'll rescale the pixel values to be between 0-1 (We don't REALLY need to do this for k-means)\n", 57 | "test_x_tensor = torch.FloatTensor((test_x.astype(float) / 255))" 58 | ] 59 | }, 60 | { 61 | "cell_type": "markdown", 62 | "metadata": {}, 63 | "source": [ 64 | "

Kmeans Initialization

\n", 65 | "Here we'll initialise the cluster centers to random values by creating a 10*784 matrix (2D Tensor) by randomly sampling 10 points from the dataset" 66 | ] 67 | }, 68 | { 69 | "cell_type": "code", 70 | "execution_count": null, 71 | "metadata": {}, 72 | "outputs": [], 73 | "source": [ 74 | "#Radnomly generate K indicies for k datapoints from the dataset (indicies need to be int)\n", 75 | "means = test_x_tensor[np.random.randint(0, num_img , num_means)]" 76 | ] 77 | }, 78 | { 79 | "cell_type": "markdown", 80 | "metadata": {}, 81 | "source": [ 82 | "

Kmeans Algorithm

\n", 83 | "Now implement the main steps of the K-Means clustering algorithm! Try and make it as efficient as possible and minimise the time/iteration" 84 | ] 85 | }, 86 | { 87 | "cell_type": "code", 88 | "execution_count": null, 89 | "metadata": {}, 90 | "outputs": [], 91 | "source": [ 92 | "start_time = time.time()\n", 93 | "for i in range(iterations):\n", 94 | "\n", 95 | " print(\"Itteration [%d/%d]\" % (i,iterations))\n", 96 | "end_time = time.time()\n", 97 | "print(\"%d iterations took %.2f seconds, which corresponds to %.2fs/iteration\" % (iterations, end_time - start_time, (end_time - start_time)/iterations))" 98 | ] 99 | }, 100 | { 101 | "cell_type": "markdown", 102 | "metadata": {}, 103 | "source": [ 104 | "

Lets visualise the the cluster centers!

" 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": null, 110 | "metadata": {}, 111 | "outputs": [], 112 | "source": [ 113 | "plt.figure(1, figsize=(20, 10))\n", 114 | "img = means.cpu().view(num_means*28,28)\n", 115 | "plt.imshow(img)" 116 | ] 117 | } 118 | ], 119 | "metadata": { 120 | "kernelspec": { 121 | "display_name": "Python 3", 122 | "language": "python", 123 | "name": "python3" 124 | }, 125 | "language_info": { 126 | "codemirror_mode": { 127 | "name": "ipython", 128 | "version": 3 129 | }, 130 | "file_extension": ".py", 131 | "mimetype": "text/x-python", 132 | "name": "python", 133 | "nbconvert_exporter": "python", 134 | "pygments_lexer": "ipython3", 135 | "version": "3.6.9" 136 | } 137 | }, 138 | "nbformat": 4, 139 | "nbformat_minor": 2 140 | } 141 | -------------------------------------------------------------------------------- /section02_pytorch_basics/notebooks/load.py: -------------------------------------------------------------------------------- 1 | 2 | import urllib.request 3 | import gzip 4 | import pickle 5 | import os 6 | import numpy as np 7 | 8 | from PIL import Image 9 | 10 | def _download(file_name): 11 | file_path = dataset_dir + "/" + file_name 12 | 13 | if os.path.exists(file_path): 14 | return 15 | 16 | print("Downloading " + file_name + " ... ") 17 | urllib.request.urlretrieve(url_base + file_name, file_path) 18 | print("Done") 19 | 20 | def download_mnist(): 21 | for v in key_file.values(): 22 | _download(v) 23 | 24 | def _load_label(file_name): 25 | file_path = dataset_dir + "/" + file_name 26 | 27 | print("Converting " + file_name + " to NumPy Array ...") 28 | with gzip.open(file_path, 'rb') as f: 29 | labels = np.frombuffer(f.read(), np.uint8, offset=8) 30 | print("Done") 31 | 32 | return labels 33 | 34 | def _load_img(file_name): 35 | file_path = dataset_dir + "/" + file_name 36 | 37 | print("Converting " + file_name + " to NumPy Array ...") 38 | with gzip.open(file_path, 'rb') as f: 39 | data = np.frombuffer(f.read(), np.uint8, offset=16) 40 | data = data.reshape(-1, img_size) 41 | print("Done") 42 | 43 | return data 44 | 45 | def _convert_numpy(): 46 | dataset = {} 47 | dataset['train_img'] = _load_img(key_file['train_img']) 48 | dataset['train_label'] = _load_label(key_file['train_label']) 49 | dataset['test_img'] = _load_img(key_file['test_img']) 50 | dataset['test_label'] = _load_label(key_file['test_label']) 51 | 52 | return dataset 53 | 54 | def init_mnist(): 55 | download_mnist() 56 | dataset = _convert_numpy() 57 | print("Creating pickle file ...") 58 | with open(save_file, 'wb') as f: 59 | pickle.dump(dataset, f, -1) 60 | print("Done") 61 | 62 | def _change_ont_hot_label(X): 63 | T = np.zeros((X.size, 10)) 64 | for idx, row in enumerate(T): 65 | row[X[idx]] = 1 66 | 67 | return T 68 | 69 | def load_mnist(normalize=True, flatten=True, one_hot_label=False): 70 | """ 71 | Parameters 72 | ---------- 73 | normalize : Normalize the pixel values 74 | flatten : Flatten the images as one array 75 | one_hot_label : Encode the labels as a one-hot array 76 | 77 | Returns 78 | ------- 79 | (Trainig Image, Training Label), (Test Image, Test Label) 80 | """ 81 | if not os.path.exists(save_file): 82 | init_mnist() 83 | 84 | with open(save_file, 'rb') as f: 85 | dataset = pickle.load(f) 86 | 87 | if normalize: 88 | for key in ('train_img', 'test_img'): 89 | dataset[key] = dataset[key].astype(np.float32) 90 | dataset[key] /= 255.0 91 | 92 | if not flatten: 93 | for key in ('train_img', 'test_img'): 94 | dataset[key] = dataset[key].reshape(-1, 1, 28, 28) 95 | 96 | if one_hot_label: 97 | dataset['train_label'] = _change_ont_hot_label(dataset['train_label']) 98 | dataset['test_label'] = _change_ont_hot_label(dataset['test_label']) 99 | 100 | return (dataset['train_img'], dataset['train_label']), (dataset['test_img'], dataset['test_label']) 101 | 102 | def img_show(img): 103 | pil_img = Image.fromarray(np.uint8(img)) 104 | pil_img.show() 105 | 106 | 107 | # Load the MNIST dataset 108 | url_base = 'https://ossci-datasets.s3.amazonaws.com/mnist/' 109 | key_file = { 110 | 'train_img':'train-images-idx3-ubyte.gz', 111 | 'train_label':'train-labels-idx1-ubyte.gz', 112 | 'test_img':'t10k-images-idx3-ubyte.gz', 113 | 'test_label':'t10k-labels-idx1-ubyte.gz' 114 | } 115 | 116 | dataset_dir = "../../datasets/MNIST/raw" 117 | save_file = dataset_dir + "/mnist.pkl" 118 | 119 | train_num = 60000 120 | test_num = 10000 121 | img_dim = (1, 28, 28) 122 | img_size = 784 123 | 124 | (train_x, train_y), (test_x, test_y) = load_mnist(normalize=False, flatten=True) 125 | 126 | 127 | -------------------------------------------------------------------------------- /section02_pytorch_basics/solutions/Pytorch1_KMeans_Solution.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "

Kmeans Clustering

\n", 8 | "\n", 9 | "With our knowledge of Python and now Numpy lets create an implementation of a famous machine learning algorithm \"K-Means Clustering\". The job of a clustering algorithm is to break a dataset into some number of \"clusters\" (groups), the number of clusters usually defined by the user. K-Means clustering working by iteratively updating a pre-defined number of cluster centers. It does this by finding the distance between each datapoint and every cluster center. Datapoints are then assigned to the cluster center they are closest to and each cluster center is updated to be the mean of the new cluster. These steps are updated for some number of steps or until the cluster centers converge (they stop moving so much)
\n", 10 | "Lets have a look at the steps of K-means clustering
\n", 11 | "1. Define the number of clusters \"k\" you want to group your data into
\n", 12 | "2. Randomly initialise k vectors with the same size as each datapoint, this is the initialisation of our cluster centers
\n", 13 | "3. Calculate the distance between each datapoint and each cluster center (using MSE or equivalent)
\n", 14 | "4. For every datapoint find the cluster center they are closest to
\n", 15 | "5. Re-calculate the cluster centers by finding the mean of every new cluster
\n", 16 | "6. Repeat steps 3-6 for n steps or until convergence" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": null, 22 | "metadata": {}, 23 | "outputs": [], 24 | "source": [ 25 | "import matplotlib.pyplot as plt \n", 26 | "import numpy as np \n", 27 | "import torch\n", 28 | "from load import test_x\n", 29 | "import time\n", 30 | "from tqdm.notebook import trange, tqdm" 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "Using the module \"load\" that comes with this notebook, lets load our dataset
\n", 38 | "The dataset we'll be using is the MNIST dataset, a dataset of small, low-res handwritten digits. There are 60000 training images and 10000 test images divided up into 10 classes (digits 0-9). Here we will be using the test set (as it's a smaller set)" 39 | ] 40 | }, 41 | { 42 | "cell_type": "code", 43 | "execution_count": null, 44 | "metadata": {}, 45 | "outputs": [], 46 | "source": [ 47 | "# Sneak peak at using GPUs for computation! (Will only work if you have a cuda enabled GPU)\n", 48 | "# device = \"cpu\"\n", 49 | "gpu_indx = 0\n", 50 | "device = torch.device(gpu_indx if torch.cuda.is_available() else \"cpu\")\n", 51 | "print(\"cuda or cpu?:\", device)" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": null, 57 | "metadata": {}, 58 | "outputs": [], 59 | "source": [ 60 | "# Number of datapoint\n", 61 | "num_img = 10000 \n", 62 | "# Number of cluster centers, 10 because the dataset contains 10 classes eg: digit 0 to 9\n", 63 | "num_means = 10 \n", 64 | "# We'll perform this many iterations of the algorithm\n", 65 | "iterations = 100\n", 66 | "# Each image is 28*28 pixels, which has been flattened to a vector 0f 784 values\n", 67 | "data_size = 28*28\n", 68 | "# The images are 8 bit greyscale images (values range from 0-255)\n", 69 | "# We'll rescale the pixel values to be between 0-1 (We don't REALLY need to do this for k-means)\n", 70 | "test_x_tensor = torch.FloatTensor((test_x.astype(float) / 255)).to(device)" 71 | ] 72 | }, 73 | { 74 | "cell_type": "markdown", 75 | "metadata": {}, 76 | "source": [ 77 | "

Kmeans Initialization

\n", 78 | "Here we'll initialise the cluster centers to random values by creating a 10*784 matrix (2D Tensor) by randomly sampling 10 points from the dataset" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": null, 84 | "metadata": {}, 85 | "outputs": [], 86 | "source": [ 87 | "# Randomly generate K indicies for k datapoints from the dataset (indicies need to be int)\n", 88 | "means = test_x_tensor[np.random.randint(0, num_img , num_means)]\n", 89 | "eye_mat = torch.eye(num_means, device=device)" 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": null, 95 | "metadata": {}, 96 | "outputs": [], 97 | "source": [ 98 | "# Random initial operation to initialize Pytorch\n", 99 | "means = torch.mm(eye_mat, means)" 100 | ] 101 | }, 102 | { 103 | "cell_type": "code", 104 | "execution_count": null, 105 | "metadata": {}, 106 | "outputs": [], 107 | "source": [ 108 | "plt.figure(1, figsize=(20, 10))\n", 109 | "img = means.cpu().float().numpy().reshape(num_means, 28, 28).transpose((1, 0, 2)).reshape(28, num_means*28)\n", 110 | "_ = plt.imshow(img)" 111 | ] 112 | }, 113 | { 114 | "cell_type": "markdown", 115 | "metadata": {}, 116 | "source": [ 117 | "

Kmeans Algorithm

\n", 118 | "Now implement the main steps of the K-Means clustering algorithm! Try and make it as efficient as possible and minimise the time/iteration" 119 | ] 120 | }, 121 | { 122 | "cell_type": "code", 123 | "execution_count": null, 124 | "metadata": {}, 125 | "outputs": [], 126 | "source": [ 127 | "start_time = time.time()\n", 128 | "with torch.no_grad():\n", 129 | " for i in trange(iterations):\n", 130 | " # Add on a dimension in the right place and use broadcasting to find the differences\n", 131 | " diff_from_means = means.unsqueeze(0) - test_x_tensor.unsqueeze(1)\n", 132 | "\n", 133 | " # Using absolute sum of differences here\n", 134 | " dist_to_means = diff_from_means.pow(2).mean(2)\n", 135 | "\n", 136 | " # Expand dims is anther way to add a dimension\n", 137 | " indx_of_means = dist_to_means.argmin(1)\n", 138 | "\n", 139 | " # Create a one hot coded vector per datapoint\n", 140 | " a = eye_mat[indx_of_means].t()\n", 141 | " # Multiply to get the sums of each cluster then divide by elements per cluster to get means\n", 142 | " means = torch.mm(a, test_x_tensor) / a.sum(1, keepdims=True)\n", 143 | "\n", 144 | "end_time = time.time()\n", 145 | "print(\"%d iterations took %.2f seconds, which corresponds to %.4fs/iteration\" % (iterations, end_time - start_time, (end_time - start_time)/iterations))" 146 | ] 147 | }, 148 | { 149 | "cell_type": "markdown", 150 | "metadata": {}, 151 | "source": [ 152 | "

Lets visualise the the cluster centers!

" 153 | ] 154 | }, 155 | { 156 | "cell_type": "code", 157 | "execution_count": null, 158 | "metadata": {}, 159 | "outputs": [], 160 | "source": [ 161 | "plt.figure(1, figsize=(20, 10))\n", 162 | "img = means.cpu().float().numpy().reshape(num_means, 28, 28).transpose((1, 0, 2)).reshape(28, num_means*28)\n", 163 | "_ = plt.imshow(img)" 164 | ] 165 | } 166 | ], 167 | "metadata": { 168 | "kernelspec": { 169 | "display_name": "Python 3 (ipykernel)", 170 | "language": "python", 171 | "name": "python3" 172 | }, 173 | "language_info": { 174 | "codemirror_mode": { 175 | "name": "ipython", 176 | "version": 3 177 | }, 178 | "file_extension": ".py", 179 | "mimetype": "text/x-python", 180 | "name": "python", 181 | "nbconvert_exporter": "python", 182 | "pygments_lexer": "ipython3", 183 | "version": "3.9.16" 184 | } 185 | }, 186 | "nbformat": 4, 187 | "nbformat_minor": 2 188 | } 189 | -------------------------------------------------------------------------------- /section02_pytorch_basics/solutions/load.py: -------------------------------------------------------------------------------- 1 | 2 | import urllib.request 3 | import gzip 4 | import pickle 5 | import os 6 | import numpy as np 7 | 8 | from PIL import Image 9 | 10 | def _download(file_name): 11 | file_path = dataset_dir + "/" + file_name 12 | 13 | if os.path.exists(file_path): 14 | return 15 | 16 | print("Downloading " + file_name + " ... ") 17 | os.makedirs(os.path.dirname(file_path), exist_ok=True) 18 | urllib.request.urlretrieve(url_base + file_name, file_path) 19 | print("Done") 20 | 21 | def download_mnist(): 22 | for v in key_file.values(): 23 | _download(v) 24 | 25 | def _load_label(file_name): 26 | file_path = dataset_dir + "/" + file_name 27 | 28 | print("Converting " + file_name + " to NumPy Array ...") 29 | with gzip.open(file_path, 'rb') as f: 30 | labels = np.frombuffer(f.read(), np.uint8, offset=8) 31 | print("Done") 32 | 33 | return labels 34 | 35 | def _load_img(file_name): 36 | file_path = dataset_dir + "/" + file_name 37 | 38 | print("Converting " + file_name + " to NumPy Array ...") 39 | with gzip.open(file_path, 'rb') as f: 40 | data = np.frombuffer(f.read(), np.uint8, offset=16) 41 | data = data.reshape(-1, img_size) 42 | print("Done") 43 | 44 | return data 45 | 46 | def _convert_numpy(): 47 | dataset = {} 48 | dataset['train_img'] = _load_img(key_file['train_img']) 49 | dataset['train_label'] = _load_label(key_file['train_label']) 50 | dataset['test_img'] = _load_img(key_file['test_img']) 51 | dataset['test_label'] = _load_label(key_file['test_label']) 52 | 53 | return dataset 54 | 55 | def init_mnist(): 56 | download_mnist() 57 | dataset = _convert_numpy() 58 | print("Creating pickle file ...") 59 | with open(save_file, 'wb') as f: 60 | pickle.dump(dataset, f, -1) 61 | print("Done") 62 | 63 | def _change_ont_hot_label(X): 64 | T = np.zeros((X.size, 10)) 65 | for idx, row in enumerate(T): 66 | row[X[idx]] = 1 67 | 68 | return T 69 | 70 | def load_mnist(normalize=True, flatten=True, one_hot_label=False): 71 | """ 72 | Parameters 73 | ---------- 74 | normalize : Normalize the pixel values 75 | flatten : Flatten the images as one array 76 | one_hot_label : Encode the labels as a one-hot array 77 | 78 | Returns 79 | ------- 80 | (Trainig Image, Training Label), (Test Image, Test Label) 81 | """ 82 | if not os.path.exists(save_file): 83 | init_mnist() 84 | 85 | with open(save_file, 'rb') as f: 86 | dataset = pickle.load(f) 87 | 88 | if normalize: 89 | for key in ('train_img', 'test_img'): 90 | dataset[key] = dataset[key].astype(np.float32) 91 | dataset[key] /= 255.0 92 | 93 | if not flatten: 94 | for key in ('train_img', 'test_img'): 95 | dataset[key] = dataset[key].reshape(-1, 1, 28, 28) 96 | 97 | if one_hot_label: 98 | dataset['train_label'] = _change_ont_hot_label(dataset['train_label']) 99 | dataset['test_label'] = _change_ont_hot_label(dataset['test_label']) 100 | 101 | return (dataset['train_img'], dataset['train_label']), (dataset['test_img'], dataset['test_label']) 102 | 103 | def img_show(img): 104 | pil_img = Image.fromarray(np.uint8(img)) 105 | pil_img.show() 106 | 107 | 108 | # Load the MNIST dataset 109 | url_base = 'https://ossci-datasets.s3.amazonaws.com/mnist/' 110 | key_file = { 111 | 'train_img':'train-images-idx3-ubyte.gz', 112 | 'train_label':'train-labels-idx1-ubyte.gz', 113 | 'test_img':'t10k-images-idx3-ubyte.gz', 114 | 'test_label':'t10k-labels-idx1-ubyte.gz' 115 | } 116 | 117 | dataset_dir = "../../datasets/MNIST/raw" 118 | save_file = dataset_dir + "/mnist.pkl" 119 | 120 | train_num = 60000 121 | test_num = 10000 122 | img_dim = (1, 28, 28) 123 | img_size = 784 124 | 125 | (train_x, train_y), (test_x, test_y) = load_mnist(normalize=False, flatten=True) 126 | 127 | 128 | -------------------------------------------------------------------------------- /section03_pytorch_mlp/data/MNIST.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section03_pytorch_mlp/data/MNIST.gif -------------------------------------------------------------------------------- /section03_pytorch_mlp/data/sine_wave.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section03_pytorch_mlp/data/sine_wave.gif -------------------------------------------------------------------------------- /section04_pytorch_cnn/data/puppy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section04_pytorch_cnn/data/puppy.jpg -------------------------------------------------------------------------------- /section04_pytorch_cnn/solutions/Network.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import torch.nn.functional as F 4 | import torch.optim as optim 5 | import torch.utils.data.dataloader as dataloader 6 | 7 | from tqdm.notebook import trange, tqdm 8 | 9 | 10 | class LeNet(nn.Module): 11 | def __init__(self, channels_in, device, loss_fun, batch_size, learning_rate): 12 | # Call the __init__ function of the parent nn.module class 13 | super(LeNet, self).__init__() 14 | # Define Convolution Layers 15 | # conv1 6 channels_inx5x5 kernels 16 | self.optimizer = None 17 | self.conv1 = nn.Conv2d(channels_in, 6, kernel_size=5) 18 | 19 | # conv2 16 6x5x5 kernels 20 | self.conv2 = nn.Conv2d(6, 16, kernel_size=5) 21 | 22 | # Define MaxPooling Layers 23 | # Default Stride is = to kernel_size 24 | self.maxpool = nn.MaxPool2d(kernel_size=2) 25 | 26 | # Define Linear/Fully connected/ Dense Layers 27 | # Input to linear1 is the number of features from previous conv - 16x5x5 28 | # output of linear1 is 120 29 | self.linear1 = nn.Linear(16*5*5, 120) 30 | # output of linear2 is 84 31 | self.linear2 = nn.Linear(120, 84) 32 | # output of linear3 is 10 33 | self.linear3 = nn.Linear(84, 10) 34 | 35 | self.device = device 36 | self.loss_fun = loss_fun 37 | self.batch_size = batch_size 38 | self.learning_rate = learning_rate 39 | 40 | self.train_loss_logger = [] 41 | 42 | self.train_acc_logger = [] 43 | self.val_acc_logger = [] 44 | 45 | self.train_loader = None 46 | self.test_loader = None 47 | self.valid_loader = None 48 | 49 | self.set_optimizer() 50 | 51 | def set_optimizer(self): 52 | self.optimizer = optim.Adam(self.parameters(), lr=self.learning_rate) 53 | 54 | def set_data(self, train_set, test_set, val_set): 55 | 56 | print(f'Number of training examples: {len(train_set)}') 57 | print(f'Number of validation examples: {len(val_set)}') 58 | print(f'Number of testing examples: {len(test_set)}') 59 | 60 | self.train_loader = dataloader.DataLoader(train_set, shuffle=True, batch_size=self.batch_size, num_workers=4) 61 | self.valid_loader = dataloader.DataLoader(val_set, batch_size=self.batch_size, num_workers=4) 62 | self.test_loader = dataloader.DataLoader(test_set, batch_size=self.batch_size, num_workers=4) 63 | 64 | # This function should perform a single training epoch using our training data 65 | def train_model(self): 66 | 67 | if self.train_loader is None: 68 | ValueError("Dataset not defined!") 69 | 70 | # Set Network in train mode 71 | self.train() 72 | for i, (x, y) in enumerate(tqdm(self.train_loader, leave=False, desc="Training")): 73 | # Forward pass of image through network and get output 74 | fx = self.forward(x.to(self.device)) 75 | 76 | # Calculate loss using loss function 77 | loss = self.loss_fun(fx, y.to(self.device)) 78 | 79 | # Zero gradients 80 | self.optimizer.zero_grad() 81 | # Backpropagate gradients 82 | loss.backward() 83 | # Do a single optimization step 84 | self.optimizer.step() 85 | 86 | # Log the loss for plotting 87 | self.train_loss_logger.append(loss.item()) 88 | 89 | # This function should perform a single evaluation epoch, it WILL NOT be used to train our model 90 | def evaluate_model(self, train_test_val="test"): 91 | if self.test_loader is None: 92 | ValueError("Dataset not defined!") 93 | 94 | state = "Evaluating " 95 | if train_test_val == "test": 96 | loader = self.test_loader 97 | state += "Test Set" 98 | elif train_test_val == "train": 99 | loader = self.train_loader 100 | state += "Train Set" 101 | elif train_test_val == "val": 102 | loader = self.valid_loader 103 | state += "Validation Set" 104 | else: 105 | ValueError("Invalid dataset, train_test_val should be train/test/val") 106 | 107 | # Initialise counter 108 | epoch_acc = 0 109 | self.eval() 110 | with torch.no_grad(): 111 | for i, (x, y) in enumerate(tqdm(loader, leave=False, desc=state)): 112 | # Forward pass of image through network 113 | fx = self.forward(x.to(self.device)) 114 | 115 | # Log the cumulative sum of the acc 116 | epoch_acc += (fx.argmax(1) == y.to(self.device)).sum().item() 117 | 118 | # Log the accuracy from the epoch 119 | if train_test_val == "train": 120 | self.train_acc_logger.append(epoch_acc / len(loader.dataset)) 121 | elif train_test_val == "val": 122 | self.val_acc_logger.append(epoch_acc / len(loader.dataset)) 123 | 124 | return epoch_acc / len(loader.dataset) 125 | 126 | def forward(self, x): 127 | # Pass input through conv layers 128 | # x shape is BatchSize-3-32-32 129 | 130 | out1 = F.relu(self.conv1(x)) 131 | # out1 shape is BatchSize-6-28-28 132 | out1 = self.maxpool(out1) 133 | # out1 shape is BatchSize-6-14-14 134 | 135 | out2 = F.relu(self.conv2(out1)) 136 | # out2 shape is BatchSize-16-10-10 137 | out2 = self.maxpool(out2) 138 | # out2 shape is BatchSize-16-5-5 139 | 140 | # Flatten out2 to shape BatchSize-16x5x5 141 | out2 = out2.view(out2.shape[0], -1) 142 | 143 | out3 = F.relu(self.linear1(out2)) 144 | # out3 shape is BatchSize-120 145 | out4 = F.relu(self.linear2(out3)) 146 | # out4 shape is BatchSize-84 147 | out5 = self.linear3(out4) 148 | # out5 shape is BatchSize-10 149 | return out5 150 | -------------------------------------------------------------------------------- /section05_transfer_learning/notebooks/Trainer.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import torch.optim as optim 4 | import torch.utils.data.dataloader as dataloader 5 | import os 6 | 7 | from tqdm.notebook import trange, tqdm 8 | 9 | 10 | class ModelTrainer(nn.Module): 11 | def __init__(self, model, device, loss_fun, batch_size, learning_rate, save_dir, model_name, start_from_checkpoint=False): 12 | # Call the __init__ function of the parent nn.module class 13 | super(ModelTrainer, self).__init__() 14 | self.optimizer = None 15 | self.model = model 16 | 17 | self.device = device 18 | self.loss_fun = loss_fun 19 | self.batch_size = batch_size 20 | self.learning_rate = learning_rate 21 | self.start_epoch = 0 22 | self.best_valid_acc = 0 23 | 24 | self.train_loss_logger = [] 25 | self.train_acc_logger = [] 26 | self.val_acc_logger = [] 27 | 28 | self.train_loader = None 29 | self.test_loader = None 30 | self.valid_loader = None 31 | 32 | self.set_optimizer() 33 | self.save_path = os.path.join(save_dir, model_name + ".pt") 34 | self.save_dir = save_dir 35 | 36 | # Create Save Path from save_dir and model_name, we will save and load our checkpoint here 37 | # Create the save directory if it does note exist 38 | if not os.path.isdir(self.save_dir): 39 | os.makedirs(self.save_dir) 40 | 41 | if start_from_checkpoint: 42 | self.load_checkpoint() 43 | else: 44 | # If checkpoint does exist and start_from_checkpoint = False 45 | # Raise an error to prevent accidental overwriting 46 | if os.path.isfile(self.save_path): 47 | raise ValueError("Warning Checkpoint exists") 48 | else: 49 | print("Starting from scratch") 50 | 51 | def set_optimizer(self): 52 | self.optimizer = optim.Adam(self.parameters(), lr=self.learning_rate) 53 | 54 | def load_checkpoint(self): 55 | # Check if checkpoint exists 56 | if os.path.isfile(self.save_path): 57 | # Load Checkpoint 58 | check_point = torch.load(self.save_path) 59 | 60 | # Checkpoint is saved as a python dictionary 61 | # Here we unpack the dictionary to get our previous training states 62 | self.model.load_state_dict(check_point['model_state_dict']) 63 | self.optimizer.load_state_dict(check_point['optimizer_state_dict']) 64 | 65 | self.start_epoch = check_point['epoch'] 66 | self.best_valid_acc = check_point['best_valid_acc'] 67 | 68 | self.train_loss_logger = check_point['train_loss_logger'] 69 | self.train_acc_logger = check_point['train_acc_logger'] 70 | self.val_acc_logger = check_point['val_acc_logger'] 71 | 72 | print("Checkpoint loaded, starting from epoch:", self.start_epoch) 73 | else: 74 | # Raise Error if it does not exist 75 | raise ValueError("Checkpoint Does not exist") 76 | 77 | def save_checkpoint(self, epoch, valid_acc): 78 | self.best_valid_acc = valid_acc 79 | 80 | torch.save({ 81 | 'epoch': epoch, 82 | 'model_state_dict': self.model.state_dict(), 83 | 'optimizer_state_dict': self.optimizer.state_dict(), 84 | 'best_valid_acc': valid_acc, 85 | 'train_loss_logger': self.train_loss_logger, 86 | 'train_acc_logger': self.train_acc_logger, 87 | 'val_acc_logger': self.val_acc_logger, 88 | }, self.save_path) 89 | 90 | def set_data(self, train_set, test_set, val_set): 91 | 92 | print(f'Number of training examples: {len(train_set)}') 93 | print(f'Number of validation examples: {len(val_set)}') 94 | print(f'Number of testing examples: {len(test_set)}') 95 | 96 | self.train_loader = dataloader.DataLoader(train_set, shuffle=True, batch_size=self.batch_size, num_workers=4) 97 | self.valid_loader = dataloader.DataLoader(val_set, shuffle=False, batch_size=self.batch_size, num_workers=4) 98 | self.test_loader = dataloader.DataLoader(test_set, shuffle=False, batch_size=self.batch_size, num_workers=4) 99 | 100 | # This function should perform a single training epoch using our training data 101 | def train_model(self): 102 | 103 | if self.train_loader is None: 104 | ValueError("Dataset not defined!") 105 | 106 | # Set Network in train mode 107 | self.train() 108 | for i, (x, y) in enumerate(tqdm(self.train_loader, leave=False, desc="Training")): 109 | # Forward pass of image through network and get output 110 | fx = self.forward(x.to(self.device)) 111 | 112 | # Calculate loss using loss function 113 | loss = self.loss_fun(fx, y.to(self.device)) 114 | 115 | # Zero gradients 116 | self.optimizer.zero_grad() 117 | 118 | # Backpropagate gradients 119 | loss.backward() 120 | # Do a single optimization step 121 | self.optimizer.step() 122 | 123 | # Log the loss for plotting 124 | self.train_loss_logger.append(loss.item()) 125 | 126 | # This function should perform a single evaluation epoch, it WILL NOT be used to train our model 127 | def evaluate_model(self, train_test_val="test"): 128 | if self.test_loader is None: 129 | ValueError("Dataset not defined!") 130 | 131 | state = "Evaluating " 132 | if train_test_val == "test": 133 | loader = self.test_loader 134 | state += "Test Set" 135 | elif train_test_val == "train": 136 | loader = self.train_loader 137 | state += "Train Set" 138 | elif train_test_val == "val": 139 | loader = self.valid_loader 140 | state += "Validation Set" 141 | else: 142 | ValueError("Invalid dataset, train_test_val should be train/test/val") 143 | 144 | # Initialise counter 145 | epoch_acc = 0 146 | self.eval() 147 | with torch.no_grad(): 148 | for i, (x, y) in enumerate(tqdm(loader, leave=False, desc=state)): 149 | # Forward pass of image through network 150 | fx = self.forward(x.to(self.device)) 151 | 152 | # Log the cumulative sum of the acc 153 | epoch_acc += (fx.argmax(1) == y.to(self.device)).sum().item() 154 | 155 | # Log the accuracy from the epoch 156 | if train_test_val == "train": 157 | self.train_acc_logger.append(epoch_acc / len(loader.dataset)) 158 | elif train_test_val == "val": 159 | self.val_acc_logger.append(epoch_acc / len(loader.dataset)) 160 | 161 | return epoch_acc / len(loader.dataset) 162 | 163 | def forward(self, x): 164 | return self.model(x) 165 | -------------------------------------------------------------------------------- /section05_transfer_learning/solutions/Trainer.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import torch.optim as optim 4 | import torch.utils.data.dataloader as dataloader 5 | import os 6 | 7 | from tqdm.notebook import trange, tqdm 8 | 9 | 10 | class ModelTrainer(nn.Module): 11 | def __init__(self, model, device, loss_fun, batch_size, learning_rate, save_dir, model_name, start_from_checkpoint=False): 12 | # Call the __init__ function of the parent nn.module class 13 | super(ModelTrainer, self).__init__() 14 | self.optimizer = None 15 | self.model = model 16 | 17 | self.device = device 18 | self.loss_fun = loss_fun 19 | self.batch_size = batch_size 20 | self.learning_rate = learning_rate 21 | self.start_epoch = 0 22 | self.best_valid_acc = 0 23 | 24 | self.train_loss_logger = [] 25 | self.train_acc_logger = [] 26 | self.val_acc_logger = [] 27 | 28 | self.train_loader = None 29 | self.test_loader = None 30 | self.valid_loader = None 31 | 32 | self.set_optimizer() 33 | self.save_path = os.path.join(save_dir, model_name + ".pt") 34 | self.save_dir = save_dir 35 | 36 | # Create Save Path from save_dir and model_name, we will save and load our checkpoint here 37 | # Create the save directory if it does note exist 38 | if not os.path.isdir(self.save_dir): 39 | os.makedirs(self.save_dir) 40 | 41 | if start_from_checkpoint: 42 | self.load_checkpoint() 43 | else: 44 | # If checkpoint does exist and start_from_checkpoint = False 45 | # Raise an error to prevent accidental overwriting 46 | if os.path.isfile(self.save_path): 47 | raise ValueError("Warning Checkpoint exists") 48 | else: 49 | print("Starting from scratch") 50 | 51 | def set_optimizer(self): 52 | self.optimizer = optim.Adam(self.parameters(), lr=self.learning_rate) 53 | 54 | def load_checkpoint(self): 55 | # Check if checkpoint exists 56 | if os.path.isfile(self.save_path): 57 | # Load Checkpoint 58 | check_point = torch.load(self.save_path) 59 | 60 | # Checkpoint is saved as a python dictionary 61 | # Here we unpack the dictionary to get our previous training states 62 | self.model.load_state_dict(check_point['model_state_dict']) 63 | self.optimizer.load_state_dict(check_point['optimizer_state_dict']) 64 | 65 | self.start_epoch = check_point['epoch'] 66 | self.best_valid_acc = check_point['best_valid_acc'] 67 | 68 | self.train_loss_logger = check_point['train_loss_logger'] 69 | self.train_acc_logger = check_point['train_acc_logger'] 70 | self.val_acc_logger = check_point['val_acc_logger'] 71 | 72 | print("Checkpoint loaded, starting from epoch:", self.start_epoch) 73 | else: 74 | # Raise Error if it does not exist 75 | raise ValueError("Checkpoint Does not exist") 76 | 77 | def save_checkpoint(self, epoch, valid_acc): 78 | self.best_valid_acc = valid_acc 79 | 80 | torch.save({ 81 | 'epoch': epoch, 82 | 'model_state_dict': self.model.state_dict(), 83 | 'optimizer_state_dict': self.optimizer.state_dict(), 84 | 'best_valid_acc': valid_acc, 85 | 'train_loss_logger': self.train_loss_logger, 86 | 'train_acc_logger': self.train_acc_logger, 87 | 'val_acc_logger': self.val_acc_logger, 88 | }, self.save_path) 89 | 90 | def set_data(self, train_set, test_set, val_set): 91 | 92 | print(f'Number of training examples: {len(train_set)}') 93 | print(f'Number of validation examples: {len(val_set)}') 94 | print(f'Number of testing examples: {len(test_set)}') 95 | 96 | self.train_loader = dataloader.DataLoader(train_set, shuffle=True, batch_size=self.batch_size, num_workers=4) 97 | self.valid_loader = dataloader.DataLoader(val_set, shuffle=False, batch_size=self.batch_size, num_workers=4) 98 | self.test_loader = dataloader.DataLoader(test_set, shuffle=False, batch_size=self.batch_size, num_workers=4) 99 | 100 | # This function should perform a single training epoch using our training data 101 | def train_model(self): 102 | 103 | if self.train_loader is None: 104 | ValueError("Dataset not defined!") 105 | 106 | # Set Network in train mode 107 | self.train() 108 | for i, (x, y) in enumerate(tqdm(self.train_loader, leave=False, desc="Training")): 109 | # Forward pass of image through network and get output 110 | fx = self.forward(x.to(self.device)) 111 | 112 | # Calculate loss using loss function 113 | loss = self.loss_fun(fx, y.to(self.device)) 114 | 115 | # Zero gradients 116 | self.optimizer.zero_grad() 117 | 118 | # Backpropagate gradients 119 | loss.backward() 120 | # Do a single optimization step 121 | self.optimizer.step() 122 | 123 | # Log the loss for plotting 124 | self.train_loss_logger.append(loss.item()) 125 | 126 | # This function should perform a single evaluation epoch, it WILL NOT be used to train our model 127 | def evaluate_model(self, train_test_val="test"): 128 | if self.test_loader is None: 129 | ValueError("Dataset not defined!") 130 | 131 | state = "Evaluating " 132 | if train_test_val == "test": 133 | loader = self.test_loader 134 | state += "Test Set" 135 | elif train_test_val == "train": 136 | loader = self.train_loader 137 | state += "Train Set" 138 | elif train_test_val == "val": 139 | loader = self.valid_loader 140 | state += "Validation Set" 141 | else: 142 | ValueError("Invalid dataset, train_test_val should be train/test/val") 143 | 144 | # Initialise counter 145 | epoch_acc = 0 146 | self.eval() 147 | with torch.no_grad(): 148 | for i, (x, y) in enumerate(tqdm(loader, leave=False, desc=state)): 149 | # Forward pass of image through network 150 | fx = self.forward(x.to(self.device)) 151 | 152 | # Log the cumulative sum of the acc 153 | epoch_acc += (fx.argmax(1) == y.to(self.device)).sum().item() 154 | 155 | # Log the accuracy from the epoch 156 | if train_test_val == "train": 157 | self.train_acc_logger.append(epoch_acc / len(loader.dataset)) 158 | elif train_test_val == "val": 159 | self.val_acc_logger.append(epoch_acc / len(loader.dataset)) 160 | 161 | return epoch_acc / len(loader.dataset) 162 | 163 | def forward(self, x): 164 | return self.model(x) 165 | -------------------------------------------------------------------------------- /section06_pretraining_augmentations/notebooks/Trainer.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import torch.optim as optim 4 | import torch.utils.data.dataloader as dataloader 5 | 6 | import os 7 | from tqdm.notebook import trange, tqdm 8 | 9 | 10 | class ModelTrainer(nn.Module): 11 | def __init__(self, model, output_size, device, loss_fun, batch_size, learning_rate, save_dir, model_name, 12 | start_from_checkpoint=False, run_evaluate=True): 13 | 14 | # Call the __init__ function of the parent nn.module class 15 | super(ModelTrainer, self).__init__() 16 | self.optimizer = None 17 | 18 | self.device = device 19 | self.loss_fun = loss_fun 20 | self.batch_size = batch_size 21 | self.learning_rate = learning_rate 22 | self.start_epoch = 0 23 | self.best_valid_acc = 0 24 | 25 | self.train_loss_logger = [] 26 | self.train_acc_logger = [] 27 | self.val_acc_logger = [] 28 | 29 | self.train_loader = None 30 | self.test_loader = None 31 | self.valid_loader = None 32 | 33 | self.output_size = output_size 34 | 35 | self.model = self.change_output(model, output_size=output_size) 36 | self.set_optimizer() 37 | self.save_path = os.path.join(save_dir, model_name + ".pt") 38 | self.save_dir = save_dir 39 | 40 | self.lr_schedule = None 41 | self.run_evaluate = run_evaluate 42 | 43 | # Create Save Path from save_dir and model_name, we will save and load our checkpoint here 44 | # Create the save directory if it does note exist 45 | if not os.path.isdir(self.save_dir): 46 | os.makedirs(self.save_dir) 47 | 48 | if start_from_checkpoint: 49 | self.load_checkpoint() 50 | else: 51 | # If checkpoint does exist and start_from_checkpoint = False 52 | # Raise an error to prevent accidental overwriting 53 | if os.path.isfile(self.save_path): 54 | raise ValueError("Warning Checkpoint exists") 55 | else: 56 | print("Starting from scratch") 57 | 58 | def __get_layer__(self, num_ftrs, output_size): 59 | 60 | layer = nn.Linear(num_ftrs, output_size).to(self.device) 61 | 62 | return layer 63 | 64 | def change_output(self, model, output_size): 65 | 66 | if hasattr(model, "fc"): 67 | num_ftrs = model.fc.in_features 68 | model.fc = self.__get_layer__(num_ftrs, output_size) 69 | elif hasattr(model, "classifier"): 70 | if isinstance(model.classifier, nn.Linear): 71 | num_ftrs = model.classifier.in_features 72 | model.classifier = self.__get_layer__(num_ftrs, output_size) 73 | if isinstance(model.classifier, nn.Sequential): 74 | num_ftrs = model.classifier[-1].in_features 75 | model.classifier[-1] = self.__get_layer__(num_ftrs, output_size) 76 | elif hasattr(model, "heads"): 77 | if isinstance(model.heads, nn.Linear): 78 | num_ftrs = model.heads.in_features 79 | model.heads = self.__get_layer__(num_ftrs, output_size) 80 | if isinstance(model.heads, nn.Sequential): 81 | num_ftrs = model.heads[-1].in_features 82 | model.heads[-1] = self.__get_layer__(num_ftrs, output_size) 83 | 84 | return model 85 | 86 | def set_optimizer(self): 87 | self.optimizer = optim.Adam(self.parameters(), lr=self.learning_rate) 88 | 89 | def set_lr_schedule(self, lr_schedule): 90 | self.lr_schedule = lr_schedule 91 | 92 | def load_checkpoint(self): 93 | # Check if checkpoint exists 94 | if os.path.isfile(self.save_path): 95 | # Load Checkpoint 96 | check_point = torch.load(self.save_path) 97 | 98 | # Checkpoint is saved as a python dictionary 99 | # Here we unpack the dictionary to get our previous training states 100 | self.model.load_state_dict(check_point['model_state_dict']) 101 | self.optimizer.load_state_dict(check_point['optimizer_state_dict']) 102 | 103 | self.start_epoch = check_point['epoch'] 104 | self.best_valid_acc = check_point['best_valid_acc'] 105 | 106 | self.train_loss_logger = check_point['train_loss_logger'] 107 | self.train_acc_logger = check_point['train_acc_logger'] 108 | self.val_acc_logger = check_point['val_acc_logger'] 109 | 110 | print("Checkpoint loaded, starting from epoch:", self.start_epoch) 111 | else: 112 | # Raise Error if it does not exist 113 | raise ValueError("Checkpoint Does not exist") 114 | 115 | def save_checkpoint(self, epoch, valid_acc): 116 | self.best_valid_acc = valid_acc 117 | 118 | torch.save({ 119 | 'epoch': epoch, 120 | 'model_state_dict': self.model.state_dict(), 121 | 'optimizer_state_dict': self.optimizer.state_dict(), 122 | 'best_valid_acc': valid_acc, 123 | 'train_loss_logger': self.train_loss_logger, 124 | 'train_acc_logger': self.train_acc_logger, 125 | 'val_acc_logger': self.val_acc_logger, 126 | }, self.save_path) 127 | 128 | def set_data(self, train_set, test_set, val_set): 129 | 130 | print(f'Number of training examples: {len(train_set)}') 131 | print(f'Number of validation examples: {len(val_set)}') 132 | print(f'Number of testing examples: {len(test_set)}') 133 | 134 | self.train_loader = dataloader.DataLoader(train_set, shuffle=True, batch_size=self.batch_size, num_workers=8) 135 | self.valid_loader = dataloader.DataLoader(val_set, shuffle=False, batch_size=self.batch_size, num_workers=8) 136 | self.test_loader = dataloader.DataLoader(test_set, shuffle=False, batch_size=self.batch_size, num_workers=8) 137 | 138 | def run_training(self, num_epochs): 139 | valid_acc = 0 140 | train_acc = 0 141 | 142 | pbar = trange(self.start_epoch, num_epochs, leave=False, desc="Epoch") 143 | for epoch in pbar: 144 | pbar.set_postfix_str('Accuracy: Train %.2f%%, Val %.2f%%' % (train_acc * 100, valid_acc * 100)) 145 | 146 | # Call the training function and pass training dataloader etc 147 | self.train_model() 148 | 149 | if self.run_evaluate: 150 | # Call the modules evaluate function for train and validation set 151 | train_acc = self.evaluate_model(train_test_val="train") 152 | valid_acc = self.evaluate_model(train_test_val="val") 153 | 154 | # Check if the current validation accuracy is greater than the previous best 155 | # If so, then save the model 156 | if valid_acc > self.best_valid_acc: 157 | self.save_checkpoint(epoch, valid_acc) 158 | else: 159 | self.save_checkpoint(epoch, 0) 160 | 161 | if self.lr_schedule is not None: 162 | self.lr_schedule.step() 163 | 164 | # This function should perform a single training epoch using our training data 165 | def train_model(self): 166 | 167 | if self.train_loader is None: 168 | ValueError("Dataset not defined!") 169 | 170 | # Set Network in train mode 171 | self.train() 172 | for i, (x, y) in enumerate(tqdm(self.train_loader, leave=False, desc="Training")): 173 | # Forward pass of image through network and get output 174 | fx = self.forward(x.to(self.device)) 175 | 176 | # Calculate loss using loss function 177 | loss = self.loss_fun(fx, y.to(self.device)) 178 | 179 | # Zero gradients 180 | self.optimizer.zero_grad() 181 | 182 | # Backpropagate gradients 183 | loss.backward() 184 | # Do a single optimization step 185 | self.optimizer.step() 186 | 187 | # Log the loss for plotting 188 | self.train_loss_logger.append(loss.item()) 189 | 190 | # This function should perform a single evaluation epoch, it WILL NOT be used to train our model 191 | def evaluate_model(self, train_test_val="test"): 192 | if self.test_loader is None: 193 | ValueError("Dataset not defined!") 194 | 195 | state = "Evaluating " 196 | if train_test_val == "test": 197 | loader = self.test_loader 198 | state += "Test Set" 199 | elif train_test_val == "train": 200 | loader = self.train_loader 201 | state += "Train Set" 202 | elif train_test_val == "val": 203 | loader = self.valid_loader 204 | state += "Validation Set" 205 | else: 206 | ValueError("Invalid dataset, train_test_val should be train/test/val") 207 | 208 | # Initialise counter 209 | epoch_acc = 0 210 | self.eval() 211 | with torch.no_grad(): 212 | for i, (x, y) in enumerate(tqdm(loader, leave=False, desc=state)): 213 | # Forward pass of image through network 214 | fx = self.forward(x.to(self.device)) 215 | 216 | # Log the cumulative sum of the acc 217 | epoch_acc += (fx.argmax(-1) == y.to(self.device)).sum().item() 218 | 219 | # Log the accuracy from the epoch 220 | if train_test_val == "train": 221 | self.train_acc_logger.append(epoch_acc / len(loader.dataset)) 222 | elif train_test_val == "val": 223 | self.val_acc_logger.append(epoch_acc / len(loader.dataset)) 224 | 225 | return epoch_acc / len(loader.dataset) 226 | 227 | def forward(self, x): 228 | return self.model(x) 229 | -------------------------------------------------------------------------------- /section06_pretraining_augmentations/solutions/Trainer.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import torch.optim as optim 4 | import torch.utils.data.dataloader as dataloader 5 | 6 | import os 7 | from tqdm.notebook import trange, tqdm 8 | 9 | 10 | class ModelTrainer(nn.Module): 11 | def __init__(self, model, output_size, device, loss_fun, batch_size, learning_rate, save_dir, model_name, 12 | start_from_checkpoint=False, run_evaluate=True): 13 | 14 | # Call the __init__ function of the parent nn.module class 15 | super(ModelTrainer, self).__init__() 16 | self.optimizer = None 17 | 18 | self.device = device 19 | self.loss_fun = loss_fun 20 | self.batch_size = batch_size 21 | self.learning_rate = learning_rate 22 | self.start_epoch = 0 23 | self.best_valid_acc = 0 24 | 25 | self.train_loss_logger = [] 26 | self.train_acc_logger = [] 27 | self.val_acc_logger = [] 28 | 29 | self.train_loader = None 30 | self.test_loader = None 31 | self.valid_loader = None 32 | 33 | self.output_size = output_size 34 | 35 | self.model = self.change_output(model, output_size=output_size) 36 | self.set_optimizer() 37 | self.save_path = os.path.join(save_dir, model_name + ".pt") 38 | self.save_dir = save_dir 39 | 40 | self.lr_schedule = None 41 | self.run_evaluate = run_evaluate 42 | 43 | # Create Save Path from save_dir and model_name, we will save and load our checkpoint here 44 | # Create the save directory if it does note exist 45 | if not os.path.isdir(self.save_dir): 46 | os.makedirs(self.save_dir) 47 | 48 | if start_from_checkpoint: 49 | self.load_checkpoint() 50 | else: 51 | # If checkpoint does exist and start_from_checkpoint = False 52 | # Raise an error to prevent accidental overwriting 53 | if os.path.isfile(self.save_path): 54 | raise ValueError("Warning Checkpoint exists") 55 | else: 56 | print("Starting from scratch") 57 | 58 | def __get_layer__(self, num_ftrs, output_size): 59 | 60 | layer = nn.Linear(num_ftrs, output_size).to(self.device) 61 | 62 | return layer 63 | 64 | def change_output(self, model, output_size): 65 | 66 | if hasattr(model, "fc"): 67 | num_ftrs = model.fc.in_features 68 | model.fc = self.__get_layer__(num_ftrs, output_size) 69 | elif hasattr(model, "classifier"): 70 | if isinstance(model.classifier, nn.Linear): 71 | num_ftrs = model.classifier.in_features 72 | model.classifier = self.__get_layer__(num_ftrs, output_size) 73 | if isinstance(model.classifier, nn.Sequential): 74 | num_ftrs = model.classifier[-1].in_features 75 | model.classifier[-1] = self.__get_layer__(num_ftrs, output_size) 76 | elif hasattr(model, "heads"): 77 | if isinstance(model.heads, nn.Linear): 78 | num_ftrs = model.heads.in_features 79 | model.heads = self.__get_layer__(num_ftrs, output_size) 80 | if isinstance(model.heads, nn.Sequential): 81 | num_ftrs = model.heads[-1].in_features 82 | model.heads[-1] = self.__get_layer__(num_ftrs, output_size) 83 | 84 | return model 85 | 86 | def set_optimizer(self): 87 | self.optimizer = optim.Adam(self.parameters(), lr=self.learning_rate) 88 | 89 | def set_lr_schedule(self, lr_schedule): 90 | self.lr_schedule = lr_schedule 91 | 92 | def load_checkpoint(self): 93 | # Check if checkpoint exists 94 | if os.path.isfile(self.save_path): 95 | # Load Checkpoint 96 | check_point = torch.load(self.save_path) 97 | 98 | # Checkpoint is saved as a python dictionary 99 | # Here we unpack the dictionary to get our previous training states 100 | self.model.load_state_dict(check_point['model_state_dict']) 101 | self.optimizer.load_state_dict(check_point['optimizer_state_dict']) 102 | 103 | self.start_epoch = check_point['epoch'] 104 | self.best_valid_acc = check_point['best_valid_acc'] 105 | 106 | self.train_loss_logger = check_point['train_loss_logger'] 107 | self.train_acc_logger = check_point['train_acc_logger'] 108 | self.val_acc_logger = check_point['val_acc_logger'] 109 | 110 | print("Checkpoint loaded, starting from epoch:", self.start_epoch) 111 | else: 112 | # Raise Error if it does not exist 113 | raise ValueError("Checkpoint Does not exist") 114 | 115 | def save_checkpoint(self, epoch, valid_acc): 116 | self.best_valid_acc = valid_acc 117 | 118 | torch.save({ 119 | 'epoch': epoch, 120 | 'model_state_dict': self.model.state_dict(), 121 | 'optimizer_state_dict': self.optimizer.state_dict(), 122 | 'best_valid_acc': valid_acc, 123 | 'train_loss_logger': self.train_loss_logger, 124 | 'train_acc_logger': self.train_acc_logger, 125 | 'val_acc_logger': self.val_acc_logger, 126 | }, self.save_path) 127 | 128 | def set_data(self, train_set, test_set, val_set): 129 | 130 | print(f'Number of training examples: {len(train_set)}') 131 | print(f'Number of validation examples: {len(val_set)}') 132 | print(f'Number of testing examples: {len(test_set)}') 133 | 134 | self.train_loader = dataloader.DataLoader(train_set, shuffle=True, batch_size=self.batch_size, num_workers=8) 135 | self.valid_loader = dataloader.DataLoader(val_set, shuffle=False, batch_size=self.batch_size, num_workers=8) 136 | self.test_loader = dataloader.DataLoader(test_set, shuffle=False, batch_size=self.batch_size, num_workers=8) 137 | 138 | def run_training(self, num_epochs): 139 | valid_acc = 0 140 | train_acc = 0 141 | 142 | pbar = trange(self.start_epoch, num_epochs, leave=False, desc="Epoch") 143 | for epoch in pbar: 144 | pbar.set_postfix_str('Accuracy: Train %.2f%%, Val %.2f%%' % (train_acc * 100, valid_acc * 100)) 145 | 146 | # Call the training function and pass training dataloader etc 147 | self.train_model() 148 | 149 | if self.run_evaluate: 150 | # Call the modules evaluate function for train and validation set 151 | train_acc = self.evaluate_model(train_test_val="train") 152 | valid_acc = self.evaluate_model(train_test_val="val") 153 | 154 | # Check if the current validation accuracy is greater than the previous best 155 | # If so, then save the model 156 | if valid_acc > self.best_valid_acc: 157 | self.save_checkpoint(epoch, valid_acc) 158 | else: 159 | self.save_checkpoint(epoch, 0) 160 | 161 | if self.lr_schedule is not None: 162 | self.lr_schedule.step() 163 | 164 | # This function should perform a single training epoch using our training data 165 | def train_model(self): 166 | 167 | if self.train_loader is None: 168 | ValueError("Dataset not defined!") 169 | 170 | # Set Network in train mode 171 | self.train() 172 | for i, (x, y) in enumerate(tqdm(self.train_loader, leave=False, desc="Training")): 173 | # Forward pass of image through network and get output 174 | fx = self.forward(x.to(self.device)) 175 | 176 | # Calculate loss using loss function 177 | loss = self.loss_fun(fx, y.to(self.device)) 178 | 179 | # Zero gradients 180 | self.optimizer.zero_grad() 181 | 182 | # Backpropagate gradients 183 | loss.backward() 184 | # Do a single optimization step 185 | self.optimizer.step() 186 | 187 | # Log the loss for plotting 188 | self.train_loss_logger.append(loss.item()) 189 | 190 | # This function should perform a single evaluation epoch, it WILL NOT be used to train our model 191 | def evaluate_model(self, train_test_val="test"): 192 | if self.test_loader is None: 193 | ValueError("Dataset not defined!") 194 | 195 | state = "Evaluating " 196 | if train_test_val == "test": 197 | loader = self.test_loader 198 | state += "Test Set" 199 | elif train_test_val == "train": 200 | loader = self.train_loader 201 | state += "Train Set" 202 | elif train_test_val == "val": 203 | loader = self.valid_loader 204 | state += "Validation Set" 205 | else: 206 | ValueError("Invalid dataset, train_test_val should be train/test/val") 207 | 208 | # Initialise counter 209 | epoch_acc = 0 210 | self.eval() 211 | with torch.no_grad(): 212 | for i, (x, y) in enumerate(tqdm(loader, leave=False, desc=state)): 213 | # Forward pass of image through network 214 | fx = self.forward(x.to(self.device)) 215 | 216 | # Log the cumulative sum of the acc 217 | epoch_acc += (fx.argmax(-1) == y.to(self.device)).sum().item() 218 | 219 | # Log the accuracy from the epoch 220 | if train_test_val == "train": 221 | self.train_acc_logger.append(epoch_acc / len(loader.dataset)) 222 | elif train_test_val == "val": 223 | self.val_acc_logger.append(epoch_acc / len(loader.dataset)) 224 | 225 | return epoch_acc / len(loader.dataset) 226 | 227 | def forward(self, x): 228 | return self.model(x) 229 | -------------------------------------------------------------------------------- /section07_autoencoders/data/VAE_3d.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section07_autoencoders/data/VAE_3d.gif -------------------------------------------------------------------------------- /section08_detection/solutions/Datasets.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from torch.utils.data.dataset import Dataset 3 | 4 | import os 5 | import pandas as pd 6 | import cv2 7 | 8 | 9 | class CUB200(Dataset): 10 | def __init__(self, data_set_root, image_size, transform, test_train=1, return_masks=False): 11 | 12 | # Dataset found here 13 | # https://www.kaggle.com/datasets/wenewone/cub2002011 14 | 15 | class_list_path = os.path.join(data_set_root, "CUB_200_2011/CUB_200_2011/image_class_labels.txt") 16 | self.data_df = pd.read_csv(class_list_path, sep=" ", names=["index", "class"]) 17 | 18 | data_list_path = os.path.join(data_set_root, "CUB_200_2011/CUB_200_2011/images.txt") 19 | cub200_df = pd.read_csv(data_list_path, sep=" ", names=["index", "file_path"]) 20 | 21 | bbox_list_path = os.path.join(data_set_root, "CUB_200_2011/CUB_200_2011/bounding_boxes.txt") 22 | bbox_df = pd.read_csv(bbox_list_path, sep=" ", names=["index", "x", "y", "width", "height"]) 23 | 24 | # Use custom test/train split 1/0 25 | split_df = pd.read_csv("test_train_split.txt", sep=" ", names=["index", "split"]) 26 | 27 | self.data_df = self.data_df.merge(cub200_df, left_on='index', right_on='index') 28 | self.data_df = self.data_df.merge(bbox_df, left_on='index', right_on='index') 29 | self.data_df = self.data_df.merge(split_df, left_on='index', right_on='index') 30 | 31 | self.data_df = self.data_df[self.data_df.split != test_train] 32 | 33 | self.return_masks = return_masks 34 | self.image_size = image_size 35 | self.transform = transform 36 | 37 | self.data_set_root = data_set_root 38 | self.image_root_dir = os.path.join(self.data_set_root, "CUB_200_2011/CUB_200_2011/images") 39 | self.mask_root_dir = os.path.join(self.data_set_root, "segmentations") 40 | 41 | def get_bbox_list(self, data, img_size): 42 | bbox_array = [data["x"], 43 | data["y"], 44 | data["width"], 45 | data["height"]] 46 | 47 | if (bbox_array[0] + bbox_array[2]) > img_size[1]: 48 | bbox_array[2] = img_size[1] - bbox_array[0] 49 | 50 | if (bbox_array[1] + bbox_array[3]) > img_size[0]: 51 | bbox_array[3] = img_size[0] - bbox_array[1] 52 | 53 | return [bbox_array] 54 | 55 | def get_output_tensors(self, data_out): 56 | if len(data_out["bboxes"]) > 0: 57 | bbox = torch.FloatTensor(data_out["bboxes"][0]) / self.image_size 58 | label = data_out["class_labels"][0] 59 | else: 60 | bbox = torch.zeros(4) 61 | label = -1 62 | 63 | return bbox, [label] 64 | 65 | def __getitem__(self, index): 66 | data_series = self.data_df.iloc("index")[index] 67 | file_path = data_series["file_path"] 68 | label = data_series["class"] 69 | 70 | img_path = os.path.join(self.image_root_dir, file_path) 71 | image = cv2.imread(img_path) 72 | image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) 73 | bbox_array = self.get_bbox_list(data_series, image.shape) 74 | 75 | if self.return_masks: 76 | mask_path = os.path.join(self.mask_root_dir, file_path).split(".jpg")[0] + ".png" 77 | mask = cv2.imread(mask_path) 78 | 79 | data_out = self.transform(image=image, bboxes=bbox_array, mask=mask, class_labels=[label]) 80 | bbox, label = self.get_output_tensors(data_out) 81 | mask = (data_out["mask"][:, :, 0] > 100).long() 82 | 83 | return data_out["image"], mask, bbox, label 84 | else: 85 | data_out = self.transform(image=image, bboxes=bbox_array, class_labels=[label]) 86 | bbox, label = self.get_output_tensors(data_out) 87 | 88 | return data_out["image"], bbox, label 89 | 90 | def __len__(self): 91 | return len(self.data_df) -------------------------------------------------------------------------------- /section08_detection/solutions/Trainer.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import torch.optim as optim 4 | import torch.utils.data.dataloader as dataloader 5 | 6 | import os 7 | from tqdm.notebook import trange, tqdm 8 | 9 | 10 | class ModelTrainer(nn.Module): 11 | def __init__(self, model, output_size, device, loss_fun, batch_size, learning_rate, save_dir, model_name, 12 | eval_metric, start_from_checkpoint=False, run_evaluate=True): 13 | 14 | # Call the __init__ function of the parent nn.module class 15 | super(ModelTrainer, self).__init__() 16 | self.optimizer = None 17 | 18 | self.device = device 19 | self.loss_fun = loss_fun 20 | self.batch_size = batch_size 21 | self.learning_rate = learning_rate 22 | self.start_epoch = 0 23 | self.best_valid_acc = 0 24 | 25 | self.train_loss_logger = [] 26 | self.train_acc_logger = [] 27 | self.val_acc_logger = [] 28 | 29 | self.train_loader = None 30 | self.test_loader = None 31 | self.valid_loader = None 32 | 33 | self.output_size = output_size 34 | 35 | self.model = self.change_output(model, output_size=output_size) 36 | self.set_optimizer() 37 | self.scaler = torch.cuda.amp.GradScaler() 38 | 39 | self.save_path = os.path.join(save_dir, model_name + ".pt") 40 | self.save_dir = save_dir 41 | 42 | self.lr_schedule = None 43 | self.run_evaluate = run_evaluate 44 | self.eval_metric = eval_metric 45 | 46 | # Create Save Path from save_dir and model_name, we will save and load our checkpoint here 47 | # Create the save directory if it does note exist 48 | if not os.path.isdir(self.save_dir): 49 | os.makedirs(self.save_dir) 50 | 51 | if start_from_checkpoint: 52 | self.load_checkpoint() 53 | else: 54 | # If checkpoint does exist and start_from_checkpoint = False 55 | # Raise an error to prevent accidental overwriting 56 | if os.path.isfile(self.save_path): 57 | raise ValueError("Warning Checkpoint exists") 58 | else: 59 | print("Starting from scratch") 60 | 61 | def __get_layer__(self, num_ftrs, output_size): 62 | 63 | layer = nn.Linear(num_ftrs, output_size).to(self.device) 64 | 65 | return layer 66 | 67 | def change_output(self, model, output_size): 68 | 69 | if output_size > 0: 70 | if hasattr(model, "fc"): 71 | num_ftrs = model.fc.in_features 72 | model.fc = self.__get_layer__(num_ftrs, output_size) 73 | elif hasattr(model, "classifier"): 74 | if isinstance(model.classifier, nn.Linear): 75 | num_ftrs = model.classifier.in_features 76 | model.classifier = self.__get_layer__(num_ftrs, output_size) 77 | if isinstance(model.classifier, nn.Sequential): 78 | num_ftrs = model.classifier[-1].in_features 79 | model.classifier[-1] = self.__get_layer__(num_ftrs, output_size) 80 | elif hasattr(model, "heads"): 81 | if isinstance(model.heads, nn.Linear): 82 | num_ftrs = model.heads.in_features 83 | model.heads = self.__get_layer__(num_ftrs, output_size) 84 | if isinstance(model.heads, nn.Sequential): 85 | num_ftrs = model.heads[-1].in_features 86 | model.heads[-1] = self.__get_layer__(num_ftrs, output_size) 87 | 88 | return model 89 | 90 | def set_optimizer(self): 91 | self.optimizer = optim.Adam(self.parameters(), lr=self.learning_rate) 92 | 93 | def set_lr_schedule(self, lr_schedule): 94 | self.lr_schedule = lr_schedule 95 | 96 | def load_checkpoint(self): 97 | # Check if checkpoint exists 98 | if os.path.isfile(self.save_path): 99 | # Load Checkpoint 100 | check_point = torch.load(self.save_path) 101 | 102 | # Checkpoint is saved as a python dictionary 103 | # Here we unpack the dictionary to get our previous training states 104 | self.model.load_state_dict(check_point['model_state_dict']) 105 | self.optimizer.load_state_dict(check_point['optimizer_state_dict']) 106 | 107 | self.start_epoch = check_point['epoch'] 108 | self.best_valid_acc = check_point['best_valid_acc'] 109 | 110 | self.train_loss_logger = check_point['train_loss_logger'] 111 | self.train_acc_logger = check_point['train_acc_logger'] 112 | self.val_acc_logger = check_point['val_acc_logger'] 113 | 114 | print("Checkpoint loaded, starting from epoch:", self.start_epoch) 115 | else: 116 | # Raise Error if it does not exist 117 | raise ValueError("Checkpoint Does not exist") 118 | 119 | def save_checkpoint(self, epoch, valid_acc): 120 | self.best_valid_acc = valid_acc 121 | 122 | torch.save({ 123 | 'epoch': epoch, 124 | 'model_state_dict': self.model.state_dict(), 125 | 'optimizer_state_dict': self.optimizer.state_dict(), 126 | 'best_valid_acc': valid_acc, 127 | 'train_loss_logger': self.train_loss_logger, 128 | 'train_acc_logger': self.train_acc_logger, 129 | 'val_acc_logger': self.val_acc_logger, 130 | }, self.save_path) 131 | 132 | def set_data(self, train_set, test_set, val_set): 133 | 134 | print(f'Number of training examples: {len(train_set)}') 135 | print(f'Number of validation examples: {len(val_set)}') 136 | print(f'Number of testing examples: {len(test_set)}') 137 | 138 | self.train_loader = dataloader.DataLoader(train_set, shuffle=True, batch_size=self.batch_size, num_workers=8) 139 | self.valid_loader = dataloader.DataLoader(val_set, shuffle=True, batch_size=self.batch_size, num_workers=8) 140 | self.test_loader = dataloader.DataLoader(test_set, shuffle=True, batch_size=self.batch_size, num_workers=8) 141 | 142 | def run_training(self, num_epochs): 143 | valid_acc = 0 144 | train_acc = 0 145 | 146 | pbar = trange(self.start_epoch, num_epochs, leave=False, desc="Epoch") 147 | for epoch in pbar: 148 | pbar.set_postfix_str('IoU: Train %.2f, Val %.2f' % (train_acc, valid_acc)) 149 | 150 | # Call the training function and pass training dataloader etc 151 | self.train_model() 152 | 153 | if self.run_evaluate: 154 | # Call the modules evaluate function for train and validation set 155 | train_acc = self.evaluate_model(train_test_val="train") 156 | valid_acc = self.evaluate_model(train_test_val="val") 157 | 158 | # Check if the current validation accuracy is greater than the previous best 159 | # If so, then save the model 160 | if valid_acc > self.best_valid_acc: 161 | self.save_checkpoint(epoch, valid_acc) 162 | else: 163 | self.save_checkpoint(epoch, 0) 164 | 165 | if self.lr_schedule is not None: 166 | self.lr_schedule.step() 167 | 168 | # This function should perform a single training epoch using our training data 169 | def train_model(self): 170 | 171 | if self.train_loader is None: 172 | ValueError("Dataset not defined!") 173 | 174 | # Set Network in train mode 175 | self.train() 176 | for i, data in enumerate(tqdm(self.train_loader, leave=False, desc="Training")): 177 | with torch.cuda.amp.autocast(): 178 | # Forward pass of image through network and get output 179 | fx = self.forward(data[0].to(self.device)) 180 | 181 | # Calculate loss using loss function 182 | loss = self.loss_fun(fx, data[1].to(self.device)) 183 | 184 | # Zero gradients 185 | self.optimizer.zero_grad() 186 | # Backpropagate gradients 187 | self.scaler.scale(loss).backward() 188 | # Clip grads 189 | self.scaler.unscale_(self.optimizer) 190 | torch.nn.utils.clip_grad_norm_(self.parameters(), 5) 191 | # Do a single optimization step 192 | self.scaler.step(self.optimizer) 193 | self.scaler.update() 194 | 195 | # Log the loss for plotting 196 | self.train_loss_logger.append(loss.item()) 197 | 198 | # This function should perform a single evaluation epoch, it WILL NOT be used to train our model 199 | def evaluate_model(self, train_test_val="test"): 200 | if self.test_loader is None: 201 | ValueError("Dataset not defined!") 202 | 203 | state = "Evaluating " 204 | if train_test_val == "test": 205 | loader = self.test_loader 206 | state += "Test Set" 207 | elif train_test_val == "train": 208 | loader = self.train_loader 209 | state += "Train Set" 210 | elif train_test_val == "val": 211 | loader = self.valid_loader 212 | state += "Validation Set" 213 | else: 214 | ValueError("Invalid dataset, train_test_val should be train/test/val") 215 | 216 | # Initialise counter 217 | epoch_acc = 0 218 | self.eval() 219 | with torch.no_grad(): 220 | for i, data in enumerate(tqdm(loader, leave=False, desc=state)): 221 | with torch.cuda.amp.autocast(): 222 | # Forward pass of image through network 223 | fx = self.forward(data[0].to(self.device)) 224 | 225 | # Log the cumulative sum of the acc 226 | epoch_acc += self.eval_metric(fx, data).sum().cpu().item() 227 | 228 | # Log the accuracy from the epoch 229 | if train_test_val == "train": 230 | self.train_acc_logger.append(epoch_acc / len(loader.dataset)) 231 | elif train_test_val == "val": 232 | self.val_acc_logger.append(epoch_acc / len(loader.dataset)) 233 | 234 | return epoch_acc / len(loader.dataset) 235 | 236 | def forward(self, x): 237 | return self.model(x) 238 | -------------------------------------------------------------------------------- /section09_generation/data/GAN.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section09_generation/data/GAN.jpg -------------------------------------------------------------------------------- /section09_generation/data/MNIST_GAN_DEMO.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section09_generation/data/MNIST_GAN_DEMO.gif -------------------------------------------------------------------------------- /section09_generation/data/ob_bg_mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section09_generation/data/ob_bg_mask.png -------------------------------------------------------------------------------- /section09_generation/data/ob_face_mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section09_generation/data/ob_face_mask.png -------------------------------------------------------------------------------- /section09_generation/data/obama.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section09_generation/data/obama.png -------------------------------------------------------------------------------- /section09_generation/solutions/Extract_Features.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "1981556d", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "import numpy as np\n", 11 | "import os\n", 12 | "from tqdm import tqdm\n", 13 | "\n", 14 | "import torch\n", 15 | "torch.backends.cuda.matmul.allow_tf32 = True\n", 16 | "torch.backends.cudnn.allow_tf32 = True\n", 17 | "\n", 18 | "from torch.utils.data import DataLoader\n", 19 | "from torchvision.datasets import ImageFolder\n", 20 | "from torchvision import transforms\n", 21 | "\n", 22 | "from diffusers.models import AutoencoderKL" 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "execution_count": null, 28 | "id": "3addd034", 29 | "metadata": {}, 30 | "outputs": [], 31 | "source": [ 32 | "use_cuda = torch.cuda.is_available()\n", 33 | "gpu_indx = 0\n", 34 | "device = torch.device(gpu_indx if use_cuda else \"cpu\")" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": null, 40 | "id": "18738d57", 41 | "metadata": {}, 42 | "outputs": [], 43 | "source": [ 44 | "image_size = 256\n", 45 | "assert image_size % 8 == 0, \"Image size must be divisible by 8 (for the VAE encoder).\"\n", 46 | "\n", 47 | "batch_size = 32\n", 48 | "dataset_dir = \".\"\"\n", 49 | "latent_save_dir = \".\"\n", 50 | "os.makedirs(latent_save_dir, exist_ok=True)" 51 | ] 52 | }, 53 | { 54 | "cell_type": "code", 55 | "execution_count": null, 56 | "id": "47972a2b", 57 | "metadata": {}, 58 | "outputs": [], 59 | "source": [ 60 | "vae = AutoencoderKL.from_pretrained(\"stabilityai/sd-vae-ft-ema\").to(device)" 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": null, 66 | "id": "af2c8a97", 67 | "metadata": {}, 68 | "outputs": [], 69 | "source": [ 70 | "# Setup data:\n", 71 | "transform = transforms.Compose([\n", 72 | " transforms.Resize(image_size),\n", 73 | " transforms.CenterCrop(image_size),\n", 74 | " transforms.ToTensor(),\n", 75 | " transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5], inplace=True)\n", 76 | "])\n", 77 | "\n", 78 | "dataset = ImageFolder(dataset_dir, transform=transform)\n", 79 | "data_loader = DataLoader(dataset, batch_size=batch_size, shuffle=False, num_workers=8, pin_memory=True)" 80 | ] 81 | }, 82 | { 83 | "cell_type": "code", 84 | "execution_count": null, 85 | "id": "dae3d7ae", 86 | "metadata": {}, 87 | "outputs": [], 88 | "source": [ 89 | "img_index = 0\n", 90 | "with torch.no_grad():\n", 91 | " for x, y in tqdm(data_loader, leave=False):\n", 92 | " x = x.to(device)\n", 93 | " y = y.to(device)\n", 94 | " with torch.cuda.amp.autocast():\n", 95 | " # Map input images to latent space + normalize latents:\n", 96 | " latent_features = vae.encode(x).latent_dist.sample().mul_(0.18215)\n", 97 | " latent_features = latent_features.detach().cpu() # (bs, 4, image_size//8, image_size//8)\n", 98 | "\n", 99 | " for latent in latent_features.split(1, 0):\n", 100 | " np.save(latent_save_dir + f'/{img_index}.npy', latent.squeeze(0).numpy())\n", 101 | " img_index += 1" 102 | ] 103 | } 104 | ], 105 | "metadata": { 106 | "kernelspec": { 107 | "display_name": "Python 3 (ipykernel)", 108 | "language": "python", 109 | "name": "python3" 110 | }, 111 | "language_info": { 112 | "codemirror_mode": { 113 | "name": "ipython", 114 | "version": 3 115 | }, 116 | "file_extension": ".py", 117 | "mimetype": "text/x-python", 118 | "name": "python", 119 | "nbconvert_exporter": "python", 120 | "pygments_lexer": "ipython3", 121 | "version": "3.9.16" 122 | } 123 | }, 124 | "nbformat": 4, 125 | "nbformat_minor": 5 126 | } 127 | -------------------------------------------------------------------------------- /section10_interpretation/data/Breast-Cancer-Ribbon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section10_interpretation/data/Breast-Cancer-Ribbon.png -------------------------------------------------------------------------------- /section10_interpretation/data/fruit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section10_interpretation/data/fruit.jpg -------------------------------------------------------------------------------- /section10_interpretation/data/husky_wolf1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section10_interpretation/data/husky_wolf1.png -------------------------------------------------------------------------------- /section10_interpretation/data/husky_wolf2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section10_interpretation/data/husky_wolf2.png -------------------------------------------------------------------------------- /section10_interpretation/data/lime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section10_interpretation/data/lime.png -------------------------------------------------------------------------------- /section10_interpretation/data/lime_logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section10_interpretation/data/lime_logo.jpg -------------------------------------------------------------------------------- /section10_interpretation/data/penguin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section10_interpretation/data/penguin.jpg -------------------------------------------------------------------------------- /section10_interpretation/data/puppy_kitten.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section10_interpretation/data/puppy_kitten.jpg -------------------------------------------------------------------------------- /section10_interpretation/data/super_pixels.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section10_interpretation/data/super_pixels.jpg -------------------------------------------------------------------------------- /section11_rl/data/PPO_Clipping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section11_rl/data/PPO_Clipping.png -------------------------------------------------------------------------------- /section11_rl/data/PROCGEN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section11_rl/data/PROCGEN.png -------------------------------------------------------------------------------- /section11_rl/data/coinrun_easy_rollout.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section11_rl/data/coinrun_easy_rollout.gif -------------------------------------------------------------------------------- /section11_rl/notebooks/Cartpole/Cartpole_DQN.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import math\n", 10 | "import random\n", 11 | "\n", 12 | "import gym\n", 13 | "import numpy as np\n", 14 | "\n", 15 | "import torch\n", 16 | "import torch.nn as nn\n", 17 | "import torch.optim as optim\n", 18 | "import torch.nn.functional as F\n", 19 | "from torch.distributions import Normal, Categorical\n", 20 | "from IPython.display import clear_output\n", 21 | "import matplotlib.pyplot as plt\n", 22 | "\n", 23 | "from collections import deque" 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": null, 29 | "metadata": {}, 30 | "outputs": [], 31 | "source": [ 32 | "env_name = 'CartPole-v0'\n", 33 | "env = gym.make(env_name)" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": null, 39 | "metadata": {}, 40 | "outputs": [], 41 | "source": [ 42 | "class QNet(nn.Module):\n", 43 | " def __init__(self, action_num=2, hidden_size=256):\n", 44 | " super(QNet, self).__init__()\n", 45 | " self.fc1 = nn.Linear(4, hidden_size)\n", 46 | " self.fc2 = nn.Linear(hidden_size, action_num)\n", 47 | "\n", 48 | " def forward(self, x):\n", 49 | " x = F.relu(self.fc1(x))\n", 50 | " q_values = self.fc2(x)\n", 51 | " return q_values" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": null, 57 | "metadata": {}, 58 | "outputs": [], 59 | "source": [ 60 | "class ReplayBuffer:\n", 61 | " def __init__(self, data_names, buffer_size, mini_batch_size):\n", 62 | " self.data_keys = data_names\n", 63 | " self.data_dict = {}\n", 64 | " self.buffer_size = buffer_size\n", 65 | " self.mini_batch_size = mini_batch_size\n", 66 | " self.reset()\n", 67 | "\n", 68 | " def reset(self):\n", 69 | " # Create a deque for each data type with set max length\n", 70 | " for name in self.data_keys:\n", 71 | " self.data_dict[name] = deque(maxlen=self.buffer_size)\n", 72 | "\n", 73 | " def buffer_full(self):\n", 74 | " return len(self.data_dict[self.data_keys[0]]) == self.buffer_size\n", 75 | "\n", 76 | " def data_log(self, data_name, data):\n", 77 | " # split tensor along batch into a list of individual datapoints\n", 78 | " data = data.cpu().split(1)\n", 79 | " # Extend the deque for data type, deque will handle popping old data to maintain buffer size\n", 80 | " self.data_dict[data_name].extend(data)\n", 81 | "\n", 82 | " def __iter__(self):\n", 83 | " batch_size = len(self.data_dict[self.data_keys[0]])\n", 84 | " batch_size = batch_size - batch_size % self.mini_batch_size\n", 85 | "\n", 86 | " ids = np.random.permutation(batch_size)\n", 87 | " ids = np.split(ids, batch_size // self.mini_batch_size)\n", 88 | " for i in range(len(ids)):\n", 89 | " batch_dict = {}\n", 90 | " for name in self.data_keys:\n", 91 | " c = [self.data_dict[name][j] for j in ids[i]]\n", 92 | " batch_dict[name] = torch.cat(c)\n", 93 | " batch_dict[\"batch_size\"] = len(ids[i])\n", 94 | " yield batch_dict\n", 95 | "\n", 96 | " def __len__(self):\n", 97 | " return len(self.data_dict[self.data_keys[0]])" 98 | ] 99 | }, 100 | { 101 | "cell_type": "code", 102 | "execution_count": null, 103 | "metadata": {}, 104 | "outputs": [], 105 | "source": [ 106 | "def test_agent():\n", 107 | " done = False\n", 108 | " total_reward = 0\n", 109 | " observation = torch.FloatTensor(env.reset()).unsqueeze(0)\n", 110 | "\n", 111 | " with torch.no_grad():\n", 112 | " while not done:\n", 113 | " q_values = q_net(observation)\n", 114 | " action = q_values.argmax().cpu().item()\n", 115 | " observation, reward, done, info = env.step(action)\n", 116 | " \n", 117 | " observation = torch.FloatTensor(observation).unsqueeze(0)\n", 118 | " total_reward += reward\n", 119 | " \n", 120 | " return total_reward" 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": null, 126 | "metadata": {}, 127 | "outputs": [], 128 | "source": [ 129 | "def dqn_update():\n", 130 | " for data_batch in replay_buffer:\n", 131 | " next_q_values = q_net(data_batch[\"next_states\"]).detach()\n", 132 | " q_values = q_net(data_batch[\"states\"])\n", 133 | " \n", 134 | " index_q_values = q_values.gather(1, data_batch[\"actions\"])\n", 135 | " max_next_q_values = next_q_values.max(1)[0].unsqueeze(1)\n", 136 | "\n", 137 | " expected_q_value = data_batch[\"rewards\"] + gamma * max_next_q_values * data_batch[\"masks\"]\n", 138 | "\n", 139 | " q_loss = (index_q_values - expected_q_value).pow(2).mean()\n", 140 | "\n", 141 | " optimizer.zero_grad()\n", 142 | " q_loss.backward()\n", 143 | " optimizer.step()" 144 | ] 145 | }, 146 | { 147 | "cell_type": "code", 148 | "execution_count": null, 149 | "metadata": {}, 150 | "outputs": [], 151 | "source": [ 152 | "lr = 1e-2\n", 153 | "gamma = 0.99\n", 154 | "buffer_size = 2000\n", 155 | "mini_batch_size = 32\n", 156 | "max_steps = 15000\n", 157 | "\n", 158 | "data_names = [\"states\", \"next_states\", \"actions\", \"rewards\", \"masks\"]\n", 159 | "\n", 160 | "q_net = QNet()\n", 161 | "optimizer = optim.Adam(q_net.parameters(), lr=lr)\n", 162 | "\n", 163 | "replay_buffer = ReplayBuffer(data_names, buffer_size, mini_batch_size)" 164 | ] 165 | }, 166 | { 167 | "cell_type": "code", 168 | "execution_count": null, 169 | "metadata": {}, 170 | "outputs": [], 171 | "source": [ 172 | "rollouts = 0\n", 173 | "step = 0\n", 174 | "initial_epsilon = 1\n", 175 | "epsilon = initial_epsilon\n", 176 | "score_logger = []" 177 | ] 178 | }, 179 | { 180 | "cell_type": "code", 181 | "execution_count": null, 182 | "metadata": {}, 183 | "outputs": [], 184 | "source": [ 185 | "while step < max_steps:\n", 186 | " observation = torch.FloatTensor(env.reset()).unsqueeze(0)\n", 187 | " done = False\n", 188 | " \n", 189 | " states = []\n", 190 | " rewards = []\n", 191 | " actions = []\n", 192 | " masks = []\n", 193 | "\n", 194 | " while not done:\n", 195 | " states.append(observation)\n", 196 | "\n", 197 | " if random.random() > epsilon:\n", 198 | " q_values = q_net(observation)\n", 199 | " action = q_values.argmax().reshape(-1, 1)\n", 200 | " else:\n", 201 | " action = torch.LongTensor([env.action_space.sample()]).reshape(-1, 1)\n", 202 | " \n", 203 | " observation, reward, done, info = env.step(action.cpu().item())\n", 204 | " \n", 205 | " reward = torch.FloatTensor([reward]).unsqueeze(0)\n", 206 | " \n", 207 | " rewards.append(reward)\n", 208 | " actions.append(action)\n", 209 | " masks.append(torch.FloatTensor([1 - done]).unsqueeze(0))\n", 210 | " \n", 211 | " observation = torch.FloatTensor(observation).unsqueeze(0)\n", 212 | " step += 1\n", 213 | " \n", 214 | " states.append(observation)\n", 215 | " \n", 216 | " replay_buffer.data_log(\"states\", torch.cat(states[:-1]))\n", 217 | " replay_buffer.data_log(\"next_states\", torch.cat(states[1:]))\n", 218 | " replay_buffer.data_log(\"rewards\", torch.cat(rewards))\n", 219 | " replay_buffer.data_log(\"actions\", torch.cat(actions))\n", 220 | " replay_buffer.data_log(\"masks\", torch.cat(masks))\n", 221 | "\n", 222 | " if replay_buffer.buffer_full():\n", 223 | " dqn_update()\n", 224 | "\n", 225 | " if rollouts % 2 == 0:\n", 226 | " new_lr = max(1e-4, ((max_steps - step)/max_steps) * lr)\n", 227 | " epsilon = max(0.2, ((max_steps - step)/max_steps) * initial_epsilon)\n", 228 | " \n", 229 | " optimizer.param_groups[0][\"lr\"] = new_lr\n", 230 | "\n", 231 | " score_logger.append(np.mean([test_agent() for _ in range(10)]))\n", 232 | " clear_output(True)\n", 233 | " plt.plot(score_logger)\n", 234 | " plt.show()\n", 235 | " rollouts +=1\n", 236 | "\n", 237 | "env.close()" 238 | ] 239 | }, 240 | { 241 | "cell_type": "code", 242 | "execution_count": null, 243 | "metadata": {}, 244 | "outputs": [], 245 | "source": [ 246 | "plt.plot(score_logger)" 247 | ] 248 | } 249 | ], 250 | "metadata": { 251 | "kernelspec": { 252 | "display_name": "Python 3", 253 | "language": "python", 254 | "name": "python3" 255 | }, 256 | "language_info": { 257 | "codemirror_mode": { 258 | "name": "ipython", 259 | "version": 3 260 | }, 261 | "file_extension": ".py", 262 | "mimetype": "text/x-python", 263 | "name": "python", 264 | "nbconvert_exporter": "python", 265 | "pygments_lexer": "ipython3", 266 | "version": "3.6.9" 267 | } 268 | }, 269 | "nbformat": 4, 270 | "nbformat_minor": 2 271 | } 272 | -------------------------------------------------------------------------------- /section11_rl/notebooks/Cartpole/Cartpole_REINFORCE.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import math\n", 10 | "import random\n", 11 | "\n", 12 | "import gym\n", 13 | "import numpy as np\n", 14 | "\n", 15 | "import torch\n", 16 | "import torch.nn as nn\n", 17 | "import torch.optim as optim\n", 18 | "import torch.nn.functional as F\n", 19 | "from torch.distributions import Normal, Categorical\n", 20 | "from IPython.display import clear_output\n", 21 | "import matplotlib.pyplot as plt" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": null, 27 | "metadata": {}, 28 | "outputs": [], 29 | "source": [ 30 | "env_name = 'CartPole-v0'\n", 31 | "env = gym.make(env_name)" 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": null, 37 | "metadata": {}, 38 | "outputs": [], 39 | "source": [ 40 | "class RL(nn.Module):\n", 41 | " def __init__(self, action_num=2, hidden_size=256):\n", 42 | " super(RL, self).__init__()\n", 43 | " self.fc1 = nn.Linear(4, hidden_size)\n", 44 | " self.fc2 = nn.Linear(hidden_size, action_num)\n", 45 | "\n", 46 | " def forward(self, x):\n", 47 | " x = F.relu(self.fc1(x))\n", 48 | " logits = self.fc2(x)\n", 49 | " return Categorical(logits = logits)" 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": null, 55 | "metadata": {}, 56 | "outputs": [], 57 | "source": [ 58 | "def calc_returns(rewards, gamma = 0.99):\n", 59 | " returns = []\n", 60 | " delta = 0\n", 61 | " for reward in rewards[::-1]:\n", 62 | " #Bug fixed on this line\n", 63 | " delta = reward + gamma*delta\n", 64 | " returns.insert(0, delta)\n", 65 | " return returns\n", 66 | "\n", 67 | "def test_agent():\n", 68 | " done = False\n", 69 | " total_reward = 0\n", 70 | " observation = torch.FloatTensor(env.reset()).unsqueeze(0)\n", 71 | "\n", 72 | " with torch.no_grad():\n", 73 | " while not done:\n", 74 | " dist = rl_model(observation)\n", 75 | " action = dist.sample().cpu().item()\n", 76 | " observation, reward, done, info = env.step(action)\n", 77 | " \n", 78 | " observation = torch.FloatTensor(observation).unsqueeze(0)\n", 79 | " total_reward += reward\n", 80 | " \n", 81 | " return total_reward" 82 | ] 83 | }, 84 | { 85 | "cell_type": "code", 86 | "execution_count": null, 87 | "metadata": {}, 88 | "outputs": [], 89 | "source": [ 90 | "rl_model = RL()\n", 91 | "lr = 1e-3\n", 92 | "optimizer = optim.Adam(rl_model.parameters(), lr=lr)" 93 | ] 94 | }, 95 | { 96 | "cell_type": "code", 97 | "execution_count": null, 98 | "metadata": {}, 99 | "outputs": [], 100 | "source": [ 101 | "max_steps = 100000\n", 102 | "rollouts = 0\n", 103 | "step = 0\n", 104 | "score_logger = []" 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": null, 110 | "metadata": {}, 111 | "outputs": [], 112 | "source": [ 113 | "while step < max_steps:\n", 114 | " observation = torch.FloatTensor(env.reset()).unsqueeze(0)\n", 115 | " done = False\n", 116 | " rewards = []\n", 117 | " log_probs = []\n", 118 | " \n", 119 | " while not done:\n", 120 | " dist = rl_model(observation)\n", 121 | " action = dist.sample()\n", 122 | " log_prob = dist.log_prob(action.unsqueeze(0))\n", 123 | " \n", 124 | " observation, reward, done, info = env.step(action.cpu().item())\n", 125 | " \n", 126 | " observation = torch.FloatTensor(observation).unsqueeze(0)\n", 127 | " reward = torch.FloatTensor([reward]).unsqueeze(0)\n", 128 | "\n", 129 | " rewards.append(reward)\n", 130 | " log_probs.append(log_prob)\n", 131 | " step +=1\n", 132 | " \n", 133 | " returns = calc_returns(rewards)\n", 134 | " \n", 135 | " returns = torch.cat(returns, 1)\n", 136 | " returns /= returns.max()\n", 137 | " log_probs = torch.cat(log_probs, 1)\n", 138 | " \n", 139 | " action_loss = - (log_probs * returns).sum()\n", 140 | " \n", 141 | " optimizer.zero_grad()\n", 142 | " action_loss.backward()\n", 143 | " optimizer.step()\n", 144 | " rollouts += 1\n", 145 | " \n", 146 | " if rollouts % 10 == 0:\n", 147 | " new_lr = ((max_steps - step)/max_steps) * lr\n", 148 | " optimizer.param_groups[0][\"lr\"] = new_lr\n", 149 | " \n", 150 | " score_logger.append(np.mean([test_agent() for _ in range(10)]))\n", 151 | " clear_output(True)\n", 152 | " plt.plot(score_logger)\n", 153 | " plt.show()\n", 154 | " \n", 155 | "env.close()" 156 | ] 157 | }, 158 | { 159 | "cell_type": "code", 160 | "execution_count": null, 161 | "metadata": {}, 162 | "outputs": [], 163 | "source": [ 164 | "plt.plot(score_logger)" 165 | ] 166 | } 167 | ], 168 | "metadata": { 169 | "kernelspec": { 170 | "display_name": "Python 3", 171 | "language": "python", 172 | "name": "python3" 173 | }, 174 | "language_info": { 175 | "codemirror_mode": { 176 | "name": "ipython", 177 | "version": 3 178 | }, 179 | "file_extension": ".py", 180 | "mimetype": "text/x-python", 181 | "name": "python", 182 | "nbconvert_exporter": "python", 183 | "pygments_lexer": "ipython3", 184 | "version": "3.6.9" 185 | } 186 | }, 187 | "nbformat": 4, 188 | "nbformat_minor": 2 189 | } 190 | -------------------------------------------------------------------------------- /section11_rl/notebooks/Cartpole/Cartpole_actor_critic.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import math\n", 10 | "import random\n", 11 | "\n", 12 | "import gym\n", 13 | "import numpy as np\n", 14 | "\n", 15 | "import torch\n", 16 | "import torch.nn as nn\n", 17 | "import torch.optim as optim\n", 18 | "import torch.nn.functional as F\n", 19 | "from torch.distributions import Normal, Categorical\n", 20 | "from IPython.display import clear_output\n", 21 | "import matplotlib.pyplot as plt" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": null, 27 | "metadata": {}, 28 | "outputs": [], 29 | "source": [ 30 | "env_name = 'CartPole-v0'\n", 31 | "env = gym.make(env_name)" 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": null, 37 | "metadata": {}, 38 | "outputs": [], 39 | "source": [ 40 | "class RL(nn.Module):\n", 41 | " def __init__(self, action_num=2, hidden_size=256):\n", 42 | " super(RL, self).__init__()\n", 43 | " self.fc_actor1 = nn.Linear(4, hidden_size)\n", 44 | " self.fc_actor2 = nn.Linear(hidden_size, action_num)\n", 45 | "\n", 46 | " self.fc_critic1 = nn.Linear(4, hidden_size)\n", 47 | " self.fc_critic2 = nn.Linear(hidden_size, 1)\n", 48 | "\n", 49 | " def forward(self, x):\n", 50 | " ax = F.relu(self.fc_actor1(x))\n", 51 | " dist = Categorical(logits = self.fc_actor2(ax))\n", 52 | " \n", 53 | " cx = F.relu(self.fc_critic1(x))\n", 54 | " value = self.fc_critic2(cx)\n", 55 | " return dist, value" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": null, 61 | "metadata": {}, 62 | "outputs": [], 63 | "source": [ 64 | "def calc_returns(rewards, gamma = 0.99):\n", 65 | " returns = []\n", 66 | " delta = 0\n", 67 | " for reward in rewards[::-1]:\n", 68 | " #Bug fixed on this line\n", 69 | " delta = reward + gamma*delta\n", 70 | " returns.insert(0, delta)\n", 71 | " return returns\n", 72 | "\n", 73 | "def test_agent():\n", 74 | " done = False\n", 75 | " total_reward = 0\n", 76 | " observation = torch.FloatTensor(env.reset()).unsqueeze(0)\n", 77 | "\n", 78 | " with torch.no_grad():\n", 79 | " while not done:\n", 80 | " dist, _ = rl_model(observation)\n", 81 | " action = dist.sample().cpu().item()\n", 82 | " observation, reward, done, info = env.step(action)\n", 83 | " \n", 84 | " observation = torch.FloatTensor(observation).unsqueeze(0)\n", 85 | " total_reward += reward\n", 86 | " \n", 87 | " return total_reward" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": null, 93 | "metadata": {}, 94 | "outputs": [], 95 | "source": [ 96 | "rl_model = RL()\n", 97 | "lr = 1e-3\n", 98 | "optimizer = optim.Adam(rl_model.parameters(), lr=lr)" 99 | ] 100 | }, 101 | { 102 | "cell_type": "code", 103 | "execution_count": null, 104 | "metadata": {}, 105 | "outputs": [], 106 | "source": [ 107 | "max_steps = 100000\n", 108 | "rollouts = 0\n", 109 | "step = 0\n", 110 | "score_logger = []" 111 | ] 112 | }, 113 | { 114 | "cell_type": "code", 115 | "execution_count": null, 116 | "metadata": {}, 117 | "outputs": [], 118 | "source": [ 119 | "while step < max_steps:\n", 120 | " observation = torch.FloatTensor(env.reset()).unsqueeze(0)\n", 121 | " done = False\n", 122 | " rewards = []\n", 123 | " values = []\n", 124 | " log_probs = []\n", 125 | " \n", 126 | " while not done:\n", 127 | " dist, value = rl_model(observation)\n", 128 | " action = dist.sample()\n", 129 | " log_prob = dist.log_prob(action.unsqueeze(0))\n", 130 | " \n", 131 | " observation, reward, done, info = env.step(action.cpu().item())\n", 132 | " \n", 133 | " observation = torch.FloatTensor(observation).unsqueeze(0)\n", 134 | " reward = torch.FloatTensor([reward]).unsqueeze(0)\n", 135 | "\n", 136 | " rewards.append(reward)\n", 137 | " values.append(value)\n", 138 | " log_probs.append(log_prob)\n", 139 | " step +=1\n", 140 | " \n", 141 | " returns = calc_returns(rewards)\n", 142 | " \n", 143 | " returns = torch.cat(returns, 1)\n", 144 | " log_probs = torch.cat(log_probs, 1)\n", 145 | " values = torch.cat(values, 1)\n", 146 | " advantage = (returns - values).detach()\n", 147 | " \n", 148 | " action_loss = - (log_probs * advantage).mean()\n", 149 | " critic_loss = (returns - values).pow(2).mean()\n", 150 | " agent_loss = action_loss + critic_loss\n", 151 | " \n", 152 | " optimizer.zero_grad()\n", 153 | " agent_loss.backward()\n", 154 | " optimizer.step()\n", 155 | " rollouts += 1\n", 156 | " \n", 157 | " if rollouts % 10 == 0:\n", 158 | " new_lr = ((max_steps - step)/max_steps) * lr\n", 159 | " optimizer.param_groups[0][\"lr\"] = new_lr\n", 160 | " \n", 161 | " score_logger.append(np.mean([test_agent() for _ in range(10)]))\n", 162 | " clear_output(True)\n", 163 | " plt.plot(score_logger)\n", 164 | " plt.show()\n", 165 | " \n", 166 | "env.close()" 167 | ] 168 | }, 169 | { 170 | "cell_type": "code", 171 | "execution_count": null, 172 | "metadata": {}, 173 | "outputs": [], 174 | "source": [ 175 | "value" 176 | ] 177 | }, 178 | { 179 | "cell_type": "code", 180 | "execution_count": null, 181 | "metadata": {}, 182 | "outputs": [], 183 | "source": [ 184 | "plt.plot(score_logger)" 185 | ] 186 | }, 187 | { 188 | "cell_type": "code", 189 | "execution_count": null, 190 | "metadata": {}, 191 | "outputs": [], 192 | "source": [] 193 | } 194 | ], 195 | "metadata": { 196 | "kernelspec": { 197 | "display_name": "Python 3", 198 | "language": "python", 199 | "name": "python3" 200 | }, 201 | "language_info": { 202 | "codemirror_mode": { 203 | "name": "ipython", 204 | "version": 3 205 | }, 206 | "file_extension": ".py", 207 | "mimetype": "text/x-python", 208 | "name": "python", 209 | "nbconvert_exporter": "python", 210 | "pygments_lexer": "ipython3", 211 | "version": "3.6.9" 212 | } 213 | }, 214 | "nbformat": 4, 215 | "nbformat_minor": 2 216 | } 217 | -------------------------------------------------------------------------------- /section11_rl/notebooks/Deep_Reinforcement_Learning.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section11_rl/notebooks/Deep_Reinforcement_Learning.pdf -------------------------------------------------------------------------------- /section11_rl/notebooks/Gridworlds/Actor_Critic_Learning.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import numpy as np\n", 10 | "import random\n", 11 | "import matplotlib.pyplot as plt\n", 12 | "from scipy.special import softmax" 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": null, 18 | "metadata": {}, 19 | "outputs": [], 20 | "source": [ 21 | "rewards = np.zeros(16)\n", 22 | "rewards[3] = 10\n", 23 | "rewards[2] = -1\n", 24 | "rewards[11] = -1\n", 25 | "rewards[10] = -1\n", 26 | "\n", 27 | "terminal_state = 3\n", 28 | "state_values = np.zeros(16)\n", 29 | "state_action_logprobs = np.random.random((16,4))\n", 30 | "alpha = 0.005\n", 31 | "\n", 32 | "score_log = []" 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": null, 38 | "metadata": { 39 | "scrolled": true 40 | }, 41 | "outputs": [], 42 | "source": [ 43 | "plt.imshow(rewards.reshape(4, 4))" 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": null, 49 | "metadata": {}, 50 | "outputs": [], 51 | "source": [ 52 | "state_transition_table = np.genfromtxt(\"state_transitions.csv\", delimiter=\",\").astype(int)" 53 | ] 54 | }, 55 | { 56 | "cell_type": "code", 57 | "execution_count": null, 58 | "metadata": {}, 59 | "outputs": [], 60 | "source": [ 61 | "def returns_calc(rewards):\n", 62 | " returns = []\n", 63 | " R = 0\n", 64 | " gamma = 0.9\n", 65 | " for i in reversed(range(len(rewards))):\n", 66 | " R = rewards[i] + gamma * R\n", 67 | " returns.insert(0, R)\n", 68 | " return returns" 69 | ] 70 | }, 71 | { 72 | "cell_type": "code", 73 | "execution_count": null, 74 | "metadata": {}, 75 | "outputs": [], 76 | "source": [ 77 | "def TD_update(next_val, values, rewards, states):\n", 78 | " gamma = 0.9\n", 79 | " next_val = next_val\n", 80 | " #fixed bug in code here\n", 81 | " new_values = np.zeros(16) + values\n", 82 | " for i in reversed(range(len(rewards))):\n", 83 | " new_values[states[i]] = values[states[i]] + alpha * (rewards[i] + gamma * next_val - values[states[i]])\n", 84 | " next_val = values[states[i]]\n", 85 | " return new_values" 86 | ] 87 | }, 88 | { 89 | "cell_type": "code", 90 | "execution_count": null, 91 | "metadata": {}, 92 | "outputs": [], 93 | "source": [ 94 | "def test_agent():\n", 95 | " state = 12\n", 96 | " done = False\n", 97 | " steps = 0\n", 98 | " total_rewards = 0\n", 99 | " states_log = []\n", 100 | " while (not(state == terminal_state)) and steps<30:\n", 101 | " states_log.append(state)\n", 102 | " action = np.argmax(np.random.multinomial(1, softmax(state_action_logprobs[state]), size=1))\n", 103 | " state = state_transition_table[state, action]\n", 104 | " total_rewards += rewards[state]\n", 105 | " steps += 1\n", 106 | " states_log.append(state)\n", 107 | " return total_rewards, states_log" 108 | ] 109 | }, 110 | { 111 | "cell_type": "code", 112 | "execution_count": null, 113 | "metadata": {}, 114 | "outputs": [], 115 | "source": [ 116 | "for _ in range(1000):\n", 117 | " state = 12\n", 118 | " state_log = []\n", 119 | " reward_log = []\n", 120 | " action_log = []\n", 121 | " values_log = []\n", 122 | "\n", 123 | " steps = 0\n", 124 | "\n", 125 | " while (not(state == terminal_state)) and steps<30:\n", 126 | " action = np.argmax(np.random.multinomial(1, softmax(state_action_logprobs[state]), size=1))\n", 127 | " \n", 128 | " state_log.append(state)\n", 129 | " values_log.append(state_values[state])\n", 130 | " action_log.append(action)\n", 131 | "\n", 132 | " state = state_transition_table[state, action]\n", 133 | " reward_log.append(rewards[state])\n", 134 | "\n", 135 | " steps += 1\n", 136 | " \n", 137 | " next_val = state_values[state]\n", 138 | " \n", 139 | " state_values = TD_update(next_val, state_values, reward_log, state_log)\n", 140 | " state_returns = returns_calc(reward_log)\n", 141 | " advantage = np.subtract(state_returns, values_log)\n", 142 | " \n", 143 | " state_action_logprobs[state_log, action_log] = state_action_logprobs[state_log, action_log] + \\\n", 144 | " alpha*advantage\n", 145 | " \n", 146 | " score_log.append(test_agent()[0])" 147 | ] 148 | }, 149 | { 150 | "cell_type": "code", 151 | "execution_count": null, 152 | "metadata": {}, 153 | "outputs": [], 154 | "source": [ 155 | "fig, ax1 = plt.subplots(1)\n", 156 | "fig.set_size_inches(18.5, 10.5)\n", 157 | "ax1.imshow(softmax(state_action_logprobs, 1).reshape(16, 4))\n", 158 | "\n", 159 | "for (j,i), label in np.ndenumerate(softmax(state_action_logprobs, 1).reshape(16, 4).round(1)):\n", 160 | " ax1.text(i,j,label,ha='center',va='center')" 161 | ] 162 | }, 163 | { 164 | "cell_type": "code", 165 | "execution_count": null, 166 | "metadata": {}, 167 | "outputs": [], 168 | "source": [ 169 | "plt.imshow(state_values.reshape(4, 4))" 170 | ] 171 | }, 172 | { 173 | "cell_type": "code", 174 | "execution_count": null, 175 | "metadata": {}, 176 | "outputs": [], 177 | "source": [ 178 | "plt.plot(score_log)" 179 | ] 180 | }, 181 | { 182 | "cell_type": "code", 183 | "execution_count": null, 184 | "metadata": {}, 185 | "outputs": [], 186 | "source": [ 187 | "_, state_log = test_agent()\n", 188 | "state_view = np.zeros(16)\n", 189 | "state_view[state_log] = 1\n", 190 | "plt.imshow(state_view.reshape(4,4))" 191 | ] 192 | }, 193 | { 194 | "cell_type": "code", 195 | "execution_count": null, 196 | "metadata": {}, 197 | "outputs": [], 198 | "source": [] 199 | } 200 | ], 201 | "metadata": { 202 | "kernelspec": { 203 | "display_name": "Python 3", 204 | "language": "python", 205 | "name": "python3" 206 | }, 207 | "language_info": { 208 | "codemirror_mode": { 209 | "name": "ipython", 210 | "version": 3 211 | }, 212 | "file_extension": ".py", 213 | "mimetype": "text/x-python", 214 | "name": "python", 215 | "nbconvert_exporter": "python", 216 | "pygments_lexer": "ipython3", 217 | "version": "3.6.9" 218 | } 219 | }, 220 | "nbformat": 4, 221 | "nbformat_minor": 4 222 | } 223 | -------------------------------------------------------------------------------- /section11_rl/notebooks/Gridworlds/Average_Returns.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import numpy as np\n", 10 | "import random\n", 11 | "import matplotlib.pyplot as plt" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": null, 17 | "metadata": {}, 18 | "outputs": [], 19 | "source": [ 20 | "rewards = np.zeros(16)\n", 21 | "rewards[3] = 1\n", 22 | "rewards[2] = 0\n", 23 | "rewards[11] = 0\n", 24 | "rewards[10] = 0\n", 25 | "\n", 26 | "terminal_state = 3\n", 27 | "state_values = np.zeros(16)\n", 28 | "score_log = []" 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": null, 34 | "metadata": {}, 35 | "outputs": [], 36 | "source": [ 37 | "plt.imshow(rewards.reshape(4,4))" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": null, 43 | "metadata": {}, 44 | "outputs": [], 45 | "source": [ 46 | "state_transition_table = np.genfromtxt(\"state_transitions.csv\", delimiter=\",\").astype(int)" 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": null, 52 | "metadata": {}, 53 | "outputs": [], 54 | "source": [ 55 | "def returns(values, rewards, states):\n", 56 | " state_count = np.zeros(16)\n", 57 | " state_returns = np.zeros(16)\n", 58 | " r = 0\n", 59 | " gamma = 0.9\n", 60 | " for i in reversed(range(len(rewards))):\n", 61 | " r = rewards[i] + gamma * r\n", 62 | " state_returns[states[i]] += r\n", 63 | " state_count[states[i]] += 1\n", 64 | "# print(states[i])\n", 65 | "\n", 66 | " return state_returns/(state_count + 1e-10)" 67 | ] 68 | }, 69 | { 70 | "cell_type": "code", 71 | "execution_count": null, 72 | "metadata": {}, 73 | "outputs": [], 74 | "source": [ 75 | "def test_agent():\n", 76 | " state = 12\n", 77 | " done = False\n", 78 | " steps = 0\n", 79 | " total_rewards = 0\n", 80 | " states_log = []\n", 81 | " while (not(state == terminal_state)) and steps<30:\n", 82 | " states_log.append(state)\n", 83 | " action = np.argmax(state_values[state_transition_table[state]])\n", 84 | " state = state_transition_table[state, action]\n", 85 | " total_rewards += rewards[state]\n", 86 | " steps += 1\n", 87 | " states_log.append(state)\n", 88 | " return total_rewards, states_log" 89 | ] 90 | }, 91 | { 92 | "cell_type": "code", 93 | "execution_count": null, 94 | "metadata": {}, 95 | "outputs": [], 96 | "source": [ 97 | "returns_log = []\n", 98 | "for _ in range(100):\n", 99 | " state = 12\n", 100 | " state_log = []\n", 101 | " reward_log = []\n", 102 | " steps = 0\n", 103 | "\n", 104 | " while (not(state == terminal_state)) and steps<30:\n", 105 | " reward_log.append(rewards[state])\n", 106 | " state_log.append(state)\n", 107 | " \n", 108 | " action = random.randint(0,3)\n", 109 | " state = state_transition_table[state, action]\n", 110 | " steps += 1\n", 111 | " \n", 112 | " reward_log.append(rewards[state])\n", 113 | " state_log.append(state)\n", 114 | " returns_log.append(returns(state_values, reward_log, state_log))\n", 115 | " state_values = np.mean(returns_log, 0)\n", 116 | " \n", 117 | " score_log.append(test_agent()[0])" 118 | ] 119 | }, 120 | { 121 | "cell_type": "code", 122 | "execution_count": null, 123 | "metadata": {}, 124 | "outputs": [], 125 | "source": [ 126 | "plt.imshow(returns_log[4].reshape(4, 4))" 127 | ] 128 | }, 129 | { 130 | "cell_type": "code", 131 | "execution_count": null, 132 | "metadata": {}, 133 | "outputs": [], 134 | "source": [ 135 | "fig1, ax1= plt.subplots(1)\n", 136 | "ax1.imshow(state_values.reshape(4, 4))\n", 137 | "\n", 138 | "for (j,i), label in np.ndenumerate(state_values.reshape(4, 4).round(2)):\n", 139 | " ax1.text(i,j,label,ha='center',va='center')" 140 | ] 141 | }, 142 | { 143 | "cell_type": "code", 144 | "execution_count": null, 145 | "metadata": {}, 146 | "outputs": [], 147 | "source": [ 148 | "plt.plot(score_log)" 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": null, 154 | "metadata": {}, 155 | "outputs": [], 156 | "source": [ 157 | "_, state_log = test_agent()\n", 158 | "state_view = np.zeros(16)\n", 159 | "state_view[state_log] = 1\n", 160 | "plt.imshow(state_view.reshape(4,4))" 161 | ] 162 | }, 163 | { 164 | "cell_type": "code", 165 | "execution_count": null, 166 | "metadata": {}, 167 | "outputs": [], 168 | "source": [] 169 | } 170 | ], 171 | "metadata": { 172 | "kernelspec": { 173 | "display_name": "Python 3", 174 | "language": "python", 175 | "name": "python3" 176 | }, 177 | "language_info": { 178 | "codemirror_mode": { 179 | "name": "ipython", 180 | "version": 3 181 | }, 182 | "file_extension": ".py", 183 | "mimetype": "text/x-python", 184 | "name": "python", 185 | "nbconvert_exporter": "python", 186 | "pygments_lexer": "ipython3", 187 | "version": "3.6.9" 188 | } 189 | }, 190 | "nbformat": 4, 191 | "nbformat_minor": 4 192 | } 193 | -------------------------------------------------------------------------------- /section11_rl/notebooks/Gridworlds/Monte_Carlo.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import numpy as np\n", 10 | "import random\n", 11 | "import matplotlib.pyplot as plt" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": null, 17 | "metadata": {}, 18 | "outputs": [], 19 | "source": [ 20 | "rewards = np.zeros(16)\n", 21 | "rewards[3] = 1\n", 22 | "rewards[2] = 0\n", 23 | "rewards[11] = 0\n", 24 | "rewards[10] = 0\n", 25 | "\n", 26 | "terminal_state = 3\n", 27 | "state_values = np.zeros(16)\n", 28 | "alpha = 0.005\n", 29 | "score_log = []" 30 | ] 31 | }, 32 | { 33 | "cell_type": "code", 34 | "execution_count": null, 35 | "metadata": {}, 36 | "outputs": [], 37 | "source": [ 38 | "plt.imshow(rewards.reshape(4,4))" 39 | ] 40 | }, 41 | { 42 | "cell_type": "code", 43 | "execution_count": null, 44 | "metadata": {}, 45 | "outputs": [], 46 | "source": [ 47 | "state_transition_table = np.genfromtxt(\"state_transitions.csv\", delimiter=\",\").astype(int)" 48 | ] 49 | }, 50 | { 51 | "cell_type": "code", 52 | "execution_count": null, 53 | "metadata": {}, 54 | "outputs": [], 55 | "source": [ 56 | "def monte_carlo_update(values, rewards, states):\n", 57 | " returns = []\n", 58 | " R = 0\n", 59 | " gamma = 0.9\n", 60 | " for i in reversed(range(len(rewards))):\n", 61 | " R = rewards[i] + gamma * R\n", 62 | " returns.insert(0, R)\n", 63 | "\n", 64 | " values[state_log] = values[states] + alpha*(returns - values[states])\n", 65 | " return values" 66 | ] 67 | }, 68 | { 69 | "cell_type": "code", 70 | "execution_count": null, 71 | "metadata": {}, 72 | "outputs": [], 73 | "source": [ 74 | "def test_agent():\n", 75 | " state = 12\n", 76 | " done = False\n", 77 | " steps = 0\n", 78 | " total_rewards = 0\n", 79 | " states_log = []\n", 80 | " while (not(state == terminal_state)) and steps<30:\n", 81 | " states_log.append(state)\n", 82 | " action = np.argmax(state_values[state_transition_table[state]])\n", 83 | " state = state_transition_table[state, action]\n", 84 | " total_rewards += rewards[state]\n", 85 | " steps += 1\n", 86 | " states_log.append(state)\n", 87 | " return total_rewards, states_log" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": null, 93 | "metadata": {}, 94 | "outputs": [], 95 | "source": [ 96 | "for _ in range(10000):\n", 97 | " state = 12\n", 98 | " state_log = []\n", 99 | " reward_log = []\n", 100 | " steps = 0\n", 101 | "# reward_log.append(rewards[state])\n", 102 | "# state_log.append(state)\n", 103 | "\n", 104 | " while (not(state == terminal_state)) and steps<30:\n", 105 | " reward_log.append(rewards[state])\n", 106 | " state_log.append(state)\n", 107 | " \n", 108 | " action = random.randint(0,3)\n", 109 | " state = state_transition_table[state, action]\n", 110 | " steps += 1\n", 111 | " \n", 112 | " reward_log.append(rewards[state])\n", 113 | " state_log.append(state)\n", 114 | " state_values = monte_carlo_update(state_values, reward_log, state_log)\n", 115 | " \n", 116 | " score_log.append(test_agent()[0])" 117 | ] 118 | }, 119 | { 120 | "cell_type": "code", 121 | "execution_count": null, 122 | "metadata": {}, 123 | "outputs": [], 124 | "source": [ 125 | "fig1, ax1= plt.subplots(1)\n", 126 | "ax1.imshow(state_values.reshape(4, 4))\n", 127 | "\n", 128 | "for (j,i), label in np.ndenumerate(state_values.reshape(4, 4).round(2)):\n", 129 | " ax1.text(i,j,label,ha='center',va='center')" 130 | ] 131 | }, 132 | { 133 | "cell_type": "code", 134 | "execution_count": null, 135 | "metadata": {}, 136 | "outputs": [], 137 | "source": [ 138 | "plt.plot(score_log)" 139 | ] 140 | }, 141 | { 142 | "cell_type": "code", 143 | "execution_count": null, 144 | "metadata": {}, 145 | "outputs": [], 146 | "source": [ 147 | "_, state_log = test_agent()\n", 148 | "state_view = np.zeros(16)\n", 149 | "state_view[state_log] = 1\n", 150 | "plt.imshow(state_view.reshape(4,4))" 151 | ] 152 | }, 153 | { 154 | "cell_type": "code", 155 | "execution_count": null, 156 | "metadata": {}, 157 | "outputs": [], 158 | "source": [] 159 | } 160 | ], 161 | "metadata": { 162 | "kernelspec": { 163 | "display_name": "Python 3", 164 | "language": "python", 165 | "name": "python3" 166 | }, 167 | "language_info": { 168 | "codemirror_mode": { 169 | "name": "ipython", 170 | "version": 3 171 | }, 172 | "file_extension": ".py", 173 | "mimetype": "text/x-python", 174 | "name": "python", 175 | "nbconvert_exporter": "python", 176 | "pygments_lexer": "ipython3", 177 | "version": "3.6.9" 178 | } 179 | }, 180 | "nbformat": 4, 181 | "nbformat_minor": 4 182 | } 183 | -------------------------------------------------------------------------------- /section11_rl/notebooks/Gridworlds/Q_Learning_e_greedy.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import numpy as np\n", 10 | "import random\n", 11 | "import matplotlib.pyplot as plt" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": null, 17 | "metadata": {}, 18 | "outputs": [], 19 | "source": [ 20 | "rewards = np.zeros(16)\n", 21 | "rewards[3] = 10\n", 22 | "rewards[2] = -1\n", 23 | "rewards[11] = -1\n", 24 | "rewards[10] = -1\n", 25 | "\n", 26 | "terminal_state = 3\n", 27 | "Q_values = np.zeros((16,4))\n", 28 | "alpha = 0.01\n", 29 | "epsilon = 0.3\n", 30 | "\n", 31 | "score_log = []" 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": null, 37 | "metadata": { 38 | "scrolled": true 39 | }, 40 | "outputs": [], 41 | "source": [ 42 | "plt.imshow(rewards.reshape(4, 4))" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": null, 48 | "metadata": {}, 49 | "outputs": [], 50 | "source": [ 51 | "state_transition_table = np.genfromtxt(\"state_transitions.csv\", delimiter=\",\").astype(int)" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": null, 57 | "metadata": {}, 58 | "outputs": [], 59 | "source": [ 60 | "def Q_update(next_Q_values, Q_values, actions, rewards, states):\n", 61 | " gamma = 0.99\n", 62 | " next_Q_values = next_Q_values\n", 63 | " new_Q_values = np.zeros((16,4)) + Q_values\n", 64 | " for i in reversed(range(len(rewards))):\n", 65 | " new_Q_values[states[i], actions[i]] = Q_values[states[i], actions[i]] + \\\n", 66 | " alpha * (rewards[i] + gamma * next_Q_values - Q_values[states[i], actions[i]])\n", 67 | " \n", 68 | " next_Q_values = np.max(Q_values[states[i],:])\n", 69 | " return new_Q_values" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": null, 75 | "metadata": {}, 76 | "outputs": [], 77 | "source": [ 78 | "def test_agent():\n", 79 | " state = 12\n", 80 | " done = False\n", 81 | " steps = 0\n", 82 | " total_rewards = 0\n", 83 | " states_log = []\n", 84 | " while (not(state == terminal_state)) and steps<100:\n", 85 | " states_log.append(state)\n", 86 | " action = np.argmax(Q_values[state])\n", 87 | " state = state_transition_table[state, action]\n", 88 | " total_rewards += rewards[state]\n", 89 | " steps += 1\n", 90 | " states_log.append(state)\n", 91 | " return total_rewards, states_log" 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": null, 97 | "metadata": {}, 98 | "outputs": [], 99 | "source": [ 100 | "for _ in range(1000):\n", 101 | " state = 12\n", 102 | " state_log = []\n", 103 | " reward_log = []\n", 104 | " action_log = []\n", 105 | "\n", 106 | " done = False\n", 107 | " steps = 0\n", 108 | "\n", 109 | " while (not(state == terminal_state)) and steps<30:\n", 110 | " z = random.random()\n", 111 | " if z >=epsilon:\n", 112 | " action = np.argmax(Q_values[state])\n", 113 | " else:\n", 114 | " action = random.randint(0,3)\n", 115 | " state_log.append(state)\n", 116 | " action_log.append(action)\n", 117 | "\n", 118 | " state = state_transition_table[state, action]\n", 119 | " reward_log.append(rewards[state])\n", 120 | "\n", 121 | " steps += 1\n", 122 | "\n", 123 | " next_Q_values = np.max(Q_values[state])\n", 124 | " Q_values = Q_update(next_Q_values, Q_values, action_log, reward_log, state_log)\n", 125 | " \n", 126 | " score_log.append(test_agent()[0])" 127 | ] 128 | }, 129 | { 130 | "cell_type": "code", 131 | "execution_count": null, 132 | "metadata": {}, 133 | "outputs": [], 134 | "source": [ 135 | "fig, ax1 = plt.subplots(1)\n", 136 | "fig.set_size_inches(18.5, 10.5)\n", 137 | "ax1.imshow(Q_values)\n", 138 | "\n", 139 | "for (j,i), label in np.ndenumerate(Q_values.round(1)):\n", 140 | " ax1.text(i,j,label,ha='center',va='center')" 141 | ] 142 | }, 143 | { 144 | "cell_type": "code", 145 | "execution_count": null, 146 | "metadata": {}, 147 | "outputs": [], 148 | "source": [ 149 | "plt.plot(score_log)" 150 | ] 151 | }, 152 | { 153 | "cell_type": "code", 154 | "execution_count": null, 155 | "metadata": {}, 156 | "outputs": [], 157 | "source": [ 158 | "_, state_log = test_agent()\n", 159 | "state_view = np.zeros(16)\n", 160 | "state_view[state_log] = 1\n", 161 | "plt.imshow(state_view.reshape(4,4))" 162 | ] 163 | }, 164 | { 165 | "cell_type": "code", 166 | "execution_count": null, 167 | "metadata": {}, 168 | "outputs": [], 169 | "source": [] 170 | } 171 | ], 172 | "metadata": { 173 | "kernelspec": { 174 | "display_name": "Python 3", 175 | "language": "python", 176 | "name": "python3" 177 | }, 178 | "language_info": { 179 | "codemirror_mode": { 180 | "name": "ipython", 181 | "version": 3 182 | }, 183 | "file_extension": ".py", 184 | "mimetype": "text/x-python", 185 | "name": "python", 186 | "nbconvert_exporter": "python", 187 | "pygments_lexer": "ipython3", 188 | "version": "3.6.9" 189 | } 190 | }, 191 | "nbformat": 4, 192 | "nbformat_minor": 4 193 | } 194 | -------------------------------------------------------------------------------- /section11_rl/notebooks/Gridworlds/REINFORCE_Grid_World.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import numpy as np\n", 10 | "import random\n", 11 | "import matplotlib.pyplot as plt\n", 12 | "from scipy.special import softmax" 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": null, 18 | "metadata": {}, 19 | "outputs": [], 20 | "source": [ 21 | "rewards = np.zeros(16)\n", 22 | "rewards[3] = 10\n", 23 | "rewards[2] = -1\n", 24 | "rewards[11] = -1\n", 25 | "rewards[10] = -1\n", 26 | "\n", 27 | "terminal_state = 3\n", 28 | "state_action_logprobs = np.random.random((16,4))\n", 29 | "alpha = 0.005\n", 30 | "\n", 31 | "score_log = []" 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": null, 37 | "metadata": { 38 | "scrolled": true 39 | }, 40 | "outputs": [], 41 | "source": [ 42 | "plt.imshow(rewards.reshape(4, 4))" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": null, 48 | "metadata": {}, 49 | "outputs": [], 50 | "source": [ 51 | "state_transition_table = np.genfromtxt(\"state_transitions.csv\", delimiter=\",\").astype(int)" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": null, 57 | "metadata": {}, 58 | "outputs": [], 59 | "source": [ 60 | "def returns_calc(rewards):\n", 61 | " returns = []\n", 62 | " R = 0\n", 63 | " gamma = 0.9\n", 64 | " for i in reversed(range(len(rewards))):\n", 65 | " R = rewards[i] + gamma * R\n", 66 | " returns.insert(0, R)\n", 67 | " return returns" 68 | ] 69 | }, 70 | { 71 | "cell_type": "code", 72 | "execution_count": null, 73 | "metadata": {}, 74 | "outputs": [], 75 | "source": [ 76 | "def test_agent():\n", 77 | " state = 12\n", 78 | " done = False\n", 79 | " steps = 0\n", 80 | " total_rewards = 0\n", 81 | " states_log = []\n", 82 | " while (not(state == terminal_state)) and steps<30:\n", 83 | " states_log.append(state)\n", 84 | " action = np.argmax(np.random.multinomial(1, softmax(state_action_logprobs[state]), size=1))\n", 85 | " state = state_transition_table[state, action]\n", 86 | " total_rewards += rewards[state]\n", 87 | " steps += 1\n", 88 | " states_log.append(state)\n", 89 | " return total_rewards, states_log" 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": null, 95 | "metadata": {}, 96 | "outputs": [], 97 | "source": [ 98 | "for _ in range(1000):\n", 99 | " state = 12\n", 100 | " state_log = []\n", 101 | " reward_log = []\n", 102 | " action_log = []\n", 103 | " values_log = []\n", 104 | "\n", 105 | " steps = 0\n", 106 | "\n", 107 | " while (not(state == terminal_state)) and steps<30:\n", 108 | " action = np.argmax(np.random.multinomial(1, softmax(state_action_logprobs[state]), size=1))\n", 109 | " \n", 110 | " state_log.append(state)\n", 111 | " action_log.append(action)\n", 112 | "\n", 113 | " state = state_transition_table[state, action]\n", 114 | " reward_log.append(rewards[state])\n", 115 | "\n", 116 | " steps += 1\n", 117 | " \n", 118 | " state_returns = returns_calc(reward_log)\n", 119 | " advantage = np.array(state_returns)\n", 120 | " \n", 121 | " state_action_logprobs[state_log, action_log] = state_action_logprobs[state_log, action_log] + alpha*advantage\n", 122 | " \n", 123 | " score_log.append(test_agent()[0])" 124 | ] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "execution_count": null, 129 | "metadata": {}, 130 | "outputs": [], 131 | "source": [ 132 | "fig, ax1 = plt.subplots(1)\n", 133 | "fig.set_size_inches(18.5, 10.5)\n", 134 | "ax1.imshow(softmax(state_action_logprobs, 1).reshape(16, 4))\n", 135 | "\n", 136 | "for (j,i), label in np.ndenumerate(softmax(state_action_logprobs, 1).reshape(16, 4).round(1)):\n", 137 | " ax1.text(i,j,label,ha='center',va='center')" 138 | ] 139 | }, 140 | { 141 | "cell_type": "code", 142 | "execution_count": null, 143 | "metadata": {}, 144 | "outputs": [], 145 | "source": [ 146 | "plt.plot(score_log)" 147 | ] 148 | }, 149 | { 150 | "cell_type": "code", 151 | "execution_count": null, 152 | "metadata": {}, 153 | "outputs": [], 154 | "source": [ 155 | "_, state_log = test_agent()\n", 156 | "state_view = np.zeros(16)\n", 157 | "state_view[state_log] = 1\n", 158 | "plt.imshow(state_view.reshape(4,4))" 159 | ] 160 | }, 161 | { 162 | "cell_type": "code", 163 | "execution_count": null, 164 | "metadata": {}, 165 | "outputs": [], 166 | "source": [] 167 | } 168 | ], 169 | "metadata": { 170 | "kernelspec": { 171 | "display_name": "Python 3", 172 | "language": "python", 173 | "name": "python3" 174 | }, 175 | "language_info": { 176 | "codemirror_mode": { 177 | "name": "ipython", 178 | "version": 3 179 | }, 180 | "file_extension": ".py", 181 | "mimetype": "text/x-python", 182 | "name": "python", 183 | "nbconvert_exporter": "python", 184 | "pygments_lexer": "ipython3", 185 | "version": "3.6.9" 186 | } 187 | }, 188 | "nbformat": 4, 189 | "nbformat_minor": 4 190 | } 191 | -------------------------------------------------------------------------------- /section11_rl/notebooks/Gridworlds/TD_Learning.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import numpy as np\n", 10 | "import random\n", 11 | "import matplotlib.pyplot as plt" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": null, 17 | "metadata": {}, 18 | "outputs": [], 19 | "source": [ 20 | "rewards = np.zeros(16)\n", 21 | "rewards[3] = 10\n", 22 | "rewards[2] = -1\n", 23 | "rewards[11] = -1\n", 24 | "rewards[10] = -1\n", 25 | "\n", 26 | "terminal_state = 3\n", 27 | "state_values = np.zeros(16)\n", 28 | "alpha = 0.02\n", 29 | "score_log = []" 30 | ] 31 | }, 32 | { 33 | "cell_type": "code", 34 | "execution_count": null, 35 | "metadata": { 36 | "scrolled": true 37 | }, 38 | "outputs": [], 39 | "source": [ 40 | "plt.imshow(rewards.reshape(4, 4))" 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": null, 46 | "metadata": {}, 47 | "outputs": [], 48 | "source": [ 49 | "state_transition_table = np.genfromtxt(\"state_transitions.csv\", delimiter=\",\").astype(int)" 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": null, 55 | "metadata": {}, 56 | "outputs": [], 57 | "source": [ 58 | "def TD_update(next_val, values, rewards, states):\n", 59 | " gamma = 0.9\n", 60 | " next_val = next_val\n", 61 | " #fixed bug in code here\n", 62 | " new_values = np.zeros(16) + values\n", 63 | " for i in reversed(range(len(rewards))):\n", 64 | " new_values[states[i]] = values[states[i]] + alpha * (rewards[i] + gamma * next_val - values[states[i]])\n", 65 | " next_val = values[states[i]]\n", 66 | " return new_values" 67 | ] 68 | }, 69 | { 70 | "cell_type": "code", 71 | "execution_count": null, 72 | "metadata": {}, 73 | "outputs": [], 74 | "source": [ 75 | "def test_agent():\n", 76 | " state = 12\n", 77 | " done = False\n", 78 | " steps = 0\n", 79 | " total_rewards = 0\n", 80 | " states_log = []\n", 81 | " while (not(state == terminal_state)) and steps<30:\n", 82 | " states_log.append(state)\n", 83 | " action = np.argmax(state_values[state_transition_table[state]])\n", 84 | " state = state_transition_table[state, action]\n", 85 | " total_rewards += rewards[state]\n", 86 | " steps += 1\n", 87 | " states_log.append(state)\n", 88 | " return total_rewards, states_log" 89 | ] 90 | }, 91 | { 92 | "cell_type": "code", 93 | "execution_count": null, 94 | "metadata": {}, 95 | "outputs": [], 96 | "source": [ 97 | "for _ in range(10000):\n", 98 | " state = 12\n", 99 | " state_log = []\n", 100 | " reward_log = []\n", 101 | " steps = 0\n", 102 | "\n", 103 | " while (not(state == terminal_state)) and steps<30:\n", 104 | " action = random.randint(0,3)\n", 105 | " state_log.append(state)\n", 106 | " reward_log.append(rewards[state])\n", 107 | "\n", 108 | " state = state_transition_table[state, action]\n", 109 | " steps += 1\n", 110 | " \n", 111 | " state_log.append(state)\n", 112 | " reward_log.append(rewards[state])\n", 113 | " next_val = 0\n", 114 | " state_values = TD_update(next_val, state_values, reward_log, state_log)\n", 115 | " \n", 116 | " score_log.append(test_agent()[0])" 117 | ] 118 | }, 119 | { 120 | "cell_type": "code", 121 | "execution_count": null, 122 | "metadata": {}, 123 | "outputs": [], 124 | "source": [ 125 | "fig1, ax1= plt.subplots(1)\n", 126 | "ax1.imshow(state_values.reshape(4, 4))\n", 127 | "\n", 128 | "for (j,i), label in np.ndenumerate(state_values.reshape(4, 4).round(3)):\n", 129 | " ax1.text(i,j,label,ha='center',va='center')" 130 | ] 131 | }, 132 | { 133 | "cell_type": "code", 134 | "execution_count": null, 135 | "metadata": {}, 136 | "outputs": [], 137 | "source": [ 138 | "plt.plot(score_log)" 139 | ] 140 | }, 141 | { 142 | "cell_type": "code", 143 | "execution_count": null, 144 | "metadata": {}, 145 | "outputs": [], 146 | "source": [ 147 | "_, state_log = test_agent()\n", 148 | "state_view = np.zeros(16)\n", 149 | "state_view[state_log] = 1\n", 150 | "plt.imshow(state_view.reshape(4,4))" 151 | ] 152 | }, 153 | { 154 | "cell_type": "code", 155 | "execution_count": null, 156 | "metadata": {}, 157 | "outputs": [], 158 | "source": [] 159 | } 160 | ], 161 | "metadata": { 162 | "kernelspec": { 163 | "display_name": "Python 3", 164 | "language": "python", 165 | "name": "python3" 166 | }, 167 | "language_info": { 168 | "codemirror_mode": { 169 | "name": "ipython", 170 | "version": 3 171 | }, 172 | "file_extension": ".py", 173 | "mimetype": "text/x-python", 174 | "name": "python", 175 | "nbconvert_exporter": "python", 176 | "pygments_lexer": "ipython3", 177 | "version": "3.6.9" 178 | } 179 | }, 180 | "nbformat": 4, 181 | "nbformat_minor": 4 182 | } 183 | -------------------------------------------------------------------------------- /section11_rl/notebooks/Gridworlds/TD_Learning_e_greedy.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import numpy as np\n", 10 | "import random\n", 11 | "import matplotlib.pyplot as plt" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": null, 17 | "metadata": {}, 18 | "outputs": [], 19 | "source": [ 20 | "rewards = np.zeros(16)\n", 21 | "rewards[3] = 10\n", 22 | "rewards[2] = -1\n", 23 | "rewards[11] = -1\n", 24 | "rewards[10] = -1\n", 25 | "\n", 26 | "terminal_state = 3\n", 27 | "state_values = np.zeros(16)\n", 28 | "alpha = 0.02\n", 29 | "epsilon = 0.3\n", 30 | "\n", 31 | "score_log = []" 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": null, 37 | "metadata": { 38 | "scrolled": true 39 | }, 40 | "outputs": [], 41 | "source": [ 42 | "plt.imshow(rewards.reshape(4, 4))" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": null, 48 | "metadata": {}, 49 | "outputs": [], 50 | "source": [ 51 | "state_transition_table = np.genfromtxt(\"state_transitions.csv\", delimiter=\",\").astype(int)" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": null, 57 | "metadata": {}, 58 | "outputs": [], 59 | "source": [ 60 | "def TD_update(next_val, values, rewards, states):\n", 61 | " gamma = 0.9\n", 62 | " next_val = next_val\n", 63 | " #fixed bug in code here\n", 64 | " new_values = np.zeros(16) + values\n", 65 | " for i in reversed(range(len(rewards))):\n", 66 | " new_values[states[i]] = values[states[i]] + alpha * (rewards[i] + gamma * next_val - values[states[i]])\n", 67 | " next_val = values[states[i]]\n", 68 | " return new_values" 69 | ] 70 | }, 71 | { 72 | "cell_type": "code", 73 | "execution_count": null, 74 | "metadata": {}, 75 | "outputs": [], 76 | "source": [ 77 | "def test_agent():\n", 78 | " state = 12\n", 79 | " done = False\n", 80 | " steps = 0\n", 81 | " total_rewards = 0\n", 82 | " states_log = []\n", 83 | " while (not(state == terminal_state)) and steps<100:\n", 84 | " states_log.append(state)\n", 85 | " action = np.argmax(state_values[state_transition_table[state]])\n", 86 | " state = state_transition_table[state, action]\n", 87 | " total_rewards += rewards[state]\n", 88 | " steps += 1\n", 89 | " states_log.append(state)\n", 90 | " return total_rewards, states_log" 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": null, 96 | "metadata": {}, 97 | "outputs": [], 98 | "source": [ 99 | "for _ in range(300):\n", 100 | " state = 12\n", 101 | " state_log = []\n", 102 | " reward_log = []\n", 103 | " done = False\n", 104 | " steps = 0\n", 105 | "\n", 106 | " while (not(state == terminal_state)) and steps<100:\n", 107 | " state_log.append(state)\n", 108 | " reward_log.append(rewards[state])\n", 109 | " \n", 110 | " z = random.random()\n", 111 | " if z >=epsilon:\n", 112 | " action = np.argmax(state_values[state_transition_table[state]])\n", 113 | " else:\n", 114 | " action = random.randint(0,3)\n", 115 | " \n", 116 | " state = state_transition_table[state, action]\n", 117 | "\n", 118 | " steps += 1\n", 119 | " \n", 120 | " state_log.append(state)\n", 121 | " reward_log.append(rewards[state])\n", 122 | " next_val = state_values[state]\n", 123 | " state_values = TD_update(next_val, state_values, reward_log, state_log)\n", 124 | " \n", 125 | " score_log.append(test_agent()[0])" 126 | ] 127 | }, 128 | { 129 | "cell_type": "code", 130 | "execution_count": null, 131 | "metadata": {}, 132 | "outputs": [], 133 | "source": [ 134 | "fig1, ax1= plt.subplots(1)\n", 135 | "ax1.imshow(state_values.reshape(4, 4))\n", 136 | "\n", 137 | "for (j,i), label in np.ndenumerate(state_values.reshape(4, 4).round(1)):\n", 138 | " ax1.text(i,j,label,ha='center',va='center')" 139 | ] 140 | }, 141 | { 142 | "cell_type": "code", 143 | "execution_count": null, 144 | "metadata": {}, 145 | "outputs": [], 146 | "source": [ 147 | "plt.plot(score_log)" 148 | ] 149 | }, 150 | { 151 | "cell_type": "code", 152 | "execution_count": null, 153 | "metadata": {}, 154 | "outputs": [], 155 | "source": [ 156 | "_, state_log = test_agent()\n", 157 | "state_view = np.zeros(16)\n", 158 | "state_view[state_log] = 1\n", 159 | "plt.imshow(state_view.reshape(4,4))" 160 | ] 161 | }, 162 | { 163 | "cell_type": "code", 164 | "execution_count": null, 165 | "metadata": {}, 166 | "outputs": [], 167 | "source": [] 168 | } 169 | ], 170 | "metadata": { 171 | "kernelspec": { 172 | "display_name": "Python 3", 173 | "language": "python", 174 | "name": "python3" 175 | }, 176 | "language_info": { 177 | "codemirror_mode": { 178 | "name": "ipython", 179 | "version": 3 180 | }, 181 | "file_extension": ".py", 182 | "mimetype": "text/x-python", 183 | "name": "python", 184 | "nbconvert_exporter": "python", 185 | "pygments_lexer": "ipython3", 186 | "version": "3.6.9" 187 | } 188 | }, 189 | "nbformat": 4, 190 | "nbformat_minor": 4 191 | } 192 | -------------------------------------------------------------------------------- /section11_rl/notebooks/Gridworlds/state_transitions.csv: -------------------------------------------------------------------------------- 1 | 0,1,4,0 2 | 1,2,5,0 3 | 2,3,6,1 4 | 3,3,7,2 5 | 0,5,8,4 6 | 1,6,9,4 7 | 2,7,10,5 8 | 3,7,11,6 9 | 4,9,12,8 10 | 5,10,13,8 11 | 6,11,14,9 12 | 7,11,15,10 13 | 8,13,12,12 14 | 9,14,13,12 15 | 10,15,14,13 16 | 11,15,15,14 -------------------------------------------------------------------------------- /section11_rl/notebooks/PPO.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section11_rl/notebooks/PPO.pdf -------------------------------------------------------------------------------- /section11_rl/notebooks/Reinforcement_Learning.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section11_rl/notebooks/Reinforcement_Learning.pdf -------------------------------------------------------------------------------- /section12_sequential/solutions/Dataset.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | 3 | import torch 4 | from torch.utils.data.dataset import Dataset 5 | 6 | 7 | class WeatherDataset(Dataset): 8 | def __init__(self, dataset_file, day_range, split_date, train_test="train"): 9 | df = pd.read_csv(dataset_file) 10 | df['Date'] = pd.to_datetime(df['Date']) # Convert 'Date' column to datetime 11 | df.set_index('Date', inplace=True) 12 | 13 | # Calculate the mean and std to normalise the data 14 | mean = df.mean() 15 | std = df.std() 16 | df = (df - mean) / std 17 | 18 | self.mean = torch.tensor(mean.to_numpy()).reshape(1, -1) 19 | self.std = torch.tensor(std.to_numpy()).reshape(1, -1) 20 | 21 | # Split the dataset to test/train set based on a split date 22 | if train_test == "train": 23 | self.dataset = df[df.index < split_date] 24 | elif train_test == "test": 25 | self.dataset = df[df.index >= split_date] 26 | else: 27 | ValueError("train_test should be train or test") 28 | 29 | self.day_range = day_range 30 | 31 | def __getitem__(self, index): 32 | # Index a range of days 33 | end_index = index + self.day_range 34 | current_series = self.dataset.iloc[index:end_index] 35 | 36 | day_tensor = torch.LongTensor(current_series.index.day.to_numpy()) 37 | month_tensor = torch.LongTensor(current_series.index.month.to_numpy()) 38 | data_values = torch.FloatTensor(current_series.values) 39 | 40 | return day_tensor, month_tensor, data_values 41 | 42 | def __len__(self): 43 | return len(self.dataset) - self.day_range 44 | -------------------------------------------------------------------------------- /section13_attention/data/LSTM_Attention.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section13_attention/data/LSTM_Attention.jpg -------------------------------------------------------------------------------- /section14_transformers/data/llm_architecture_comparison.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section14_transformers/data/llm_architecture_comparison.png -------------------------------------------------------------------------------- /section15_deploying_models/data/dog.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDitria/pytorch_tutorials/671863362869b526ffc1022a5a1f393e8d6dbac1/section15_deploying_models/data/dog.jpg -------------------------------------------------------------------------------- /section15_deploying_models/solutions/ONNX_Basics/Exporting_Pytorch_as_ONNX.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "a00a74cc", 6 | "metadata": {}, 7 | "source": [ 8 | "# Converting to an Open Neural Network Exchange (ONNX) Model\n", 9 | "\"ONNX is an open format built to represent machine learning models. ONNX defines a common set of operators - the building blocks of machine learning and deep learning models - and a common file format to enable AI developers to use models with a variety of frameworks, tools, runtimes, and compilers.\"
\n", 10 | "[ONNX](https://onnx.ai)
\n", 11 | "
\n", 12 | "By converting our trained model to an ONNX format, we not only get a speed-up from memory and compute efficiencies, but we can port our model to other frameworks or use it with all sorts of Neural Network accelerators! This flexibility can be crucial for deployment or integration into different environments.
\n", 13 | "[How ONNX Optimizes Models](https://onnxruntime.ai/docs/performance/model-optimizations/graph-optimizations.html#graph-optimizations-in-onnx-runtime)\n", 14 | "
\n", 15 | "
\n", 16 | "You can also just download a model from ONNX's model zoo!
\n", 17 | "[ONNX Model Zoo](https://onnx.ai/models/)" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": null, 23 | "id": "1aa6808e", 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "import torch\n", 28 | "import torch.nn as nn\n", 29 | "import torchvision.models as models\n", 30 | "import torchvision.transforms as transforms\n", 31 | "\n", 32 | "import numpy as np\n", 33 | "from PIL import Image\n", 34 | "import time" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": null, 40 | "id": "7bc488ec", 41 | "metadata": {}, 42 | "outputs": [], 43 | "source": [ 44 | "# Create a transform as defined for the pre-trained model\n", 45 | "# https://pytorch.org/blog/introducing-torchvision-new-multi-weight-support-api/\n", 46 | "\n", 47 | "transform = transforms.Compose([transforms.Resize(224),\n", 48 | " transforms.CenterCrop(224),\n", 49 | " transforms.ToTensor(),\n", 50 | " transforms.Normalize(mean=[0.485, 0.456, 0.406],\n", 51 | " std=[0.229, 0.224, 0.225])]) \n", 52 | "\n", 53 | "device = torch.device(0 if torch.cuda.is_available() else 'cpu')" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": null, 59 | "id": "f2a0df51", 60 | "metadata": {}, 61 | "outputs": [], 62 | "source": [ 63 | "# Load the pre-trained efficientnet_b1 model\n", 64 | "res_net = models.efficientnet_b1(weights=\"IMAGENET1K_V2\").to(device)\n", 65 | "\n", 66 | "# Set to eval mode for inference!\n", 67 | "res_net.eval()" 68 | ] 69 | }, 70 | { 71 | "cell_type": "markdown", 72 | "id": "35bf71fe", 73 | "metadata": {}, 74 | "source": [ 75 | "## How fast is inference?\n", 76 | "Lets get a measure of how fast inference is with the Pytorch model" 77 | ] 78 | }, 79 | { 80 | "cell_type": "code", 81 | "execution_count": null, 82 | "id": "823f7de8", 83 | "metadata": {}, 84 | "outputs": [], 85 | "source": [ 86 | "# Load test image\n", 87 | "test_image = Image.open(\"../../data/dog.jpg\")\n", 88 | "test_image.resize((256, 256))" 89 | ] 90 | }, 91 | { 92 | "cell_type": "code", 93 | "execution_count": null, 94 | "id": "23804841", 95 | "metadata": {}, 96 | "outputs": [], 97 | "source": [ 98 | "# Convert to tensor\n", 99 | "tensor_image = transform(test_image).unsqueeze(0)" 100 | ] 101 | }, 102 | { 103 | "cell_type": "code", 104 | "execution_count": null, 105 | "id": "a564c693", 106 | "metadata": {}, 107 | "outputs": [], 108 | "source": [ 109 | "# List to store inference times\n", 110 | "inference_time = []\n", 111 | "\n", 112 | "# Perform multiple inference runs (10 in this case)\n", 113 | "for _ in range(10):\n", 114 | " # Record start time\n", 115 | " start_time = time.time()\n", 116 | "\n", 117 | " # Forward pass of model\n", 118 | " out_put = res_net(tensor_image.to(device))\n", 119 | "\n", 120 | " # Record end time\n", 121 | " end_time = time.time()\n", 122 | "\n", 123 | " # Calculate and store inference time for this run\n", 124 | " inference_time.append(end_time - start_time)\n", 125 | "\n", 126 | "# Print the minimum inference time observed across the runs\n", 127 | "print(\"Minimum inference time %.4fs\" % np.min(inference_time))" 128 | ] 129 | }, 130 | { 131 | "cell_type": "markdown", 132 | "id": "7ae85284", 133 | "metadata": {}, 134 | "source": [ 135 | "## Convert to ONNX\n", 136 | "Pytorch has functionality to export a model to the ONNX format.
\n", 137 | "[About Pytorch ONNX](https://pytorch.org/docs/stable/onnx.html)
\n", 138 | "
\n", 139 | "We need to provide an example input that Pytorch will use to \"trace\" our model, basically \"recording\" the forward pass of our model in order to build the ONNX graph.
\n", 140 | "[Pytorch ONNX Tutorial](https://pytorch.org/tutorials/advanced/super_resolution_with_onnxruntime.html)
\n", 141 | "
\n", 142 | "Because Pytorch needs to perform this tracing, we must make sure the forward pass only uses Pytorch functions and datatypes! It must also be deterministic, if your model's behaviour changes with conditional statements, then a trace won't be able to capture this!
\n", 143 | "[Avoiding ONNX Conversion Pitfalls](https://pytorch.org/docs/stable/onnx_torchscript.html#avoiding-pitfalls)" 144 | ] 145 | }, 146 | { 147 | "cell_type": "code", 148 | "execution_count": null, 149 | "id": "ae16c9f9", 150 | "metadata": {}, 151 | "outputs": [], 152 | "source": [ 153 | "# Generate a random input tensor to be used for tracing (it does not need to be a \"real\" example!)\n", 154 | "test_input = torch.randn(1, 3, 224, 224, device=device)\n", 155 | "\n", 156 | "# Export the model to ONNX format\n", 157 | "torch.onnx.export(\n", 158 | " res_net, # Model to convert\n", 159 | " test_input, # Example input\n", 160 | " \"efficientnet_b1.onnx\", # Output save name\n", 161 | " opset_version=12, # Version of ONNX operations to use\n", 162 | " export_params=True, # We will store the trained parameter weights inside the ONNX model file\n", 163 | " do_constant_folding=True, # Whether to execute \"constant folding\" for optimization\n", 164 | " input_names=['input'], # Define the model's input names\n", 165 | " output_names=['output'], # Define the model's output names\n", 166 | " dynamic_axes={'input' : {0 : 'batch_size'}, # Define any variable length axes\n", 167 | " 'output' : {0 : 'batch_size'}}\n", 168 | ")" 169 | ] 170 | } 171 | ], 172 | "metadata": { 173 | "kernelspec": { 174 | "display_name": "Python 3 (ipykernel)", 175 | "language": "python", 176 | "name": "python3" 177 | }, 178 | "language_info": { 179 | "codemirror_mode": { 180 | "name": "ipython", 181 | "version": 3 182 | }, 183 | "file_extension": ".py", 184 | "mimetype": "text/x-python", 185 | "name": "python", 186 | "nbconvert_exporter": "python", 187 | "pygments_lexer": "ipython3", 188 | "version": "3.9.16" 189 | } 190 | }, 191 | "nbformat": 4, 192 | "nbformat_minor": 5 193 | } 194 | -------------------------------------------------------------------------------- /section15_deploying_models/solutions/ONNX_Basics/Running_ONNX_Model.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "d98da01c", 6 | "metadata": {}, 7 | "source": [ 8 | "# Runnning an ONNX Model with ONNX Runtime!\n", 9 | "Now that we have our model in the ONNX formate we can use ONNX Runtime perform inference with our model!
\n", 10 | "[ONNX Runtime](https://onnxruntime.ai/)
\n", 11 | "ONNX Runtime is not only avaliable as a Python library, but has versions in:\n", 12 | "* C++\n", 13 | "* C\n", 14 | "* C#\n", 15 | "* Java\n", 16 | "* JavaScript\n", 17 | "* Objective-C\n", 18 | "* Julia and Ruby APIs \n", 19 | "
\n", 20 | "
\n", 21 | "All of which can use the same ONNX file!" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": null, 27 | "id": "496fd2ba", 28 | "metadata": {}, 29 | "outputs": [], 30 | "source": [ 31 | "import numpy as np\n", 32 | "import onnxruntime\n", 33 | "from PIL import Image\n", 34 | "import json\n", 35 | "import time\n", 36 | "\n", 37 | "## You will need to Install onnxruntime\n", 38 | "\n", 39 | "# If you don't have a GPU install cpu version\n", 40 | "# pip install onnxruntime\n", 41 | "\n", 42 | "# If you have a GPU install gpu version\n", 43 | "# pip install onnxruntime-gpu\n", 44 | "\n", 45 | "# Make sure you install the correct version for your version of CUDA!\n", 46 | "# Also check dependencies!\n", 47 | "# https://onnxruntime.ai/docs/execution-providers/CUDA-ExecutionProvider.html\n", 48 | "# EG for CUDA version 12.2 use \n", 49 | "# pip install onnxruntime-gpu==1.17\n", 50 | "\n", 51 | "# NOTE Pytorch has it's own cuDNN that gets installed with torch\n", 52 | "# If you want to use other applications that need cuDNNm like onnxruntime-gpu (without having to import torch)\n", 53 | "# You need to install cuDNN separately (it doesn't come with NVIDIA Toolkit)\n", 54 | "# NOTE: at time of writing only cuDNN 8.X versions are supported!!\n", 55 | "# https://docs.nvidia.com/deeplearning/cudnn/archives/cudnn-890/install-guide/index.html" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": null, 61 | "id": "f51feda2", 62 | "metadata": {}, 63 | "outputs": [], 64 | "source": [ 65 | "# Create an ONNX Runtime inference session with GPU support\n", 66 | "ort_session = onnxruntime.InferenceSession(\"./efficientnet_b1.onnx\", providers=['CUDAExecutionProvider'])\n", 67 | "\n", 68 | "# Load image classification labels from JSON file (assuming labels are in imagenet_classes.json)\n", 69 | "with open(\"../../data/imagenet_classes.json\", \"r\") as file:\n", 70 | " img_net_classes = json.load(file)" 71 | ] 72 | }, 73 | { 74 | "cell_type": "markdown", 75 | "id": "ac7ed165", 76 | "metadata": {}, 77 | "source": [ 78 | "## Create helper functions" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": null, 84 | "id": "2fea2439", 85 | "metadata": {}, 86 | "outputs": [], 87 | "source": [ 88 | "def crop_resize(image, new_size):\n", 89 | " # Get the dimensions of the original image\n", 90 | " width, height = image.size\n", 91 | "\n", 92 | " # Calculate the size of the square crop\n", 93 | " min_dim = min(width, height)\n", 94 | "\n", 95 | " # Calculate coordinates for the center crop\n", 96 | " left = (width - min_dim) // 2\n", 97 | " upper = (height - min_dim) // 2\n", 98 | " right = left + min_dim\n", 99 | " lower = upper + min_dim\n", 100 | "\n", 101 | " # Crop the image to a square\n", 102 | " square_image = image.crop((left, upper, right, lower))\n", 103 | "\n", 104 | " # Resize the image to the specified size\n", 105 | " resized_image = square_image.resize((new_size, new_size))\n", 106 | "\n", 107 | " return resized_image" 108 | ] 109 | }, 110 | { 111 | "cell_type": "code", 112 | "execution_count": null, 113 | "id": "677bb6fb", 114 | "metadata": {}, 115 | "outputs": [], 116 | "source": [ 117 | "def image_normalise_reshape(image, mean, std):\n", 118 | " # Get image dimensions (height, width, channels)\n", 119 | " h, w, c = image.shape\n", 120 | "\n", 121 | " # Move channel dimension to the front (assuming PyTorch format) and normalize pixel values by 255\n", 122 | " image = image.transpose((2, 0, 1)) / 255.0 \n", 123 | "\n", 124 | " # Reshape mean and std into numpy arrays with proper dimensions for broadcasting\n", 125 | " np_means = np.array(mean).reshape(c, 1, 1) \n", 126 | " np_stds = np.array(std).reshape(c, 1, 1) \n", 127 | "\n", 128 | " # Normalize the image by subtracting the mean and dividing by the standard deviation (with epsilon for stability)\n", 129 | " norm_image = (image - np_means) / (np_stds + 1e-6)\n", 130 | "\n", 131 | " # Expand the dimension at index 0 to create a batch dimension (assuming batch size of 1)\n", 132 | " # and cast the data type to float32 for compatibility with most models\n", 133 | " return np.expand_dims(norm_image, 0).astype(np.float32)" 134 | ] 135 | }, 136 | { 137 | "cell_type": "markdown", 138 | "id": "5eaae5d7", 139 | "metadata": {}, 140 | "source": [ 141 | "## Load and test ONNX model" 142 | ] 143 | }, 144 | { 145 | "cell_type": "code", 146 | "execution_count": null, 147 | "id": "f07c7472", 148 | "metadata": {}, 149 | "outputs": [], 150 | "source": [ 151 | "# Assuming a function 'crop_resize' exists for image cropping and resizing\n", 152 | "test_image = crop_resize(Image.open(\"../../data/dog.jpg\"), 224)\n", 153 | "\n", 154 | "# 'test_image' now holds the cropped and resized image from the dog.jpg file" 155 | ] 156 | }, 157 | { 158 | "cell_type": "code", 159 | "execution_count": null, 160 | "id": "497bd1c2", 161 | "metadata": {}, 162 | "outputs": [], 163 | "source": [ 164 | "# Convert PIL image to numpy array\n", 165 | "np_image = np.array(test_image)\n", 166 | "\n", 167 | "# Define mean and standard deviation values (assuming these are for normalization)\n", 168 | "mean = [0.485, 0.456, 0.406]\n", 169 | "std = [0.229, 0.224, 0.225]\n", 170 | "\n", 171 | "# Normalize and reshape the image for inference using the 'image_normalise_reshape' function\n", 172 | "norm_image = image_normalise_reshape(np_image, mean, std)\n", 173 | "\n", 174 | "# Comment about batch processing (not implemented in this code block)\n", 175 | "# Should also work with batch of images!\n", 176 | "# norm_image_batch = np.concatenate((norm_image, norm_image), 0)\n", 177 | "\n", 178 | "# Prepare input data for ONNX Runtime session\n", 179 | "onnxruntime_input = {ort_session.get_inputs()[0].name: norm_image}" 180 | ] 181 | }, 182 | { 183 | "cell_type": "markdown", 184 | "id": "e12ee73f", 185 | "metadata": {}, 186 | "source": [ 187 | "## How fast is inference?" 188 | ] 189 | }, 190 | { 191 | "cell_type": "code", 192 | "execution_count": null, 193 | "id": "ac9314dd", 194 | "metadata": {}, 195 | "outputs": [], 196 | "source": [ 197 | "# List to store inference times\n", 198 | "inference_time = []\n", 199 | "\n", 200 | "# Perform multiple inference runs (10 in this case)\n", 201 | "for _ in range(10):\n", 202 | " # Record start time\n", 203 | " start_time = time.time()\n", 204 | "\n", 205 | " # Run inference using ONNX Runtime session\n", 206 | " onnxruntime_outputs = ort_session.run(None, onnxruntime_input)\n", 207 | "\n", 208 | " # Record end time\n", 209 | " end_time = time.time()\n", 210 | "\n", 211 | " # Calculate and store inference time for this run\n", 212 | " inference_time.append(end_time - start_time)\n", 213 | "\n", 214 | "# Print the minimum inference time observed across the runs\n", 215 | "print(\"Minimum inference time %.4fs\" % np.min(inference_time))" 216 | ] 217 | }, 218 | { 219 | "cell_type": "code", 220 | "execution_count": null, 221 | "id": "7c428abc", 222 | "metadata": {}, 223 | "outputs": [], 224 | "source": [ 225 | "# Print the outputs from ONNX Runtime inference\n", 226 | "print(\"ONNX Runtime outputs:\")\n", 227 | "for output in onnxruntime_outputs:\n", 228 | " # Get the predicted class index (assuming the output represents class probabilities)\n", 229 | " class_index = np.argmax(output)\n", 230 | " print(\"Class index:\", class_index)\n", 231 | "\n", 232 | " # Assuming 'img_net_classes' is a dictionary mapping class indices to labels\n", 233 | " # Look up the class label corresponding to the predicted class index\n", 234 | " print(\"Class Label:\", img_net_classes.get(str(class_index))) # No code change" 235 | ] 236 | } 237 | ], 238 | "metadata": { 239 | "kernelspec": { 240 | "display_name": "Python 3 (ipykernel)", 241 | "language": "python", 242 | "name": "python3" 243 | }, 244 | "language_info": { 245 | "codemirror_mode": { 246 | "name": "ipython", 247 | "version": 3 248 | }, 249 | "file_extension": ".py", 250 | "mimetype": "text/x-python", 251 | "name": "python", 252 | "nbconvert_exporter": "python", 253 | "pygments_lexer": "ipython3", 254 | "version": "3.9.16" 255 | } 256 | }, 257 | "nbformat": 4, 258 | "nbformat_minor": 5 259 | } 260 | -------------------------------------------------------------------------------- /section15_deploying_models/solutions/ONNX_Basics/onnx_inference.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | from PIL import Image 3 | import numpy as np 4 | import onnxruntime 5 | import json 6 | import time 7 | 8 | 9 | def crop_resize(image, new_size): 10 | # Get the dimensions of the original image 11 | width, height = image.size 12 | 13 | # Calculate the size of the square crop 14 | min_dim = min(width, height) 15 | 16 | # Calculate coordinates for the center crop 17 | left = (width - min_dim) // 2 18 | upper = (height - min_dim) // 2 19 | right = left + min_dim 20 | lower = upper + min_dim 21 | 22 | # Crop the image to a square 23 | square_image = image.crop((left, upper, right, lower)) 24 | 25 | # Resize the image to the specified size 26 | resized_image = square_image.resize((new_size, new_size)) 27 | 28 | return resized_image 29 | 30 | 31 | def image_normalise_reshape(image, mean, std): 32 | # Get image dimensions (height, width, channels) 33 | h, w, c = image.shape 34 | 35 | # Move channel dimension to the front (assuming PyTorch format) and normalize pixel values by 255 36 | image = image.transpose((2, 0, 1)) / 255.0 37 | 38 | # Reshape mean and std into numpy arrays with proper dimensions for broadcasting 39 | np_means = np.array(mean).reshape(c, 1, 1) 40 | np_stds = np.array(std).reshape(c, 1, 1) 41 | 42 | # Normalize the image by subtracting the mean and dividing by the standard deviation (with epsilon for stability) 43 | norm_image = (image - np_means) / (np_stds + 1e-6) 44 | 45 | # Expand the dimension at index 0 to create a batch dimension (assuming batch size of 1) 46 | # and cast the data type to float32 for compatibility with most models 47 | return np.expand_dims(norm_image, 0).astype(np.float32) 48 | 49 | 50 | def run_sample(session, image): 51 | # Prepare the input for the ONNX model 52 | onnxruntime_input = {session.get_inputs()[0].name: image} 53 | 54 | # Run the model and get the output 55 | onnxruntime_outputs = session.run(None, onnxruntime_input) 56 | 57 | # Get the class index with the highest score 58 | class_index = np.argmax(onnxruntime_outputs[0]) 59 | 60 | return class_index 61 | 62 | 63 | if __name__ == "__main__": 64 | # Initialize the ONNX runtime session with the specified model 65 | ort_session = onnxruntime.InferenceSession("./efficientnet_b1.onnx", providers=['CPUExecutionProvider']) 66 | 67 | # Define mean and standard deviation for normalization 68 | mean = [0.485, 0.456, 0.406] 69 | std = [0.229, 0.224, 0.225] 70 | 71 | # Load the class labels from a JSON file 72 | with open("../../data/imagenet_classes.json", "r") as file: 73 | img_net_classes = json.load(file) 74 | 75 | # Open a connection to the webcam 76 | cap = cv2.VideoCapture(0) 77 | 78 | if not cap.isOpened(): 79 | print("Error: Could not open video stream.") 80 | exit() 81 | print("Press 'q' to quit, 'spacebar' to call the function.") 82 | 83 | while True: 84 | # Capture a frame from the webcam 85 | ret, frame = cap.read() 86 | 87 | if not ret: 88 | print("Error: Failed to capture frame.") 89 | break 90 | 91 | # Display the captured frame 92 | cv2.imshow('Camera', frame) 93 | 94 | # Wait for key press for 5 ms 95 | key = cv2.waitKey(5) 96 | 97 | if key & 0xFF == ord('q'): # Exit if 'q' is pressed 98 | print("Exiting...") 99 | break 100 | elif key & 0xFF == ord(' '): # Call function if space-bar is pressed 101 | # Process the captured frame 102 | cropped_frame = crop_resize(Image.fromarray(frame), 224) 103 | norm_image = image_normalise_reshape(np.array(cropped_frame), mean, std) 104 | 105 | # Measure the inference time 106 | start_time = time.time() 107 | image_class = run_sample(ort_session, norm_image) 108 | end_time = time.time() 109 | 110 | # Display the classification result and inference time 111 | print("Class index:", image_class) 112 | print("Class Label:", img_net_classes[str(image_class)]) 113 | print("Inference Time: %.4fs" % (end_time - start_time)) 114 | 115 | # Release the webcam and close all OpenCV windows 116 | cap.release() 117 | cv2.destroyAllWindows() --------------------------------------------------------------------------------