Tensors in PyTorch : Part-2
Table of Content
Overview of Tensors in PyTorch
In our previous blog, "Understanding Tensors in PyTorch - Part 1" we explored the basics of tensors, including their importance, various methods of creation, and the different data types they support. In this blog, we will delve deeper into tensor operations. We will cover essential mathematical operations, as well as the concepts of vectorization and broadcasting, which enhance the computational power of tensors. Additionally, we will demonstrate how to seamlessly switch between CPU and GPU in PyTorch, further optimizing your tensor computations. Let's continue our journey into the world of PyTorch tensors!
Maths and Logic with Tensor Pytorch
Now that you know how to create tensors, let's dive into what you can actually do with them. The fun starts with basic arithmetic operations and how tensors interact with simple scalars.
Vectorization
Let's begin with some fundamental operations:
As shown, arithmetic operations like addition, subtraction,
multiplication, division,
and exponentiation apply element-wise to the
tensor. The result of each operation is a new tensor, which means you can chain
operations together, following the usual operator precedence rules.
But what about operations between two tensors? They behave just as you would
expect:
Notice that all tensors in these examples share the same shape. But what happens if
we try to perform operations on tensors of different shapes?
Here's a hint: it doesn't go well. The
following
example throws a runtime error on
purpose:
In general, you can not perform binary operations on tensors with different shapes, even if they contain the same number of elements. This concept is crucial to understanding tensor operations and avoiding common pitfalls in your PyTorch projects.
Note: For vectorization two tensors must have the same shape.
Tensor Broadcasting
The exception to the same-shapes rule is tensor broadcasting. Here’s an example:
What is the trick here? How
did
we multiply a 2x4 tensor by a 1x4
tensor?
Broadcasting allows you to perform operations between
tensors that have compatible shapes. In the example above, the
one-row,
four-column tensor is multiplied by both rows of the two-row, four-column
tensor.
This operation is essential in deep learning, especially when
multiplying learning weights by a batch of input tensors, applying the operation
to
each instance in the batch separately, and returning a tensor of identical
shape.
The rules for broadcasting are:
1. Each tensor must have at least one dimension—no empty tensors.
2. When comparing dimension sizes of the two tensors from last to first:
-
Each dimension must be equal, or
-
One of the dimensions must be of size 1, or
-
The dimension does not exist in one of the tensors
Tensors of identical shape, of course, are trivially “broadcastable,” as seen earlier.
More Maths with Tensor
PyTorch tensors have over three hundred operations that can be performed on them.
Altering Tensors in Place
When you perform operations on tensors, they usually create
new tensors. For example, if you have c = a * b (where a and b are
tensors),
c will be a new tensor stored in a different memory location from a and b.
However, sometimes you may want to modify a tensor directly, especially
if
you
don't need to keep the original values. PyTorch provides in-place operations for
this
purpose. These functions have an underscore (_) at the
end
of
their names.
Here's an example:
These in-place arithmetic
functions are methods of
the torch.Tensor object, not the torch module.
For example, a.add_(b) modifies a directly.
Another way to place the result of a computation in an existing tensor is by
using the out argument.
Many PyTorch
functions
and
methods, including tensor creation methods, have this argument.
Here's an example:
Using in-place operations and the out argument helps manage memory more efficiently by reusing existing tensors. This can be especially important in deep learning applications where memory usage is critical.
Copying Tensor:
When you assign a tensor to a variable in Python, you are not creating a new copy of the tensor; you are just creating a new reference to the same tensor. For example:
In this case, modifying a also changes b because both variables point to the same tensor. But what if you need an actual copy? That's where the clone() method comes in:
Using clone(), b is a separate tensor with the same data as a, but changes to a don't affect b.
Important Note on clone() and Autograd
If your source tensor has autograd enabled, the cloned tensor will also have autograd enabled. This can be useful if both the original tensor and its clone are involved in a model's forward pass and need to track gradients for learning. However, there are situations where you might want to disable autograd for the clone to save on performance. For this, you can use the detach() method.
The detach() method effectively removes the tensor from its computation history, allowing you to perform operations without tracking gradients, which can improve performance in certain cases.
Using GPU with PyTorch
One of the key benefits of PyTorch is its ability to use Nvidia GPUs for faster computation. Nvidia GPUs use a technology called CUDA (Compute Unified Device Architecture) to handle many calculations at once, making them much faster than CPUs for certain tasks.
Here’s how you can take advantage of this:
1. Check for GPU Availability
Before you can use a GPU, you need to check if your system has one available. You can do this with the torch.cuda.is_available() method:
2.Creating Tensors on the GPU
By default, PyTorch creates tensors on the CPU. To create a tensor directly on the GPU, you can specify the device argument:
3. Handling Multiple GPUs
If you have more than one GPU, you can select which one to use by specifying its index. Use torch.cuda.device_count() to check how many GPUs are available:
4. Using a Device Handle
Instead of using hardcoded strings, it is better to use a device handle. This way, your code can automatically use the GPU if available, or fall back to the CPU if not:
5. Moving Existing Tensors to a Different Device
If you already have a tensor and want to move it to a different device (e.g., from CPU to GPU), you can use the to() method:
6. Ensure Tensors Are on the Same Device6.Ensure Tensors Are on the Same Device
When performing operations with multiple tensors, they must all be on the same device. Otherwise, you’ll get an error:
Changing Tensor Shape in PyTorch
Sometimes you need to change the shape of a tensor to fit your needs. Here are some common scenarios and how to handle them:
Adding and Removing Dimensions
Adding Dimensions:
PyTorch models often expect inputs in batches. For instance, if your model works with images of shape (3, 226, 226) (3 color channels, 226x226 pixels), it expects the input shape to be (N, 3, 226, 226), where N is the number of images in the batch. To create a batch with just one image, you need to add an extra dimension:
Removing Dimensions:
If you have a tensor with unnecessary dimensions of size 1, you can remove them with the squeeze() method:
If you need to remove a dimension that is not of size 1, squeeze() won’t change the shape:
Reshaping Tensors
To change a tensor's shape while keeping the same number of elements, use reshape():
When using reshape(), PyTorch tries to return a view of the original tensor if possible. This means changes to the original tensor will affect the reshaped tensor. To avoid this, you can use clone() to make a copy.
Conclusion
Tensors are the fundamental building blocks in PyTorch, offering a versatile way to handle and manipulate data. From understanding basic operations to managing tensor shapes, mastering tensors is crucial for efficient and effective use of PyTorch in your deep learning projects.
Key Takeaways:
- Creating Tensors: PyTorch provides flexible methods to create tensors with different dimensions and data types, allowing you to start building your models right away.
- Manipulating Shapes: Techniques like unsqueeze() and squeeze() help you adjust tensor dimensions to meet the requirements of your models or specific computations. Using reshape() enables you to transform tensors while preserving their data.
- Tensor Operations: PyTorch supports a wide range of operations, from simple arithmetic to complex transformations. Knowing how to perform these operations, both in-place and out-of-place, is essential for efficient computation and memory management.
- GPU Acceleration: PyTorch seamlessly integrates with CUDA-enabled GPUs to accelerate tensor computations. Ensuring your tensors are on the appropriate device helps leverage the full power of your hardware.
By understanding and utilizing these tensor operations, you can optimize your workflow, debug more effectively, and enhance the performance of your deep learning models. Continue to explore and experiment with tensors in PyTorch to fully harness their capabilities and elevate your machine learning projects.
Stay tuned for our next installment, where we will introduce you to the basics of building a Neural Network from scratch in our upcoming topic, “Neural Network from Scratch"!
Written By
Impetus Ai Solutions
Impetus is a pioneer in AI and ML, specializing in developing cutting-edge solutions that drive innovation and efficiency. Our expertise extends to product engineering, warranty management, and building robust cloud infrastructures. We leverage advanced AI and ML techniques to provide state-of-the-art technological and IT-related services, ensuring our clients stay ahead in the digital era.