Where the classifier looks

Cover one square of a photo at a time and rerun a real image classifier to map which regions its answer depends on, and which it can do without.

IntermediateExplained in Where the classifier looks

Where the classifier looks

Cover one square of the picture at a time and run the classifier again. Squares that lower the label turn red; squares that raise it turn blue.

Square size
Fill the square with
Sample photos

Your photos and camera frames are processed on this device and never uploaded.

Recorded from DeiT-Tiny

Loading the model's recorded outputs for the eight sample photos.

About the download

Map which parts of any picture the classifier's answer depends on. This uses DeiT-Tiny, a 6-million-parameter vision transformer from Meta (2020) that sorts a photo into 1,000 ImageNet categories.

The first time, your browser downloads about 24 MB of model files from Hugging Face, plus a 14 MB runtime from this site. Both are cached for later visits. Everything runs on your device; no photo or camera frame is sent anywhere.

Manage downloads

Loading the recorded results

DeiT-Tiny sees the picture stretched to 224 × 224 pixels, so each square here is the same share of the picture the model sees.

Nothing coveredn/a
Biggest drop from one squaren/a
Squares that change the top labeln/a
Timen/a

Challenges

0 of 3 complete

  1. Cover one square that takes away more than half of the top label's probability. (not yet)
  2. Find one square that, covered, changes the classifier's top label. (not yet)
  3. With the largest squares, find a label above 60% that no single square can cut by even a quarter. (not yet)

Try this

  • On the dog, find the square that hurts “boxer” most. Then switch to one-patch squares and look for it again.
  • On the street or the cafe, cover squares until the top label changes.
  • Try the corridor with the largest squares. Can any one square bring “prison” down by much?
  • Pick a square, then switch the fill between gray and blur. Does the classifier care what replaced the square?
An occlusion map, and evidence it missesPython with NumPy
import numpy as np

def softmax(z):
    e = np.exp(z - z.max())
    return e / e.sum()

# A toy classifier for 4 x 4 pictures with two labels. "Stripe" scores the
# brightest row; "blob" scores the middle four pixels. Pixels are already
# normalized, so 0 is the average color, the gray a covered square gets.
def probs(img):
    stripe = 3 * img.sum(axis=1).max() / 4
    blob = 3 * img[1:3, 1:3].mean()
    return softmax(np.array([stripe, blob]))

def occlusion_map(img, label, size=2):
    base = probs(img)[label]
    n = img.shape[0] // size
    drops = np.zeros((n, n))
    for r in range(n):
        for c in range(n):
            covered = img.copy()
            covered[r*size:(r+1)*size, c*size:(c+1)*size] = 0  # gray square
            drops[r, c] = base - probs(covered)[label]
    return base, drops

img = np.zeros((4, 4))
img[0, :] = 1.0            # a bright stripe along the top
base, drops = occlusion_map(img, label=0)
print(f"stripe with nothing covered: {base:.2f}")
print("drop when each 2 x 2 square is covered:")
print(np.round(drops, 2))

# The same evidence twice: a second stripe along the bottom. The classifier
# takes the brightest row, so covering either stripe leaves the other.
img[3, :] = 1.0
base, drops = occlusion_map(img, label=0)
print(f"two stripes, nothing covered: {base:.2f}")
print(np.round(drops, 2))
stripe with nothing covered: 0.95
drop when each 2 x 2 square is covered:
[[0.13 0.13]
 [0.   0.  ]]
two stripes, nothing covered: 0.95
[[0. 0.]
 [0. 0.]]

Sample photos, all CC0 on Wikimedia Commons: Dog by Joselodos; Cat by Vyshnavi medicharla; Cat on a motorcycle by CAPTAIN RAJU; Breakfast by Andy Li; Street by Mucklagh; Cafe by Andy Li; Corridor by Sindugab; Woodblock print by Utagawa Hiroshige (Metropolitan Museum of Art).

Try "embedding", "softmax", "overfitting", or "backpropagation".