RuntimeError: Expected all tensors to be on the same device, but found at least two devices, cuda:0 and cpu
ID: cuda/tensor-device-mismatch
Version Compatibility
| Version | Status | Introduced | Deprecated | Notes |
|---|---|---|---|---|
| 12 | active | — | — | — |
Root Cause
Model parameters are on GPU but input tensors remain on CPU, or vice versa. Extremely common when .to(device) is not applied consistently to all tensors involved in an operation.
genericWorkarounds
-
96% success Define a single device variable and apply .to(device) to model, inputs, and labels consistently
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') model = model.to(device) inputs = inputs.to(device) labels = labels.to(device)Sources: https://pytorch.org/tutorials/beginner/blitz/cifar10_tutorial.html
-
92% success Use a DataLoader with pin_memory=True and move batches to GPU inside the training loop
loader = DataLoader(dataset, pin_memory=True) for batch in loader: inputs, labels = batch[0].to(device), batch[1].to(device)Sources: https://pytorch.org/docs/stable/data.html#torch.utils.data.DataLoader
-
88% success Check intermediate tensors created inside the model (e.g., positional encodings, masks) are also on the correct device
Inside nn.Module.forward(), use self.weight.device or input.device to create new tensors on the correct device: mask = torch.ones(size, device=input.device)
Sources: https://pytorch.org/docs/stable/notes/cuda.html#best-practices
Dead Ends
Common approaches that don't work:
-
Add .cuda() calls only to the model and not to the input data
90% fail
Both model parameters AND input tensors must be on the same device; moving only the model leaves input tensors on CPU
-
Wrap the entire forward pass in torch.no_grad() hoping to bypass the device check
95% fail
torch.no_grad() only disables gradient computation, it does not change tensor device placement or bypass device validation
-
Convert tensors to numpy and back to force device alignment
85% fail
Tensor.numpy() only works on CPU tensors and adds unnecessary overhead; the real fix is consistent .to(device) usage