Deep Learning Models -- A collection of various deep learning architectures, models, and tips for TensorFlow and PyTorch in Jupyter Notebooks.
%load_ext watermark
%watermark -a 'Sebastian Raschka' -v -p torch
Sebastian Raschka CPython 3.6.8 IPython 7.2.0 torch 1.1.0
This notebook provides an example for how to prepare a custom dataset for PyTorch's data loading utilities. More in-depth information can be found in the official documentation at:
In this example, we are using the Asian Face Dataset (AFAD), which is a face image dataset with age labels [1]. There are two versions of this dataset, a smaller Lite version and the full version, which are available at
Here, we will be working with the Lite dataset, but the same code can be used for the full dataset as well -- the Lite dataset is just slightly smaller than the full dataset and thus faster to process.
[1] Niu, Z., Zhou, M., Wang, L., Gao, X., & Hua, G. (2016). Ordinal regression with multiple output cnn for age estimation. In Proceedings of the IEEE conference on computer vision and pattern recognition (pp. 4920-4928).
import time
import os
import pandas as pd
import numpy as np
from PIL import Image
from torchvision import datasets
from torchvision import transforms
from torch.utils.data import DataLoader
from torch.utils.data import SubsetRandomSampler
from torch.utils.data import Dataset
import torch.nn.functional as F
import torch
if torch.cuda.is_available():
torch.backends.cudnn.deterministic = True
The following lines of code (bash commands) will download, unzip, and untar the dataset from GitHub.
# Download
!git clone https://github.com/afad-dataset/tarball-lite.git
Cloning into 'tarball-lite'... remote: Enumerating objects: 37, done. remote: Total 37 (delta 0), reused 0 (delta 0), pack-reused 37 Unpacking objects: 100% (37/37), done. Checking out files: 100% (30/30), done.
# Join individual tars
!cat tarball-lite/AFAD-Lite.tar.xz* > tarball-lite/AFAD-Lite.tar.xz
# "Unzip"
!tar xf tarball-lite/AFAD-Lite.tar.xz
# Get image paths
rootDir = 'AFAD-Lite'
files = [os.path.relpath(os.path.join(dirpath, file), rootDir)
for (dirpath, dirnames, filenames) in os.walk(rootDir)
for file in filenames if file.endswith('.jpg')]
print(f'Number of images in total: {len(files)}')
Number of images in total: 59344
d = {}
d['age'] = []
d['gender'] = []
d['file'] = []
d['path'] = []
for f in files:
age, gender, fname = f.split('/')
if gender == '111':
gender = 'male'
else:
gender = 'female'
d['age'].append(age)
d['gender'].append(gender)
d['file'].append(fname)
d['path'].append(f)
df = pd.DataFrame.from_dict(d)
df.head()
age | gender | file | path | |
---|---|---|---|---|
0 | 39 | female | 474596-0.jpg | 39/112/474596-0.jpg |
1 | 39 | female | 397477-0.jpg | 39/112/397477-0.jpg |
2 | 39 | female | 576466-0.jpg | 39/112/576466-0.jpg |
3 | 39 | female | 399405-0.jpg | 39/112/399405-0.jpg |
4 | 39 | female | 410524-0.jpg | 39/112/410524-0.jpg |
Normalize labels such that they start with 0
:
df['age'].min()
'18'
df['age'] = df['age'].values.astype(int) - int(df['age'].min())
Seperate dataset into training and test subsets:
np.random.seed(123)
msk = np.random.rand(len(df)) < 0.8
df_train = df[msk]
df_test = df[~msk]
Save data partitioning as CSV:
df_train.to_csv('training_set_lite.csv', index=False)
df_test.to_csv('test_set_lite.csv', index=False)
num_ages = np.unique(df['age'].values).shape[0]
print(f'Number of age labels: {num_ages}')
Number of age labels: 22
print(f'Number of training examples: {df_train.shape[0]}')
print(f'Number of test examples: {df_test.shape[0]}')
Number of training examples: 47524 Number of test examples: 11820
class AFADDatasetAge(Dataset):
"""Custom Dataset for loading AFAD face images"""
def __init__(self, csv_path, img_dir, transform=None):
df = pd.read_csv(csv_path)
self.img_dir = img_dir
self.csv_path = csv_path
self.df = df
self.y = df['age'].values
self.transform = transform
def __getitem__(self, index):
img = Image.open(os.path.join(self.img_dir,
self.df.iloc[index]['path']))
if self.transform is not None:
img = self.transform(img)
label = self.y[index]
return img, label
def __len__(self):
return self.y.shape[0]
TRAIN_CSV_PATH = 'training_set_lite.csv'
TEST_CSV_PATH = 'test_set_lite.csv'
IMAGE_PATH = 'AFAD-Lite'
BATCH_SIZE = 128
test_transform = transforms.Compose([transforms.Resize((128, 128)),
transforms.CenterCrop((120, 120)),
transforms.ToTensor()])
test_dataset = AFADDatasetAge(csv_path=TEST_CSV_PATH,
img_dir=IMAGE_PATH,
transform=test_transform)
test_loader = DataLoader(dataset=test_dataset,
batch_size=BATCH_SIZE,
num_workers=4,
shuffle=False)
# Checking the dataset
for images, labels in test_loader:
print('Image batch dimensions:', images.shape)
print('Image label dimensions:', labels.shape)
break
Image batch dimensions: torch.Size([128, 3, 120, 120]) Image label dimensions: torch.Size([128])
train_indices = torch.arange(0, 46000).numpy()
valid_indices = torch.arange(46000, 47524).numpy()
train_sampler = SubsetRandomSampler(train_indices)
valid_sampler = SubsetRandomSampler(valid_indices)
train_transform = transforms.Compose([transforms.Resize((128, 128)),
transforms.RandomCrop((120, 120)),
transforms.ToTensor()])
test_transform = transforms.Compose([transforms.Resize((128, 128)),
transforms.CenterCrop((120, 120)),
transforms.ToTensor()])
train_dataset = AFADDatasetAge(csv_path=TRAIN_CSV_PATH,
img_dir=IMAGE_PATH,
transform=train_transform)
valid_dataset = AFADDatasetAge(csv_path=TRAIN_CSV_PATH,
img_dir=IMAGE_PATH,
transform=test_transform)
test_dataset = AFADDatasetAge(csv_path=TEST_CSV_PATH,
img_dir=IMAGE_PATH,
transform=test_transform)
train_loader = DataLoader(train_dataset,
batch_size=BATCH_SIZE,
num_workers=4,
sampler=train_sampler)
valid_loader = DataLoader(valid_dataset,
batch_size=BATCH_SIZE,
num_workers=4,
sampler=valid_sampler)
test_loader = DataLoader(dataset=test_dataset,
batch_size=BATCH_SIZE,
num_workers=4,
shuffle=False)
# Checking the dataset
for images, labels in test_loader:
print('Image batch dimensions:', images.shape)
print('Image label dimensions:', labels.shape)
break
for images, labels in valid_loader:
print('Image batch dimensions:', images.shape)
print('Image label dimensions:', labels.shape)
break
for images, labels in train_loader:
print('Image batch dimensions:', images.shape)
print('Image label dimensions:', labels.shape)
break
Image batch dimensions: torch.Size([128, 3, 120, 120]) Image label dimensions: torch.Size([128]) Image batch dimensions: torch.Size([128, 3, 120, 120]) Image label dimensions: torch.Size([128]) Image batch dimensions: torch.Size([128, 3, 120, 120]) Image label dimensions: torch.Size([128])
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
torch.manual_seed(0)
num_epochs = 2
for epoch in range(num_epochs):
for batch_idx, (x, y) in enumerate(train_loader):
print('Epoch:', epoch+1, end='')
print(' | Batch index:', batch_idx, end='')
print(' | Batch size:', y.size()[0])
x = x.to(device)
y = y.to(device)
break
Epoch: 1 | Batch index: 0 | Batch size: 128 Epoch: 2 | Batch index: 0 | Batch size: 128