'tuple' object has no attribute 'to' in pytorch

I got this error while trying to test CNN model.
I already checked type about this error point’variable.

Here is error point
10. imgs = imgs.to(device) #imgs type <class ‘torch.Tensor’>
—> 11 labels = labels.to(device) #labels type <class ‘torch.Tensor’>

AttributeError: ‘tuple’ object has no attribute ‘to’

Both are Tensor type, there is not tuple type.

I try to make image classification to use multiple folders
ex)

folderA  > folderB> folderC
CatDataSet    -Train - White
                     - Black
                     - Brown

              -Test  - White
                     - Black
                     - Brown

              -Val   - White
                     - Black
                     - Brown

Here is my test function

def test(model, data_loader, device):
    print('Start test..')
    model.eval()
    with torch.no_grad():
        correct = 0
        total = 0
        for i, (imgs, labels) in enumerate(data_loader): 
            imgs = imgs.to(device)   #imgs type <class 'torch.Tensor'> 
            print("first labels",type(label))
            labels =  labels.to(device)  
            print("second labels",type(label))
            outputs = model(imgs)   
            print(outputs)     
            _, argmax = torch.max(outputs, 1)  
            total += imgs.size(0)
            correct += (labels == argmax).sum().item()
    print('Test accuracy for {} images: {:.2f}%'.format(total, correct / total * 100))
    model.train()

Here is my CNN function

class SimpleCNN(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv = nn.Sequential(   
            nn.Conv2d(3, 32, 3),  
            nn.BatchNorm2d(32),
            nn.ReLU(), 
            nn.MaxPool2d(2),   
            
            nn.Conv2d(32,64,3),    
            nn.BatchNorm2d(64),    
            nn.ReLU(),    
            nn.MaxPool2d(2),    
            
            nn.Conv2d(64,128,3),   
            nn.BatchNorm2d(128), 
            nn.ReLU(),   
            nn.MaxPool2d(2),  
            
            nn.Conv2d(128,128,3),  
            nn.BatchNorm2d(128),    
            nn.ReLU(),   
            nn.MaxPool2d(2),  
        )        
       
        self.fc1 = nn.Linear(128*5*5,512) 
        self.fc2 = nn.Linear(512,3) 
        self.relu = nn.ReLU()
    
    def forward(self, x):
        x = self.conv(x)
        x = x.view(x.shape[0], -1)
        x = F.relu(self.fc1(x))
        x = self.fc2(x)
        return x

Here is DataSet function

class CatDataset(Dataset):
    def __init__(self, data_dir, mode, transform=None):    
        self.all_data = sorted(glob.glob(os.path.join(data_dir, mode, '*', '*')))        
        self.transform = transform
    
    def __getitem__(self, index):
        data_path = self.all_data[index]   
        imgs = Image.open(data_path)
        imgs = self.transform(imgs)       
        label = os.path.basename(data_path) 
        print("data_path label",type(label)) #label type : str
        if label.startswith("Black"):
            label=0
        elif label.startswith("White"):
            label=1
        elif label.startswith("Brown"):
            label=2
        print("label",type(label)) #label type : int
        return imgs, label
    
    def __len__(self):
        length = len(self.all_data)
        return length

Please help me where did I wrote wrong code.

Based on the error message it seems that labels is indeed a tuple, while you expect it to be a tensor. You can check it by printing its type before calling the to() operation during the training and see, if this type changes in a specific iteration:

print(type(labels))
labels = labels.to(device)

Hi @ptrblck , Thank you for the helping.
I try to what you saying and here is the debugging result.

I did “print(type(labels))” in train function and test function.
When I did in train, It worked well. (before and after to() type print ‘torch.Tensor’)

But, in test function, It wasn’t.
Until 59s, It worked well.(Print 13 times like below)
CatDataset print → label type : class ‘str’ (data_path label)->class ‘int’(label)
Test print → labels type : <class ‘torch.Tensor’> → <class ‘torch.Tensor’>

And after 59s, It print like below.
CatDataset print → label type : class ‘str’ (data_path label)->class ‘str’(label)
Test print → labels type : <class ‘Tuple’’>

I’m not sure which point make it to change tuple.

Here is my debug result in test.

data_path label <class 'str'>
label <class 'int'>
...
first labels <class 'torch.Tensor'>
second labels <class 'torch.Tensor'>
tensor([[  5.1988, -10.0191,   5.5972],
        [  5.4772,  -8.9061,   4.4289],
		...
        [  2.7645,  -6.2371,   3.9978]])

...
x 13 times 
...

data_path label <class 'str'>
label <class 'str'>
first labels<class 'torch.Tensor'>
second labels<class 'torch.Tensor'>
tensor([[-2.4606,  4.5739, -2.4098],
        [-1.8784,  4.2095, -2.1987]])
first labels <class 'tuple'>
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-30-b1da10816353> in <module>()
----> 1 test(model, test_loader, device)

<ipython-input-28-297ea79abffe> in test(model, data_loader, device)
     10             imgs = imgs.to(device)
     11             print("first labels",type(labels))
---> 12             labels =  labels.to(device)       
     13             print("second labels",type(labels))

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

Based on the output it seems that the working iterations are creating the label initially as a str and in the conditions are replacing it with an int:

        print("data_path label",type(label)) #label type : str
        if label.startswith("Black"):
            label=0
        elif label.startswith("White"):
            label=1
        elif label.startswith("Brown"):
            label=2

However, the breaking case seems to fall through the condition (no condition returns True) and thus the label stays as a str type and raises the error in the test loop.
In that case, you should check why no condition is met and if you would need to define a default else branch in the __getitem__ method.

Thank you a lot @ptrblck!!
I spend a lot of time to fix the bug :disappointed_relieved: