# Hierarchical Classification
Hierarchical classification means classifying something into a *tree* of categories instead of a flat list. You start at the root, pick the best child, then the best child of that, and so on until you reach a leaf. Patent classes, product catalogs, medical subject headings, a codebase's folder structure, an agent's skill catalog, a moderation policy: a lot of real-world labels are organized this way.
Why not just flatten the tree and pick a leaf? Because real taxonomies have thousands of leaves, and a flat choice over thousands of options is both expensive and confusing (for models and humans alike). The tree is also information. Knowing that "Cat Window Beds" lives under "Pet Supplies > Cats" helps you pick it. And when you can't pick the leaf with confidence, the parent is still a useful answer.
## The general idea
Two well-known ideas meet here.
**Top-down hierarchical classification.** Koller and Sahami (ICML 1997, "Hierarchically classifying documents using very few words") decomposed document classification into a small classifier at each node of a topic tree, each one only needing a few features to separate its children. Easier problems per node, and the tree does the rest. The weakness: errors propagate. A wrong turn at the top can never be undone by a better decision lower down.
**Beam search.** Instead of committing to the single best option at each step (greedy), keep the top K partial paths and expand all of them, pruning back to K at each level. It dates back to the Harpy speech recognition system (Bruce Lowerre's 1976 dissertation) and it's how machine translation and LLM decoding explored multiple candidate outputs for decades. Applied to a taxonomy, it lets later evidence rescue an ambiguous early choice.
A third idea completes the picture: **backoff to the parent.** When the fine-grained answer is uncertain, report the coarser label instead. It's the hierarchical version of the reject option (see [[Confidence-Gated Routing]]).
## How Jev makes it cheap
Every node becomes one [[Jev]] Choice question whose options are the node's children, and the Choice returns a full probability distribution over them. The trick that makes beam search affordable: all K frontier nodes of the beam are asked as *parallel questions in the same request*. So exploring three paths costs about the same wall-clock time as exploring one. You pay one round trip per level of the tree, not one per path.
This is also one of the three cases where the docs say you genuinely need sequential requests (the next level's options depend on the previous answer), so depth drives latency. See [[Speculative Fan-Out]].
Paths are scored with a length-normalized geometric mean, so shallow and deep leaves compare fairly:
```python
path_score = product(edge_probabilities) ** (1 / decisions)
# for deep trees (>10 levels), avoid underflow:
path_score = exp(mean(log(edge_probabilities)))
```
A useful diagnostic (not used for pruning) is `separation = top_path_score / second_path_score`: near 1x means ambiguous, a large ratio means clear. An alternative criterion, `min(top_prob / second_prob)` per node, favors paths where every decision was clear.
## Worked examples
**Beam vs greedy.** The hierarchical classification cookbook ran four examples on four hierarchies: CPC patents (2026.05), the Shopify product taxonomy (2026-02), MeSH biomedical subjects (2026, a DAG expanded through its tree numbers) and TypeSafe's own cookbook repository file tree.
| Hierarchy | Expected leaf | Greedy | Beam K=3 |
|---|---|---|---|
| CPC patents | A01K31/12 Perches for poultry or birds | E99Z99/00 "not otherwise provided for" | correct |
| Shopify products | Cat Window Beds & Perches | Pet Chairs | correct |
| MeSH | Crohn Disease | correct | correct |
| Cookbook files | retrievers.py | correct | correct |
Beam 4/4, greedy 2/4. The CPC failure is telling: greedy took an early turn it couldn't recover from and ended in the "not otherwise provided for" bucket.
**Backoff to the parent.** The classification-using-confidence cookbook classified 60 SEC 10-K business descriptions into 75 industry groups with a single Choice (the tree is shallow enough to flatten one level). Groups roll up into 10 divisions. Rule: confidence ≥ 0.9 reports the group, otherwise report its division. The confident half was right 90% of the time. The unsure half was right 40% at group level, and 70% when reported at division level. 48/60 useful answers instead of 39/60, with no second call. The low-confidence cases were genuinely hard: startups describing a business they *intend* to run, and a company that sold one of its two segments weeks before filing.
**Walking a taxonomy inside one question.** The advanced primitives docs show another option for smaller trees: give each Choice option its *subtree* as the description, so the model sees what lives under a branch before committing. Example: a bike bottle that fits both "Sporting Goods > Cycling > Bike Bottles & Cages" and "Home & Kitchen > Drinkware > Water Bottles". Trim big subtrees to direct children plus a sample of leaves.
## Gotchas
- **Four examples is a demo.** The cookbook says so itself. Measure beam vs greedy on your own labeled data
- **Latency grows with depth.** One round trip per level. A 6-level tree at ~100 ms per level is still fast, but it's not one call
- **Node descriptions carry the weight.** Many taxonomy nodes have terse or empty names (42 of the 75 SIC groups only had an umbrella title, the rest none). The SEC cookbook described each group by its member industries, which is what a human would match against anyway
- **"Other" buckets are traps.** Catch-all nodes ("not otherwise provided for") attract uncertain items. Consider penalizing them or treating them as a low-confidence signal
- **DAGs need care.** When a node has several parents (MeSH), expand the paths explicitly or you'll double-count
- **Choice limit.** 255 options per node (the docs call ~240 the reliable range). Wider nodes need an intermediate grouping
## My take
I like this pattern because it turns classification into something observable. With one big flat prompt, a wrong label is just wrong. With a tree walk, you can see *where* it went wrong: which node, which edge, how close the second path was. The cookbook points that out explicitly: you can count traversals per node, find the nodes that produce most errors, and unit-test the effect of editing the taxonomy. That's software engineering applied to classification, which is exactly TypeSafe's pitch.
The backoff trick is the part I'd reuse first. "Right at a coarser level" is often more valuable than "wrong at a finer level", and it's free when your labels are already a hierarchy. The same advice works for filing things by hand in any tree (see [[Hierarchical Organization]]): when you're unsure where something belongs, a broader home beats a wrong specific one.
## References
- [Hierarchical classification cookbook (TypeSafe docs)](https://docs.typesafe.ai/cookbooks/hierarchical_classification)
- [Classification using confidence cookbook (TypeSafe docs)](https://docs.typesafe.ai/cookbooks/classification_using_confidence)
- [Advanced primitives: structure (TypeSafe docs)](https://docs.typesafe.ai/primitives/advanced)
- [Koller and Sahami, Hierarchically Classifying Documents Using Very Few Words (ICML 1997)](https://ai.stanford.edu/~koller/Papers/Koller+Sahami:ICML97.pdf)
- [Beam search (Wikipedia)](https://en.wikipedia.org/wiki/Beam_search)
## Related
- [[Confidence-Gated Routing]]
- [[Speculative Fan-Out]]
- [[System One Primitives]]
- [[Hierarchical Data]]
- [[Hierarchical Organization]]
- [[Data classification]]
- [[Binary Classification]]
- [[Jev]]
- [[Decision Models (DMs)]]