Flavor = taste + aroma. The taste side ships and is clean (sweet/bitter/umami ~0.95 AUROC + intensity + sour/salty). The aroma side is deliberately deferred β not for lack of effort, but because the public, commercially-clean data is insufficient. This documents the evaluation, so the decision is legible.
A model mapping a molecule's structure β odor descriptors (sweet, floral,
woodyβ¦). The state of the art is the Principal Odor Map (Lee et al., Science
2023; Google/Osmo), reimplemented open-source as OpenPOM (BioMachineLearning/ openpom, MIT) β a message-passing GNN trained on ~5,000 expert-labeled molecules
(the GS-LF dataset).
OpenPOM's code is MIT, but its training data is restricted. A full audit of the
Pyrfume catalogue (see DATA-SOURCES.md) found that every rich
odor-descriptor dataset is proprietary or NonCommercial β Leffingwell, GoodScents,
Arctander, Flavornet (Β©Datu), FlavorDB / FooDB (NC), OlfactionBase ("all rights
reserved"), AromaDB (CSIR), Dravnieks (ASTM Β©), sharma_2021 (ACS Β©), snitz_2019
(CC-BY-NC). The only commercially-clean odor-descriptor set is keller_2016
(Keller & Vosshall 2016, BMC Neuroscience, CC-BY-4.0; ~480 molecules, 20 descriptors).
We aggregated keller_2016 (training/build_aroma_dataset.py)
and trained one RandomForest regressor per descriptor on 2048-bit Morgan fingerprints
(training/train_aroma.py), scored by honest 5-fold
cross-validation (400 trees):
| descriptor | CV-RΒ² | descriptor | CV-RΒ² | descriptor | CV-RΒ² | descriptor | CV-RΒ² |
|---|---|---|---|---|---|---|---|
| acid | β0.24 | cold | β0.19 | fruit | β0.05 | sour | β0.05 |
| ammonia | β0.15 | decayed | β0.20 | garlic | β0.12 | spices | β0.12 |
| bakery | β0.22 | edible | β0.19 | grass | β0.45 | sweaty | +0.03 |
| burnt | β0.13 | fish | β0.04 | musky | β0.23 | sweet | β0.05 |
| chemical | β0.10 | flower | β0.12 | warm | β0.28 | wood | β0.08 |
All 20 descriptors scored CV-RΒ² β€ 0 (range β0.45 to +0.03) β every model is worse than predicting the mean. 0/20 usable heads.
keller_2016 is naive-subject data: random volunteers rating unfamiliar
molecules on a 0β100 scale. People can't reliably name what a molecule smells like,
so the labels are noise (the per-descriptor means compress to ~22β30 for nearly every
molecule). The learnable odor data uses expert labels β which is exactly the
data that's restricted. That is the structural reason the entire field trains on
GS-LF, and why a clean public aroma model isn't currently possible.
We do not ship a negative-RΒ² model β it would output confident, wrong smells, and
a flavor chemist would catch it instantly (worse than nothing). predict_aroma()
returns an honest "not available" marker, and we lead with the taste engine.
The OpenPOM scaffold (training/train_odor.py) is kept as the aroma engine for
when clean fuel exists:
- License PMP 2001 (~$2,775, Leffingwell & Associates) β re-curate β train.
- A customer's own odor data (the paid pilot) β train on-prem.
- A future open expert-labeled dataset, if one emerges.
Until then: aroma comes with your data β which is on-thesis (public data proves the method; the customer's data unlocks the rest).