{ "cells": [ { "cell_type": "markdown", "id": "1750d835", "metadata": {}, "source": [ "# 3. 📌 Introduction to PyTorch\n", "\n", "![Status](https://img.shields.io/static/v1.svg?label=Status&message=Finished&color=green)\n", "\n", "**Filled notebook:** \n", "[![View filled on Github](https://img.shields.io/static/v1.svg?logo=github&label=Repo&message=View%20On%20Github&color=lightgrey)](https://github.com/bfortuno/Surgical-Phase-Recognition/blob/main/docs/tutorial_notebooks/tutorial3/pytorch_intro.ipynb)\n", "[![Open filled In Collab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/bfortuno/Surgical-Phase-Recognition/blob/main/docs/tutorial_notebooks/tutorial3/pytorch_intro.ipynb) \n", "**Author:** Benjamin I. Fortuno" ] }, { "cell_type": "markdown", "id": "246dd75b", "metadata": {}, "source": [ "This notebook provides a comprehensive introduction to PyTorch, covering:\n", "- 🔢 Tensor creation and operations\n", "- 📏 Reshaping and slicing\n", "- ⚡ Matrix operations\n", "- 🎯 Automatic differentiation with `autograd`\n", "- 📈 Visualization of computation graphs\n", "\n", "By the end of this notebook, you'll be comfortable using PyTorch for basic computations and setting up gradients for deep learning models." ] }, { "cell_type": "markdown", "id": "11360d8f", "metadata": {}, "source": [ "## Setting Up PyTorch" ] }, { "cell_type": "code", "execution_count": 1, "id": "95e5b530", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Using device: cpu\n" ] } ], "source": [ "import torch\n", "import numpy as np\n", "import time\n", "\n", "# Check if GPU is available\n", "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n", "print(f\"Using device: {device}\")" ] }, { "cell_type": "markdown", "id": "9a1ffe02", "metadata": {}, "source": [ "## Creating and Manipulating Tensors\n", "Tensors are similar to NumPy arrays but come with GPU acceleration and automatic differentiation." ] }, { "cell_type": "code", "execution_count": 2, "id": "48ca89ee", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Tensor x: tensor([[1., 2.],\n", " [3., 4.]]) \n", "\n", "Random Tensor y: tensor([[0.7179, 0.9155],\n", " [0.9773, 0.2281]]) \n", "\n", "Zero Tensor: tensor([[0., 0., 0.],\n", " [0., 0., 0.],\n", " [0., 0., 0.]]) \n", "\n", "Ones Tensor: tensor([[1., 1., 1.],\n", " [1., 1., 1.],\n", " [1., 1., 1.]])\n" ] } ], "source": [ "# Creating different types of tensors\n", "x = torch.tensor([[1, 2], [3, 4]], dtype=torch.float32, device=device)\n", "y = torch.rand((2,2), device=device) # Random tensor\n", "z = torch.zeros((3,3), device=device) # Zero tensor\n", "ones = torch.ones((3,3), device=device) # Ones tensor\n", "\n", "print(\"Tensor x:\", x, \"\\n\")\n", "print(\"Random Tensor y:\", y, \"\\n\")\n", "print(\"Zero Tensor:\", z, \"\\n\")\n", "print(\"Ones Tensor:\", ones)" ] }, { "cell_type": "markdown", "id": "8e653380", "metadata": {}, "source": [ "### 📏 Tensor Shapes and Reshaping" ] }, { "cell_type": "code", "execution_count": 3, "id": "140c7b29", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Original Shape: torch.Size([2, 2])\n", "Reshaped Tensor:\n", "tensor([[1.],\n", " [2.],\n", " [3.],\n", " [4.]])\n" ] } ], "source": [ "# Reshaping tensors\n", "x_reshaped = x.view(4, 1) # Change shape to (4,1)\n", "print(\"Original Shape:\", x.shape)\n", "print(\"Reshaped Tensor:\")\n", "print(x_reshaped)" ] }, { "cell_type": "markdown", "id": "7b9807cd", "metadata": {}, "source": [ "### 🎭 Indexing and Slicing Tensors" ] }, { "cell_type": "code", "execution_count": 4, "id": "ef0cd428", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Original Tensor: tensor([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])\n", "First element: tensor(0)\n", "Last element: tensor(9)\n", "First 5 elements: tensor([0, 1, 2, 3, 4])\n", "Elements from index 3 to 7: tensor([3, 4, 5, 6, 7])\n" ] } ], "source": [ "# Indexing tensors\n", "tensor = torch.arange(10)\n", "print(\"Original Tensor:\", tensor)\n", "\n", "# Accessing elements\n", "print(\"First element:\", tensor[0])\n", "print(\"Last element:\", tensor[-1])\n", "\n", "# Slicing\n", "print(\"First 5 elements:\", tensor[:5])\n", "print(\"Elements from index 3 to 7:\", tensor[3:8])" ] }, { "cell_type": "markdown", "id": "49798afc", "metadata": {}, "source": [ "### ⚡ Matrix Operations" ] }, { "cell_type": "code", "execution_count": 5, "id": "dcbbc027", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Matrix Multiplication:\n", "tensor([[ 7., 10.],\n", " [15., 22.]])\n" ] } ], "source": [ "# Matrix multiplication\n", "dot_result = torch.mm(x, x)\n", "print(\"Matrix Multiplication:\")\n", "print(dot_result)" ] }, { "cell_type": "markdown", "id": "f72ee322", "metadata": {}, "source": [ "### 🔢 Advanced Tensor Operations\n", "Understanding advanced tensor operations helps in optimizing computations and reducing code complexity." ] }, { "cell_type": "code", "execution_count": 6, "id": "9bc5c4c5", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Stacked Tensor Shape: torch.Size([2, 3, 3])\n", "Concatenated Tensor Shape: torch.Size([6, 3])\n" ] } ], "source": [ "# Stacking and Concatenation\n", "a = torch.rand((3, 3)) # Random 3x3 tensor\n", "b = torch.rand((3, 3)) # Random 3x3 tensor\n", "stacked = torch.stack((a, b)) # Stack along a new dimension\n", "concatenated = torch.cat((a, b), dim=0) # Concatenate along an existing dimension\n", "\n", "print(\"Stacked Tensor Shape:\", stacked.shape)\n", "print(\"Concatenated Tensor Shape:\", concatenated.shape)" ] }, { "cell_type": "markdown", "id": "9ae66b83", "metadata": {}, "source": [ "## Automatic Differentiation with Autograd\n", "PyTorch's `autograd` module enables automatic differentiation for computing gradients in neural networks." ] }, { "cell_type": "code", "execution_count": 7, "id": "b97b2e47", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Function values:\n", "tensor([ 68., 134.], grad_fn=)\n" ] } ], "source": [ "# Defining tensors with gradient tracking\n", "a = torch.tensor([2.0, 3.0], requires_grad=True, device=device)\n", "b = torch.tensor([4.0, 5.0], requires_grad=True, device=device)\n", "\n", "# Define function: f = a^2 + b^3\n", "f = a**2 + b**3\n", "print(\"Function values:\")\n", "print(f)" ] }, { "cell_type": "code", "execution_count": 8, "id": "5cbd3e09", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Gradients:\n", "df/da: tensor([4., 6.])\n", "df/db: tensor([48., 75.])\n" ] } ], "source": [ "# Compute Gradients\n", "f.backward(torch.tensor([1.0, 1.0], device=device))\n", "print(\"\\nGradients:\")\n", "print(f\"df/da: {a.grad}\")\n", "print(f\"df/db: {b.grad}\")" ] }, { "cell_type": "markdown", "id": "89c9aa5d", "metadata": {}, "source": [ "### 📈 Visualizing Gradients\n", "We can visualize the computation graph and gradients using PyTorch's `torchviz` package." ] }, { "cell_type": "code", "execution_count": 9, "id": "499ac223", "metadata": {}, "outputs": [ { "data": { "image/png": "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VlaWnJycubk50bV8WEJCAkKIQqFoamoaGRn1bYdKU1NTeno6QohKpWpraxsaGoq4Solgs9n5+fna2trCrmAgKpL4TB84cKC0tNTQ0LC2tra2tnbHjh1ubm59mE5oaKienp66unpNTU1LS0t4eLgU7QRHCJWWli5ZsoTH471//97ExOTEiRMkP+hk4cKFFhYWysrKxcXFSkpKhw8f7sP3SGFh4cKFC/Hj+0tKSgwMDI4fPy5d93h8+PDh5s2bdXV1y8rKfH19N27cKL6xJLTK6unp+fvvv1+7di04OPjrr79ubm7G2zs6OnJycoqKirhcLt7y6tWr9+/fC762rq4O/4pFCIWGhv7+++/Xr193dnY+dOgQvw++5MnLy+s6dFNTU0ZGBn437K5qa2tfvnzJ4XB6HlfY9HNycioqKpqbm5OTkxMSEurr6/H25ubmtLS0ToN+9913JiYmt2/fvnPnTm1t7enTp4W9XeSxbt2606dP3717V09Pb+/evfz25ubm9PR0/o3i2Wx2UlJSp9cWFRXxr4UbHh5+5syZe/fusdnsc+fO8fu8f/8+JSWl2y3Durq6jIyMtra2bgsrLCxMT0//4LjCpp+YmMhms6urqxMTExMSEvhnk9TV1aWmpgqeD93e3v71118vXrz4ypUrERERERER+IqDmEh6G3LGjBlNTU34fo6MjAxPT8/169cvXrw4MDCwuroaIbRnz54nT54IviQyMvLMmTOdpmNkZFRTU4P//fjx4xkzZuzatSs0NNTf35+fCoTQsWPHJk2atGPHjlmzZl24cKHTRIqLi4OCggoKCqhUag/j9jD98PDwLVu2uLu779u37/jx43hinzx5Mnny5B07dvj6+qakpOA9ORzOkydPfHx8MAwbNGjQjBkzHjx40Oe3UcLodPq0adP4X083btyYPHnyzp07vb29t2zZgl+aMSQkBL/lON+uXbv++ecfwRYajaarq8ufcb/99tuMGTP279/v4+Ozbds23v+O4nz37t3q1atnzJixc+fOGTNmvHnzplM9sbGxCxcu5HA4PY8rbPoIodDQ0C+//NLHxyc8PPz48eMtLS0IoVOnTrm7u+/evTsgIADfyEIIpaSk1NXV+fj4IITMzMzs7OzEOuMkvRmGpw6/Q8OePXsmTZq0efNmDoezbNmyY8eO7dq1y97ePjk5ecqUKQUFBYqK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", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from torchviz import make_dot\n", "from IPython.display import Image\n", "\n", "dot_graph = make_dot(f, params={'a': a, 'b': b}) # Create graph\n", "dot_graph.render(\"computation_graph\", format=\"png\") # Save as PNG\n", "\n", "# Display inline in Jupyter\n", "display(Image(filename=\"computation_graph.png\"))\n" ] }, { "cell_type": "markdown", "id": "4b93d4f3", "metadata": {}, "source": [ "## GPU Acceleration\n", "Using GPUs can significantly speed up AI computations, especially when training deep learning models." ] }, { "cell_type": "code", "execution_count": 10, "id": "ef67c5de", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "No GPU available. Skipping computation speed comparison.\n" ] } ], "source": [ "# Run this cell if GPU is available\n", "if device == torch.device(\"cuda\"):\n", " # Comparing CPU vs GPU computation speed\n", " x_cpu = torch.rand((10000, 10000))\n", " x_gpu = x_cpu.to(device)\n", "\n", " # Measure time on CPU\n", " start_cpu = time.time()\n", " y_cpu = x_cpu * x_cpu\n", " end_cpu = time.time()\n", "\n", " # Measure time on GPU\n", " start_gpu = time.time()\n", " y_gpu = x_gpu * x_gpu\n", " torch.cuda.synchronize()\n", " end_gpu = time.time()\n", "\n", " print(f\"CPU Time: {end_cpu - start_cpu:.5f} sec\")\n", " print(f\"GPU Time: {end_gpu - start_gpu:.5f} sec\")\n", "\n", "else:\n", " print(\"No GPU available. Skipping computation speed comparison.\")" ] }, { "cell_type": "markdown", "id": "f805b830", "metadata": {}, "source": [ "## Exercise: Compute Gradients\n", "**Task:** Define a new function `g = 3a^3 + 2b^2` and compute its gradients.\n", "\n", "Try implementing this in the cell below!" ] }, { "cell_type": "code", "execution_count": 11, "id": "47343e01", "metadata": {}, "outputs": [], "source": [ "# TODO: Define g = 3a^3 + 2b^2 and compute gradients" ] }, { "cell_type": "markdown", "id": "1305c653", "metadata": {}, "source": [ "## Summary\n", "- ✅ Learned how to create and manipulate tensors\n", "- ✅ Performed basic mathematical operations\n", "- ✅ Explored automatic differentiation with `autograd`\n", "- ✅ Visualized computation graphs\n", "\n", "PyTorch is a powerful tool for deep learning, and mastering these fundamentals will help you in future projects! 🚀" ] } ], "metadata": { "kernelspec": { "display_name": "trien_project", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.6" } }, "nbformat": 4, "nbformat_minor": 5 }