Jovian
⭐️
Sign In

Ship_Vessel Detection

Ship or vessel detection has a wide range of applications, in the areas of maritime safety, fisheries management, marine pollution, defence and maritime security, protection from piracy, illegal migration, etc.

Keeping this in mind, a Governmental Maritime and Coastguard Agency is planning to deploy a computer vision based automated system to identify ship type only from the images taken by the survey boats. You have been hired as a consultant to build an efficient model for this project.

In [1]:
import matplotlib.pyplot as plt

import torch
from torchvision import datasets, transforms
import pandas as pd
import os
from PIL import Image
import shutil
import torchvision
import numpy as np
In [2]:
#define the transformations
transform_ship = transforms.Compose([transforms.ToTensor()])
In [3]:
#define the batchsize
training_batchsize = 5

Write a custom script to create train data structure

In [4]:
#read the train csv

train_data = pd.read_csv("train.csv")
In [5]:
train_data.head()
Out[5]:
In [6]:
#Create a directory train
train_folder_path = "train"
test_folder_path = "test"
image_folder_path = "images"

if not os.path.exists(train_folder_path):
    os.mkdir(train_folder_path)
In [7]:
#looping through the dataframe

for index, row in train_data.iterrows():
    destination_folder = os.path.join(train_folder_path, str(row['category']))
    source_path = os.path.join(image_folder_path, row["image"])
    
    #checking if the destination folder exits or not
    if not os.path.exists(destination_folder):
        os.mkdir(destination_folder)
    
    #copying the images to the destination folder
    shutil.copy(source_path, destination_folder)
In [8]:
#copying testing data to one folder

#read the test csv

test_data = pd.read_csv("test_ApKoW4T.csv")
test_data.head()
Out[8]:
In [10]:
#looping through the dataframe

#checking if the destination folder exits or not
destination_folder = os.path.join(test_folder_path)

if not os.path.exists(destination_folder):
        os.mkdir(destination_folder)
        
for index, row in test_data.iterrows():
    source_path = os.path.join(image_folder_path, row["image"])

    #copying the images to the destination folder
    shutil.copy(source_path, destination_folder)
In [ ]:
 

Create a DataLoader

In [11]:
#define the transformations
transform = transforms.Compose([transforms.Resize(225),
                               transforms.CenterCrop(224),
                               transforms.ToTensor()])
In [12]:
#define the batchsize
training_batchsize = 5
In [13]:
#retrieve the full data
full_data = datasets.ImageFolder(train_folder_path, transform=transform)
In [14]:
#split the training data into train and validation

train_size = int(0.8 * len(full_data))
validation_size = len(full_data) - train_size

train_dataset, validation_dataset = torch.utils.data.random_split(full_data, [train_size, validation_size])
In [15]:
#create a dataloader
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=training_batchsize, shuffle=True)
validation_loader = torch.utils.data.DataLoader(validation_dataset, batch_size=training_batchsize, shuffle=True)
In [32]:
test_dataset = datasets.ImageFolder("test/", transform=transform)
In [33]:
test_dataset.targets = test_data.values.T.tolist()[0]
In [34]:
#create a dataloader 
test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=training_batchsize, shuffle=False)
In [ ]:
 
In [36]:
#custom function to display images

def imshow(img, title):
    
    #convert image from tensor to numpy for visualization
    npimg = img.numpy()
    #define the size of a figure
    plt.figure(figsize = (15, 15))
    plt.axis("off")
    
    #interchaging the image sizes - transposing
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    plt.title(title, fontsize=15)
    plt.show()
In [37]:
#function to get images and feed into our custom function 'imshow'

def show_batch_images(dataloader):
    
    #getting the images
    images, labels = next(iter(dataloader))
    #make a grid from those images
    img = torchvision.utils.make_grid(images)
    
    imshow(img, "classes: " + str([str(x.item()) for x in labels]))
In [38]:
show_batch_images(train_loader)
Notebook Image

Modeling

In [39]:
#checking for available gpu

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(device)
cuda:0
In [40]:
from torchvision import models
import torch.optim as optim
In [41]:
#define the number of classes for the final layer
num_classes = 5

Data Augmentation

A common strategy for training neural networks is to introduce randomness in the input data itself. For example, you can randomly rotate, mirror, scale, and/or crop your images during training. This will help your network generalize as it's seeing the same images but in different locations, with different sizes, in different orientations, etc.

To randomly rotate, scale and crop, then flip your images you would define your transforms like this:

train_transforms = transforms.Compose([transforms.RandomRotation(30),
                                       transforms.RandomResizedCrop(224),
                                       transforms.RandomHorizontalFlip(),
                                       transforms.ToTensor(),
                                       transforms.Normalize([0.5, 0.5, 0.5], 

You'll also typically want to normalize images with transforms.Normalize. You pass in a list of means and list of standard deviations, then the color channels are normalized like so

input[channel] = (input[channel] - mean[channel]) / std[channel]

Subtracting mean centers the data around zero and dividing by std squishes the values to be between -1 and 1. Normalizing helps keep the network work weights near zero which in turn makes backpropagation more stable. Without normalization, networks will tend to fail to learn.

You can find a list of all the available transforms here. When you're testing however, you'll want to use images that aren't altered (except you'll need to normalize the same way). So, for validation/test images, you'll typically just resize and crop.

In [42]:
transform_train = transforms.Compose([
    #cropping and resizing the image to 64x64
    transforms.RandomResizedCrop(224),
    transforms.RandomHorizontalFlip(),
    transforms.RandomVerticalFlip(),
    transforms.RandomRotation(30),
    #convert image to tensor
    transforms.ToTensor(),
    #normalizing the input mean = 0.5 and std = 0.5 for three channels (R,G,B)
    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
In [43]:
#retrieve the full data
full_data = datasets.ImageFolder(train_folder_path, transform=transform_train)

#split the training data into train and validation

train_size = int(0.8 * len(full_data))
validation_size = len(full_data) - train_size

train_dataset, validation_dataset = torch.utils.data.random_split(full_data, [train_size, validation_size])

#create a dataloader
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=training_batchsize, shuffle=True)
validation_loader = torch.utils.data.DataLoader(validation_dataset, batch_size=training_batchsize, shuffle=True)
In [44]:
#num of samples in training 

print(len(train_loader.dataset))
5001
In [45]:
#num of samples in validation

print(len(validation_loader.dataset))
1251

Resnet Model

In [ ]:
 
In [46]:
import torch.nn as nn
import copy
In [47]:
#create a iterator

dataiter = iter(train_loader)
images, labels = dataiter.next()

#shape of images bunch
print(images.shape)

#shape of first image in a group of 4
print(images[1].shape)

#class label for first image
print(labels[1])
torch.Size([5, 3, 224, 224]) torch.Size([3, 224, 224]) tensor(0)
In [48]:
model_ft = models.resnet18(pretrained=True)
In [49]:
print(model_ft)
ResNet( (conv1): Conv2d(3, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False) (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace) (maxpool): MaxPool2d(kernel_size=3, stride=2, padding=1, dilation=1, ceil_mode=False) (layer1): Sequential( (0): BasicBlock( (conv1): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace) (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) ) (1): BasicBlock( (conv1): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace) (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) ) ) (layer2): Sequential( (0): BasicBlock( (conv1): Conv2d(64, 128, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False) (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace) (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (downsample): Sequential( (0): Conv2d(64, 128, kernel_size=(1, 1), stride=(2, 2), bias=False) (1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) ) ) (1): BasicBlock( (conv1): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace) (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) ) ) (layer3): Sequential( (0): BasicBlock( (conv1): Conv2d(128, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False) (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace) (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (downsample): Sequential( (0): Conv2d(128, 256, kernel_size=(1, 1), stride=(2, 2), bias=False) (1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) ) ) (1): BasicBlock( (conv1): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace) (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) ) ) (layer4): Sequential( (0): BasicBlock( (conv1): Conv2d(256, 512, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False) (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace) (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (downsample): Sequential( (0): Conv2d(256, 512, kernel_size=(1, 1), stride=(2, 2), bias=False) (1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) ) ) (1): BasicBlock( (conv1): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (relu): ReLU(inplace) (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) ) ) (avgpool): AdaptiveAvgPool2d(output_size=(1, 1)) (fc): Linear(in_features=512, out_features=1000, bias=True) )
In [50]:
for param in model_ft.parameters():
    param.requires_grad = False
In [51]:
num_classes
Out[51]:
5
In [52]:
# Parameters of newly constructed modules have requires_grad=True by default

from collections import OrderedDict
fc = nn.Sequential(OrderedDict([
                          ('fc1', nn.Linear(512, 256)),
                          ('relu', nn.ReLU()),
                          ('dropout1', nn.Dropout()),
                          ('fc2', nn.Linear(256, num_classes)),
                          ('output', nn.Softmax(dim=1))
                          ]))
    
In [53]:
model_ft.fc = fc
In [54]:
out = model_ft(images)
In [55]:
out
Out[55]:
tensor([[0.2019, 0.0928, 0.3436, 0.1405, 0.2211],
        [0.2347, 0.0797, 0.3450, 0.0815, 0.2592],
        [0.2668, 0.2079, 0.3013, 0.0926, 0.1314],
        [0.2231, 0.1525, 0.2548, 0.1721, 0.1976],
        [0.2247, 0.1534, 0.1955, 0.2802, 0.1461]], grad_fn=<SoftmaxBackward>)
In [56]:
torch.max(out, 1)[1]
Out[56]:
tensor([2, 2, 2, 2, 3])
In [57]:
labels
Out[57]:
tensor([0, 0, 1, 4, 1])
In [ ]:
 
In [58]:
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('Epoch {}/{}'.format(epoch, num_epochs - 1))
        print('-' * 10)

        # Each epoch has a training and validation phase
        for phase in ['train', 'val']:
            if phase == 'train':
                scheduler.step()
                model.train()  # Set model to training mode
            else:
                model.eval()   # Set model to evaluate mode

            running_loss = 0.0
            running_corrects = 0

            # Iterate over data.
            for inputs, labels in dataloaders[phase]:
                inputs = inputs.to(device)
                labels = labels.to(device)

                # zero the parameter gradients
                optimizer.zero_grad()

                # forward
                # track history if only in train
                with torch.set_grad_enabled(phase == 'train'):
                    outputs = model(inputs)
                    _, preds = torch.max(outputs, 1)
                    loss = criterion(outputs, labels)

                    # backward + optimize only if in training phase
                    if phase == 'train':
                        loss.backward()
                        optimizer.step()

                # statistics
                running_loss += loss.item() * inputs.size(0)
                running_corrects += torch.sum(preds == labels.data)

            epoch_loss = running_loss / dataset_sizes[phase]
            epoch_acc = running_corrects.double() / dataset_sizes[phase]

            print('{} Loss: {:.4f} Acc: {:.4f}'.format(
                phase, epoch_loss, epoch_acc))

            # deep copy the model
            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('Training complete in {:.0f}m {:.0f}s'.format(
        time_elapsed // 60, time_elapsed % 60))
    print('Best val Acc: {:4f}'.format(best_acc))

    #saving the best model
    torch.save(model_ft.state_dict(best_model_wts),"saved.pth")
    print("best model saved")
    
    # load best model weights
    model.load_state_dict(best_model_wts)
    return model

Training Model

In [59]:
import time
In [64]:
criterion = nn.CrossEntropyLoss()

model_ft = model_ft.to(device)

# Observe that all parameters are being optimized
optimizer_ft = optim.SGD(model_ft.parameters(), lr=0.001, momentum=0.9)

# Decay LR by a factor of 0.1 every 7 epochs
exp_lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer_ft, step_size=7, gamma=0.1)
In [65]:
dataloaders = {"train": train_loader, "val": validation_loader}
In [66]:
dataset_sizes = {"train": len(train_loader.dataset), "val": len(validation_loader.dataset)}
In [ ]:
model_ft = train_model(model_ft, criterion, optimizer_ft, exp_lr_scheduler,
                       num_epochs=75)
Epoch 0/74 ---------- train Loss: 1.5025 Acc: 0.3941 val Loss: 1.4441 Acc: 0.4660 Epoch 1/74 ---------- train Loss: 1.4770 Acc: 0.4169 val Loss: 1.4310 Acc: 0.4604 Epoch 2/74 ---------- train Loss: 1.4647 Acc: 0.4267 val Loss: 1.4073 Acc: 0.4804 Epoch 3/74 ---------- train Loss: 1.4567 Acc: 0.4281 val Loss: 1.3871 Acc: 0.5324 Epoch 4/74 ---------- train Loss: 1.4285 Acc: 0.4727 val Loss: 1.3545 Acc: 0.5659 Epoch 5/74 ---------- train Loss: 1.4090 Acc: 0.4887 val Loss: 1.3401 Acc: 0.5635 Epoch 6/74 ---------- train Loss: 1.3971 Acc: 0.5069 val Loss: 1.3055 Acc: 0.6115 Epoch 7/74 ---------- train Loss: 1.3704 Acc: 0.5355 val Loss: 1.3081 Acc: 0.5939 Epoch 8/74 ---------- train Loss: 1.3676 Acc: 0.5395 val Loss: 1.3052 Acc: 0.6091 Epoch 9/74 ---------- train Loss: 1.3738 Acc: 0.5323 val Loss: 1.2947 Acc: 0.6155 Epoch 10/74 ---------- train Loss: 1.3582 Acc: 0.5533 val Loss: 1.2807 Acc: 0.6331 Epoch 11/74 ---------- train Loss: 1.3562 Acc: 0.5479 val Loss: 1.2795 Acc: 0.6395 Epoch 12/74 ---------- train Loss: 1.3585 Acc: 0.5515 val Loss: 1.2668 Acc: 0.6579 Epoch 13/74 ---------- train Loss: 1.3584 Acc: 0.5551 val Loss: 1.2848 Acc: 0.6307 Epoch 14/74 ---------- train Loss: 1.3503 Acc: 0.5599 val Loss: 1.2724 Acc: 0.6595 Epoch 15/74 ---------- train Loss: 1.3523 Acc: 0.5609 val Loss: 1.2789 Acc: 0.6427 Epoch 16/74 ---------- train Loss: 1.3544 Acc: 0.5549 val Loss: 1.2624 Acc: 0.6603 Epoch 17/74 ---------- train Loss: 1.3540 Acc: 0.5601 val Loss: 1.2762 Acc: 0.6323 Epoch 18/74 ---------- train Loss: 1.3580 Acc: 0.5481 val Loss: 1.2741 Acc: 0.6323 Epoch 19/74 ---------- train Loss: 1.3465 Acc: 0.5637 val Loss: 1.2750 Acc: 0.6307 Epoch 20/74 ---------- train Loss: 1.3508 Acc: 0.5559 val Loss: 1.2675 Acc: 0.6579 Epoch 21/74 ---------- train Loss: 1.3503 Acc: 0.5619 val Loss: 1.2740 Acc: 0.6307 Epoch 22/74 ---------- train Loss: 1.3544 Acc: 0.5519 val Loss: 1.2786 Acc: 0.6451 Epoch 23/74 ---------- train Loss: 1.3489 Acc: 0.5609 val Loss: 1.2722 Acc: 0.6499 Epoch 24/74 ---------- train Loss: 1.3481 Acc: 0.5665 val Loss: 1.2748 Acc: 0.6475 Epoch 25/74 ---------- train Loss: 1.3547 Acc: 0.5513 val Loss: 1.2700 Acc: 0.6603 Epoch 26/74 ---------- train Loss: 1.3515 Acc: 0.5625 val Loss: 1.2834 Acc: 0.6419 Epoch 27/74 ---------- train Loss: 1.3523 Acc: 0.5571 val Loss: 1.2748 Acc: 0.6443 Epoch 28/74 ---------- train Loss: 1.3493 Acc: 0.5665 val Loss: 1.2685 Acc: 0.6483 Epoch 29/74 ---------- train Loss: 1.3472 Acc: 0.5625 val Loss: 1.2774 Acc: 0.6307 Epoch 30/74 ---------- train Loss: 1.3535 Acc: 0.5611 val Loss: 1.2778 Acc: 0.6419 Epoch 31/74 ---------- train Loss: 1.3447 Acc: 0.5687 val Loss: 1.2615 Acc: 0.6643 Epoch 32/74 ---------- train Loss: 1.3489 Acc: 0.5687 val Loss: 1.2786 Acc: 0.6435 Epoch 33/74 ---------- train Loss: 1.3506 Acc: 0.5591 val Loss: 1.2759 Acc: 0.6459 Epoch 34/74 ---------- train Loss: 1.3540 Acc: 0.5583 val Loss: 1.2716 Acc: 0.6531 Epoch 35/74 ---------- train Loss: 1.3638 Acc: 0.5417 val Loss: 1.2788 Acc: 0.6355 Epoch 36/74 ---------- train Loss: 1.3516 Acc: 0.5581 val Loss: 1.2883 Acc: 0.6203 Epoch 37/74 ---------- train Loss: 1.3560 Acc: 0.5533 val Loss: 1.2712 Acc: 0.6531 Epoch 38/74 ---------- train Loss: 1.3564 Acc: 0.5525 val Loss: 1.2779 Acc: 0.6459 Epoch 39/74 ---------- train Loss: 1.3534 Acc: 0.5643 val Loss: 1.2763 Acc: 0.6451 Epoch 40/74 ---------- train Loss: 1.3488 Acc: 0.5695 val Loss: 1.2813 Acc: 0.6363 Epoch 41/74 ---------- train Loss: 1.3536 Acc: 0.5549 val Loss: 1.2779 Acc: 0.6467 Epoch 42/74 ---------- train Loss: 1.3534 Acc: 0.5595 val Loss: 1.2823 Acc: 0.6339 Epoch 43/74 ---------- train Loss: 1.3459 Acc: 0.5697 val Loss: 1.2667 Acc: 0.6611 Epoch 44/74 ---------- train Loss: 1.3553 Acc: 0.5539 val Loss: 1.2675 Acc: 0.6507 Epoch 45/74 ---------- train Loss: 1.3492 Acc: 0.5727 val Loss: 1.2851 Acc: 0.6347 Epoch 46/74 ---------- train Loss: 1.3513 Acc: 0.5647 val Loss: 1.2680 Acc: 0.6539 Epoch 47/74 ---------- train Loss: 1.3524 Acc: 0.5641 val Loss: 1.2875 Acc: 0.6283 Epoch 48/74 ---------- train Loss: 1.3539 Acc: 0.5537 val Loss: 1.2792 Acc: 0.6459 Epoch 49/74 ---------- train Loss: 1.3511 Acc: 0.5599 val Loss: 1.2785 Acc: 0.6347 Epoch 50/74 ---------- train Loss: 1.3547 Acc: 0.5581 val Loss: 1.2688 Acc: 0.6531 Epoch 51/74 ---------- train Loss: 1.3501 Acc: 0.5631 val Loss: 1.2795 Acc: 0.6387 Epoch 52/74 ---------- train Loss: 1.3427 Acc: 0.5769 val Loss: 1.2659 Acc: 0.6771 Epoch 53/74 ---------- train Loss: 1.3487 Acc: 0.5603 val Loss: 1.2739 Acc: 0.6443 Epoch 54/74 ---------- train Loss: 1.3536 Acc: 0.5573 val Loss: 1.2641 Acc: 0.6531 Epoch 55/74 ---------- train Loss: 1.3543 Acc: 0.5593 val Loss: 1.2768 Acc: 0.6299 Epoch 56/74 ---------- train Loss: 1.3586 Acc: 0.5519 val Loss: 1.2659 Acc: 0.6587 Epoch 57/74 ---------- train Loss: 1.3519 Acc: 0.5551 val Loss: 1.2731 Acc: 0.6603 Epoch 58/74 ---------- train Loss: 1.3562 Acc: 0.5567 val Loss: 1.2780 Acc: 0.6395 Epoch 59/74 ---------- train Loss: 1.3464 Acc: 0.5661 val Loss: 1.2735 Acc: 0.6507 Epoch 60/74 ---------- train Loss: 1.3445 Acc: 0.5751 val Loss: 1.2723 Acc: 0.6483 Epoch 61/74 ---------- train Loss: 1.3457 Acc: 0.5685 val Loss: 1.2667 Acc: 0.6499 Epoch 62/74 ---------- train Loss: 1.3555 Acc: 0.5527 val Loss: 1.2721 Acc: 0.6459 Epoch 63/74 ---------- train Loss: 1.3530 Acc: 0.5591 val Loss: 1.2659 Acc: 0.6619 Epoch 64/74 ---------- train Loss: 1.3517 Acc: 0.5629 val Loss: 1.2693 Acc: 0.6491 Epoch 65/74 ---------- train Loss: 1.3556 Acc: 0.5543 val Loss: 1.2690 Acc: 0.6547 Epoch 66/74 ---------- train Loss: 1.3551 Acc: 0.5617 val Loss: 1.2906 Acc: 0.6123 Epoch 67/74 ---------- train Loss: 1.3516 Acc: 0.5629 val Loss: 1.2731 Acc: 0.6555 Epoch 68/74 ---------- train Loss: 1.3538 Acc: 0.5589 val Loss: 1.2666 Acc: 0.6507 Epoch 69/74 ---------- train Loss: 1.3478 Acc: 0.5667 val Loss: 1.2782 Acc: 0.6427 Epoch 70/74 ---------- train Loss: 1.3489 Acc: 0.5617 val Loss: 1.2794 Acc: 0.6435 Epoch 71/74 ---------- train Loss: 1.3555 Acc: 0.5583 val Loss: 1.2702 Acc: 0.6571 Epoch 72/74 ---------- train Loss: 1.3521 Acc: 0.5573 val Loss: 1.2778 Acc: 0.6411 Epoch 73/74 ---------- train Loss: 1.3498 Acc: 0.5603
In [ ]:
 

Visualizing the model predictions

Generic function to display predictions for a few images

In [ ]:
class_names = train_dataset.dataset.classes
In [ ]:
class_names
In [ ]:
def imshow_validation(inp, title=None):
    """Imshow for Tensor."""
    inp = inp.numpy().transpose((1, 2, 0))
    mean = np.array([0.485, 0.456, 0.406])
    std = np.array([0.229, 0.224, 0.225])
    inp = std * inp + mean
    inp = np.clip(inp, 0, 1)
    plt.imshow(inp)
    if title is not None:
        plt.title(title)
    plt.pause(0.001)  # pause a bit so that plots are updated


# Get a batch of training data
inputs, classes = next(iter(dataloaders['train']))

# Make a grid from batch
out = torchvision.utils.make_grid(inputs)

imshow(out, title=[class_names[x] for x in classes])
In [ ]:
def visualize_model(model, num_images=6):
    was_training = model.training
    model.eval()
    images_so_far = 0
    fig = plt.figure()

    with torch.no_grad():
        for i, (inputs, labels) in enumerate(dataloaders['val']):
            inputs = inputs.to(device)
            labels = labels.to(device)

            outputs = model(inputs)
            _, preds = torch.max(outputs, 1)

            for j in range(inputs.size()[0]):
                fig = plt.figure(figsize = (10, 10))
                images_so_far += 1
                ax = plt.subplot(num_images//2, 2, images_so_far)
                ax.axis('off')
                ax.set_title('predicted: {}'.format(class_names[preds[j]]))
                imshow_validation(inputs.cpu().data[j])

                if images_so_far == num_images:
                    model.train(mode=was_training)
                    return
        model.train(mode=was_training)
    plt.show()
In [ ]:
visualize_model(model_ft)
In [ ]:
 

Loading Saved Model

The simplest thing to do is simply save the state dict with torch.save. For example, we can save it to a file 'checkpoint.pth'.

In [ ]:
state_dict = torch.load('saved.pth')
print(state_dict.keys())

Then we can load the state dict with torch.load.

Seems pretty straightforward, but as usual it's a bit more complicated. Loading the state dict works only if the model architecture is exactly the same as the checkpoint architecture. If I create a model with a different architecture, this fails.

In [ ]:
model = models.resnet18()
In [ ]:
for param in model.parameters():
    param.requires_grad = False
In [ ]:
model.fc = fc
In [ ]:
#now we modified the resent architecture, so we can load the weights

model.load_state_dict(state_dict)
model.eval()
In [ ]:
 

Making Predictions

In [ ]:
#create a iterator

dataiter = iter(test_loader)
images, labels = dataiter.next()

#shape of images bunch
print(images.shape)

#shape of first image in a group of 4
print(images[1].shape)

#class label for first image
print(labels[1])
In [ ]:
def make_predictions(dataloader, trained_model):
    
    j = 0
    pred = dict()
    trained_model.eval()

    for  data in dataloader:
        images, labels = data
        images = images.cuda()
        
        #push model to cuda
        trained_model.cuda()
        outputs = trained_model(images)

        for i in range(len(images)):
            #print(torch.argmax(outputs[i]))
            detect_class = torch.argmax(outputs[i]).item() + 1
            detect_image_name = test_dataset.samples[j][0].split("\\")[1]
            j = j + 1

            pred[detect_image_name] = detect_class   

    df = pd.DataFrame(list(pred.items()), columns=['image', 'category'])
    return(df)
In [ ]:
test_dataset.samples[0][0].split("\\")[1]
In [ ]:
df = make_predictions(test_loader, model)
In [ ]:
df.to_csv("submission.csv", index=False)
In [ ]:
df
In [ ]:
 
In [1]:
import jovian
In [ ]:
jovian.commit()
[jovian] Saving notebook..
[jovian] Creating a new notebook on https://jvn.io
[jovian] Error: The current API key is invalid or expired.
[jovian] Please enter your API key (from https://jvn.io ):
In [ ]:
 
In [ ]: