Error: 'tuple' object has no attribute 'log_softmax'

Hello I have been trying my own code to make an Isolated Digits Speech using LCNN, but I have been running through issues lately with it

import matplotlib.pyplot as plt
from matplotlib.backend_bases import RendererBase
from scipy import signal
from scipy.io import wavfile
#import soundfile as sf
import os
import numpy as np
from PIL import Image
from scipy.fftpack import fft
from torch.optim import Adam, SGD
from torch.nn import Linear, CrossEntropyLoss, Sequential, Conv2d, MaxPool2d, Module, Softmax
import math
import torch
import torch.nn as nn
import torch.nn.functional as F

%matplotlib inline

audio_path = 'Dataset/train/'
pict_Path = 'Dataset/pics/train/'
test_audio_path = 'Dataset/test/'
test_pict_Path = 'Dataset/pics/test/'
val_audio_path = 'Dataset/validation/'
val_pict_Path = 'Dataset/pics/validation/'

samples = []

subFolderList = []
for x in os.listdir(audio_path):
    if os.path.isdir(audio_path + x):
        subFolderList.append(x)

subFolderList_test = []
for y in os.listdir(audio_path):
    if os.path.isdir(audio_path + y):
        subFolderList_test.append(y)

subFolderList_val = []
for k in os.listdir(val_audio_path):
    if os.path.isdir(val_audio_path + k):
        subFolderList_val.append(k)

if not os.path.exists(pict_Path):
    os.makedirs(pict_Path)

if not os.path.exists(test_pict_Path):
    os.makedirs(test_pict_Path)

if not os.path.exists(val_pict_Path):
    os.makedirs(val_pict_Path)

subFolderList = []
for x in os.listdir(audio_path):
    if os.path.isdir(audio_path + x):
        subFolderList.append(x)
        if not os.path.exists(pict_Path + x):
            os.makedirs(pict_Path + x)

subFolderList_test = []
for y in os.listdir(audio_path):
    if os.path.isdir(audio_path + y):
        subFolderList_test.append(y)
        if not os.path.exists(test_pict_Path + y):
            os.makedirs(test_pict_Path + y)

subFolderList_val = []
for k in os.listdir(val_audio_path):
    if os.path.isdir(val_audio_path + k):
        subFolderList_val.append(k)
        if not os.path.exists(val_pict_Path + k):
            os.makedirs(val_pict_Path + k)

sample_audio = []
total = 0
for x in subFolderList:
    
    # get all the wave files
    all_files = [y for y in os.listdir(audio_path + x) if '.wav' in y]
    total += len(all_files)
    # collect the first file from each dir
    sample_audio.append(audio_path  + x + '/'+ all_files[0])
    
    # show file counts
    print('count: %d : %s' % (len(all_files), x ))
print(total)

sample_audio = []
total = 0
for x in subFolderList_test:
    
    # get all the wave files
    all_files = [y for y in os.listdir(test_audio_path + x) if '.wav' in y]
    total += len(all_files)
    # collect the first file from each dir
    sample_audio.append(test_audio_path  + x + '/'+ all_files[0])
    
    # show file counts
    print('count: %d : %s' % (len(all_files), x ))
print(total)

sample_audio = []
total = 0
for x in subFolderList_val:
    
    # get all the wave files
    all_files = [y for y in os.listdir(val_audio_path + x) if '.wav' in y]
    total += len(all_files)
    # collect the first file from each dir
    sample_audio.append(val_audio_path  + x + '/'+ all_files[0])
    
    # show file counts
    print('count: %d : %s' % (len(all_files), x ))
print(total)

def log_specgram(audio, sample_rate, window_size=20,
                 step_size=10, eps=1e-10):
    nperseg = int(round(window_size * sample_rate / 1e3))
    noverlap = int(round(step_size * sample_rate / 1e3))
    freqs, _, spec = signal.spectrogram(audio,
                                    fs=sample_rate,
                                    window='hann',
                                    nperseg=nperseg,
                                    noverlap=noverlap,
                                    detrend=False)
    return freqs, np.log(spec.T.astype(np.float32) + eps)

def wav2img(wav_path, targetdir='', figsize=(4,4)):
    """
    takes in wave file path
    and the fig size. Default 4,4 will make images 288 x 288
    """

    fig = plt.figure(figsize=figsize)    
    # use soundfile library to read in the wave files
    samplerate, test_sound  = wavfile.read(wav_path)
    _, spectrogram = log_specgram(test_sound, samplerate)
    
    ## create output path
    output_file = wav_path.split('/')[-1].split('.wav')[0]
    output_file = targetdir +'/'+ output_file
    plt.imshow(spectrogram.T, aspect='auto', origin='lower')
    plt.imsave('%s.png' % output_file, spectrogram)
    plt.close()

for i, x in enumerate(subFolderList):
    print(i, ':', x)
    # get all the wave files
    all_files = [y for y in os.listdir(audio_path + x) if '.wav' in y]
    for file in all_files[:]:
        wav2img(audio_path + x + '/' + file, pict_Path + x)
print("Done!")

for i, x in enumerate(subFolderList_test):
    print(i, ':', x)
    # get all the wave files
    all_files = [y for y in os.listdir(test_audio_path + x) if '.wav' in y]
    for file in all_files[:]:
        wav2img(test_audio_path + x + '/' + file, test_pict_Path + x)
print("Done!")

for i, x in enumerate(subFolderList_val):
    print(i, ':', x)
    # get all the wave files
    all_files = [y for y in os.listdir(val_audio_path + x) if '.wav' in y]
    for file in all_files[:]:
        wav2img(val_audio_path + x + '/' + file, val_pict_Path + x)
print("Done!")

class mfm(nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size=3, stride=1, padding=1, type=1):
        super(mfm, self).__init__()
        self.out_channels = out_channels
        if type == 1:
            self.filter = nn.Conv2d(in_channels, 2*out_channels, kernel_size=kernel_size, stride=stride, padding=padding)
        else:
            self.filter = nn.Linear(in_channels, 2*out_channels)

    def forward(self, x):
        x = self.filter(x)
        out = torch.split(x, self.out_channels, 1)
        return torch.max(out[0], out[1])

class group(nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size, stride, padding):
        super(group, self).__init__()
        self.conv_a = mfm(in_channels, in_channels, 1, 1, 0)
        self.conv   = mfm(in_channels, out_channels, kernel_size, stride, padding)

    def forward(self, x):
        x = self.conv_a(x)
        x = self.conv(x)
        return x

class resblock(nn.Module):
    def __init__(self, in_channels, out_channels):
        super(resblock, self).__init__()
        self.conv1 = mfm(in_channels, out_channels, kernel_size=3, stride=1, padding=1)
        self.conv2 = mfm(in_channels, out_channels, kernel_size=3, stride=1, padding=1)

    def forward(self, x):
        res = x
        out = self.conv1(x)
        out = self.conv2(out)
        out = out + res
        return out

class network_9layers(nn.Module):
    def __init__(self, num_classes=79077):
        super(network_9layers, self).__init__()
        self.features = nn.Sequential(
            mfm(1, 48, 5, 1, 2), 
            nn.MaxPool2d(kernel_size=2, stride=2, ceil_mode=True), 
            group(48, 96, 3, 1, 1), 
            nn.MaxPool2d(kernel_size=2, stride=2, ceil_mode=True),
            group(96, 192, 3, 1, 1),
            nn.MaxPool2d(kernel_size=2, stride=2, ceil_mode=True), 
            group(192, 128, 3, 1, 1),
            group(128, 128, 3, 1, 1),
            nn.MaxPool2d(kernel_size=2, stride=2, ceil_mode=True),
            )
        self.fc1 = mfm(8*8*128, 256, type=0)
        self.fc2 = nn.Linear(256, num_classes)

    def forward(self, x):
        x = self.features(x)
        x = x.view(x.size(0), -1)
        x = self.fc1(x)
        x = F.dropout(x, training=self.training)
        out = self.fc2(x)
        return out, x

def LightCNN_9Layers(**kwargs):
    model = network_9layers(**kwargs)
    return model

model = LightCNN_9Layers(num_classes=79077)

def load_dataset(data_path):
    import torch
    import torchvision
    import torchvision.transforms as transforms
    # Load all the images
    transformation = transforms.Compose([
        # Randomly augment the image data
            # Random horizontal flip
        transforms.RandomHorizontalFlip(0.5),
            # Random vertical flip
        transforms.RandomVerticalFlip(0.3),
        # transform to tensors
        transforms.ToTensor(),
        # Normalize the pixel values (in R, G, and B channels)
        transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]),
        # Resize Images
        transforms.Resize((128,128)),
        # Transform images to grayscale
        transforms.Grayscale(num_output_channels=1)
    ])

    # Load all of the images, transforming them
    full_dataset = torchvision.datasets.ImageFolder(
        root='Dataset/pics/train',
        transform=transformation
    )
    full_dataset_test  = torchvision.datasets.ImageFolder(
        root='Dataset/pics/test',
        transform=transformation
    )
    
    # Split into training (70% and testing (30%) datasets)
    train_dataset = full_dataset
    test_dataset = full_dataset_test
    
    # use torch.utils.data.random_split for training/test split
    #train_dataset, test_dataset = torch.utils.data.random_split(full_dataset, [train_size, test_size])
    
    # define a loader for the training data we can iterate through in 50-image batches
    train_loader = torch.utils.data.DataLoader(
        train_dataset,
        batch_size=50,
        num_workers=0,
        shuffle=False
    )
    
    # define a loader for the testing data we can iterate through in 50-image batches
    test_loader = torch.utils.data.DataLoader(
        test_dataset,
        batch_size=50,
        num_workers=0,
        shuffle=False
    )
        
    return train_loader, test_loader

# Recall that we have resized the images and saved them into
train_folder = 'Dataset/pics/train'

# Get the iterative dataloaders for test and training data
train_loader, test_loader = load_dataset(train_folder)
batch_size = train_loader.batch_size
print("Data loaders ready to read", train_folder)

def train(model, device, train_loader, optimizer, epoch):
    # Set the model to training mode
    model.train()
    train_loss = 0
    print("Epoch:", epoch)
    # Process the images in batches
    for batch_idx, (data, target) in enumerate(train_loader):
        # Use the CPU or GPU as appropriate
        # Recall that GPU is optimized for the operations we are dealing with
        data, target = data.to(device), target.to(device)
        
        # Reset the optimizer
        optimizer.zero_grad()
        
        # Push the data forward through the model layers
        output = model(data)
        
        # Get the loss
        loss = loss_criteria(output, target)

        # Keep a running total
        train_loss += loss.item()
        
        # Backpropagate
        loss.backward()
        optimizer.step()
        
        # Print metrics so we see some progress
        print('\tTraining batch {} Loss: {:.6f}'.format(batch_idx + 1, loss.item()))
            
    # return average loss for the epoch
    avg_loss = train_loss / (batch_idx+1)
    print('Training set: Average loss: {:.6f}'.format(avg_loss))
    return avg_loss

def test(model, device, test_loader):
    # Switch the model to evaluation mode (so we don't backpropagate or drop)
    model.eval()
    test_loss = 0
    correct = 0
    with torch.no_grad():
        batch_count = 0
        for data, target in test_loader:
            batch_count += 1
            data, target = data.to(device), target.to(device)
            
            # Get the predicted classes for this batch
            output = model(data)
            
            # Calculate the loss for this batch
            test_loss += loss_criteria(output, target).item()
            
            # Calculate the accuracy for this batch
            _, predicted = torch.max(output.data, 1)
            correct += torch.sum(target==predicted).item()

    # Calculate the average loss and total accuracy for this epoch
    avg_loss = test_loss / batch_count
    print('Validation set: Average loss: {:.6f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
        avg_loss, correct, len(test_loader.dataset),
        100. * correct / len(test_loader.dataset)))
    
    # return average loss for the epoch
    return avg_loss

# The images are in a folder named 'input/natural-images/natural_images'
training_folder_name = 'Dataset/pics/train'

# All images are 128x128 pixels
img_size = (128,128)

# The folder contains a subfolder for each class of shape
classes = sorted(os.listdir(training_folder_name))
print(classes)

device = "cpu"
if (torch.cuda.is_available()):
    # if GPU available, use cuda (on a cpu, training will take a considerable length of time!)
    device = "cuda"

# Create an instance of the model class and allocate it to the device
model = LightCNN_9Layers(num_classes=len(classes)).to(device)

print(model)

optimizer = torch.optim.Adam(model.parameters(), lr=0.07)

# Specify the loss criteria
loss_criteria = nn.CrossEntropyLoss()

# Track metrics in these arrays
epoch_nums = []
training_loss = []
validation_loss = []

# Train over 10 epochs (We restrict to 10 for time issues)
epochs = 10
print('Training on', device)
for epoch in range(1, epochs + 1):
        train_loss = train(model, device, train_loader, optimizer, epoch)
        test_loss = test(model, device, test_loader)
        epoch_nums.append(epoch)
        training_loss.append(train_loss)
        validation_loss.append(test_loss)

The Error I am getting:

AttributeError                            Traceback (most recent call last)
<ipython-input-59-6617127f2cd1> in <module>
     13 print('Training on', device)
     14 for epoch in range(1, epochs + 1):
---> 15         train_loss = train(model, device, train_loader, optimizer, epoch)
     16         test_loss = test(model, device, test_loader)
     17         epoch_nums.append(epoch)

<ipython-input-54-f40348c75629> in train(model, device, train_loader, optimizer, epoch)
     17 
     18         # Get the loss
---> 19         loss = loss_criteria(output, target)
     20 
     21         # Keep a running total

~\anaconda3\lib\site-packages\torch\nn\modules\module.py in _call_impl(self, *input, **kwargs)
    887             result = self._slow_forward(*input, **kwargs)
    888         else:
--> 889             result = self.forward(*input, **kwargs)
    890         for hook in itertools.chain(
    891                 _global_forward_hooks.values(),

~\anaconda3\lib\site-packages\torch\nn\modules\loss.py in forward(self, input, target)
   1045     def forward(self, input: Tensor, target: Tensor) -> Tensor:
   1046         assert self.weight is None or isinstance(self.weight, Tensor)
-> 1047         return F.cross_entropy(input, target, weight=self.weight,
   1048                                ignore_index=self.ignore_index, reduction=self.reduction)
   1049 

~\anaconda3\lib\site-packages\torch\nn\functional.py in cross_entropy(input, target, weight, size_average, ignore_index, reduce, reduction)
   2691     if size_average is not None or reduce is not None:
   2692         reduction = _Reduction.legacy_get_string(size_average, reduce)
-> 2693     return nll_loss(log_softmax(input, 1), target, weight, None, ignore_index, None, reduction)
   2694 
   2695 

~\anaconda3\lib\site-packages\torch\nn\functional.py in log_softmax(input, dim, _stacklevel, dtype)
   1670         dim = _get_softmax_dim("log_softmax", input.dim(), _stacklevel)
   1671     if dtype is None:
-> 1672         ret = input.log_softmax(dim)
   1673     else:
   1674         ret = input.log_softmax(dim, dtype=dtype)

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

EDIT: I found that my error has a relation with nn.CrossEntropyLoss() but I still haven’t figured out how to fix it

It looks like your model returns a tuple of both the output and the original input instead of just the output. You might need to manually unpack the output (e.g., do output[0]) before passing it to the loss function.

@eqy, If you mean by output in the train module then it gave me an error as well when I put it before the for loop or inside it

def train(model, device, train_loader, optimizer, epoch):
    # Set the model to training mode
    model.train()
    train_loss = 0
    output[0]
    print("Epoch:", epoch)
    # Process the images in batches
    for batch_idx, (data, target) in enumerate(train_loader):
        # Use the CPU or GPU as appropriate
        # Recall that GPU is optimized for the operations we are dealing with
        data, target = data.to(device), target.to(device)
        
        # Reset the optimizer
        optimizer.zero_grad()
        
        # Push the data forward through the model layers
        output = model(data)
        
        # Get the loss
        loss = loss_criteria(output, target)

        # Keep a running total
        train_loss += loss.item()
        
        # Backpropagate
        loss.backward()
        optimizer.step()
        
        # Print metrics so we see some progress
        print('\tTraining batch {} Loss: {:.6f}'.format(batch_idx + 1, loss.item()))
            
    # return average loss for the epoch
    avg_loss = train_loss / (batch_idx+1)
    print('Training set: Average loss: {:.6f}'.format(avg_loss))
    return avg_loss

I get this error:
UnboundLocalError: local variable 'output' referenced before assignment

If I put it after output = model(data) I get the same tuple error

I mean where you do loss = loss_criteria(output, target) you might want to try loss = loss_criteria(output[0], target) as the loss function likely doesn’t expect the first argument to be a tuple but rather just a single tensor for the predictions.

@eqy, I did change it and now it works. But, I get this output which doesn’t look right

Training on cpu
Epoch: 1
	Training batch 1 Loss: 2.339761
	Training batch 2 Loss: 0.000000
	Training batch 3 Loss: 0.000000
	Training batch 4 Loss: 0.000000
	Training batch 5 Loss: 0.000000
	Training batch 6 Loss: 0.000000
	Training batch 7 Loss: 0.000000
	Training batch 8 Loss: 0.000000
	Training batch 9 Loss: 0.000000

You might want to check if there is a data loading issue or some other problem in the training pipeline. A properly initialized model with 79,077 classes should have an initial cross entropy loss of -ln(1/79077) or about 11.3. You can try passing in random data into your model to see if this is the loss you get. If you do see the correct loss, then it likely means the problem is with data loading somewhere.

@eqy, I tried again with random numbers like 3 and 454545 and also tried 79077, I still get result for the loss, the first one is like 11.3 and the rest is 0, and sometimes when I run it again the whole result becomes 0 for the losses

EDIT: Originally in the code for data loading the part where I load data it should be like this

def load_dataset(data_path):
.
.
.
.
 # Split into training (70% and testing (30%) datasets)
    train_size = int(0.7 * len(full_dataset))
    test_size = len(full_dataset) - train_size

# use torch.utils.data.random_split for training/test split
    train_dataset, test_dataset = torch.utils.data.random_split(full_dataset, [train_size, test_size])
.
.
.
.

where I have changed it to this because I have the images of the test and train data in their own separate folders

def load_dataset(data_path):
.
.
.
.
.
 # Split into training (70% and testing (30%) datasets)
    train_dataset = full_dataset
    test_dataset = full_dataset_test

# use torch.utils.data.random_split for training/test split
# train_dataset, test_dataset = torch.utils.data.random_split(full_dataset, [train_size, test_size])
.
.
.
.
.

EDIT 2: I tried changing the code back to the original form but now the values are way incorrect

Training on cpu
Epoch: 1
	Training batch 1 Loss: 3191074.500000
	Training batch 2 Loss: 46193868800.000000
	Training batch 3 Loss: 360812150784.000000
	Training batch 4 Loss: 154258833408.000000
	Training batch 5 Loss: 3475833356288.000000

EDIT 3: I figured why I was getting the initial value when training was 10 (model = LightCNN_9Layers(num_classes=len(classes)).to(device) classes was 10)) when I thought I had it 79077. I changed it to 79077, and now I got the initial value correct, but I am now still getting the exact same issue for values after the first loss, always 0

If the loss is zero can you check if the model predictions and labels going into the loss function make sense? (e.g., make sure they are not all the same value, etc.)

@eqy, if you mean by doing the evaluation process, I am getting the exact same error when running it

# Defining Labels and Predictions
truelabels = []
predictions = []
model.eval()
print("Getting predictions from test set...")
for data, target in test_loader:
    for label in target.data.numpy():
        truelabels.append(label)
    for prediction in model(data).data.numpy().argmax(1):
        predictions.append(prediction) 

# Plot the confusion matrix
cm = confusion_matrix(truelabels, predictions)
tick_marks = np.arange(len(classes))

df_cm = pd.DataFrame(cm, index = classes, columns = classes)
plt.figure(figsize = (7,7))
sns.heatmap(df_cm, annot=True, cmap=plt.cm.Blues, fmt='g')
plt.xlabel("Predicted Shape", fontsize = 20)
plt.ylabel("True Shape", fontsize = 20)
plt.show()
Getting predictions from test set...
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-140-460554552293> in <module>
      7     for label in target.data.numpy():
      8         truelabels.append(label)
----> 9     for prediction in model(data).data.numpy().argmax([1]):
     10         predictions.append(prediction)
     11 

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

Right, you likely need to unpack the result of model(data) as before.

@eqy, I read more about my error and still lost to where to point it, my issue is related to the loss function. Loss not changing when training · Issue #2711 · keras-team/keras · GitHub
This person had somewhat the same issue as me (they’re using Keras) but their issue that the loss was increasing after they changed the learning rate to 0.1, I did that and nothing has changed for me. Still 0, and also whenever I run it again the first loss is 0 that, if I don’t run the model first again.
Also, I am running into another issue now when training the model,

AttributeError                            Traceback (most recent call last)
<ipython-input-149-00156a3f4784> in <module>
     14 for epoch in range(1, epochs + 1):
     15         train_loss = train(model, device, train_loader, optimizer, epoch)
---> 16         test_loss = test(model, device, test_loader)
     17         epoch_nums.append(epoch)
     18         training_loss.append(train_loss)

<ipython-input-147-0b55539a29e4> in test(model, device, test_loader)
     17 
     18             # Calculate the accuracy for this batch
---> 19             _, predicted = torch.max(output.data, 1)
     20             correct += torch.sum(target==predicted).item()
     21             print(predicted)

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

EDIT: I tried running the code again without changing anything, only change I did now is changed back to the old dataset and num_classes=18620, I am still getting 0 for losses but when I left it to run for a while I get this these values

Training batch 212 Loss: 0.000000
	Training batch 213 Loss: 0.000000
	Training batch 214 Loss: 0.000000
	Training batch 215 Loss: 0.000000
	Training batch 216 Loss: 0.000000
	Training batch 217 Loss: 0.000000
	Training batch 218 Loss: 0.000000
	Training batch 219 Loss: 0.000000
	Training batch 220 Loss: 0.000000
	Training batch 221 Loss: 0.000000
	Training batch 222 Loss: 0.000000
	Training batch 223 Loss: 0.000000
	Training batch 224 Loss: 255631682738539731091456.000000
	Training batch 225 Loss: 198830284639908483039232.000000
	Training batch 226 Loss: 30875532879146954784768.000000
	Training batch 227 Loss: 66122300089016359321600.000000
	Training batch 228 Loss: 57328341773031732936704.000000
	Training batch 229 Loss: 17179621415651750445056.000000
	Training batch 230 Loss: 19574190967024177381376.000000
	Training batch 231 Loss: 30508435466320730914816.000000
	Training batch 232 Loss: 105737439592045019660288.000000
	Training batch 233 Loss: 17526280367469060161536.000000
	Training batch 234 Loss: 78442401272846616625152.000000