Skip to content

Theano - Shared Variables

Modern Deep Learning Frameworks - Parameters and State

Section titled “Modern Deep Learning Frameworks - Parameters and State”

In deep learning, models contain parameters (often called weights and biases) that need to be updated during training and persist across different function calls or training batches. For example, when training a neural network, the weight matrices are adjusted iteratively based on the computed gradients. These parameters must be accessible and modifiable throughout the training process.

Modern frameworks like PyTorch and TensorFlow provide mechanisms to handle this state efficiently. Instead of a specific ‘shared variable’ construct like in the older Theano library, parameters are typically represented as tensors that are specifically marked as requiring gradient computation. These frameworks also automatically handle moving these tensors to accelerators like GPUs when available, significantly speeding up computations.

In PyTorch, learnable parameters are usually instances of torch.nn.Parameter, which is a subclass of torch.Tensor. When assigned as an attribute to a torch.nn.Module, they are automatically registered as model parameters and tracked for gradient calculations.

Alternatively, you can create a standard tensor and explicitly state that it requires gradients using the requires_grad=True argument during creation. This is fundamental for automatic differentiation (autograd).

import torch
# Create a tensor representing parameters (e.g., weights)
# requires_grad=True indicates that we need to compute gradients for this tensor during backpropagation
W = torch.tensor([0.1, 0.25, 0.15, 0.3], requires_grad=True)
# For parameters within a model, torch.nn.Parameter is often preferred:
# W_param = torch.nn.Parameter(torch.tensor([0.1, 0.25, 0.15, 0.3]))

You can access the data of a tensor directly using its .data attribute. Modifying the tensor’s data can be done in-place. During training, parameter updates are typically handled by optimizers (like SGD or Adam) which modify the parameters based on their computed gradients.

import torch
# Initial parameter tensor
W = torch.tensor([0.1, 0.25, 0.15, 0.3], requires_grad=True)
print("Original W:", W)
print("Original W data:", W.data)
# Check gradient status
print("Requires gradient?", W.requires_grad)
# Modify the tensor's data in-place
new_values = torch.tensor([0.5, 0.2, 0.4, 0.2])
with torch.no_grad(): # Disable gradient tracking for manual updates
W.data[:] = new_values
print("\nSetting new values (0.5, 0.2, 0.4, 0.2)")
print("After modification W:", W)
print("After modification W data:", W.data)
# Note: In a typical training loop, updates are done via optimizers:
# optimizer.step() # This would update W based on W.grad

The torch.no_grad() context manager is used here to temporarily disable gradient calculation when performing manual, in-place updates to the tensor’s data. This prevents these operations from being tracked by the autograd engine, which is usually desired for direct parameter manipulation outside the standard optimization step.

Modern frameworks make it easy to leverage GPUs. In PyTorch, you can move tensors (including parameters) to a specific device (like a CUDA-enabled GPU) using the .to() method.

# Check if CUDA (GPU support) is available
if torch.cuda.is_available():
device = torch.device("cuda")
else:
device = torch.device("cpu")
print(f"Using device: {device}")
# Move the tensor W to the selected device
W_gpu = W.to(device)
print("W on device:", W_gpu.device)
# Operations involving W_gpu will now run on the GPU (if available)

This seamless integration with hardware accelerators is a key advantage of modern deep learning libraries, enabling the training of large models on massive datasets.