Skip to content
AI360XpertBeta

Adversarial Examples in Vision

A nudge to a photo so small you cannot see it can still flip what a model reports, because the nudge points exactly along the model's own gradients.

A photo leading 0.50 to 0.40 flips to 0.375 against 0.40 after one gradient step capped at epsilon 0.25, and no pixel moves further than that
A photo leading 0.50 to 0.40 flips to 0.375 against 0.40 after one gradient step capped at epsilon 0.25, and no pixel moves further than that

Why Does This Exist?

An image classifier can score above 90 percent on its test set and still call a panda a gibbon with 99 percent confidence after an attacker shifts every pixel by an amount no human can see. That is not a broken model. It is the normal consequence of deciding by boundaries in a high-dimensional space: most photos sit closer to a boundary than intuition suggests, and a nudge aligned with the model's gradients crosses it.

This matters wherever a camera feeds a decision: driver assistance, face gates, content filters. An attacker with the model, or a decent copy of it, manufactures the nudge with a single backward pass. No camera hacking, no stickers required.

This page covers the attack: what the perturbation is, how one gradient step finds it, and why it transfers between models. Training a model to survive such noise is a separate topic in adversarial training, and the confidence scores under attack are the classifier outputs from image classification.

The one idea you need first. A classifier turns pixel values into one score per class and reports the highest. The gradient of the loss with respect to the pixels says which direction in pixel space raises the loss fastest. Training follows that signal to change the weights; an attacker follows it to change the photo while the weights stay frozen.

Think of It Like This

A marble resting near the ridge of a roof

Picture a marble resting on a roof, just left of the ridge. The left slope reads panda, the right slope reads gibbon. You are allowed one fingertip tap so light that nobody watching sees the marble move. Tap it in a random direction and nothing happens. Tap it exactly along the steepest downward line and it rolls over the ridge into the gibbon gutter.

Each part maps. The marble's spot is the photo, the ridge is the decision boundary, the steepest line is the gradient, and the lightness cap on your tap is epsilon. Random noise fails for the same reason a random tap fails: only the computed direction spends the tiny budget where it counts.

Where it stops: a real roof has one ridge and gravity finishes the job, while a classifier has millions of flat directions and the attacker must compute the slope with calculus. The tap here is mathematics, not touch.

How It Actually Works

Write the photo as a vector xx and its true label as yy. The trained weights θ\theta stay frozen: nothing is retrained. The attacker wants a perturbation η\eta such that the model misreads x+ηx + \eta, under the cap that no pixel moves more than ϵ\epsilon. In symbols, ∥η∥∞≤ϵ\lVert \eta \rVert_\infty \le \epsilon: every coordinate shifts by at most ϵ\epsilon, which is what keeps the change invisible.

The Fast Gradient Sign Method finds η\eta in one step. Take the gradient of the loss with respect to the photo itself, ∇xJ(θ,x,y)\nabla_x J(\theta, x, y), keep only each coordinate's sign, and scale by ϵ\epsilon:

η=ϵ⋅sign(∇xJ(θ,x,y))\eta = \epsilon \cdot \text{sign}(\nabla_x J(\theta, x, y))

In plain words: every pixel moves the full allowed amount in whichever direction raises the loss. The sign matters more than the size, because across hundreds of thousands of pixels the tiny pushes accumulate along the model's most sensitive direction.

Worked example: two pixels flip a verdict

Take a toy classifier with two pixel values x=[1.0,1.0]x = [1.0, 1.0]. The true class scores with weights w0=[0.5,0.0]w_0 = [0.5, 0.0] and the rival with w1=[0.2,0.2]w_1 = [0.2, 0.2]. Before the attack:

s0=0.5×1.0+0.0×1.0=0.50,s1=0.2×1.0+0.2×1.0=0.40s_0 = 0.5 \times 1.0 + 0.0 \times 1.0 = 0.50, \quad s_1 = 0.2 \times 1.0 + 0.2 \times 1.0 = 0.40

The true class leads 0.50 to 0.40. The margin (rival minus true) grows fastest along w1−w0=[−0.3,0.2]w_1 - w_0 = [-0.3, 0.2], whose signs are [−1,+1][-1, +1]. One FGSM step with ϵ=0.25\epsilon = 0.25:

x′=[1.0,1.0]+0.25×[−1,+1]=[0.75,1.25]x' = [1.0, 1.0] + 0.25 \times [-1, +1] = [0.75, 1.25]

New scores: s0=0.5×0.75=0.375s_0 = 0.5 \times 0.75 = 0.375 and s1=0.2×0.75+0.2×1.25=0.15+0.25=0.40s_1 = 0.2 \times 0.75 + 0.2 \times 1.25 = 0.15 + 0.25 = 0.40. The rival now leads 0.40 to 0.375, so the label flips, yet each pixel moved only 0.25. A real photo runs the same arithmetic over hundreds of thousands of pixels, which is why ϵ\epsilon can stay invisible while the verdict changes completely.

Untargeted attacks accept any wrong answer; targeted ones maximize one chosen wrong class, the gibbon in the famous example. Perturbations also transfer: noise brewed on one model often fools another trained on similar data, so attackers do not always need your exact weights. If you want to know which pixels the model leaned on, the same gradients power the saliency maps in explainability.

Code

The point of this snippet is the flip, not the formula: one capped step along the margin gradient turns a correct verdict into a wrong one on numbers you can check by hand.

import numpy as np
w_true: np.ndarray = np.array([0.5, 0.0])  # weights voting for the right answerw_rival: np.ndarray = np.array([0.2, 0.2])  # weights voting for the wrong answerx: np.ndarray = np.array([1.0, 1.0])
print(w_true @ x, w_rival @ x)  # -> 0.5 0.4
step: np.ndarray = 0.25 * np.sign(w_rival - w_true)  # one FGSM step, capped at 0.25x_adv: np.ndarray = x + stepprint(x_adv)  # -> [0.75 1.25]print(w_true @ x_adv, w_rival @ x_adv)  # -> 0.375 0.4

The true score falls from 0.50 to 0.375 while the rival holds 0.40, so argmax switches sides. Every input moved by at most 0.25, the ϵ\epsilon budget from the worked example.

Watch Out For

Judging safety by the clean test set

A model can top the leaderboard on untouched photos and collapse on perturbed ones, because accuracy measures typical inputs and attacks craft worst-case ones. The symptom is a deployment that looks excellent in evaluation and misbehaves under scrutiny, for example a content filter that passes every audit image yet misses doctored uploads.

Fix it by reporting accuracy at an explicit perturbation budget alongside clean accuracy, for instance accuracy under ϵ=0.05\epsilon = 0.05 FGSM noise, and by treating that number as the security metric. Uncertainty estimation helps flag shaky inputs, but it does not replace the adversarial evaluation.

Blurring the input and calling it a defense

Blurring, bit-depth reduction, or random input noise can erase one specific attack and feel like a fix. The symptom is a defense that works against the demo attack and fails the week after, because a slightly stronger or adaptive attack simply optimizes through the transform.

Fix it with training that assumes an adaptive attacker: include perturbed examples in training batches, as adversarial training does. Any defense evaluated only against the attack it was designed for is a filter, not a fix.

The Quick Version

  • An adversarial example is a normal input plus a tiny crafted perturbation that flips the model's answer.
  • FGSM finds the perturbation in one step: each input moves by ϵ\epsilon along the sign of the loss gradient.
  • The worked toy flips a verdict from 0.50 versus 0.40 to 0.375 versus 0.40 with every pixel moving at most 0.25.
  • Targeted attacks aim at one chosen wrong class; untargeted attacks accept any wrong answer.
  • Perturbations transfer between models trained on similar data, so hiding the weights is not a defense.
  • Clean accuracy says nothing about robustness; evaluate at an explicit epsilon budget and train on perturbed inputs.