Snack category classifier β€” AutoML over classical ML

Predicts which of five snack categories a packaged product belongs to, using only the eight numbers printed on its Nutrition Facts panel.

Built for24-679 Designing & Prototyping AI Systems. Selected by an Optuna AutoML search over six scikit-learn model families.

Purpose and intended use

Intended. Demonstrating AutoML on a small tabular dataset; a reference implementation of leakage-aware evaluation when a dataset is mostly synthetic.

Not intended. Nutrition advice, dietary guidance, food-safety decisions, regulatory labelling, or any consumer-facing product. The model is trained on 30 real snacks. It has no basis for generalising to the packaged-food market.

Data

  • Source: shanexf/packaged-snack-nutrition-data, collected by a classmate for HW1. Used as published; I am not the dataset author.
  • Composition: 429 rows = 30 real snacks + 399 synthetic rows generated from them by additive jitter, multiplicative scaling, within-class mixup, and neighbour interpolation.
  • Classes: candy, chips, cookies, crackers, granola_bars β€” 6 real snacks each.

Splits

I built my own split with two rules:

  1. Split the 30 real snacks, a jittered copy of a training snack lands in test and every model looks perfect.
  2. Mixup rows blend two parents (parent_id, second_parent_id). A row joins a split only if both parents are on that side; 189 rows straddle the boundary and were dropped.
Split Parents Rows
Training pool 20 (4/class) 246
Test β€” all 10 (2/class) 90
Test β€” real snacks only 10 10
Dropped (straddling) β€” 93

Features and preprocessing

Input is 8 floats in this exact order:

['serving_size_g', 'servings_per_container', 'calories', 'total_fat_g', 'sodium_mg', 'carbs_g', 'sugar_g', 'protein_g']

Preprocessing is StandardScaler, bundled inside the saved Pipeline. Provenance columns (source_id, parent_id, augmentation, …) are deliberately excluded; they encode the augmentation structure and would leak the label.

Training and model selection

Engine optuna==5.0.0, TPE sampler, seed 24679
Budget 150 trials / 600s wall-clock cap β€” 150 trials ran
Search space model family Γ— its hyperparameters, jointly (logreg, RF, extra-trees, HistGB, SVM, kNN)
Validation 4-fold CV, folds grouped on parent snack, both-parents rule applied per fold
Objective macro-F1, scored on real rows only
Selected svm β€” {'svc_C': 8.55142672640755, 'svc_kernel': 'linear', 'svc_gamma': 'auto'}

Why the objective is scored on real rows only

A validation fold's synthetic rows are jittered copies of that same fold's real snacks, so scoring on them rewards a model for memorising augmentation artefacts. Running the identical search both ways:

CV scoring CV macro-F1 Test (all) macro-F1 Test (real) macro-F1
All rows 0.8788 0.7333 0.7333
Real rows only 0.8167 0.7531 0.8933

Scoring on all rows gives a higher CV score that does not survive the test set. The real-rows-only search was selected.

Results

Split n macro-F1 accuracy
CV (model selection) β€” 0.8167 β€”
Test β€” all rows 90 0.7531 0.8556
Test β€” real snacks 10 0.8933 0.9

Bootstrap 95% CI on the real-snack macro-F1 (5000 resamples): 0.600 – 1.000. The interval is wide because n = 10. Quote the interval, not the point estimate.

Per-class breakdown (test, all rows):

              precision    recall  f1-score   support

       candy      1.000     0.083     0.154        12
       chips      0.929     1.000     0.963        26
     cookies      0.522     1.000     0.686        12
    crackers      1.000     0.929     0.963        28
granola_bars      1.000     1.000     1.000        12

    accuracy                          0.856        90
   macro avg      0.890     0.802     0.753        90
weighted avg      0.916     0.856     0.823        90

Limitations

  • 30 real snacks. Everything else is a deterministic transform of those 30. The effective sample size is 30.
  • Sampling bias. All 30 products were photographed by one student in one place. Brands, portion conventions, and category norms differ by country and by retailer; this model encodes one shelf.

Ethical notes

Nutrition data invites misuse as dietary guidance. This model classifies a marketing category and says nothing about whether a food is healthy, suitable for any diet, or safe for any allergy.

Reproducing

Hardware / compute budget: Google Colab CPU runtime (no accelerator). The full search completes in well under the 600s cap; total notebook runtime is a few minutes.

Usage

import joblib, numpy as np
from huggingface_hub import hf_hub_download

pipe = joblib.load(hf_hub_download("akshara-ns/snack-category-automl", "model.joblib"))

# serving_size_g, servings_per_container, calories, total_fat_g,
# sodium_mg, carbs_g, sugar_g, protein_g
x = np.array([[28, 6, 150, 9, 160, 16, 0.5, 1]])
print(pipe.predict(x))   # -> e.g. ['chips']

Citation and credit

Dataset by a 24-679 classmate: shanexf/packaged-snack-nutrition-data. Model by Akshara N.S..

AI-usage disclosure. Generative AI assisted in drafting the notebook code and this card; I reviewed, ran, corrected, and edited all of it. Every number above is produced by executing the notebook. The task definition, split design, search space, and interpretation are mine.

License: Apache-2.0. The dataset remains under the license stated on its own Hub page.

Downloads last month
-
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support

Dataset used to train akshara-ns/snack-category-automl

Evaluation results

  • macro-F1 (held-out real snacks) on packaged-snack-nutrition-data
    self-reported
    0.893
  • accuracy (held-out real snacks) on packaged-snack-nutrition-data
    self-reported
    0.900