{"id":50772691,"url":"https://github.com/ryanrudes/cs148a","last_synced_at":"2026-06-11T20:30:44.946Z","repository":{"id":339117558,"uuid":"1151043382","full_name":"ryanrudes/cs148a","owner":"ryanrudes","description":"An overpowered digit classifier built for EE/CNS/CS 148A @ Caltech. Make digits out of anything.","archived":false,"fork":false,"pushed_at":"2026-03-23T02:02:49.000Z","size":68378,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-31T18:33:11.960Z","etag":null,"topics":["digit-classification"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ryanrudes.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-06T01:39:06.000Z","updated_at":"2026-04-15T03:09:58.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ryanrudes/cs148a","commit_stats":null,"previous_names":["ryanrudes/cs148-project-2","ryanrudes/cs148a"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ryanrudes/cs148a","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ryanrudes%2Fcs148a","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ryanrudes%2Fcs148a/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ryanrudes%2Fcs148a/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ryanrudes%2Fcs148a/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ryanrudes","download_url":"https://codeload.github.com/ryanrudes/cs148a/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ryanrudes%2Fcs148a/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34217312,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-11T02:00:06.485Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["digit-classification"],"created_at":"2026-06-11T20:30:44.443Z","updated_at":"2026-06-11T20:30:44.938Z","avatar_url":"https://github.com/ryanrudes.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Digit Classifier\n\nhttps://github.com/user-attachments/assets/e00540d3-96e7-4360-9dc5-eb5e3a0b5e29\n\n\nhttps://github.com/user-attachments/assets/53767d51-1e46-442c-ae86-29178b18e6b4\n\n\nhttps://github.com/user-attachments/assets/7913d9f3-2399-483e-9ce6-53ac047e823b\n\n\nhttps://github.com/user-attachments/assets/b402ce11-aa25-47d0-b2e9-c745f2e88d0c\n\n\n\nA PyTorch training pipeline for digit classification using **ResNeXt** with\nYOLO-style augmentation, external dataset mixing, and comprehensive experiment\ntracking.\n\n## Features\n\n- **ResNeXt-101** with stochastic depth (drop-path) and grouped convolutions\n- **YOLO-style augmentation** pipeline with digit-safe hyper-parameters (label-conditional horizontal flip for symmetric digits 0 and 8)\n- **External dataset mixing** — SVHN, MNIST, EMNIST, USPS, QMNIST and Semeion are lazily loaded and mixed into training via a `RatioBatchSampler` (95 % original / 5 % external per batch)\n- **NIST-like deduplication** — SHA-1 fingerprints remove exact pixel duplicates across external datasets (cached as a tiny JSON so dedup only runs once)\n- **Mixup / CutMix** (element-wise mode) with dynamic loss switching — `SoftTargetCrossEntropy` while active, plain `CrossEntropyLoss` when disabled for the final *N* epochs\n- **EMA model** with `use_buffers=True`\n- **Warm-restart scheduler** — linear warmup followed by `CosineAnnealingWarmRestarts` with pre-restart checkpoints\n- **AMP** with `GradScaler` (CUDA-only) and gradient norm clipping\n- **Weights \u0026 Biases** integration for experiment tracking and artifact storage\n- **Rich** console output — tables, progress bars and structured logging\n- **HuggingFace Hub** integration for pushing/pulling dataset caches\n- **Webcam inference** with real-time probability visualisation\n\n## Quick start (local)\n\n```bash\n# Create a virtual environment\npython -m venv .venv\nsource .venv/bin/activate\n\n# Install the package in development mode\npip install -e \".[dev]\"\n\n# 1. Download the raw dataset from Google Drive\npython -m digit_classifier download\n\n# 2. Preprocess and cache as .npz\npython -m digit_classifier preprocess --name mnist_rgb_224 --color --size 224\n\n# 3. Train (runs external dataset download + dedup on first run)\npython -m digit_classifier train\n\n# 4. Run webcam inference (uses EMA model; pass --mean/--std if checkpoint lacks them)\npython -m digit_classifier infer --checkpoint checkpoints/\u003crun_id\u003e/best.pt \\\n  --mean 0.57 0.52 0.48 --std 0.23 0.23 0.23\n\n# 5. Export compiled pipeline (TorchScript)\n\nThis compiles the model _and_ preprocessing into a single TorchScript file\nthat accepts raw image tensors and can be uploaded to the Hub. The export\nuses the **EMA** model from the checkpoint.\n\n**Normalization (mean/std):** Checkpoints do not currently store mean/std.\nYou must pass `--mean` and `--std` so the exported pipeline matches your\ntraining normalization. Get these from the training log (e.g. `mean=[0.13, 0.13, 0.13]`)\nor from your cached dataset. If omitted, 0.5/0.5 is used (likely incorrect).\n\n```bash\n# RGB (3 channels) — use the mean/std printed during train\npython -m digit_classifier export-pipeline \\\n  --checkpoint checkpoints/\u003crun_id\u003e/best.pt \\\n  --output pipeline-cnn.pt \\\n  --mean 0.13 0.13 0.13 \\\n  --std 0.31 0.31 0.31\n\n# Grayscale (1 channel)\npython -m digit_classifier export-pipeline \\\n  --checkpoint checkpoints/\u003crun_id\u003e/best.pt \\\n  --output pipeline-cnn.pt \\\n  --input-channels 1 \\\n  --mean 0.13 \\\n  --std 0.31\n\n# Upload to HuggingFace Hub\npython -m digit_classifier export-pipeline \\\n  --checkpoint checkpoints/\u003crun_id\u003e/best.pt \\\n  --output pipeline-cnn.pt \\\n  --mean 0.13 0.13 0.13 --std 0.31 0.31 0.31 \\\n  --push-to-hf --hf-repo \u003cusername\u003e/\u003crepo\u003e\n```\n\n## Cloud training\n\nAfter running locally at least once (so caches exist), push them to\nHuggingFace Hub and pull on any cloud VM:\n\n### Push caches (run locally)\n\n```bash\n# Authenticate with HuggingFace (one-time)\nhuggingface-cli login\n\n# Push internal dataset cache + dedup indices to a private HF repo\npython -m digit_classifier push-cache --repo \u003cyour-username\u003e/digit-classification-cache\n```\n\nThis uploads:\n- `datasets/mnist_rgb_224.npz` — preprocessed internal dataset (~1.5 GB)\n- `datasets/dedup_indices_*.json` — dedup index cache (a few KB)\n\n### Pull caches and train (run on cloud VM)\n\n```bash\n# Install\npip install -e \".[dev]\"\n\n# Authenticate with HuggingFace\nhuggingface-cli login\n# Or set the token directly:\n# export HF_TOKEN=hf_...\n\n# Pull caches from HuggingFace Hub\npython -m digit_classifier pull-cache --repo \u003cyour-username\u003e/digit-classification-cache\n\n# Train — external datasets (SVHN, MNIST, etc.) download automatically\n# from torchvision on first access; dedup is skipped (cached indices)\npython -m digit_classifier train\n```\n\n## CLI reference\n\n| Command | Description |\n|---|---|\n| `download` | Fetch the raw JPEG archive from Google Drive |\n| `preprocess` | Resize, colour-convert, compute mean/std and cache as `.npz` |\n| `train` | Run the full training pipeline |\n| `infer` | Real-time webcam digit recognition (uses EMA; pass `--mean`/`--std` if checkpoint lacks them) |\n| `export-pipeline` | Compile model + preprocessing into a TorchScript pipeline (uses EMA; pass `--mean`/`--std` if checkpoint lacks them) |\n| `visualize` | Debug-view augmented + mixed-up training batches |\n| `push-cache` | Push dataset caches to a HuggingFace Hub repo |\n| `pull-cache` | Pull dataset caches from a HuggingFace Hub repo |\n\nEvery training hyper-parameter is exposed as a CLI flag with the current\ndefaults.  Run `python -m digit_classifier train --help` for the full list.\n\n## Project layout\n\n```\nsrc/digit_classifier/\n  __init__.py          Package version\n  __main__.py          CLI entry-point (argparse subcommands)\n  config.py            Dataclass-based configuration\n  model.py             ResNeXt + DropPath\n  dataset.py           DigitDataset (tensor-backed)\n  splitting.py         Train/val split + external mixing + stats\n  external.py          ExternalOnDemandDataset + deduplication\n  sampler.py           RatioBatchSampler\n  augmentation.py      YOLO-style augmentation pipeline\n  mixup.py             timm Mixup/CutMix wrapper\n  preprocessing.py     Download + preprocess + cache\n  training.py          Training loop\n  inference.py         Webcam inference\n  visualize.py         Debug batch visualisation\n  hub.py               HuggingFace Hub push/pull\ntests/                 pytest test suite\n```\n\n## Running tests\n\n```bash\npytest\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fryanrudes%2Fcs148a","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fryanrudes%2Fcs148a","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fryanrudes%2Fcs148a/lists"}