Exploring Epicure the Food Embedding Model

FlavorGraph is a large-scale graph network that combines data from over a million recipes with chemical compound information from 1,500+ flavor molecules to predict ingredient pairings. It uses graph embedding methods to represent foods as dense vectors, enabling data-driven food pairing suggestions that go beyond human or chef intuition. In FlavorGraph, the chemical and recipe context signals are fused at training time via a fixed metapath design, leaving no inference-time knob to adjust their relative weights in the final embeddings.

One could call Epicure an enhanced FlavorGraph. It builds on FlavorGraph to produce 300-D embeddings, but instead of a single embedding that combines both chemical and recipe context signals. It has three embedding models Cooc, Chem, and Core. That way, as a user, you can choose the embedding you want. It also includes more recipes from other languages, not just English. Recipes from English, Chinese, Russian, Vietnamese, Spanish, Turkish, Indonesian, German, and Indian-English are included. They are machine-translated into English for this. It’s good to see Indian recipes covered as well.

They have also normalized the raw ingredient strings to 1,790 canonical entries using an LLM. I found this interesting. I also found that this doesn’t necessarily cover everything that an Indian recipe needs. For example, there are only coriander and coriander_root. It’s usually coriander seeds or coriander leaves in India. In their list, there is no way to differentiate it.

The most interesting part is how they traverse the graph to construct metapaths for training Metapath2Vec models. The three models follow different logic to construct it. But they all have the same architecture and hyperparameters: 300-dim embeddings, walks_per_node=100, walk_length=50, context_size=7, 5 negative samples, batch_size=32,768, lr=0.0025, 20 epochs, no warm restart.

From the paper, the methodology (I have embedded the SVG flowchart below) is easy to understand and, if required, can be replicated. But the raw data is not available, though sources are mentioned. Maybe it can be replicated with a different dataset. They have also used LLMs quite a bit in the data and training pipeline. For me, constructing the metapaths was the most interesting part.

  1. Epicure-Cooc. Walks the Cooc graph: pure I–I random walks weighted by NPMI. No compound nodes.
  2. Epicure-Core. Walks the typed-compound graph and injects pure I–I walks at --ii_repeat=10 alongside the typed-compound metapaths. Edge transitions are weighted so I–C hops are not oversampled relative to the smaller I–I edge set. The resulting embedding blends chemical and recipe-context signal.
  3. Epicure-Chem. Walks the typed-compound graph but with –ii_repeat=0: the I–I templates are absent and the only walks the skip-gram sees are compound-mediated. The chemistry extreme of the family.
Epicure Methodology Flowchart

You can explore the embedding online or using a simple script below. In my exploration, I found that it is okay to use the nearest neighbors for an ingredient in a recipe, in chemistry, or in mixed contexts. But I don’t think it’s at a level where we could just replace ingredients. It also doesn’t have any information about allergens, texture, etc. But it is small and can be used to build on top of it.

Online explorere Epicure three sibling ingredient embeddings.
Online explorer Epicure three sibling ingredient embeddings.
# /// script
# requires-python = ">=3.11"
# dependencies = [
#   "numpy",
#   "huggingface_hub",
# ]
# ///

from epicure import Epicure

m = Epicure.from_pretrained("Kaikaku/epicure-core")

print("neighbors : chicken")
print(m.neighbors("chicken", k=5))

print("neighbors : coriander")
print(m.neighbors("coriander", k=5))

print("slerp : rice, cuisine:South_Asian")
print(m.slerp("rice", "cuisine:South_Asian", theta_deg=30, k=5))

print("closest_mode : chocolate")
print(m.closest_mode("chocolate", kind="factor", k=3))

To run the above script, include the epicure.py file from the HF repo in the same folder. The epicure package on PyPI is a different package.

# Output of above script
neighbors : chicken
[('pork', 0.5807677507400513), ('beef', 0.5712239742279053), ('chicken_broth', 0.5498523116111755), ('peanut', 0.5233361124992371), ('cream_of_chicken_soup', 0.5217206478118896)]
neighbors : coriander
[('cumin', 0.7044016122817993), ('scallion', 0.6711025238037109), ('chili_pepper', 0.6609357595443726), ('turmeric', 0.6468461155891418), ('chicken_broth', 0.6463753581047058)]
slerp : rice, cuisine:South_Asian
[('turmeric', 0.7607102225586673), ('mustard_seed', 0.756659547118539), ('fenugreek_seed', 0.7468295496269882), ('coriander', 0.7428115853809022), ('cumin', 0.7388300342030064)]
closest_mode : chocolate
[('F_15/M0', 'American sweet confections and dessert bases', 0.7751955986022949), ('F_7/M1', 'Sweet liqueurs and confections', 0.7553147673606873), ('F_8/M4', 'Sweet confections and dessert ingredients', 0.7396374344825745)]

Definitions

Word2Vec is a neural method for building word embeddings from raw text. Words appearing in similar contexts end up close together in the vector space. It comes in two variants skip-gram and CBOW.

  • skip-gram: predict the surrounding context words from a target word
  • CBOW: predict the target word from its surrounding context

Metapath2Vec (PDF) It does random walks through the graph guided by a defined metapath, then feeds those walks to a skip-gram model to learn node embeddings. It works well for Heterogeneous graphs. Basically, it’s like creating sentences based on the metapath, then running Word2Vec skip-gram on them.

  • A metapath is a typed node sequence, such as author – paper – venue – paper – author. It’s predefined by manually by the model designer.

You can read this blog using RSS Feed. But if you are the person who loves getting emails, then you can join my readers by signing up.

Join 2,279 other subscribers

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.