|
| 1 | +# Aroma — evaluated and deferred |
| 2 | + |
| 3 | +Flavor = taste **+** aroma. The taste side ships and is clean (sweet/bitter/umami |
| 4 | +~0.95 AUROC + intensity + sour/salty). The aroma side is **deliberately deferred** — |
| 5 | +not for lack of effort, but because the public, commercially-clean data is |
| 6 | +insufficient. This documents the evaluation, so the decision is legible. |
| 7 | + |
| 8 | +## What an aroma model needs |
| 9 | + |
| 10 | +A model mapping a molecule's **structure → odor descriptors** (sweet, floral, |
| 11 | +woody…). The state of the art is the **Principal Odor Map** (Lee et al., *Science* |
| 12 | +2023; Google/Osmo), reimplemented open-source as **OpenPOM** (`BioMachineLearning/ |
| 13 | +openpom`, MIT) — a message-passing GNN trained on ~5,000 **expert-labeled** molecules |
| 14 | +(the GS-LF dataset). |
| 15 | + |
| 16 | +## The licensing wall (audited) |
| 17 | + |
| 18 | +OpenPOM's *code* is MIT, but its *training data* is restricted. A full audit of the |
| 19 | +Pyrfume catalogue (see [`DATA-SOURCES.md`](DATA-SOURCES.md)) found that **every rich |
| 20 | +odor-descriptor dataset is proprietary or NonCommercial** — Leffingwell, GoodScents, |
| 21 | +Arctander, Flavornet (©Datu), FlavorDB / FooDB (NC), OlfactionBase ("all rights |
| 22 | +reserved"), AromaDB (CSIR), Dravnieks (ASTM ©), sharma_2021 (ACS ©), snitz_2019 |
| 23 | +(CC-BY-NC). The **only** commercially-clean odor-descriptor set is **`keller_2016`** |
| 24 | +(Keller & Vosshall 2016, *BMC Neuroscience*, CC-BY-4.0; ~480 molecules, 20 descriptors). |
| 25 | + |
| 26 | +## The empirical result |
| 27 | + |
| 28 | +We aggregated `keller_2016` ([`training/build_aroma_dataset.py`](../training/build_aroma_dataset.py)) |
| 29 | +and trained one RandomForest regressor per descriptor on 2048-bit Morgan fingerprints |
| 30 | +([`training/train_aroma.py`](../training/train_aroma.py)), scored by **honest 5-fold |
| 31 | +cross-validation** (400 trees): |
| 32 | + |
| 33 | +| descriptor | CV-R² | descriptor | CV-R² | descriptor | CV-R² | descriptor | CV-R² | |
| 34 | +|---|---|---|---|---|---|---|---| |
| 35 | +| acid | −0.24 | cold | −0.19 | fruit | −0.05 | sour | −0.05 | |
| 36 | +| ammonia | −0.15 | decayed | −0.20 | garlic | −0.12 | spices | −0.12 | |
| 37 | +| bakery | −0.22 | edible | −0.19 | grass | −0.45 | sweaty | +0.03 | |
| 38 | +| burnt | −0.13 | fish | −0.04 | musky | −0.23 | sweet | −0.05 | |
| 39 | +| chemical | −0.10 | flower | −0.12 | warm | −0.28 | wood | −0.08 | |
| 40 | + |
| 41 | +**All 20 descriptors scored CV-R² ≤ 0** (range −0.45 to +0.03) — every model is |
| 42 | +*worse than predicting the mean*. **0/20 usable heads.** |
| 43 | + |
| 44 | +## Root cause |
| 45 | + |
| 46 | +`keller_2016` is **naive-subject** data: random volunteers rating *unfamiliar* |
| 47 | +molecules on a 0–100 scale. People can't reliably name what a molecule smells like, |
| 48 | +so the labels are noise (the per-descriptor means compress to ~22–30 for nearly every |
| 49 | +molecule). The *learnable* odor data uses **expert** labels — which is exactly the |
| 50 | +data that's restricted. **That is the structural reason the entire field trains on |
| 51 | +GS-LF**, and why a clean public aroma model isn't currently possible. |
| 52 | + |
| 53 | +## Decision |
| 54 | + |
| 55 | +We do **not** ship a negative-R² model — it would output confident, wrong smells, and |
| 56 | +a flavor chemist would catch it instantly (worse than nothing). `predict_aroma()` |
| 57 | +returns an honest "not available" marker, and **we lead with the taste engine**. |
| 58 | + |
| 59 | +The **OpenPOM scaffold (`training/train_odor.py`) is kept** as the aroma engine for |
| 60 | +when clean fuel exists: |
| 61 | +1. **License PMP 2001** (~$2,775, Leffingwell & Associates) → re-curate → train. |
| 62 | +2. **A customer's own odor data** (the paid pilot) → train on-prem. |
| 63 | +3. A future open *expert-labeled* dataset, if one emerges. |
| 64 | + |
| 65 | +Until then: **aroma comes with your data** — which is on-thesis (public data proves |
| 66 | +the method; the customer's data unlocks the rest). |
0 commit comments