AttributeError: 'str' object has no attribute 'to'

I am not 100% sure that this is an error that you guys will care about, but I built a DataBunch using two datasets from a class I built. The issue might be my understanding of datasets, but here is what my dataset class looks like:

class DCMImageDictDataset(LabelDataset):
    "Abstract `Dataset` containing images."
    def __init__(self, dictionary):
        self.patientID = list(dictionary.keys())
        #self.imgs = trn_dict
        #self.y = np.array(y)
        self.images = []
        self.bbs = []
        self.lbs = []
        for i in list(dictionary.values()):
            image = i[0][0]
            bb = [k for j,k,l in i]
            lb = [l for j,k,l in i]
            self.images.append(image)
            self.bbs.append(bb)
            self.lbs.append(lb)

    def __getitem__(self,i): return self.patientID[i],(self.images[i], self.bbs[i], self.lbs[i])
    def __getattr__(self, name): return f'Invalid attribute: {name}'
    def __repr__(self): return f'{len(self.patientID)} total records'
    def __len__(self): return len(self.patientID)
    @property
    def c(self): 
        unique_list = []
        unique_words = 0
        for i in self.lbs:
            for j in i:
                if j not in unique_list:
                    unique_list.append(j)
                    unique_words+=1
        return unique_words

The biggest things here are that when I feed in a number, it gives me an id, image, bounding box coordinates, and labels for those boxes. (this is for the RSNA Pneumonia challenge).

Once I have my train and validation dataset, I create a DataBunch with this:

trn_ds = DCMImageDictDataset(trn_anno)
val_ds = DCMImageDictDataset(val_anno)

bs = 2
sz = 224
data = DataBunch.create(trn_ds, val_ds, path=PATH, bs=bs, num_workers=0)

When I try to do a learn.fit, I get AttributeError: 'str' object has no attribute 'to' which I was able to narrow down to it not liking how my dataloader is trying to pull information out. It wants an xb and yb so I am wondering if I need to change something clear back at the top with my DCMImageDictDataset class?

Here is a dump of the error if that is helpful (for xb,yb in progress_bar(data.train_dl, parent=pbar)):

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-79-021fad3debde> in <module>()
----> 1 learn.fit(a, 1e-5)

~/fastai_v1/fastai/kaggle/Competitions/fastai/basic_train.py in fit(self, epochs, lr, wd, callbacks)
    127         callbacks = [cb(self) for cb in self.callback_fns] + listify(callbacks)
    128         fit(epochs, self.model, self.loss_fn, opt=self.opt, data=self.data, metrics=self.metrics,
--> 129             callbacks=self.callbacks+callbacks)
    130 
    131     def create_opt(self, lr:Floats, wd:Floats=0.)->None:

~/fastai_v1/fastai/kaggle/Competitions/fastai/basic_train.py in fit(epochs, model, loss_fn, opt, data, callbacks, metrics)
     80     except Exception as e:
     81         exception = e
---> 82         raise e
     83     finally: cb_handler.on_train_end(exception)
     84 

~/fastai_v1/fastai/kaggle/Competitions/fastai/basic_train.py in fit(epochs, model, loss_fn, opt, data, callbacks, metrics)
     64             cb_handler.on_epoch_begin()
     65 
---> 66             for xb,yb in progress_bar(data.train_dl, parent=pbar):
     67                 xb, yb = cb_handler.on_batch_begin(xb, yb)
     68                 loss,_ = loss_batch(model, xb, yb, loss_fn, opt, cb_handler)

~/anaconda3/envs/fastai/lib/python3.6/site-packages/fastprogress/fastprogress.py in __iter__(self)
     59         self.update(0)
     60         try:
---> 61             for i,o in enumerate(self._gen):
     62                 yield o
     63                 if self.auto_update: self.update(i+1)

~/fastai_v1/fastai/kaggle/Competitions/fastai/data.py in proc_batch(self, b)
     39     def proc_batch(self,b:Tensor)->Tensor:
     40         "Proces batch `b` of `TensorImage`."
---> 41         b = to_device(b, self.device)
     42         for f in listify(self.tfms): b = f(b)
     43         return b

~/fastai_v1/fastai/kaggle/Competitions/fastai/torch_core.py in to_device(b, device)
     71     "Ensure `b` is on `device`."
     72     device = ifnone(device, default_device)
---> 73     if is_listy(b): return [to_device(o, device) for o in b]
     74     return b.to(device)
     75 

~/fastai_v1/fastai/kaggle/Competitions/fastai/torch_core.py in <listcomp>(.0)
     71     "Ensure `b` is on `device`."
     72     device = ifnone(device, default_device)
---> 73     if is_listy(b): return [to_device(o, device) for o in b]
     74     return b.to(device)
     75 

~/fastai_v1/fastai/kaggle/Competitions/fastai/torch_core.py in to_device(b, device)
     71     "Ensure `b` is on `device`."
     72     device = ifnone(device, default_device)
---> 73     if is_listy(b): return [to_device(o, device) for o in b]
     74     return b.to(device)
     75 

~/fastai_v1/fastai/kaggle/Competitions/fastai/torch_core.py in <listcomp>(.0)
     71     "Ensure `b` is on `device`."
     72     device = ifnone(device, default_device)
---> 73     if is_listy(b): return [to_device(o, device) for o in b]
     74     return b.to(device)
     75 

~/fastai_v1/fastai/kaggle/Competitions/fastai/torch_core.py in to_device(b, device)
     72     device = ifnone(device, default_device)
     73     if is_listy(b): return [to_device(o, device) for o in b]
---> 74     return b.to(device)
     75 
     76 def data_collate(batch:ItemsList)->Tensor:

AttributeError: 'str' object has no attribute 'to'

Here is an easier piece of code to recreate the same issue:

for xb, yb in data.train_dl:
    print(xb)

Gives this error:

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-78-095290fa3351> in <module>()
----> 1 for xb, yb in data.train_dl:
      2     print(xb)

~/fastai_v1/fastai/kaggle/Competitions/fastai/data.py in proc_batch(self, b)
     39     def proc_batch(self,b:Tensor)->Tensor:
     40         "Proces batch `b` of `TensorImage`."
---> 41         b = to_device(b, self.device)
     42         for f in listify(self.tfms): b = f(b)
     43         return b

~/fastai_v1/fastai/kaggle/Competitions/fastai/torch_core.py in to_device(b, device)
     71     "Ensure `b` is on `device`."
     72     device = ifnone(device, default_device)
---> 73     if is_listy(b): return [to_device(o, device) for o in b]
     74     return b.to(device)
     75 

~/fastai_v1/fastai/kaggle/Competitions/fastai/torch_core.py in <listcomp>(.0)
     71     "Ensure `b` is on `device`."
     72     device = ifnone(device, default_device)
---> 73     if is_listy(b): return [to_device(o, device) for o in b]
     74     return b.to(device)
     75 

~/fastai_v1/fastai/kaggle/Competitions/fastai/torch_core.py in to_device(b, device)
     71     "Ensure `b` is on `device`."
     72     device = ifnone(device, default_device)
---> 73     if is_listy(b): return [to_device(o, device) for o in b]
     74     return b.to(device)
     75 

~/fastai_v1/fastai/kaggle/Competitions/fastai/torch_core.py in <listcomp>(.0)
     71     "Ensure `b` is on `device`."
     72     device = ifnone(device, default_device)
---> 73     if is_listy(b): return [to_device(o, device) for o in b]
     74     return b.to(device)
     75 

~/fastai_v1/fastai/kaggle/Competitions/fastai/torch_core.py in to_device(b, device)
     72     device = ifnone(device, default_device)
     73     if is_listy(b): return [to_device(o, device) for o in b]
---> 74     return b.to(device)
     75 
     76 def data_collate(batch:ItemsList)->Tensor:

AttributeError: 'str' object has no attribute 'to'
1 Like

Datasets need to return tensors. It looks like yours is returning a string.

1 Like

Hi, have you solved the issue?

Hi Jeremy, thanks for your amazing lectures and the library! Do you mean the __getitem__ method should return tensors? I have encountered the same error. My inputs are paths to images stored in a data frame with labels, but the images are transformed into tensors in the __getitem__ function.

My __getitem__ ended up looking quite a bit different to work, let me know if this helps:

class DCMImageDictDataset(LabelDataset):
    "Abstract `Dataset` containing images."
    def __init__(self, dictionary):
        self.patientID = list(dictionary.keys())
        #self.imgs = trn_dict
        #self.y = np.array(y)
        self.images = []
        self.bbs = []
        self.lbs = []
        for i in list(dictionary.values()):
            image = i[0][0]
            bb = [k for j,k,l in i]
            lb = [l for j,k,l in i]
            self.images.append(image)
            self.bbs.append(bb)
            self.lbs.append(lb)
    def __getitem__(self,i):
        x = self.images[i]
        cats = LongTensor(self.lbs[i])
        return (x, (ImageBBox.create(self.bbs[i], *x.size, cats)))
        #return (torch.tensor(np.array([(self.images[i].data.float()/255)[0].numpy(),(self.images[i].data.float()/255)[0].numpy(),(self.images[i].data.float()/255)[0].numpy()]))),(self.lbs[i])
    def __getattr__(self, name): return f'Invalid attribute: {name}'
    def __repr__(self): return f'{len(self.patientID)} total records'
    def __len__(self): return len(self.patientID)
    @property
    def c(self): 
        unique_list = []
        unique_words = 0
        for i in self.lbs:
            for j in i:
                if j not in unique_list:
                    unique_list.append(j)
                    unique_words+=1
        return unique_words

Otherwise could you share what you’re trying to do and I can try to help you get yours working properly?

I am not sure that I was able to get this actually training either when I was working on this as an fyi. I remember working on this, but I never submitted anything to the competition.

1 Like

Hi Kevin, thanks for your reply. Looks like your __getitem__ function returned a tuple of path and tensor? I saw people using TensorDataset from Pytorch to create a data bunch: https://gist.github.com/bfarzin/9e94eaa0597b971dda611be4b9735c94, not sure if that’s the only solution.

I’m trying to tile high resolution images into smaller pieces without occupying my hard disk. I used PIL loader to load images and transform them as tensors in my __getitem__ method. My code is mainly copied from this post.

class SegmentedDataset(data.Dataset):
    def __init__(self, images, labels, segments_per_image, tfms = transforms.ToTensor()):
        self.images = images
        self.labels = labels
        self.segments_per_image = segments_per_image
        self.c = 2
        self.tfms = tfms
        
    def get_segment(self, image, segment_ID):
        'returns one segment of one image'
        image_split1 = image.chunk(2, dim=1)
        if segment_ID < 3:
            image_split2 = image_split1[0].chunk(2, dim=2)
            return image_split2[segment_ID-1]
        else: 
            image_split2 = image_split1[1].chunk(2, dim=2)
            return image_split2[segment_ID-3]   #pil_loader('/mnt/sdf/training_data/'+image)

    def __len__(self):
        return len(self.labels)*self.segments_per_image

    def __getitem__(self, index):
        'Generates one sample of data'
        segment_ID = index%self.segments_per_image
        image_ID = index//self.segments_per_image

        image = self.images[image_ID]
        image = pil_loader('/mnt/sdf/training_data/'+image)  
        image = self.tfms(image)

        X = self.get_segment(image, segment_ID)
        y = self.labels[image_ID]
        return X, y

By running the following code, I end up with the exact same error:

training_set = SegmentedDataset(dfs['filename_new'][0:100], dfs['treatment'][0:100],4)
validation_set = SegmentedDataset(dfs['filename_new'][101:200], dfs['treatment'][101:200],4)
imdata = ImageDataBunch.create(train_ds=training_set, valid_ds=validation_set, path='/mnt/sdf/training_data/')
for xb,yb in imdata.train_dl:
    print(xb)

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-103-19784011f1ee> in <module>
----> 1 for xb,yb in imdata.train_dl:
      2     print(xb)

~/anaconda3/envs/adam/lib/python3.7/site-packages/fastai/basic_data.py in __iter__(self)
     73     def __iter__(self):
     74         "Process and returns items from `DataLoader`."
---> 75         for b in self.dl: yield self.proc_batch(b)
     76 
     77     @classmethod

~/anaconda3/envs/adam/lib/python3.7/site-packages/fastai/basic_data.py in proc_batch(self, b)
     67     def proc_batch(self,b:Tensor)->Tensor:
     68         "Process batch `b` of `TensorImage`."
---> 69         b = to_device(b, self.device)
     70         for f in listify(self.tfms): b = f(b)
     71         return b

~/anaconda3/envs/adam/lib/python3.7/site-packages/fastai/torch_core.py in to_device(b, device)
    121     "Recursively put `b` on `device`."
    122     device = ifnone(device, defaults.device)
--> 123     return recurse(lambda x: x.to(device, non_blocking=True), b)
    124 
    125 def data_collate(batch:ItemsList)->Tensor:

~/anaconda3/envs/adam/lib/python3.7/site-packages/fastai/core.py in recurse(func, x, *args, **kwargs)
     76 
     77 def recurse(func:Callable, x:Any, *args, **kwargs)->Any:
---> 78     if is_listy(x): return [recurse(func, o, *args, **kwargs) for o in x]
     79     if is_dict(x):  return {k: recurse(func, v, *args, **kwargs) for k,v in x.items()}
     80     return func(x, *args, **kwargs)

~/anaconda3/envs/adam/lib/python3.7/site-packages/fastai/core.py in <listcomp>(.0)
     76 
     77 def recurse(func:Callable, x:Any, *args, **kwargs)->Any:
---> 78     if is_listy(x): return [recurse(func, o, *args, **kwargs) for o in x]
     79     if is_dict(x):  return {k: recurse(func, v, *args, **kwargs) for k,v in x.items()}
     80     return func(x, *args, **kwargs)

~/anaconda3/envs/adam/lib/python3.7/site-packages/fastai/core.py in recurse(func, x, *args, **kwargs)
     76 
     77 def recurse(func:Callable, x:Any, *args, **kwargs)->Any:
---> 78     if is_listy(x): return [recurse(func, o, *args, **kwargs) for o in x]
     79     if is_dict(x):  return {k: recurse(func, v, *args, **kwargs) for k,v in x.items()}
     80     return func(x, *args, **kwargs)

~/anaconda3/envs/adam/lib/python3.7/site-packages/fastai/core.py in <listcomp>(.0)
     76 
     77 def recurse(func:Callable, x:Any, *args, **kwargs)->Any:
---> 78     if is_listy(x): return [recurse(func, o, *args, **kwargs) for o in x]
     79     if is_dict(x):  return {k: recurse(func, v, *args, **kwargs) for k,v in x.items()}
     80     return func(x, *args, **kwargs)

~/anaconda3/envs/adam/lib/python3.7/site-packages/fastai/core.py in recurse(func, x, *args, **kwargs)
     78     if is_listy(x): return [recurse(func, o, *args, **kwargs) for o in x]
     79     if is_dict(x):  return {k: recurse(func, v, *args, **kwargs) for k,v in x.items()}
---> 80     return func(x, *args, **kwargs)
     81 
     82 def first_el(x: Any)->Any:

~/anaconda3/envs/adam/lib/python3.7/site-packages/fastai/torch_core.py in <lambda>(x)
    121     "Recursively put `b` on `device`."
    122     device = ifnone(device, defaults.device)
--> 123     return recurse(lambda x: x.to(device, non_blocking=True), b)
    124 
    125 def data_collate(batch:ItemsList)->Tensor:

AttributeError: 'str' object has no attribute 'to'

I think I returned the image and an ImageBBox. Let me do some exploring and I will see if I can at least give you a better understanding of what I was returning

1 Like

I tried to get back to that point in my workflow, but it’s pretty broken at this point. I believe I returned an Image and an ImageBBox though.

No worries, I found another way to reduce image file size without tiling, thanks for your help!

1 Like