Metadata-Version: 2.4
Name: emberml
Version: 0.5.0
Summary: A lightweight, powerful Python framework for training AI models — inspired by numpy, scikit-learn, PyTorch and TensorFlow.
Author: Levi Enama
License: MIT
Project-URL: Homepage, https://github.com/levi-enama/emberml
Project-URL: Repository, https://github.com/levi-enama/emberml
Keywords: machine-learning,deep-learning,autograd,neural-network,numpy,artificial-intelligence,training-framework,gpu,mixed-precision
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.21
Provides-Extra: gpu
Requires-Dist: cupy-cuda12x; extra == "gpu"
Provides-Extra: torch
Requires-Dist: torch; extra == "torch"
Provides-Extra: tf
Requires-Dist: tensorflow; extra == "tf"
Provides-Extra: sklearn
Requires-Dist: scikit-learn; extra == "sklearn"
Provides-Extra: cv
Requires-Dist: opencv-python; extra == "cv"
Provides-Extra: all
Requires-Dist: cupy-cuda12x; extra == "all"
Requires-Dist: torch; extra == "all"
Requires-Dist: tensorflow; extra == "all"
Requires-Dist: scikit-learn; extra == "all"
Requires-Dist: opencv-python; extra == "all"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pybind11>=2.11; extra == "dev"
Dynamic: license-file

# EmberML

![CI](https://github.com/levi-enama/emberml/actions/workflows/ci.yml/badge.svg)
![License](https://img.shields.io/badge/license-MIT-blue.svg)
![Python](https://img.shields.io/badge/python-3.9%2B-blue.svg)
![Version](https://img.shields.io/badge/version-0.3.0-orange.svg)

**A lightweight, powerful Python framework for training AI models — built from scratch.**

EmberML combines the essentials of `numpy`, `scikit-learn`, `PyTorch` and `TensorFlow` into one small, readable codebase. The core has exactly one hard dependency (`numpy`); everything else — GPU support, PyTorch/TensorFlow/scikit-learn/OpenCV interop — is an optional extra you opt into.

Author: **Levi Enama**

---

## Honest positioning

EmberML's own autograd engine is pure numpy. It is **not** a replacement for PyTorch or TensorFlow on real production workloads — it doesn't have compiled CUDA kernels, doesn't do distributed multi-node training, and won't match their raw throughput on large models. What it gives you instead:

- A training engine you can read end-to-end in an afternoon, with every op unit-tested and several (`Conv2d`, `MaxPool2d`, checkpointing) verified against numerical gradients
- A single, consistent API across deep learning *and* classical ML *and* data loading *and* metrics
- Real, working bridges to PyTorch/TensorFlow/scikit-learn/OpenCV for when you need their production-grade speed or ecosystem, without leaving EmberML's workflow

## Features

**Core (numpy only)**
- Full reverse-mode autograd on n-dimensional tensors
- `nn`: `Linear`, `Conv2d`, `MaxPool2d`, `Flatten`, `Dropout`, `BatchNorm1d`, `SimpleRNN`, `Sequential`
- `optim`: `SGD`, `Adam`, `AdamW`, `RMSprop` + `lr_scheduler`: `StepLR`, `ExponentialLR`, `ReduceLROnPlateau`
- `ml`: `LinearRegression`, `LogisticRegression`, `KMeans`, `KNNClassifier`, `GaussianNB`, `PCA`, `DecisionTreeClassifier`, `RandomForestClassifier`
- `metrics`: precision, recall, F1, confusion matrix, MSE, R²
- `Dataset` / `DataLoader` (generic, works with lazy/on-disk data too) / `train_test_split`
- Model persistence: `.save()/.load()` for networks, `save_model()/load_model()` for classical ML
- `EarlyStopping` callback

**Native acceleration (optional C++/C/Rust, auto-detected — see [`emberml/_native/`](emberml/_native/))**
- `Conv2d`/`MaxPool2d` transparently use a compiled C++/OpenMP extension
  for the im2col/col2im/max-pool steps on CPU float32 tensors — the
  actual measured bottleneck in the pure-Python path (`np.add.at`-based
  `col2im`), *not* the matmul itself (numpy's BLAS already wins there).
  Falls back to the original pure-numpy code automatically on GPU
  tensors, other dtypes, or if no C++ compiler was available at install
  time — results are bit-for-bit checked equal either way (`tests/test_native.py`).
- `emberml.security`: HMAC-SHA256 signing for saved models
  (`save_model_secure()`/`load_model_secure()`), backed by a small,
  dependency-free C implementation of SHA-256/HMAC. Addresses the fact
  that plain `pickle.load()` (what `save_model()`/`load_model()` use)
  runs arbitrary code from the file — verification happens *before*
  unpickling, so a wrong key or a tampered file is rejected outright.
- `emberml.safeformat`: a pickle-free binary container (`.emsf`) for
  `{name: numpy.ndarray}` dicts — no unpickling step exists at all, so
  there's no code-execution surface to defend, unlike signed pickle.
  Implemented in Rust (PyO3) for safe parsing of untrusted files (bounds
  checking, no manual buffer arithmetic), with a byte-for-byte
  cross-compatible pure-Python fallback. `security.save_tensors_secure()`/
  `load_tensors_secure()` combine it with the same HMAC signing as
  above — the strongest option when what you're saving is purely
  tensors (`nn.Module` weights, embeddings). `Module.save(path, format="safe")`
  uses it directly.
- Check `emberml.HAVE_NATIVE_OPS` / `emberml.HAVE_NATIVE_CRYPTO` /
  `emberml._native.HAVE_NATIVE_SAFEFORMAT` at runtime to see which
  compiled extensions are active.

**Scaling up (still numpy/stdlib only)**
- **GPU**: `Tensor(..., device="cuda")` / `Module.to("cuda")` via CuPy — a drop-in numpy-compatible GPU array library. Requires `pip install emberml[gpu]` and an NVIDIA GPU; raises a clear error rather than silently running on CPU if unavailable.
- **Mixed precision**: `emberml.amp.autocast()` + `GradScaler` (dynamic loss scaling)
- **Gradient checkpointing**: `emberml.checkpoint.checkpoint(fn, *tensors)` — trade compute for memory on deep networks
- **CPU data parallelism**: `emberml.parallel.DataParallelCPU` — shard a batch across processes on one machine (not multi-GPU/multi-node)

**`emberml.integrations` (optional bridges, not imported by default)**
- `torch_bridge` — hand off to real PyTorch when you need production GPU speed
- `tf_bridge` — hand off to real TensorFlow
- `sklearn_bridge` — use `emberml.ml` models inside scikit-learn `Pipeline`/`GridSearchCV`, or reach for a real scikit-learn model by name
- `cv` — OpenCV-backed `ImageDataset`, image loading, flip/rotation augmentation

## Installation

```bash
git clone https://github.com/<your-username>/emberml.git
cd emberml
pip install -e .                 # core only (numpy) — native extensions build automatically if a C/C++ compiler is present
pip install -e .[torch]          # + PyTorch bridge
pip install -e .[tf]             # + TensorFlow bridge
pip install -e .[sklearn]        # + scikit-learn bridge
pip install -e .[cv]             # + OpenCV bridge
pip install -e .[gpu]            # + CuPy (needs an NVIDIA GPU)
pip install -e .[all]            # everything
```

No C/C++ compiler or Rust toolchain on the machine (or a build hiccup)?
Installation still succeeds — `pip` prints a one-line warning per missing
toolchain and emberml runs on its pure-Python/numpy fallbacks. Verify
what actually got built:

```python
import emberml
print(emberml.HAVE_NATIVE_OPS, emberml.HAVE_NATIVE_CRYPTO, emberml._native.HAVE_NATIVE_SAFEFORMAT)
```

### Signing a model checkpoint

```python
from emberml import security

key = security.generate_key()          # keep this secret; os.urandom(32) under the hood
security.save_model_secure(model, "model.emlpkl", key)

# ... later, possibly on another machine / after downloading the file ...
model = security.load_model_secure("model.emlpkl", key)   # raises SecurityError on wrong key or tampering
```

### Pickle-free tensor checkpoints (recommended for `nn.Module` weights)

```python
# strongest option for pure-tensor checkpoints: no pickle anywhere, plus HMAC authenticity
from emberml import security

key = security.generate_key()
security.save_tensors_secure({"w1": w1, "b1": b1}, "weights.bin", key)
tensors = security.load_tensors_secure("weights.bin", key)   # SecurityError on wrong key/tampering

# or, unsigned, straight from a Module:
model.save("checkpoint", format="safe")   # writes checkpoint.emsf, no pickle involved
model.load("checkpoint", format="safe")
```

## Quick start

### Train a CNN, GPU if available

```python
import numpy as np
from emberml import Tensor, nn, optim

device = "cuda"  # falls back to "cpu" if you don't have CuPy + a GPU
X = Tensor(np.random.randn(32, 1, 28, 28).astype(np.float32), device="cpu")
y = Tensor(np.random.randint(0, 2, (32, 1)).astype(np.float32), device="cpu")

model = nn.Sequential(
    nn.Conv2d(1, 8, kernel_size=3, padding=1), nn.ReLU(), nn.MaxPool2d(2),
    nn.Flatten(), nn.Linear(8 * 14 * 14, 32), nn.ReLU(), nn.Dropout(0.3),
    nn.Linear(32, 1), nn.Sigmoid(),
)
# model.to("cuda"); X = X.to("cuda"); y = y.to("cuda")  # uncomment on a GPU box

optimizer = optim.Adam(model.parameters(), lr=0.001)
for epoch in range(10):
    optimizer.zero_grad()
    loss = nn.bce_loss(model(X), y)
    loss.backward()
    optimizer.step()

model.save("mnist_like_model")
```

### Mixed precision training

```python
from emberml.amp import autocast, GradScaler

scaler = GradScaler()
with autocast():
    pred = model(X)
    loss = nn.mse_loss(pred, y)
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()
```

### When you need real PyTorch speed

```python
from emberml.integrations import torch_bridge

torch_state = torch_bridge.emberml_module_to_torch_state(model)
# now train_state in real torch.Tensors on a real GPU with real torch.optim
```

### Classic ML inside a scikit-learn Pipeline

```python
from emberml import ml
from emberml.integrations.sklearn_bridge import EmberMLClassifier
from sklearn.model_selection import cross_val_score

clf = EmberMLClassifier(ml.RandomForestClassifier, n_estimators=50)
scores = cross_val_score(clf, X_train, y_train, cv=5)
```

### Real image data via OpenCV

```python
from emberml.integrations.cv import ImageDataset
from emberml import DataLoader

ds = ImageDataset(image_paths, labels, size=(64, 64), augment=True)
loader = DataLoader(ds, batch_size=32, shuffle=True)
```

## Command-line interface

```bash
python -m emberml info                        # library info
python -m emberml demo                        # run a live training demo
python -m emberml benchmark --iters 500        # benchmark forward+backward speed
python -m emberml --help                       # list all commands
```

## Architecture

```
emberml/
├── tensor.py            # Core: Tensor + autograd, device (CPU/CUDA) + dtype aware
├── functional.py        # conv2d / max_pool2d (im2col/col2im, gradient-checked, device-aware)
├── amp.py                # autocast + GradScaler (mixed precision)
├── checkpoint.py         # gradient checkpointing
├── parallel.py            # CPU multiprocessing data parallelism
├── nn/                     # Linear, Conv2d, MaxPool2d, Flatten, Dropout,
│                            # BatchNorm1d, SimpleRNN, Sequential, losses
├── optim.py                 # SGD, Adam, AdamW, RMSprop
├── lr_scheduler.py           # StepLR, ExponentialLR, ReduceLROnPlateau
├── callbacks.py                # EarlyStopping
├── ml/                          # LinearRegression, LogisticRegression, KMeans,
│                                 # KNNClassifier, GaussianNB, PCA, DecisionTree,
│                                 # RandomForest
├── data.py                       # Dataset, DataLoader (generic), train_test_split
├── metrics.py                     # precision/recall/F1/confusion_matrix/MSE/R²
├── utils.py                        # seed, accuracy, one_hot, normalize, save/load
├── security.py                      # HMAC-SHA256 model signing (save_model_secure/load_model_secure)
├── safeformat.py                     # pickle-free tensor container dispatcher (.emsf)
├── _safeformat_py.py                  # pure-Python reference impl of the .emsf format
├── cli.py                            # info / demo / benchmark
├── _native/                            # OPTIONAL — compiled C++/C/Rust extensions, auto-detected
│   ├── ops.cpp                          # im2col/col2im/max_pool2d/activations (pybind11 + OpenMP)
│   ├── sha256.c, sha256.h, _crypto.c     # SHA-256/HMAC (plain C, no dependencies)
│   ├── rust_safeformat/                   # Cargo crate: .emsf reader/writer (PyO3, memory-safe parsing)
│   └── __init__.py                       # try/except imports; HAVE_NATIVE_OPS / _CRYPTO / _SAFEFORMAT
└── integrations/                     # OPTIONAL — never imported by default
    ├── torch_bridge.py
    ├── tf_bridge.py
    ├── sklearn_bridge.py
    └── cv.py
tests/                                 # 48 pytest tests (gradient checks, native-vs-reference checks, integration bridges)
examples/train_cnn_classifier.py        # full end-to-end training example
setup.py                                 # builds ext_modules (C/C++) + cargo build (Rust); metadata stays in pyproject.toml
.github/workflows/ci.yml                 # CI: core job (numpy only, + Rust toolchain) + integrations job (all extras)
```

## Testing

```bash
pip install -e .[dev]
pytest tests/ -v                    # core tests, including native-extension checks (auto-skipped if not built)
pip install -e .[all,dev]
pytest tests/ -v                    # + integration bridge tests
```

## Roadmap

- [ ] LSTM / GRU cells
- [ ] Real multi-GPU / multi-node distributed training (would hand off to `torch.distributed` rather than reinvent it)
- [ ] ONNX-style model export

## License

MIT — see [LICENSE](LICENSE).

## Author

**Levi Enama**
