{
  "source_file": "0930",
  "source_platform": "研究机构",
  "author": "PyTorch Tutorials",
  "title_listed": "What is torch.nn really?",
  "keywords": "torch.nn,神经网络,PyTorch",
  "domain": "机器学习",
  "content_type": "教程",
  "is_paid_flag": "否",
  "url": "https://pytorch.org/tutorials/beginner/nn_tutorial.html",
  "publish_date": "持续更新",
  "corpus_stage": "机器学习",
  "notes": "torch.nn 深入讲解",
  "id": "49594a3fe0ed",
  "collected_at": "2026-10-02T16:16:12",
  "title": "What is torch.nn really? — PyTorch Tutorials 2.14.0+cu130 documentation",
  "body_markdown": "Note\nGo to the end\nto download the full example code.\nWhat is\ntorch.nn\nreally\n?\n#\nCreated On: Dec 26, 2018 | Last Updated: Jan 24, 2025 | Last Verified: Nov 05, 2024\nAuthors:\nJeremy Howard,\nfast.ai\n. Thanks to Rachel Thomas and Francisco Ingham.\nWe recommend running this tutorial as a notebook, not a script. To download the notebook (\n.ipynb\n) file,\nclick the link at the top of the page.\nPyTorch provides the elegantly designed modules and classes\ntorch.nn\n,\ntorch.optim\n,\nDataset\n,\nand\nDataLoader\nto help you create and train neural networks.\nIn order to fully utilize their power and customize\nthem for your problem, you need to really understand exactly what they’re\ndoing. To develop this understanding, we will first train basic neural net\non the MNIST data set without using any features from these models; we will\ninitially only use the most basic PyTorch tensor functionality. Then, we will\nincrementally add one feature from\ntorch.nn\n,\ntorch.optim\n,\nDataset\n, or\nDataLoader\nat a time, showing exactly what each piece does, and how it\nworks to make the code either more concise, or more flexible.\nThis tutorial assumes you already have PyTorch installed, and are familiar\nwith the basics of tensor operations.\n(If you’re familiar with Numpy array\noperations, you’ll find the PyTorch tensor operations used here nearly identical).\nMNIST data setup\n#\nWe will use the classic\nMNIST\ndataset,\nwhich consists of black-and-white images of hand-drawn digits (between 0 and 9).\nWe will use\npathlib\nfor dealing with paths (part of the Python 3 standard library), and will\ndownload the dataset using\nrequests\n. We will only\nimport modules when we use them, so you can see exactly what’s being\nused at each point.\nfrom\npathlib\nimport\nPath\nimport\nrequests\nDATA_PATH\n=\nPath\n(\n\"data\"\n)\nPATH\n=\nDATA_PATH\n/\n\"mnist\"\nPATH\n.\nmkdir\n(\nparents\n=\nTrue\n,\nexist_ok\n=\nTrue\n)\nURL\n=\n\"https://github.com/pytorch/tutorials/raw/main/_static/\"\nFILENAME\n=\n\"mnist.pkl.gz\"\nif\nnot\n(\nPATH\n/\nFILENAME\n)\n.\nexists\n():\ncontent\n=\nrequests\n.\nget\n(\nURL\n+\nFILENAME\n)\n.\ncontent\n(\nPATH\n/\nFILENAME\n)\n.\nopen\n(\n\"wb\"\n)\n.\nwrite\n(\ncontent\n)\nThis dataset is in numpy array format, and has been stored using pickle,\na python-specific format for serializing data.\nimport\npickle\nimport\ngzip\nwith\ngzip\n.\nopen\n((\nPATH\n/\nFILENAME\n)\n.\nas_posix\n(),\n\"rb\"\n)\nas\nf\n:\n((\nx_train\n,\ny_train\n),\n(\nx_valid\n,\ny_valid\n),\n_\n)\n=\npickle\n.\nload\n(\nf\n,\nencoding\n=\n\"latin-1\"\n)\nEach image is 28 x 28, and is being stored as a flattened row of length\n784 (=28x28). Let’s take a look at one; we need to reshape it to 2d\nfirst.\nfrom\nmatplotlib\nimport\npyplot\nimport\nnumpy\nas\nnp\npyplot\n.\nimshow\n(\nx_train\n[\n0\n]\n.\nreshape\n((\n28\n,\n28\n)),\ncmap\n=\n\"gray\"\n)\n# ``pyplot.show()`` only if not on Colab\ntry\n:\nimport\ngoogle.colab\nexcept\nImportError\n:\npyplot\n.\nshow\n()\nprint\n(\nx_train\n.\nshape\n)\n(50000, 784)\nPyTorch uses\ntorch.tensor\n, rather than numpy arrays, so we need to\nconvert our data.\nimport\ntorch\nx_train\n,\ny_train\n,\nx_valid\n,\ny_valid\n=\nmap\n(\ntorch\n.\ntensor\n,\n(\nx_train\n,\ny_train\n,\nx_valid\n,\ny_valid\n)\n)\nn\n,\nc\n=\nx_train\n.\nshape\nprint\n(\nx_train\n,\ny_train\n)\nprint\n(\nx_train\n.\nshape\n)\nprint\n(\ny_train\n.\nmin\n(),\ny_train\n.\nmax\n())\ntensor([[0., 0., 0.,  ..., 0., 0., 0.],\n        [0., 0., 0.,  ..., 0., 0., 0.],\n        [0., 0., 0.,  ..., 0., 0., 0.],\n        ...,\n        [0., 0., 0.,  ..., 0., 0., 0.],\n        [0., 0., 0.,  ..., 0., 0., 0.],\n        [0., 0., 0.,  ..., 0., 0., 0.]]) tensor([5, 0, 4,  ..., 8, 4, 8])\ntorch.Size([50000, 784])\ntensor(0) tensor(9)\nNeural net from scratch (without\ntorch.nn\n)\n#\nLet’s first create a model using nothing but PyTorch tensor operations. We’re assuming\nyou’re already familiar with the basics of neural networks. (If you’re not, you can\nlearn them at\ncourse.fast.ai\n).\nPyTorch provides methods to create random or zero-filled tensors, which we will\nuse to create our weights and bias for a simple linear model. These are just regular\ntensors, with one very special addition: we tell PyTorch that they require a\ngradient. This causes PyTorch to record all of the operations done on the tensor,\nso that it can calculate the gradient during back-propagation\nautomatically\n!\nFor the weights, we set\nrequires_grad\nafter\nthe initialization, since we\ndon’t want that step included in the gradient. (Note that a trailing\n_\nin\nPyTorch signifies that the operation is performed in-place.)\nNote\nWe are initializing the weights here with\nXavier initialisation\n(by multiplying with\n1/sqrt(n)\n).\nimport\nmath\nweights\n=\ntorch\n.\nrandn\n(\n784\n,\n10\n)\n/\nmath\n.\nsqrt\n(\n784\n)\nweights\n.\nrequires_grad_\n()\nbias\n=\ntorch\n.\nzeros\n(\n10\n,\nrequires_grad\n=\nTrue\n)\nThanks to PyTorch’s ability to calculate gradients automatically, we can\nuse any standard Python function (or callable object) as a model! So\nlet’s just write a plain matrix multiplication and broadcasted addition\nto create a simple linear model. We also need an activation function, so\nwe’ll write\nlog_softmax\nand use it. Remember: although PyTorch\nprovides lots of prewritten loss functions, activation functions, and\nso forth, you can easily write your own using plain python. PyTorch will\neven create fast accelerator or vectorized CPU code for your function\nautomatically.\ndef\nlog_softmax\n(\nx\n):\nreturn\nx\n-\nx\n.\nexp\n()\n.\nsum\n(\n-\n1\n)\n.\nlog\n()\n.\nunsqueeze\n(\n-\n1\n)\ndef\nmodel\n(\nxb\n):\nreturn\nlog_softmax\n(\nxb\n@\nweights\n+\nbias\n)\nIn the above, the\n@\nstands for the matrix multiplication operation. We will call\nour function on one batch of data (in this case, 64 images).  This is\none\nforward pass\n.  Note that our predictions won’t be any better than\nrandom at this stage, since we start with random weights.\nbs\n=\n64\n# batch size\nxb\n=\nx_train\n[\n0\n:\nbs\n]\n# a mini-batch from x\npreds\n=\nmodel\n(\nxb\n)\n# predictions\npreds\n[\n0\n],\npreds\n.\nshape\nprint\n(\npreds\n[\n0\n],\npreds\n.\nshape\n)\ntensor([-2.5495, -2.6069, -2.0307, -1.6827, -2.8096, -2.8205, -2.7063, -1.8902,\n        -2.6554, -2.0948], grad_fn=<SelectBackward0>) torch.Size([64, 10])\nAs you see, the\npreds\ntensor contains not only the tensor values, but also a\ngradient function. We’ll use this later to do backprop.\nLet’s implement negative log-likelihood to use as the loss function\n(again, we can just use standard Python):\ndef\nnll\n(\ninput\n,\ntarget\n):\nreturn\n-\ninput\n[\nrange\n(\ntarget\n.\nshape\n[\n0\n]),\ntarget\n]\n.\nmean\n()\nloss_func\n=\nnll\nLet’s check our loss with our random model, so we can see if we improve\nafter a backprop pass later.\nyb\n=\ny_train\n[\n0\n:\nbs\n]\nprint\n(\nloss_func\n(\npreds\n,\nyb\n))\ntensor(2.2345, grad_fn=<NegBackward0>)\nLet’s also implement a function to calculate the accuracy of our model.\nFor each prediction, if the index with the largest value matches the\ntarget value, then the prediction was correct.\ndef\naccuracy\n(\nout\n,\nyb\n):\npreds\n=\ntorch\n.\nargmax\n(\nout\n,\ndim\n=\n1\n)\nreturn\n(\npreds\n==\nyb\n)\n.\nfloat\n()\n.\nmean\n()\nLet’s check the accuracy of our random model, so we can see if our\naccuracy improves as our loss improves.\nprint\n(\naccuracy\n(\npreds\n,\nyb\n))\ntensor(0.2188)\nWe can now run a training loop.  For each iteration, we will:\nselect a mini-batch of data (of size\nbs\n)\nuse the model to make predictions\ncalculate the loss\nloss.backward()\nupdates the gradients of the model, in this case,\nweights\nand\nbias\n.\nWe now use these gradients to update the weights and bias.  We do this\nwithin the\ntorch.no_grad()\ncontext manager, because we do not want these\nactions to be recorded for our next calculation of the gradient.  You can read\nmore about how PyTorch’s Autograd records operations\nhere\n.\nWe then set the\ngradients to zero, so that we are ready for the next loop.\nOtherwise, our gradients would record a running tally of all the operations\nthat had happened (i.e.\nloss.backward()\nadds\nthe gradients to whatever is\nalready stored, rather than replacing them).\nTip\nYou can use the standard python debugger to step through PyTorch\ncode, allowing you to check the various variable values at each step.\nUncomment\nset_trace()\nbelow to try it out.\nfrom\nIPython.core.debugger\nimport\nset_trace\nlr\n=\n0.5\n# learning rate\nepochs\n=\n2\n# how many epochs to train for\nfor\nepoch\nin\nrange\n(\nepochs\n):\nfor\ni\nin\nrange\n((\nn\n-\n1\n)\n//\nbs\n+\n1\n):\n#         set_trace()\nstart_i\n=\ni\n*\nbs\nend_i\n=\nstart_i\n+\nbs\nxb\n=\nx_train\n[\nstart_i\n:\nend_i\n]\nyb\n=\ny_train\n[\nstart_i\n:\nend_i\n]\npred\n=\nmodel\n(\nxb\n)\nloss\n=\nloss_func\n(\npred\n,\nyb\n)\nloss\n.\nbackward\n()\nwith\ntorch\n.\nno_grad\n():\nweights\n-=\nweights\n.\ngrad\n*\nlr\nbias\n-=\nbias\n.\ngrad\n*\nlr\nweights\n.\ngrad\n.\nzero_\n()\nbias\n.\ngrad\n.\nzero_\n()\nThat’s it: we’ve created and trained a minimal neural network (in this case, a\nlogistic regression, since we have no hidden layers) entirely from scratch!\nLet’s check the loss and accuracy and compare those to what we got\nearlier. We expect that the loss will have decreased and accuracy to\nhave increased, and they have.\nprint\n(\nloss_func\n(\nmodel\n(\nxb\n),\nyb\n),\naccuracy\n(\nmodel\n(\nxb\n),\nyb\n))\ntensor(0.0840, grad_fn=<NegBackward0>) tensor(1.)\nUsing\ntorch.nn.functional\n#\nWe will now refactor our code, so that it does the same thing as before, only\nwe’ll start taking advantage of PyTorch’s\nnn\nclasses to make it more concise\nand flexible. At each step from here, we should be making our code one or more\nof: shorter, more understandable, and/or more flexible.\nThe first and easiest step is to make our code shorter by replacing our\nhand-written activation and loss functions with those from\ntorch.nn.functional\n(which is generally imported into the namespace\nF\nby convention). This module\ncontains all the functions in the\ntorch.nn\nlibrary (whereas other parts of the\nlibrary contain classes). As well as a wide range of loss and activation\nfunctions, you’ll also find here some convenient functions for creating neural\nnets, such as pooling functions. (There are also functions for doing convolutions,\nlinear layers, etc, but as we’ll see, these are usually better handled using\nother parts of the library.)\nIf you’re using negative log likelihood loss and log softmax activation,\nthen Pytorch provides a single function\nF.cross_entropy\nthat combines\nthe two. So we can even remove the activation function from our model.\nimport\ntorch.nn.functional\nas\nF\nloss_func\n=\nF\n.\ncross_entropy\ndef\nmodel\n(\nxb\n):\nreturn\nxb\n@\nweights\n+\nbias\nNote that we no longer call\nlog_softmax\nin the\nmodel\nfunction. Let’s\nconfirm that our loss and accuracy are the same as before:\nprint\n(\nloss_func\n(\nmodel\n(\nxb\n),\nyb\n),\naccuracy\n(\nmodel\n(\nxb\n),\nyb\n))\ntensor(0.0840, grad_fn=<NllLossBackward0>) tensor(1.)\nRefactor using\nnn.Module\n#\nNext up, we’ll use\nnn.Module\nand\nnn.Parameter\n, for a clearer and more\nconcise training loop. We subclass\nnn.Module\n(which itself is a class and\nable to keep track of state).  In this case, we want to create a class that\nholds our weights, bias, and method for the forward step.\nnn.Module\nhas a\nnumber of attributes and methods (such as\n.parameters()\nand\n.zero_grad()\n)\nwhich we will be using.\nNote\nnn.Module\n(uppercase M) is a PyTorch specific concept, and is a\nclass we’ll be using a lot.\nnn.Module\nis not to be confused with the Python\nconcept of a (lowercase\nm\n)\nmodule\n,\nwhich is a file of Python code that can be imported.\nfrom\ntorch\nimport\nnn\nclass\nMnist_Logistic\n(\nnn\n.\nModule\n):\ndef\n__init__\n(\nself\n):\nsuper\n()\n.\n__init__\n()\nself\n.\nweights\n=\nnn\n.\nParameter\n(\ntorch\n.\nrandn\n(\n784\n,\n10\n)\n/\nmath\n.\nsqrt\n(\n784\n))\nself\n.\nbias\n=\nnn\n.\nParameter\n(\ntorch\n.\nzeros\n(\n10\n))\ndef\nforward\n(\nself\n,\nxb\n):\nreturn\nxb\n@\nself\n.\nweights\n+\nself\n.\nbias\nSince we’re now using an object instead of just using a function, we\nfirst have to instantiate our model:\nmodel\n=\nMnist_Logistic\n()\nNow we can calculate the loss in the same way as before. Note that\nnn.Module\nobjects are used as if they are functions (i.e they are\ncallable\n), but behind the scenes Pytorch will call our\nforward\nmethod automatically.\nprint\n(\nloss_func\n(\nmodel\n(\nxb\n),\nyb\n))\ntensor(2.4244, grad_fn=<NllLossBackward0>)\nPreviously for our training loop we had to update the values for each parameter\nby name, and manually zero out the grads for each parameter separately, like this:\nwith\ntorch\n.\nno_grad\n():\nweights\n-=\nweights\n.\ngrad\n*\nlr\nbias\n-=\nbias\n.\ngrad\n*\nlr\nweights\n.\ngrad\n.\nzero_\n()\nbias\n.\ngrad\n.\nzero_\n()\nNow we can take advantage of model.parameters() and model.zero_grad() (which\nare both defined by PyTorch for\nnn.Module\n) to make those steps more concise\nand less prone to the error of forgetting some of our parameters, particularly\nif we had a more complicated model:\nwith\ntorch\n.\nno_grad\n():\nfor\np\nin\nmodel\n.\nparameters\n():\np\n-=\np\n.\ngrad\n*\nlr\nmodel\n.\nzero_grad\n()\nWe’ll wrap our little training loop in a\nfit\nfunction so we can run it\nagain later.\ndef\nfit\n():\nfor\nepoch\nin\nrange\n(\nepochs\n):\nfor\ni\nin\nrange\n((\nn\n-\n1\n)\n//\nbs\n+\n1\n):\nstart_i\n=\ni\n*\nbs\nend_i\n=\nstart_i\n+\nbs\nxb\n=\nx_train\n[\nstart_i\n:\nend_i\n]\nyb\n=\ny_train\n[\nstart_i\n:\nend_i\n]\npred\n=\nmodel\n(\nxb\n)\nloss\n=\nloss_func\n(\npred\n,\nyb\n)\nloss\n.\nbackward\n()\nwith\ntorch\n.\nno_grad\n():\nfor\np\nin\nmodel\n.\nparameters\n():\np\n-=\np\n.\ngrad\n*\nlr\nmodel\n.\nzero_grad\n()\nfit\n()\nLet’s double-check that our loss has gone down:\nprint\n(\nloss_func\n(\nmodel\n(\nxb\n),\nyb\n))\ntensor(0.0797, grad_fn=<NllLossBackward0>)\nRefactor using\nnn.Linear\n#\nWe continue to refactor our code.  Instead of manually defining and\ninitializing\nself.weights\nand\nself.bias\n, and calculating\nxb\n@\nself.weights\n+\nself.bias\n, we will instead use the Pytorch class\nnn.Linear\nfor a\nlinear layer, which does all that for us. Pytorch has many types of\npredefined layers that can greatly simplify our code, and often makes it\nfaster too.\nclass\nMnist_Logistic\n(\nnn\n.\nModule\n):\ndef\n__init__\n(\nself\n):\nsuper\n()\n.\n__init__\n()\nself\n.\nlin\n=\nnn\n.\nLinear\n(\n784\n,\n10\n)\ndef\nforward\n(\nself\n,\nxb\n):\nreturn\nself\n.\nlin\n(\nxb\n)\nWe instantiate our model and calculate the loss in the same way as before:\nmodel\n=\nMnist_Logistic\n()\nprint\n(\nloss_func\n(\nmodel\n(\nxb\n),\nyb\n))\ntensor(2.4200, grad_fn=<NllLossBackward0>)\nWe are still able to use our same\nfit\nmethod as before.\nfit\n()\nprint\n(\nloss_func\n(\nmodel\n(\nxb\n),\nyb\n))\ntensor(0.0817, grad_fn=<NllLossBackward0>)\nRefactor using\ntorch.optim\n#\nPytorch also has a package with various optimization algorithms,\ntorch.optim\n.\nWe can use the\nstep\nmethod from our optimizer to take a forward step, instead\nof manually updating each parameter.\nThis will let us replace our previous manually coded optimization step:\nwith\ntorch\n.\nno_grad\n():\nfor\np\nin\nmodel\n.\nparameters\n():\np\n-=\np\n.\ngrad\n*\nlr\nmodel\n.\nzero_grad\n()\nand instead use just:\nopt\n.\nstep\n()\nopt\n.\nzero_grad\n()\n(\noptim.zero_grad()\nresets the gradient to 0 and we need to call it before\ncomputing the gradient for the next minibatch.)\nfrom\ntorch\nimport\noptim\nWe’ll define a little function to create our model and optimizer so we\ncan reuse it in the future.\ndef\nget_model\n():\nmodel\n=\nMnist_Logistic\n()\nreturn\nmodel\n,\noptim\n.\nSGD\n(\nmodel\n.\nparameters\n(),\nlr\n=\nlr\n)\nmodel\n,\nopt\n=\nget_model\n()\nprint\n(\nloss_func\n(\nmodel\n(\nxb\n),\nyb\n))\nfor\nepoch\nin\nrange\n(\nepochs\n):\nfor\ni\nin\nrange\n((\nn\n-\n1\n)\n//\nbs\n+\n1\n):\nstart_i\n=\ni\n*\nbs\nend_i\n=\nstart_i\n+\nbs\nxb\n=\nx_train\n[\nstart_i\n:\nend_i\n]\nyb\n=\ny_train\n[\nstart_i\n:\nend_i\n]\npred\n=\nmodel\n(\nxb\n)\nloss\n=\nloss_func\n(\npred\n,\nyb\n)\nloss\n.\nbackward\n()\nopt\n.\nstep\n()\nopt\n.\nzero_grad\n()\nprint\n(\nloss_func\n(\nmodel\n(\nxb\n),\nyb\n))\ntensor(2.4211, grad_fn=<NllLossBackward0>)\ntensor(0.0831, grad_fn=<NllLossBackward0>)\nRefactor using Dataset\n#\nPyTorch has an abstract Dataset class.  A Dataset can be anything that has\na\n__len__\nfunction (called by Python’s standard\nlen\nfunction) and\na\n__getitem__\nfunction as a way of indexing into it.\nThis tutorial\nwalks through a nice example of creating a custom\nFacialLandmarkDataset\nclass\nas a subclass of\nDataset\n.\nPyTorch’s\nTensorDataset\nis a Dataset wrapping tensors. By defining a length and way of indexing,\nthis also gives us a way to iterate, index, and slice along the first\ndimension of a tensor. This will make it easier to access both the\nindependent and dependent variables in the same line as we train.\nfrom\ntorch.utils.data\nimport\nTensorDataset\nBoth\nx_train\nand\ny_train\ncan be combined in a single\nTensorDataset\n,\nwhich will be easier to iterate over and slice.\ntrain_ds\n=\nTensorDataset\n(\nx_train\n,\ny_train\n)\nPreviously, we had to iterate through minibatches of\nx\nand\ny\nvalues separately:\nxb\n=\nx_train\n[\nstart_i\n:\nend_i\n]\nyb\n=\ny_train\n[\nstart_i\n:\nend_i\n]\nNow, we can do these two steps together:\nxb\n,\nyb\n=\ntrain_ds\n[\ni\n*\nbs\n:\ni\n*\nbs\n+\nbs\n]\nmodel\n,\nopt\n=\nget_model\n()\nfor\nepoch\nin\nrange\n(\nepochs\n):\nfor\ni\nin\nrange\n((\nn\n-\n1\n)\n//\nbs\n+\n1\n):\nxb\n,\nyb\n=\ntrain_ds\n[\ni\n*\nbs\n:\ni\n*\nbs\n+\nbs\n]\npred\n=\nmodel\n(\nxb\n)\nloss\n=\nloss_func\n(\npred\n,\nyb\n)\nloss\n.\nbackward\n()\nopt\n.\nstep\n()\nopt\n.\nzero_grad\n()\nprint\n(\nloss_func\n(\nmodel\n(\nxb\n),\nyb\n))\ntensor(0.0798, grad_fn=<NllLossBackward0>)\nRefactor using\nDataLoader\n#\nPyTorch’s\nDataLoader\nis responsible for managing batches. You can\ncreate a\nDataLoader\nfrom any\nDataset\n.\nDataLoader\nmakes it easier\nto iterate over batches. Rather than having to use\ntrain_ds[i*bs\n:\ni*bs+bs]\n,\nthe\nDataLoader\ngives us each minibatch automatically.\nfrom\ntorch.utils.data\nimport\nDataLoader\ntrain_ds\n=\nTensorDataset\n(\nx_train\n,\ny_train\n)\ntrain_dl\n=\nDataLoader\n(\ntrain_ds\n,\nbatch_size\n=\nbs\n)\nPreviously, our loop iterated over batches\n(xb,\nyb)\nlike this:\nfor\ni\nin\nrange\n((\nn\n-\n1\n)\n//\nbs\n+\n1\n):\nxb\n,\nyb\n=\ntrain_ds\n[\ni\n*\nbs\n:\ni\n*\nbs\n+\nbs\n]\npred\n=\nmodel\n(\nxb\n)\nNow, our loop is much cleaner, as\n(xb,\nyb)\nare loaded automatically from the data loader:\nfor\nxb\n,\nyb\nin\ntrain_dl\n:\npred\n=\nmodel\n(\nxb\n)\nmodel\n,\nopt\n=\nget_model\n()\nfor\nepoch\nin\nrange\n(\nepochs\n):\nfor\nxb\n,\nyb\nin\ntrain_dl\n:\npred\n=\nmodel\n(\nxb\n)\nloss\n=\nloss_func\n(\npred\n,\nyb\n)\nloss\n.\nbackward\n()\nopt\n.\nstep\n()\nopt\n.\nzero_grad\n()\nprint\n(\nloss_func\n(\nmodel\n(\nxb\n),\nyb\n))\ntensor(0.0832, grad_fn=<NllLossBackward0>)\nThanks to PyTorch’s\nnn.Module\n,\nnn.Parameter\n,\nDataset\n, and\nDataLoader\n,\nour training loop is now dramatically smaller and easier to understand. Let’s\nnow try to add the basic features necessary to create effective models in practice.\nAdd validation\n#\nIn section 1, we were just trying to get a reasonable training loop set up for\nuse on our training data.  In reality, you\nalways\nshould also have\na\nvalidation set\n, in order\nto identify if you are overfitting.\nShuffling the training data is\nimportant\nto prevent correlation between batches and overfitting. On the other hand, the\nvalidation loss will be identical whether we shuffle the validation set or not.\nSince shuffling takes extra time, it makes no sense to shuffle the validation data.\nWe’ll use a batch size for the validation set that is twice as large as\nthat for the training set. This is because the validation set does not\nneed backpropagation and thus takes less memory (it doesn’t need to\nstore the gradients). We take advantage of this to use a larger batch\nsize and compute the loss more quickly.\ntrain_ds\n=\nTensorDataset\n(\nx_train\n,\ny_train\n)\ntrain_dl\n=\nDataLoader\n(\ntrain_ds\n,\nbatch_size\n=\nbs\n,\nshuffle\n=\nTrue\n)\nvalid_ds\n=\nTensorDataset\n(\nx_valid\n,\ny_valid\n)\nvalid_dl\n=\nDataLoader\n(\nvalid_ds\n,\nbatch_size\n=\nbs\n*\n2\n)\nWe will calculate and print the validation loss at the end of each epoch.\n(Note that we always call\nmodel.train()\nbefore training, and\nmodel.eval()\nbefore inference, because these are used by layers such as\nnn.BatchNorm2d\nand\nnn.Dropout\nto ensure appropriate behavior for these different phases.)\nmodel\n,\nopt\n=\nget_model\n()\nfor\nepoch\nin\nrange\n(\nepochs\n):\nmodel\n.\ntrain\n()\nfor\nxb\n,\nyb\nin\ntrain_dl\n:\npred\n=\nmodel\n(\nxb\n)\nloss\n=\nloss_func\n(\npred\n,\nyb\n)\nloss\n.\nbackward\n()\nopt\n.\nstep\n()\nopt\n.\nzero_grad\n()\nmodel\n.\neval\n()\nwith\ntorch\n.\nno_grad\n():\nvalid_loss\n=\nsum\n(\nloss_func\n(\nmodel\n(\nxb\n),\nyb\n)\nfor\nxb\n,\nyb\nin\nvalid_dl\n)\nprint\n(\nepoch\n,\nvalid_loss\n/\nlen\n(\nvalid_dl\n))\n0 tensor(0.2937)\n1 tensor(0.3261)\nCreate fit() and get_data()\n#\nWe’ll now do a little refactoring of our own. Since we go through a similar\nprocess twice of calculating the loss for both the training set and the\nvalidation set, let’s make that into its own function,\nloss_batch\n, which\ncomputes the loss for one batch.\nWe pass an optimizer in for the training set, and use it to perform\nbackprop.  For the validation set, we don’t pass an optimizer, so the\nmethod doesn’t perform backprop.\ndef\nloss_batch\n(\nmodel\n,\nloss_func\n,\nxb\n,\nyb\n,\nopt\n=\nNone\n):\nloss\n=\nloss_func\n(\nmodel\n(\nxb\n),\nyb\n)\nif\nopt\nis\nnot\nNone\n:\nloss\n.\nbackward\n()\nopt\n.\nstep\n()\nopt\n.\nzero_grad\n()\nreturn\nloss\n.\nitem\n(),\nlen\n(\nxb\n)\nfit\nruns the necessary operations to train our model and compute the\ntraining and validation losses for each epoch.\nimport\nnumpy\nas\nnp\ndef\nfit\n(\nepochs\n,\nmodel\n,\nloss_func\n,\nopt\n,\ntrain_dl\n,\nvalid_dl\n):\nfor\nepoch\nin\nrange\n(\nepochs\n):\nmodel\n.\ntrain\n()\nfor\nxb\n,\nyb\nin\ntrain_dl\n:\nloss_batch\n(\nmodel\n,\nloss_func\n,\nxb\n,\nyb\n,\nopt\n)\nmodel\n.\neval\n()\nwith\ntorch\n.\nno_grad\n():\nlosses\n,\nnums\n=\nzip\n(\n*\n[\nloss_batch\n(\nmodel\n,\nloss_func\n,\nxb\n,\nyb\n)\nfor\nxb\n,\nyb\nin\nvalid_dl\n]\n)\nval_loss\n=\nnp\n.\nsum\n(\nnp\n.\nmultiply\n(\nlosses\n,\nnums\n))\n/\nnp\n.\nsum\n(\nnums\n)\nprint\n(\nepoch\n,\nval_loss\n)\nget_data\nreturns dataloaders for the training and validation sets.\ndef\nget_data\n(\ntrain_ds\n,\nvalid_ds\n,\nbs\n):\nreturn\n(\nDataLoader\n(\ntrain_ds\n,\nbatch_size\n=\nbs\n,\nshuffle\n=\nTrue\n),\nDataLoader\n(\nvalid_ds\n,\nbatch_size\n=\nbs\n*\n2\n),\n)\nNow, our whole process of obtaining the data loaders and fitting the\nmodel can be run in 3 lines of code:\ntrain_dl\n,\nvalid_dl\n=\nget_data\n(\ntrain_ds\n,\nvalid_ds\n,\nbs\n)\nmodel\n,\nopt\n=\nget_model\n()\nfit\n(\nepochs\n,\nmodel\n,\nloss_func\n,\nopt\n,\ntrain_dl\n,\nvalid_dl\n)\n0 0.49115848174095156\n1 0.2780046082675457\nYou can use these basic 3 lines of code to train a wide variety of models.\nLet’s see if we can use them to train a convolutional neural network (CNN)!\nSwitch to CNN\n#\nWe are now going to build our neural network with three convolutional layers.\nBecause none of the functions in the previous section assume anything about\nthe model form, we’ll be able to use them to train a CNN without any modification.\nWe will use PyTorch’s predefined\nConv2d\nclass\nas our convolutional layer. We define a CNN with 3 convolutional layers.\nEach convolution is followed by a ReLU.  At the end, we perform an\naverage pooling.  (Note that\nview\nis PyTorch’s version of Numpy’s\nreshape\n)\nclass\nMnist_CNN\n(\nnn\n.\nModule\n):\ndef\n__init__\n(\nself\n):\nsuper\n()\n.\n__init__\n()\nself\n.\nconv1\n=\nnn\n.\nConv2d\n(\n1\n,\n16\n,\nkernel_size\n=\n3\n,\nstride\n=\n2\n,\npadding\n=\n1\n)\nself\n.\nconv2\n=\nnn\n.\nConv2d\n(\n16\n,\n16\n,\nkernel_size\n=\n3\n,\nstride\n=\n2\n,\npadding\n=\n1\n)\nself\n.\nconv3\n=\nnn\n.\nConv2d\n(\n16\n,\n10\n,\nkernel_size\n=\n3\n,\nstride\n=\n2\n,\npadding\n=\n1\n)\ndef\nforward\n(\nself\n,\nxb\n):\nxb\n=\nxb\n.\nview\n(\n-\n1\n,\n1\n,\n28\n,\n28\n)\nxb\n=\nF\n.\nrelu\n(\nself\n.\nconv1\n(\nxb\n))\nxb\n=\nF\n.\nrelu\n(\nself\n.\nconv2\n(\nxb\n))\nxb\n=\nF\n.\nrelu\n(\nself\n.\nconv3\n(\nxb\n))\nxb\n=\nF\n.\navg_pool2d\n(\nxb\n,\n4\n)\nreturn\nxb\n.\nview\n(\n-\n1\n,\nxb\n.\nsize\n(\n1\n))\nlr\n=\n0.1\nMomentum\nis a variation on\nstochastic gradient descent that takes previous updates into account as well\nand generally leads to faster training.\nmodel\n=\nMnist_CNN\n()\nopt\n=\noptim\n.\nSGD\n(\nmodel\n.\nparameters\n(),\nlr\n=\nlr\n,\nmomentum\n=\n0.9\n)\nfit\n(\nepochs\n,\nmodel\n,\nloss_func\n,\nopt\n,\ntrain_dl\n,\nvalid_dl\n)\n0 0.3234035752534866\n1 0.2392186249256134\nUsing\nnn.Sequential\n#\ntorch.nn\nhas another handy class we can use to simplify our code:\nSequential\n.\nA\nSequential\nobject runs each of the modules contained within it, in a\nsequential manner. This is a simpler way of writing our neural network.\nTo take advantage of this, we need to be able to easily define a\ncustom layer\nfrom a given function.  For instance, PyTorch doesn’t\nhave a\nview\nlayer, and we need to create one for our network.\nLambda\nwill create a layer that we can then use when defining a network with\nSequential\n.\nclass\nLambda\n(\nnn\n.\nModule\n):\ndef\n__init__\n(\nself\n,\nfunc\n):\nsuper\n()\n.\n__init__\n()\nself\n.\nfunc\n=\nfunc\ndef\nforward\n(\nself\n,\nx\n):\nreturn\nself\n.\nfunc\n(\nx\n)\ndef\npreprocess\n(\nx\n):\nreturn\nx\n.\nview\n(\n-\n1\n,\n1\n,\n28\n,\n28\n)\nThe model created with\nSequential\nis simple:\nmodel\n=\nnn\n.\nSequential\n(\nLambda\n(\npreprocess\n),\nnn\n.\nConv2d\n(\n1\n,\n16\n,\nkernel_size\n=\n3\n,\nstride\n=\n2\n,\npadding\n=\n1\n),\nnn\n.\nReLU\n(),\nnn\n.\nConv2d\n(\n16\n,\n16\n,\nkernel_size\n=\n3\n,\nstride\n=\n2\n,\npadding\n=\n1\n),\nnn\n.\nReLU\n(),\nnn\n.\nConv2d\n(\n16\n,\n10\n,\nkernel_size\n=\n3\n,\nstride\n=\n2\n,\npadding\n=\n1\n),\nnn\n.\nReLU\n(),\nnn\n.\nAvgPool2d\n(\n4\n),\nLambda\n(\nlambda\nx\n:\nx\n.\nview\n(\nx\n.\nsize\n(\n0\n),\n-\n1\n)),\n)\nopt\n=\noptim\n.\nSGD\n(\nmodel\n.\nparameters\n(),\nlr\n=\nlr\n,\nmomentum\n=\n0.9\n)\nfit\n(\nepochs\n,\nmodel\n,\nloss_func\n,\nopt\n,\ntrain_dl\n,\nvalid_dl\n)\n0 0.3693028878688812\n1 0.24750863423347474\nWrapping\nDataLoader\n#\nOur CNN is fairly concise, but it only works with MNIST, because:\nIt assumes the input is a 28*28 long vector\nIt assumes that the final CNN grid size is 4*4 (since that’s the average pooling kernel size we used)\nLet’s get rid of these two assumptions, so our model works with any 2d\nsingle channel image. First, we can remove the initial Lambda layer by\nmoving the data preprocessing into a generator:\ndef\npreprocess\n(\nx\n,\ny\n):\nreturn\nx\n.\nview\n(\n-\n1\n,\n1\n,\n28\n,\n28\n),\ny\nclass\nWrappedDataLoader\n:\ndef\n__init__\n(\nself\n,\ndl\n,\nfunc\n):\nself\n.\ndl\n=\ndl\nself\n.\nfunc\n=\nfunc\ndef\n__len__\n(\nself\n):\nreturn\nlen\n(\nself\n.\ndl\n)\ndef\n__iter__\n(\nself\n):\nfor\nb\nin\nself\n.\ndl\n:\nyield\n(\nself\n.\nfunc\n(\n*\nb\n))\ntrain_dl\n,\nvalid_dl\n=\nget_data\n(\ntrain_ds\n,\nvalid_ds\n,\nbs\n)\ntrain_dl\n=\nWrappedDataLoader\n(\ntrain_dl\n,\npreprocess\n)\nvalid_dl\n=\nWrappedDataLoader\n(\nvalid_dl\n,\npreprocess\n)\nNext, we can replace\nnn.AvgPool2d\nwith\nnn.AdaptiveAvgPool2d\n, which\nallows us to define the size of the\noutput\ntensor we want, rather than\nthe\ninput\ntensor we have. As a result, our model will work with any\nsize input.\nmodel\n=\nnn\n.\nSequential\n(\nnn\n.\nConv2d\n(\n1\n,\n16\n,\nkernel_size\n=\n3\n,\nstride\n=\n2\n,\npadding\n=\n1\n),\nnn\n.\nReLU\n(),\nnn\n.\nConv2d\n(\n16\n,\n16\n,\nkernel_size\n=\n3\n,\nstride\n=\n2\n,\npadding\n=\n1\n),\nnn\n.\nReLU\n(),\nnn\n.\nConv2d\n(\n16\n,\n10\n,\nkernel_size\n=\n3\n,\nstride\n=\n2\n,\npadding\n=\n1\n),\nnn\n.\nReLU\n(),\nnn\n.\nAdaptiveAvgPool2d\n(\n1\n),\nLambda\n(\nlambda\nx\n:\nx\n.\nview\n(\nx\n.\nsize\n(\n0\n),\n-\n1\n)),\n)\nopt\n=\noptim\n.\nSGD\n(\nmodel\n.\nparameters\n(),\nlr\n=\nlr\n,\nmomentum\n=\n0.9\n)\nLet’s try it out:\nfit\n(\nepochs\n,\nmodel\n,\nloss_func\n,\nopt\n,\ntrain_dl\n,\nvalid_dl\n)\n0 0.38702895646095276\n1 0.24111936665773392\nUsing your\nAccelerator\n#\nIf you’re lucky enough to have access to an accelerator such as CUDA (you can\nrent one for about $0.50/hour from most cloud providers) you can\nuse it to speed up your code. First check that your accelerator is working in\nPytorch:\n# If the current accelerator is available, we will use it. Otherwise, we use the CPU.\ndevice\n=\ntorch\n.\naccelerator\n.\ncurrent_accelerator\n()\n.\ntype\nif\ntorch\n.\naccelerator\n.\nis_available\n()\nelse\n\"cpu\"\nprint\n(\nf\n\"Using\n{\ndevice\n}\ndevice\"\n)\nUsing cuda device\nLet’s update\npreprocess\nto move batches to the accelerator:\ndef\npreprocess\n(\nx\n,\ny\n):\nreturn\nx\n.\nview\n(\n-\n1\n,\n1\n,\n28\n,\n28\n)\n.\nto\n(\ndevice\n),\ny\n.\nto\n(\ndevice\n)\ntrain_dl\n,\nvalid_dl\n=\nget_data\n(\ntrain_ds\n,\nvalid_ds\n,\nbs\n)\ntrain_dl\n=\nWrappedDataLoader\n(\ntrain_dl\n,\npreprocess\n)\nvalid_dl\n=\nWrappedDataLoader\n(\nvalid_dl\n,\npreprocess\n)\nFinally, we can move our model to the accelerator.\nmodel\n.\nto\n(\ndevice\n)\nopt\n=\noptim\n.\nSGD\n(\nmodel\n.\nparameters\n(),\nlr\n=\nlr\n,\nmomentum\n=\n0.9\n)\nYou should find it runs faster now:\nfit\n(\nepochs\n,\nmodel\n,\nloss_func\n,\nopt\n,\ntrain_dl\n,\nvalid_dl\n)\n0 0.21412573025226592\n1 0.18783334712982178\nClosing thoughts\n#\nWe now have a general data pipeline and training loop which you can use for\ntraining many types of models using Pytorch. To see how simple training a model\ncan now be, take a look at the\nmnist_sample notebook\n.\nOf course, there are many things you’ll want to add, such as data augmentation,\nhyperparameter tuning, monitoring training, transfer learning, and so forth.\nThese features are available in the fastai library, which has been developed\nusing the same design approach shown in this tutorial, providing a natural\nnext step for practitioners looking to take their models further.\nWe promised at the start of this tutorial we’d explain through example each of\ntorch.nn\n,\ntorch.optim\n,\nDataset\n, and\nDataLoader\n. So let’s summarize\nwhat we’ve seen:\ntorch.nn\n:\nModule\n: creates a callable which behaves like a function, but can also\ncontain state(such as neural net layer weights). It knows what\nParameter\n(s) it\ncontains and can zero all their gradients, loop through them for weight updates, etc.\nParameter\n: a wrapper for a tensor that tells a\nModule\nthat it has weights\nthat need updating during backprop. Only tensors with the\nrequires_grad\nattribute set are updated\nfunctional\n: a module(usually imported into the\nF\nnamespace by convention)\nwhich contains activation functions, loss functions, etc, as well as non-stateful\nversions of layers such as convolutional and linear layers.\ntorch.optim\n: Contains optimizers such as\nSGD\n, which update the weights\nof\nParameter\nduring the backward step\nDataset\n: An abstract interface of objects with a\n__len__\nand a\n__getitem__\n,\nincluding classes provided with Pytorch such as\nTensorDataset\nDataLoader\n: Takes any\nDataset\nand creates an iterator which returns batches of data.\nTotal running time of the script:\n(0 minutes 25.118 seconds)\nDownload\nJupyter\nnotebook:\nnn_tutorial.ipynb\nDownload\nPython\nsource\ncode:\nnn_tutorial.py\nDownload\nzipped:\nnn_tutorial.zip",
  "metadata": {
    "title": "What is torch.nn really? — PyTorch Tutorials 2.14.0+cu130 documentation",
    "description": null,
    "keywords": null,
    "author": null,
    "og:image": "https://docs.pytorch.org/docs/stable/_static/img/pytorch_seo.png",
    "article:modified_time": "2022-07-20T23:02:43+00:00"
  },
  "paywall": {
    "listed_paid": false,
    "detected": false,
    "marker": "",
    "public_only": false,
    "policy": "付费/会员内容只保留页面公开可见部分，不绕过付费墙"
  },
  "crawl": {
    "success": true,
    "status_code": 200,
    "error": ""
  },
  "code_blocks": [
    {
      "file": "attachments/49594a3fe0ed/code/body-000.txt",
      "chars": 368,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-001.txt",
      "chars": 173,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-002.txt",
      "chars": 236,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-003.txt",
      "chars": 13,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-004.txt",
      "chars": 215,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-005.txt",
      "chars": 332,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-006.txt",
      "chars": 129,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-007.txt",
      "chars": 131,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-008.txt",
      "chars": 149,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-009.txt",
      "chars": 155,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-010.txt",
      "chars": 98,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-011.txt",
      "chars": 47,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-012.txt",
      "chars": 39,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-013.txt",
      "chars": 100,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-014.txt",
      "chars": 27,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-015.txt",
      "chars": 15,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-016.txt",
      "chars": 592,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-017.txt",
      "chars": 57,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-018.txt",
      "chars": 50,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-019.txt",
      "chars": 108,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-020.txt",
      "chars": 57,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-021.txt",
      "chars": 54,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-022.txt",
      "chars": 304,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-023.txt",
      "chars": 25,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-024.txt",
      "chars": 32,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-025.txt",
      "chars": 43,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-026.txt",
      "chars": 129,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-027.txt",
      "chars": 94,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-028.txt",
      "chars": 482,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-029.txt",
      "chars": 32,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-030.txt",
      "chars": 43,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-031.txt",
      "chars": 178,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-032.txt",
      "chars": 57,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-033.txt",
      "chars": 43,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-034.txt",
      "chars": 39,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-035.txt",
      "chars": 43,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-036.txt",
      "chars": 94,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-037.txt",
      "chars": 27,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-038.txt",
      "chars": 24,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-039.txt",
      "chars": 514,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-040.txt",
      "chars": 86,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-041.txt",
      "chars": 43,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-042.txt",
      "chars": 43,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-043.txt",
      "chars": 56,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-044.txt",
      "chars": 33,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-045.txt",
      "chars": 301,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-046.txt",
      "chars": 43,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-047.txt",
      "chars": 131,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-048.txt",
      "chars": 89,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-049.txt",
      "chars": 44,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-050.txt",
      "chars": 243,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-051.txt",
      "chars": 43,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-052.txt",
      "chars": 199,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-053.txt",
      "chars": 392,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-054.txt",
      "chars": 34,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-055.txt",
      "chars": 213,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-056.txt",
      "chars": 497,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-057.txt",
      "chars": 165,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-058.txt",
      "chars": 134,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-059.txt",
      "chars": 43,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-060.txt",
      "chars": 557,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-061.txt",
      "chars": 133,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-062.txt",
      "chars": 42,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-063.txt",
      "chars": 216,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-064.txt",
      "chars": 450,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-065.txt",
      "chars": 43,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-066.txt",
      "chars": 459,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-067.txt",
      "chars": 378,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-068.txt",
      "chars": 55,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-069.txt",
      "chars": 44,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-070.txt",
      "chars": 219,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-071.txt",
      "chars": 18,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-072.txt",
      "chars": 238,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-073.txt",
      "chars": 74,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-074.txt",
      "chars": 55,
      "language": "text"
    },
    {
      "file": "attachments/49594a3fe0ed/code/body-075.txt",
      "chars": 44,
      "language": "text"
    }
  ],
  "images": [
    {
      "status": "ok",
      "url": "https://docs.pytorch.org/tutorials/_images/sphx_glr_nn_tutorial_001.png",
      "bytes": 8164,
      "reused": false,
      "local_path": "attachments/49594a3fe0ed/images/body-15ba6a164e4c696d49f1.png",
      "src": "https://docs.pytorch.org/tutorials/_images/sphx_glr_nn_tutorial_001.png",
      "alt": "nn tutorial",
      "downloaded": true
    }
  ],
  "audio": [],
  "videos": [],
  "links": [
    {
      "href": "https://www.fast.ai/",
      "label": "fast.ai",
      "type": "a"
    },
    {
      "href": "https://pytorch.org/docs/stable/nn.html",
      "label": "torch.nn",
      "type": "a"
    },
    {
      "href": "https://pytorch.org/docs/stable/optim.html",
      "label": "torch.optim",
      "type": "a"
    },
    {
      "href": "https://pytorch.org/docs/stable/data.html?highlight=dataset",
      "label": "Dataset",
      "type": "a"
    },
    {
      "href": "https://pytorch.org/docs/stable/data.html?highlight=dataloader",
      "label": "DataLoader",
      "type": "a"
    },
    {
      "href": "https://yann.lecun.com/exdb/mnist/index.html",
      "label": "MNIST",
      "type": "a"
    },
    {
      "href": "https://docs.python.org/3/library/pathlib.html",
      "label": "pathlib",
      "type": "a"
    },
    {
      "href": "http://docs.python-requests.org/en/master/",
      "label": "requests",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/docs/stable/tensors.html",
      "label": "x_train",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/docs/stable/size.html",
      "label": "x_train . shape",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/docs/stable/generated/torch.tensor.html",
      "label": "torch . tensor",
      "type": "a"
    },
    {
      "href": "https://course.fast.ai/",
      "label": "course.fast.ai",
      "type": "a"
    },
    {
      "href": "http://proceedings.mlr.press/v9/glorot10a/glorot10a.pdf",
      "label": "Xavier initialisation",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/docs/stable/generated/torch.randn.html",
      "label": "torch . randn",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/docs/stable/generated/torch.zeros.html",
      "label": "torch . zeros",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/docs/stable/generated/torch.nn.Sequential.html",
      "label": "model",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/docs/stable/generated/torch.argmax.html",
      "label": "torch . argmax",
      "type": "a"
    },
    {
      "href": "https://pytorch.org/docs/stable/notes/autograd.html",
      "label": "here",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/docs/stable/generated/torch.Tensor.backward.html",
      "label": "loss . backward",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/docs/stable/generated/torch.no_grad.html",
      "label": "torch . no_grad",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/docs/stable/generated/torch.nn.functional.cross_entropy.html",
      "label": "F . cross_entropy",
      "type": "a"
    },
    {
      "href": "https://docs.python.org/3/tutorial/modules.html",
      "label": "module",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/docs/stable/generated/torch.nn.Module.html",
      "label": "nn . Module",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/docs/stable/generated/torch.nn.parameter.Parameter.html",
      "label": "nn . Parameter",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/docs/stable/generated/torch.nn.Linear.html",
      "label": "nn . Linear",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/docs/stable/generated/torch.optim.SGD.html",
      "label": "opt . step",
      "type": "a"
    },
    {
      "href": "https://pytorch.org/tutorials/beginner/data_loading_tutorial.html",
      "label": "This tutorial",
      "type": "a"
    },
    {
      "href": "https://pytorch.org/docs/stable/_modules/torch/utils/data/dataset.html",
      "label": "TensorDataset",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/docs/stable/data.html",
      "label": "TensorDataset",
      "type": "a"
    },
    {
      "href": "https://www.fast.ai/2017/11/13/validation-sets/",
      "label": "validation set",
      "type": "a"
    },
    {
      "href": "https://www.quora.com/Does-the-order-of-training-data-matter-when-training-neural-networks",
      "label": "important",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/docs/stable/generated/torch.nn.Conv2d.html",
      "label": "nn . Conv2d",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/docs/stable/generated/torch.nn.functional.relu.html",
      "label": "F . relu",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/docs/stable/generated/torch.nn.functional.avg_pool2d.html",
      "label": "F . avg_pool2d",
      "type": "a"
    },
    {
      "href": "https://cs231n.github.io/neural-networks-3/",
      "label": "Momentum",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/docs/stable/generated/torch.nn.ReLU.html",
      "label": "nn . ReLU",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/docs/stable/generated/torch.nn.AvgPool2d.html",
      "label": "nn . AvgPool2d",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/docs/stable/generated/torch.nn.AdaptiveAvgPool2d.html",
      "label": "nn . AdaptiveAvgPool2d",
      "type": "a"
    },
    {
      "href": "https://pytorch.org/docs/stable/torch.html",
      "label": "Accelerator",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/docs/stable/generated/torch.accelerator.current_accelerator.html",
      "label": "torch . accelerator . current_accelerator",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/docs/stable/generated/torch.accelerator.is_available.html",
      "label": "torch . accelerator . is_available",
      "type": "a"
    },
    {
      "href": "https://github.com/fastai/fastai_dev/blob/master/dev_nb/mnist_sample.ipynb",
      "label": "mnist_sample notebook",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/tutorials/_downloads/d9398fce39ca80dc4bb8b8ea55b575a8/nn_tutorial.ipynb",
      "label": "Download Jupyter notebook: nn_tutorial.ipynb",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/tutorials/_downloads/f16255c783f9e487235b8eff6c8792b9/nn_tutorial.py",
      "label": "Download Python source code: nn_tutorial.py",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/tutorials/_downloads/15164824fff70e07a432631c2b8dbaae/nn_tutorial.zip",
      "label": "Download zipped: nn_tutorial.zip",
      "type": "a"
    }
  ],
  "repair_version": 2,
  "repaired_at": "2026-10-14T10:53:53Z",
  "source_url": "https://pytorch.org/tutorials/beginner/nn_tutorial.html",
  "papers": [
    {
      "status": "ok",
      "url": "http://proceedings.mlr.press/v9/glorot10a/glorot10a.pdf",
      "bytes": 1647622,
      "reused": false,
      "local_path": "papers/repair/96efcdd1070a50d5d6d5.pdf"
    }
  ],
  "actual_video_files": [],
  "body_chars": 28987,
  "expected_body_images": 1,
  "discovery_status": "ok",
  "delivery_status": "ok",
  "missing_reason": "",
  "raw_html": "attachments/49594a3fe0ed/pages/source.html",
  "local_html": "attachments/49594a3fe0ed/pages/article.html",
  "body_base_path": "attachments/49594a3fe0ed/pages/",
  "body_markdown_path": "attachments/49594a3fe0ed/pages/article.md"
}