TypeError: cannot unpack non-iterable int object

Hi,

Im trying to make my first CNN using pyTorch and am following online help and code already people wrote. i am trying to reproduce their results. I’m using the Kaggle Dogs Breed Dataset for this and below is the error I get. The trainloader does not return my images and labels and any attempt to get them leads in an error:

Traceback (most recent call last):
  File "E:\Program Files\JetBrains\PyCharm Community Edition 2018.2.4\helpers\pydev\pydevd.py", line 1664, in <module>
    main()
  File "E:\Program Files\JetBrains\PyCharm Community Edition 2018.2.4\helpers\pydev\pydevd.py", line 1658, in main
    globals = debugger.run(setup['file'], None, None, is_module)
  File "E:\Program Files\JetBrains\PyCharm Community Edition 2018.2.4\helpers\pydev\pydevd.py", line 1068, in run
    pydev_imports.execfile(file, globals, locals)  # execute the script
  File "E:\Program Files\JetBrains\PyCharm Community Edition 2018.2.4\helpers\pydev\_pydev_imps\_pydev_execfile.py", line 18, in execfile
    exec(compile(contents+"\n", file, 'exec'), glob, loc)
  File "C:/Users/sbzfk/PycharmProjects/my_FCN_attempt/Kaggle_Dogs_Competition.py", line 85, in <module>
    img, label = next(iter(train_loader))
  File "C:\Users\sbzfk\AppData\Local\Programs\Python\Python37\lib\site-packages\torch\utils\data\dataloader.py", line 314, in __next__
    batch = self.collate_fn([self.dataset[i] for i in indices])
  File "C:\Users\sbzfk\AppData\Local\Programs\Python\Python37\lib\site-packages\torch\utils\data\dataloader.py", line 314, in <listcomp>
    batch = self.collate_fn([self.dataset[i] for i in indices])
  File "C:/Users/sbzfk/PycharmProjects/my_FCN_attempt/Kaggle_Dogs_Competition.py", line 42, in __getitem__
    img = self.transform(img)
  File "C:\Users\sbzfk\AppData\Local\Programs\Python\Python37\lib\site-packages\torchvision\transforms.py", line 34, in __call__
    img = t(img)
  File "C:\Users\sbzfk\AppData\Local\Programs\Python\Python37\lib\site-packages\torchvision\transforms.py", line 187, in __call__
    w, h = img.size
TypeError: cannot unpack non-iterable int object

Below is my code:

class DogsDataset(Dataset):

    def __init__(self, filenames, labels, root_dir, transform=None):
        assert len(filenames) == len(labels)        # if the two are not of equal length throw an error
        self.filenames = filenames
        self.labels = labels
        self.root_dir = root_dir
        self.transform = transform

    def __len__(self):
        return len(self.filenames)

    def __getitem__(self, idx):
        this_img = join(self.root_dir, 'train', self.filenames[idx]+'.jpg')
        print(this_img)
        img = io.imread(this_img)
        label = self.labels[idx]
        print(label)

        if self.transform:
            img = self.transform(img)

        return [img, label]



batch_size = 64
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

dataset_root = expanduser(join('~', 'Documents', 'kaggle_dogs_dataset'))
# join will intelligently join directories irrespective of OS, and expanduser will
# replace with /home/ in linux or the username in Windows

csv_file = pd.read_csv(join(dataset_root, 'labels.csv'))        # csv file has two columns, id which are filenames and breed which are labels
filenames = csv_file.id.values              # convert that column to an array, id is the column name and values converty to numpy array
# le = LabelEncoder()
# labels = le.fit_transform(csv_file.breed)           # this will just encode the names between 0 to models-1 , basically changing strings to integers
labels = csv_file.breed.values

filenames_train, filenames_eval, labels_train, labels_eval = train_test_split(filenames, labels,
                                                                              test_size=0.1, stratify=labels)       # this is an import from sklearn as the name implies, it randomly splits data into train and eval, 10% of it to test and rest train

data_transform = transforms.Compose([transforms.Scale(224),
                                     transforms.CenterCrop(224),
                                     transforms.ToTensor(),
                                     transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))])

dog_train = DogsDataset(filenames_train, labels_train, dataset_root, transform=data_transform)
train_loader = DataLoader(dog_train, batch_size, shuffle=True)

dog_eval = DogsDataset(filenames_eval, labels_eval, dataset_root, transform=data_transform)
eval_loader = DataLoader(dog_eval, batch_size, shuffle=True)


def im_show(axis, inp):
    """Denormalize and show"""
    inp = inp.numpy().transpose((1, 2, 0))
    mean = np.array([0.485, 0.456, 0.406])
    std = np.array([0.229, 0.224, 0.225])
    inp = std * inp + mean
    axis.imshow(inp)


img, label = next(iter(train_loader))
print(img.size(), label.size())
fig = plt.figure(1, figsize=(16, 4))
grid = ImageGrid(fig, 111, nrows_ncols=(1, 4), axes_pad=0.05)
for i in range(img.size()[0]):
    ax = grid[i]
    im_show(ax, img[i])

I guess your self.transform contains some image transformations other than ToTensor(), e.g. RandomCrop().
These transformations currently work on PIL.Images, so if you load your image with img = Image.open(this_img) it should work.

2 Likes

Thanks for answering @ptrblck. I didint know that transforms only work with PIL objects.

It allowed me to go further in my code but now I am presented with another error:

 Traceback (most recent call last):
  File "C:/Users/sbzfk/PycharmProjects/my_FCN_attempt/Kaggle_Dogs_Competition.py", line 113, in <module>
    model = train_model(data_loaders, net, criterion, optimizer, exp_lr_scheduler, num_epochs=2)
  File "C:\Users\sbzfk\PycharmProjects\my_FCN_attempt\TrainModel.py", line 34, in train_model
    loss = criterion(outputs, labels)
  File "C:\Users\sbzfk\AppData\Local\Programs\Python\Python37\lib\site-packages\torch\nn\modules\module.py", line 477, in __call__
    result = self.forward(*input, **kwargs)
  File "C:\Users\sbzfk\AppData\Local\Programs\Python\Python37\lib\site-packages\torch\nn\modules\loss.py", line 862, in forward
    ignore_index=self.ignore_index, reduction=self.reduction)
  File "C:\Users\sbzfk\AppData\Local\Programs\Python\Python37\lib\site-packages\torch\nn\functional.py", line 1550, in cross_entropy
    return nll_loss(log_softmax(input, 1), target, weight, None, ignore_index, None, reduction)
  File "C:\Users\sbzfk\AppData\Local\Programs\Python\Python37\lib\site-packages\torch\nn\functional.py", line 1403, in nll_loss
    if input.size(0) != target.size(0):
AttributeError: 'tuple' object has no attribute 'size'

Apparently the labels list/array is giving problems. Is it because its not a Tensor? But all the examples I read online, none of them convert their labels to tensors, they are used as is as I have done here. The error is from the TrainModel code which is below:

import time
import torch


def train_model(dataloders, model, criterion, optimizer, scheduler, num_epochs=25):
    use_gpu = torch.cuda.is_available()
    since = time.time()
    best_model_wts = model.state_dict()
    best_acc = 0.0
    dataset_sizes = {'train': len(dataloders['train'].dataset),
                     'valid': len(dataloders['valid'].dataset)}

    for epoch in range(num_epochs):
        for phase in ['train', 'valid']:            # for every epoch first train and that validate, phase will take string of train first and next iter it will be valid
            if phase == 'train':
                scheduler.step()
                model.train(True)
            else:
                model.train(False)                  # set to evaluation mode

            running_loss = 0.0
            running_corrects = 0

            for inputs, labels in dataloders[phase]:            # this will load data from train data loader first and then from validation dataloader
                if use_gpu:
                    inputs, labels = inputs.cuda(), labels.cuda()

                optimizer.zero_grad()

                outputs = model(inputs)
                print(outputs.size())
                print(labels.size())
                _, preds = torch.max(outputs.data, 1)
                loss = criterion(outputs, labels)

                if phase == 'train':
                    loss.backward()
                    optimizer.step()

                running_loss += loss.data[0]
                running_corrects += torch.sum(preds == labels.data)

            if phase == 'train':
                train_epoch_loss = running_loss / dataset_sizes[phase]
                train_epoch_acc = running_corrects / dataset_sizes[phase]
            else:
                valid_epoch_loss = running_loss / dataset_sizes[phase]
                valid_epoch_acc = running_corrects / dataset_sizes[phase]

            if phase == 'valid' and valid_epoch_acc > best_acc:
                best_acc = valid_epoch_acc
                best_model_wts = model.state_dict()

        print('Epoch [{}/{}] train loss: {:.4f} acc: {:.4f} '
              'valid loss: {:.4f} acc: {:.4f}'.format(
            epoch, num_epochs - 1,
            train_epoch_loss, train_epoch_acc,
            valid_epoch_loss, valid_epoch_acc))

    print('Best val Acc: {:4f}'.format(best_acc))

    model.load_state_dict(best_model_wts)
    return model

The above print lines of print(outputs.size()) gives the output torch.Size([64, 10222]) where as the print(labels.size()) gives the error: AttributeError: 'tuple' object has no attribute 'size'

Can you please help me with this,

Many thanks

Your labels should also be tensors as otherwise you couldn’t even push them onto your GPU:

labels = labels.cuda()

Are your labels currently stored in a list or a numpy array?
For the former you could just wrap them using torch.tensor(label) in your Dataset, for the latter use torch.from_numpy(label).

Helllo @ptrblck . I trained Resnet50 on my dataset. At the end of training I got following error. Can you please let me know for solving this issue?

Thank you

This is my code

Just normalization for validation

data_transforms = {
‘train’: transforms.Compose([
transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
]),
‘val’: transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
]),

 'test': transforms.Compose([
    transforms.Resize(256),
    transforms.CenterCrop(224),
    transforms.ToTensor(),
    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
]),

}

data_dir = ‘/content/drive/MyDrive/raphcatr’
image_datasets = {x: datasets.ImageFolder(os.path.join(data_dir, x),
data_transforms[x])
for x in [‘train’, ‘val’, ‘test’]}
dataloaders = {x: torch.utils.data.DataLoader(image_datasets[x], batch_size=16,
shuffle=True, num_workers=4)
for x in [‘train’, ‘val’, ‘test’]}
dataset_sizes = {x: len(image_datasets[x]) for x in [‘train’, ‘val’, ‘test’]}
class_names = image_datasets[‘train’].classes

device = torch.device(“cuda:0” if torch.cuda.is_available() else “cpu”)

def train_model(model, criterion, optimizer, num_epochs=25):
since = time.time()

best_model_wts = copy.deepcopy(model.state_dict())
best_acc = 0.0

for epoch in range(num_epochs):
    print('Epoch {}/{}'.format(epoch, num_epochs - 1))
    print('-' * 10)

    # Each epoch has a training and validation phase
    for phase in ['train', 'val']:
        if phase == 'train':
            model.train()  # Set model to training mode
        else:
            model.eval()   # Set model to evaluate mode

        running_loss = 0.0
        running_corrects = 0

        # Iterate over data.
        for inputs, labels in dataloaders[phase]:
            inputs = inputs.to(device)
            labels = labels.to(device)

            # zero the parameter gradients
            optimizer.zero_grad()

            # forward
            # track history if only in train
            with torch.set_grad_enabled(phase == 'train'):
                outputs = model(inputs)
                _, preds = torch.max(outputs, 1)
                loss = criterion(outputs, labels)

                # backward + optimize only if in training phase
                if phase == 'train':
                    loss.backward()
                    optimizer.step()

            # statistics
            running_loss += loss.item() * inputs.size(0)
            running_corrects += torch.sum(preds == labels.data)
        

        epoch_loss = running_loss / dataset_sizes[phase]
        epoch_acc = running_corrects.double() / dataset_sizes[phase]

        print('{} Loss: {:.4f} Acc: {:.4f}'.format(
            phase, epoch_loss, epoch_acc))

        # deep copy the model
        if phase == 'val' and epoch_acc > best_acc:
            best_acc = epoch_acc
            best_model_wts = copy.deepcopy(model.state_dict())

    print()

time_elapsed = time.time() - since
print('Training complete in {:.0f}m {:.0f}s'.format(
    time_elapsed // 60, time_elapsed % 60))
print('Best val Acc: {:4f}'.format(best_acc))

# load best model weights
model.load_state_dict(best_model_wts)
return model

model = models.resnet50(pretrained=True)
# Freeze training for all “features” layers
for param in model.parameters():
param.requires_grad = False

classifier = nn.Sequential(
nn.Linear(2048,512),
nn.ReLU(),
nn.Dropout(p=0.5),
nn.Linear(512, 3))

model.fc = classifier

class_weights = torch.FloatTensor([1./127, 1./141, 1./257]) * 100

criterion = nn.CrossEntropyLoss(weight = class_weights).to(device)

Only train the classifier parameters, feature parameters are frozen

optimizer = optim.Adam(model.fc.parameters(), lr=0.0002)

from PIL import Image, ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES=True

resnet_model, history = train_model(model, criterion, optimizer,
num_epochs=25)

You are returning the model only, while trying to assign resnet_model, history as seen here:

def fun():
    model = models.resnet18()
    return model

model = fun() # works
model, history = fun()
# > TypeError: cannot unpack non-iterable ResNet object

I see it. Thank you very much @ptrblck

Hi @ptrblck

How can I plot train loss and val loss in my code?

Thank you

Store the losses in e.g. a list, transform them to e.g. numpy arrays, and plot them via e.g. matplotlib.

Hi @ptrblck

Thank you for your response. Do i missing something to plot train acc and val acc, as well as train loss and val loss?

My code: This is actually original pytorch tutorial code . I would like to plot train acc and val acc , train loss and val loss separately

def train_model(model, criterion, optimizer, scheduler, num_epochs=25):
since = time.time()
history=[]

best_model_wts = copy.deepcopy(model.state_dict())
best_acc = 0.0

for epoch in range(num_epochs):
    print('Epoch {}/{}'.format(epoch, num_epochs - 1))
    print('-' * 10)

    # Each epoch has a training and validation phase
    for phase in ['train', 'val']:
        if phase == 'train':
            model.train()  # Set model to training mode
        else:
            model.eval()   # Set model to evaluate mode

        running_loss = 0.0
        running_corrects = 0

        # Iterate over data.
        for inputs, labels in dataloaders[phase]:
            inputs = inputs.to(device)
            labels = labels.to(device)

            # zero the parameter gradients
            optimizer.zero_grad()

            # forward
            # track history if only in train
            with torch.set_grad_enabled(phase == 'train'):
                outputs = model(inputs)
                _, preds = torch.max(outputs, 1)
                loss = criterion(outputs, labels)

                # backward + optimize only if in training phase
                if phase == 'train':
                    loss.backward()
                    optimizer.step()

            # statistics
            running_loss += loss.item() * inputs.size(0)
            running_corrects += torch.sum(preds == labels.data)
        if phase == 'train':
            scheduler.step()

        epoch_loss = running_loss / dataset_sizes[phase]
        epoch_acc = running_corrects.double() / dataset_sizes[phase]

        print('{} Loss: {:.4f} Acc: {:.4f}'.format(
            phase, epoch_loss, epoch_acc))

        # deep copy the model
        if phase == 'val' and epoch_acc > best_acc:
            best_acc = epoch_acc
            best_model_wts = copy.deepcopy(model.state_dict())

    print()

time_elapsed = time.time() - since
print('Training complete in {:.0f}m {:.0f}s'.format(
    time_elapsed // 60, time_elapsed % 60))
print('Best val Acc: {:4f}'.format(best_acc))

# load best model weights
model.load_state_dict(best_model_wts)
history.append(epoch_acc, epoch_loss)

return model, history

import matplotlib.pyplot as plt
import numpy as np

history = np.array(history)

plt.plot(history[:,0:2])

Based on your code it seems you are appending epoch_acc and epoch_loss only once after the training is done so I would assume plt.plot just plots one point?
I guess you want to append all epoch stats so move the history.append into the epoch loop?