How to Fix "Target size must be the same as input size" in BCEWithLogitsLoss
Your target tensor and your logits tensor have different shapes, and nn.BCEWithLogitsLoss refuses to guess how to line them up. The usual pair is a model output of shape (N, 1) against labels of shape (N). Fix it with target = target.unsqueeze(1), or drop the extra dimension from the output with output.squeeze(1), and make sure the target is a float tensor.
One logit, one label, same position. BCEWithLogitsLoss is elementwise, so output (8, 1) pairs with target (8, 1), not (8) and not (1, 8). The dtype of the target must be floating point too.
Check your own shapes
Type the shape your model returns, the shape of your labels, their dtype, and the loss you call. The tool prints the exact exception PyTorch raises, or Valid, plus a fix. Every string comes from running the pair on torch 2.8.0, CPU, and copying the exception out of the traceback.
Runs entirely in your browser. Nothing you type is sent anywhere. The tool assumes a float32 model output. Leave a box empty to mean a 0-dim scalar.
The error
ValueError: Target size (torch.Size([8])) must be the same as input size (torch.Size([8, 1]))
The check lives in torch.nn.functional.binary_cross_entropy_with_logits, so it fires for the module and for the functional call alike. It compares target.size() to input.size() with a plain equality test. There is no broadcasting step before it and no attempt to squeeze away a size-1 dimension. If the two torch.Size objects differ in any way, you get this ValueError.
Note the exception class. It is a ValueError, not a RuntimeError, which matters if you wrapped your training step in except RuntimeError to catch shape bugs.
Why this loss refuses to broadcast
Binary cross entropy with logits is defined per element. Logit number k is scored against label number k, and the result is averaged. A label vector of shape (8) and a logit matrix of shape (8, 1) could broadcast to (8, 8) under the normal tensor rules, and every logit would then be compared against all eight labels. That number is meaningless, so the function checks shape equality first and stops.
nn.MSELoss takes the other path. It broadcasts, emits a UserWarning, and returns a wrong loss that still moves when you train. For the pair (2, 1) against (2) with values 0 and 2 in both tensors, the correct mean squared error is 0.0 and the broadcast version returns 2.0. The strict check in BCEWithLogitsLoss is a feature. It turns a silent wrong number into a loud exception.
Cause 1, output is (N, 1) and target is (N)
A binary classifier usually ends in nn.Linear(hidden, 1), so the logits carry a trailing dimension of size 1. Labels from a DataLoader arrive as a flat vector of length N. The shapes are (8, 1) and (8), and the loss rejects them.
logits = model(x) # torch.Size([8, 1])
target = batch["label"].float() # torch.Size([8])
loss = nn.BCEWithLogitsLoss()(logits, target)
# ValueError: Target size (torch.Size([8])) must be the same as input size (torch.Size([8, 1]))
loss = nn.BCEWithLogitsLoss()(logits, target.unsqueeze(1)) # (8, 1) vs (8, 1)
loss = nn.BCEWithLogitsLoss()(logits.squeeze(1), target) # (8) vs (8)
loss = nn.BCEWithLogitsLoss()(logits, target.view(-1, 1)) # (8, 1) vs (8, 1)
All three fixes ran clean on torch 2.8.0. Pick one and use it everywhere, including in your validation loop and your metric code, so the shapes never drift apart again.
The reversed case raises the same error with the sizes swapped. An output of (8) against a target of (8, 1) gives Target size (torch.Size([8, 1])) must be the same as input size (torch.Size([8])). Same cause, same fixes, opposite direction.
Cause 2, the target is transposed
Reshaping the label with view(1, -1) instead of view(-1, 1) gives (1, 8), which has the right number of elements in the wrong order. MSELoss would silently broadcast this pair to (8, 8). BCEWithLogitsLoss raises.
ValueError: Target size (torch.Size([1, 8])) must be the same as input size (torch.Size([8, 1]))
target = target.reshape(8, 1) # or target.t()
Cause 3, class indices against multi-class logits
People reach for BCEWithLogitsLoss with a three-way classifier because the name sounds like the general classification loss. The logits are (8, 3), the labels are integers in [0, 3) of shape (8), and the loss raises the shape error before it ever looks at the values.
logits = torch.randn(8, 3)
labels = torch.randint(0, 3, (8,))
nn.BCEWithLogitsLoss()(logits, labels)
# ValueError: Target size (torch.Size([8])) must be the same as input size (torch.Size([8, 3]))
Which fix you want depends on the problem. If the classes are mutually exclusive, one label per sample, switch to nn.CrossEntropyLoss()(logits, labels). It takes exactly this pair, (N, C) logits and (N) long indices, and ran clean in the same test. If a sample may carry several labels at once, stay with BCEWithLogitsLoss and expand the target to one column per class:
target = F.one_hot(labels, num_classes=3).float() # torch.Size([8, 3])
loss = nn.BCEWithLogitsLoss()(logits, target)
The .float() is not optional. F.one_hot returns int64, which leads straight to the next error.
Cause 4, the shapes match but the target is long
Once the shapes agree, the kernel needs a floating point target. An int64 label tensor of the right shape raises a different message from the same call.
RuntimeError: result type Float can't be cast to the desired output type Long
An int32 target says desired output type Int. A bool target trips even earlier, inside the 1 - target term, with RuntimeError: Subtraction, the `-` operator, with two bool tensors is not supported. Use the `^` or `logical_xor()` operator instead. The fix is one call in every case:
target = target.float()
A float64 target with float32 logits does not raise. The loss is promoted to float64 and backward() runs, which is fine on CPU and slower than it needs to be on a GPU.
BCELoss and the functional form say the same thing differently
Plain nn.BCELoss checks the same equality and raises a ValueError with older wording that still mentions deprecation:
ValueError: Using a target size (torch.Size([8])) that is different to the input size (torch.Size([8, 1])) is deprecated. Please ensure they have the same size.
Its dtype check is stricter as well. BCELoss wants the target dtype to equal the input dtype, so a float64 target against float32 probabilities raises RuntimeError: Found dtype Double but expected Float, and long, int32 and bool targets raise the same message with Long, Int and Bool. BCELoss also demands that every input value is in [0, 1]. Feed it raw logits and you get RuntimeError: all elements of input should be between 0 and 1. Switching to BCEWithLogitsLoss removes that constraint and the sigmoid call.
F.binary_cross_entropy_with_logits(logits, target) raises the exact same strings as the module for every case in the table below, because the module is a thin wrapper around it.
pos_weight has its own shape rule
pos_weight is broadcast against the target, and the documented contract asks for a tensor with one entry per class. For logits of shape (8, 3), a pos_weight of shape (3) runs. A pos_weight of shape (8), one entry per sample, does not:
RuntimeError: The size of tensor a (8) must match the size of tensor b (3) at non-singleton dimension 1
A 0-dim scalar pos_weight runs, and for (8, 1) logits both shape (1) and shape (8, 1) run. The one to remember is (C), matching the last dimension of the logits.
What PyTorch 2.8.0 actually raises
Every row was produced by running the pair through the loss on torch 2.8.0, CPU, and copying the exception text. The model output is float32 in all rows. The checker at the top of this page reproduces each one.
| Setup | Result |
|---|---|
| BCEWithLogitsLoss, output (8, 1), target (8) float32 | ValueError: Target size (torch.Size([8])) must be the same as input size (torch.Size([8, 1])) |
| BCEWithLogitsLoss, output (8), target (8, 1) float32 | ValueError: Target size (torch.Size([8, 1])) must be the same as input size (torch.Size([8])) |
| BCEWithLogitsLoss, output (8, 1), target (1, 8) float32 | ValueError: Target size (torch.Size([1, 8])) must be the same as input size (torch.Size([8, 1])) |
| BCEWithLogitsLoss, output (8, 3), target (8) long | ValueError: Target size (torch.Size([8])) must be the same as input size (torch.Size([8, 3])) |
| BCEWithLogitsLoss, output (8), target () float32 | ValueError: Target size (torch.Size([])) must be the same as input size (torch.Size([8])) |
| BCEWithLogitsLoss, output (8, 1), target (8, 1) long | RuntimeError: result type Float can't be cast to the desired output type Long |
| BCEWithLogitsLoss, output (8, 1), target (8, 1) int32 | RuntimeError: result type Float can't be cast to the desired output type Int |
| BCEWithLogitsLoss, output (8, 1), target (8, 1) bool | RuntimeError: Subtraction, the `-` operator, with two bool tensors is not supported. Use the `^` or `logical_xor()` operator instead. |
| BCEWithLogitsLoss, output (8, 1), target (8, 1) float32 | Valid |
| BCEWithLogitsLoss, output (8, 1), target (8, 1) float64 | Valid, loss is float64 |
BCEWithLogitsLoss, output (8, 3), target (8, 3) float32 from F.one_hot | Valid |
| F.binary_cross_entropy_with_logits, output (8, 1), target (8) float32 | ValueError: Target size (torch.Size([8])) must be the same as input size (torch.Size([8, 1])) |
| F.binary_cross_entropy_with_logits, output (8, 1), target (8, 1) long | RuntimeError: result type Float can't be cast to the desired output type Long |
| BCELoss, output (8, 1), target (8) float32 | ValueError: Using a target size (torch.Size([8])) that is different to the input size (torch.Size([8, 1])) is deprecated. Please ensure they have the same size. |
| BCELoss, output (8, 1), target (8, 1) long | RuntimeError: Found dtype Long but expected Float |
| BCELoss, output (8, 1), target (8, 1) float64 | RuntimeError: Found dtype Double but expected Float |
| BCELoss, raw logits as input, target (8, 1) float32 | RuntimeError: all elements of input should be between 0 and 1 |
| CrossEntropyLoss, output (8, 3), target (8) long | Valid |
| MSELoss, output (8, 1), target (8) float32 | Runs. UserWarning: Using a target size (torch.Size([8])) that is different to the input size (torch.Size([8, 1])). This will likely lead to incorrect results due to broadcasting. Please ensure they have the same size. |
| MSELoss, output (8, 3), target (8) float32 | RuntimeError: The size of tensor a (3) must match the size of tensor b (8) at non-singleton dimension 1 |
MSELoss, output (8, 1), target (8, 1) long, then backward() | Forward runs. RuntimeError: Found dtype Long but expected Float on backward |
BCEWithLogitsLoss, output (8, 3), pos_weight shape (8) | RuntimeError: The size of tensor a (8) must match the size of tensor b (3) at non-singleton dimension 1 |
Quick reference
| You have | Do this |
|---|---|
| logits (N, 1), labels (N) | target.unsqueeze(1) or output.squeeze(1) |
| logits (N), labels (N, 1) | target.squeeze(1) or output.unsqueeze(1) |
| logits (N, 1), labels (1, N) | target.reshape(N, 1) |
| logits (N, C), integer labels (N), one class per sample | nn.CrossEntropyLoss()(logits, labels) |
| logits (N, C), several labels per sample | F.one_hot(labels, C).float() or a float multi-hot target of shape (N, C) |
| matching shapes, long or int or bool labels | target.float() |
probabilities and nn.BCELoss | drop the sigmoid and use nn.BCEWithLogitsLoss on raw logits |
The documented shape contract is on the PyTorch reference page torch.nn.BCEWithLogitsLoss, which at the time of writing redirects to the 2.14 build of the docs. It lists Input as (*), where * means any number of dimensions, Target as (*), same shape as the input, and describes pos_weight as a tensor with equal size along the class dimension to the number of classes. The error strings themselves appear nowhere in the docs, so treat them as observed behaviour on torch 2.8.0.
Built by Michael Lip. Behaviour verified against torch 2.8.0 on 6 September 2026.