torch.tensor(): creating from data
Why this matters
Every PyTorch computation starts with a tensor. You need to know how to convert your data into tensors with the right dtype and device before you can run any model, loss function, or optimization step.
Explanation
torch.tensor() takes Python data (lists, nested lists, scalars, or NumPy arrays) and wraps it into a PyTorch tensor object.
Mechanically, it allocates memory (on CPU by default), copies your data into that memory, and returns a tensor object that knows its shape, dtype, device, and whether gradients should be tracked. The tensor becomes the fundamental unit of computation in PyTorch: it's what flows through neural networks.
Use torch.tensor() when you're building small test tensors, creating ground truth labels, or initializing data that originated as Python structures. For large data pipelines, consider torch.from_numpy() or DataLoaders instead, as they're more efficient.
Analogy
Think of torch.tensor() as packaging raw ingredients (Python list) into a labeled container (tensor) that knows where it lives (CPU/GPU), what it contains (dtype), and whether it's marked for inspection (requires_grad). Once packaged, the container can be processed by kitchen equipment (neural networks) that only understands labeled containers.
Code
import torch
import numpy as np
list_data = [1, 2, 3, 4]
tensor_from_list = torch.tensor(list_data)
print(f"From list: {tensor_from_list}")
print(f"Shape: {tensor_from_list.shape}")
print(f"Dtype: {tensor_from_list.dtype}")
print(f"Device: {tensor_from_list.device}")
print()
nested_list = [[1.0, 2.0], [3.0, 4.0]]
tensor_from_nested = torch.tensor(nested_list)
print(f"From nested list: {tensor_from_nested}")
print(f"Shape: {tensor_from_nested.shape}")
print()
scalar = torch.tensor(5)
print(f"Scalar tensor: {scalar}")
print(f"Is 0-d: {scalar.dim() == 0}")
print()
np_array = np.array([10, 20, 30])
tensor_from_numpy = torch.tensor(np_array)
print(f"From NumPy: {tensor_from_numpy}")
print()
tensor_with_dtype = torch.tensor([1, 2, 3], dtype=torch.float32)
print(f"Explicit dtype: {tensor_with_dtype}")
print(f"Dtype is float32: {tensor_with_dtype.dtype == torch.float32}")
print()
tensor_with_requires_grad = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)
print(f"With requires_grad: {tensor_with_requires_grad}")
print(f"Requires grad: {tensor_with_requires_grad.requires_grad}") From list: tensor([1, 2, 3, 4])
Shape: torch.Size([4])
Dtype: torch.int64
Device: cpu
From nested list: tensor([[1., 2.],
[3., 4.]])
Shape: torch.Size([2, 2])
Scalar tensor: tensor(5)
Is 0-d: True
From NumPy: tensor([10, 20, 30])
Explicit dtype: tensor([1., 2., 3.])
Dtype is float32: True
With requires_grad: tensor([1., 2., 3.], requires_grad=True)
Requires grad: True What just happened?
The code created five different tensors: one from a Python list (int64 by default), one from a 2D nested list (float32 because of decimal points), one scalar (0-dimensional), one from a NumPy array, one with explicit float32 dtype, and one marked to track gradients. Each tensor printed with its internal structure and properties. torch.tensor() inferred dtype from the input data (integers → int64, floats → float32) unless we specified it explicitly.
Common gotcha
torch.tensor() creates a copy of your input data. If you pass a NumPy array and then modify the NumPy array, the tensor stays unchanged. Conversely, if you later modify the tensor, the NumPy array won't update. Many beginners expect them to be linked. Use torch.from_numpy() if you want a zero-copy view instead (but then they stay linked and you can't move to GPU without breaking the link).
Error recovery
TypeErrorRuntimeError: expected scalar typeValueError: only one element tensors can be converted to Python scalarsExperienced dev note
In production pipelines, avoid torch.tensor() for large datasets: it's slow for high-dimensional data and copies memory unnecessarily. Use torch.from_numpy() with pinned memory (pin_memory=True in DataLoader) or memory-mapped files instead. Also: torch.tensor() defaults to requiring gradients=False, but if you're building a custom training loop and forget to set requires_grad=True on learnable parameters, your backprop will silently fail. Use torch.nn.Parameter() for model weights instead: it sets requires_grad=True automatically.
Check your understanding
If you load a NumPy array with dtype float64 into a tensor using torch.tensor(), then create another tensor from a Python list of floats, why might their dtypes differ and what's the safest way to ensure consistency?
Show answer hint
A correct answer recognizes that torch.tensor() infers dtype from input (NumPy's float64 stays float64, Python floats become torch.float32 by default), and the safest approach is explicit dtype specification on all tensor creation calls, or always cast: torch.tensor(data, dtype=torch.float32).