1. What is a Tensor?¶
In PyTorch, a Tensor is the fundamental unit for storing and manipulating data. It is similar to NumPy arrays but supports GPU acceleration and serves as the core data structure for neural network computations. Think of a tensor as a “container” holding numbers (integers, floats, etc.) on which you can perform various mathematical operations.
2. Creating Tensors¶
To start working with tensors, you need to learn how to create them. PyTorch provides multiple ways to create tensors, with the most common methods listed below:
1. From Python Lists/NumPy Arrays¶
Use torch.tensor() or torch.as_tensor() to directly create tensors from data:
import torch
import numpy as np
# From a Python list
data_list = [1, 2, 3, 4]
tensor_list = torch.tensor(data_list) # Output: tensor([1, 2, 3, 4])
# From a NumPy array
np_array = np.array([1, 2, 3])
tensor_np = torch.as_tensor(np_array) # Output: tensor([1, 2, 3])
2. Using Constructor Functions¶
torch.zeros(*size): Creates a tensor filled with zeros (e.g.,(2, 3)for a 2x3 matrix).torch.ones(*size): Creates a tensor filled with ones.torch.rand(*size): Creates a tensor with random values in[0, 1):
zeros_tensor = torch.zeros(2, 3) # 2x3 matrix of zeros
ones_tensor = torch.ones(1, 4) # 1x4 matrix of ones
rand_tensor = torch.rand(3, 3) # 3x3 matrix of random numbers
3. Basic Tensor Properties¶
After creating a tensor, understanding its properties is essential for subsequent operations:
- Shape: Use .shape or .size() to view the tensor’s dimensions (returns a tuple).
- Data Type (dtype): Use .dtype to check the data type (e.g., torch.float32 by default, torch.int64).
- Device: Use .device to check which device the tensor is on (default: CPU; can be moved to GPU with .to('cuda')).
tensor = torch.rand(2, 3)
print(tensor.shape) # torch.Size([2, 3])
print(tensor.dtype) # torch.float32
print(tensor.device) # cpu
# Convert data type and move to GPU
tensor = tensor.to(torch.float64).to('cuda') # First convert to double, then transfer to GPU
4. Tensor Operations¶
Tensor operations are central to PyTorch; mastering them enables flexible data manipulation.
1. Arithmetic Operations¶
Tensors support common arithmetic operations:
- Addition: + or torch.add()
- Subtraction: - or torch.sub()
- Multiplication: * or torch.mul() (matrix multiplication uses @ or torch.matmul())
- Division: / or torch.div()
a = torch.tensor([1, 2, 3])
b = torch.tensor([4, 5, 6])
# Addition
print(a + b) # tensor([5, 7, 9])
print(torch.add(a, b)) # Same as above
# Matrix multiplication (2x2 * 2x2)
a = torch.tensor([[1, 2], [3, 4]])
b = torch.tensor([[5, 6], [7, 8]])
print(a @ b) # tensor([[19, 22], [43, 50]])
2. Indexing and Slicing¶
Tensors support indexing and slicing similar to Python lists, with multidimensional support:
tensor = torch.tensor([[1, 2, 3], [4, 5, 6]])
# First row (dimension 0)
print(tensor[0]) # tensor([1, 2, 3])
# Second column (dimension 1)
print(tensor[:, 1]) # tensor([2, 5])
# Rows 0-2 and columns 0-1
print(tensor[0:2, 0:2]) # tensor([[1, 2], [4, 5]])
3. Reshaping Operations¶
These operations adjust tensor dimensions while preserving the total number of elements:
- reshape(): Changes the shape (raises error if dimensions are invalid).
- squeeze(): Removes axes of size 1 (e.g., (1, 3, 1) → (3,)).
- unsqueeze(): Adds a new axis of size 1 (e.g., (3,) → (1, 3)).
tensor = torch.rand(1, 2, 1, 4) # Shape: (1, 2, 1, 4)
print(tensor.shape) # torch.Size([1, 2, 1, 4])
# Remove size-1 dimensions
squeezed = tensor.squeeze() # Shape: (2, 4)
print(squeezed.shape) # torch.Size([2, 4])
# Add a size-1 dimension at position 1
unsqueezed = tensor.unsqueeze(1) # Shape: (1, 1, 2, 1, 4)
print(unsqueezed.shape) # torch.Size([1, 1, 2, 1, 4])
4. Concatenation and Splitting¶
torch.cat(): Concatenates tensors along a specified dimension (requires matching other dimensions).torch.stack(): Adds a new dimension to concatenate (requires all dimensions to match).torch.split()/torch.chunk(): Splits tensors into parts.
a = torch.tensor([[1, 2], [3, 4]])
b = torch.tensor([[5, 6], [7, 8]])
# Concatenate along dimension 0 (vertical stacking)
cat_dim0 = torch.cat([a, b], dim=0) # Shape: (4, 2)
print(cat_dim0)
# Concatenate along dimension 1 (horizontal stacking)
cat_dim1 = torch.cat([a, b], dim=1) # Shape: (2, 4)
print(cat_dim1)
# Split tensor into 2 parts of size 2 along dimension 0
split_tensor = torch.cat([a, b], dim=0)
split_parts = torch.split(split_tensor, split_size_or_sections=2, dim=0)
print(split_parts) # (tensor([[1,2],[3,4]]), tensor([[5,6],[7,8]]))
5. Automatic Differentiation (Autograd)¶
Autograd is PyTorch’s core for backpropagation, enabling automatic gradient computation.
1. Key Concepts¶
requires_grad: A tensor property; set toTrueto track gradients.- Computational Graph: Records the flow of operations to enable gradient computation.
backward(): Triggers gradient calculation for leaf nodes in the graph.grad: The gradient of a tensor (only valid forrequires_grad=Truetensors).
2. Autograd Example¶
Using the function y = x² to demonstrate gradient computation:
# Create a tensor to track gradients
x = torch.tensor(3.0, requires_grad=True) # x=3.0, track gradients
# Build the computational graph: y = x²
y = x ** 2
# Backpropagate to compute gradients
y.backward()
# Check gradient: dy/dx = 2x = 6
print(x.grad) # tensor(6.)
3. Important Notes¶
- Non-leaf gradients: Gradients of intermediate variables are released after
backward()(useretain_graph=Trueto retain). - Gradient accumulation: Multiple
backward()calls accumulate gradients; reset withx.grad.zero_(). detach(): Detaches a tensor from the computation graph (returns a gradient-free tensor).
x = torch.tensor(2.0, requires_grad=True)
y = x ** 2
z = y ** 2 # z = (x²)² = x⁴
# First backward pass (y = x²)
y.backward()
print(x.grad) # tensor(4.) (dy/dx = 2x = 4)
# Second backward pass (z = x⁴)
z.backward(retain_graph=True)
print(x.grad) # tensor(16.) (4 + 12 = 16; dy/dx = 4, dz/dx = 4x³ = 32? Wait, correction: z = y², y = x² → dz/dx = 2y*2x = 4xy = 4*2*2=16. Yes.)
6. Summary¶
- Tensors are the fundamental data structure in PyTorch, supporting creation, arithmetic, indexing, reshaping, and more.
- Autograd enables automatic gradient computation via
requires_gradandbackward(), critical for training neural networks. - Key considerations: shape matching, gradient accumulation, and device management (CPU/GPU).
With this foundation, you’re ready to explore more advanced PyTorch concepts like neural networks and optimization!