Fix PyTorch RuntimeError: Expected Scalar Type Long but Found Float
If you are training a classification model in PyTorch and your training loop suddenly crashes with
RuntimeError: expected scalar type Long but found Float, you are in the right place.
This is one of the most common dtype errors beginners encounter, and it always has the same root cause:
your loss function expects integer class indices, but it is receiving floating-point values.
The fix is a single method call.
Contents
- The exact error message (both variants)
- Root cause: CrossEntropyLoss needs torch.long
- The one-line fix: labels.long()
- BCELoss vs CrossEntropyLoss dtype requirements
- Embedding layer: the same error, a different context
- Full dtype table for all common loss functions
- Debugging snippet: print dtypes before the forward pass
- Related fixes
1. The Exact Error Message (Both Variants)
Depending on your PyTorch version, you will see one of these two messages in your traceback:
Modern PyTorch (≥1.8)
RuntimeError: expected scalar type Long but found Float
Older PyTorch (<1.8)
RuntimeError: Expected object of scalar type Long but got scalar type Float
for argument #2 'target' in call to _thnn_nll_loss_forward
Both messages point to the same problem. A full traceback typically looks like this:
Traceback (most recent call last):
File "train.py", line 34, in <training loop>
loss = criterion(outputs, labels)
File ".../torch/nn/modules/loss.py", line 1179, in forward
return F.cross_entropy(input, target, weight=self.weight,
File ".../torch/nn/functional.py", line 3029, in cross_entropy
return torch._C._nn.cross_entropy_loss(input, target, ...)
RuntimeError: expected scalar type Long but found Float
The key line is loss = criterion(outputs, labels). PyTorch is telling you that
labels arrived as a torch.float32 tensor when the loss function
required torch.int64 (also called torch.long).
2. Root Cause: CrossEntropyLoss Expects Class Indices as torch.long
nn.CrossEntropyLoss computes the negative log-likelihood over a set of class
probabilities. Internally it calls torch.nll_loss, which uses each element of the
target tensor as an integer index into the output dimension. Integer indexing must
use a 64-bit integer type — torch.int64, aliased as torch.long in
PyTorch's C++ backend.
The most common reason your labels end up as float32 instead of int64:
- You loaded them from a CSV or NumPy array without specifying a dtype, so they defaulted to
float64orfloat32. - You used
torch.tensor([0, 1, 2])on a Python list that happened to be inferred as float. - You applied a normalisation step (e.g., dividing by the number of classes) that converted the integer tensor to float.
- Your
Dataset.__getitem__returnsnp.float32arrays for both inputs and targets, so the DataLoader converts everything to float tensors. - You used
label_binarizeor one-hot encoding and forgot to convert back to class indices.
3. The One-Line Fix: labels.long()
Call .long() on your target tensor before passing it to the loss function.
That is the entire fix.
import torch
import torch.nn as nn
criterion = nn.CrossEntropyLoss()
# Suppose labels arrived as float (common when loaded from CSV/numpy)
labels = torch.tensor([0, 2, 1, 3], dtype=torch.float32)
print(labels.dtype) # torch.float32 ← wrong for CrossEntropyLoss
# One-line fix
labels = labels.long()
print(labels.dtype) # torch.int64 ← correct
outputs = torch.randn(4, 4) # batch_size=4, num_classes=4
loss = criterion(outputs, labels) # works without error
print(loss.item())
You can also apply the cast directly in your training loop to keep things concise:
for inputs, labels in dataloader:
inputs = inputs.to(device)
labels = labels.long().to(device) # <-- cast here, before loss
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
Alternatively, fix the dtype at the source — in your Dataset — so you never
have to think about it again:
import numpy as np
import torch
from torch.utils.data import Dataset
class MyDataset(Dataset):
def __init__(self, X, y):
self.X = torch.tensor(X, dtype=torch.float32)
self.y = torch.tensor(y, dtype=torch.long) # enforce long here
def __len__(self):
return len(self.y)
def __getitem__(self, idx):
return self.X[idx], self.y[idx]
.long() is equivalent to .to(torch.int64) and
.type(torch.LongTensor). They all do the same thing; .long() is the
shortest and most idiomatic form.
4. BCELoss vs CrossEntropyLoss Dtype Requirements (Common Confusion)
A frequent source of confusion is switching between binary and multi-class classification. The two loss functions have opposite requirements for the target tensor:
- CrossEntropyLoss — targets must be
torch.long(integer class indices, shape[batch_size]) - BCELoss / BCEWithLogitsLoss — targets must be
torch.float32(probabilities or binary 0/1 values, same shape as the output)
import torch
import torch.nn as nn
batch_size = 8
num_classes = 5
# ── Multi-class: CrossEntropyLoss ──────────────────────────────
logits_ce = torch.randn(batch_size, num_classes)
targets_ce = torch.randint(0, num_classes, (batch_size,)) # dtype=torch.long automatically
criterion_ce = nn.CrossEntropyLoss()
loss_ce = criterion_ce(logits_ce, targets_ce) # OK
# ── Binary: BCEWithLogitsLoss ──────────────────────────────────
logits_bce = torch.randn(batch_size, 1)
targets_bce = torch.randint(0, 2, (batch_size, 1)).float() # must cast to float!
criterion_bce = nn.BCEWithLogitsLoss()
loss_bce = criterion_bce(logits_bce, targets_bce) # OK
# ── Common mistake: using float labels with CrossEntropyLoss ──
bad_targets = torch.randint(0, num_classes, (batch_size,)).float() # accidentally float
# criterion_ce(logits_ce, bad_targets) # <-- RuntimeError!
criterion_ce(logits_ce, bad_targets.long()) # fixed with .long()
If you are migrating a binary classifier to multi-class (or vice versa), double-check which loss function you are using and flip the target dtype accordingly.
5. Embedding Layer Index Type (Same Error, Different Context)
You will encounter the exact same RuntimeError: expected scalar type Long but found Float
when using nn.Embedding. An embedding layer maps integer token indices to dense
vectors — so its input must also be torch.long.
import torch
import torch.nn as nn
vocab_size = 1000
embed_dim = 64
embedding = nn.Embedding(vocab_size, embed_dim)
# Token indices should be long
indices_wrong = torch.tensor([4, 17, 42, 0], dtype=torch.float32)
# embedding(indices_wrong) # RuntimeError: expected scalar type Long but found Float
indices_correct = indices_wrong.long()
output = embedding(indices_correct) # works: shape [4, 64]
print(output.shape)
The fix is identical: call .long() on the index tensor before passing it to the
embedding layer. This is especially easy to forget when token ids come from a tokeniser that
returns NumPy arrays — those default to int32 on some platforms, which is still
the wrong type; PyTorch requires int64.
import numpy as np
# numpy default integer is int64 on Linux/macOS but int32 on some Windows builds
token_ids_np = np.array([4, 17, 42, 0])
token_ids = torch.from_numpy(token_ids_np).long() # safe on all platforms
output = embedding(token_ids)
6. Full Dtype Table for Common PyTorch Loss Functions
Use this table as a quick reference whenever you are not sure which dtype to use for inputs and targets.
| Loss Function | Input (predictions) dtype | Target dtype | Notes |
|---|---|---|---|
nn.CrossEntropyLoss |
float32 | int64 (long) | Targets are class indices, shape [N]. Also accepts class probabilities as float when using soft targets (PyTorch ≥1.10). |
nn.NLLLoss |
float32 | int64 (long) | Input should be log-probabilities (apply log_softmax first). Same index requirement as CrossEntropyLoss. |
nn.BCELoss |
float32 | float32 | Binary classification. Input must be in [0,1] (apply sigmoid first). Target values are 0.0 or 1.0. |
nn.BCEWithLogitsLoss |
float32 | float32 | Combines sigmoid + BCELoss in one numerically stable step. Preferred over BCELoss for training. |
nn.MSELoss |
float32 | float32 | Regression. Input and target must have the same shape and dtype. No integer targets. |
nn.L1Loss |
float32 | float32 | Mean absolute error. Same shape requirements as MSELoss. Often used for robust regression. |
A handy rule of thumb: if the loss function is counting discrete class indices (CrossEntropyLoss,
NLLLoss), targets must be long. For everything else — regression, binary, or
probability outputs — targets are float32.
7. Debugging Snippet: Print Dtype of Every Tensor Before the Forward Pass
When you are not sure which tensor has the wrong dtype, add a quick diagnostic block before the forward pass. This prints the name, shape, and dtype of every tensor you are about to use and makes the culprit obvious.
def debug_dtypes(**tensors):
"""Print name, shape, and dtype for each tensor. Call before forward pass."""
print("=" * 55)
for name, t in tensors.items():
if hasattr(t, "dtype"):
print(f" {name:20s} shape={str(tuple(t.shape)):20s} dtype={t.dtype}")
else:
print(f" {name:20s} (not a tensor, type={type(t).__name__})")
print("=" * 55)
# Usage inside your training loop:
for inputs, labels in dataloader:
debug_dtypes(inputs=inputs, labels=labels)
# After confirming dtypes, comment out the line above to speed up training
labels = labels.long()
outputs = model(inputs)
loss = criterion(outputs, labels)
Example output when labels are accidentally float:
=======================================================
inputs shape=(32, 3, 224, 224) dtype=torch.float32
labels shape=(32,) dtype=torch.float32 <-- wrong!
=======================================================
After applying labels.long():
=======================================================
inputs shape=(32, 3, 224, 224) dtype=torch.float32
labels shape=(32,) dtype=torch.int64 <-- correct
=======================================================
You can also check dtypes inline without a helper function using PyTorch's built-in
.dtype attribute and assertions:
assert inputs.dtype == torch.float32, f"inputs dtype mismatch: {inputs.dtype}"
assert labels.dtype == torch.long, f"labels dtype mismatch: {labels.dtype}"
Adding assertions like these at the top of your training loop — and removing them once the model is stable — is a lightweight form of data validation that catches dtype bugs the moment they appear rather than after hours of debugging.
Other common dtype-related gotchas to watch for
-
Mixed precision (
torch.cuda.amp): When using automatic mixed precision, model outputs may befloat16. The loss function will upcast them internally, but make sure your loss criterion itself remains infloat32(it does by default when you useGradScaler). -
Double precision models: If you call
model.double(), your input tensors also need to befloat64. Labels still staylong. -
Segmentation masks: Pixel-wise segmentation uses
CrossEntropyLosswith 2D targets of shape[N, H, W]— each pixel value is a class index and must belong, not float. -
One-hot targets:
CrossEntropyLossexpects class indices (a 1D tensor of integers), not one-hot encoded vectors. If you pass a one-hot float tensor, you get a shape error on top of the dtype error. Convert back to class indices withlabels = one_hot_labels.argmax(dim=1).long().
8. Related Fixes
If you fixed this error but are now running into other PyTorch issues during training, these guides cover the most common next problems:
Found a mistake or have a question? The examples on this page were tested on PyTorch 2.x. The dtype behaviour described here has been consistent since PyTorch 1.6.