AttributeError: 'str' object has no attribute 'cuda' - python

I am trying to move my data to GPU by doing this
batch["img"] = [img.cuda() for img in batch["img"]]
batch["label"] = [label.cuda() for label in batch["label"]]
However, i get this error for the labels for OCR
AttributeError: 'str' object has no attribute 'cuda'
I also tried .to('cuda') and similar error.
More details are as follows. This is the pytorch Dataset class
class SynthDataset(Dataset):
def __init__(self, opt):
super(SynthDataset, self).__init__()
self.path = os.path.join(opt['path'], opt['imgdir'])
self.images = os.listdir(self.path)
self.nSamples = len(self.images)
f = lambda x: os.path.join(self.path, x)
self.imagepaths = list(map(f, self.images))
transform_list = [transforms.Grayscale(1),
transforms.ToTensor(),
transforms.Normalize((0.5,), (0.5,))]
self.transform = transforms.Compose(transform_list)
self.collate_fn = SynthCollator()
def __len__(self):
return self.nSamples
def __getitem__(self, index):
assert index <= len(self), 'index range error'
imagepath = self.imagepaths[index]
imagefile = os.path.basename(imagepath)
img = Image.open(imagepath)
if self.transform is not None:
img = self.transform(img)
item = {'img': img, 'idx':index}
item['label'] = imagefile.split('_')[0]
return item
As you can see the generator outputs a dictionary with image and labels where the labels is the text contained in the image

Yo !
If I get your code right, your are building your custom dataset to output an image and its label. Your label comes from the first part of your image filename, so it is a string, as stated in the error message. Your object needs to be a Torch tensor to be moved on the GPU with .cuda().
If you want to keep your code as is, you need to transform your label into a vector form. I suspect your labels to be stuff like "cat", "dog", etc The usual way to transform labels into numerical forms are one-hot-encoding, or simply map each label to an integer. There is abundant resources about it on the web. Then you can turn your label into a Tensor object and move it to the GPU.
However, I would highly recommand to change the way you build your tensor dataset. You are calling .cuda() on every image tensor in your list. When forwarding a neural network, you batch your data into a tensor (for instance stacking your images into a new dimension), then call .cuda() on the whole batch tensor. Same thing for the labels. Check the Pytorch documentation as there is probably the exact example you are looking for (a custom dataset with an image and its label).
Good luck !

Related

How replace exif images in dataset from pytorch torchvision? I don`t understand how to do the item assignment on the last line

I have a function that removes exif from images, which was giving me issues in the training of a neural network. I am trying to apply the function to replace images in dataset from torchvision, but I don't understand how to do an item assignment. The error message is from the last line of code
root = "http://groups.csail.mit.edu/vision/SUN/"
SUN = torchvision.datasets.SUN397(root = root, transform = None, target_transform = None, download = True)
def modify_image(image):
if not image.getexif():
return
data = list(image.getdata())
image_modified = Image.new(image.mode, image.size)
image_modified.putdata(data)
return image_modified
for i in range(len(SUN):
image, label = SUN.__getitem__(i)
modified_image = modify_image(image)
#assign new value to dataset
if modified_image:
SUN[i] = (modified_image, label) ## gives error
TypeError: 'SUN397' object does not support item assignment

Correct way to run tensorflow function or any function using map on tf dataset

Below is the data augmentation function that I created.
import tensorFlow as tf
import tensorflow_addons as tfa
def augment_data(ds):
seed = tf.random.Generator.from_seed(1).normal([])
seed_2d = (1, 2)
# flipped images
ds_flipped = ds.map(lambda img, lbl: (tf.image.flip_left_right(img), lbl))
# induce random brightness
ds_rnb = ds.map(lambda img, lbl :
(tf.image.stateless_random_brightness(img,
max_delta=0.65,
seed=seed_2d),
lbl))
print('ds_flipped, ds_rnb ran successfully')
# centre crop
ds_cc = ds.map(lambda img, lbl:
(tf.image.central_crop(img,
central_fraction=0.8),
lbl))
ds_ran_zoom = ds.map(lambda img, lbl:
(tf.keras.preprocessing.image.random_zoom(img,
zoom_range=(.30, .70)),
lbl))
return ds_flipped, ds_rnb, ds_cc, ds_ran_zoom
The functions for flipped images and random brightness are working fine but tf.image.central_crop and tf.keras.preprocessing.image.random_zoom are not working.
Calling augment_data(ds) gives the following error
Running tf.image.central_crop giving me an error:
ValueError: image should either be a Tensor with rank = 3 or
rank = 4. Had rank = None.
Running tf.keras.preprocessing.image.random_zoom giving me an error
in transform_matrix_offset_center *
o_x = float(x) / 2 + 0.5
TypeError: float() argument must be a string or a number, not 'NoneType'
But if I run the central_crop function without using the map then the below code works fine
for image, label in train_data:
_ = tf.image.central_crop(image, central_fraction=0.8)
print('tf.image.central_crop ran successfully')
outputs
tf.keras.preprocessing.image.random_zoom ran successfully
If we run tf.keras.preprocessing.image.random.zoom in the same way then we get the error
for image, label in train_data:
_ = tf.keras.preprocessing.image.random_zoom(image, zoom_range=(.30, .70))
RuntimeError: affine matrix has wrong number of rows
Where in order to run tf.keras.preprocessing.image.random.zoom requires un-batching of the dataset. So the below code works fine
for image, label in train_data.unbatch().take(1):
_ = tf.keras.preprocessing.image.random_zoom(image, zoom_range=(.30, .70))
print('tf.keras.preprocessing.image.random_zoom ran successfully')
I have created a google colab notebook to replicate the issue.
What is the best way to run the TensorFlow function using the map function on the tf dataset?
What is the way to know whether any function is able to run on tf dataset using map function?
How to create a function that runs on batched and un-batched dataset both?
As you see above most of the functions are able to run on a single image but when it comes to running them using a map, different functions are throwing different errors.
The problem is that the shape of the images and labels is unknown. You should use set_shape at the end of the read_tfrecord function : decoded_image.set_shape(img_x, img_y, channels) and also for the label.
If you set the image and label shape in the dataset, most Tensorflow functions will work by applying map, both batched and unbatched.
tf.keras.preprocessing.image.random_zoom has a problem because it only takes a 3D Tensorflow tensor as an input and outputs a numpy array. This particular transformation is problematic.

How can I save the path of an image (string) from my dataloader (PyTorch)?

I've created a dataloader for my object detection task.
However, I cannot place the image/path name to a tensor. Instead I have it indexed, where in the last portion of the dataloader class, I have this:
target = {}
target['boxes'] = boxes
target['labels'] = labels
target['image_id'] = torch.tensor([index])
target['area'] = area
target['iscrowd'] = iscrowd
target['image_name'] = torch.tensor(index)
return image, target
where atm image_id and image_name are the same thing.
When I print out the image_name from the dataloader, I of course get this:
for image, target in valid_data_loader:
print(target[0]['image_name'])
Output:
tensor(0)
tensor(1)
tensor(2)
tensor(3)
tensor(4)
tensor(5)
tensor(6)
tensor(7)
I'm aware that strings can't be saved into torch tensors, so is there any way I can refer back to the original image name rather than the index of the tensor? Or would I just have to use the number that comes out and refer back to the dataset class (not dataloader)?
I ultimately want to save the image name, and attributes such as bounding box info to a separate numpy dataframe.
Ok, so this is a bit ad-hoc and not exactly what I was thinking but here is one method I have used to retrieve the paths/image names. I basically find the id from the dataloader by removing it from the tensor. I then use the tensor_id to find the corresponding id in the original dataframe:
for image, target in valid_data_loader:
tensor_id = target[0]['image_name'].item()
print(valid_df.iloc[tensor_id]['image_id'])
I don't know if this is efficient though but it got what I wanted...

How to convert string labels to int labels when using `tf.data.Dataset.list_files`

I am new to machine learning and have tried many different ways to create an image data pipeline. I went through Udacity and Google Tutorials and they all work great, so long as you get your image data from TF Hub or some other online source. Unfortunately, I haven't found ANY end-to-end examples (that work) of how to do this correctly when you have your own locally stored images. This is as close as I have gotten.
I have my own image data and am trying to get it in the correct Tensor format to fit my model (I'm using a feature extractor from TensorFlow Hub). I froze all the layers, added a binary output layer.
I get the following error when I run a training session with train_batches model.fit
UnimplementedError: Cast string to float is not supported
I'm pretty sure it has to do with the class labels being strings (pulled from folder name). Is there anyway I can pass a dictionary with values 0 and 1 to relabel?
Am I missing something much more crucial than this?
image_path_list = glob('/content/sample_data/tng (1)/*/*.jpg')
data = tf.data.Dataset.list_files(image_path_list)
print(image_path_list)
print()
print(data)
def load_images(path):
image = tf.io.read_file(path)
image = tf.io.decode_jpeg(image, channels=3)
label = tf.strings.split(path, os.path.sep)[-2]
return image, label
data = data.map(load_images)
def preprocess(image, label):
image = tf.image.resize(image, (IMG_RES, IMG_RES))
image /= 255.
return image, label
BATCH_SIZE = 1
train_batches = data.shuffle(len(image_path_list)).map(preprocess).batch(BATCH_SIZE).prefetch(1)
Ok, looks like I found a fix that seems to work but it requires me to relabel the folders by number. Since I have only two classes, I wrote a simple script to relabel the folders by number 0 and 1 respectively.
Once that was done, I added the following line after the label is defined:
label = tf.strings.to_number(label)
This takes my numeric labels from str to int type and everything seems to work fine. Would love to hear if there is a completely different approach to take since I couldn't find any clue on Stackoverflow or in ANY of the TF documentation.
Say I know in advance the classes, I simply used if-else to return integer-code of classes. This method should work for TF>=2.0
def parse_label(label):
if label == 'a':
return 0
elif label == 'b':
return 1
else:
return 2
def load_images(path):
image = tf.io.read_file(path)
image = tf.io.decode_jpeg(image, channels=3)
label = tf.strings.split(path, os.path.sep)[-2]
label = parse_label(label)
return image, label
data = data.map(load_images)
def preprocess(image, label):
image = tf.image.resize(image, (IMG_RES, IMG_RES))
image /= 255.
return image, label
BATCH_SIZE = 1
train_batches = data.shuffle(len(image_path_list)).map(preprocess).batch(BATCH_SIZE).prefetch(1)

Tensorflow Create Dataset from csv and mapping

I try to create a Dataset for Tensorflow from a CSV file that I created with pandas.
The csv file looks like this:
feature1 feature2 filepath label
0.25 0.35 test1.jpg A
0.33 0.15 test2.jpg B
I read the dataframe like this
mydf = pd.read_csv("TraingDatafinal.csv",header=0)
Now I have defined a function which should return a dataframe. This is all according to the quickstart guide
def train_input_fn(features, labels, batch_size):
"""An input function for training"""
# Convert the inputs to a Dataset.
dataset = tf.data.Dataset.from_tensor_slices((dict(features), labels))
# Shuffle, repeat, and batch the examples.
dataset = dataset.shuffle(1000).repeat().batch(batch_size)
dataset = dataset.map(mappingfunction)
# Return the dataset
return dataset
I call this function like this;
mydataset = train_input_fn(mydf.drop(["label"],axis=1),mydf["label"],200)
This works, if I remove the mapping but I get a questionmark when I print the shape. Why? The dimensions seem to be clearly defined.
This is where the real struggle begins. I want to create a mapping function, that replaces the filepath with an array of the image.
I tried to achieve that by writing this mappingfunction
def mappingfunction(feature,label):
print(feature['Filename'])
image = tf.read_file(feature['Filename'])
image = tf.image.decode_image(image)
return image,label
This will only return the image and the label. I don't know how I would realize it to return all the features but the filepath.
But even this simplified verison won't work. I get an "expected binary or unicode string" error. Can you help me?
The mapping function should return all features and the label. For example:
def mappingfunction(feature,label):
print(feature['Filename'])
image = tf.read_file(feature['Filename'])
image = tf.image.decode_image(image)
features['image'] = image
return features, label
This will add an image key to the features dictionary.

Categories