-
Notifications
You must be signed in to change notification settings - Fork 192
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added initial pass at the machine learning project
- Loading branch information
1 parent
449d363
commit fdd3bcc
Showing
4 changed files
with
337 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,4 +11,5 @@ projects/.c9/* | |
*.idea | ||
cmake-build-debug | ||
build | ||
data | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
# Project 1.4 | ||
Welcome to this first robotics themed projects of the RoboJackets software training | ||
curriculum. The point of these projects is to give you hands on experience playing | ||
around with code related to a robotics topic that we have covered in lecture. | ||
I hope you enjoy this one | ||
|
||
# Project Objective | ||
During this project we will be training a Neural Network on the MNIST dataset. | ||
This is the canonical hello world of the machine learning world. The goal is to | ||
classify images of handwritten digits into what it is. We will be using a | ||
convolutional neural network in order to effectively process the image data. | ||
|
||
# Starter Code | ||
We will be using something called Google Colab. This allows you to run a python notebook | ||
on google servers using their GPU's. Often GPU setup is nontrivial and this allows us to | ||
get up an running almost immediately. You would not want to use this for more general | ||
work unless you do not have another option. Open up the file here and click the button | ||
to add it to your own drive. There is also a complete version of the code that can be found | ||
here in script form. | ||
|
||
Today we will be using Python but you *DO NOT NEED TO KNOW PYTHON*. A lot of machine | ||
learning libraries are implemented with python wrappers, hence why we are using python. | ||
Don't worry though, you won't have to edit any of the code that isn't very explicit. | ||
|
||
## Python Notebooks Basics | ||
|
||
Each block is of the same code, just press the play button to run that section of code. | ||
The results from each section are kept in memory, this allows you to run a further | ||
along section of the code multiple times without constantly rerunning the initial sections. | ||
If you change a section above another you will have to rerun it. | ||
|
||
## Part 1 | ||
For this section you should run the entire notebook step by step. I would recommend | ||
using the button at the top pictured here in order to make it easier on yourself. | ||
|
||
You should see output similar to this, the instructions inside the notebook should | ||
cover any questions you might have about what each section of code is doing. | ||
You should not read over each line of code unless instructed to do so, you won't | ||
need to know how any of the code works in order to complete the exercise (I wrote | ||
it and I barely know how it works). | ||
|
||
You should see a graph similar to the one below at the final output. | ||
|
||
## Part 2 | ||
Now that we can train a simple model we want to try to increase out accuracy. Remember | ||
how we discussed underfitting the model previously. Now our model has enough room to | ||
accurate model the data (trust me on this one). So what else could we look at in order to | ||
get a better model for the data? Consider the graph at the end, what should be | ||
happening to know that we have maxed out our accuracy? | ||
|
||
Remember that you should only have to change locations where it says change me. | ||
I have also provided bounds on how much you should change certain variables, this | ||
is mostly so you don't spend hours on this part. | ||
Which one of those locations in the code listed below would be the right place | ||
to solve this issue? | ||
|
||
````python | ||
learning_rate = 1e-6 # <=================== CHANGE ME HERE range:[1e-8, 1e-1] | ||
num_epochs = 3 # <=================== CHANGE ME HERE range:[1, 20] | ||
```` | ||
|
||
<details> | ||
<summary>Answer</summary> | ||
That graph tells me that we should be training for longer, out error curve | ||
has not leveled out yet, maybe just increasing | ||
the number of epochs would significantly improve out network. | ||
</details> | ||
|
||
<details> | ||
<summary>Answer</summary> | ||
That graph tells me that we should be training for longer, out error curve | ||
has not leveled out yet, maybe just increasing | ||
the number of epochs would significantly improve out network. | ||
</details> | ||
|
||
## Part 3 | ||
Now that we have gotten better results could we have gotten here faster? Try | ||
thinking about what we should change in order to converge faster. Let's really put | ||
the speed to the test and use the lower upper limit here. Are your results any better? | ||
If results are bad, try starting at a known good value and increasing until | ||
the performance starts to degrade significantly. | ||
|
||
You should be getting accuracy above 90%. | ||
|
||
## Part 4 | ||
Now that we have a beautiful neural network for the current dataset let us try | ||
something a little more difficult. We will be using KMNIST, a dataset very similar to MNIST. | ||
|
||
TODO include graphic | ||
|
||
Go back to the section where we loaded in the dataset and change the lines to be | ||
|
||
````python | ||
## download and load training dataset | ||
trainset = torchvision.datasets.KMNIST(root='./data', train=True, | ||
download=True, transform=transform) | ||
trainloader = torch.utils.data.DataLoader(trainset, batch_size=BATCH_SIZE, | ||
shuffle=True, num_workers=2) | ||
|
||
## download and load testing dataset | ||
testset = torchvision.datasets.KMNIST(root='./data', train=False, | ||
download=True, transform=transform) | ||
testloader = torch.utils.data.DataLoader(testset, batch_size=BATCH_SIZE, | ||
shuffle=False, num_workers=2) | ||
```` | ||
|
||
Now try playing around with the new dataset and see if you can change values | ||
to give accuracy above 91%. | ||
[Training](https://www.google.com/url?sa=i&url=https%3A%2F%2Fwww.reddit.com%2Fr%2FProgrammerHumor%2Fcomments%2F9cu51a%2Fshamelessly_stolen_from_xkcd_credit_where_is_due%2F&psig=AOvVaw00gXQw1TnbHykteNddM-rV&ust=1599439002194000&source=images&cd=vfe&ved=0CAIQjRxqFwoTCNjm5-Ck0-sCFQAAAAAdAAAAABAg) | ||
is a good time to check out the videos below | ||
that give a better intuition into what is happening with these neural networks. This | ||
guy has some fancy visualizations. | ||
|
||
Here are the links to some videos | ||
|
||
[This series](https://www.youtube.com/watch?v=aircAruvnKk&list=PLZHQObOWTQDNU6R1_67000Dx_ZCJB-3pi) | ||
of videos will cover what we covered in this presentation in video 1 and 2. | ||
Three and four go into how backpropagation actually works, be warned there is math. | ||
|
||
|
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,215 @@ | ||
import torch | ||
import torch.nn as nn | ||
import torch.nn.functional as F | ||
import torchvision | ||
import torchvision.transforms as transforms | ||
|
||
BATCH_SIZE=32 | ||
|
||
## transformations | ||
transform = transforms.Compose( | ||
[transforms.ToTensor()]) | ||
|
||
## download and load training dataset | ||
trainset = torchvision.datasets.MNIST(root='./data', train=True, | ||
download=True, transform=transform) | ||
trainloader = torch.utils.data.DataLoader(trainset, batch_size=BATCH_SIZE, | ||
shuffle=True, num_workers=2) | ||
|
||
## download and load testing dataset | ||
testset = torchvision.datasets.MNIST(root='./data', train=False, | ||
download=True, transform=transform) | ||
testloader = torch.utils.data.DataLoader(testset, batch_size=BATCH_SIZE, | ||
shuffle=False, num_workers=2) | ||
|
||
# ==================================================================== | ||
|
||
import matplotlib.pyplot as plt | ||
import numpy as np | ||
|
||
|
||
## functions to show an image | ||
def imshow(img): | ||
#img = img / 2 + 0.5 # unnormalize | ||
npimg = img.numpy() | ||
plt.imshow(np.transpose(npimg, (1, 2, 0))) | ||
plt.show() | ||
|
||
## get some random training images | ||
dataiter = iter(trainloader) | ||
images, labels = dataiter.next() | ||
|
||
## show images | ||
#imshow(torchvision.utils.make_grid(images)) TODO | ||
|
||
# ==================================================================== | ||
|
||
class MyModel(nn.Module): | ||
def __init__(self): | ||
super(MyModel, self).__init__() | ||
|
||
# 28x28x1 => 26x26x32 | ||
self.conv1 = nn.Conv2d(in_channels=1, out_channels=32, kernel_size=3) | ||
self.d1 = nn.Linear(26 * 26 * 32, 128) | ||
self.d2 = nn.Linear(128, 10) | ||
|
||
def forward(self, x): | ||
# 32x1x28x28 => 32x32x26x26 | ||
x = self.conv1(x) | ||
x = F.relu(x) | ||
|
||
# flatten => 32 x (32*26*26) | ||
x = x.flatten(start_dim = 1) | ||
|
||
# 32 x (32*26*26) => 32x128 | ||
x = self.d1(x) | ||
x = F.relu(x) | ||
|
||
# logits => 32x10 | ||
logits = self.d2(x) | ||
out = F.softmax(logits, dim=1) | ||
return out | ||
|
||
# ==================================================================== | ||
|
||
# run the random model through the data to make sure our ins and outs correspond to each other | ||
#model = MyModel() | ||
#for images, labels in trainloader: | ||
# print("batch size:", images.shape) | ||
# out = model(images) | ||
# print(out.shape) | ||
# break | ||
|
||
# ==================================================================== | ||
|
||
## compute accuracy | ||
def get_accuracy(logit, target, batch_size): | ||
''' Obtain accuracy for training round ''' | ||
corrects = (torch.max(logit, 1)[1].view(target.size()).data == target.data).sum() | ||
accuracy = 100.0 * corrects/batch_size | ||
return accuracy.item() | ||
|
||
|
||
def insepct_detection(image, output): | ||
fig, axs = plt.subplots(2) | ||
fig.suptitle('Plots') | ||
axs[0].set_title("Image") | ||
image = np.transpose(image.numpy(), (1,2,0)) | ||
#print(np.transpose(image.numpy(), (1,2,0)).shape) | ||
#imshow(image) | ||
axs[0].imshow(image) | ||
axs[1].bar(range(0,10), sample_output) | ||
axs[1].legend(loc='upper right', frameon=False) | ||
axs[1].set_title("Probability of digit") | ||
plt.show() | ||
|
||
# ==================================================================== | ||
|
||
# possible exercises | ||
# not running long enough | ||
# learning rate = 1e-6 | ||
# num_epochs = 3 | ||
|
||
# run for longer | ||
# num_epochs = 20 | ||
|
||
# Increase the learning rate | ||
# learning_rate = 1e-1 | ||
# num_epochs = 3 | ||
|
||
# decrease the learning rate to get a better result on 3 epochs (> 90% accuracy) | ||
# learning_rate = 1e-3 | ||
# num_epochs = 3 | ||
|
||
# increase the number of epochs, can we learn more? | ||
# num_epochs = 20 | ||
|
||
# give them KMNIST and then have then see if they can beat my top accuracy | ||
# 91 | ||
|
||
learning_rate = 1e-3 # <=================== CHANGE ME HERE | ||
num_epochs = 50 # <=================== CHANGE ME HERE | ||
|
||
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") | ||
model = MyModel() | ||
model = model.to(device) | ||
criterion = nn.CrossEntropyLoss() | ||
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate) | ||
|
||
|
||
training_loss_plot = [] | ||
training_accuracy_plot = [] | ||
test_accuracy_plot = [] | ||
|
||
test_acc = 0.0 | ||
for i, (images, labels) in enumerate(testloader, 0): | ||
images = images.to(device) | ||
labels = labels.to(device) | ||
outputs = model(images) | ||
test_acc += get_accuracy(outputs, labels, BATCH_SIZE) | ||
if i == 0: | ||
image = torchvision.utils.make_grid(images.cpu()[0]) | ||
sample_output = outputs[0].cpu().detach().numpy() | ||
insepct_detection(image, sample_output) | ||
|
||
|
||
print('Initial Test Accuracy: %.2f'%( test_acc/i)) | ||
|
||
for epoch in range(num_epochs): | ||
train_running_loss = 0.0 | ||
train_acc = 0.0 | ||
|
||
model = model.train() | ||
|
||
## training step | ||
for i, (images, labels) in enumerate(trainloader): | ||
|
||
images = images.to(device) | ||
labels = labels.to(device) | ||
|
||
## forward + backprop + loss | ||
logits = model(images) | ||
loss = criterion(logits, labels) | ||
optimizer.zero_grad() | ||
loss.backward() | ||
|
||
## update model params | ||
optimizer.step() | ||
|
||
train_running_loss += loss.detach().item() | ||
train_acc += get_accuracy(logits, labels, BATCH_SIZE) | ||
|
||
model.eval() | ||
average_train_loss = train_running_loss / i | ||
average_train_accuracy = train_acc / i | ||
training_loss_plot.append(100 - average_train_loss) | ||
training_accuracy_plot.append(100 - average_train_accuracy) | ||
print('Epoch: %d | Loss: %.4f | Train Accuracy: %.2f' \ | ||
%(epoch, average_train_loss, average_train_accuracy)) | ||
|
||
|
||
test_acc = 0.0 | ||
for i, (images, labels) in enumerate(testloader, 0): | ||
images = images.to(device) | ||
labels = labels.to(device) | ||
outputs = model(images) | ||
test_acc += get_accuracy(outputs, labels, BATCH_SIZE) | ||
# only show result on the last epoch | ||
if i == 0 and (epoch+1 is num_epochs or epoch == 0): | ||
image = torchvision.utils.make_grid(images.cpu()[0]) | ||
sample_output = outputs[0].cpu().detach().numpy() | ||
insepct_detection(image, sample_output) | ||
test_accuracy_plot.append(100 - test_acc/i) | ||
print('Test Accuracy: %.2f'%( test_acc/i)) | ||
|
||
# ==================================================================== | ||
|
||
plt.title("Training vs Test accuracy") | ||
plt.plot(range(1,num_epochs+1), training_accuracy_plot, label='Training Error (%)') | ||
plt.plot(range(1,num_epochs+1), test_accuracy_plot, label='Testing Error (%)') | ||
plt.legend(loc='upper right', frameon=False) | ||
plt.show() | ||
|
||
# ==================================================================== | ||
|
||
|