ZealotTV
ZealotTV

Reputation: 45

How to fix AttributeError: 'tuple' object has no attribute 'to'?


Today i faced with problem AttributeError: 'tuple' object has no attribute 'to'
I read a data from csv file with 2 columns: Image (where the file path is) and finding (where the photo's label is)
Model:

model = models.resnet18(pretrained=False)
model.fc = nn.Sequential(nn.Linear(model.fc.in_features, 256),
                         nn.ReLU(),
                         nn.Dropout(p=0.3),
                         nn.Linear(256, 100),
                         nn.ReLU(),
                         nn.Dropout(p=0.4),
                         nn.Linear(100,9))
# model.load_state_dict(torch.load('model.pth'))

for name, param in model.named_parameters():
    if("bn" not in name):
        param.requires_grad = False

Transforms:

transform = transforms.Compose([
    transforms.Resize((224, 224)),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.5457, 0.5457, 0.5457], std=[0.2342, 0.2342, 0.2342])    
])

Dataset class:

class Col(Dataset):
    def __init__(self, csv, main_dir, transform):
        self.df = pd.read_csv(csv)
        self.main_dir = main_dir
        self.transform = transform

    def __len__(self):
        return self.df.shape[0]

    def __getitem__(self, idx):
        image = transform(Image.open(self.df.Image[idx]).convert("RGB"))
        label = self.df.Finding[idx]
        return image, label

Prepairing Data:

data = Col(main_dir=root_dir,csv=csv_file, transform=transform)
dataset = torch.utils.data.DataLoader(data, batch_size=130)
train_set, validate_set= torch.utils.data.random_split(dataset, [round(len(dataset)*0.7), (len(dataset) - round(len(dataset)*0.7))])

Train func:

def train(model, optimizer, loss_fn, train_set, validate_set, epochs=20, device="cpu"):
    for epoch in range(1, epochs+1):
        training_loss = 0.0
        valid_loss = 0.0
        model.train()
        for batch in train_set:
            optimizer.zero_grad()
            inputs, labels = batch
            inputs = inputs.to(device)
            labels = labels.to(device)
            output = model(inputs)
            loss = loss_fn(output, labels)
            loss.backward()
            optimizer.step()
            training_loss += loss.data.item() * inputs.size(0)
        training_loss /= len(train_set.dataset)
        
        model.eval()
        num_correct = 0 
        num_examples = 0
        for batch in validate_set:
            inputs, labels = batch
            inputs = inputs.to(device)
            output = model(inputs)
            labels = labels.to(device)
            loss = loss_fn(output, labels) 
            valid_loss += loss.data.item() * inputs.size(0)
            correct = torch.eq(torch.max(F.softmax(output, dim=1), dim=1)[1], targets)
            num_correct += torch.sum(correct).item()
            num_examples += correct.shape[0]
        valid_loss /= len(validate_set.dataset)

        print('Epoch: {}, Training Loss: {:.2f}, Validation Loss: {:.2f}, accuracy = {:.2f}'.format(epoch, training_loss,
        valid_loss, num_correct / num_examples))

Optimazer:

optimizer = optim.Adam(model.parameters(), lr=0.0001)

After calling train func

train(model, optimizer,torch.nn.CrossEntropyLoss(), train_set.dataset, validate_set.dataset, epochs=100, device=device)

I've got this error:

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
/tmp/ipykernel_9156/634509595.py in <module>
----> 1 train(model, optimizer,torch.nn.CrossEntropyLoss(), train_set.dataset, validate_set.dataset, epochs=100, device=device)

/tmp/ipykernel_9156/2858123881.py in train(model, optimizer, loss_fn, train_set, validate_set, epochs, device)
      8             inputs, labels = batch
      9             inputs = inputs.to(device)
---> 10             labels = labels.to(device)
     11             output = model(inputs)
     12             loss = loss_fn(output, labels)

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

If i try to call this

for batch in train_set.dataset:
    inputs, labels = batch
    print(labels)

then it will display many large tuples with labels.(like this)

('polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps', 'polyps')

How can i fix this? Help me please

Upvotes: 1

Views: 5450

Answers (1)

Ivan
Ivan

Reputation: 40748

You need to encode your labels into integers (or one-hot encodings depending on your loss_fn).

You should have classes, a list of 9 class names. If not you can get it with self.df.Finding.unique().

You can then invert this list into a dictionnary, essentially a label-name -> label-id mapping:

self.encode = {k: i for i, k in enumerate(classes)}

Then in your dataset __getitem__ definition convert the label names to label ids with encode:

def __getitem__(self, idx):
    image = transform(Image.open(self.df.Image[idx]).convert("RGB"))
    label = self.encode[self.df.Finding[idx]]
    return image, label

Upvotes: 2

Related Questions