I am attempting transfer learning with a CNN (vgg19) on the Oxford102 category dataset consisting of 8189 samples of flowers labeled from 1 through 102. Instead of loading the data with ImageFolder, which requires a tedious process of structuring my data into train, valid and test folders with each class being a sub-folder holding my images, I decided to load it in using the Custom Dataset class following This tutorial
My directory paths set up for the image, the labels (from 1 through 102), and class label map which
maps the label to flower name
data_dir_path = 'data/images/'
labels_path = 'data/imagelabels.mat'
class_label_path = 'data/class_label_map'y
my data is not in the ImageFolder, and as far as I know If writing a custom dataset, you don’t need your data to be in that format.
data/images/image_00001.jpg
data/images/image_00002.jpg
data/images/image_00003.jpg
data/images/image_00004.jpg
.
.
.
data/images/image_08189.jpg
my labels list which is in data/imagelabels.mat is as follows,
[77 77 77 … 62 62 62]
The transforms I am using for train, test and valid are
data_transforms = {
'train': transforms.Compose([
transforms.RandomRotation(45),
transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406],
[0.229, 0.224, 0.225])
]),
'valid': transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406],
[0.229, 0.224, 0.225])
]),
'test': transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406],
[0.229, 0.224, 0.225])
]),
}
Here is my code to load in data, it works as I can, index into my images and labels.
class MyDataset(Dataset):
def __init__(self, image_labels, data_dir, transform=None):
"""
:param image_labels_path: path to our labels
:param root_dir: the directory which houses our images
:param transform: apply any transform on our sample
"""
self.image_labels = image_labels
self.root_dir = data_dir
self.transform = transform
def __len__(self):
label_dict = scipy.io.loadmat(self.image_labels)
return len(label_dict['labels'][0]) # [0] cause its a list of lists
def __getitem__(self, idx):
image_path_list = [os.path.join(self.root_dir, filename) for filename in os.listdir(self.root_dir)]
image = Image.open(image_path_list[idx])
label_dict = scipy.io.loadmat(self.image_labels)
label_list = label_dict['labels'][0]
# labels for pytorch should start from zero
label_list[:] = [i - 1 for i in label_list]
label = label_list[idx]
if self.transform:
image = self.transform(image)
return image, label
Here I am creating my trainset, validset and testset
image_datasets = {x: MyDataset(image_labels=labels_path, data_dir=data_dir_path, transform=data_transforms[x]) for x in
['train', 'valid', 'test']}
I subtracted 1 from my label list since Pytorch expects labels to start from 0. Thus 0 through 101 for 102 labels.
Correct me if I am wrong cause I get a “current target >=0 and current target <= n_classes failed” error if I don’t subtract one. Since this fails at 102, I thus surmised that class representation starts from 0.
I have a class_label_map is a dict which maps class labels to flowers names
{ 1": “pink primrose”,
“2”: “hard-leaved pocket orchid”,
“3”: “canterbury bells”,
“4”: “sweet pea”,
“5”: “english marigold”,
“6”: "tiger lily }
My big problem is to get a class_to_idx mapping, how do I do this, my flower names do not match the images if I visualize them, I get totally different flower names for my flowers.
I first created a mapping by having a dict with key my original label before I subtracted 1, and value the one after. Example:
class_to_idx = {77:76, 73:72, 1:0, 65:64......102:101...65:54}
This is beyond doubt wrong as I was getting totally wrong label for my images.
The first label of my image in my data_dir_path = ‘data/images’ is 77, upon subtracting one I get 76. Would this mean the index for all labels 76 is 0, and if the next class is 72 , would that mean the index for all classes 72 is 1? So…
class_to_idx = {76:0, 72:1, 0:2, 65:3....and so on}
But this again works if the class number does not repeat after a certain classes, i.e. if 72 were to repeat
after class 65, then 72 is no more the 2nd class. The code for how ImageFolder find class_to_index is…
classes = [d for d in os.listdir(dir) if os.path.isdir(os.path.join(dir, d))]
classes.sort()
class_to_idx = {classes[i]: i for i in range(len(classes))}
I don’t properly understand this. Performing it on my label_list give me the largest index of the a certain
class {1: 39, 2: 99, 3: 139, 4: 195, 5: 260, 6: 305, 7: 345, 8: 430, 9: 476}.
The ImageFolder seems to have a class_to_idx attribute which if used on my Dataset throws an error,
image_datasets['train'].class_to_idx
AttributeError: ‘MyDataset’ object has no attribute ‘class_to_idx’
This is obviously the case because my Dataset class does not contain any such attribute. If I am reasoing is correct, Pytorch gives out a tuple of probabilities and corresponding index when we do probs.max(dim=1). This index is the value in our class_to_index map. The key is the also the key in our
label_map with, the value of which helps us trace the flower name of the our prediction.
So How do I map my classes to my index in this scenario? It isn’t structured like ImageFolder.
This is super important as I need to checkpoint my model and load it back again to fire out predictions.It may sound really silly, but I really don’t know what to do here, please please please help?