Instructions to use akshara-ns/snack-category-automl with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Scikit-learn
How to use akshara-ns/snack-category-automl with Scikit-learn:
from huggingface_hub import hf_hub_download import joblib model = joblib.load( hf_hub_download("akshara-ns/snack-category-automl", "sklearn_model.joblib") ) # only load pickle files from sources you trust # read more about it here https://skops.readthedocs.io/en/stable/persistence.html - Notebooks
- Google Colab
- Kaggle
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:
- Split the 30 real snacks, a jittered copy of a training snack lands in test and every model looks perfect.
- 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
- -
Dataset used to train akshara-ns/snack-category-automl
Evaluation results
- macro-F1 (held-out real snacks) on packaged-snack-nutrition-dataself-reported0.893
- accuracy (held-out real snacks) on packaged-snack-nutrition-dataself-reported0.900