fast.ai Chapter 4
path = untar_data(URLs.MNIST_SAMPLE)
Path.BASE_PATH = path
threes = (path/'train'/'3').ls().sorted()
sevens = (path/'train'/'7').ls().sorted()
Now we can read each image and then convert it to a tensor:
seven_tensors = [tensor(Image.open(o)) for o in sevens]
three_tensors = [tensor(Image.open(o)) for o in threes]
valid_3_tens = torch.stack([tensor(Image.open(o))
for o in (path/'valid'/'3').ls()])
valid_7_tens = torch.stack([tensor(Image.open(o))
for o in (path/'valid'/'7').ls()])
We normalize the pixels by dividing them by 255:
stacked_sevens = torch.stack(seven_tensors).float()/255
stacked_threes = torch.stack(three_tensors).float()/255
valid_3_tens = valid_3_tens.float()/255
valid_7_tens = valid_7_tens.float()/255
We can then put all of the training images on a rank 2 tensor:
train_x = torch.cat([stacked_threes, stacked_sevens]).view(-1, 28*28)
valid_x = torch.cat([valid_3_tens, valid_7_tens]).view(-1, 28*28)
For the labels, we put them in a column vector:
train_y = tensor([1]*len(threes) + [0]*len(sevens)).unsqueeze(1)
valid_y = tensor([1]*len(valid_3_tens) + [0]*len(valid_7_tens)).unsqueeze(1)
Finally, we put both of these tensors in the dataLoaders:
dset = list(zip(train_x,train_y))
dl = DataLoader(dset, batch_size=256)
valid_dset = list(zip(valid_x,valid_y))
valid_dl = DataLoader(valid_dset, batch_size=256)
dls = DataLoaders(dl, valid_dl)
simple_net = nn.Sequential(
nn.Linear(28*28,30),
nn.ReLU(),
nn.Linear(30,1),
nn.Sigmoid()
)
SGD
nn.BCELoss()
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
class Learner():
def __init__(self, dataloaders, model, opt, loss):
self.train_dl = dataloaders[0]
self.valid_dl = dataloaders[1]
self.model = model
self.opt = opt(self.model.parameters(), lr=0.1)
self.loss = loss
def batch_accuracy(self, preds, yb):
correct = (preds>0.5) == yb
return correct.float().mean()
def validate_epoch(self):
accs = [self.batch_accuracy(self.model(xb), yb) for xb,yb in self.valid_dl]
return round(torch.stack(accs).mean().item(), 4)
def train_epoch(self):
for xb,yb in self.train_dl:
preds = self.model(xb)
calculated_loss = self.loss(preds, yb*1.0)
calculated_loss.backward()
self.opt.step()
self.opt.zero_grad()
def fit(self, n):
for epoch in range(n):
self.train_epoch()
print(self.validate_epoch(), end=' ')
simple_net = nn.Sequential(
nn.Linear(28*28,30),
nn.ReLU(),
nn.Linear(30,1),
nn.Sigmoid()
)
example = Learner(dls, simple_net, SGD, nn.BCELoss())
example.fit(40)
Second Research Question
Complete all the steps in this chapter using the full MNIST datasets (that is, for all digits, not just 3s and 7s). This is a significant project and will take you quite a bit of time to complete! You'll need to do some of your own research to figure out how to overcome some obstacles you'll meet on the way.
Getting the data
path = untar_data(URLs.MNIST)
path.ls()
We can use the DataBlock
API to read both the train and test folders.
mnist = DataBlock(blocks=(ImageBlock(cls=PILImageBW), CategoryBlock),
get_items=get_image_files,
splitter=GrandparentSplitter(train_name='training', valid_name='testing'),
get_y=parent_label)
dls = mnist.dataloaders(path, num_workers = 0)
dls.show_batch(max_n=9, figsize=(4,4))
We must modify our sequential model to first flatten the 28x28 images that we just loaded. Also, we must output in the final layer 10 activations
simple_net = nn.Sequential(
Flatten(),
nn.Linear(28*28,30),
nn.ReLU(),
nn.Linear(30, 10)
)
Also, we must change our loss function and our definition of accuracy now that we have a multi-class problem
class Learner():
def __init__(self, dataloaders, model, opt, loss):
self.train_dl = dataloaders[0]
self.valid_dl = dataloaders[1]
self.model = model
self.opt = opt(self.model.parameters(), lr=0.1)
self.loss = loss
def train_epoch(self):
for xb,yb in self.train_dl:
preds = self.model(xb)
calculated_loss = self.loss(preds, yb.long())
calculated_loss.backward()
self.opt.step()
self.opt.zero_grad()
def batch_accuracy(self, preds, yb):
probs = torch.softmax(preds, dim=1)
winners = probs.argmax(dim=1)
corrects = (winners == yb)
accuracy = corrects.sum().float() / float( yb.size(0) )
return accuracy
def validate_epoch(self):
accs = [self.batch_accuracy(self.model(xb), yb) for xb,yb in self.valid_dl]
return round(torch.stack(accs).mean().item(), 4)
def fit(self, n):
for epoch in range(n):
self.train_epoch()
print(self.validate_epoch(), end=' ')
mnist_learner = Learner(dls, simple_net.to(device), SGD, nn.CrossEntropyLoss())
mnist_learner.fit(10)
96% accuracy! not that bad for such a simple model.