From b555b45d1b1a7767b0fd433a14ebfb65db036c2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joseph=20Hopfm=C3=BCller?= Date: Fri, 21 Oct 2022 17:29:09 +0200 Subject: [PATCH] finish chapter 15 --- .gitignore | 4 +- 14_cnn => 14_cnn.py | 54 +++++++++++----- 15_transfer.py | 153 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 192 insertions(+), 19 deletions(-) rename 14_cnn => 14_cnn.py (59%) create mode 100644 15_transfer.py diff --git a/.gitignore b/.gitignore index ff0d053..e3d0f44 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,5 @@ lib64 share/ pyvenv.cfg .python-version -data/MNIST/ -data/CIFAR10/ \ No newline at end of file +data/* +!data/wine/ \ No newline at end of file diff --git a/14_cnn b/14_cnn.py similarity index 59% rename from 14_cnn rename to 14_cnn.py index b3be0ba..ca99d70 100644 --- a/14_cnn +++ b/14_cnn.py @@ -27,8 +27,8 @@ device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') print(f'Device is {device}') # hyper parameters -num_epochs = 4 -batch_size = 4 +num_epochs = 10 +batch_size = 10 learning_rate = 0.001 # dataset has PILImage images of range [0,1] @@ -51,24 +51,44 @@ test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=batch_size) classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck') # can also get them from the data, probably -#implement conv net -class ConvNet(nn.Module): - def __init__(self) -> None: - super().__init__() - self.layers = nn.Sequential([ - nn.Conv2d(3, 6, 5), # 3 color channels, 6 output channels, kernel size 5 - nn.MaxPool2d(2, 2), # kernel size 2, stride 2 (whats stride?) - - # 3:23:19 - ]) - pass +# #implement conv net +# class ConvNet(nn.Module): +# def __init__(self): +# super(ConvNet, self).__init__() +# self.layers = nn.Sequential( # -> 32x32 +# nn.Conv2d(3, 6, 5), # 3 color channels, 6 output channels, kernel size 5 -> image size shrinks by 2 pixels in each direction -> 28x28 +# nn.ReLU(), +# nn.MaxPool2d(2, 2), # kernel size 2, stride 2 -> shift by 2 pixels after each max-pooling -> image size shrinks to half size -> 14x14 +# nn.Conv2d(6, 16, 5), # input size is output size of previous conv layer, -> image size shrinks by 2 pixels in each direction -> 10x10 +# nn.ReLU(), +# nn.MaxPool2d(2, 2), # kernel size 2, stride 2 -> shift by 2 pixels after each max-pooling -> image size shrinks to half size -> 5x5 +# nn.Flatten(), +# nn.Linear(16*5*5, 120), # 16 channels * 5px * 5px +# nn.ReLU(), +# nn.Linear(120, 84), +# nn.ReLU(), +# nn.Linear(84, 10) # output size 10 for 10 classes +# ) - def forward(self, x): - # return self.layers(x) - pass +# def forward(self, x): +# return self.layers(x) -model = ConvNet().to(device) +# model = ConvNet().to(device) +model = nn.Sequential( # -> 32x32 + nn.Conv2d(3, 6, 5), # 3 color channels, 6 output channels, kernel size 5 -> image size shrinks by 2 pixels in each direction -> 28x28 + nn.ReLU(), + nn.MaxPool2d(2, 2), # kernel size 2, stride 2 -> shift by 2 pixels after each max-pooling -> image size shrinks to half size -> 14x14 + nn.Conv2d(6, 16, 5), # input size is output size of previous conv layer, -> image size shrinks by 2 pixels in each direction -> 10x10 + nn.ReLU(), + nn.MaxPool2d(2, 2), # kernel size 2, stride 2 -> shift by 2 pixels after each max-pooling -> image size shrinks to half size -> 5x5 + nn.Flatten(), + nn.Linear(16*5*5, 120), # 16 channels * 5px * 5px + nn.ReLU(), + nn.Linear(120, 84), + nn.ReLU(), + nn.Linear(84, 10) # output size 10 for 10 classes + ).to(device) criterion = nn.CrossEntropyLoss() optimizer = torch.optim.SGD(model.parameters(), lr = learning_rate) diff --git a/15_transfer.py b/15_transfer.py new file mode 100644 index 0000000..3c398dd --- /dev/null +++ b/15_transfer.py @@ -0,0 +1,153 @@ +# new: + +import torch +import torch.nn as nn +import torch.optim as optim +from torch.optim import lr_scheduler +import numpy as np +import torchvision +from torchvision import datasets, models, transforms +import matplotlib.pyplot as plt +import time +import os +import copy + +device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') + +mean = np.array([0.485, 0.456, 0.406]) +std = np.array([0.229, 0.224, 0.225]) + +data_transforms = { + 'train': transforms.Compose([ + transforms.RandomResizedCrop(224), + transforms.RandomHorizontalFlip(), + transforms.ToTensor(), + transforms.Normalize(mean, std) + ]), + 'val': transforms.Compose([ + transforms.Resize(256), + transforms.CenterCrop(224), + transforms.ToTensor(), + transforms.Normalize(mean, std) + ]) +} + +#import data +data_dir = 'data/hymenoptera_data' +sets = ['train', 'val'] +image_datasets = {x: datasets.ImageFolder(os.path.join(data_dir, x), data_transforms[x]) for x in sets} +dataloaders = {x: torch.utils.data.DataLoader(image_datasets[x], batch_size=4, shuffle=True, num_workers=0) for x in sets} +dataset_sizes = {x: len(image_datasets[x]) for x in sets} +class_names = image_datasets['train'].classes +print(f'Classes: {class_names}') + + +# # show sample images +# def imshow(inp, title): +# """Imshow for Tensor.""" +# inp = inp.numpy().transpose((1, 2, 0)) +# inp = std * inp + mean +# inp = np.clip(inp, 0, 1) +# plt.imshow(inp) +# plt.title(title) +# plt.show() +# inputs, classes = next(iter(dataloaders['train'])) +# out = torchvision.utils.make_grid(inputs) +# imshow(out, title=[class_names[x] for x in classes]) + + +def train_model(model, criterion, optimizer, scheduler, num_epochs=25): + since = time.time() + best_model_wts = copy.deepcopy(model.state_dict()) + best_acc = 0.0 + + for epoch in range(num_epochs): + print(F'Epoch {epoch+1}/{num_epochs}') + print('-'*11) + + for phase in sets: + if phase == 'train': + model.train() + else: + model.eval() + + running_loss = 0.0 + running_corrects = 0 + + for inputs, labels in dataloaders[phase]: + inputs = inputs.to(device) + labels = labels.to(device) + + with torch.set_grad_enabled(phase == 'train'): + outputs = model(inputs) + _, preds = torch.max(outputs, 1) + loss = criterion(outputs, labels) + + if phase == 'train': + optimizer.zero_grad() + loss.backward() + optimizer.step() + + running_loss += loss.item() * inputs.size(0) + running_corrects += torch.sum(preds == labels.data) + + if phase == 'train': + scheduler.step() + + epoch_loss = running_loss/dataset_sizes[phase] + epoch_acc = running_corrects.double()/dataset_sizes[phase] + + print(f'{phase} Loss: {epoch_loss:.4f}, Acc: {epoch_acc:.4f}') + + if phase == 'val' and epoch_acc > best_acc: + best_acc = epoch_acc + best_model_wts = copy.deepcopy(model.state_dict()) + + print() + + time_elapsed = time.time() - since + print(f'Training complete in {time_elapsed//(60*60):02.0f}:{(time_elapsed%(60*60)//60):02.0f}:{time_elapsed%60:02.0f}') + print(f'Best val Acc: {best_acc:.4f}') + + model.load_state_dict(best_model_wts) + return model + +## transfer learning - finetuning ## +model = torchvision.models.resnet18(weights=torchvision.models.ResNet18_Weights.DEFAULT) # import pretrained model + +#change last layer +num_ftrs = model.fc.in_features +model.fc = nn.Linear(num_ftrs, 2) +model.to(device) + +criterion = nn.CrossEntropyLoss() +optimizer = torch.optim.SGD(model.parameters(), lr=0.001) + +# scheduler +# updates the learning rate +step_lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=7, gamma=0.1) # every seven epochs: lr = lr*gamma + +model = train_model(model, criterion, optimizer, step_lr_scheduler, num_epochs=50) + +print('-'*50) +print() + +## transfer learning - feature extractor ## +model = torchvision.models.resnet18(weights=torchvision.models.ResNet18_Weights.DEFAULT) # import pretrained model # import pretrained model +for param in model.parameters(): + param.requires_grad = False + # freezes all layers in beginning + +#change last layer +num_ftrs = model.fc.in_features +model.fc = nn.Linear(num_ftrs, 2) +model.to(device) + +criterion = nn.CrossEntropyLoss() +optimizer = torch.optim.SGD(model.parameters(), lr=0.001) + +# scheduler +# updates the learning rate +step_lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=7, gamma=0.1) # every seven epochs: lr = lr*gamma + +model = train_model(model, criterion, optimizer, step_lr_scheduler, num_epochs=50) \ No newline at end of file