"Target and input must have the same number of elements" ERROR for training ConvNet

username: johnvalen1
cooked: <p>Hi guys.</p>
<p>I’m training a convNet on gray-scale 224*224 images with <strong>2 classes</strong>.</p>
<p>This is my convNet:</p>
<pre><code class="lang-auto">class Frequentist_CNN(ModuleWrapper):
    def __init__(self, outputs, inputs):
        super(Frequentist_CNN, self).__init__()

        self.num_classes = outputs
        self.num_channels = inputs

        self.layer1 = nn.Sequential(
            nn.Conv2d(inputs, 6, kernel_size=5, stride=1, padding=2),
            nn.Softplus(),
            nn.MaxPool2d(kernel_size=2, stride=2))
        self.layer2 = nn.Sequential(
            nn.Conv2d(6, 16, kernel_size=5, stride=1, padding=0),
            nn.Softplus(),
            nn.MaxPool2d(kernel_size=2, stride=2))
        self.layer3 = nn.Sequential(
            nn.Conv2d(16, 16, kernel_size=5, stride=1, padding=1),
            nn.Softplus(),
            nn.MaxPool2d(kernel_size=3, stride=2))
        
        self.flatten = FlattenLayer(25)
        self.fc1 = nn.Linear(25, 1000)
        self.act1 = nn.Softplus()
        self.fc2 = nn.Linear(1000, 500)
        self.act2 = nn.Softplus()
        self.fc3 = nn.Linear(500, outputs)

    def forward(self, x):
        out = self.layer1(x)
        out = self.layer2(out)
        out = self.layer3(out)
        # out = out.view(1, -1)
        out = self.fc1(out)
        #out = nn.Softplus(self.fc2(out))
        #out = nn.Softplus(self.fc3(out))
        out = self.fc2(out)
        out = self.fc3(out)
        return out
</code></pre>
<p>And I’m running the training loop:</p>
<pre><code class="lang-auto"># Train the model

freq_net = Frequentist_CNN(num_classes, num_channels)#.to(device)

# Loss and optimizer
criterion = nn.BCELoss()
optimizer = torch.optim.Adam(freq_net.parameters(), lr=0.01)

total_step = len(train_loader)
loss_list = []
acc_list = []
for epoch in range(n_epochs):
    for i, (images, labels) in enumerate(train_loader):
        # Run the forward pass
        print("Images have shape: {}".format(images.shape))
        # images, labels = images.to(device), labels.to(device)
        #labels = labels.unsqueeze(1)
        outputs = freq_net(images)
        #outputs = outputs.argmax(dim=1, keepdim=True)
        print("Outputs have shape: {}".format(outputs.shape))
        print("Labels have shape: {}".format(labels.shape))

        loss = criterion(outputs, labels)
        loss_list.append(loss.item())

        # Backprop and perform Adam optimisation
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        # Track the accuracy
        total = labels.size(0)
        _, predicted = torch.max(outputs.data, 1)
        correct = (predicted == labels).sum().item()
        acc_list.append(correct / total)

        if (i + 1) % 100 == 0:
            print('Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}, Accuracy: {:.2f}%'
                  .format(epoch + 1, num_epochs, i + 1, total_step, loss.item(),
                          (correct / total) * 100))
</code></pre>
<p>Giving me the output AND error:<br>
(Notice the shapes)</p>
<pre><code class="lang-auto">Images have shape: torch.Size([32, 1, 224, 224])
Outputs have shape: torch.Size([32, 16, 25, 2])
Labels have shape: torch.Size([32])


/usr/local/lib/python3.6/dist-packages/torch/nn/modules/loss.py:516: UserWarning: Using a target size (torch.Size([32])) that is different to the input size (torch.Size([32, 16, 25, 2])) is deprecated. Please ensure they have the same size.
  return F.binary_cross_entropy(input, target, weight=self.weight, reduction=self.reduction)

...

ValueError: Target and input must have the same number of elements. target nelement (32) != input nelement (25600)
</code></pre>
<p>As you can see, I’ve tried unsqueezing labels, training on the GPU… I’ve also tried CrossEntropyLoss(). That didn’t work either.</p>
<p>Does anybody have any idea why this may be happening?</p>
<p>Thanks so much! <img src="https://discuss.pytorch.org/images/emoji/apple/slight_smile.png?v=9" title=":slight_smile:" class="emoji" alt=":slight_smile:"><br>
John</p>

username: ptrblck
cooked: <p>It seems you are not flattening the activations in your model before feeding them to the linear layers, so I would recommend to uncomment the <code>view</code> operation and change it to <code>out = out.view(out.size(0), -1)</code>.</p>
<p>Also, you are not using activation functions between the linear layers.</p>
<p><code>nn.BCELoss</code> expects a probability input, so you should either apply <code>sigmoid</code> on the output or use <code>nn.BCEWithLogitsLoss</code>.</p>
<p>However, based on the target shape it seems you are dealing with a multi-class classification with <code>output</code> classes.<br>
If that’s the case, you should use <code>nn.CrossEntropyLoss</code> instead.<br>
The model output should have the shape <code>[batch_size, nb_classes]</code> and the target <code>[batch_size]</code> containing the class indices in the range <code>[0, nb_classes-1]</code>.</p>

username: johnvalen1
cooked: <p>Hi! Thanks for all that! What ended up working was <code>nn.CrossEntropyLoss()</code> along with <code>out = out.view(out.size(0), -1)</code> instead of my original <code>out = out.view(1, -1)</code>.</p>
<p>Now… I have a new problem, although it’s slightly unrelated.<br>
Basically, I suspect that <strong>my model isn’t properly saving and loading</strong>.</p>
<p>I run the training loop where I define the directory to save to:</p>
<pre><code class="lang-auto"># Train the model

freq_net = Frequentist_CNN(num_classes, num_channels).to(device)

# Loss and optimizer
# criterion = nn.BCEWithLogitsLoss()
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(freq_net.parameters(), lr=0.01)

# Where to save the model:
freq_ckpt_dir = 'saved_model/COVID/model_custom/'
# File name
freq_ckpt_name = 'freq_COVID_CNN.pt'

path_freq_CNN = freq_ckpt_dir + freq_ckpt_name

# If it doesn't exist, make it.
if not os.path.exists(freq_ckpt_dir):
    os.makedirs(freq_ckpt_dir, exist_ok=True)     


total_step = len(train_loader)
loss_list = []
acc_list = []
# Train
print("Training.....")
for epoch in range(n_epochs):
    for i, (images, labels) in enumerate(train_loader):
        print("Batch: \t{}".format(i+1))
        # Run the forward pass
        #print("Images have shape: {}".format(images.shape))
        images, labels = images.to(device), labels.to(device)
        #labels = labels.unsqueeze(1)
        outputs = freq_net(images).to(device)
        #outputs = outputs.argmax(dim=1, keepdim=True)
        #print("Outputs have shape: {}".format(outputs.shape))
        #print("Labels have shape: {}".format(labels.shape))

        loss = criterion(outputs, labels)
        loss_list.append(loss.item())

        # Backprop and perform Adam optimisation
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        # Track the accuracy
        total = labels.size(0)
        _, predicted = torch.max(outputs.data, 1)
        correct = (predicted == labels).sum().item()
        acc_list.append(correct / total)

        if (i + 1) % 4 == 0:
            print('Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}, Accuracy: {:.2f}%'
                  .format(epoch + 1, n_epochs, i + 1, total_step, loss.item(),
                          (correct / total) * 100))
            

        # Save.
        torch.save(freq_net.state_dict(), path_freq_CNN)  
print("Done training.")
</code></pre>
<p>And it gets this performance after 10 epochs:</p>
<pre><code class="lang-auto">Epoch [10/10], Step [4/5], Loss: 0.8934, Accuracy: 78.12%
</code></pre>
<p>But now I go <strong>load it</strong> and <strong>test</strong>:</p>
<pre><code class="lang-auto"># Test the model

freq_net.load_state_dict(torch.load(path_freq_CNN))


freq_net.eval()
with torch.no_grad():
    correct = 0
    total = 0
    for images, labels in test_loader:
        images, labels = images.to(device), labels.to(device)
        outputs = freq_net(images).to(device)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

    print('Test Accuracy of the model on the 60 test images: {} %'.format((correct / total) * 100))

</code></pre>
<p>Only to see:</p>
<pre><code class="lang-auto">Test Accuracy of the model on the 60 test images: 50.0 %
</code></pre>
<p>My test-set is balanced, so it seems to be outputting just one class every time.<br>
Is the problem that I’m not saving/loading correctly? It seems my <code>test_loader</code> is defined correctly, so that can’t be it…</p>
<p>Thanks so much!</p>

username: ptrblck
cooked: <p>I think your saving and loading is correct.<br>
However, it seems you are comparing the training with the validation accuracy, so I would recommend to add a validation loop before saving and compare it to the restored model.</p>

username: johnvalen1
cooked: <p>Hi, I’m not sure I understand…</p>
<p>In the first case, I am training and getting a training accuracy of 78.12%. I save the model.</p>
<p>I then load it, and test it on my test data. It seems to be getting 50.0% every time (balanced binary test set, so it seems to be outputting the same thing every time, perhaps).</p>
<p>I did not include a validation set because the dataset is so small… so I did training and testing only.</p>

username: ptrblck
cooked: <p>Sorry for the confusion.<br>
I just think that your storing and loading might be alright.<br>
You could add the testing loop in the first script, store the model, load it in the other script and retest.<br>
If both test scores are ~50%, then the serialization is alright and your model might be overfitting.</p>