├── .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 |
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()
--------------------------------------------------------------------------------