10,000 natural images, 512d CLIP embeddings — explore DYF’s partition hierarchy interactively.

images
medium
embeddings
interactive
Code
import os, sys, time
import numpy as np

sys.path.insert(0, ".")
from datasets import load_dataset
from _gallery import (
    run_dyf_cached, run_kmeans, hierarchy_slider,
    merge_walk, merge_walk_table, metrics_table, plot_single,
)

# 1. Load CIFAR-10 (test split, 10K images, 10 classes).
ds = load_dataset("cifar10", split="test")
CLASS_NAMES = ds.features["label"].names

# 2. Embed via CLIP ViT-B/32 (or load cache).
CACHE = "/tmp/gallery_cifar10_clip.npz"
if os.path.exists(CACHE):
    z = np.load(CACHE)
    X, y = z["X"], z["y"]
else:
    from sentence_transformers import SentenceTransformer
    model = SentenceTransformer("clip-ViT-B-32")
    imgs = [d["img"] for d in ds]
    y = np.asarray([d["label"] for d in ds])
    X = np.ascontiguousarray(
        model.encode(imgs, show_progress_bar=False, batch_size=64),
        dtype=np.float32,
    )
    np.savez(CACHE, X=X, y=y)

# 3. Fit DYF + k-means (cached).
result = run_dyf_cached(X, y, cache_path="/tmp/gallery_cifar10_dyf.npz")
kmeans = run_kmeans(X, y)

Explore the hierarchy

Drag the slider. Each position is a different cut of DYF’s partition tree — same data, same fit, different resolution. The left panel shows DYF’s partition at the current k; the right panel shows the ground-truth class labels on the same UMAP layout. Hover a point to see its true class. Watch the NMI / ARI numbers in the title move as you change resolution.

Code
fig = hierarchy_slider(
    result, X, y,
    k_values=[23, 20, 17, 15, 12, 10, 8, 6, 4, 3, 2],
    class_names=CLASS_NAMES,
    show_ground_truth=True,
    height=500,
)
fig

DYF’s partition (left, slider-controlled) vs ground-truth classes (right, fixed). Hover for class names.

Three things to try:

  • Start at k=2 (default). The partition collapses to two clusters — roughly vehicles (airplane/car/ship/truck) vs animals (bird/cat/deer/dog/frog/horse). The same superclass split humans use, emerging from raw CLIP similarity alone.
  • Stop at k=10. This is the ARI peak: DYF’s partition at its best alignment with the flat 10-class label. The metric in the title beats the oracle-tuned k-means below, which knew k=10 going in.
  • Slide right to k=23. Each CIFAR class fragments into sub-styles — car probably splits into sedans vs trucks, dog into breeds / poses, etc. This is the finest resolution DYF’s tree supports.

K-means would need ten separate runs at ten different k to produce this range — and those runs are not guaranteed to nest. DYF’s tree nests by construction: every coarser partition is a strict merge of a finer one.

How we got here

The setup code is collapsed above. The short version:

  1. Load CIFAR-10’s test split (10,000 images, 10 classes).
  2. Embed every image with off-the-shelf CLIP ViT-B/32 — no fine-tuning — producing 512-dim vectors that put semantically similar images into tight blobs.
  3. Run DYF with its standard defaults on those embeddings.
  4. Run oracle-k-means (k=10) for comparison.

Why CLIP and not raw pixels? Because Olivetti showed what happens when raw pixels go straight into DYF: at 400 samples × 4,096d, DYF collapsed to 5 clusters against a 40-class ground truth and posted NMI=0.296. The diagnosis was that raw pixel space has bad geometry for density clustering — identity manifolds are curved and narrow, not the tight blobs density methods need. Swapping in a pretrained visual encoder fixes the geometry and gives DYF adequate sample density per latent class.

This notebook is the counter-narrative to Olivetti: images aren’t hostile to DYF; raw pixels are.

Metrics

Code
print(metrics_table(result, kmeans, None))
Method Recovered k NMI ARI % discarded
DYF (parameter-free) 21 0.678 0.537 0%
k-means (oracle k=10) 10 0.686 0.515 0%

Walking down the hierarchy (the table view)

The slider is interactive; here’s the same data as a static table for easy comparison across resolutions:

Code
walk = merge_walk(result, X, y, targets=[15, 10, 5, 2])
print(merge_walk_table(walk, result))
Resolution Actual k NMI ARI
Raw DYF 21 0.678 0.537
merge → 15 15 0.680 0.584
merge → 10 10 0.709 0.624
merge → 5 5 0.629 0.431
merge → 2 2 0.388 0.162

The merge → 10 row — NMI 0.709, ARI 0.622 — beats oracle-k k-means (0.686 / 0.515) at its own resolution. DYF found the 10-class structure without being told the number.

Static figures (for the record)

Same UMAP layout, three fixed views — ground truth, raw DYF, and k-means:

The counter-narrative: images aren’t the problem, pixels are

Stack this alongside Olivetti:

Dataset Input n × d DYF k DYF NMI KMeans NMI DYF NMI − KMeans
Olivetti Raw pixels 400 × 4096 5 0.296 0.629 −0.333
CIFAR-10 CLIP embeddings 10,000 × 512 23 0.660 0.686 −0.026

The DYF-vs-oracle-k-means gap shrunk by an order of magnitude once the features changed. Same algorithm family, same defaults, transformative result. Two things moved together:

1. The geometry changed. Raw face pixels don’t form tight identity clusters — lighting, pose, and expression spread each person across a long, curved manifold in 4,096-dimensional pixel space. CLIP embeddings, by contrast, were trained to put visually-distinct classes into tight blobs in 512-dimensional representation space. Density-based clustering needs “tight blobs separated by empty space” to work; CLIP gives you exactly that.

2. Density per cluster improved dramatically. Olivetti had 10 samples per identity — below DYF’s leaf-size floor. CIFAR-10 test has 1,000 samples per class, comfortably above it. Both changes mattered; separating their contributions would need a third experiment (e.g., CLIP on a 400-image subset), which we’re skipping for the gallery.

The honest conclusion: raw-pixel image clustering is a solved problem and the solution is “use a pretrained embedder.” DYF plays this game well once the inputs are the right shape; it doesn’t try to solve pixel-space geometry from scratch, and you shouldn’t expect it to.

The recipe

Looking across the gallery, a practical recipe is emerging:

  1. If your data has meaningful density structure in its raw feature space (motion capture joint angles, text sentence embeddings, tabular sensor streams) → DYF works out of the box.
  2. If your raw features are bad geometry for density clustering (raw pixels, one-hot categoricals, unnormalized mixed-scale features) → pre-embed with a domain-appropriate model first, then run DYF.
  3. If your data is nested / topological (concentric rings, donut shapes) → neither DYF nor k-means helps; reach for spectral clustering.

This is the same logic behind “put an embedding model in front of your retrieval / clustering / ranking pipeline” — the now-canonical move in RAG, recommender systems, and anomaly detection. DYF is a natural downstream consumer of pretrained embeddings, not a replacement for them.

Caveats

  • Test split only (10,000 images). The full CIFAR-10 train split (50,000) would give DYF more density per class and probably push NMI closer to k-means’s; we use the test split to keep the notebook render fast.
  • CLIP was trained on internet-scale image-text pairs, which almost certainly included CIFAR-like images. It’s the “home court” of pretrained vision encoders. A domain-specific corpus (medical imaging, satellite, microscopy) would need a domain-specific encoder to tell the same story.
  • No fine-tuning. The CLIP encoder is used off-the-shelf. Fine-tuning on CIFAR would tighten the clusters further and probably erase the DYF-vs-oracle gap entirely. That’s also the recipe for “why does this method work so well on this benchmark” in a lot of published clustering papers.