{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"%matplotlib inline"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\nIntroduction to PyTorch\n***********************\n\nIntroduction to Torch's tensor library\n======================================\n\nAll of deep learning is computations on tensors, which are\ngeneralizations of a matrix that can be indexed in more than 2\ndimensions. We will see exactly what this means in-depth later. First,\nlet's look what we can do with tensors.\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# Author: Robert Guthrie\n\nimport torch\n\ntorch.manual_seed(1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Creating Tensors\n~~~~~~~~~~~~~~~~\n\nTensors can be created from Python lists with the torch.tensor()\nfunction.\n\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# torch.tensor(data) creates a torch.Tensor object with the given data.\nV_data = [1., 2., 3.]\nV = torch.tensor(V_data)\nprint(V)\n\n# Creates a matrix\nM_data = [[1., 2., 3.], [4., 5., 6]]\nM = torch.tensor(M_data)\nprint(M)\n\n# Create a 3D tensor of size 2x2x2.\nT_data = [[[1., 2.], [3., 4.]],\n [[5., 6.], [7., 8.]]]\nT = torch.tensor(T_data)\nprint(T)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"What is a 3D tensor anyway? Think about it like this. If you have a\nvector, indexing into the vector gives you a scalar. If you have a\nmatrix, indexing into the matrix gives you a vector. If you have a 3D\ntensor, then indexing into the tensor gives you a matrix!\n\nA note on terminology:\nwhen I say \"tensor\" in this tutorial, it refers\nto any torch.Tensor object. Matrices and vectors are special cases of\ntorch.Tensors, where their dimension is 2 and 1 respectively. When I am\ntalking about 3D tensors, I will explicitly use the term \"3D tensor\".\n\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# Index into V and get a scalar (0 dimensional tensor)\nprint(V[0])\n# Get a Python number from it\nprint(V[0].item())\n\n# Index into M and get a vector\nprint(M[0])\n\n# Index into T and get a matrix\nprint(T[0])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You can also create tensors of other data types. To create a tensor of integer types, try\ntorch.tensor([[1, 2], [3, 4]]) (where all elements in the list are integers).\nYou can also specify a data type by passing in ``dtype=torch.data_type``.\nCheck the documentation for more data types, but\nFloat and Long will be the most common.\n\n\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You can create a tensor with random data and the supplied dimensionality\nwith torch.randn()\n\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"x = torch.randn((3, 4, 5))\nprint(x)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Operations with Tensors\n~~~~~~~~~~~~~~~~~~~~~~~\n\nYou can operate on tensors in the ways you would expect.\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"x = torch.tensor([1., 2., 3.])\ny = torch.tensor([4., 5., 6.])\nz = x + y\nprint(z)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"See `the documentation `__ for a\ncomplete list of the massive number of operations available to you. They\nexpand beyond just mathematical operations.\n\nOne helpful operation that we will make use of later is concatenation.\n\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# By default, it concatenates along the first axis (concatenates rows)\nx_1 = torch.randn(2, 5)\ny_1 = torch.randn(3, 5)\nz_1 = torch.cat([x_1, y_1])\nprint(z_1)\n\n# Concatenate columns:\nx_2 = torch.randn(2, 3)\ny_2 = torch.randn(2, 5)\n# second arg specifies which axis to concat along\nz_2 = torch.cat([x_2, y_2], 1)\nprint(z_2)\n\n# If your tensors are not compatible, torch will complain. Uncomment to see the error\n# torch.cat([x_1, x_2])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Reshaping Tensors\n~~~~~~~~~~~~~~~~~\n\nUse the .view() method to reshape a tensor. This method receives heavy\nuse, because many neural network components expect their inputs to have\na certain shape. Often you will need to reshape before passing your data\nto the component.\n\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"x = torch.randn(2, 3, 4)\nprint(x)\nprint(x.view(2, 12)) # Reshape to 2 rows, 12 columns\n# Same as above. If one of the dimensions is -1, its size can be inferred\nprint(x.view(2, -1))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Computation Graphs and Automatic Differentiation\n================================================\n\nThe concept of a computation graph is essential to efficient deep\nlearning programming, because it allows you to not have to write the\nback propagation gradients yourself. A computation graph is simply a\nspecification of how your data is combined to give you the output. Since\nthe graph totally specifies what parameters were involved with which\noperations, it contains enough information to compute derivatives. This\nprobably sounds vague, so let's see what is going on using the\nfundamental flag ``requires_grad``.\n\nFirst, think from a programmers perspective. What is stored in the\ntorch.Tensor objects we were creating above? Obviously the data and the\nshape, and maybe a few other things. But when we added two tensors\ntogether, we got an output tensor. All this output tensor knows is its\ndata and shape. It has no idea that it was the sum of two other tensors\n(it could have been read in from a file, it could be the result of some\nother operation, etc.)\n\nIf ``requires_grad=True``, the Tensor object keeps track of how it was\ncreated. Let's see it in action.\n\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# Tensor factory methods have a ``requires_grad`` flag\nx = torch.tensor([1., 2., 3], requires_grad=True)\n\n# With requires_grad=True, you can still do all the operations you previously\n# could\ny = torch.tensor([4., 5., 6], requires_grad=True)\nz = x + y\nprint(z)\n\n# BUT z knows something extra.\nprint(z.grad_fn)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"So Tensors know what created them. z knows that it wasn't read in from\na file, it wasn't the result of a multiplication or exponential or\nwhatever. And if you keep following z.grad_fn, you will find yourself at\nx and y.\n\nBut how does that help us compute a gradient?\n\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# Let's sum up all the entries in z\ns = z.sum()\nprint(s)\nprint(s.grad_fn)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"So now, what is the derivative of this sum with respect to the first\ncomponent of x? In math, we want\n\n\\begin{align}\\frac{\\partial s}{\\partial x_0}\\end{align}\n\n\n\nWell, s knows that it was created as a sum of the tensor z. z knows\nthat it was the sum x + y. So\n\n\\begin{align}s = \\overbrace{x_0 + y_0}^\\text{$z_0$} + \\overbrace{x_1 + y_1}^\\text{$z_1$} + \\overbrace{x_2 + y_2}^\\text{$z_2$}\\end{align}\n\nAnd so s contains enough information to determine that the derivative\nwe want is 1!\n\nOf course this glosses over the challenge of how to actually compute\nthat derivative. The point here is that s is carrying along enough\ninformation that it is possible to compute it. In reality, the\ndevelopers of Pytorch program the sum() and + operations to know how to\ncompute their gradients, and run the back propagation algorithm. An\nin-depth discussion of that algorithm is beyond the scope of this\ntutorial.\n\n\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's have Pytorch compute the gradient, and see that we were right:\n(note if you run this block multiple times, the gradient will increment.\nThat is because Pytorch *accumulates* the gradient into the .grad\nproperty, since for many models this is very convenient.)\n\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# calling .backward() on any variable will run backprop, starting from it.\ns.backward()\nprint(x.grad)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Understanding what is going on in the block below is crucial for being a\nsuccessful programmer in deep learning.\n\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"x = torch.randn(2, 2)\ny = torch.randn(2, 2)\n# By default, user created Tensors have ``requires_grad=False``\nprint(x.requires_grad, y.requires_grad)\nz = x + y\n# So you can't backprop through z\nprint(z.grad_fn)\n\n# ``.requires_grad_( ... )`` changes an existing Tensor's ``requires_grad``\n# flag in-place. The input flag defaults to ``True`` if not given.\nx = x.requires_grad_()\ny = y.requires_grad_()\n# z contains enough information to compute gradients, as we saw above\nz = x + y\nprint(z.grad_fn)\n# If any input to an operation has ``requires_grad=True``, so will the output\nprint(z.requires_grad)\n\n# Now z has the computation history that relates itself to x and y\n# Can we just take its values, and **detach** it from its history?\nnew_z = z.detach()\n\n# ... does new_z have information to backprop to x and y?\n# NO!\nprint(new_z.grad_fn)\n# And how could it? ``z.detach()`` returns a tensor that shares the same storage\n# as ``z``, but with the computation history forgotten. It doesn't know anything\n# about how it was computed.\n# In essence, we have broken the Tensor away from its past history"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You can also stop autograd from tracking history on Tensors\nwith ``.requires_grad=True`` by wrapping the code block in\n``with torch.no_grad():``\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"print(x.requires_grad)\nprint((x ** 2).requires_grad)\n\nwith torch.no_grad():\n\tprint((x ** 2).requires_grad)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"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.6.13"
}
},
"nbformat": 4,
"nbformat_minor": 0
}