snack-category-automl-tabular
Predicts a packaged snack's category from its nutrition panel. Trained with AutoGluon TabularPredictor
over a portfolio of boosted trees, tree ensembles, neural networks, linear models, nearest neighbours and
tabular foundation models, bagged on parent_id-grouped, class-stratified folds.
Built for Homework 2 of 24-679 Designing with AI.
1. Purpose
Given the eight numbers on a nutrition label, predict which of five snack categories the product belongs to:
candy, chips, cookies, crackers, granola_bars.
Intended use: coursework and demonstration of an AutoML workflow on a small tabular dataset. Out of scope: any dietary, nutritional, regulatory or purchasing decision.
2. Data origin and splits
Source: shanexf/packaged-snack-nutrition-data, collected by a classmate for HW1.
I am not the dataset author and did not contribute to its collection.
| Split | Rows | Used for |
|---|---|---|
train |
420 | AutoGluon fit (bagged cross-validation) |
validation + test |
9 | Held out; inspected once, after the fit returned |
The training split contains roughly 20 rows per real
snack, because the dataset author augmented each original product with numeric-jitter variants. Every fold
is grouped by parent_id, supplied to AutoGluon through its groups argument, so all variants of one snack
stay on the same side of every split. An ungrouped K-fold on this data returns an inflated, meaningless score.
Folds are also stratified by class. With roughly four real snacks per category, grouping alone produces folds whose training half is missing a category outright, which breaks the fit. The fold count was searched downward from 5 to the largest value where every training half retains all five classes; it came out at 4.
3. Features and target
Features (8, all numeric): serving_size_g, servings_per_container, calories, total_fat_g, sodium_mg, carbs_g, sugar_g, protein_g
Target: category — 5 classes.
Excluded: source_id, parent_id (row identity — leaks the label), and augmentation,
is_augmented, second_parent_id, mix_weight (describe how a row was synthesised, not the snack).
4. Preprocessing
Handled by AutoGluon's own feature pipeline: type inference, imputation and per-model scaling are applied where the model family needs them. No manual feature engineering was performed. The source data has no missing values in the feature columns.
5. Training setup and search space
| Library | AutoGluon TabularPredictor 1.6.3 |
| Objective | eval_metric='f1_macro' |
| Validation | 4-fold LeaveOneGroupOut on a StratifiedGroupKFold fold_id grouped by parent_id |
| Budget | time_limit=900 s, 641.7 s actual, 16 models trained |
| HPO | 8 random configurations per tunable family |
| Stacking | num_stack_levels=0 — ~21 independent snacks cannot support a second level |
| Hardware | Google Colab CPU runtime, no accelerator |
| Seed | 20260922 |
Portfolio searched: LightGBM, CatBoost, XGBoost, random forest, extra trees, PyTorch NN, FastAI NN, linear model, k-NN, TabICL, TabPFNMix, and a DUMMY baseline. Families whose package is unavailable on the runtime are skipped and logged.
Selected model: WeightedEnsemble_L2 (best single model: NeuralNetFastAI_BAG_L1/5a625_00000)
Selected hyperparameters for NeuralNetFastAI_BAG_L1/5a625_00000:
{
"layers": null,
"emb_drop": 0.1,
"ps": 0.1,
"bs": 256,
"lr": 0.01,
"epochs": 30,
"early.stopping.min_delta": 0.0001,
"early.stopping.patience": 20,
"smoothing": 0.0,
"random_seed": 0
}
Ensemble composition:
| Component | Weight |
|---|---|
TabPFNMix_BAG_L1 |
0.5714 |
NeuralNetFastAI_BAG_L1/5a625_00000 |
0.2857 |
LightGBM_BAG_L1/T1 |
0.1429 |
6. Metrics
| Metric | Value |
|---|---|
| Grouped out-of-fold macro-F1 (primary) | 0.6462 ± 0.0591 |
| Holdout accuracy (n=9) | 0.889 (95 % CI ≈ ±0.205) |
| Holdout macro-F1 (n=9) | 0.893 |
| DUMMY baseline, same folds and metric | 0.0000 |
| Lift over baseline | +0.6462 |
The out-of-fold figure is the one to quote, and the ± is the spread across folds — the variation you would see if the dataset's snacks had been a different set of snacks. The holdout has nine rows: its confidence interval is roughly ±30 percentage points, so it can only confirm the model is not broken.
7. Limitations
- The evidence base is ~21 real snacks. Row counts in the hundreds come from augmentation, not from independent observations. The grouped bagging accounts for this; the raw row count does not.
- The holdout is nine rows. Any comparison between this model and another on that holdout is noise.
- Nutrition panels underdetermine category. A granola bar and a cookie can have near-identical panels. There is an irreducible error floor here that no AutoML budget will pass.
- Single-annotator labels. Categories were assigned by one person, the dataset author, with no independent check.
- Narrow domain. Packaged Western supermarket snacks only. Nothing here transfers to fresh food, meals, or non-US labelling conventions.
- Augmentation is numeric jitter, so the model has seen a smooth neighbourhood around each real snack but no genuinely new products.
- Portfolio portability. If the foundation models or boosted-tree packages were unavailable on the
runtime that produced this artifact, they were skipped;
leaderboard.csvin this repo records exactly which models were actually trained.
8. Ethical considerations
The data describes commercial products, not people — no personal data is involved. The realistic harm is misuse as nutritional guidance: a category prediction is not a health claim, and a model trained on ~21 snacks should not inform anyone's diet. The limitations above are stated plainly rather than buried for that reason.
9. License
CC-BY-4.0, matching the source dataset's terms. Attribution to the dataset author for the underlying data.
10. Compute budget
Google Colab CPU runtime, no GPU. 16 models trained in 641.7 seconds under a 900-second limit.
11. AI usage disclosure
Generative AI (Claude, Anthropic) was used as a coding and writing assistant: it drafted the AutoGluon configuration, the fold-construction logic, the plotting code, and the prose of this card. The choices of task, target, feature exclusions, grouping strategy and evaluation design were reviewed and accepted by me, and every cell was executed and checked before upload. I am responsible for the content.
12. How to use
import pandas as pd
from huggingface_hub import snapshot_download
from autogluon.tabular import TabularPredictor
predictor = TabularPredictor.load(snapshot_download("sunkaiwen/snack-category-automl-tabular"), require_version_match=False)
x = pd.DataFrame([{
"serving_size_g": 30.0, "servings_per_container": 8.0, "calories": 150.0,
"total_fat_g": 8.0, "sodium_mg": 180.0, "carbs_g": 17.0,
"sugar_g": 1.0, "protein_g": 2.0,
}])
print(predictor.predict(x))
- Downloads last month
- 55