Deep Learning PyTorch Course, Overview of PyTorch

Deep learning is a field of machine learning that uses artificial neural networks to process and learn from data. In recent years, much of machine learning has evolved into deep learning technologies, demonstrating their potential in various fields such as data analysis, image recognition, and natural language processing.

1. What is PyTorch?

PyTorch is an open-source machine learning library developed by the Facebook AI Research (FAIR). PyTorch has gained popularity among researchers and developers for developing deep learning models due to its natural and intuitive approach. This is mainly due to the following reasons:

  • Flexibility: PyTorch uses a dynamic computation graph, allowing for free modification of the model structure. This enables flexible model design.
  • User-friendly: With its intuitive API design, it provides a familiar environment for Python users.
  • GPU support: It can process large datasets using GPUs and operates at high speed.

2. Key Features of PyTorch

Some key features of PyTorch include:

2.1. Dynamic Graphs

PyTorch uses a dynamic computation graph in a “Define-by-Run” manner. This graph is constructed at runtime, making debugging easier during model development.

2.2. Tensors

The basic data structure in PyTorch is the tensor. A tensor is a multi-dimensional array that is very similar to a NumPy array but can perform operations using GPUs. Tensors are crucial for storing data of various sizes and shapes.

2.3. Autograd

PyTorch provides an Autograd feature that automatically calculates the derivatives of all operations. This simplifies model training through backpropagation.

3. Installing PyTorch

Installing PyTorch is very straightforward. You can install it using the following command:

pip install torch torchvision torchaudio

This command installs PyTorch, torchvision, and torchaudio. The torchvision library is useful for image processing, while torchaudio is used to handle audio data.

4. Basic Usage of PyTorch

Let’s take a look at basic tensor operations in PyTorch. The following example shows how to create tensors and perform basic operations:


import torch

# Create tensors
tensor_a = torch.tensor([[1, 2], [3, 4]])
tensor_b = torch.tensor([[5, 6], [7, 8]])

# Tensor addition
result_add = tensor_a + tensor_b

# Tensor multiplication
result_mul = torch.matmul(tensor_a, tensor_b)

print("Tensor A:\n", tensor_a)
print("Tensor B:\n", tensor_b)
print("Addition Result:\n", result_add)
print("Multiplication Result:\n", result_mul)
    

4.1. Creating Tensors

The code above shows how to create two 2×2 tensors. It performs basic addition and multiplication using the previously created tensors.

4.2. Tensor Operations

Operations between tensors are very intuitive and support most linear algebra operations. Running the code above produces the following results:


Tensor A:
 tensor([[1, 2],
        [3, 4]])
Tensor B:
 tensor([[5, 6],
        [7, 8]])
Addition Result:
 tensor([[ 6,  8],
        [10, 12]])
Multiplication Result:
 tensor([[19, 22],
        [43, 50]])
    

5. Building a PyTorch Model

The process of building a deep learning model with PyTorch proceeds through the following steps:

  1. Data preparation
  2. Model definition
  3. Loss function and optimizer definition
  4. Training loop
  5. Validation and testing

5.1. Data Preparation

First, we start with data preparation. Below is the code to load the MNIST dataset:


from torchvision import datasets, transforms

# Define data transformations
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5,), (0.5,))
])

# Download the MNIST dataset
train_data = datasets.MNIST(root='data', train=True, download=True, transform=transform)
test_data = datasets.MNIST(root='data', train=False, download=True, transform=transform)
    

5.2. Model Definition

Defining a neural network model is done by inheriting from the nn.Module class. Below is an example of defining a simple fully connected neural network:


import torch.nn as nn
import torch.nn.functional as F

class SimpleNN(nn.Module):
    def __init__(self):
        super(SimpleNN, self).__init__()
        self.fc1 = nn.Linear(28 * 28, 128)
        self.fc2 = nn.Linear(128, 10)

    def forward(self, x):
        x = x.view(-1, 28 * 28)  # Flatten the input
        x = F.relu(self.fc1(x))
        x = self.fc2(x)
        return x
    

5.3. Loss Function and Optimizer Definition

The loss function and optimizer are essential elements for model training:


import torch.optim as optim

model = SimpleNN()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
    

5.4. Training Loop

The training loop for the model can be defined as follows:


from torch.utils.data import DataLoader

train_loader = DataLoader(train_data, batch_size=64, shuffle=True)

# Training loop
for epoch in range(5):  # 5 epochs
    for data, target in train_loader:
        optimizer.zero_grad()  # Zero the gradient
        output = model(data)   # Model prediction
        loss = criterion(output, target)  # Calculate loss
        loss.backward()        # Backpropagation
        optimizer.step()       # Update parameters
    print(f'Epoch {epoch+1}, Loss: {loss.item()}')
    

5.5. Validation and Testing

After training, the model can be evaluated with the test data to check its performance:


test_loader = DataLoader(test_data, batch_size=64, shuffle=False)

correct = 0
total = 0

with torch.no_grad():
    for data, target in test_loader:
        output = model(data)
        _, predicted = torch.max(output.data, 1)
        total += target.size(0)
        correct += (predicted == target).sum().item()

print(f'Accuracy: {100 * correct / total}%')
    

6. Conclusion

In this article, we explained an overview of PyTorch and its basic usage. PyTorch is a very useful tool for deep learning research and development, and its flexibility and powerful features have made it popular among many researchers and engineers. In the next lecture, we will cover various advanced topics and practical applications using PyTorch. Stay tuned!

Deep Learning PyTorch Course, Feature Map Visualization

In deep learning, it is important to understand how models learn from data. In particular,
feature maps are low-dimensional data generated in the intermediate layers of neural networks,
which serve as important indicators of what features the model is extracting from the input data.
In this article, we will explain in detail how to visualize feature maps using PyTorch,
and we will understand the process through hands-on practice.

1. What is a feature map?

A feature map refers to the output that is mapped through various filters generated by a convolutional neural network (CNN).
Each filter plays a role in detecting specific patterns or features in the input image, visually expressing
what information the model is learning in this process.

2. Why visualize feature maps?

The reasons for visualizing feature maps are as follows:

  • To understand the model’s decision-making and enhance interpretability.
  • To determine how sensitive the model is to specific features.
  • To provide insights for error analysis and model improvement.

3. Installing required libraries

To visualize feature maps, PyTorch and a few additional libraries are needed.
You can install the necessary libraries using the command below.

pip install torch torchvision matplotlib

4. Preparing the dataset

We will use the MNIST dataset to process handwritten digit images. To do this,
we will load the data using the datasets module from torchvision.


import torch
import torchvision.transforms as transforms
from torchvision import datasets
from torch.utils.data import DataLoader

# Define transformations
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5,), (0.5,))
])

# Download MNIST dataset
mnist_data = datasets.MNIST(root='./data', train=True, transform=transform, download=True)
data_loader = DataLoader(mnist_data, batch_size=64, shuffle=True)
    

5. Building a simple CNN model

We will build a simple CNN model to classify images. The model consists of the following:
Convolutional Layer -> ReLU -> Max Pooling -> Fully Connected Layer


import torch.nn as nn
import torch.nn.functional as F

class SimpleCNN(nn.Module):
    def __init__(self):
        super(SimpleCNN, self).__init__()
        self.conv1 = nn.Conv2d(1, 16, kernel_size=5)
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(16, 32, kernel_size=5)
        self.fc1 = nn.Linear(32 * 4 * 4, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = x.view(-1, 32 * 4 * 4)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

model = SimpleCNN()
    

6. Visualizing feature maps

We can extract and visualize feature maps from a specific layer of the model. Here, we will visualize
the feature maps of the first convolutional layer.


import matplotlib.pyplot as plt

# Get a batch of virtual images
data_iter = iter(data_loader)
images, labels = next(data_iter)

# Pass through the model and extract feature maps
with torch.no_grad():
    feature_maps = model.conv1(images)

# Visualize feature maps
def show_feature_maps(feature_maps):
    feature_maps = feature_maps[0].detach().numpy()
    num_feature_maps = feature_maps.shape[0]

    plt.figure(figsize=(15, 15))
    for i in range(num_feature_maps):
        plt.subplot(8, 8, i + 1)
        plt.imshow(feature_maps[i], cmap='gray')
        plt.axis('off')
    plt.show()

show_feature_maps(feature_maps)
    

7. Interpreting results

Through the visualization of feature maps, we can see what features the model is extracting from each input image.
Each feature map is the result of applying different filters, with specific patterns or shapes highlighted.
This allows us to gain insights into the model’s learning process.

8. Conclusion

In this tutorial, we introduced how to implement a simple CNN model using PyTorch and how to visualize
feature maps from the first convolutional layer. Visualizing feature maps is a useful tool for understanding
the internal workings of the model and gaining insights into the patterns being generated.

The fields of machine learning and deep learning continue to evolve, and these visualization techniques can
help us explain and improve complex models.
I encourage you to continue learning by tackling various topics similar to this one.

Deep Learning PyTorch Course, Feature Extraction Techniques

Deep learning is a powerful technology that enables problem-solving by automatically learning useful features from various data. Today, we will address feature extraction techniques using the PyTorch library. This plays a crucial role in extracting attributes from various forms of data such as images, text, and audio to enhance the performance of machine learning models.

What is Feature Extraction?

Feature extraction refers to the process of transforming original data into a lower dimension to extract useful information. This process helps reduce noise in the data and alleviates the difficulties encountered by the model during learning. For example, in an image classification problem, instead of directly using the pixel values of the images, we can utilize CNN (Convolutional Neural Network) to extract only the important features.

1. Extracting Features from Image Data

We will look at an example of using CNN to extract features in the field of image processing. CNN is structured favorably to capture local information in images.

1.1 Data Preparation

import torchvision.transforms as transforms
import torchvision.datasets as datasets
import torch
import torch.nn as nn
import torchvision.models as models

# Download and preprocess dataset
transform = transforms.Compose([
    transforms.Resize((224, 224)),
    transforms.ToTensor(),
])

# Example of CIFAR10 dataset
train_dataset = datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=32, shuffle=True)

1.2 Define CNN Model

We will use a model based on ResNet to extract features.

# Load ResNet model
model = models.resnet18(pretrained=True)  # Pre-trained model
model.fc = nn.Identity()  # Remove the last layer to output features only

# Set GPU usage
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = model.to(device)

1.3 Extract Features

To check the shape of the extracted features, let’s pass the data through the model.

def extract_features(data_loader):
    features = []
    
    model.eval()  # Switch to evaluation mode

    with torch.no_grad():  # Disable gradient calculation
        for images, labels in data_loader:
            images = images.to(device)
            feature = model(images)
            features.append(feature.cpu())

    return torch.cat(features)

# Execute feature extraction
features = extract_features(train_loader)
print("Size of extracted features:", features.size())

2. Extracting Features from Text Data

To handle text data, we will explore how to extract features using RNN (Recurrent Neural Network). This is commonly used in natural language processing (NLP).

2.1 Data Preparation

from torchtext.datasets import AG_NEWS
from torchtext.data import Field, BucketIterator

TEXT = Field(tokenize='spacy', lower=True)
LABEL = Field(sequential=False)

# Load AG News dataset
train_data, test_data = AG_NEWS splits=(TEXT, LABEL))
TEXT.build_vocab(train_data)
LABEL.build_vocab(train_data)

# Build data loaders
train_iterator, test_iterator = BucketIterator.splits((train_data, test_data), batch_size=64, device=device)

2.2 Define RNN Model

class RNN(nn.Module):
    def __init__(self, input_dim, embed_dim, hidden_dim, output_dim):
        super().__init__()
        self.embedding = nn.Embedding(input_dim, embed_dim)
        self.rnn = nn.RNN(embed_dim, hidden_dim)
        self.fc = nn.Linear(hidden_dim, output_dim)

    def forward(self, text):
        embedded = self.embedding(text)
        output, hidden = self.rnn(embedded)
        return hidden

# Instantiate the model
input_dim = len(TEXT.vocab)
embed_dim = 100
hidden_dim = 256
output_dim = len(LABEL.vocab)

model = RNN(input_dim, embed_dim, hidden_dim, output_dim).to(device)

2.3 Extract Features

Now we will use the RNN model to extract features from the text data.

def extract_text_features(data_loader):
    text_features = []

    model.eval()

    with torch.no_grad():
        for batch in data_loader:
            text, labels = batch.text
            text = text.to(device)
            hidden = model(text)
            text_features.append(hidden.cpu())

    return torch.cat(text_features)

# Execute feature extraction
text_features = extract_text_features(train_iterator)
print("Size of extracted text features:", text_features.size())

Conclusion

In this post, we explored how to extract features from image and text data using PyTorch. We confirmed that we can implement feature extraction methods suitable for each data type using structures such as CNN and RNN. Feature extraction is an essential step in improving the performance of machine learning models and enabling smooth data analysis. We encourage further exploration of various models and techniques!

If you have any questions related to feature extraction or need more information, please leave a comment. Thank you!

Deep Learning PyTorch Course, Transformer Attention

Deep learning has become a key technology that has brought innovations to the field of artificial intelligence (AI) in recent years. Among various deep learning models, the Transformer has shown outstanding performance in the field of Natural Language Processing (NLP) and has attracted the attention of many researchers. In this article, we will provide an in-depth explanation of the Transformer architecture and attention mechanism using the PyTorch framework, along with practical code examples.

1. What is a Transformer?

The Transformer is a model proposed by researchers including Vaswani from Google in 2017, designed to overcome the limitations of traditional Recurrent Neural Networks (RNNs) and Long Short-Term Memory networks (LSTMs). The Transformer can process the entire input sequence at once, making parallelization easier and allowing it to learn longer dependencies.

1.1 Structure of the Transformer

The Transformer consists of two main components: the encoder and the decoder. The encoder takes in the input sequence, and the decoder generates the output sequence based on the encoder’s output. The key part here is the attention mechanism.

2. Attention Mechanism

Attention is a mechanism that allows focusing on specific parts of the input sequence. In other words, each word (or input vector) computes weights based on its relationships with other words to extract information. Attention fundamentally consists of three elements: Query, Key, and Value.

2.1 Attention Score

The attention score is calculated as the dot product between the query and key. This score indicates how much each word in the input sequence influences the current word.

2.2 Softmax Function

To normalize the attention scores, the softmax function is used to compute the weights. This ensures that all weights fall between 0 and 1, and their sum equals 1.

2.3 Attention Operation

Once the weights are determined, they are multiplied with the Values to generate the final attention output. The final output is the sum of the weighted Values.

3. Implementing Transformer with PyTorch

Now, let’s implement the Transformer and attention mechanism using PyTorch. The code below is an example of a basic attention module.

3.1 Installing Required Libraries

!pip install torch torchvision

3.2 Implementing Attention Class


import torch
import torch.nn as nn
import torch.nn.functional as F

class ScaledDotProductAttention(nn.Module):
    def __init__(self):
        super(ScaledDotProductAttention, self).__init__()

    def forward(self, query, key, value, mask=None):
        # Calculate dot product between query and key
        scores = torch.matmul(query, key.transpose(-2, -1)) / (key.size(-1) ** 0.5)

        # Masking if a mask is provided
        if mask is not None:
            scores.masked_fill_(mask == 0, -1e9)

        # Normalize using softmax function
        attn_weights = F.softmax(scores, dim=-1)

        # Calculate attention output by multiplying weights with values
        output = torch.matmul(attn_weights, value)
        return output, attn_weights
    

3.3 Implementing Transformer Encoder


class TransformerEncoder(nn.Module):
    def __init__(self, embed_size, heads, num_layers, drop_out):
        super(TransformerEncoder, self).__init__()
        self.embed_size = embed_size
        self.heads = heads
        self.num_layers = num_layers
        self.drop_out = drop_out

        self.attention = ScaledDotProductAttention()
        self.linear = nn.Linear(embed_size, embed_size)
        self.dropout = nn.Dropout(drop_out)
        self.norm = nn.LayerNorm(embed_size)

    def forward(self, x, mask):
        for _ in range(self.num_layers):
            attention_output, _ = self.attention(x, x, x, mask)
            x = self.norm(x + self.dropout(attention_output))
            x = self.norm(x + self.dropout(self.linear(x)))
        return x
    

4. Model Training and Evaluation

After implementing the Transformer encoder, we will explain how to train and evaluate the model using real data.

4.1 Data Preparation

To train the model, we first need to prepare the training data. Typically, sequence data such as text data is used.

4.2 Model Initialization


embed_size = 256  # Embedding dimension
heads = 8  # Number of attention heads
num_layers = 6  # Number of encoder layers
drop_out = 0.1  # Dropout rate

model = TransformerEncoder(embed_size, heads, num_layers, drop_out)
    

4.3 Setting Loss Function and Optimizer


optimizer = torch.optim.Adam(model.parameters(), lr=0.0001)
loss_fn = nn.CrossEntropyLoss()
    

4.4 Training Loop


for epoch in range(num_epochs):
    model.train()
    total_loss = 0
    for batch in train_loader:
        optimizer.zero_grad()
        output = model(batch['input'], batch['mask'])
        loss = loss_fn(output.view(-1, output.size(-1)), batch['target'])
        loss.backward()
        optimizer.step()
        total_loss += loss.item()
    print(f"Epoch: {epoch+1}, Loss: {total_loss/len(train_loader)}")
    

4.5 Evaluation and Testing

After training is completed, we evaluate the model to measure its performance. Generally, metrics such as accuracy, precision, and recall are used on test data.

5. Conclusion

In this article, we explained the Transformer architecture and attention mechanism, and demonstrated how to implement them using PyTorch. The Transformer model is useful for building high-performance natural language processing models and is applied in various fields. Since the performance can vary significantly depending on the training data and model hyperparameters, it is important to find the optimal combination through various experiments.

The Transformer is currently making innovative contributions to NLP modeling and is expected to continue to evolve through various research outcomes. In the next article, we will cover the use cases of Transformer models in natural language processing. We appreciate your interest.

© 2023 Deep Learning Research Institute. All Rights Reserved.

Deep Learning PyTorch Course, Handling Tensors

One of the basic components of deep learning is the tensor. A tensor represents an N-dimensional array and is used as a foundation for neural network training in PyTorch. In this course, we will learn in detail how to create and manipulate tensors in PyTorch.

1. Basic Understanding of Tensors

A tensor is fundamentally a set of numbers. A 0-dimensional tensor is called a scalar, a 1-dimensional tensor is a vector, a 2-dimensional tensor is a matrix, and a 3-dimensional tensor is known as a multi-dimensional array. PyTorch provides various functionalities to easily create and manipulate tensors.

1.1. Installing PyTorch

First, you need to install PyTorch. If you are using Anaconda, you can run the code below to install it:

conda install pytorch torchvision torchaudio cpuonly -c pytorch

2. Creating Tensors

There are several ways to create tensors in PyTorch. The most basic method is to use the torch.tensor() function.

2.1. Basic Tensor Creation

import torch

# Create tensor using a list
tensor1 = torch.tensor([1, 2, 3])
print(tensor1)

When you run the above code, you can get the following result:

tensor([1, 2, 3])

2.2. Various Ways to Create Tensors

In PyTorch, you can create tensors in various ways. For example:

  • torch.zeros(): Create a tensor where all elements are 0
  • torch.ones(): Create a tensor where all elements are 1
  • torch.arange(): Create a tensor with elements in a specified range
  • torch.randn(): Create a tensor following a normal distribution with mean 0 and standard deviation 1

Example Code

# Create various tensors
zeros_tensor = torch.zeros(3, 4)
ones_tensor = torch.ones(3, 4)
arange_tensor = torch.arange(0, 10, step=1)
random_tensor = torch.randn(3, 4)

print("Zeros Tensor:\n", zeros_tensor)
print("Ones Tensor:\n", ones_tensor)
print("Arange Tensor:\n", arange_tensor)
print("Random Tensor:\n", random_tensor)

3. Tensor Properties

Tensors have various properties. After creating a tensor, you can check its properties. Below are the key properties:

  • tensor.shape: The dimension (shape) of the tensor
  • tensor.dtype: The data type of the tensor
  • tensor.device: The device where the tensor exists (CPU or GPU)

Example Code

print("Shape:", tensor1.shape)
print("Data Type:", tensor1.dtype)
print("Device:", tensor1.device)

4. Tensor Operations

Tensors support various operations, ranging from basic arithmetic operations to advanced operations.

4.1. Basic Arithmetic Operations

tensor_a = torch.tensor([1, 2, 3])
tensor_b = torch.tensor([4, 5, 6])

# Addition
add_result = tensor_a + tensor_b
print("Addition Result:", add_result)

# Multiplication
mul_result = tensor_a * tensor_b
print("Multiplication Result:", mul_result)

4.2. Matrix Operations

Matrix multiplication can be performed using torch.mm() or the @ operator.

matrix_a = torch.tensor([[1, 2],
                              [3, 4]])

matrix_b = torch.tensor([[5, 6],
                          [7, 8]])

matrix_product = torch.mm(matrix_a, matrix_b)
print("Matrix Product:\n", matrix_product)

5. Tensor Slicing and Indexing

Since tensors are N-dimensional arrays, you can extract desired data through slicing and indexing.

5.1. Basic Indexing

tensor = torch.tensor([[1, 2, 3],
                           [4, 5, 6],
                           [7, 8, 9]])

# Element at the first row and second column
element = tensor[0, 1]
print("Element at (0, 1):", element)

5.2. Slicing

# Slicing all rows of the second column
slice_tensor = tensor[:, 1]
print("Slice Tensor:", slice_tensor)

6. Tensors and GPU

In PyTorch, you can utilize the GPU to accelerate operations. To move a tensor to the GPU, you can use the .to() method.

Example Code

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
tensor_gpu = tensor.to(device)
print("Tensor on GPU:", tensor_gpu)

7. Reshaping Tensors

As you work with tensors, there will often be a need to change their shape. To do this, you can use torch.view() or torch.reshape().

Example Code

reshaped_tensor = tensor.view(1, 9)
print("Reshaped Tensor:\n", reshaped_tensor)

8. Comprehensive Example

Now, let’s combine everything we have learned so far to create a simple neural network model. We will create a model to classify hand-written digits using the MNIST dataset.

Creating a PyTorch Model

import torchvision.transforms as transforms
from torchvision import datasets
from torch.utils.data import DataLoader

# Download and load dataset
transform = transforms.Compose([transforms.ToTensor()])
train_dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)

# Define a simple model for validation
import torch.nn as nn
import torch.optim as optim

class SimpleNN(nn.Module):
    def __init__(self):
        super(SimpleNN, self).__init__()
        self.fc1 = nn.Linear(28*28, 128)  # 28x28 is the size of MNIST images
        self.fc2 = nn.Linear(128, 10)      # 10 is the number of classes to classify

    def forward(self, x):
        x = x.view(-1, 28*28) # flatten the input
        x = torch.relu(self.fc1(x))
        x = self.fc2(x)
        return x

# Setting up the model, loss function, and optimizer
model = SimpleNN().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)

# Training loop
for epoch in range(5):  # Train for 5 epochs
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)

        optimizer.zero_grad()   # Reset previous gradients to zero
        output = model(data)    # Pass data through the model
        loss = criterion(output, target)  # Calculate loss
        loss.backward()         # Compute gradients
        optimizer.step()        # Update parameters

        if batch_idx % 100 == 0:
            print(f'Epoch: {epoch}, Batch: {batch_idx}, Loss: {loss.item()}')

In the code above, we built a simple neural network model to classify hand-written digits by training on the MNIST dataset. It includes the processes of creating tensors, performing operations, and running on the GPU.

Conclusion

In this tutorial, we learned various methods to create and manipulate tensors in PyTorch. Tensors are fundamental components of deep learning and play a crucial role in the model training and testing processes. In the next step, you can learn about more complex models and deep learning techniques.

Thank you!