PyTorch: Add validation error in training

17,663

Here is an example how to split your dataset for training and validation, then switch between the two phases every epoch:

import numpy as np
import torch
from torchvision import datasets
from torch.autograd import Variable
from torch.utils.data.sampler import SubsetRandomSampler

# Examples:
my_dataset = datasets.MNIST(root="/home/benjamin/datasets/mnist", train=True, download=True)
validation_split = 0.1

dataset_len = len(my_dataset)
indices = list(range(dataset_len))

# Randomly splitting indices:
val_len = int(np.floor(validation_split * dataset_len))
validation_idx = np.random.choice(indices, size=val_len, replace=False)
train_idx = list(set(indices) - set(validation_idx))

# Contiguous split
# train_idx, validation_idx = indices[split:], indices[:split]

## Defining the samplers for each phase based on the random indices:
train_sampler = SubsetRandomSampler(train_idx)
validation_sampler = SubsetRandomSampler(validation_idx)

train_loader = torch.utils.data.DataLoader(my_dataset, sampler=train_sampler)
validation_loader = torch.utils.data.DataLoader(my_dataset, sampler=validation_sampler)
data_loaders = {"train": train_loader, "val": validation_loader}
data_lengths = {"train": len(train_idx), "val": val_len}

# Training with Validation (your code + code from Pytorch tutorial: https://pytorch.org/tutorials/beginner/transfer_learning_tutorial.html)
n_epochs = 40
net = ...

for epoch in range(n_epochs):
    print('Epoch {}/{}'.format(epoch, n_epochs - 1))
    print('-' * 10)

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

        running_loss = 0.0

        # Iterate over data.
        for data in data_loaders[phase]:

            # get the input images and their corresponding labels
            images = data['image']
            key_pts = data['keypoints']

            # flatten pts
            key_pts = key_pts.view(key_pts.size(0), -1)

            # wrap them in a torch Variable
            images, key_pts = Variable(images), Variable(key_pts)

            # convert variables to floats for regression loss
            key_pts = key_pts.type(torch.FloatTensor)
            images = images.type(torch.FloatTensor)

            # forward pass to get outputs
            output_pts = net(images)

            # calculate the loss between predicted and target keypoints
            loss = criterion(output_pts, key_pts)

            # zero the parameter (weight) gradients
            optimizer.zero_grad()

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

            # print loss statistics
            running_loss += loss.data[0]

        epoch_loss = running_loss / data_lengths[phase]
        print('{} Loss: {:.4f}'.format(phase, epoch_loss))
Share:
17,663
Edamame
Author by

Edamame

Updated on June 05, 2022

Comments

  • Edamame
    Edamame about 2 years

    I am using PyTorch to train a cnn model. Here is my Network architecture:

    import torch
    from torch.autograd import Variable
    import torch.nn as nn
    import torch.nn.functional as F
    import torch.nn.init as I
    
    
        class Net(nn.Module):
    
            def __init__(self):
                super(Net, self).__init__()
    
                self.conv1 = nn.Conv2d(1, 32, 5)
                self.pool = nn.MaxPool2d(2,2)
                self.conv1_bn = nn.BatchNorm2d(32)
                self.conv2 = nn.Conv2d(32, 64, 5)
                self.conv2_drop = nn.Dropout2d()
                self.conv2_bn = nn.BatchNorm2d(64)
                self.fc1 = torch.nn.Linear(53*53*64, 256)
                self.fc2 = nn.Linear(256, 136)
    
    
            def forward(self, x):
    
                x = F.relu(self.conv1_bn(self.pool(self.conv1(x))))
                x = F.relu(self.conv2_bn(self.pool(self.conv2_drop(self.conv2(x)))))
                x = x.view(-1, 53*53*64)
                x = F.relu(self.fc1(x))
                x = F.dropout(x, training=self.training)
                x = self.fc2(x)
    
                return x
    

    Then I train the model like below:

    # prepare the net for training
        net.train()
    
        for epoch in range(n_epochs):  # loop over the dataset multiple times
    
            running_loss = 0.0
    
            # train on batches of data, assumes you already have train_loader
            for batch_i, data in enumerate(train_loader):
                # get the input images and their corresponding labels
                images = data['image']
                key_pts = data['keypoints']
    
                # flatten pts
                key_pts = key_pts.view(key_pts.size(0), -1)
    
                # wrap them in a torch Variable
                images, key_pts = Variable(images), Variable(key_pts)
    
                # convert variables to floats for regression loss
                key_pts = key_pts.type(torch.FloatTensor)
                images = images.type(torch.FloatTensor)
    
                # forward pass to get outputs
                output_pts = net(images)
    
                # calculate the loss between predicted and target keypoints
                loss = criterion(output_pts, key_pts)
    
                # zero the parameter (weight) gradients
                optimizer.zero_grad()
    
                # backward pass to calculate the weight gradients
                loss.backward()
    
                # update the weights
                optimizer.step()
    
                # print loss statistics
                running_loss += loss.data[0]
    

    I am wondering if it is possible to add the validation error in the training? I mean something like this (validation split) in Keras:

    myModel.fit(trainX, trainY, epochs=50, batch_size=1, verbose=2, validation_split = 0.1)
    
  • Shamoon
    Shamoon over 4 years
    Does it make sense to set the optimizer in each epoch as opposd to once before?