{
  "source_file": "0930",
  "source_platform": "研究机构",
  "author": "PyTorch Tutorials",
  "title_listed": "Adversarial Example Generation",
  "keywords": "对抗样本,FGSM,安全",
  "domain": "图像处理",
  "content_type": "教程",
  "is_paid_flag": "否",
  "url": "https://pytorch.org/tutorials/beginner/fgsm_tutorial.html",
  "publish_date": "持续更新",
  "corpus_stage": "图像处理",
  "notes": "对抗样本生成",
  "id": "5da26e70a48f",
  "collected_at": "2026-10-02T16:34:20",
  "title": "Adversarial Example Generation — PyTorch Tutorials 2.14.0+cu130 documentation",
  "body_markdown": "Note\nGo to the end\nto download the full example code.\nAdversarial Example Generation\n#\nCreated On: Aug 14, 2018 | Last Updated: Jan 27, 2025 | Last Verified: Not Verified\nAuthor:\nNathan Inkawhich\nIf you are reading this, hopefully you can appreciate how effective some\nmachine learning models are. Research is constantly pushing ML models to\nbe faster, more accurate, and more efficient. However, an often\noverlooked aspect of designing and training models is security and\nrobustness, especially in the face of an adversary who wishes to fool\nthe model.\nThis tutorial will raise your awareness to the security vulnerabilities\nof ML models, and will give insight into the hot topic of adversarial\nmachine learning. You may be surprised to find that adding imperceptible\nperturbations to an image\ncan\ncause drastically different model\nperformance. Given that this is a tutorial, we will explore the topic\nvia example on an image classifier. Specifically, we will use one of the\nfirst and most popular attack methods, the Fast Gradient Sign Attack\n(FGSM), to fool an MNIST classifier.\nThreat Model\n#\nFor context, there are many categories of adversarial attacks, each with\na different goal and assumption of the attacker’s knowledge. However, in\ngeneral the overarching goal is to add the least amount of perturbation\nto the input data to cause the desired misclassification. There are\nseveral kinds of assumptions of the attacker’s knowledge, two of which\nare:\nwhite-box\nand\nblack-box\n. A\nwhite-box\nattack assumes the\nattacker has full knowledge and access to the model, including\narchitecture, inputs, outputs, and weights. A\nblack-box\nattack assumes\nthe attacker only has access to the inputs and outputs of the model, and\nknows nothing about the underlying architecture or weights. There are\nalso several types of goals, including\nmisclassification\nand\nsource/target misclassification\n. A goal of\nmisclassification\nmeans\nthe adversary only wants the output classification to be wrong but does\nnot care what the new classification is. A\nsource/target\nmisclassification\nmeans the adversary wants to alter an image that is\noriginally of a specific source class so that it is classified as a\nspecific target class.\nIn this case, the FGSM attack is a\nwhite-box\nattack with the goal of\nmisclassification\n. With this background information, we can now\ndiscuss the attack in detail.\nFast Gradient Sign Attack\n#\nOne of the first and most popular adversarial attacks to date is\nreferred to as the\nFast Gradient Sign Attack (FGSM)\nand is described\nby Goodfellow et. al. in\nExplaining and Harnessing Adversarial\nExamples\n. The attack is remarkably\npowerful, and yet intuitive. It is designed to attack neural networks by\nleveraging the way they learn,\ngradients\n. The idea is simple, rather\nthan working to minimize the loss by adjusting the weights based on the\nbackpropagated gradients, the attack\nadjusts the input data to maximize\nthe loss\nbased on the same backpropagated gradients. In other words,\nthe attack uses the gradient of the loss w.r.t the input data, then\nadjusts the input data to maximize the loss.\nBefore we jump into the code, let’s look at the famous\nFGSM\npanda example and extract\nsome notation.\nFrom the figure,\n\\(\\mathbf{x}\\)\nis the original input image\ncorrectly classified as a “panda”,\n\\(y\\)\nis the ground truth label\nfor\n\\(\\mathbf{x}\\)\n,\n\\(\\mathbf{\\theta}\\)\nrepresents the model\nparameters, and\n\\(J(\\mathbf{\\theta}, \\mathbf{x}, y)\\)\nis the loss\nthat is used to train the network. The attack backpropagates the\ngradient back to the input data to calculate\n\\(\\nabla_{x} J(\\mathbf{\\theta}, \\mathbf{x}, y)\\)\n. Then, it adjusts\nthe input data by a small step (\n\\(\\epsilon\\)\nor\n\\(0.007\\)\nin the\npicture) in the direction (i.e.\n\\(sign(\\nabla_{x} J(\\mathbf{\\theta}, \\mathbf{x}, y))\\)\n) that will\nmaximize the loss. The resulting perturbed image,\n\\(x'\\)\n, is then\nmisclassified\nby the target network as a “gibbon” when it is still\nclearly a “panda”.\nHopefully now the motivation for this tutorial is clear, so lets jump\ninto the implementation.\nimport\ntorch\nimport\ntorch.nn\nas\nnn\nimport\ntorch.nn.functional\nas\nF\nimport\ntorch.optim\nas\noptim\nfrom\ntorchvision\nimport\ndatasets\n,\ntransforms\nimport\nnumpy\nas\nnp\nimport\nmatplotlib.pyplot\nas\nplt\nImplementation\n#\nIn this section, we will discuss the input parameters for the tutorial,\ndefine the model under attack, then code the attack and run some tests.\nInputs\n#\nThere are only three inputs for this tutorial, and are defined as\nfollows:\nepsilons\n- List of epsilon values to use for the run. It is\nimportant to keep 0 in the list because it represents the model\nperformance on the original test set. Also, intuitively we would\nexpect the larger the epsilon, the more noticeable the perturbations\nbut the more effective the attack in terms of degrading model\naccuracy. Since the data range here is\n\\([0,1]\\)\n, no epsilon\nvalue should exceed 1.\npretrained_model\n- path to the pretrained MNIST model which was\ntrained with\npytorch/examples/mnist\n.\nFor simplicity, download the pretrained model\nhere\n.\nepsilons\n=\n[\n0\n,\n.05\n,\n.1\n,\n.15\n,\n.2\n,\n.25\n,\n.3\n]\npretrained_model\n=\n\"data/lenet_mnist_model.pth\"\n# Set random seed for reproducibility\ntorch\n.\nmanual_seed\n(\n42\n)\n<torch._C.Generator object at 0x7f389eb72090>\nModel Under Attack\n#\nAs mentioned, the model under attack is the same MNIST model from\npytorch/examples/mnist\n.\nYou may train and save your own MNIST model or you can download and use\nthe provided model. The\nNet\ndefinition and test dataloader here have\nbeen copied from the MNIST example. The purpose of this section is to\ndefine the model and dataloader, then initialize the model and load the\npretrained weights.\n# LeNet Model definition\nclass\nNet\n(\nnn\n.\nModule\n):\ndef\n__init__\n(\nself\n):\nsuper\n(\nNet\n,\nself\n)\n.\n__init__\n()\nself\n.\nconv1\n=\nnn\n.\nConv2d\n(\n1\n,\n32\n,\n3\n,\n1\n)\nself\n.\nconv2\n=\nnn\n.\nConv2d\n(\n32\n,\n64\n,\n3\n,\n1\n)\nself\n.\ndropout1\n=\nnn\n.\nDropout\n(\n0.25\n)\nself\n.\ndropout2\n=\nnn\n.\nDropout\n(\n0.5\n)\nself\n.\nfc1\n=\nnn\n.\nLinear\n(\n9216\n,\n128\n)\nself\n.\nfc2\n=\nnn\n.\nLinear\n(\n128\n,\n10\n)\ndef\nforward\n(\nself\n,\nx\n):\nx\n=\nself\n.\nconv1\n(\nx\n)\nx\n=\nF\n.\nrelu\n(\nx\n)\nx\n=\nself\n.\nconv2\n(\nx\n)\nx\n=\nF\n.\nrelu\n(\nx\n)\nx\n=\nF\n.\nmax_pool2d\n(\nx\n,\n2\n)\nx\n=\nself\n.\ndropout1\n(\nx\n)\nx\n=\ntorch\n.\nflatten\n(\nx\n,\n1\n)\nx\n=\nself\n.\nfc1\n(\nx\n)\nx\n=\nF\n.\nrelu\n(\nx\n)\nx\n=\nself\n.\ndropout2\n(\nx\n)\nx\n=\nself\n.\nfc2\n(\nx\n)\noutput\n=\nF\n.\nlog_softmax\n(\nx\n,\ndim\n=\n1\n)\nreturn\noutput\n# MNIST Test dataset and dataloader declaration\ntest_loader\n=\ntorch\n.\nutils\n.\ndata\n.\nDataLoader\n(\ndatasets\n.\nMNIST\n(\n'../data'\n,\ntrain\n=\nFalse\n,\ndownload\n=\nTrue\n,\ntransform\n=\ntransforms\n.\nCompose\n([\ntransforms\n.\nToTensor\n(),\ntransforms\n.\nNormalize\n((\n0.1307\n,),\n(\n0.3081\n,)),\n])),\nbatch_size\n=\n1\n,\nshuffle\n=\nTrue\n)\n# We want to be able to train our model on an `accelerator <https://pytorch.org/docs/stable/torch.html#accelerators>`__\n# such as CUDA, MPS, MTIA, or XPU. 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)\n# Initialize the network\nmodel\n=\nNet\n()\n.\nto\n(\ndevice\n)\n# Load the pretrained model\nmodel\n.\nload_state_dict\n(\ntorch\n.\nload\n(\npretrained_model\n,\nmap_location\n=\ndevice\n,\nweights_only\n=\nTrue\n))\n# Set the model in evaluation mode. In this case this is for the Dropout layers\nmodel\n.\neval\n()\n0%|          | 0.00/9.91M [00:00<?, ?B/s]\n100%|██████████| 9.91M/9.91M [00:00<00:00, 135MB/s]\n\n  0%|          | 0.00/28.9k [00:00<?, ?B/s]\n100%|██████████| 28.9k/28.9k [00:00<00:00, 40.8MB/s]\n\n  0%|          | 0.00/1.65M [00:00<?, ?B/s]\n100%|██████████| 1.65M/1.65M [00:00<00:00, 170MB/s]\n\n  0%|          | 0.00/4.54k [00:00<?, ?B/s]\n100%|██████████| 4.54k/4.54k [00:00<00:00, 30.0MB/s]\nUsing cuda device\n\nNet(\n  (conv1): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1))\n  (conv2): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1))\n  (dropout1): Dropout(p=0.25, inplace=False)\n  (dropout2): Dropout(p=0.5, inplace=False)\n  (fc1): Linear(in_features=9216, out_features=128, bias=True)\n  (fc2): Linear(in_features=128, out_features=10, bias=True)\n)\nFGSM Attack\n#\nNow, we can define the function that creates the adversarial examples by\nperturbing the original inputs. The\nfgsm_attack\nfunction takes three\ninputs,\nimage\nis the original clean image (\n\\(x\\)\n),\nepsilon\nis\nthe pixel-wise perturbation amount (\n\\(\\epsilon\\)\n), and\ndata_grad\nis gradient of the loss w.r.t the input image\n(\n\\(\\nabla_{x} J(\\mathbf{\\theta}, \\mathbf{x}, y)\\)\n). The function\nthen creates perturbed image as\n\\[perturbed\\_image = image + epsilon*sign(data\\_grad) = x + \\epsilon * sign(\\nabla_{x} J(\\mathbf{\\theta}, \\mathbf{x}, y))\n\n\\]\nFinally, in order to maintain the original range of the data, the\nperturbed image is clipped to range\n\\([0,1]\\)\n.\n# FGSM attack code\ndef\nfgsm_attack\n(\nimage\n,\nepsilon\n,\ndata_grad\n):\n# Collect the element-wise sign of the data gradient\nsign_data_grad\n=\ndata_grad\n.\nsign\n()\n# Create the perturbed image by adjusting each pixel of the input image\nperturbed_image\n=\nimage\n+\nepsilon\n*\nsign_data_grad\n# Adding clipping to maintain [0,1] range\nperturbed_image\n=\ntorch\n.\nclamp\n(\nperturbed_image\n,\n0\n,\n1\n)\n# Return the perturbed image\nreturn\nperturbed_image\n# restores the tensors to their original scale\ndef\ndenorm\n(\nbatch\n,\nmean\n=\n[\n0.1307\n],\nstd\n=\n[\n0.3081\n]):\n\"\"\"\nConvert a batch of tensors to their original scale.\nArgs:\nbatch (torch.Tensor): Batch of normalized tensors.\nmean (torch.Tensor or list): Mean used for normalization.\nstd (torch.Tensor or list): Standard deviation used for normalization.\nReturns:\ntorch.Tensor: batch of tensors without normalization applied to them.\n\"\"\"\nif\nisinstance\n(\nmean\n,\nlist\n):\nmean\n=\ntorch\n.\ntensor\n(\nmean\n)\n.\nto\n(\ndevice\n)\nif\nisinstance\n(\nstd\n,\nlist\n):\nstd\n=\ntorch\n.\ntensor\n(\nstd\n)\n.\nto\n(\ndevice\n)\nreturn\nbatch\n*\nstd\n.\nview\n(\n1\n,\n-\n1\n,\n1\n,\n1\n)\n+\nmean\n.\nview\n(\n1\n,\n-\n1\n,\n1\n,\n1\n)\nTesting Function\n#\nFinally, the central result of this tutorial comes from the\ntest\nfunction. Each call to this test function performs a full test step on\nthe MNIST test set and reports a final accuracy. However, notice that\nthis function also takes an\nepsilon\ninput. This is because the\ntest\nfunction reports the accuracy of a model that is under attack\nfrom an adversary with strength\n\\(\\epsilon\\)\n. More specifically, for\neach sample in the test set, the function computes the gradient of the\nloss w.r.t the input data (\n\\(data\\_grad\\)\n), creates a perturbed\nimage with\nfgsm_attack\n(\n\\(perturbed\\_data\\)\n), then checks to see\nif the perturbed example is adversarial. In addition to testing the\naccuracy of the model, the function also saves and returns some\nsuccessful adversarial examples to be visualized later.\ndef\ntest\n(\nmodel\n,\ndevice\n,\ntest_loader\n,\nepsilon\n):\n# Accuracy counter\ncorrect\n=\n0\nadv_examples\n=\n[]\n# Loop over all examples in test set\nfor\ndata\n,\ntarget\nin\ntest_loader\n:\n# Send the data and label to the device\ndata\n,\ntarget\n=\ndata\n.\nto\n(\ndevice\n),\ntarget\n.\nto\n(\ndevice\n)\n# Set requires_grad attribute of tensor. Important for Attack\ndata\n.\nrequires_grad\n=\nTrue\n# Forward pass the data through the model\noutput\n=\nmodel\n(\ndata\n)\ninit_pred\n=\noutput\n.\nmax\n(\n1\n,\nkeepdim\n=\nTrue\n)[\n1\n]\n# get the index of the max log-probability\n# If the initial prediction is wrong, don't bother attacking, just move on\nif\ninit_pred\n.\nitem\n()\n!=\ntarget\n.\nitem\n():\ncontinue\n# Calculate the loss\nloss\n=\nF\n.\nnll_loss\n(\noutput\n,\ntarget\n)\n# Zero all existing gradients\nmodel\n.\nzero_grad\n()\n# Calculate gradients of model in backward pass\nloss\n.\nbackward\n()\n# Collect ``datagrad``\ndata_grad\n=\ndata\n.\ngrad\n.\ndata\n# Restore the data to its original scale\ndata_denorm\n=\ndenorm\n(\ndata\n)\n# Call FGSM Attack\nperturbed_data\n=\nfgsm_attack\n(\ndata_denorm\n,\nepsilon\n,\ndata_grad\n)\n# Reapply normalization\nperturbed_data_normalized\n=\ntransforms\n.\nNormalize\n((\n0.1307\n,),\n(\n0.3081\n,))(\nperturbed_data\n)\n# Re-classify the perturbed image\noutput\n=\nmodel\n(\nperturbed_data_normalized\n)\n# Check for success\nfinal_pred\n=\noutput\n.\nmax\n(\n1\n,\nkeepdim\n=\nTrue\n)[\n1\n]\n# get the index of the max log-probability\nif\nfinal_pred\n.\nitem\n()\n==\ntarget\n.\nitem\n():\ncorrect\n+=\n1\n# Special case for saving 0 epsilon examples\nif\nepsilon\n==\n0\nand\nlen\n(\nadv_examples\n)\n<\n5\n:\nadv_ex\n=\nperturbed_data\n.\nsqueeze\n()\n.\ndetach\n()\n.\ncpu\n()\n.\nnumpy\n()\nadv_examples\n.\nappend\n(\n(\ninit_pred\n.\nitem\n(),\nfinal_pred\n.\nitem\n(),\nadv_ex\n)\n)\nelse\n:\n# Save some adv examples for visualization later\nif\nlen\n(\nadv_examples\n)\n<\n5\n:\nadv_ex\n=\nperturbed_data\n.\nsqueeze\n()\n.\ndetach\n()\n.\ncpu\n()\n.\nnumpy\n()\nadv_examples\n.\nappend\n(\n(\ninit_pred\n.\nitem\n(),\nfinal_pred\n.\nitem\n(),\nadv_ex\n)\n)\n# Calculate final accuracy for this epsilon\nfinal_acc\n=\ncorrect\n/\nfloat\n(\nlen\n(\ntest_loader\n))\nprint\n(\nf\n\"Epsilon:\n{\nepsilon\n}\n\\t\nTest Accuracy =\n{\ncorrect\n}\n/\n{\nlen\n(\ntest_loader\n)\n}\n=\n{\nfinal_acc\n}\n\"\n)\n# Return the accuracy and an adversarial example\nreturn\nfinal_acc\n,\nadv_examples\nRun Attack\n#\nThe last part of the implementation is to actually run the attack. Here,\nwe run a full test step for each epsilon value in the\nepsilons\ninput.\nFor each epsilon we also save the final accuracy and some successful\nadversarial examples to be plotted in the coming sections. Notice how\nthe printed accuracies decrease as the epsilon value increases. Also,\nnote the\n\\(\\epsilon=0\\)\ncase represents the original test accuracy,\nwith no attack.\naccuracies\n=\n[]\nexamples\n=\n[]\n# Run test for each epsilon\nfor\neps\nin\nepsilons\n:\nacc\n,\nex\n=\ntest\n(\nmodel\n,\ndevice\n,\ntest_loader\n,\neps\n)\naccuracies\n.\nappend\n(\nacc\n)\nexamples\n.\nappend\n(\nex\n)\nEpsilon: 0      Test Accuracy = 9873 / 10000 = 0.9873\nEpsilon: 0.05   Test Accuracy = 9630 / 10000 = 0.963\nEpsilon: 0.1    Test Accuracy = 9060 / 10000 = 0.906\nEpsilon: 0.15   Test Accuracy = 7793 / 10000 = 0.7793\nEpsilon: 0.2    Test Accuracy = 5581 / 10000 = 0.5581\nEpsilon: 0.25   Test Accuracy = 3254 / 10000 = 0.3254\nEpsilon: 0.3    Test Accuracy = 1654 / 10000 = 0.1654\nResults\n#\nAccuracy vs Epsilon\n#\nThe first result is the accuracy versus epsilon plot. As alluded to\nearlier, as epsilon increases we expect the test accuracy to decrease.\nThis is because larger epsilons mean we take a larger step in the\ndirection that will maximize the loss. Notice the trend in the curve is\nnot linear even though the epsilon values are linearly spaced. For\nexample, the accuracy at\n\\(\\epsilon=0.05\\)\nis only about 4% lower\nthan\n\\(\\epsilon=0\\)\n, but the accuracy at\n\\(\\epsilon=0.2\\)\nis 25%\nlower than\n\\(\\epsilon=0.15\\)\n. Also, notice the accuracy of the model\nhits random accuracy for a 10-class classifier between\n\\(\\epsilon=0.25\\)\nand\n\\(\\epsilon=0.3\\)\n.\nplt\n.\nfigure\n(\nfigsize\n=\n(\n5\n,\n5\n))\nplt\n.\nplot\n(\nepsilons\n,\naccuracies\n,\n\"*-\"\n)\nplt\n.\nyticks\n(\nnp\n.\narange\n(\n0\n,\n1.1\n,\nstep\n=\n0.1\n))\nplt\n.\nxticks\n(\nnp\n.\narange\n(\n0\n,\n.35\n,\nstep\n=\n0.05\n))\nplt\n.\ntitle\n(\n\"Accuracy vs Epsilon\"\n)\nplt\n.\nxlabel\n(\n\"Epsilon\"\n)\nplt\n.\nylabel\n(\n\"Accuracy\"\n)\nplt\n.\nshow\n()\nSample Adversarial Examples\n#\nRemember the idea of no free lunch? In this case, as epsilon increases\nthe test accuracy decreases\nBUT\nthe perturbations become more easily\nperceptible. In reality, there is a tradeoff between accuracy\ndegradation and perceptibility that an attacker must consider. Here, we\nshow some examples of successful adversarial examples at each epsilon\nvalue. Each row of the plot shows a different epsilon value. The first\nrow is the\n\\(\\epsilon=0\\)\nexamples which represent the original\n“clean” images with no perturbation. The title of each image shows the\n“original classification -> adversarial classification.” Notice, the\nperturbations start to become evident at\n\\(\\epsilon=0.15\\)\nand are\nquite evident at\n\\(\\epsilon=0.3\\)\n. However, in all cases humans are\nstill capable of identifying the correct class despite the added noise.\n# Plot several examples of adversarial samples at each epsilon\ncnt\n=\n0\nplt\n.\nfigure\n(\nfigsize\n=\n(\n8\n,\n10\n))\nfor\ni\nin\nrange\n(\nlen\n(\nepsilons\n)):\nfor\nj\nin\nrange\n(\nlen\n(\nexamples\n[\ni\n])):\ncnt\n+=\n1\nplt\n.\nsubplot\n(\nlen\n(\nepsilons\n),\nlen\n(\nexamples\n[\n0\n]),\ncnt\n)\nplt\n.\nxticks\n([],\n[])\nplt\n.\nyticks\n([],\n[])\nif\nj\n==\n0\n:\nplt\n.\nylabel\n(\nf\n\"Eps:\n{\nepsilons\n[\ni\n]\n}\n\"\n,\nfontsize\n=\n14\n)\norig\n,\nadv\n,\nex\n=\nexamples\n[\ni\n][\nj\n]\nplt\n.\ntitle\n(\nf\n\"\n{\norig\n}\n->\n{\nadv\n}\n\"\n)\nplt\n.\nimshow\n(\nex\n,\ncmap\n=\n\"gray\"\n)\nplt\n.\ntight_layout\n()\nplt\n.\nshow\n()\nWhere to go next?\n#\nHopefully this tutorial gives some insight into the topic of adversarial\nmachine learning. There are many potential directions to go from here.\nThis attack represents the very beginning of adversarial attack research\nand since there have been many subsequent ideas for how to attack and\ndefend ML models from an adversary. In fact, at NIPS 2017 there was an\nadversarial attack and defense competition and many of the methods used\nin the competition are described in this paper:\nAdversarial Attacks and\nDefences Competition\n. The work\non defense also leads into the idea of making machine learning models\nmore\nrobust\nin general, to both naturally perturbed and adversarially\ncrafted inputs.\nAnother direction to go is adversarial attacks and defense in different\ndomains. Adversarial research is not limited to the image domain, check\nout\nthis\nattack on\nspeech-to-text models. But perhaps the best way to learn more about\nadversarial machine learning is to get your hands dirty. Try to\nimplement a different attack from the NIPS 2017 competition, and see how\nit differs from FGSM. Then, try to defend the model from your own\nattacks.\nA further direction to go, depending on available resources, is to modify\nthe code to support processing work in batch, in parallel, and or distributed\nvs working on one attack at a time in the above for each\nepsilon\ntest()\nloop.\nTotal running time of the script:\n(2 minutes 23.894 seconds)\nDownload\nJupyter\nnotebook:\nfgsm_tutorial.ipynb\nDownload\nPython\nsource\ncode:\nfgsm_tutorial.py\nDownload\nzipped:\nfgsm_tutorial.zip",
  "metadata": {
    "title": "Adversarial Example Generation — 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/5da26e70a48f/code/body-000.txt",
      "chars": 191,
      "language": "text"
    },
    {
      "file": "attachments/5da26e70a48f/code/body-001.txt",
      "chars": 150,
      "language": "text"
    },
    {
      "file": "attachments/5da26e70a48f/code/body-002.txt",
      "chars": 46,
      "language": "text"
    },
    {
      "file": "attachments/5da26e70a48f/code/body-003.txt",
      "chars": 1696,
      "language": "text"
    },
    {
      "file": "attachments/5da26e70a48f/code/body-004.txt",
      "chars": 749,
      "language": "text"
    },
    {
      "file": "attachments/5da26e70a48f/code/body-005.txt",
      "chars": 1141,
      "language": "text"
    },
    {
      "file": "attachments/5da26e70a48f/code/body-006.txt",
      "chars": 2434,
      "language": "text"
    },
    {
      "file": "attachments/5da26e70a48f/code/body-007.txt",
      "chars": 183,
      "language": "text"
    },
    {
      "file": "attachments/5da26e70a48f/code/body-008.txt",
      "chars": 376,
      "language": "text"
    },
    {
      "file": "attachments/5da26e70a48f/code/body-009.txt",
      "chars": 233,
      "language": "text"
    },
    {
      "file": "attachments/5da26e70a48f/code/body-010.txt",
      "chars": 513,
      "language": "text"
    }
  ],
  "images": [
    {
      "status": "ok",
      "url": "https://docs.pytorch.org/tutorials/_images/fgsm_panda_image.png",
      "bytes": 957679,
      "reused": false,
      "local_path": "attachments/5da26e70a48f/images/body-dcf184b28121539892cf.png",
      "src": "https://docs.pytorch.org/tutorials/_images/fgsm_panda_image.png",
      "alt": "fgsm_panda_image",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://docs.pytorch.org/tutorials/_images/sphx_glr_fgsm_tutorial_001.png",
      "bytes": 23934,
      "reused": false,
      "local_path": "attachments/5da26e70a48f/images/body-eb76618d935f906cf7ef.png",
      "src": "https://docs.pytorch.org/tutorials/_images/sphx_glr_fgsm_tutorial_001.png",
      "alt": "Accuracy vs Epsilon",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://docs.pytorch.org/tutorials/_images/sphx_glr_fgsm_tutorial_002.png",
      "bytes": 74405,
      "reused": false,
      "local_path": "attachments/5da26e70a48f/images/body-19ee8840a43139cfe51c.png",
      "src": "https://docs.pytorch.org/tutorials/_images/sphx_glr_fgsm_tutorial_002.png",
      "alt": "7 -> 7, 9 -> 9, 0 -> 0, 3 -> 3, 5 -> 5, 1 -> 6, 4 -> 9, 7 -> 3, 2 -> 0, 5 -> 8, 9 -> 4, 5 -> 6, 9 -> 5, 3 -> 2, 7 -> 9, 3 -> 5, 3 -> 5, 5 -> 3, 9 -> 7, 8 -> 6, 7 -> 2, 3 -> 2, 4 -> 8, 3 -> 7, 5 -> 3, 6 -> 5, 5 -> 6, 6 -> 8, 8 -> 6, 1 -> 2, 1 -> 7, 7 -> 2, 1 -> 8, 5 -> 8, 0 -> 6",
      "downloaded": true
    }
  ],
  "audio": [],
  "videos": [],
  "links": [
    {
      "href": "https://github.com/inkawhich",
      "label": "Nathan Inkawhich",
      "type": "a"
    },
    {
      "href": "https://arxiv.org/abs/1412.6572",
      "label": "Explaining and Harnessing Adversarial\nExamples",
      "type": "a"
    },
    {
      "href": "https://github.com/pytorch/examples/tree/master/mnist",
      "label": "pytorch/examples/mnist",
      "type": "a"
    },
    {
      "href": "https://drive.google.com/file/d/1HJV2nUHJqclXQ8flKvcWmjZ-OU5DGatl/view?usp=drive_link",
      "label": "here",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/docs/stable/generated/torch.manual_seed.html",
      "label": "torch . manual_seed",
      "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.Conv2d.html",
      "label": "nn . Conv2d",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/docs/stable/generated/torch.nn.Dropout.html",
      "label": "nn . Dropout",
      "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.nn.functional.relu.html",
      "label": "F . relu",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/docs/stable/generated/torch.nn.functional.max_pool2d.html",
      "label": "F . max_pool2d",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/docs/stable/generated/torch.flatten.html",
      "label": "torch . flatten",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/docs/stable/generated/torch.nn.functional.log_softmax.html",
      "label": "F . log_softmax",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/docs/stable/data.html",
      "label": "test_loader",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/vision/stable/generated/torchvision.datasets.MNIST.html",
      "label": "datasets . MNIST",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/vision/stable/generated/torchvision.transforms.Compose.html",
      "label": "transforms . Compose",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/vision/stable/generated/torchvision.transforms.ToTensor.html",
      "label": "transforms . ToTensor",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/vision/stable/generated/torchvision.transforms.Normalize.html",
      "label": "transforms . Normalize",
      "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://docs.pytorch.org/docs/stable/generated/torch.load.html",
      "label": "torch . load",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/docs/stable/generated/torch.clamp.html",
      "label": "torch . clamp",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/docs/stable/generated/torch.tensor.html",
      "label": "torch . tensor",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/docs/stable/generated/torch.nn.functional.nll_loss.html",
      "label": "F . nll_loss",
      "type": "a"
    },
    {
      "href": "https://arxiv.org/pdf/1804.00097.pdf",
      "label": "Adversarial Attacks and\nDefences Competition",
      "type": "a"
    },
    {
      "href": "https://arxiv.org/pdf/1801.01944.pdf",
      "label": "this",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/tutorials/_downloads/56c122e1c18e5e07666673e900acaed5/fgsm_tutorial.ipynb",
      "label": "Download Jupyter notebook: fgsm_tutorial.ipynb",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/tutorials/_downloads/377bf4a7b1761e5f081e057385870d8e/fgsm_tutorial.py",
      "label": "Download Python source code: fgsm_tutorial.py",
      "type": "a"
    },
    {
      "href": "https://docs.pytorch.org/tutorials/_downloads/20e0cd443ee8621ecc24d0dfdeb6a3d0/fgsm_tutorial.zip",
      "label": "Download zipped: fgsm_tutorial.zip",
      "type": "a"
    }
  ],
  "repair_version": 2,
  "repaired_at": "2026-10-14T10:53:56Z",
  "source_url": "https://pytorch.org/tutorials/beginner/fgsm_tutorial.html",
  "papers": [
    {
      "status": "ok",
      "url": "https://arxiv.org/pdf/1412.6572",
      "bytes": 1036360,
      "reused": true,
      "local_path": "papers/repair/1b6f12d7db56a03ce596.pdf"
    },
    {
      "status": "ok",
      "url": "https://arxiv.org/pdf/1804.00097.pdf",
      "bytes": 1926416,
      "reused": false,
      "local_path": "papers/repair/611380c3a835088507f4.pdf"
    },
    {
      "status": "ok",
      "url": "https://arxiv.org/pdf/1801.01944.pdf",
      "bytes": 403723,
      "reused": false,
      "local_path": "papers/repair/7e469052847da21a1f2c.pdf"
    }
  ],
  "actual_video_files": [],
  "body_chars": 17814,
  "expected_body_images": 3,
  "discovery_status": "ok",
  "delivery_status": "ok",
  "missing_reason": "",
  "raw_html": "attachments/5da26e70a48f/pages/source.html",
  "local_html": "attachments/5da26e70a48f/pages/article.html",
  "body_base_path": "attachments/5da26e70a48f/pages/",
  "body_markdown_path": "attachments/5da26e70a48f/pages/article.md"
}