pytorch / pages /21_Intro2Tensors.py
eaglelandsonce's picture
Rename pages/1_Intro2Tensors.py to pages/21_Intro2Tensors.py
0d51b23 verified
raw
history blame
14.8 kB
import streamlit as st
import torch
import io
import sys
# Function to execute the input code and capture print statements
def execute_code(code):
# Redirect stdout to capture print statements
old_stdout = sys.stdout
sys.stdout = mystdout = io.StringIO()
global_vars = {"torch": torch}
local_vars = {}
try:
exec(code, global_vars, local_vars)
output = mystdout.getvalue()
except Exception as e:
output = str(e)
finally:
# Reset redirect.
sys.stdout = old_stdout
return output, local_vars
# Dictionary with exercise details
exercises = {
"Exercise 1: Create and Manipulate Tensors": {
"description": "Tensors are the core data structure in PyTorch, similar to arrays in NumPy but with additional capabilities for GPU acceleration. This exercise introduces how to create tensors from various data sources such as lists and NumPy arrays. It also covers basic tensor operations like addition, subtraction, and element-wise multiplication, which are fundamental for manipulating data in PyTorch.",
"code": '''import torch
import numpy as np
# Creating tensors from Python lists
# This creates a 1D tensor from the list [1, 2, 3]
tensor_from_list = torch.tensor([1, 2, 3])
print("Tensor from list:", tensor_from_list)
# Creating tensors from NumPy arrays
# This converts a NumPy array to a tensor
numpy_array = np.array([4, 5, 6])
tensor_from_numpy = torch.tensor(numpy_array)
print("Tensor from NumPy array:", tensor_from_numpy)
# Performing basic tensor operations
tensor1 = torch.tensor([1, 2, 3])
tensor2 = torch.tensor([4, 5, 6])
# Addition
addition = tensor1 + tensor2
print("Addition:", addition)
# Subtraction
subtraction = tensor1 - tensor2
print("Subtraction:", subtraction)
# Element-wise multiplication
elementwise_multiplication = tensor1 * tensor2
print("Element-wise Multiplication:", elementwise_multiplication)
'''
},
"Exercise 2: Tensor Indexing and Slicing": {
"description": "Indexing and slicing allow you to access and manipulate specific elements and sub-tensors. This is crucial for tasks such as data preprocessing and manipulation in machine learning workflows. This exercise demonstrates how to index and slice tensors to extract and modify elements efficiently.",
"code": '''import torch
# Creating a 2D tensor (matrix)
tensor = torch.tensor([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# Indexing elements
# Accessing the element at the 2nd row and 3rd column (indexing starts at 0)
element = tensor[1, 2]
print("Element at index [1, 2]:", element)
# Slicing sub-tensors
# Extracting the entire second row
row = tensor[1, :]
print("Second row:", row)
# Extracting the entire third column
column = tensor[:, 2]
print("Third column:", column)
# Modifying elements
# Changing the first element of the tensor to 10
tensor[0, 0] = 10
print("Modified tensor:", tensor)
'''
},
"Exercise 3: Tensor Reshaping and Transposing": {
"description": "Reshaping and transposing tensors are common operations when preparing data for neural networks. Reshaping allows you to change the layout of tensor data without altering its data. Transposing changes the orientation of the data, which is useful for operations like matrix multiplication. This exercise covers reshaping using `view()` and `reshape()`, and transposing using `transpose()` and `permute()`.",
"code": '''import torch
# Creating a 2D tensor
tensor = torch.tensor([[1, 2, 3], [4, 5, 6]])
# Reshaping the tensor
# Changing the shape of the tensor to (3, 2)
reshaped_tensor = tensor.view(3, 2)
print("Reshaped tensor:", reshaped_tensor)
# Another way to reshape using reshape()
reshaped_tensor2 = tensor.reshape(-1) # Flattening the tensor
print("Reshaped tensor (flattened):", reshaped_tensor2)
# Transposing the tensor
# Swapping the dimensions of the tensor (transpose rows and columns)
transposed_tensor = tensor.t()
print("Transposed tensor:", transposed_tensor)
# Using permute for higher-dimensional tensors
tensor_3d = torch.randn(2, 3, 4) # Creating a random 3D tensor
# Permuting dimensions
permuted_tensor = tensor_3d.permute(2, 0, 1)
print("Permuted tensor shape:", permuted_tensor.shape)
'''
},
"Exercise 4: Tensor Operations and Broadcasting": {
"description": "Broadcasting in PyTorch allows you to perform arithmetic operations on tensors of different shapes. This can simplify code and reduce the need for explicit reshaping. This exercise demonstrates matrix multiplication, the concept of broadcasting, and the application of element-wise functions like sine and exponential.",
"code": '''import torch
# Matrix multiplication
tensor1 = torch.tensor([[1, 2], [3, 4]])
tensor2 = torch.tensor([[5, 6], [7, 8]])
# Performing matrix multiplication using matmul
matmul_result = torch.matmul(tensor1, tensor2)
print("Matrix multiplication result:", matmul_result)
# Broadcasting example
# Broadcasting allows tensor3 and tensor4 to be added despite their different shapes
tensor3 = torch.tensor([1, 2, 3])
tensor4 = torch.tensor([[1], [2], [3]])
broadcast_result = tensor3 + tensor4
print("Broadcasting result:", broadcast_result)
# Element-wise functions
# Applying sine function element-wise
sin_result = torch.sin(tensor3)
print("Sine of tensor3:", sin_result)
# Applying exponential function element-wise
exp_result = torch.exp(tensor3)
print("Exponential of tensor3:", exp_result)
# Applying square root function element-wise
# Note: sqrt requires the tensor to be of floating-point type
sqrt_result = torch.sqrt(tensor3.float())
print("Square root of tensor3:", sqrt_result)
'''
},
"Exercise 5: Tensor Initialization": {
"description": "Proper initialization of tensors is crucial for neural network training and other machine learning tasks. This exercise explores various methods to initialize tensors, such as filling them with zeros, ones, random values, and specific distributions. Understanding these methods helps in setting up model parameters and test data.",
"code": '''import torch
# Creating tensors filled with zeros and ones
zeros_tensor = torch.zeros(3, 3)
print("Zeros tensor:", zeros_tensor)
ones_tensor = torch.ones(3, 3)
print("Ones tensor:", ones_tensor)
# Randomly initialized tensors
# Uniform distribution in the range [0, 1)
rand_tensor = torch.rand(3, 3)
print("Uniform random tensor:", rand_tensor)
# Normal distribution with mean 0 and variance 1
randn_tensor = torch.randn(3, 3)
print("Normal random tensor:", randn_tensor)
# Random integers in the range [0, 10)
randint_tensor = torch.randint(0, 10, (3, 3))
print("Random integer tensor:", randint_tensor)
# Initializing tensors with specific distributions
# Normal distribution with custom mean and standard deviation
normal_tensor = torch.normal(mean=0, std=1, size=(3, 3))
print("Normal distribution tensor:", normal_tensor)
# Uniform distribution in a custom range [0, 1)
uniform_tensor = torch.empty(3, 3).uniform_(0, 1)
print("Uniform distribution tensor:", uniform_tensor)
'''
},
"Exercise 6: Tensor Arithmetic Operations": {
"description": "Arithmetic operations on tensors are essential for numerous tasks in machine learning, including data preprocessing and neural network computations. This exercise covers basic arithmetic with tensors, in-place operations, and aggregation functions that compute summaries over tensor elements.",
"code": '''import torch
# Creating a 1D tensor
tensor = torch.tensor([1.0, 2.0, 3.0])
# Scalar-tensor operations
# Adding a scalar to each element of the tensor
add_scalar = tensor + 5
print("Addition with scalar:", add_scalar)
# Multiplying each element by a scalar
mul_scalar = tensor * 2
print("Multiplication with scalar:", mul_scalar)
# In-place operations (modify the original tensor)
# Adding 5 to each element of the tensor
tensor.add_(5)
print("In-place addition:", tensor)
# Tensor aggregation operations
tensor = torch.tensor([[1, 2], [3, 4]])
# Summing all elements
sum_all = tensor.sum()
print("Sum of all elements:", sum_all)
# Computing the mean of all elements
mean_all = tensor.mean()
print("Mean of all elements:", mean_all)
# Finding the maximum element
max_all = tensor.max()
print("Maximum element:", max_all)
'''
},
"Exercise 7: Tensor Comparison and Logical Operations": {
"description": "Comparison and logical operations enable you to make conditional selections and perform logical checks on tensors. This is crucial for tasks such as filtering data and implementing decision-based logic in machine learning workflows. This exercise demonstrates the use of comparison operators and logical functions, which are often used in neural network training and data analysis.",
"code": '''import torch
# Creating two tensors for comparison
tensor1 = torch.tensor([1, 2, 3])
tensor2 = torch.tensor([3, 2, 1])
# Comparison operations
# Checking if elements in tensor1 are greater than corresponding elements in tensor2
greater_than = tensor1 > tensor2
print("Greater than comparison:", greater_than)
# Checking if elements in tensor1 are equal to corresponding elements in tensor2
equal = tensor1 == tensor2
print("Equality comparison:", equal)
# Logical operations
# Logical AND operation between comparison results
logical_and = torch.logical_and(tensor1 > 1, tensor2 < 3)
print("Logical AND:", logical_and)
# Logical OR operation between comparison results
logical_or = torch.logical_or(tensor1 < 3, tensor2 > 1)
print("Logical OR:", logical_or)
# Logical NOT operation
logical_not = torch.logical_not(tensor1 < 3)
print("Logical NOT:", logical_not)
# Conditional selection using torch.where
# Selecting elements from tensor1 if condition is True, otherwise from tensor2
selected_tensor = torch.where(tensor1 > 2, tensor1, tensor2)
print("Conditional selection:", selected_tensor)
'''
},
"Exercise 8: Tensor Reduction Operations": {
"description": "Reduction operations aggregate tensor elements along specified dimensions, providing summarized results like sums, means, and indices of maximum or minimum values. This exercise covers essential reduction operations, which are commonly used in loss functions and performance metrics in machine learning.",
"code": '''import torch
# Creating a 2D tensor (matrix)
tensor = torch.tensor([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# Reduction operations along dimensions
# Summing elements along dimension 0 (rows)
sum_dim0 = tensor.sum(dim=0)
print("Sum along dimension 0:", sum_dim0)
# Computing the mean along dimension 1 (columns)
mean_dim1 = tensor.mean(dim=1)
print("Mean along dimension 1:", mean_dim1)
# Computing the product of all elements in the tensor
prod_all = tensor.prod()
print("Product of all elements:", prod_all)
# Advanced reduction operations
# Finding the index of the maximum element in the flattened tensor
argmax_all = tensor.argmax()
print("Index of maximum element:", argmax_all)
# Finding the indices of the minimum elements along dimension 0
argmin_dim0 = tensor.argmin(dim=0)
print("Indices of minimum elements along dimension 0:", argmin_dim0)
'''
},
"Exercise 9: Tensor Cloning and Detachment": {
"description": "Cloning and detaching tensors are important when working with gradients in neural networks. Cloning creates a copy of a tensor, while detaching removes it from the computation graph to prevent gradient tracking. This exercise demonstrates these operations, which are crucial for managing tensor operations in complex neural networks.",
"code": '''import torch
# Creating a tensor with gradient tracking enabled
tensor = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)
# Cloning the tensor
# This creates a new tensor with the same data but no gradient history
cloned_tensor = tensor.clone()
print("Original tensor:", tensor)
print("Cloned tensor:", cloned_tensor)
# Detaching the tensor from the computation graph
# This removes the tensor from the gradient computation graph
detached_tensor = tensor.detach()
print("Detached tensor:", detached_tensor)
# Comparing original and detached tensors
# Modifying the original tensor (note that in-place operations modify the tensor in place)
tensor.add_(1)
print("Modified original tensor:", tensor)
print("Detached tensor remains unchanged:", detached_tensor)
'''
},
"Exercise 10: GPU Operations with Tensors": {
"description": "Utilizing GPUs can significantly speed up tensor operations, which is crucial for training large neural networks. This exercise covers checking for GPU availability, moving tensors to GPU, and performing operations on GPU to compare performance with CPU operations.",
"code": '''import torch
import time
# Check for GPU availability
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print("Using device:", device)
# Creating a tensor and moving it to GPU if available
tensor = torch.tensor([1.0, 2.0, 3.0])
tensor = tensor.to(device)
print("Tensor on GPU:", tensor)
# Perform operations on GPU
# Adding a scalar to each element of the tensor on GPU
tensor_add = tensor + 5
print("Tensor after addition on GPU:", tensor_add)
# Compare speed of operations on CPU vs GPU
# Creating a large tensor for performance comparison
cpu_tensor = torch.randn(10000, 10000)
# CPU operation
start_time = time.time()
cpu_result = cpu_tensor @ cpu_tensor.T # Matrix multiplication
end_time = time.time()
print("CPU operation time:", end_time - start_time, "seconds")
# GPU operation (if available)
if torch.cuda.is_available():
gpu_tensor = cpu_tensor.to(device)
start_time = time.time()
gpu_result = gpu_tensor @ gpu_tensor.T # Matrix multiplication
torch.cuda.synchronize() # Wait for GPU operation to finish
end_time = time.time()
print("GPU operation time:", end_time - start_time, "seconds")
'''
}
}
st.title('PyTorch Code Runner')
# Side menu for exercises
exercise_choice = st.sidebar.radio("Choose an exercise", list(exercises.keys()))
# Display the chosen exercise description
st.subheader(exercise_choice)
st.write(exercises[exercise_choice]["description"])
# Text area for inputting the PyTorch code
code_input = st.text_area("Enter your PyTorch code here", height=300, value=exercises[exercise_choice]["code"])
# Button to execute the code
if st.button("Run Code"):
# Prepend the import statement
code_to_run = "import torch\n" + code_input
# Execute the code and capture the output
output, variables = execute_code(code_to_run)
# Display the output
st.subheader('Output')
st.text(output)
# Display returned variables
if variables:
st.subheader('Variables')
for key, value in variables.items():
st.text(f"{key}: {value}")