Essay section 2: is the famous cat-and-car neuron a V-shape neuron? A direct check on the original weights

Author

Mostly Claude and Brian Swingle

The essay’s first piece of evidence is a reinterpretation of the most famous polysemantic neuron in the field, InceptionV1’s 4e:55 (channel 55 of layer mixed4e). Zoom In (2020) presented it as responding to cat faces and car fronts and said outright that it “isn’t responding to some commonality of cars and cat faces”. The essay now says it is a V-shape neuron: pointed things drive it positive, round things drive it negative, and the cats and cars are two of many things with a V in them.

The original 2014 InceptionV1 weights are public (the inception5h graph that the Lucid and Lucent libraries ship), so this is a claim we can check directly. We do not have ImageNet, so we use the 5,000 COCO validation images instead, which have cats, cars, dogs and much else, and which come with object labels we can use for statistics.

What the essay claims, specifically

  1. The neuron’s strongest inputs from the previous layer include 4d:493 (fox snout), 4d:477 (wolf face) and 4d:201 (van front); its strongest reader downstream is 5a:94 (V-shaped flames).
  2. Its negative pole is “an O, round where the V is pointed”.
  3. Its positive pole generalises beyond cats and cars to anything pointed: fox faces, pool tables, bullet trains.
  4. Therefore the right description is a shared pattern (a verb, “pointed versus round”), not a list of nouns (cats OR cars).

Claims 1 and 2 are checkable from the weights and from any photo collection. Claim 3 we can check partially with COCO’s labels. Claim 4 is an interpretation, and the last section discusses how much the evidence supports it.

Setup: load the original InceptionV1 and scan COCO (cached after the first run)
import sys, pathlib, json, numpy as np, torch, matplotlib.pyplot as plt
from PIL import Image
ROOT = pathlib.Path.cwd().parent
sys.path.insert(0, str(ROOT))
from common.inception import load_inceptionv1, scan_layer, receptive_crop
plt.rcParams.update({"figure.dpi": 110, "font.size": 9})
DEV = "cuda" if torch.cuda.is_available() else "cpu"
CH = 55
model = load_inceptionv1(DEV)
paths = sorted((ROOT / "data/coco/val2017").glob("*.jpg"))
cache = ROOT / "data/cache/mixed4e_1x1_prerelu_coco.npz"
if cache.exists():
    z = dict(np.load(cache))
else:
    res = scan_layer(model, "mixed4e_1x1_pre_relu_conv", paths, device=DEV, batch=100)
    np.savez_compressed(cache, **{k: v for k, v in res.items() if k != "paths"}, paths=np.array(res["paths"]))
    z = dict(np.load(cache))
mx, mn, grid = z["max"][:, CH], z["min"][:, CH], int(z["grid"][0])
print(f"{len(paths)} images; 4e:55 pre-ReLU spatial max: median {np.median(mx):.0f}, "
      f"99th pct {np.percentile(mx, 99):.0f}, top {mx.max():.0f}; spatial min: bottom {mn.min():.0f}")
5000 images; 4e:55 pre-ReLU spatial max: median 196, 99th pct 766, top 1719; spatial min: bottom -575

A note on what is measured. mixed4e is a concatenation of four branches and channel 55 is in the 1×1 branch, so we hook that branch’s convolution before its ReLU. That way the negative pole is visible; after the ReLU it would be zero everywhere. For each image we record the maximum and minimum over the 14×14 spatial grid and where they occur, and show a crop around that location as a stand-in for the receptive field.

The two poles on 5,000 photographs

Contact sheets of the strongest positive and negative responses
def sheet(order, locs, n=40, ncol=8, size=96):
    W = Image.new("RGB", (ncol*size, (n//ncol)*size), "white")
    for k, i in enumerate(order[:n]):
        c = receptive_crop(pathlib.Path(z["paths"][i]), locs[i, CH], grid).resize((size, size))
        W.paste(c, ((k % ncol)*size, (k//ncol)*size))
    return W
pos = sheet(np.argsort(-mx), z["argmax_yx"]); neg = sheet(np.argsort(mn), z["argmin_yx"])
fig, axes = plt.subplots(1, 2, figsize=(11, 3.8))
for ax, im, t in zip(axes, [pos, neg], ["positive pole (top 40)", "negative pole (bottom 40)"]):
    ax.imshow(im); ax.set_title(t); ax.axis("off")
plt.tight_layout()

Left: crops around the location of the 40 strongest positive responses of 4e:55 in COCO val2017. Right: the 40 strongest negative responses. Each crop is about 45% of the 224-pixel input, centred on the activation’s location.

The positive sheet is the famous grid: frontal cat faces and the fronts of cars, with a couple of small dogs. The negative sheet is clocks, the round heads of bears and cows, teddy bears, the backs of human heads, plates and bowls. The essay’s description of the negative pole as “an O” holds up on a dataset the neuron was never ranked on before.

Is it really just cats and cars? Using the labels

COCO’s instance annotations tell us what is in each image. If the neuron were an OR of cat and car, images with neither should not reach the top. If it is a V-shape detector, some should, and they should be pointed things.

Label statistics of the top images
ann = json.load(open(ROOT / "data/coco/annotations/instances_val2017.json"))
cat_name = {c["id"]: c["name"] for c in ann["categories"]}
labels = {}
for a in ann["annotations"]:
    labels.setdefault(a["image_id"], set()).add(cat_name[a["category_id"]])
ids = [int(pathlib.Path(p).stem) for p in z["paths"]]
def has(i, names): return bool(labels.get(ids[i], set()) & set(names))
order = np.argsort(-mx)
vehicles = ["car", "truck", "bus"]
print(f"{'top-k':>6s} {'cat':>6s} {'car/truck/bus':>14s} {'dog':>6s} {'none of these':>14s}")
for k in (20, 50, 200, 500):
    top = order[:k]
    print(f"{k:6d} {np.mean([has(i,['cat']) for i in top]):6.2f} "
          f"{np.mean([has(i,vehicles) for i in top]):14.2f} "
          f"{np.mean([has(i,['dog']) for i in top]):6.2f} "
          f"{np.mean([not has(i,['cat','dog']+vehicles) for i in top]):14.2f}")
 top-k    cat  car/truck/bus    dog  none of these
    20   0.85           0.10   0.05           0.00
    50   0.66           0.30   0.04           0.02
   200   0.27           0.42   0.07           0.26
   500   0.14           0.38   0.06           0.44
The strongest responses among images with no cat, dog, car, truck or bus
rest = [i for i in order if not has(i, ["cat", "dog"] + vehicles)]
print(f"best 'neither' image: activation {mx[rest[0]]:.0f} vs overall top {mx[order[0]]:.0f} "
      f"and 99th percentile {np.percentile(mx, 99):.0f}")
from collections import Counter
print("most common labels in the top 60 of these:",
      Counter(l for i in rest[:60] for l in labels.get(ids[i], [])).most_common(10))
fig, ax = plt.subplots(figsize=(8, 5))
ax.imshow(sheet(rest, z["argmax_yx"])); ax.axis("off")
best 'neither' image: activation 877 vs overall top 1719 and 99th percentile 766
most common labels in the top 60 of these: [('person', 21), ('chair', 14), ('bottle', 8), ('cup', 8), ('remote', 7), ('tennis racket', 7), ('sink', 7), ('bed', 6), ('tv', 6), ('airplane', 6)]

The 40 strongest positive responses among COCO images that contain no cat, dog, car, truck or bus. The best of these reaches above the 99th percentile of all images.

What drives the neuron when cats and cars are excluded: frontal animal faces (goat, bighorn sheep, horse, zebra, bear, small birds), the noses of trains and aircraft, computer mice and remote controls seen end-on, and scenes with strong converging perspective lines such as tennis courts, baseball diamonds, railway tracks and bed corners. The essay’s own examples of what the V generalises to were fox faces, pool tables and bullet trains. The COCO list is the same kind of thing: pointed fronts and converging lines. This is the strongest evidence on this page for the “V, not OR(cat, car)” reading.

The circuit: inputs and readers from the weights

The essay names specific neighbours. Because mixed4e’s 1×1 branch is a 1×1 convolution, its weight matrix is directly the vector of votes each mixed4d channel casts for 4e:55, and the mixed5a convolutions give the votes 4e:55 casts for each mixed5a channel.

Rank the essay’s named inputs and reader by weight
w_in = model.mixed4e_1x1_pre_relu_conv.weight[CH, :, 0, 0].detach().cpu()
top = torch.argsort(-w_in)[:8]
print("strongest inputs from mixed4d  (channel, weight):", [(int(i), round(float(w_in[i]), 3)) for i in top])
for c, name in [(493, "fox snout"), (477, "wolf face"), (201, "van front")]:
    print(f"  essay's 4d:{c} ({name}): weight {float(w_in[c]):+.3f}, rank {int((w_in > w_in[c]).sum())+1} of 528")
w1 = model.mixed5a_1x1_pre_relu_conv.weight[:, CH, 0, 0].detach().cpu()
print("strongest readers in mixed5a 1x1 branch:", [(int(i), round(float(w1[i]), 3)) for i in torch.argsort(-w1)[:6]])
print(f"  essay's 5a:94 (V-shaped flames): weight {float(w1[94]):+.3f}, rank {int((w1 > w1[94]).sum())+1} of 256")
strongest inputs from mixed4d  (channel, weight): [(493, 0.311), (511, 0.128), (61, 0.116), (352, 0.102), (334, 0.08), (383, 0.079), (143, 0.072), (477, 0.068)]
  essay's 4d:493 (fox snout): weight +0.311, rank 1 of 528
  essay's 4d:477 (wolf face): weight +0.068, rank 8 of 528
  essay's 4d:201 (van front): weight +0.057, rank 15 of 528
strongest readers in mixed5a 1x1 branch: [(94, 0.1), (240, 0.058), (81, 0.057), (254, 0.057), (102, 0.057), (62, 0.055)]
  essay's 5a:94 (V-shaped flames): weight +0.100, rank 1 of 256

The essay’s named input 4d:493 is the single strongest weight into the neuron, by a factor of more than two over the next, and its named reader 5a:94 is the single strongest reader in that branch. The other two named inputs are in the top fifteen of 528. These are raw weights rather than data-weighted attributions, so the agreement is if anything stronger than the essay needed. (We have not visualised 4d:493 or 5a:94 ourselves; the labels “fox snout” and “V-shaped flames” are the essay’s.)

A synthetic probe: does a bare V excite it?

If the neuron were a geometric V detector in the way an early-layer curve detector is a curve detector, a line drawing of a V should drive it. We draw simple shapes on a grey background and record the pre-ReLU maximum and minimum.

Line-drawn shapes on grey
from PIL import ImageDraw
feats = {}
hook = dict(model.named_modules())["mixed4e_1x1_pre_relu_conv"].register_forward_hook(
    lambda m, i, o: feats.__setitem__("x", o))
def canvas(): return Image.new("RGB", (224, 224), (128, 128, 128))
def shape(kind, size=90, width=10, angle=0.0, color=(0, 0, 0), cx=112, cy=112):
    im = canvas(); d = ImageDraw.Draw(im)
    if kind == "V":
        a = np.deg2rad(angle); half = np.deg2rad(35)
        apex = np.array([cx, cy]) + size*0.5*np.array([np.sin(a), np.cos(a)])
        for s in (+1, -1):
            end = apex - size*np.array([np.sin(a+s*half), np.cos(a+s*half)])
            d.line([tuple(apex), tuple(end)], fill=color, width=width)
    elif kind == "O": d.ellipse([cx-size/2, cy-size/2, cx+size/2, cy+size/2], outline=color, width=width)
    elif kind == "disc": d.ellipse([cx-size/2, cy-size/2, cx+size/2, cy+size/2], fill=color)
    elif kind == "square": d.rectangle([cx-size/2, cy-size/2, cx+size/2, cy+size/2], outline=color, width=width)
    elif kind == "X":
        d.line([(cx-size/2, cy-size/2), (cx+size/2, cy+size/2)], fill=color, width=width)
        d.line([(cx-size/2, cy+size/2), (cx+size/2, cy-size/2)], fill=color, width=width)
    elif kind == "triangle":
        d.polygon([(cx, cy+size/2), (cx-size/2, cy-size/2), (cx+size/2, cy-size/2)], outline=color, width=width)
    return im
@torch.no_grad()
def run(imgs):
    x = torch.stack([torch.from_numpy(np.asarray(im, dtype=np.float32) - 117).permute(2, 0, 1) for im in imgs]).to(DEV)
    model(x); fm = feats["x"][:, CH]
    return fm.flatten(1).max(1).values.cpu().numpy(), fm.flatten(1).min(1).values.cpu().numpy()
kinds = ["V", "triangle", "X", "square", "O", "disc", "blank"]
sizes, widths, colours = [60, 90, 120, 150], [6, 12], [(0, 0, 0), (255, 255, 255)]
print(f"{'shape':9s} {'mean max':>9s} {'mean min':>9s}    (over 4 sizes x 2 widths x 2 colours; photo scale: 99th pct max = {np.percentile(mx,99):.0f})")
for kind in kinds:
    imgs = [canvas() if kind == "blank" else shape(kind, s, w, color=c) for s in sizes for w in widths for c in colours]
    a, b = run(imgs); print(f"{kind:9s} {a.mean():9.0f} {b.mean():9.0f}")
hook.remove()
shape      mean max  mean min    (over 4 sizes x 2 widths x 2 colours; photo scale: 99th pct max = 766)
V                54       -85
triangle         97      -100
X               148       -85
square           42      -125
O                44      -136
disc             16      -160
blank            -0       -76

Two things come out. The round shapes (an outline O and a filled disc) give the most negative responses of any stimulus, a good deal below a blank grey field, so “round pushes it negative” survives even in line drawings. But a bare V does not excite the neuron much: its positive response is about a tenth of what a real cat face produces and no larger than a square’s. Whatever “V” means for this neuron, it is a pattern inside high-level structure (a face, a car front, a converging road) rather than a geometric primitive. The essay says as much in its footnote: “generalization happens inside high-level patterns”, and the V neuron “really does take in cat circuitry”. A mid-network neuron’s inputs are already fairly abstract, so this is not a surprise, but it is a limit on how literally to take the word “V”.

Assessment

  • Confirmed on public weights: the named inputs and reader are the strongest by weight; the negative pole is round things; the positive pole extends beyond cats and cars to frontal animal faces, vehicle noses and converging lines.
  • Supported but not proven: that “pointed versus round” is the right one-line description. It fits everything we saw, and it fits better than OR(cat, car), which cannot explain the goats, trains and tennis courts. A line-drawn V alone is not enough, so the description is of a high-level shape cue, not a contour.
  • Not addressed here: the second half of the essay’s section, the car-into-dogs circuit and the “generalised pose neurons”. That needs feature visualisation with and without the bias toward natural paths, which is the subject of the next section.

Questions from the reading group

Raised in discussion on 22 October 2026. The answers are ours, not the essay’s.

Before the essay, was the idea that 4e:55 fired on both cats and cars because it was helping encode those two features in superposition, with many other neurons firing too?

Roughly yes, with a historical wrinkle. Zoom In (2020) described 4e:55 as responding to cat faces, cat legs and car fronts, and offered superposition as a hypothesis for why: a network that needs more features than it has neurons folds several into one, and a neuron that is “OR(cat, car)” is the visible symptom. Toy Models of Superposition only made this precise in 2022. Under the precise version a feature is a direction in activation space, and if the car direction is not neuron-aligned it spans several neurons: 4e:55 carries a piece of “car” and a piece of “cat”, other neurons carry other pieces, and a downstream reader recovers “car” by the linear combination that cancels the cat part. Interference is tolerable because cats and cars rarely co-occur. The car-into-dogs circuit was read the same way, as the car being stored across several dog neurons for later decoding.

The essay’s rebuttal is aimed at the decoding step. If that were the mechanism, some downstream weight pattern would have to read the cancelling combination. The essay says none does: the readers of 4e:55 use it as one thing (its strongest reader is a V-shaped-flame neuron, which we confirmed from the weights above), and the dog neurons the car writes into are shared pose neurons feeding car specialists. “Nothing reads that combination” turns a question about appearances into a question about weights.

The neuron does not fire strongly for a literal drawn V. How should we make sense of the “V-shape” label? Is it “V-shapes in objects”?

Yes, and we would go a little further. A neuron at mixed4e is defined by its inputs, and its strongest inputs are already part detectors: a fox snout, a wolf face, a van front. What those share is a pointed, convergent frontal structure built from the textures and parts that mid-level neurons respond to. A line drawing has the geometry but none of the parts, so it drives none of the inputs and therefore barely drives the neuron. The essay’s own footnote says this: to see the shared pattern you have to visualise what the neuron’s inputs have in common, not the neuron on its own.

So “V” is a compression of “pointed convergent front, as it appears in natural objects”. The COCO result supports that compression, since goats, horses, train noses, aircraft and converging court lines all drive it, which OR(cat, car) cannot explain. What the evidence does not settle is whether “V” is a better label than “frontal pointed object”. A sharper synthetic test would use photographic crops of pointed things with no face, such as boat bows, roof gables and arrowheads, rather than drawn lines.

Discussion questions

  1. The neuron fires on cat faces far more strongly than on anything else in COCO. Is a neuron whose top of the distribution is 85% cats, but whose tail is “pointed things”, best called a cat neuron, a V neuron, or something else? Does the answer depend on what reads it?
  2. The essay’s recommended trick is to describe a neuron without nouns. Try it on the negative-pole sheet above. Is “round” the only candidate?
  3. How would one tell a “V-shape” neuron from a “frontal face or front of object” neuron? What stimulus would separate them?