1) What is CleverHans?
CleverHans is a Python library built to generate adversarial examples and evaluate model robustness—especially for deep learning models. It became popular because it provides clean, research-aligned implementations of core adversarial attacks (e.g., FGSM, PGD) and helps standardize how teams test robustness.
Where it fits in the workflow:
Red-team / testing: “Can my model be fooled under a realistic threat model?”
Defense iteration: “If I add adversarial training or preprocessing, does robustness actually improve?”
Benchmarking: Comparing robustness across models, datasets, and training strategies.
2) Why CleverHans is important in adversarial ML
CleverHans is valuable because it helps you operationalize three things:
A) Reproducible attacks
You don’t want a “homebrew” attack that accidentally underestimates risk. CleverHans gives you known baselines.
B) Threat-model aware evaluation
It’s easy to claim robustness by testing weak attacks. CleverHans makes it straightforward to run stronger iterative attacks (e.g., PGD) and report parameters.
C) Defense validation
Defenses often “look good” under one attack but collapse under another. CleverHans supports a more honest attack suite mindset.
3) Core concepts you should know before using it
Evasion attacks (inference-time)
You keep the model fixed, and you perturb the input xxx slightly to get x′x'x′ such that:
Humans see “the same” input
Model prediction flips (or confidence changes drastically)
Norm bounds
Most attacks constrain perturbations by a norm (common in images):
L∞ bound: every pixel changes by at most ε
L2 bound: overall perturbation energy is bounded
White-box vs black-box
CleverHans is mostly used for white-box testing (attacker can compute gradients). That’s still crucial because white-box testing is the “worst-case” sanity check.
4) Setup: Install CleverHans
pip install cleverhans
Note: CleverHans is most commonly used with TensorFlow 2.x APIs. (If you’re using PyTorch-heavy workflows, ART/Foolbox are often used too, but CleverHans still helps conceptually and sometimes practically depending on version.)
5) Minimal, high-quality example (TensorFlow 2): FGSM + PGD evaluation
This example shows:
Train a simple CNN on MNIST
Attack it with FGSM and PGD
Compare clean accuracy vs adversarial accuracy
5.1 Train a baseline model
import tensorflow as tf
import numpy as np
# 1) Data
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
x_train = (x_train.astype("float32") / 255.0)[..., None] # (N, 28, 28, 1)
x_test = (x_test.astype("float32") / 255.0)[..., None]
y_train_oh = tf.one_hot(y_train, depth=10)
y_test_oh = tf.one_hot(y_test, depth=10)
# 2) Model
model = tf.keras.Sequential([
tf.keras.layers.Conv2D(32, 3, activation="relu", input_shape=(28, 28, 1)),
tf.keras.layers.MaxPool2D(),
tf.keras.layers.Conv2D(64, 3, activation="relu"),
tf.keras.layers.MaxPool2D(),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(128, activation="relu"),
tf.keras.layers.Dense(10) # logits
])
loss_fn = tf.keras.losses.CategoricalCrossentropy(from_logits=True)
model.compile(optimizer=tf.keras.optimizers.Adam(1e-3),
loss=loss_fn,
metrics=[tf.keras.metrics.CategoricalAccuracy(name="acc")])
model.fit(x_train, y_train_oh, epochs=2, batch_size=128, validation_split=0.1)
clean_eval = model.evaluate(x_test, y_test_oh, verbose=0)
print("Clean test accuracy:", clean_eval[1])
5.2 Generate adversarial examples using CleverHans
FGSM (Fast Gradient Sign Method)
from cleverhans.tf2.attacks.fast_gradient_method import fast_gradient_method
eps = 0.2 # L-infinity bound
x_adv_fgsm = fast_gradient_method(model_fn=model, x=x_test, eps=eps, norm=np.inf)
adv_eval_fgsm = model.evaluate(x_adv_fgsm, y_test_oh, verbose=0)
print(f"FGSM (eps={eps}) accuracy:", adv_eval_fgsm[1])
PGD (Projected Gradient Descent)
from cleverhans.tf2.attacks.projected_gradient_descent import projected_gradient_descent
eps = 0.3
eps_iter = 0.03
nb_iter = 20
x_adv_pgd = projected_gradient_descent(model_fn=model,
x=x_test,
eps=eps,
eps_iter=eps_iter,
nb_iter=nb_iter,
norm=np.inf,
targeted=False)
adv_eval_pgd = model.evaluate(x_adv_pgd, y_test_oh, verbose=0)
print(f"PGD (eps={eps}, steps={nb_iter}) accuracy:", adv_eval_pgd[1])
Interpretation guideline (what learners should notice):
Clean accuracy might be high (e.g., >98% on MNIST)
FGSM drops accuracy noticeably
PGD usually drops it much more, because it’s a stronger iterative attack
6) Using CleverHans for Adversarial Training (defense)
Adversarial training is the most practical baseline defense: train on a mix of clean and adversarial samples so the model learns a smoother, more robust decision boundary.
6.1 Simple FGSM-based adversarial training loop (TensorFlow 2)
@tf.function
def train_step(x, y, eps=0.2):
# Generate FGSM examples on-the-fly
x_adv = fast_gradient_method(model_fn=model, x=x, eps=eps, norm=np.inf)
with tf.GradientTape() as tape:
# Mix clean + adversarial in one batch (simple strategy)
x_mix = tf.concat([x, x_adv], axis=0)
y_mix = tf.concat([y, y], axis=0)
logits = model(x_mix, training=True)
loss = loss_fn(y_mix, logits)
grads = tape.gradient(loss, model.trainable_variables)
model.optimizer.apply_gradients(zip(grads, model.trainable_variables))
return loss
# Build a dataset pipeline
train_ds = tf.data.Dataset.from_tensor_slices((x_train, y_train_oh)).shuffle(10000).batch(128)
# Run a few adversarial-training epochs
for epoch in range(2):
losses = []
for xb, yb in train_ds:
l = train_step(xb, yb, eps=0.2)
losses.append(float(l))
print(f"Adv-training epoch {epoch+1} | loss={np.mean(losses):.4f}")
# Re-evaluate
clean_eval2 = model.evaluate(x_test, y_test_oh, verbose=0)
print("Clean accuracy after adv training:", clean_eval2[1])
x_adv_pgd2 = projected_gradient_descent(model_fn=model, x=x_test, eps=0.3, eps_iter=0.03, nb_iter=20,
norm=np.inf, targeted=False)
adv_eval_pgd2 = model.evaluate(x_adv_pgd2, y_test_oh, verbose=0)
print("PGD accuracy after adv training:", adv_eval_pgd2[1])
What learners typically observe:
Robust accuracy under PGD often improves
Clean accuracy might dip slightly (robustness–accuracy trade-off)
7) Common mistakes (and how to teach learners to avoid them)
Mistake 1: “It’s robust because FGSM didn’t work”
FGSM is often too weak. Always test at least one iterative attack like PGD.
Mistake 2: Not reporting attack parameters
Robustness is meaningless without:
ε (budget)
step size
number of steps
norm type
Mistake 3: Gradient masking illusions
Some defenses break gradients; attacks fail without real robustness. Validate using:
stronger attacks
transfer-style checks (attack a surrogate and test on target)
sanity checks (does robust accuracy collapse under adaptive settings?)


Comments are closed.