{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "Introduction to PyTorch \n", "========================\n", "\n", "Below contents are taken from PyTorch tutorials.\n", "\n", "Tensors are the central data abstraction in PyTorch.\n", "\n", "First things first, let’s import the PyTorch module." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import torch" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Creating Tensors\n", "----------------\n", "\n", "The simplest way to create a tensor is with the ``torch.empty()`` call:\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "execution": { "iopub.execute_input": "2022-01-20T03:08:38.261951Z", "iopub.status.busy": "2022-01-20T03:08:38.261683Z", "iopub.status.idle": "2022-01-20T03:08:38.345349Z", "shell.execute_reply": "2022-01-20T03:08:38.344486Z", "shell.execute_reply.started": "2022-01-20T03:08:38.261921Z" }, "jupyter": { "outputs_hidden": false }, "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "tensor([[0., 0., 0., 0.],\n", " [0., 0., 0., 0.],\n", " [0., 0., 0., 0.]])\n" ] } ], "source": [ "x = torch.empty(3, 4)\n", "print(type(x))\n", "print(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let’s unpack what we just did:\n", "\n", "- We created a tensor using one of the numerous factory methods\n", " attached to the ``torch`` module.\n", "- The tensor itself is 2-dimensional, having 3 rows and 4 columns.\n", "- The type of the object returned is ``torch.Tensor``, which is an\n", " alias for ``torch.FloatTensor``; by default, PyTorch tensors are\n", " populated with 32-bit floating point numbers. (More on data types\n", " below.)\n", "- You will probably see some random-looking values when printing your\n", " tensor. The ``torch.empty()`` call allocates memory for the tensor,\n", " but does not initialize it with any values - so what you’re seeing is\n", " whatever was in memory at the time of allocation.\n", "\n", "A brief note about tensors and their number of dimensions, and\n", "terminology:\n", "\n", "- You will sometimes see a 1-dimensional tensor called a\n", " *vector.* \n", "- Likewise, a 2-dimensional tensor is often referred to as a\n", " *matrix.* \n", "- Anything with more than two dimensions is generally just\n", " called a tensor.\n", "\n", "More often than not, you’ll want to initialize your tensor with some\n", "value. Common cases are all zeros, all ones, or random values, and the\n", "``torch`` module provides factory methods for all of these:\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "execution": { "iopub.execute_input": "2022-01-20T02:12:34.751044Z", "iopub.status.busy": "2022-01-20T02:12:34.749827Z", "iopub.status.idle": "2022-01-20T02:12:34.761889Z", "shell.execute_reply": "2022-01-20T02:12:34.760543Z", "shell.execute_reply.started": "2022-01-20T02:12:34.750956Z" }, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor([[0., 0., 0.],\n", " [0., 0., 0.]])\n", "tensor([[1., 1., 1.],\n", " [1., 1., 1.]])\n", "tensor([[0.3615, 0.8562, 0.5153],\n", " [0.2048, 0.8640, 0.0635]])\n" ] } ], "source": [ "zeros = torch.zeros(2, 3)\n", "print(zeros)\n", "\n", "ones = torch.ones(2, 3)\n", "print(ones)\n", "\n", "random = torch.rand(2, 3)\n", "print(random)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The factory methods all do just what you’d expect - we have a tensor\n", "full of zeros, another full of ones, and another with random values\n", "between 0 and 1.\n", "\n", "Random Tensors and Seeding\n", "~~~~~~~~~~~~~~~~~~~~~~~~~~\n", "\n" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "execution": { "iopub.execute_input": "2022-01-20T03:10:47.488796Z", "iopub.status.busy": "2022-01-20T03:10:47.488464Z", "iopub.status.idle": "2022-01-20T03:10:47.509105Z", "shell.execute_reply": "2022-01-20T03:10:47.508448Z", "shell.execute_reply.started": "2022-01-20T03:10:47.488762Z" }, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor([[0.3126, 0.3791, 0.3087],\n", " [0.0736, 0.4216, 0.0691]])\n", "tensor([[0.2332, 0.4047, 0.2162],\n", " [0.9927, 0.4128, 0.5938]])\n", "tensor([[0.3126, 0.3791, 0.3087],\n", " [0.0736, 0.4216, 0.0691]])\n" ] } ], "source": [ "torch.manual_seed(1729)\n", "random1 = torch.rand(2, 3)\n", "print(random1)\n", "\n", "random2 = torch.rand(2, 3)\n", "print(random2)\n", "\n", "torch.manual_seed(1729)\n", "random3 = torch.rand(2, 3)\n", "print(random3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What you should see above is that ``random1`` and ``random3`` carry\n", "identical values. Manually setting\n", "the RNG’s seed resets it, so that identical computations depending on\n", "random number should, in most settings, provide identical results." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The last way to create a tensor that will cover is to specify its data\n", "directly from a PyTorch collection:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "execution": { "iopub.execute_input": "2022-01-20T03:12:43.801619Z", "iopub.status.busy": "2022-01-20T03:12:43.801096Z", "iopub.status.idle": "2022-01-20T03:12:43.812193Z", "shell.execute_reply": "2022-01-20T03:12:43.811007Z", "shell.execute_reply.started": "2022-01-20T03:12:43.801578Z" }, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor([[3.1416, 2.7183],\n", " [1.6180, 0.0073]])\n", "tensor([ 2, 3, 5, 7, 11, 13, 17, 19])\n", "tensor([[2, 4, 6],\n", " [3, 6, 9]])\n" ] } ], "source": [ "some_constants = torch.tensor([[3.1415926, 2.71828], [1.61803, 0.0072897]])\n", "print(some_constants)\n", "\n", "some_integers = torch.tensor((2, 3, 5, 7, 11, 13, 17, 19))\n", "print(some_integers)\n", "\n", "more_integers = torch.tensor(((2, 4, 6), [3, 6, 9]))\n", "print(more_integers)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using ``torch.tensor()`` is the most straightforward way to create a\n", "tensor if you already have data in a Python tuple or list. As shown\n", "above, nesting the collections will result in a multi-dimensional\n", "tensor.\n", "\n", "Tensor Data Types\n", "\n", "Setting the datatype of a tensor is possible a couple of ways:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "execution": { "iopub.execute_input": "2022-01-20T03:15:03.056508Z", "iopub.status.busy": "2022-01-20T03:15:03.056231Z", "iopub.status.idle": "2022-01-20T03:15:03.069431Z", "shell.execute_reply": "2022-01-20T03:15:03.068408Z", "shell.execute_reply.started": "2022-01-20T03:15:03.05648Z" }, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor([[1, 1, 1],\n", " [1, 1, 1]], dtype=torch.int16)\n", "tensor([[17.3151, 14.5980, 6.0404],\n", " [18.0429, 7.2532, 19.6519]], dtype=torch.float64)\n", "tensor([[17, 14, 6],\n", " [18, 7, 19]], dtype=torch.int32)\n" ] } ], "source": [ "a = torch.ones((2, 3), dtype=torch.int16)\n", "print(a)\n", "\n", "b = torch.rand((2, 3), dtype=torch.float64) * 20.\n", "print(b)\n", "\n", "c = b.to(torch.int32)\n", "print(c)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The simplest way to set the underlying data type of a tensor is with an\n", "optional argument at creation time. In the first line of the cell above,\n", "we set ``dtype=torch.int16`` for the tensor ``a``. When we print ``a``,\n", "we can see that it’s full of ``1`` rather than ``1.`` - Python’s subtle\n", "cue that this is an integer type rather than floating point.\n", "\n", "Another thing to notice about printing ``a`` is that, unlike when we\n", "left ``dtype`` as the default (32-bit floating point), printing the\n", "tensor also specifies its ``dtype``.\n", "\n", "The other way to set the datatype is with the ``.to()`` method. In the\n", "cell above, we create a random floating point tensor ``b`` in the usual\n", "way. Following that, we create ``c`` by converting ``b`` to a 32-bit\n", "integer with the ``.to()`` method. Note that ``c`` contains all the same\n", "values as ``b``, but truncated to integers.\n", "\n", "Available data types include:\n", "\n", "- ``torch.bool``\n", "- ``torch.int8``\n", "- ``torch.uint8``\n", "- ``torch.int16``\n", "- ``torch.int32``\n", "- ``torch.int64``\n", "- ``torch.half``\n", "- ``torch.float``\n", "- ``torch.double``\n", "\n", "Math & Logic with PyTorch Tensors\n", "---------------------------------\n", "\n", "Now that you know some of the ways to create a tensor… what can you do\n", "with them?\n", "\n", "Let’s look at basic arithmetic first, and how tensors interact with\n", "simple scalars:\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "execution": { "iopub.execute_input": "2022-01-20T03:18:27.790422Z", "iopub.status.busy": "2022-01-20T03:18:27.78915Z", "iopub.status.idle": "2022-01-20T03:18:27.818612Z", "shell.execute_reply": "2022-01-20T03:18:27.817811Z", "shell.execute_reply.started": "2022-01-20T03:18:27.790371Z" }, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor([[1., 1.],\n", " [1., 1.]])\n", "tensor([[2., 2.],\n", " [2., 2.]])\n", "tensor([[3., 3.],\n", " [3., 3.]])\n", "tensor([[4., 4.],\n", " [4., 4.]])\n", "tensor([[1.4142, 1.4142],\n", " [1.4142, 1.4142]])\n" ] } ], "source": [ "ones = torch.zeros(2, 2) + 1\n", "twos = torch.ones(2, 2) * 2\n", "threes = (torch.ones(2, 2) * 7 - 1) / 2\n", "fours = twos ** 2\n", "sqrt2s = twos ** 0.5\n", "\n", "print(ones)\n", "print(twos)\n", "print(threes)\n", "print(fours)\n", "print(sqrt2s)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As you can see above, arithmetic operations between tensors and scalars,\n", "such as addition, subtraction, multiplication, division, and\n", "exponentiation are distributed over every element of the tensor. Because\n", "the output of such an operation will be a tensor, you can chain them\n", "together with the usual operator precedence rules, as in the line where\n", "we create ``threes``.\n", "\n", "Similar operations between two tensors also behave like you’d\n", "intuitively expect:\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "execution": { "iopub.execute_input": "2022-01-20T03:20:03.841466Z", "iopub.status.busy": "2022-01-20T03:20:03.840528Z", "iopub.status.idle": "2022-01-20T03:20:03.85142Z", "shell.execute_reply": "2022-01-20T03:20:03.850653Z", "shell.execute_reply.started": "2022-01-20T03:20:03.841415Z" }, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor([[ 2., 4.],\n", " [ 8., 16.]])\n", "tensor([[5., 5.],\n", " [5., 5.]])\n", "tensor([[12., 12.],\n", " [12., 12.]])\n" ] } ], "source": [ "powers2 = twos ** torch.tensor([[1, 2], [3, 4]])\n", "print(powers2)\n", "\n", "fives = ones + fours\n", "print(fives)\n", "\n", "dozens = threes * fours\n", "print(dozens)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It’s important to note here that all of the tensors in the previous code\n", "cell were of identical shape. What happens when we try to perform a\n", "binary operation on tensors if dissimilar shape?\n", "\n", "\n", " \n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "execution": { "iopub.execute_input": "2022-01-20T03:21:27.052638Z", "iopub.status.busy": "2022-01-20T03:21:27.051692Z", "iopub.status.idle": "2022-01-20T03:21:27.194872Z", "shell.execute_reply": "2022-01-20T03:21:27.193017Z", "shell.execute_reply.started": "2022-01-20T03:21:27.052543Z" }, "scrolled": true }, "outputs": [ { "ename": "RuntimeError", "evalue": "The size of tensor a (3) must match the size of tensor b (2) at non-singleton dimension 1", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mRuntimeError\u001b[0m Traceback (most recent call last)", "Cell \u001b[0;32mIn[15], line 4\u001b[0m\n\u001b[1;32m 1\u001b[0m a \u001b[38;5;241m=\u001b[39m torch\u001b[38;5;241m.\u001b[39mrand(\u001b[38;5;241m2\u001b[39m, \u001b[38;5;241m3\u001b[39m)\n\u001b[1;32m 2\u001b[0m b \u001b[38;5;241m=\u001b[39m torch\u001b[38;5;241m.\u001b[39mrand(\u001b[38;5;241m3\u001b[39m, \u001b[38;5;241m2\u001b[39m)\n\u001b[0;32m----> 4\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[43ma\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mb\u001b[49m)\n", "\u001b[0;31mRuntimeError\u001b[0m: The size of tensor a (3) must match the size of tensor b (2) at non-singleton dimension 1" ] } ], "source": [ "a = torch.rand(2, 3)\n", "b = torch.rand(3, 2)\n", "\n", "print(a * b)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the general case, you cannot operate on tensors of different shape\n", "this way (element-wise), even in a case like the cell above, where the tensors have an\n", "identical number of elements.\n", "\n" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor([[0.4518, 0.7276],\n", " [0.4494, 0.6302]])\n" ] } ], "source": [ "print(a @ b)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For matrix multiplication, you should use '@' instead of '*'" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "execution": { "iopub.execute_input": "2022-01-20T03:23:25.800228Z", "iopub.status.busy": "2022-01-20T03:23:25.799321Z", "iopub.status.idle": "2022-01-20T03:23:25.811303Z", "shell.execute_reply": "2022-01-20T03:23:25.810297Z", "shell.execute_reply.started": "2022-01-20T03:23:25.800174Z" }, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor([[0.0703, 0.5105, 0.9451, 0.2359],\n", " [0.1979, 0.3327, 0.6146, 0.5999]])\n", "tensor([[0.1405, 1.0210, 1.8901, 0.4717],\n", " [0.3959, 0.6655, 1.2291, 1.1998]])\n" ] } ], "source": [ "rand = torch.rand(2, 4)\n", "doubled = rand * (torch.ones(1, 4) * 2)\n", "\n", "print(rand)\n", "print(doubled)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What’s the trick here? How is it we got to multiply a 2x4 tensor by a\n", "1x4 tensor?\n", "\n", "Broadcasting is a way to perform an operation between tensors that have\n", "similarities in their shapes. In the example above, the one-row,\n", "four-column tensor is multiplied by *both rows* of the two-row,\n", "four-column tensor.\n", "\n", "This is an important operation in Deep Learning. The common example is\n", "multiplying a tensor of learning weights by a *batch* of input tensors,\n", "applying the operation to each instance in the batch separately, and\n", "returning a tensor of identical shape - just like our (2, 4) \\* (1, 4)\n", "example above returned a tensor of shape (2, 4).\n", "\n", "The rules for broadcasting are:\n", "\n", "- Each tensor must have at least one dimension - no empty tensors.\n", "\n", "- Comparing the dimension sizes of the two tensors, *going from last to\n", " first:*\n", "\n", " - Each dimension must be equal, *or*\n", "\n", " - One of the dimensions must be of size 1, *or*\n", "\n", " - The dimension does not exist in one of the tensors\n", "\n", "Tensors of identical shape, of course, are trivially “broadcastable”, as\n", "you saw earlier.\n", "\n", "Here are some examples of situations that honor the above rules and\n", "allow broadcasting:\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "execution": { "iopub.execute_input": "2022-01-20T03:31:48.916345Z", "iopub.status.busy": "2022-01-20T03:31:48.916048Z", "iopub.status.idle": "2022-01-20T03:31:48.926128Z", "shell.execute_reply": "2022-01-20T03:31:48.925446Z", "shell.execute_reply.started": "2022-01-20T03:31:48.916314Z" }, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor([[[0.0703, 0.5105],\n", " [0.9451, 0.2359],\n", " [0.1979, 0.3327]],\n", "\n", " [[0.0703, 0.5105],\n", " [0.9451, 0.2359],\n", " [0.1979, 0.3327]],\n", "\n", " [[0.0703, 0.5105],\n", " [0.9451, 0.2359],\n", " [0.1979, 0.3327]],\n", "\n", " [[0.0703, 0.5105],\n", " [0.9451, 0.2359],\n", " [0.1979, 0.3327]]]) torch.Size([4, 3, 2])\n", "tensor([[[0.6146, 0.6146],\n", " [0.5999, 0.5999],\n", " [0.5013, 0.5013]],\n", "\n", " [[0.6146, 0.6146],\n", " [0.5999, 0.5999],\n", " [0.5013, 0.5013]],\n", "\n", " [[0.6146, 0.6146],\n", " [0.5999, 0.5999],\n", " [0.5013, 0.5013]],\n", "\n", " [[0.6146, 0.6146],\n", " [0.5999, 0.5999],\n", " [0.5013, 0.5013]]]) torch.Size([4, 3, 2])\n", "tensor([[[0.9397, 0.8656],\n", " [0.9397, 0.8656],\n", " [0.9397, 0.8656]],\n", "\n", " [[0.9397, 0.8656],\n", " [0.9397, 0.8656],\n", " [0.9397, 0.8656]],\n", "\n", " [[0.9397, 0.8656],\n", " [0.9397, 0.8656],\n", " [0.9397, 0.8656]],\n", "\n", " [[0.9397, 0.8656],\n", " [0.9397, 0.8656],\n", " [0.9397, 0.8656]]]) torch.Size([4, 3, 2])\n" ] } ], "source": [ "a = torch.ones(4, 3, 2)\n", "\n", "b = a * torch.rand(1, 3, 2) # 3rd & 2nd dims identical to a, dim 1 absent\n", "print(b, b.size())\n", "\n", "c = a * torch.rand(3, 1) # 3rd dim = 1, 2nd dim identical to a\n", "print(c, c.size())\n", "\n", "d = a * torch.rand(1, 2) # 3rd dim identical to a, 2nd dim = 1\n", "print(d, d.size())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Look closely at the values of each tensor above: \n", "For (layer, rwo, column)\n", "\n", "- The multiplication operation that created ``b`` was \n", " broadcast over every “layer” of ``a``.\n", "- For ``c``, the operation was broadcast over ever layer and row of\n", " ``a`` - every 3-element column is identical. \n", "- For ``d``, we switched it around - now every *row* is identical,\n", " across layers and columns.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "More Math with Tensors\n", "~~~~~~~~~~~~~~~~~~~~~~\n", "\n", "PyTorch tensors have over three hundred operations that can be performed\n", "on them.\n", "\n", "Here is a small sample from some of the major categories of operations:\n", "\n" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "execution": { "iopub.execute_input": "2022-01-20T03:32:45.971242Z", "iopub.status.busy": "2022-01-20T03:32:45.97084Z", "iopub.status.idle": "2022-01-20T03:32:45.975648Z", "shell.execute_reply": "2022-01-20T03:32:45.974722Z", "shell.execute_reply.started": "2022-01-20T03:32:45.971194Z" } }, "outputs": [], "source": [ "import math" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "execution": { "iopub.execute_input": "2022-01-20T03:33:45.238621Z", "iopub.status.busy": "2022-01-20T03:33:45.23833Z", "iopub.status.idle": "2022-01-20T03:33:45.341287Z", "shell.execute_reply": "2022-01-20T03:33:45.340625Z", "shell.execute_reply.started": "2022-01-20T03:33:45.238587Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Common functions:\n", "tensor([[0.0413, 0.3730, 0.2772, 0.2987],\n", " [0.4734, 0.0476, 0.8904, 0.5951]])\n", "tensor([[1., 1., -0., 1.],\n", " [-0., -0., -0., -0.]])\n", "tensor([[ 0., 0., -1., 0.],\n", " [-1., -1., -1., -1.]])\n" ] }, { "ename": "NameError", "evalue": "name 'math' is not defined", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", "Cell \u001b[0;32mIn[18], line 9\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[38;5;28mprint\u001b[39m(torch\u001b[38;5;241m.\u001b[39mfloor(a))\n\u001b[1;32m 8\u001b[0m \u001b[38;5;66;03m# trigonometric functions and their inverses\u001b[39;00m\n\u001b[0;32m----> 9\u001b[0m angles \u001b[38;5;241m=\u001b[39m torch\u001b[38;5;241m.\u001b[39mtensor([\u001b[38;5;241m0\u001b[39m, \u001b[43mmath\u001b[49m\u001b[38;5;241m.\u001b[39mpi \u001b[38;5;241m/\u001b[39m \u001b[38;5;241m4\u001b[39m, math\u001b[38;5;241m.\u001b[39mpi \u001b[38;5;241m/\u001b[39m \u001b[38;5;241m2\u001b[39m, \u001b[38;5;241m3\u001b[39m \u001b[38;5;241m*\u001b[39m math\u001b[38;5;241m.\u001b[39mpi \u001b[38;5;241m/\u001b[39m \u001b[38;5;241m4\u001b[39m])\n\u001b[1;32m 10\u001b[0m sines \u001b[38;5;241m=\u001b[39m torch\u001b[38;5;241m.\u001b[39msin(angles)\n\u001b[1;32m 11\u001b[0m inverses \u001b[38;5;241m=\u001b[39m torch\u001b[38;5;241m.\u001b[39masin(sines)\n", "\u001b[0;31mNameError\u001b[0m: name 'math' is not defined" ] } ], "source": [ "# common functions\n", "a = torch.rand(2, 4) * 2 - 1\n", "print('Common functions:')\n", "print(torch.abs(a))\n", "print(torch.ceil(a))\n", "print(torch.floor(a))\n", "\n", "# trigonometric functions and their inverses\n", "angles = torch.tensor([0, math.pi / 4, math.pi / 2, 3 * math.pi / 4])\n", "sines = torch.sin(angles)\n", "inverses = torch.asin(sines)\n", "print('\\nSine and arcsine:')\n", "print(angles)\n", "print(sines)\n", "print(inverses)\n", "\n", "# bitwise operations\n", "print('\\nBitwise XOR:')\n", "b = torch.tensor([1, 5, 11])\n", "c = torch.tensor([2, 7, 10])\n", "print(torch.bitwise_xor(b, c))\n", "\n", "# comparisons:\n", "print('\\nBroadcasted, element-wise equality comparison:')\n", "d = torch.tensor([[1., 2.], [3., 4.]])\n", "e = torch.ones(1, 2) # many comparison ops support broadcasting!\n", "print(torch.eq(d, e)) # returns a tensor of type bool\n", "\n", "# reductions:\n", "print('\\nReduction ops:')\n", "print(torch.max(d)) # returns a single-element tensor\n", "print(torch.mean(d)) # average\n", "print(torch.std(d)) # standard deviation\n", "print(torch.prod(d)) # product of all numbers\n", "print(torch.unique(torch.tensor([1, 2, 1, 2, 1, 2]))) # filter unique elements\n", "\n", "# vector and linear algebra operations\n", "m1 = torch.rand(2, 2) # random matrix\n", "m2 = torch.tensor([[3., 0.], [0., 3.]]) # three times identity matrix\n", "\n", "print('\\nMatrices:')\n", "print(m1)\n", "m3 = torch.matmul(m1, m2) # torch.mm\n", "print(m3) # 3 times m1\n", "print(torch.svd(m3)) # singular value decomposition" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "Autograd\n", "============================\n", "\n", "PyTorch’s *Autograd* feature is part of what make PyTorch flexible and\n", "fast for building machine learning projects. It allows for the rapid and\n", "easy computation of multiple partial derivatives (also referred to as\n", "*gradients)* over a complex computation. This operation is central to\n", "backpropagation-based neural network learning.\n", "\n", "The power of autograd comes from the fact that it traces your\n", "computation dynamically *at runtime,* meaning that if your model has\n", "decision branches, or loops whose lengths are not known until runtime,\n", "the computation will still be traced correctly, and you’ll get correct\n", "gradients to drive learning. This, combined with the fact that your\n", "models are built in Python, offers far more flexibility than frameworks\n", "that rely on static analysis of a more rigidly-structured model for\n", "computing gradients.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "A Simple Example\n", "----------------\n", "\n", "That was a lot of theory - but what does it look like to use autograd in\n", "practice?\n", "\n", "Let’s start with a straightforward example. First, we’ll do some imports\n", "to let us graph our results:\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "execution": { "iopub.execute_input": "2022-01-20T03:42:12.773499Z", "iopub.status.busy": "2022-01-20T03:42:12.773135Z", "iopub.status.idle": "2022-01-20T03:42:12.778813Z", "shell.execute_reply": "2022-01-20T03:42:12.778058Z", "shell.execute_reply.started": "2022-01-20T03:42:12.773461Z" }, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "# %matplotlib inline\n", "\n", "import torch\n", "\n", "import matplotlib.pyplot as plt\n", "import matplotlib.ticker as ticker\n", "import math" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, we’ll create an input tensor full of evenly spaced values on the\n", "interval $[0, 2{\\pi}]$, and specify ``requires_grad=True``. (Like\n", "most functions that create tensors, ``torch.linspace()`` accepts an\n", "optional ``requires_grad`` option.) Setting this flag means that in\n", "every computation that follows, autograd will be accumulating the\n", "history of the computation in the output tensors of that computation.\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "execution": { "iopub.execute_input": "2022-01-20T03:42:27.645673Z", "iopub.status.busy": "2022-01-20T03:42:27.645361Z", "iopub.status.idle": "2022-01-20T03:42:27.65522Z", "shell.execute_reply": "2022-01-20T03:42:27.654546Z", "shell.execute_reply.started": "2022-01-20T03:42:27.645642Z" }, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor([0.0000, 0.2618, 0.5236, 0.7854, 1.0472, 1.3090, 1.5708, 1.8326, 2.0944,\n", " 2.3562, 2.6180, 2.8798, 3.1416, 3.4034, 3.6652, 3.9270, 4.1888, 4.4506,\n", " 4.7124, 4.9742, 5.2360, 5.4978, 5.7596, 6.0214, 6.2832],\n", " requires_grad=True)\n" ] } ], "source": [ "a = torch.linspace(0., 2. * math.pi, steps=25, requires_grad=True)\n", "print(a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, we’ll perform a computation, and plot its output in terms of its\n", "inputs:\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "execution": { "iopub.execute_input": "2022-01-20T03:42:39.72062Z", "iopub.status.busy": "2022-01-20T03:42:39.72013Z", "iopub.status.idle": "2022-01-20T03:42:39.931487Z", "shell.execute_reply": "2022-01-20T03:42:39.930832Z", "shell.execute_reply.started": "2022-01-20T03:42:39.720568Z" }, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "b = torch.sin(a)\n", "plt.plot(a.detach(), b.detach())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let’s have a closer look at the tensor ``b``. When we print it, we see\n", "an indicator that it is tracking its computation history:\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "execution": { "iopub.execute_input": "2022-01-20T03:42:43.607123Z", "iopub.status.busy": "2022-01-20T03:42:43.606846Z", "iopub.status.idle": "2022-01-20T03:42:43.613676Z", "shell.execute_reply": "2022-01-20T03:42:43.612818Z", "shell.execute_reply.started": "2022-01-20T03:42:43.607092Z" }, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor([ 0.0000e+00, 2.5882e-01, 5.0000e-01, 7.0711e-01, 8.6603e-01,\n", " 9.6593e-01, 1.0000e+00, 9.6593e-01, 8.6603e-01, 7.0711e-01,\n", " 5.0000e-01, 2.5882e-01, -8.7423e-08, -2.5882e-01, -5.0000e-01,\n", " -7.0711e-01, -8.6603e-01, -9.6593e-01, -1.0000e+00, -9.6593e-01,\n", " -8.6603e-01, -7.0711e-01, -5.0000e-01, -2.5882e-01, 1.7485e-07],\n", " grad_fn=)\n" ] } ], "source": [ "print(b)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This ``grad_fn`` gives us a hint that when we execute the\n", "backpropagation step and compute gradients, we’ll need to compute the\n", "derivative of $sin(x)$ for all this tensor’s inputs.\n", "\n", "Let’s perform some more computations:\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "execution": { "iopub.execute_input": "2022-01-20T03:43:00.994489Z", "iopub.status.busy": "2022-01-20T03:43:00.994219Z", "iopub.status.idle": "2022-01-20T03:43:01.002737Z", "shell.execute_reply": "2022-01-20T03:43:01.001757Z", "shell.execute_reply.started": "2022-01-20T03:43:00.99446Z" }, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor([ 0.0000e+00, 5.1764e-01, 1.0000e+00, 1.4142e+00, 1.7321e+00,\n", " 1.9319e+00, 2.0000e+00, 1.9319e+00, 1.7321e+00, 1.4142e+00,\n", " 1.0000e+00, 5.1764e-01, -1.7485e-07, -5.1764e-01, -1.0000e+00,\n", " -1.4142e+00, -1.7321e+00, -1.9319e+00, -2.0000e+00, -1.9319e+00,\n", " -1.7321e+00, -1.4142e+00, -1.0000e+00, -5.1764e-01, 3.4969e-07],\n", " grad_fn=)\n", "tensor([ 1.0000e+00, 1.5176e+00, 2.0000e+00, 2.4142e+00, 2.7321e+00,\n", " 2.9319e+00, 3.0000e+00, 2.9319e+00, 2.7321e+00, 2.4142e+00,\n", " 2.0000e+00, 1.5176e+00, 1.0000e+00, 4.8236e-01, -3.5763e-07,\n", " -4.1421e-01, -7.3205e-01, -9.3185e-01, -1.0000e+00, -9.3185e-01,\n", " -7.3205e-01, -4.1421e-01, 4.7684e-07, 4.8236e-01, 1.0000e+00],\n", " grad_fn=)\n" ] } ], "source": [ "c = 2 * b\n", "print(c)\n", "\n", "d = c + 1\n", "print(d)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, let’s compute a single-element output. When you call\n", "``.backward()`` on a tensor with no arguments, it expects the calling\n", "tensor to contain only a single element, as is the case when computing a\n", "loss function.\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "execution": { "iopub.execute_input": "2022-01-20T03:43:09.01646Z", "iopub.status.busy": "2022-01-20T03:43:09.016005Z", "iopub.status.idle": "2022-01-20T03:43:09.023232Z", "shell.execute_reply": "2022-01-20T03:43:09.022421Z", "shell.execute_reply.started": "2022-01-20T03:43:09.016413Z" }, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor(25., grad_fn=)\n" ] } ], "source": [ "# out = (sin(a) * 2 + 1).sum()\n", "out = d.sum()\n", "print(out)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Each ``grad_fn`` stored with our tensors allows you to walk the\n", "computation all the way back to its inputs with its ``next_functions``\n", "property. We can see below that drilling down on this property on ``d``\n", "shows us the gradient functions for all the prior tensors. Note that\n", "``a.grad_fn`` is reported as ``None``, indicating that this was an input\n", "to the function with no history of its own.\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "execution": { "iopub.execute_input": "2022-01-20T03:43:21.364902Z", "iopub.status.busy": "2022-01-20T03:43:21.364331Z", "iopub.status.idle": "2022-01-20T03:43:21.37522Z", "shell.execute_reply": "2022-01-20T03:43:21.374284Z", "shell.execute_reply.started": "2022-01-20T03:43:21.364853Z" }, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "d:\n", "\n", "\n", "\n", "\n", "()\n", "\n", "c:\n", "\n", "\n", "b:\n", "\n", "\n", "a:\n", "None\n" ] } ], "source": [ "# d = sin(a) * 2 + 1\n", "print('d:')\n", "print(d.grad_fn)\n", "print(d.grad_fn.next_functions[0][0])\n", "print(d.grad_fn.next_functions[0][0].next_functions[0][0])\n", "print(d.grad_fn.next_functions[0][0].next_functions[0][0].next_functions[0][0])\n", "print(d.grad_fn.next_functions[0][0].next_functions[0][0].next_functions[0][0].next_functions)\n", "\n", "# c = sin(a) * 2\n", "print('\\nc:')\n", "print(c.grad_fn)\n", "\n", "# b = sin(a)\n", "print('\\nb:')\n", "print(b.grad_fn)\n", "\n", "\n", "print('\\na:')\n", "print(a.grad_fn)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With all this machinery in place, how do we get derivatives out? You\n", "call the ``backward()`` method on the output, and check the input’s\n", "``grad`` property to inspect the gradients:\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "execution": { "iopub.execute_input": "2022-01-20T03:43:58.062579Z", "iopub.status.busy": "2022-01-20T03:43:58.061927Z", "iopub.status.idle": "2022-01-20T03:43:58.278347Z", "shell.execute_reply": "2022-01-20T03:43:58.277577Z", "shell.execute_reply.started": "2022-01-20T03:43:58.062513Z" }, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor([ 2.0000e+00, 1.9319e+00, 1.7321e+00, 1.4142e+00, 1.0000e+00,\n", " 5.1764e-01, -8.7423e-08, -5.1764e-01, -1.0000e+00, -1.4142e+00,\n", " -1.7321e+00, -1.9319e+00, -2.0000e+00, -1.9319e+00, -1.7321e+00,\n", " -1.4142e+00, -1.0000e+00, -5.1764e-01, 2.3850e-08, 5.1764e-01,\n", " 1.0000e+00, 1.4142e+00, 1.7321e+00, 1.9319e+00, 2.0000e+00])\n" ] }, { "data": { "text/plain": [ "[]" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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QkJER7ELAwYM+NW2DOfOnUNUVJSgRC2LHxs1wdy5czFlyhT06dMH/fr1w1tvvYXKykpMmzZNdDS7UFFR0einnaysLKSlpcHf3x+RkZECk4k3a9YsrFmzBl999RV8fHys86S0Wi08PDwEpxNvwYIFGDNmDCIjI1FeXo41a9Zg586d2Lx5s+howvn4+PxqbpSXlxfatm3LOVMAfv/73+P+++9HVFQU8vPzsWjRIiiVSjz22GOio9mFF154AcnJyfjHP/6BRx99FIcOHcKyZcuwbNky0dFahujlTo7i7bffliMjI2W1Wi3369dPPnDggOhIdmPHjh0ygF89pkyZIjqacDf6vgCQV6xYITqaXXjqqafkqKgoWa1Wy4GBgfLw4cPlH374QXQsu8Wl0j+ZMGGCHBISIqvVajksLEyeMGGCnJmZKTqWXfnmm2/kbt26yRqNRo6Li5OXLVsmOlKLkWRZlgX1JiIiIqJm45wXIiIicigsL0RERORQWF6IiIjIobC8EBERkUNheSEiIiKHwvJCREREDoXlhYiIiBwKywsRERE5FJYXIiIicigsL0RERORQWF6IiIjIobC8EBERkUP5/9dIlpz1NzARAAAAAElFTkSuQmCC\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "out.backward()\n", "print(a.grad)\n", "plt.plot(a.detach(), a.grad.detach())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Recall the computation steps we took to get here:\n", "\n", "```\n", "\n", " a = torch.linspace(0., 2. * math.pi, steps=25, requires_grad=True)\n", " b = torch.sin(a)\n", " c = 2 * b\n", " d = c + 1\n", " out = d.sum()\n", "```\n", "\n", "Adding a constant, as we did to compute ``d``, does not change the\n", "derivative. That leaves $c = 2 * b = 2 * sin(a)$, the derivative\n", "of which should be $2 * cos(a)$. Looking at the graph above,\n", "that’s just what we see.\n", "\n", "Be aware than only *leaf nodes* of the computation have their gradients\n", "computed. If you tried, for example, ``print(c.grad)`` you’d get back\n", "``None``. In this simple example, only the input is a leaf node, so only\n", "it has gradients computed." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "None\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "/var/folders/s6/yqpth6w93j5d3mshrxfc32pw0000gn/T/ipykernel_20142/3425888651.py:1: UserWarning: The .grad attribute of a Tensor that is not a leaf Tensor is being accessed. Its .grad attribute won't be populated during autograd.backward(). If you indeed want the .grad field to be populated for a non-leaf Tensor, use .retain_grad() on the non-leaf Tensor. If you access the non-leaf Tensor by mistake, make sure you access the leaf Tensor instead. See github.com/pytorch/pytorch/pull/30531 for more informations. (Triggered internally at /Users/runner/work/_temp/anaconda/conda-bld/pytorch_1670525849783/work/build/aten/src/ATen/core/TensorBody.h:485.)\n", " print(c.grad)\n" ] } ], "source": [ "print(c.grad)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Building Models with PyTorch\n", "============================\n", "\n", "``torch.nn.Module`` and ``torch.nn.Parameter``\n", "----------------------------------------------\n", "\n", "``torch.nn.Module`` is the PyTorch base class meant\n", "to encapsulate behaviors specific to PyTorch Models and their\n", "components.\n", "\n", "One important behavior of ``torch.nn.Module`` is registering parameters.\n", "If a particular ``Module`` subclass has learning weights, these weights\n", "are expressed as instances of ``torch.nn.Parameter``. The ``Parameter``\n", "class is a subclass of ``torch.Tensor``, with the special behavior that\n", "when they are assigned as attributes of a ``Module``, they are added to\n", "the list of that modules parameters. These parameters may be accessed\n", "through the ``parameters()`` method on the ``Module`` class.\n", "\n", "As a simple example, here’s a very simple model with two linear layers\n", "and an activation function. We’ll create an instance of it and ask it to\n", "report on its parameters:\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "execution": { "iopub.execute_input": "2022-01-20T03:46:13.199152Z", "iopub.status.busy": "2022-01-20T03:46:13.198516Z", "iopub.status.idle": "2022-01-20T03:46:13.219074Z", "shell.execute_reply": "2022-01-20T03:46:13.21846Z", "shell.execute_reply.started": "2022-01-20T03:46:13.199096Z" }, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The model:\n", "TinyModel(\n", " (linear1): Linear(in_features=100, out_features=200, bias=True)\n", " (activation): ReLU()\n", " (linear2): Linear(in_features=200, out_features=10, bias=True)\n", " (softmax): Softmax(dim=None)\n", ")\n", "\n", "\n", "Just one layer:\n", "Linear(in_features=200, out_features=10, bias=True)\n", "\n", "\n", "Model params:\n", "linear1.weight torch.Size([200, 100])\n", "linear1.bias torch.Size([200])\n", "linear2.weight torch.Size([10, 200])\n", "linear2.bias torch.Size([10])\n", "\n", "\n", "Layer params:\n", "Parameter containing:\n", "tensor([[-0.0569, -0.0149, -0.0570, ..., -0.0580, 0.0173, 0.0384],\n", " [ 0.0461, -0.0352, -0.0688, ..., 0.0234, -0.0590, -0.0442],\n", " [ 0.0644, -0.0121, -0.0230, ..., 0.0022, 0.0334, -0.0081],\n", " ...,\n", " [ 0.0630, 0.0589, -0.0343, ..., 0.0355, -0.0558, 0.0173],\n", " [-0.0115, -0.0556, -0.0164, ..., -0.0494, -0.0416, -0.0285],\n", " [-0.0606, -0.0218, 0.0501, ..., -0.0519, -0.0179, 0.0639]],\n", " requires_grad=True)\n", "Parameter containing:\n", "tensor([-0.0453, -0.0434, 0.0570, -0.0346, -0.0043, 0.0112, -0.0636, 0.0130,\n", " -0.0035, -0.0433], requires_grad=True)\n" ] } ], "source": [ "import torch\n", "\n", "class TinyModel(torch.nn.Module):\n", " \n", " def __init__(self):\n", " super(TinyModel, self).__init__()\n", " \n", " self.linear1 = torch.nn.Linear(100, 200)\n", " self.activation = torch.nn.ReLU()\n", " self.linear2 = torch.nn.Linear(200, 10)\n", " self.softmax = torch.nn.Softmax()\n", " \n", " def forward(self, x):\n", " x = self.linear1(x)\n", " x = self.activation(x)\n", " x = self.linear2(x)\n", " x = self.softmax(x)\n", " return x\n", "\n", "tinymodel = TinyModel()\n", "\n", "print('The model:')\n", "print(tinymodel)\n", "\n", "print('\\n\\nJust one layer:')\n", "print(tinymodel.linear2)\n", "\n", "print('\\n\\nModel params:')\n", "for name, param in tinymodel.named_parameters():\n", " print(name, param.size())\n", "\n", "print('\\n\\nLayer params:')\n", "for param in tinymodel.linear2.parameters():\n", " print(param)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This shows the fundamental structure of a PyTorch model: there is an\n", "``__init__()`` method that defines the layers and other components of a\n", "model, and a ``forward()`` method where the computation gets done. Note\n", "that we can print the model, or any of its submodules, to learn about\n", "its structure.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Other Resources\n", "---------------\n", "\n", "- Docs on the `data\n", " utilities `__, including\n", " Dataset and DataLoader, at pytorch.org\n", "- A `note on the use of pinned\n", " memory `__\n", " for GPU training\n", "- Documentation on the datasets available in\n", " `TorchVision `__,\n", " `TorchText `__, and\n", " `TorchAudio `__\n", "- Documentation on the `loss\n", " functions `__\n", " available in PyTorch\n", "- Documentation on the `torch.optim\n", " package `__, which\n", " includes optimizers and related tools, such as learning rate\n", " scheduling\n", "- A detailed `tutorial on saving and loading\n", " models `__\n", "- The `Tutorials section of\n", " pytorch.org `__ contains tutorials on\n", " a broad variety of training tasks, including classification in\n", " different domains, generative adversarial networks, reinforcement\n", " learning, and more \n", "\n", "\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.9.15" }, "vscode": { "interpreter": { "hash": "376d1a70a9f5a517edf0ae82d6ab8acb1238048da669e4153e9581473fc054b3" } } }, "nbformat": 4, "nbformat_minor": 4 }