josefchen commited on
Commit
4e48ffc
·
verified ·
1 Parent(s): 2c35165

Add basket pairings, multi-direction SLERP, and Mikolov arithmetic tabs

Browse files
__pycache__/app.cpython-310.pyc ADDED
Binary file (11.7 kB). View file
 
__pycache__/epicure.cpython-310.pyc ADDED
Binary file (9.08 kB). View file
 
app.py CHANGED
@@ -1,29 +1,29 @@
1
- """Epicure Explorer: a chef-facing interactive demo of the three sibling embeddings.
2
 
3
- Three tabs:
4
- - Pairings: top-K cosine neighbours + closest emergent mode for a chosen ingredient.
5
- - Supervised SLERP: rotate a seed toward a supervised pole (cuisine, food group,
6
- NOVA, sensory, USDA macros) by a chosen angle.
7
- - Emergent SLERP: rotate a seed toward an emergent factor-mode pole.
8
 
9
- Loads all three siblings on startup from their HF model repos.
10
  """
11
 
12
  from __future__ import annotations
13
 
14
  import os
15
  import sys
 
16
  import gradio as gr
17
 
18
- # epicure.py is loaded from the cooc repo's snapshot at runtime; alternatively
19
- # copy it into this Space's root for offline development.
20
  try:
21
- from epicure import Epicure # noqa: F401
22
  except ImportError:
23
  from huggingface_hub import hf_hub_download
24
  epicure_py = hf_hub_download("Kaikaku/epicure-cooc", "epicure.py")
25
  sys.path.insert(0, os.path.dirname(epicure_py))
26
- from epicure import Epicure # noqa: F401
27
 
28
  MODELS = {
29
  "cooc": Epicure.from_pretrained("Kaikaku/epicure-cooc"),
@@ -34,164 +34,265 @@ MODELS = {
34
  ALL_INGREDIENTS = sorted(MODELS["cooc"].vocab.keys())
35
 
36
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  def _supervised_choices(sibling: str) -> list[str]:
38
  return sorted(MODELS[sibling].supervised_poles.keys())
39
 
40
 
41
- def _factor_modes(sibling: str) -> list[tuple[str, str]]:
42
  return [
43
- (f"{m.mode_id} - {m.label}", m.mode_id)
44
  for m in MODELS[sibling].modes
45
  if m.kind == "factor"
46
  ]
47
 
48
 
49
- def pairings(sibling: str, ingredient: str, k: int):
50
- if not ingredient or ingredient not in MODELS[sibling].vocab:
51
- return [], []
52
  m = MODELS[sibling]
53
- nb = m.neighbors(ingredient, k=k)
54
- cm = m.closest_mode(ingredient, kind=None, k=k)
 
 
 
 
 
 
 
 
55
  return (
56
  [[name, f"{sim:.4f}"] for name, sim in nb],
57
- [[mid, label, f"{sim:.4f}"] for mid, label, sim in cm],
58
  )
59
 
60
 
61
- def supervised_slerp(sibling: str, seed: str, direction: str, theta: float, k: int):
62
- if not seed or seed not in MODELS[sibling].vocab:
63
- return []
64
- if direction not in MODELS[sibling].supervised_poles:
 
 
65
  return []
66
- r = MODELS[sibling].slerp(seed, direction, theta_deg=theta, k=k)
67
- return [[name, f"{sim:.4f}"] for name, sim in r]
 
 
 
 
 
 
 
 
68
 
69
 
70
- def emergent_slerp(sibling: str, seed: str, factor_mode_id: str, theta: float, k: int):
71
- if not seed or seed not in MODELS[sibling].vocab:
 
 
 
 
 
 
 
72
  return []
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  m = MODELS[sibling]
74
- pole = None
75
- for mode in m.modes:
76
- if mode.mode_id == factor_mode_id:
77
- pole = mode.pole
78
- break
79
- if pole is None:
80
  return []
81
- r = m.slerp(seed, pole, theta_deg=theta, k=k)
82
- return [[name, f"{sim:.4f}"] for name, sim in r]
 
 
 
 
 
 
 
 
83
 
84
 
 
85
  with gr.Blocks(title="Epicure Explorer") as demo:
86
  gr.Markdown(
87
  """# Epicure Explorer
88
 
89
- Interactive chef-facing operators over the three Epicure sibling embeddings (Cooc, Core, Chem),
90
- from the paper [Epicure: Navigating the Emergent Geometry of Food Ingredient Embeddings](https://arxiv.org/abs/2605.22391).
91
 
92
- Each sibling sits at a different point on the chemistry-vs-recipe-context spectrum:
93
  - **Cooc** walks recipe co-occurrence only. Neighbours are recipe companions.
94
- - **Core** blends typed FlavorDB compound walks with injected ingredient-ingredient walks. Concentrated geometry, tightest modes.
95
  - **Chem** walks typed FlavorDB compound metapaths only. Strongest supervised-direction recovery; neighbours are flavour-profile peers.
96
  """
97
  )
98
 
99
- sibling = gr.Radio(
100
- choices=["cooc", "core", "chem"],
101
- value="chem",
102
- label="Sibling embedding",
103
- )
104
 
105
- with gr.Tab("Pairings"):
106
- ingredient = gr.Dropdown(
107
- choices=ALL_INGREDIENTS, value="chicken", label="Ingredient", allow_custom_value=False
 
 
 
108
  )
109
- k_pair = gr.Slider(1, 10, value=5, step=1, label="K")
 
 
 
 
 
 
 
110
  pair_btn = gr.Button("Find pairings", variant="primary")
111
  with gr.Row():
112
  nb_table = gr.Dataframe(
113
- headers=["Neighbour", "Cosine"], label="Top-K nearest neighbours", interactive=False
114
  )
115
  mode_table = gr.Dataframe(
116
- headers=["Mode id", "Label", "Cosine"], label="Closest modes", interactive=False
 
117
  )
118
- pair_btn.click(
119
- pairings, inputs=[sibling, ingredient, k_pair], outputs=[nb_table, mode_table]
120
- )
121
 
 
122
  with gr.Tab("Supervised SLERP"):
123
- sup_seed = gr.Dropdown(
124
- choices=ALL_INGREDIENTS, value="rice", label="Seed ingredient"
 
 
125
  )
126
- sup_dir = gr.Dropdown(
 
 
 
 
127
  choices=_supervised_choices("chem"),
128
- value="cuisine:South_Asian",
129
- label="Supervised direction",
 
130
  )
131
  sup_theta = gr.Slider(0, 90, value=30, step=5, label="Rotation angle (deg)")
132
- sup_k = gr.Slider(1, 10, value=5, step=1, label="K")
133
  sup_btn = gr.Button("Rotate", variant="primary")
134
- sup_table = gr.Dataframe(
135
- headers=["Ingredient", "Cosine"], label="Top-K rotated-query neighbours"
136
- )
137
  sup_btn.click(
138
- supervised_slerp,
139
- inputs=[sibling, sup_seed, sup_dir, sup_theta, sup_k],
140
  outputs=sup_table,
141
  )
142
  sibling.change(
143
- lambda s: gr.Dropdown(choices=_supervised_choices(s), value=None),
144
- inputs=sibling,
145
- outputs=sup_dir,
146
  )
147
 
 
148
  with gr.Tab("Emergent SLERP"):
149
- em_seed = gr.Dropdown(
150
- choices=ALL_INGREDIENTS, value="chocolate", label="Seed ingredient"
 
151
  )
152
- factor_options = _factor_modes("chem")
153
- em_mode = gr.Dropdown(
154
- choices=[label for label, _ in factor_options],
155
- value=factor_options[0][0] if factor_options else None,
156
- label="Emergent factor mode (label - mode_id)",
 
 
 
 
 
157
  )
158
  em_theta = gr.Slider(0, 90, value=30, step=5, label="Rotation angle (deg)")
159
- em_k = gr.Slider(1, 10, value=5, step=1, label="K")
160
  em_btn = gr.Button("Rotate", variant="primary")
161
- em_table = gr.Dataframe(
162
- headers=["Ingredient", "Cosine"], label="Top-K rotated-query neighbours"
163
- )
164
-
165
- def _resolve_factor(sib, label, seed, theta, k):
166
- options = _factor_modes(sib)
167
- mode_id = None
168
- for lab, mid in options:
169
- if lab == label:
170
- mode_id = mid
171
- break
172
- if mode_id is None and options:
173
- mode_id = options[0][1]
174
- if mode_id is None:
175
- return []
176
- return emergent_slerp(sib, seed, mode_id, theta, k)
177
-
178
  em_btn.click(
179
- _resolve_factor,
180
- inputs=[sibling, em_mode, em_seed, em_theta, em_k],
181
  outputs=em_table,
182
  )
183
  sibling.change(
184
- lambda s: gr.Dropdown(choices=[label for label, _ in _factor_modes(s)], value=None),
185
- inputs=sibling,
186
- outputs=em_mode,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  )
 
 
 
 
188
 
189
  gr.Markdown(
190
  """---
191
- **Cite:** Radzikowski and Chen 2026, *Epicure: Navigating the Emergent Geometry of Food Ingredient Embeddings*, arXiv:2605.22391.
192
 
193
- Models: [epicure-cooc](https://huggingface.co/Kaikaku/epicure-cooc), [epicure-core](https://huggingface.co/Kaikaku/epicure-core), [epicure-chem](https://huggingface.co/Kaikaku/epicure-chem).
194
- Dataset: [epicure-corpus-resources](https://huggingface.co/datasets/Kaikaku/epicure-corpus-resources).
195
  """
196
  )
197
 
 
1
+ """Epicure Explorer: chef-facing operators over the three sibling embeddings.
2
 
3
+ Four tabs:
4
+ - Basket pairings: pick 1+ ingredients, get neighbours and closest modes of the basket centroid.
5
+ - Supervised SLERP: rotate a (possibly multi-ingredient) seed toward 1+ supervised poles.
6
+ - Emergent SLERP: rotate a (possibly multi-ingredient) seed toward 1+ emergent factor modes.
7
+ - Arithmetic: Mikolov-style 'positives - negatives' returning nearest neighbours.
8
 
9
+ All three siblings (Cooc, Core, Chem) load on startup from public HF model repos.
10
  """
11
 
12
  from __future__ import annotations
13
 
14
  import os
15
  import sys
16
+ import numpy as np
17
  import gradio as gr
18
 
19
+ # epicure.py is shipped alongside this app.py in the Space; fall back to HF if absent.
 
20
  try:
21
+ from epicure import Epicure
22
  except ImportError:
23
  from huggingface_hub import hf_hub_download
24
  epicure_py = hf_hub_download("Kaikaku/epicure-cooc", "epicure.py")
25
  sys.path.insert(0, os.path.dirname(epicure_py))
26
+ from epicure import Epicure
27
 
28
  MODELS = {
29
  "cooc": Epicure.from_pretrained("Kaikaku/epicure-cooc"),
 
34
  ALL_INGREDIENTS = sorted(MODELS["cooc"].vocab.keys())
35
 
36
 
37
+ def _unit(v: np.ndarray, eps: float = 1e-9) -> np.ndarray:
38
+ n = np.linalg.norm(v)
39
+ return v / max(n, eps)
40
+
41
+
42
+ def _basket_centroid(m: Epicure, names: list[str]) -> np.ndarray:
43
+ """L2-normalised mean of the unit vectors of the named ingredients."""
44
+ valid = [n for n in (names or []) if n in m.vocab]
45
+ if not valid:
46
+ return None
47
+ idxs = [m.vocab[n] for n in valid]
48
+ centroid = m.E[idxs].mean(axis=0)
49
+ return _unit(centroid)
50
+
51
+
52
+ def _stack_directions(m: Epicure, keys: list[str], use_factor_pole: bool = False) -> np.ndarray:
53
+ """L2-normalised sum of the named supervised pole vectors (or factor mode poles)."""
54
+ poles = []
55
+ for k in keys or []:
56
+ if use_factor_pole:
57
+ for mode in m.modes:
58
+ if mode.mode_id == k:
59
+ poles.append(_unit(mode.pole))
60
+ break
61
+ else:
62
+ if k in m.supervised_poles:
63
+ poles.append(_unit(m.supervised_poles[k]))
64
+ if not poles:
65
+ return None
66
+ return _unit(np.stack(poles, axis=0).sum(axis=0))
67
+
68
+
69
+ def _topk_from_query(m: Epicure, q: np.ndarray, k: int, exclude: list[str]) -> list[tuple[str, float]]:
70
+ sims = m.E @ q
71
+ for name in exclude or []:
72
+ if name in m.vocab:
73
+ sims[m.vocab[name]] = -np.inf
74
+ order = np.argsort(-sims)
75
+ return [(m.itos[int(i)], float(sims[i])) for i in order[:k]]
76
+
77
+
78
  def _supervised_choices(sibling: str) -> list[str]:
79
  return sorted(MODELS[sibling].supervised_poles.keys())
80
 
81
 
82
+ def _factor_mode_choices(sibling: str) -> list[tuple[str, str]]:
83
  return [
84
+ (f"{m.label} ({m.mode_id})", m.mode_id)
85
  for m in MODELS[sibling].modes
86
  if m.kind == "factor"
87
  ]
88
 
89
 
90
+ # ===== Tab 1: Basket pairings =====
91
+ def basket_pairings(sibling: str, basket: list[str], k: int):
 
92
  m = MODELS[sibling]
93
+ centroid = _basket_centroid(m, basket)
94
+ if centroid is None:
95
+ return [], []
96
+ nb = _topk_from_query(m, centroid, k=k, exclude=basket or [])
97
+ # Closest modes to the basket centroid
98
+ scored = [
99
+ (mode.mode_id, mode.label, mode.kind, float(_unit(mode.pole) @ centroid))
100
+ for mode in m.modes
101
+ ]
102
+ scored.sort(key=lambda x: -x[3])
103
  return (
104
  [[name, f"{sim:.4f}"] for name, sim in nb],
105
+ [[mid, label, kind, f"{sim:.4f}"] for mid, label, kind, sim in scored[:k]],
106
  )
107
 
108
 
109
+ # ===== Tab 2: Supervised SLERP (multi-direction, multi-seed) =====
110
+ def supervised_slerp_multi(sibling: str, basket: list[str], directions: list[str], theta: float, k: int):
111
+ m = MODELS[sibling]
112
+ v = _basket_centroid(m, basket)
113
+ d = _stack_directions(m, directions, use_factor_pole=False)
114
+ if v is None or d is None:
115
  return []
116
+ # SLERP from v toward d
117
+ d_perp = d - (d @ v) * v
118
+ n_perp = np.linalg.norm(d_perp)
119
+ if n_perp < 1e-9:
120
+ return _topk_from_query(m, v, k=k, exclude=basket or [])
121
+ d_perp = d_perp / n_perp
122
+ theta_rad = np.deg2rad(float(theta))
123
+ q = _unit(np.cos(theta_rad) * v + np.sin(theta_rad) * d_perp)
124
+ hits = _topk_from_query(m, q, k=k, exclude=basket or [])
125
+ return [[name, f"{sim:.4f}"] for name, sim in hits]
126
 
127
 
128
+ # ===== Tab 3: Emergent SLERP (multi-direction, multi-seed) =====
129
+ def emergent_slerp_multi(sibling: str, basket: list[str], mode_labels: list[str], theta: float, k: int):
130
+ m = MODELS[sibling]
131
+ # Resolve label strings back to mode_ids
132
+ label_to_id = {f"{mode.label} ({mode.mode_id})": mode.mode_id for mode in m.modes if mode.kind == "factor"}
133
+ mode_ids = [label_to_id[lab] for lab in (mode_labels or []) if lab in label_to_id]
134
+ v = _basket_centroid(m, basket)
135
+ d = _stack_directions(m, mode_ids, use_factor_pole=True)
136
+ if v is None or d is None:
137
  return []
138
+ d_perp = d - (d @ v) * v
139
+ n_perp = np.linalg.norm(d_perp)
140
+ if n_perp < 1e-9:
141
+ return [[n, f"{s:.4f}"] for n, s in _topk_from_query(m, v, k=k, exclude=basket or [])]
142
+ d_perp = d_perp / n_perp
143
+ theta_rad = np.deg2rad(float(theta))
144
+ q = _unit(np.cos(theta_rad) * v + np.sin(theta_rad) * d_perp)
145
+ hits = _topk_from_query(m, q, k=k, exclude=basket or [])
146
+ return [[name, f"{sim:.4f}"] for name, sim in hits]
147
+
148
+
149
+ # ===== Tab 4: Mikolov arithmetic =====
150
+ def arithmetic(sibling: str, positives: list[str], negatives: list[str], k: int):
151
  m = MODELS[sibling]
152
+ pos = _basket_centroid(m, positives)
153
+ if pos is None:
 
 
 
 
154
  return []
155
+ neg = _basket_centroid(m, negatives) if negatives else None
156
+ if neg is None:
157
+ q = pos
158
+ else:
159
+ # pos - neg, then renormalise. This is the king - man + woman pattern reshaped:
160
+ # the user supplies the 'positives' and 'negatives' sets directly.
161
+ q = _unit(pos - neg)
162
+ exclude = (positives or []) + (negatives or [])
163
+ hits = _topk_from_query(m, q, k=k, exclude=exclude)
164
+ return [[name, f"{sim:.4f}"] for name, sim in hits]
165
 
166
 
167
+ # ===== UI =====
168
  with gr.Blocks(title="Epicure Explorer") as demo:
169
  gr.Markdown(
170
  """# Epicure Explorer
171
 
172
+ Chef-facing operators over the three Epicure sibling embeddings (Cooc, Core, Chem),
173
+ from [arXiv:2605.22391](https://arxiv.org/abs/2605.22391).
174
 
 
175
  - **Cooc** walks recipe co-occurrence only. Neighbours are recipe companions.
176
+ - **Core** blends typed FlavorDB compound walks with injected I-I walks. Concentrated geometry, tightest modes.
177
  - **Chem** walks typed FlavorDB compound metapaths only. Strongest supervised-direction recovery; neighbours are flavour-profile peers.
178
  """
179
  )
180
 
181
+ sibling = gr.Radio(choices=["cooc", "core", "chem"], value="chem", label="Sibling embedding")
 
 
 
 
182
 
183
+ # -------- Tab 1: Basket pairings --------
184
+ with gr.Tab("Basket pairings"):
185
+ gr.Markdown(
186
+ "Pick one or more ingredients. The tool averages their unit vectors and returns "
187
+ "what is nearest to that centroid in the embedding. Useful for 'what should I add "
188
+ "to the ingredients I already have?'"
189
  )
190
+ basket = gr.Dropdown(
191
+ choices=ALL_INGREDIENTS,
192
+ value=["chicken", "lemon", "garlic"],
193
+ label="Ingredient basket (pick 1+)",
194
+ multiselect=True,
195
+ max_choices=10,
196
+ )
197
+ k_pair = gr.Slider(1, 15, value=8, step=1, label="K")
198
  pair_btn = gr.Button("Find pairings", variant="primary")
199
  with gr.Row():
200
  nb_table = gr.Dataframe(
201
+ headers=["Neighbour", "Cosine"], label="Top-K nearest neighbours to basket centroid", interactive=False
202
  )
203
  mode_table = gr.Dataframe(
204
+ headers=["Mode id", "Label", "Kind", "Cosine"],
205
+ label="Closest modes (factor + supervised)", interactive=False
206
  )
207
+ pair_btn.click(basket_pairings, inputs=[sibling, basket, k_pair], outputs=[nb_table, mode_table])
 
 
208
 
209
+ # -------- Tab 2: Supervised SLERP (multi) --------
210
  with gr.Tab("Supervised SLERP"):
211
+ gr.Markdown(
212
+ "Rotate the (possibly multi-ingredient) seed toward one or more supervised direction poles. "
213
+ "Multiple directions are summed and L2-normalised before rotation, matching the paper's "
214
+ "'chicken + processed + Western_Atlantic' style multi-constraint queries."
215
  )
216
+ sup_basket = gr.Dropdown(
217
+ choices=ALL_INGREDIENTS, value=["rice"], label="Seed basket (pick 1+)",
218
+ multiselect=True, max_choices=10,
219
+ )
220
+ sup_dirs = gr.Dropdown(
221
  choices=_supervised_choices("chem"),
222
+ value=["cuisine:South_Asian"],
223
+ label="Supervised directions (pick 1+; summed before rotation)",
224
+ multiselect=True, max_choices=5,
225
  )
226
  sup_theta = gr.Slider(0, 90, value=30, step=5, label="Rotation angle (deg)")
227
+ sup_k = gr.Slider(1, 15, value=8, step=1, label="K")
228
  sup_btn = gr.Button("Rotate", variant="primary")
229
+ sup_table = gr.Dataframe(headers=["Ingredient", "Cosine"], label="Top-K rotated-query neighbours")
 
 
230
  sup_btn.click(
231
+ supervised_slerp_multi,
232
+ inputs=[sibling, sup_basket, sup_dirs, sup_theta, sup_k],
233
  outputs=sup_table,
234
  )
235
  sibling.change(
236
+ lambda s: gr.Dropdown(choices=_supervised_choices(s), value=[]),
237
+ inputs=sibling, outputs=sup_dirs,
 
238
  )
239
 
240
+ # -------- Tab 3: Emergent SLERP (multi) --------
241
  with gr.Tab("Emergent SLERP"):
242
+ gr.Markdown(
243
+ "Rotate the seed basket toward one or more emergent factor-mode poles discovered "
244
+ "by multi-seed-stable FastICA + GMM. Stack mode targets to combine culinary axes."
245
  )
246
+ em_basket = gr.Dropdown(
247
+ choices=ALL_INGREDIENTS, value=["chocolate"], label="Seed basket (pick 1+)",
248
+ multiselect=True, max_choices=10,
249
+ )
250
+ factor_opts = _factor_mode_choices("chem")
251
+ em_modes = gr.Dropdown(
252
+ choices=[label for label, _ in factor_opts],
253
+ value=[factor_opts[0][0]] if factor_opts else [],
254
+ label="Factor modes (pick 1+; summed before rotation)",
255
+ multiselect=True, max_choices=5,
256
  )
257
  em_theta = gr.Slider(0, 90, value=30, step=5, label="Rotation angle (deg)")
258
+ em_k = gr.Slider(1, 15, value=8, step=1, label="K")
259
  em_btn = gr.Button("Rotate", variant="primary")
260
+ em_table = gr.Dataframe(headers=["Ingredient", "Cosine"], label="Top-K rotated-query neighbours")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
261
  em_btn.click(
262
+ emergent_slerp_multi,
263
+ inputs=[sibling, em_basket, em_modes, em_theta, em_k],
264
  outputs=em_table,
265
  )
266
  sibling.change(
267
+ lambda s: gr.Dropdown(choices=[label for label, _ in _factor_mode_choices(s)], value=[]),
268
+ inputs=sibling, outputs=em_modes,
269
+ )
270
+
271
+ # -------- Tab 4: Mikolov arithmetic --------
272
+ with gr.Tab("Arithmetic"):
273
+ gr.Markdown(
274
+ "Classic Mikolov-style vector arithmetic: `centroid(positives) - centroid(negatives)`, "
275
+ "then top-K nearest neighbours. Try `miso - salty` (no negative-set), or `chicken - "
276
+ "Western + Asian` style queries (split your own intuition into positives and negatives)."
277
+ )
278
+ pos_box = gr.Dropdown(
279
+ choices=ALL_INGREDIENTS, value=["miso"],
280
+ label="Positives (added)", multiselect=True, max_choices=10,
281
+ )
282
+ neg_box = gr.Dropdown(
283
+ choices=ALL_INGREDIENTS, value=[],
284
+ label="Negatives (subtracted)", multiselect=True, max_choices=10,
285
  )
286
+ ar_k = gr.Slider(1, 15, value=8, step=1, label="K")
287
+ ar_btn = gr.Button("Compute", variant="primary")
288
+ ar_table = gr.Dataframe(headers=["Ingredient", "Cosine"], label="Top-K nearest to result vector")
289
+ ar_btn.click(arithmetic, inputs=[sibling, pos_box, neg_box, ar_k], outputs=ar_table)
290
 
291
  gr.Markdown(
292
  """---
293
+ **Cite:** Radzikowski and Chen, 2026, *Epicure: Navigating the Emergent Geometry of Food Ingredient Embeddings*, [arXiv:2605.22391](https://arxiv.org/abs/2605.22391).
294
 
295
+ Models: [epicure-cooc](https://huggingface.co/Kaikaku/epicure-cooc) | [epicure-core](https://huggingface.co/Kaikaku/epicure-core) | [epicure-chem](https://huggingface.co/Kaikaku/epicure-chem). Dataset: [epicure-corpus-resources](https://huggingface.co/datasets/Kaikaku/epicure-corpus-resources).
 
296
  """
297
  )
298