Biological learning toy

The Fly's Trick

A small MNIST experiment inspired by the fly olfactory system: expand into many sparse random features, keep a tiny active tag, and learn with a simple local readout.

July 2026 · sparse expansion · MNIST · local plasticity

No backprop through features Sparse random projection Top-k code Local readout

The fly olfactory circuit has a surprisingly useful computational pattern: project a compact sensory input into a much larger population, keep only a small sparse set of active cells, and let downstream neurons read out that sparse tag.

Plainly: the fly turns a small smell signal into a big sparse code that is easier to learn from.

I wanted a tiny artificial version of that idea. Not a biologically faithful fly model, and not an MNIST benchmark. Just a clean mechanism test: if I turn each digit into a sparse "Kenyon-cell-like" tag, how much structure does that tag preserve before any deep training?

Plainly: this is a mechanism demo, not a claim that the toy is a real fly model or a strong MNIST system.

Figure from Dasgupta, Stevens, and Navlakha showing the fly olfactory system as a sparse expansion circuit
Biological inspiration: Figure 1 from Dasgupta, Stevens, and Navlakha, linked from the Science-hosted source image. My toy circuit below is an artificial MNIST translation of the same expansion-and-sparsification motif.

What happens in the fly

In the fly, smell is not represented as a single scalar like "banana" or "danger." Odor molecules activate receptor neurons, those signals are organized into glomeruli in the antennal lobe, and projection neurons carry a compact odor pattern into higher brain areas. The mushroom body then performs the part that matters for this post: it expands that compact pattern into many Kenyon cells, keeps the response sparse, and gives downstream neurons a clean tag to learn from.

Plainly: the fly first makes a compact odor vector, then expands it and keeps only a few active neurons.

Fly olfactory pathway as sparse random expansion Odor inputs form a compact glomerular pattern. Projection neurons converge apparently randomly onto many Kenyon cells. APL feedback and thresholds keep only a sparse subset active, and output neurons read out the sparse Kenyon-cell tag. odor input antennal lobe mushroom body readout molecules activate receptor neurons glomeruli compact odor vector PNs Kenyon cells large expanded population only a small sparse tag remains active APL feedback global inhibition MBONs learned output The computational motif compact odor code apparently random expansion sparse tag same shape as the toy circuit below

The important detail from Caron, Ruta, Abbott, and Axel is that individual Kenyon cells receive apparently random combinations of olfactory glomerular inputs. The important detail from Lin and colleagues is that feedback inhibition helps keep Kenyon-cell odor responses sparse and decorrelated.

1

Detect and compress

Odor receptors feed the antennal lobe. Glomeruli turn a chemical mixture into a compact population pattern.

2

Expand randomly

Projection-neuron activity fans into the mushroom body. Kenyon cells sample different, apparently random mixtures of glomerular input.

3

Sparsify hard

High thresholds and APL feedback suppress most Kenyon cells, so each odor leaves a small active tag rather than a dense response.

4

Read out associations

Downstream mushroom-body output neurons can attach learned value to the sparse tag. In the toy, this is the final readout matrix.

Fly input glomerular odor pattern Toy input \(x \in \mathbb{R}^{784}\)
PN-to-KC convergence fixed random-looking fan-in Toy expansion \(W \in \mathbb{R}^{F \times 784}\)
KC sparsening thresholds plus inhibition Toy sparsening \(\operatorname{TopK}(a)\)
MB output learned association from a sparse tag Toy readout \(z = tR\)

What I intentionally leave out: spikes, timing, receptor chemistry, lateral-horn pathways, dopamine details, and the real mushroom body's cell-type structure. This post only keeps the expansion-and-sparsification algorithmic skeleton.

Plainly: I am borrowing the circuit shape, not trying to model every biological detail.

The toy circuit

\(28 \times 28\) pixels
\(x \in \mathbb{R}^{784}\)
\(W \in \mathbb{R}^{F \times 784}\)
\(a = |W x| \in \mathbb{R}^{F}\)
\(t = \operatorname{TopK}_{5\%}(a) \in \{0,1\}^{F}\)
\(z = tR \in \mathbb{R}^{10}\)

Pixels1

\(28 \times 28\) grayscale image. There are \(784\) input numbers.

Flatten2

784-vector same information, now a vector

The image becomes one vector \(x \in \mathbb{R}^{784}\). Nothing has been learned yet.

Sparse random projection3

sparse random W x

The projection matrix is \(W \in \mathbb{R}^{F \times 784}\). It creates many random measurements of the same image before the top-k step keeps only the strongest responses.

Expanded activations4

expanded responses F swept from 400 to 64,000

The projection gives \(a = |Wx| \in \mathbb{R}^{F}\). For \(F=64{,}000\), one digit has 64,000 responses.

Keep top 5%5

top-k WTA if F=64,000: 3,200 active 60,800 silent dimensions

The dense response becomes \(t \in \{0,1\}^{F}\), with \(\|t\|_0 = 0.05F\).

Readout6

readout scores x

The readout is \(R \in \mathbb{R}^{F \times 10}\), producing \(z = tR \in \mathbb{R}^{10}\).

The toy replaces odors with pixels. The important shape is the bottleneck: a compact image vector becomes a much larger random response, then a tiny sparse tag. The readout only sees that tag.

Why this can work

Random expansion is useful when the sparse tag preserves overlap: two versions of the same digit should share more active cells than unrelated digits. The readout does not need to understand pixels directly; it only needs to attach class evidence to the cells that keep winning together.

Plainly: similar inputs should activate overlapping sparse features, and the readout can learn from that overlap.

Sparse tags preserve useful overlap Two examples of the digit three produce sparse tags with several shared active cells. A different digit activates mostly different cells. A class prototype or readout can score a new tag by looking up weights on its active cells. input top-k sparse tag readout evidence 3 3 7 class 3 evidence cells that fire often for 3s get weight score a new tag: 3 7

The trick is not that random features know the label. It is that expansion creates many possible coincidences, sparsity keeps the strongest ones, and downstream learning can reuse repeated winners as class evidence.

1. Random measurements preserve more than they destroy

The Johnson-Lindenstrauss story says that a random linear map can preserve pairwise distances for a finite cloud of points with surprisingly few dimensions:

Plainly: random projections can keep useful geometry even when the measurements look arbitrary.

\[(1-\epsilon)\|x-y\|_2^2 \leq \|P(x-y)\|_2^2 \leq (1+\epsilon)\|x-y\|_2^2.\]

That theorem is usually stated for dimensionality reduction. Here the direction is flipped: I am using many random measurements as an overcomplete feature map. The same concentration intuition still matters: one random row is noisy, but many random rows give a stable statistical sketch of the input geometry.

Plainly: one random feature is weak, but thousands of them can form a useful sketch.

2. Similar inputs induce correlated responses

If \(x\) and \(y\) are close or aligned, then a random row \(w_i\) tends to give related responses \(w_i^\top x\) and \(w_i^\top y\). For an isotropic random row, the raw dot products are unbiased probes of input similarity:

Plainly: close inputs tend to make the same random tests respond in related ways.

\[\mathbb{E}\left[(w_i^\top x)(w_i^\top y)\right] \propto x^\top y.\]

The absolute value in this toy ignores sign and keeps response magnitude:

\[a_i(x) = |w_i^\top x|.\]

So the random projection is not inventing labels. It is producing many weak, geometry-sensitive tests.

Plainly: the random layer does not know the answer; it only exposes many small clues.

3. Top-k turns weak tests into a sparse locality code

The top-k step keeps only the cells with unusually large responses:

Plainly: keep the strongest responses and turn everything else off.

\[t_j(x)=1[j \in \operatorname{TopK}(a(x))].\]

Now similarity becomes overlap. Two nearby inputs should share more winners, while unrelated inputs should share fewer. This is close in spirit to locality-sensitive hashing: preserve neighborhood structure, not every pixel-level detail.

Plainly: similar examples should share winners; different examples should share fewer.

4. The readout only has to add evidence

Once the tag is sparse, classification can be a sum over active cells:

Plainly: prediction becomes adding up evidence from the few cells that fired.

\[\operatorname{score}_c(x)=\sum_{j:t_j(x)=1} R_{jc}.\]

The class-mean baseline estimates which cells tend to fire for each digit. The delta readout does the same kind of evidence accumulation, but adjusts weights using output error, so it can amplify useful winners and suppress misleading ones.

Plainly: the readout learns which active cells usually help or hurt each digit.

The important caveat is that this only helps if task structure is already visible in the geometry of the input. MNIST has enough geometry that random sparse expansion can expose useful coincidences. A task requiring invariance, sequence memory, or compositional reasoning would need more than this frozen random map.

Plainly: this works best when the raw input already has structure that random features can uncover.

The minimal code

The core of the experiment is just sparse random projection plus a top-k tag. The feature extractor is fixed; only the final readout learns.

Plainly: the random feature layer stays frozen, and only the last layer changes.

acts = abs(X @ W.T)                      # random expanded responses
winners = argpartition(-acts, k - 1)     # top-k Kenyon-cell-like tag

logits = tag @ W_readout
probs = softmax(logits)
err = one_hot(label) - probs
W_readout += lr * tag.T @ err / batch    # local readout update

View the full public script

Dependencies: NumPy and SciPy for the MNIST run; scikit-learn only if you use the optional --digits smoke test.

What I measured

I swept the number of expanded features F. Each expanded feature samples 12 random pixels, and the code keeps the top 5% active cells. I compared two deliberately simple readouts:

Plainly: I changed the size of the random feature bank and compared two simple ways to read the sparse tag.

  • Class-mean baseline: build one prototype tag per digit by counting which cells usually fire for that digit. At test time, score a new tag by summing the prototype values under its active cells and pick the largest score.
  • Delta readout: a single local error-Hebbian layer trained from tag activity and output error.

The local readout is much stronger, but the important part is the shape: expansion helps even though the feature map is random and frozen. The class-mean readout saturates early; the learned local readout keeps benefiting at larger capacity.

Plainly: the learnable readout uses the sparse tag better, especially as the random feature bank grows.

Sparse expansion improves MNIST tag accuracy A line chart showing class-mean baseline accuracy staying near 60 to 64 percent, and local delta readout accuracy rising to 87.50 percent as expanded features increase to 64,000. 55% 65% 75% 85% 400 800 2k 4k 8k 16k 32k 64k local delta readout class-mean baseline

Expansion helps the learned local readout more than the class-mean prototype readout. The absolute numbers are not the claim; the useful observation is that a very simple, forward-only feature map creates a tag that a local readout can exploit.

Expanded features Active tag size Expansion Class-mean baseline Delta readout
400200.5x59.64%76.88%
800401.0x60.87%80.00%
2,0001002.6x62.76%82.52%
4,0002005.1x63.24%82.75%
8,00040010.2x64.21%85.11%
16,00080020.4x63.99%83.48%
32,0001,60040.8x64.04%87.16%
64,0003,20081.6x63.83%87.50%

Fixed \(F=64{,}000\): shrinking the active tag

I also held the random expansion fixed at \(F=64{,}000\) and made the winner-take-all tag smaller. This asks a different question: once the random feature bank is very large, how many active cells does the readout actually need?

Plainly: after making a huge random feature bank, I tested how sparse the active tag can get before accuracy breaks.

Accuracy at 64,000 random features as active tag size shrinks A line chart showing fixed 64,000 feature accuracy as active tag size shrinks from 25,600 to 25. The class-mean baseline stays near 64 percent, while the local delta readout peaks near 3,200 to 12,800 active cells and falls at both extremes. 60% 70% 80% 90% 25.6k 12.8k 6.4k 3.2k 1.6k 800 400 200 100 50 25 active cells kept, k local delta readout class-mean baseline

With \(F=64{,}000\), the class-mean prototype readout is almost insensitive to \(k\) until the tag gets extremely small. The learned local readout has a broad sweet spot: dense tags are not automatically better, while even \(k=25\), only 0.039% of the feature bank, still reaches 80.40%.

Active tag size Sparsity Class-mean baseline Delta readout
25,60040.000%64.00%83.81%
12,80020.000%64.01%87.46%
6,40010.000%63.85%85.64%
3,2005.000%63.83%87.47%
1,6002.500%64.08%86.14%
8001.250%64.32%85.36%
4000.625%64.32%85.16%
2000.313%64.39%84.80%
1000.156%64.32%84.04%
500.078%63.92%82.60%
250.039%63.22%80.40%

Why this is interesting

The usual deep learning move is to learn the feature extractor end-to-end. This toy goes the other direction: spend a lot of cheap random features, sparsify hard, and ask whether the resulting tag is already useful.

Plainly: instead of learning features, this asks how far fixed random features can get.

That is why I like the fly analogy. Expansion and sparsity are not just implementation details; they are a bet about representation. A random dense projection smears everything. A sparse expanded tag gives downstream learning a high-dimensional set of coincidences to attach meaning to.

Plainly: sparse expansion gives the readout many reusable hooks instead of one blurred representation.

The next question is not "can this beat a CNN on MNIST?" It should not. The next question is which parts of the trick survive when the task needs invariance, temporal order, or continual learning.

Plainly: MNIST is just the sandbox; the real question is whether this trick helps on harder learning problems.

What I am not saying

  • This is not a faithful fly model. It is a small artificial circuit inspired by one computational motif.
  • This is not state-of-the-art MNIST. A normal CNN will crush it.
  • This is not the latest private research direction. It is the safe public scaffold: mechanism, toy code, and measured behavior.

References

  1. Dasgupta, Stevens, and Navlakha. A neural algorithm for a fundamental computing problem. Science, 2017.
  2. Caron, Ruta, Abbott, and Axel. Random convergence of olfactory inputs in the Drosophila mushroom body. Nature, 2013.
  3. Lin, Bygrave, de Calignon, Lee, and Miesenbock. Sparse, decorrelated odor coding in the mushroom body enhances learned odor discrimination. Nature Neuroscience, 2014.
  4. Johnson and Lindenstrauss. Extensions of Lipschitz mappings into a Hilbert space. Contemporary Mathematics, 1984.
  5. Dasgupta and Gupta. An elementary proof of a theorem of Johnson and Lindenstrauss. Random Structures & Algorithms, 2003.
  6. Achlioptas. Database-friendly random projections: Johnson-Lindenstrauss with binary coins. Journal of Computer and System Sciences, 2003.
  7. LeCun, Cortes, and Burges. The MNIST database of handwritten digits.
  8. Indyk and Motwani. Approximate nearest neighbors: towards removing the curse of dimensionality. STOC, 1998.
  9. Rahimi and Recht. Random Features for Large-Scale Kernel Machines. NeurIPS, 2007.