C.W.K.
Stream
Lesson 03 of 11 · published

Shape, Dtype, Device — The Three Numbers

~12 min · dtype, device, shape, metadata

Level 0Tensor Curious
0 XP0/62 lessons0/13 achievements
0/120 XP to next level120 XP to go0% complete

Every tensor carries three pieces of identity

When you call print(t), PyTorch shows you the values. When something is broken, the values rarely matter — the metadata does. Three attributes, in order of debugging importance:

  1. shape — the dimensions. The first thing to check on any error.
  2. dtype — the element type. The second thing to check, especially with mixed-precision training.
  3. device — where the data physically lives. The "expected cuda:0 got cpu" error.

Internalize the dtype family because mixed-precision training is now the default for serious work:

  • torch.float32 (alias torch.float) — the universal default for activations and weights.
  • torch.float16 (alias torch.half) — half the memory, half the bandwidth, but a narrow dynamic range that requires a GradScaler.
  • torch.bfloat16 — same exponent range as float32, less mantissa precision. The modern mixed-precision dtype on Ampere+ GPUs and Apple Silicon. No GradScaler usually needed.
  • torch.float64 (double) — full precision. Use for scientific computing or when you suspect numerical issues, never as a default for training.
  • torch.int64 (long) — the default for class labels, indices, embedding lookups.
  • torch.bool — masks.

Casting

.to() is the universal mover/caster. It accepts a dtype, a device, or both, and is a no-op if the tensor is already in the requested form. There are also short forms (.float(), .half(), .cpu(), .cuda()) — use whichever reads cleanest in context.

Code

The three identity reads·python
import torch

t = torch.randn(8, 3, 224, 224, device="cpu")

print(t.shape)    # torch.Size([8, 3, 224, 224])
print(t.size())   # same thing — both shape and size() exist
print(t.ndim)     # 4
print(t.numel())  # 8*3*224*224 = 1,204,224

print(t.dtype)    # torch.float32
print(t.device)   # cpu
print(t.layout)   # torch.strided (contiguous memory, default)
Casting with .to() and the shorthands·python
import torch

t = torch.randn(3, 3)

# dtype only
t_half = t.half()                  # → float16
t_bf16 = t.bfloat16()              # → bfloat16
t_long = t.long()                  # → int64
t_float64 = t.to(torch.float64)    # → double

# device only
t_gpu = t.to("cuda")               # if available
t_cpu = t_gpu.to("cpu")
t_mps = t.to("mps")                # Apple Silicon

# both at once
t_gpu_half = t.to("cuda", dtype=torch.float16)

# Critically: .to() is a NO-OP if already in the requested form,
# so it's safe to call defensively.
assert t.to(t.dtype, t.device) is not None
Mixed dtype = error — fix at the boundary·python
import torch
import torch.nn as nn

# A common bug: float64 data colliding with a float32 model.
data = torch.tensor([[1.0, 2.0]], dtype=torch.float64)
linear = nn.Linear(2, 4)  # weights are float32 by default
# linear(data)  # RuntimeError: expected scalar type Float but got Double

# The fix at the boundary — once, where data enters the model
data = data.to(dtype=torch.float32)
out = linear(data)
print(out.shape)  # torch.Size([1, 4])

External links

Exercise

Pick a model you've trained before (or build a 3-layer MLP). Print the dtype of: model parameters, optimizer state, a single batch of input, and the loss. Verify they're all the same dtype. Then cast the input to float64 and observe the error message — read it carefully. That same error will show up in the wild.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.