Tabular Classification
PyTorch
LiteRT
TF-Keras
ONNX
LiteRT
industrial
edge-ai
tensorflow
synthetic-data
Instructions to use sankalpsthakur/forge-tiny-drift-multiruntime with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- LiteRT
How to use sankalpsthakur/forge-tiny-drift-multiruntime with LiteRT:
# No code snippets available yet for this library. # To use this model, check the repository files and the library's documentation. # Want to help? PRs adding snippets are welcome at: # https://github.com/huggingface/huggingface.js
- Notebooks
- Google Colab
- Kaggle
| from __future__ import annotations | |
| import json | |
| import os | |
| import random | |
| import shutil | |
| import sys | |
| from importlib.metadata import version | |
| from pathlib import Path | |
| os.environ.setdefault("TF_CPP_MIN_LOG_LEVEL", "2") | |
| os.environ.setdefault("TF_ENABLE_ONEDNN_OPTS", "0") | |
| import numpy as np | |
| import torch | |
| from torch import nn | |
| ROOT = Path(__file__).resolve().parent | |
| sys.path.insert(0, str(ROOT / "src")) | |
| from forge_lab.models.numpy_runtime import ( # noqa: E402 | |
| DEFAULT_CENTER, | |
| DEFAULT_SCALE, | |
| WINDOW_SIZE, | |
| ) | |
| from forge_lab.models.pytorch_model import TinyDriftNet # noqa: E402 | |
| ARTIFACTS = ROOT | |
| MODEL_VERSION = "forge-tiny-drift-v0.1" | |
| def synthetic_windows(count: int = 2048, seed: int = 17) -> tuple[np.ndarray, np.ndarray]: | |
| rng = np.random.default_rng(seed) | |
| windows = np.empty((count, WINDOW_SIZE, 2), dtype=np.float32) | |
| labels = np.empty(count, dtype=np.float32) | |
| x = np.arange(WINDOW_SIZE, dtype=np.float32) | |
| for index in range(count): | |
| slope = rng.uniform(-0.01, 0.13) | |
| force = ( | |
| rng.uniform(96.0, 104.0) | |
| + slope * x | |
| + rng.normal(0.0, rng.uniform(0.03, 0.18), WINDOW_SIZE) | |
| ) | |
| deviation = ( | |
| 0.07 + np.maximum(0.0, force - 101.0) * 0.011 + rng.normal(0.0, 0.002, WINDOW_SIZE) | |
| ) | |
| windows[index, :, 0] = force | |
| windows[index, :, 1] = deviation | |
| shift = force[-10:].mean() - force[:10].mean() | |
| labels[index] = float(slope >= 0.07 and shift >= 2.0) | |
| return windows, labels | |
| def train_model() -> tuple[TinyDriftNet, dict]: | |
| random.seed(17) | |
| np.random.seed(17) | |
| torch.manual_seed(17) | |
| x, y = synthetic_windows() | |
| model = TinyDriftNet() | |
| optimizer = torch.optim.AdamW(model.classifier.parameters(), lr=0.04, weight_decay=0.001) | |
| loss_fn = nn.BCELoss() | |
| x_tensor = torch.from_numpy(x) | |
| y_tensor = torch.from_numpy(y) | |
| model.train() | |
| for _ in range(240): | |
| optimizer.zero_grad(set_to_none=True) | |
| loss = loss_fn(model(x_tensor), y_tensor) | |
| loss.backward() | |
| optimizer.step() | |
| model.eval() | |
| with torch.no_grad(): | |
| probabilities = model(x_tensor) | |
| predictions = probabilities >= 0.5 | |
| labels = y_tensor.bool() | |
| accuracy = float((predictions == labels).float().mean()) | |
| false_positives = ((predictions == 1) & (labels == 0)).sum() | |
| false_negatives = ((predictions == 0) & (labels == 1)).sum() | |
| false_positive_rate = float(false_positives / (labels == 0).sum()) | |
| false_negative_rate = float(false_negatives / (labels == 1).sum()) | |
| metrics = { | |
| "synthetic_examples": len(x), | |
| "accuracy": round(accuracy, 6), | |
| "false_positive_rate": round(false_positive_rate, 6), | |
| "false_negative_rate": round(false_negative_rate, 6), | |
| "training_seed": 17, | |
| } | |
| return model, metrics | |
| def export_pytorch_and_onnx(model: TinyDriftNet) -> None: | |
| torch.save( | |
| { | |
| "model_version": MODEL_VERSION, | |
| "state_dict": model.state_dict(), | |
| }, | |
| ARTIFACTS / "tiny_drift_pytorch.pt", | |
| ) | |
| example = torch.zeros(2, WINDOW_SIZE, 2, dtype=torch.float32) | |
| batch = torch.export.Dim("batch", min=1, max=512) | |
| onnx_program = torch.onnx.export( | |
| model, | |
| (example,), | |
| input_names=["telemetry"], | |
| output_names=["probability"], | |
| dynamic_shapes=({0: batch},), | |
| dynamo=True, | |
| verify=True, | |
| external_data=False, | |
| ) | |
| onnx_program.save(ARTIFACTS / "tiny_drift.onnx") | |
| def export_tensorflow_and_litert(weight: np.ndarray, bias: float) -> None: | |
| import tensorflow as tf | |
| from forge_lab.models.tensorflow_model import TensorFlowDriftModel | |
| model = TensorFlowDriftModel(weight, bias) | |
| saved_model = ARTIFACTS / "tensorflow_saved_model" | |
| if saved_model.exists(): | |
| shutil.rmtree(saved_model) | |
| concrete = model.__call__.get_concrete_function() | |
| tf.saved_model.save(model, saved_model, signatures={"serving_default": concrete}) | |
| converter = tf.lite.TFLiteConverter.from_concrete_functions([concrete], model) | |
| converter.optimizations = [tf.lite.Optimize.DEFAULT] | |
| (ARTIFACTS / "tiny_drift.tflite").write_bytes(converter.convert()) | |
| def write_metadata(model: TinyDriftNet, metrics: dict) -> None: | |
| weight = model.classifier.weight.detach().cpu().numpy().reshape(-1) | |
| bias = float(model.classifier.bias.detach().cpu().item()) | |
| weights = { | |
| "model_version": MODEL_VERSION, | |
| "feature_center": DEFAULT_CENTER.tolist(), | |
| "feature_scale": DEFAULT_SCALE.tolist(), | |
| "linear_weight": weight.tolist(), | |
| "linear_bias": bias, | |
| } | |
| (ARTIFACTS / "weights.json").write_text(json.dumps(weights, indent=2) + "\n") | |
| manifest = { | |
| "model_version": MODEL_VERSION, | |
| "input": {"name": "telemetry", "shape": ["batch", WINDOW_SIZE, 2], "dtype": "float32"}, | |
| "channels": ["peak_force_kn", "part_deviation_mm"], | |
| "outputs": ["probability"], | |
| "runtimes": ["numpy", "pytorch", "onnxruntime", "tensorflow", "litert"], | |
| "framework_versions": { | |
| "python": sys.version.split()[0], | |
| "numpy": np.__version__, | |
| "pytorch": torch.__version__, | |
| "pytorch_cuda_build": torch.version.cuda, | |
| "onnx": version("onnx"), | |
| "onnxruntime": version("onnxruntime"), | |
| "tensorflow": version("tensorflow-cpu"), | |
| "litert": version("ai-edge-litert"), | |
| }, | |
| "metrics": metrics, | |
| "data": "deterministic synthetic telemetry; no plant data", | |
| "safety": "L0 decision support only; output cannot actuate equipment", | |
| "cuda_validation": "not executed; no GPU or nvcc in the build workspace", | |
| } | |
| (ARTIFACTS / "model_manifest.json").write_text(json.dumps(manifest, indent=2) + "\n") | |
| def main() -> None: | |
| ARTIFACTS.mkdir(parents=True, exist_ok=True) | |
| model, metrics = train_model() | |
| write_metadata(model, metrics) | |
| weight = model.classifier.weight.detach().cpu().numpy().reshape(-1) | |
| bias = float(model.classifier.bias.detach().cpu().item()) | |
| export_pytorch_and_onnx(model) | |
| export_tensorflow_and_litert(weight, bias) | |
| print(json.dumps(metrics, indent=2)) | |
| if __name__ == "__main__": | |
| main() | |