Lesson 5

Hello and welcome! Today, we will delve deeper into the world of tensor processing in PyTorch by discussing and implementing the crucial concepts of **Linear Layers** and **Activation Functions**.

When working with tensors in neural networks, it is essential to understand that they are processed through various layers. A layer in a neural network refers to a collection of neurons (nodes) operating together at the same depth level within the network. PyTorch provides us with the `torch.nn`

module, an easy and powerful tool for creating and organizing these layers.

A vital part of most neural networks is the linear layer, which performs a linear transformation on its input data. A linear layer operates via the formula:

$y = Wx + b$

Where $y$ is the output, $W$ represents the weight matrix, $x$ is the input vector, and $b$ indicates the bias vector. The weight matrix scales the input data, and the bias vector then shifts it, thereby producing the output.

One of the powerful aspects of linear layers is their ability to transform the shape of the output as desired. By specifying the number of input and output features, you can control the dimensions of the tensor output from the layer. This flexibility allows the neural network to adapt to a variety of input shapes and deliver outputs that fit the requirements of the subsequent layers in the network.

In PyTorch, we commonly create these linear layers using the `nn.Linear()`

function. Let's create such a layer, which will have 2 input features and 3 output features.

Python`1import torch 2import torch.nn as nn 3 4# Define an input tensor with specific values 5input_tensor = torch.tensor([[1.0, 2.0]], dtype=torch.float32) 6 7# Create a linear layer with 2 input features and 3 output features 8layer = nn.Linear(in_features=2, out_features=3) 9 10# Process the input through the linear layer to get initial output 11output_tensor = layer(input_tensor) 12 13# Display the original input tensor 14print(f"Input Tensor:\n{input_tensor}\n") 15 16# Display the output before activation to see the linear transformation effect 17print(f"Output Tensor Before Activation:\n{output_tensor}\n")`

The output of the above code will depend on the initial weights and biases, but an example might look like:

Plain text`1Input Tensor: 2tensor([[1., 2.]]) 3 4Output Tensor Before Activation: 5tensor([[ 0.2392, -1.2186, -0.9254]], grad_fn=<AddmmBackward0>)`

The output tensor displayed above results from passing the input tensor through the linear layer. This layer applies a weighted sum of the input tensor values and adds a bias term to each output. The weights and biases are initialized randomly, so the exact output can vary. The `grad_fn=<AddmmBackward0>`

in the output means that PyTorch is keeping track of this operation, which will help compute the gradients automatically during model training.

Activation functions introduce non-linearity into the model, enabling it to handle more complex patterns in the data. Two commonly used activation functions are ReLU (Rectified Linear Unit) and Sigmoid.

Mathematically, ReLU is represented as:

$f(x) = \max(0, x)$

Where $x$ is the input to the function. The ReLU function ensures that positive input values remain unchanged, while negative ones are transformed to zero, creating a non-linear transformation.

The Sigmoid function, on the other hand, is represented as:

$\sigma(x) = \frac{1}{1 + e^{-x}}$

Where $x$ is the input. The Sigmoid function squashes the input value to lie between 0 and 1, which can be useful for binary classification tasks. However, in practice, ReLU is often preferred over Sigmoid for hidden layers due to its simplicity and performance benefits.

We can define a ReLU activation function in PyTorch using the `nn.ReLU()`

function from the `torch.nn`

module. Then, it can be applied to the output tensor from our linear layer.

Python`1# Define a ReLU activation function to introduce non-linearity 2relu = nn.ReLU() 3 4# Apply the ReLU function to the output of the linear layer 5activated_output_relu = relu(output_tensor) 6 7# Display the output after activation to observe the effect of ReLU 8print(f"Output Tensor After ReLU Activation:\n{activated_output_relu}")`

The output tensor after activation will look as follows:

Plain text`1Output Tensor After ReLU Activation: 2tensor([[0.2392, 0.0000, 0.0000]], grad_fn=<ReluBackward0>)`

The output tensor after activation demonstrates the effect of the ReLU function. It zeroes out any negative values, converting them to zero, while keeping positive values unchanged. This introduces non-linearity into the model, which is crucial for handling more complex patterns in the data. The `grad_fn=<ReluBackward0>`

shows that the ReLU operation is also being tracked for automatic differentiation during training.

Similarly, we can define and apply a Sigmoid activation function in PyTorch using the `nn.Sigmoid()`

function from the `torch.nn`

module.

Python`1# Define a Sigmoid activation function 2sigmoid = nn.Sigmoid() 3 4# Apply the Sigmoid function to the output of the linear layer 5activated_output_sigmoid = sigmoid(output_tensor) 6 7# Display the output after applying the Sigmoid function 8print(f"Output Tensor After Sigmoid Activation:\n{activated_output_sigmoid}")`

The output tensor after applying the Sigmoid activation will look as follows:

Plain text`1Output Tensor After Sigmoid Activation: 2tensor([[0.5595, 0.2282, 0.2839]], grad_fn=<SigmoidBackward0>)`

The output tensor after activation shows the effect of the Sigmoid function. It squashes the input values to lie between 0 and 1. This can be particularly useful in scenarios where you want to interpret the output as probabilities. The `grad_fn=<SigmoidBackward0>`

indicates that the Sigmoid operation is tracked for automatic differentiation during training.

Great job! Today, we explored the concept of tensor processing through Linear Layers and Activation Functions in PyTorch. Through a practical code exercise, we learned how to use these two functions in combination to transform and process an input tensor.

In the next set of exercises, you will have the opportunity to apply these concepts yourself actively. This practice will be crucial to solidifying your understanding of these concepts and your ability to process tensors effectively as you move forward in building more complex neural network architectures in PyTorch. Keep it up!