What this usually means
These errors occur when tensor operations (like matrix multiplication, addition, or loss functions) receive tensors whose dimensions are incompatible. In PyTorch, most operations require the number of dimensions and the size of each dimension (except specific broadcastable ones) to match. The error message tells you the mismatched dimension and its expected vs actual sizes. Common causes include a wrong input size from a data loader, a model forward pass that reshapes incorrectly, or a loss function expecting specific shapes (e.g., CrossEntropyLoss expects raw logits, not softmax output).
The first ten minutes — establish facts before touching code.
- 1Read the error message: note the expected size and actual size, and the dimension index. That is the first clue.
- 2Add print(model) and print(input.shape) right before the failing operation to see the tensor shapes flowing through.
- 3Insert torch.set_anomaly_enabled(True) at the top of your script to get a stack trace with the exact line.
- 4Use the Python debugger (pdb) or torch.jit.trace to isolate the operation that fails.
- 5Check your loss function: verify that the model output shape matches what the loss expects (e.g., CrossEntropyLoss expects (N, C) for logits and (N) for targets).
- 6Run a forward pass with a batch of size 1 to see if the issue is related to batching logic.
The specific files, logs, configs, and dashboards that usually own this bug.
- searchThe exact line in the error traceback, especially the torch function call (e.g., torch.mm, F.cross_entropy).
- searchYour model's forward() method: check every reshape, view, permute, and linear layer.
- searchThe data loader output: verify batch shapes with next(iter(dataloader)).
- searchLoss function documentation: confirm expected shapes for your specific loss (e.g., BCELoss vs BCEWithLogitsLoss).
- searchAny custom collate_fn if using a DataLoader.
- searchPyTorch issue tracker for known shape bugs in specific versions.
Practical causes, not theory. These are the things you will actually find.
- warningMismatch between model output channels and number of classes in loss function (e.g., model outputs 10, loss expects 1000).
- warningIncorrect use of view() or reshape() that flattens unintended dimensions.
- warningData loader returns batches with different sizes due to variable-length inputs without proper padding.
- warningForgetting to unsqueeze or squeeze dimensions for operations expecting specific ndim.
- warningUsing softmax or sigmoid before a loss function that expects raw logits (e.g., CrossEntropyLoss internally applies softmax).
- warningBroadcasting bugs: adding tensors of shapes (N, 1, H, W) and (N, H, W) thinking they match.
- warningOff-by-one errors in the number of features in Linear layers after a flatten.
Concrete fix directions. Pick the one that matches your root cause.
- buildAdd explicit shape assertions in the forward method using assert x.shape == (batch, channels, h, w), f"Expected shape ... got {x.shape}".
- buildUse x = x.view(batch, -1) only when you are certain of the total element count; prefer torch.flatten starting from dim=1.
- buildFor variable-length inputs, pad sequences to the same length using pad_sequence or pad_packed_sequence.
- buildSwitch to the correct loss function: use BCEWithLogitsLoss instead of BCELoss if you haven't applied sigmoid.
- buildWrap your model with torch.jit.script to get a graph with explicit shapes for debugging.
- buildUse the --shape option in torch.onnx.export to trace shapes.
- buildReshape tensors with .unsqueeze() and .squeeze() explicitly rather than relying on broadcasting.
A fix you cannot prove is a guess. Close the loop.
- verifiedRun the same batch through the model with torch.inference_mode() and assert all intermediate shapes match expected.
- verifiedWrite a unit test that feeds a dummy input of the expected shape and asserts the output shape.
- verifiedMonitor the shapes in a forward hook: register_forward_hook to log shapes of each layer.
- verifiedRun the training loop for a few steps and confirm the loss is non-NaN and decreasing.
- verifiedCompare the shapes of model output and target using torch.allclose after reshaping if needed.
- verifiedUse torch.jit.load and torch.jit.script to verify the model runs without errors.
Things that make this bug worse or harder to find.
- warningDon't ignore the dimension index in the error message; it points to the exact axis that mismatches.
- warningDon't blindly reshape tensors without understanding the memory layout; use permute and contiguous when needed.
- warningDon't assume the data loader returns the same shape every time; always check for variable-length batches.
- warningDon't use torch.Tensor.reshape() as a fix without verifying the total number of elements.
- warningDon't disable anomaly detection after one fix; keep it on until you're confident all shape issues are resolved.
- warningDon't fix shape errors by adding try-except; always understand why the shape changed.
The 10-class model that thought it had 1000 classes
Timeline
- 09:00New model branch merged; training crashes immediately with shape mismatch on CrossEntropyLoss.
- 09:05Read error: 'Expected input batch_size (32) to match target batch_size (32)' — seems like batch size mismatch, but both are 32.
- 09:10Check model output shape: torch.Size([32, 1000]). That's the problem: ResNet-18 default has 1000 classes, but CIFAR-10 has 10.
- 09:12Realize the model was loaded from an ImageNet pretrained checkpoint without adjusting the final layer.
- 09:15Replace model.fc with nn.Linear(512, 10) and re-run.
- 09:20Training starts, but loss is NaN after 100 iterations. Something else is wrong.
- 09:25Check target shape: torch.Size([32]) — that's correct for CrossEntropyLoss. Check model output: still shape [32, 10].
- 09:30Check data normalization: images not normalized; large pixel values cause exploding gradients. Added normalization transform.
- 09:35Training runs successfully; loss converges normally.
I merged a new branch that upgraded our model from a simple CNN to ResNet-18. The first training run crashed immediately with a shape mismatch error on the loss function. The error said 'Expected input batch_size (32) to match target batch_size (32)', which was confusing because both were 32. I printed the shapes and saw the model output was [32, 1000] — 1000 classes, but CIFAR-10 only has 10. The ResNet checkpoint had been pretrained on ImageNet, and I'd forgotten to replace the final fully connected layer.
After swapping model.fc to output 10, the training started but the loss was NaN. I checked the target shape and it was fine, but the model output had reasonable values. I realized the data wasn't normalized — pixel values were 0-255, which caused the gradients to explode. I added transforms.Normalize with ImageNet stats and the loss became stable.
The root cause was two-fold: first a shape mismatch due to a wrong final layer, then a data normalization issue that only appeared after fixing the shape. The lesson: always check both model architecture and data preprocessing when debugging shape errors, as they can cascade.
Root cause
ResNet-18 final fully connected layer outputs 1000 classes (ImageNet default) instead of 10 for CIFAR-10.
The fix
Replace model.fc with nn.Linear(512, 10) and add proper image normalization.
The lesson
Shape mismatch errors often mask deeper issues like data preprocessing. Always verify both model output shape and input data range.
PyTorch shape mismatch errors always include the dimension index and the expected vs actual sizes. For example: 'The size of tensor a (64) must match the size of tensor b (32) at non-singleton dimension 1'. This means at dimension 1, tensor a has size 64 and tensor b has size 32. The 'non-singleton' part means dimensions of size 1 are broadcastable, so the mismatch is on a non-broadcastable dimension. Always note the dimension index — it's the axis that's wrong.
Common patterns: For matrix multiplication, the error says 'mat1 and mat2 shapes cannot be multiplied (X x Y and Z x W)'. This tells you that the last dimension of mat1 (Y) must equal the second-to-last dimension of mat2 (Z). For loss functions, the error often includes 'Expected input batch_size (N) to match target batch_size (N)'. This usually means the first dimension (batch) of the prediction and target tensors differ, perhaps due to an extra dimension or a reduction operation.
PyTorch follows NumPy broadcasting rules: two tensors are compatible if their dimensions are equal or one of them is 1. A common mistake is adding tensors of shapes (N, 1, H, W) and (N, H, W) — these are not broadcastable because dimension 1 has size 1 vs H (assuming H != 1). The fix is to unsqueeze the second tensor to (N, 1, H, W) or squeeze the first. Another pitfall is when a tensor has an extra dimension of size 1 that you forgot to squeeze, causing a mismatch in a subsequent operation.
To debug broadcasting issues, print both shapes and use torch.broadcast_tensors to see what shape they would broadcast to. If the broadcast fails, it raises an error with the dimension mismatch. Also, be careful with operations that reduce dimensions (like sum or mean) — they may remove a dimension, breaking later operations that expect the original number of dimensions.
A frequent source of shape mismatches is the DataLoader returning batches of varying sizes, especially when dealing with variable-length sequences, images of different sizes, or custom collate functions. The default collate_fn stacks tensors along a new batch dimension, but if the tensors have different shapes, it fails. The error may be cryptic: 'stack expects each tensor to be equal size, but got ...' The fix is to use a custom collate_fn that pads or resizes inputs to a common shape.
Another issue is when the dataset __getitem__ returns a tuple with inconsistent shapes — for example, an image and its label, but the image is transformed differently each time (random crop). Always ensure that your transforms produce consistent output shapes across all items in a batch. Use a fixed size or resize to a constant shape. You can also use torch.utils.data.default_collate and catch errors early with a for loop checking shapes.
Each loss function in PyTorch expects specific input and target shapes. CrossEntropyLoss expects input of shape (N, C) where C is the number of classes, and target of shape (N) with class indices. A common mistake is passing a one-hot encoded target (shape N, C) instead of indices. Similarly, BCELoss expects input and target both of shape (N, *) where * can be any number of dimensions, but the input must be probabilities (after sigmoid). If you use BCEWithLogitsLoss, the input should be raw logits without sigmoid.
For segmentation losses like Dice loss or custom losses, shape mismatches often occur because of channel dimensions. For example, a segmentation mask might have shape (N, H, W) while the model output has shape (N, C, H, W). You need to convert the mask to one-hot or use an appropriate loss that handles class indices. Always check the documentation and print shapes immediately before the loss call.
Frequently asked questions
Why does my model run fine with batch size 32 but fail with batch size 64?
This is often due to a hardcoded dimension somewhere in the model, like a view() that assumes a specific batch size. For example, x.view(32, -1) will fail for any batch size other than 32 because the total number of elements must be divisible by the first dimension. Replace hardcoded dimensions with -1 for the batch dimension: x.view(x.size(0), -1). Also check for layers like nn.BatchNorm that behave differently during training vs eval; they don't cause shape errors but can cause inconsistencies.
What does 'mat1 and mat2 shapes cannot be multiplied' mean exactly?
It means you are trying to perform a matrix multiplication (torch.mm or torch.matmul) between two tensors where the inner dimensions don't match. For 2D tensors, mat1 must have shape (a, b) and mat2 (b, c). The error tells you the shapes: e.g., (64, 128) and (256, 10) fails because 128 != 256. This often happens after a flatten operation: you flattened the wrong dimensions or the number of features in a Linear layer is wrong.
How do I debug shape mismatches in a custom loss function?
Add print statements or use torch.set_anomaly_enabled(True) to get a trace. Inside the loss function, print the shapes of all tensors at the start. Use assertions to check dimensions. If the loss uses broadcasting, explicitly reshape tensors to the same shape before operations. Also, test the loss with dummy tensors of known shapes to isolate the issue from the model.
Is it safe to use reshape instead of view to fix shape errors?
reshape is more permissive than view because it can handle non-contiguous tensors by creating a copy. However, it can hide bugs: if you reshape a tensor to a shape that doesn't match the total number of elements, it will raise an error. More dangerously, it can silently change the memory layout, causing performance issues or subtle bugs in later operations. Use view when you are sure the tensor is contiguous and the shape is correct; use reshape only when you need to handle non-contiguous tensors, but always verify the total elements match.