How to use map() on a DataLoader dataset? - python

I'm trying to train a pretrained visual transformer (ViT) on a new dataset.
The dataset is made up of jpg images sorted into folders (train, val, test) and has 4 calsses.
I want to use map() on the dataset for preprocessing.
I added 'getitem' and 'len' so that it'll be a map-style dataset.
But I still get the error:
AttributeError: 'DataLoader' object has no attribute 'map'
Here's the code:
from torch.utils.data import DataLoader
class MyDataset(Dataset):
def __init__(self, path, transform):
self.files = glob.glob(path)
print(type(self.files))
self.transform = transform
self.labels = [filepath.split('/')[-2] for filepath in self.files]
def __getitem__(self, item):
file = self.files[item]
label = self.labels[item]
file = Image.open(file)
file = self.transform(file)
return file, label
def __len__(self):
return len(self.files)
transform=transforms.Compose([transforms.ToTensor()])
train_data = MyDataset(train_path, transform)
val_data = MyDataset(val_path, transform)
test_data = MyDataset(test_path, transform)
train = DataLoader(train_data , batch_size=1, shuffle=True, num_workers=3)
val = DataLoader(val_data , batch_size=1, shuffle=True)
test = DataLoader(test_data , batch_size=1, shuffle=True)
feature_extractor = ViTFeatureExtractor.from_pretrained('google/vit-base-patch16-224-in21k')
data_collator = default_data_collator
def preprocess_images(examples):
images = examples['img']
images = [np.array(image, dtype=np.uint8) for image in images]
images = [np.moveaxis(image, source=-1, destination=0) for image in images]
inputs = feature_extractor(images=images)
examples['pixel_values'] = inputs['pixel_values']
return examples
features = Features({
'label': ClassLabel(
names=['class1', 'class2', 'class3', 'class4']),
'img': Array3D(dtype="int64", shape=(3, 32, 32)),
'pixel_values': Array3D(dtype="float32", shape=(3, 224, 224)),
})
preprocessed_train_ds = train.map(preprocess_images, batched=True, features=features)
preprocessed_val_ds = val.map(preprocess_images, batched=True, features=features)
preprocessed_test_ds = test.map(preprocess_images, batched=True, features=features)
What else can I do?

When you instantiate the DataLoader for train, test and val dataset, you can point the flag collate_fn=preprocess_images function. You will have to update the function to match to your requirement.
e.g.,
DataLoader(train_data, collate_fn=preprocess_images, , batch_size=1, shuffle=True, num_workers=3)
See here
To quote:
collate_fn (Callable, optional): merges a list of samples to form a
mini-batch of Tensor(s). Used when using batched loading from a
map-style dataset.

Related

How to get the filenames of misclassified images in the test data set - pytorch?

I am trying to use AlexNet to classify spectrogram images generated for 3s audio segments. I have successfully trained my dataset and am trying to identify which images the model misclassified.
I am able to obtain the filename of a image by calling iterator.dataset.data_df.adressfname, however I am unsure how to use this statement in the for loop in the get_predictions function. If I try to retrieve the filenames using iterator.dataset.data_df.adressfname[i], I get the following error: expected Tensor as element 0 in argument 0, but got str
Ultimately, I want to create a dataframe that contains the filename, actual label and predicted label. Does anyone have any suggestions?
class CustomDataset(Dataset):
def __init__(self, img_path, csv_file, transforms):
self.imgs_path = img_path
self.csv_train_file = csv_file
file_list = glob.glob(self.imgs_path + "*")
self.data = []
self.data_df = pd.read_csv(self.csv_train_file)
self.transforms = transforms
for ind in self.data_df.index:
img_path = self.data_df['spectrogramSegFilename'][ind]
class_name = self.data_df['dx'][ind]
self.data.append([img_path, class_name])
self.class_map = {"ProbableAD" : 0, "Control": 1}
self.img_dim = (256, 256)
def __len__(self):
return len(self.data)
def __getitem__(self, idx):
img_path, class_name = self.data[idx]
img = cv2.imread(img_path)
img = cv2.resize(img, self.img_dim)
class_id = self.class_map[class_name]
img_tensor = torch.from_numpy(img)
img_tensor = img_tensor.permute(2, 0, 1)
data = self.transforms(img_tensor)
class_id = torch.tensor([class_id])
return data, class_id
if __name__ == "__main__":
transformations = transforms.Compose([transforms.ToPILImage(), transforms.Resize(256), transforms.CenterCrop(256), transforms.ToTensor(), transforms.Normalize((0.49966475, 0.1840554, 0.34930056), (0.35317238, 0.17343724, 0.1894943))])
train_dataset = CustomDataset("/spectrogram_images/spectrogram_train/", "train_features_segmented.csv", transformations)
test_dataset = CustomDataset("/spectrogram_images/spectrogram_test/", "test_features_segmented.csv", transformations)
train_dataset = CustomDataset("spectrogram_images/spectrogram_train/", "/train_features_segmented.csv")
test_dataset = CustomDataset("spectrogram_images/spectrogram_test/", "/test_features_segmented.csv")
train_data_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_data_loader = DataLoader(test_dataset, batch_size=64, shuffle=True)
def get_predictions(model, iterator, device):
model.eval()
images = []
labels = []
probs = []
participant_ids = []
with torch.no_grad():
for i, (x, y) in enumerate(iterator):
x = x.to(device)
y_pred = model(x)
y_prob = F.softmax(y_pred, dim=-1)
participant_ids.append(iterator.dataset.data_df.adressfname[i])
images.append(x.cpu())
labels.append(y.cpu())
probs.append(y_prob.cpu())
images = torch.cat(images, dim=0)
labels = torch.cat(labels, dim=0)
probs = torch.cat(probs, dim=0)
participant_ids = torch.cat(participant_ids, dim=0)
return images, labels, probs, participant_ids
Just add a third field to the return signature of __getitem__():
def __getitem__(self,idx):
...
return data,class_id,img_path
Then, when you call the dataloader:
for i, (x,y,img_paths) in enumerate(iterator):
... call model ...
... compare outputs to labels ...
... identify incorrect batch indices
mislabeled_files = [img_paths[idx] for idx in incorrect_batch_indices]
And there you have it. Note that in your current code block, i indexes the batch index, which really has nothing to do with the dataset object wrapped in the dataloader (both because the dataloader has a batch size so it collates multiple elements from the dataset into a single index, and because the dataloader shuffles the elements in the dataset) so you should not use this index (i) to reference the dataset object. If you wanted to reference the underlying data items in the dataset object you could instead simply return the data idx in __getitem__():
def __getitem__(self,idx):
...
return data,class_id,idx
You can then use this idx to reference the original dataset data and get the file names that way.

How do I predict using a PyTorch model?

I created a pyTorch Model to classify images.
I saved it once via state_dict and the entire model like that:
torch.save(model.state_dict(), "model1_statedict")
torch.save(model, "model1_complete")
How can i use these models?
I'd like to check them with some images to see if they're good.
I am loading the model with:
model = torch.load(path_model)
model.eval()
This works alright, but i have no idea how to use it to predict on a new picture.
def predict(self, test_images):
self.eval()
# model is self(VGG class's object)
count = test_images.shape[0]
result_np = []
for idx in range(0, count):
# print(idx)
img = test_images[idx, :, :, :]
img = np.expand_dims(img, axis=0)
img = torch.Tensor(img).permute(0, 3, 1, 2).to(device)
# print(img.shape)
pred = self(img)
pred_np = pred.cpu().detach().numpy()
for elem in pred_np:
result_np.append(elem)
return result_np
network is VGG-19 and ref my source code.
like this architecture:
class VGG(object):
def __init__(self):
...
def train(self, train_images, valid_images):
train_dataset = torch.utils.data.Dataset(train_images)
valid_dataset = torch.utils.data.Dataset(valid_images)
trainloader = torch.utils.data.DataLoader(train_dataset)
validloader = torch.utils.data.DataLoader(valid_dataset)
self.optimizer = Adam(...)
self.criterion = CrossEntropyLoss(...)
for epoch in range(0, epochs):
...
self.evaluate(validloader, model=self, criterion=self.criterion)
...
def evaluate(self, dataloader, model, criterion):
model.eval()
for i, sample in enumerate(dataloader):
...
def predict(self, test_images):
...
if __name__ == "__main__":
network = VGG()
trainset, validset = get_dataset() # abstract function for showing
testset = get_test_dataset()
network.train(trainset, validset)
result = network.predict(testset)
A pytorch model is a function. You provide it with appropriately defined input, and it returns an output. If you just want to visually inspect the output given a specific input image, simply call it:
model.eval()
output = model(example_image)

Mapping images and their labels

I have images in a folder(train) and csv file(train.csv) containing image names and labels.
how to map images in one folder and labels in another csv file
how can i create a data frame with image data and labels.
multiclass classification
import tensorflow as tf
from tensorflow import keras
import pandas as pd
class MyTrainingData(keras.utils.Sequence):
def __init__(self, file, labels, batchSize):
self.file = file
self.label = labels
self.batchSize = batchSize
self.n_bathces = int(len(self.file) / self.batchSize)
def on_epoch_end(self): # it is called after every epoch
self.file, self.label = shuffle(self.file, self.label)
for i in range(50):
print(self.file[i], self.label[i], 'file-label')
def __len__(self):
return self.n_bathces
# called after every batch to get new batch or new 32 images and labels
def __getitem__(self, idx):
# this method calls by fit method with idx ranging from 0 to len(training_exmaples) / batch_size =
batchX = self.file[idx*self.batchSize: (idx+1)*self.batchSize]
batchY = self.label[idx*self.batchSize: (idx+1)*self.batchSize]
imgFiles = [image.load_img(name, target_size=(224, 224, 3)) for name in batchX] #loading 32 images
imgFiles = [image.img_to_array(img) for img in imgFiles] #preprocessing
imgFiles = [img / 255 for img in imgFiles] batchY = to_categorical(batchY, 4) # 4 represent number of classes (4 in that case)
return np.array(imgFiles), np.array(batchY)
def getfilePath(filenames):
path = './train/' # or any other path according to directory structure
filePaths = []
for name in filenames:
filePaths.append(path + name) # './train/' + 'img1.jpg' = './train/img1.jpg
return filePaths
df = pd.read_csv()
img_names = df['img_names']
labels = df['labels']
img_names = ['img1.jpg', 'img2.jpg', -----]
img_names = getfilePath(img_names)
img_names = ['./train/img1.jpg', './train/img2.jpg', -----]
label = [3, 1, 2, 0, ----]
batch_size = 32
data = MyTrainingData(fileNames, labels, batchSize
model = defineModel()
sgd = SGD(learning_rate=0.0001, momentum=0.9, nesterov=True)
model.compile(optimizer=sgd, loss='binary_crossentropy', metrics=['accuracy'])
model.fit(data, epochs=10, verbose=1)
The above code does much more than mapping. In case of large dataset(to big to fit in RAM). this techniwue will help you to load, preprocess and generate input data dynamically. Hope you find it usefull. Give feedback so that i can further improve it.

convery my own datasets to Cifar10 format (X_train, y_train),(X_test, y_test)

I have a data set composed of two folders each folder contains images, and I want convert my data set into Cifar10 datasets to use it on code which i found it in Github ,
like this :
(X_train, y_train), (X_test, y_test), label_names = load_cifar10
please help !!
class ImageLoader:
"""Load images in arrays without batches."""
def __init__(self, train_dir, test_dir):
"""Create class."""
self.train_dir = train_dir
self.test_dir = test_dir
def load_data(self):
"""Load the data."""
features, labels = [], []
for source in [self.train_dir, self.test_dir]:
input, output = [], []
for class_name in os.listdir(source):
if os.path.isdir(class_name):
for img_name in os.listdir(class_name):
img = cv2.imread(os.path.join(self.train_dir, class_name, img_name))
# ...
# Modify your image array here.
# ...
input.append(img)
output.append(class_name) # or other method to convert label
# Shuffle labels.
combine = list(zip(input, output)) # zip as list for Python 3
np.random.shuffle(combine)
input, output = zip(*combine)
features.append(input)
labels.append(output)
return [[np.array(features[0], dtype=np.float32),
np.array(labels[0], dtype=np.float32)],
[np.array(features[1], dtype=np.float32),
np.array(labels[1], dtype=np.float32)]]
cifar10 = ImageLoader('path-to-training', 'path-to-testing')
(trainX, trainY), (testX, testY) = cifar10.load_data()

How make customised dataset in Pytorch for images and their masks?

I have two dataset folder of tif images, one is a folder called BMMCdata, and the other one is the mask of BMMCdata images called BMMCmasks(the name of images are corresponds). I am trying to make a customised dataset and also split the data randomly to train and test. at the moment I am getting an error
self.filenames.append(fn)
AttributeError: 'CustomDataset' object has no attribute 'filenames'
Any comment will be appreciated a lot.
import torch
from torch.utils.data.dataset import Dataset # For custom data-sets
from torchvision import transforms
from PIL import Image
import os.path as osp
import glob
folder_data = "/Users/parto/PycharmProjects/U-net/BMMCdata/data"
class CustomDataset(Dataset):
def __init__(self, root):
self.filename = folder_data
self.root = root
self.to_tensor = transforms.ToTensor()
filenames = glob.glob(osp.join(folder_data, '*.tif'))
for fn in filenames:
self.filenames.append(fn)
self.len = len(self.filenames)
print(fn)
def __getitem__(self, index):
image = Image.open(self.filenames[index])
return self.transform(image)
def __len__(self):
return self.len
custom_img = CustomDataset(folder_data)
# total images in set
print(custom_img.len)
train_len = int(0.6*custom_img.len)
test_len = custom_img.len - train_len
train_set, test_set = CustomDataset.random_split(custom_img, lengths=[train_len, test_len])
# check lens of subset
len(train_set), len(test_set)
train_set = CustomDataset(folder_data)
train_set = torch.utils.data.TensorDataset(train_set, train=True, batch_size=4)
train_loader = torch.utils.data.DataLoader(train_set, batch_size=4, shuffle=True, num_workers=1)
print(train_set)
print(train_loader)
test_set = torch.utils.data.DataLoader(Dataset, batch_size=4, sampler= train_sampler)
test_loader = torch.utils.data.DataLoader(Dataset, batch_size=4)
answer given by #ptrblck in pytorch community. thank you
# get all the image and mask path and number of images
folder_data = glob.glob("D:\\Neda\\Pytorch\\U-net\\BMMCdata\\data\\*.tif")
folder_mask = glob.glob("D:\\Neda\\Pytorch\\U-net\\BMMCmasks\\masks\\*.tif")
# split these path using a certain percentage
len_data = len(folder_data)
print(len_data)
train_size = 0.6
train_image_paths = folder_data[:int(len_data*train_size)]
test_image_paths = folder_data[int(len_data*train_size):]
train_mask_paths = folder_mask[:int(len_data*train_size)]
test_mask_paths = folder_mask[int(len_data*train_size):]
class CustomDataset(Dataset):
def __init__(self, image_paths, target_paths, train=True): # initial logic
happens like transform
self.image_paths = image_paths
self.target_paths = target_paths
self.transforms = transforms.ToTensor()
def __getitem__(self, index):
image = Image.open(self.image_paths[index])
mask = Image.open(self.target_paths[index])
t_image = self.transforms(image)
return t_image, mask
def __len__(self): # return count of sample we have
return len(self.image_paths)
train_dataset = CustomDataset(train_image_paths, train_mask_paths, train=True)
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=4, shuffle=True, num_workers=1)
test_dataset = CustomDataset(test_image_paths, test_mask_paths, train=False)
test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=4, shuffle=False, num_workers=1)

Categories