How to fix an error with the quickstart tutorial for pytorch?

Viewed 109

I am trying to follow the tutorial on pytorch HERE, but there seems to be a problem. I have created a custom dataloader named training_data that returns an object as required HERE which is a dictionary

{"image": image, "label": label}

where image is a tensor and label is a string. I then follow the tutorial and create a DataLoader as follows:

train_dataloader = DataLoader(training_data, batch_size=batch_size)

and use that DataLoader in the method train:

def train(dataloader, model, loss_fn, optimizer):
    size = len(dataloader)
    for batch, (X, y) in enumerate(dataloader):
        X, y = X.to(device), y.to(device)

        # Compute prediction error
        pred = model(X)
        loss = loss_fn(pred, y)

        # Backpropagation
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        if batch % 100 == 0:
            loss, current = loss.item(), batch * len(X)
            print(f"loss: {loss:>7f}  [{current:>5d}/{size:>5d}]")

However, when I call the training method for a batch

train(train_dataloader, model, loss_fn, optimizer)

I get an error

Traceback (most recent call last):
  File "train_network.py", line 110, in <module>
    train(train_dataloader, model, loss_fn, optimizer)
  File "train_network.py", line 76, in train
    X, y = X.to(device), y.to(device)
AttributeError: 'str' object has no attribute 'to'

as y is a string with the content label. What am I doing wrong?

2 Answers

Your labels y need to be torch tensors. Since you currently have strings, and assuming you are doing classification among n classes, you can simply map them using a list. For example, with three classes, inside the __init__ of your Dataset class:

self.label_names = ["class1", "class2", "class3"]

Then, in __getitem__, you could add:

label = torch.tensor(label_names.index(label))

where label previously stored a string.

A dictionary is a valid type for your dataset to return (though to be fair the more common practice is to have your dataset's __getitem__ method return data,label as two separate tensors.)

In any case, when your dataset __getitem__ method returns an object of type dict, the collate_fn of pytorch returns a dict as well, with the same keys. Your issue is that you are trying to move the key (a string), rather than the value (a tensor) to the GPU.

Instead do:

for batch, datum in enumerate(dataloader):
   X = datum["image"].to(device)
   y = datum["label"].to(device)
   ...

However, as noted by @GoodDeeds, I am fairly confident that you need to convert from string labels to integer-type labels for classification (via one-hot encoding as detailed in that answer).

# from GoodDeeds answer, I include to condense both relevant changes into one answer
self.label_names = ["class1", "class2", "class3"]
label = torch.tensor(label_names.index(label)) # in __getitem__ method

If you don't want to edit the dataset at all you could define a custom collate_fn for your dataloader that replaces string class names with integers, or else perform this type conversion after loading the data batch (not recommended).

Related