Instructions to use FluidInference/jeff-coreml with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- GLiFormer
How to use FluidInference/jeff-coreml with GLiFormer:
# 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
| """Validate Jeff's trained classifier graph and export its complete decision path.""" | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import warnings | |
| from pathlib import Path | |
| import numpy as np | |
| import torch | |
| import torch.nn.functional as F | |
| from huggingface_hub import snapshot_download | |
| from jeff.backends.torch_backend import TorchBackend | |
| from jeff.core.backend import Group | |
| from jeff_decision import JeffDecision, marker_positions | |
| from trace_compat import finite_fp16_mask, install_trace_compatibility | |
| SOURCE = "knowledgator/gliformer-large-v1" | |
| REVISION = "d0a4e53d09cebe6bc963dd9be319d4279084bb2d" | |
| BUCKET = 128 | |
| MAX_CATEGORIES = 8 | |
| FIXTURES = ( | |
| ( | |
| "billing", | |
| "The invoice was charged twice and the customer asks for a refund.", | |
| Group(key="route", labels=("billing: invoice or payment issue", "support: technical product issue"), | |
| name="Choose the correct support queue"), | |
| ), | |
| ( | |
| "technical", | |
| "The app crashes when I save my project. Please help me recover the file.", | |
| Group(key="route", labels=("billing: invoice or payment issue", "support: technical product issue"), | |
| name="Choose the correct support queue"), | |
| ), | |
| ( | |
| "three_way", | |
| "Tomorrow at 9 a.m. works well for the appointment.", | |
| Group(key="intent", labels=("schedule: appointment request", "billing: payment issue", | |
| "support: technical issue"), name="Classify the user intent"), | |
| ), | |
| ( | |
| "boolean", | |
| "I cannot sign in after resetting my password.", | |
| Group(key="answer", labels=("yes", "no"), name="Is this a technical support request?"), | |
| ), | |
| ) | |
| def make_batch(backend: TorchBackend, text: str, group: Group) -> dict: | |
| tokens, _, _ = backend.model.prepare_inputs([text]) | |
| return backend._collator([{ | |
| "tokenized_text": tokens[0], | |
| "classification": [{ | |
| "name": group.name, | |
| "description": group.description, | |
| "all_labels": list(group.labels), | |
| "true_labels": [], | |
| }], | |
| }]) | |
| def model_inputs(batch: dict, config) -> tuple[torch.Tensor, ...]: | |
| ids = batch["input_ids"] | |
| mask = batch["attention_mask"] | |
| if ids.shape[1] > BUCKET: | |
| raise ValueError(f"input has {ids.shape[1]} tokens; L{BUCKET} cannot serve it") | |
| parent, children, count = marker_positions(ids, config, MAX_CATEGORIES) | |
| if count != len(batch["classes_mapping"].cat_mapping[0].cat_class_to_id[0].class_to_id): | |
| raise ValueError("collator category mapping does not match marker count") | |
| pad = BUCKET - ids.shape[1] | |
| return ( | |
| F.pad(ids.to(torch.int32), (0, pad)), | |
| F.pad(mask.to(torch.int32), (0, pad)), | |
| parent, | |
| children, | |
| ) | |
| def native_report(backend: TorchBackend, decision: JeffDecision) -> tuple[list[dict], tuple[torch.Tensor, ...]]: | |
| records = [] | |
| first = None | |
| for name, text, group in FIXTURES: | |
| batch = make_batch(backend, text, group) | |
| tensors = model_inputs(batch, backend.model.config) | |
| native = backend.model.model(**batch, include_media=False).cat_logits.detach().float().numpy()[0] | |
| converted = decision(*tensors).detach().float().numpy()[0, :len(group.labels)] | |
| error = float(np.max(np.abs(native - converted))) | |
| records.append({ | |
| "name": name, | |
| "token_count": int(batch["attention_mask"].sum()), | |
| "labels": list(group.labels), | |
| "native_logits": native.tolist(), | |
| "decision_logits": converted.tolist(), | |
| "max_logit_error": error, | |
| }) | |
| if error > 1e-3: | |
| raise AssertionError(f"{name}: trained graph differs from native logits by {error}") | |
| if first is None: | |
| first = tensors | |
| assert first is not None | |
| return records, first | |
| def main() -> None: | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument("--convert", action="store_true") | |
| parser.add_argument("--precision", choices=("fp16", "fp32"), default="fp16") | |
| args = parser.parse_args() | |
| torch.set_num_threads(2) | |
| checkpoint = snapshot_download(SOURCE, revision=REVISION, local_files_only=True) | |
| with warnings.catch_warnings(): | |
| warnings.filterwarnings("ignore", message=r"attn_kernel=.*flashdeberta") | |
| backend = TorchBackend(checkpoint, device="cpu", dtype="float32", attn_kernel="eager", batch_size=1) | |
| decision = JeffDecision(backend.model.model).eval() | |
| report, first = native_report(backend, decision) | |
| out = Path("build") | |
| out.mkdir(exist_ok=True) | |
| (out / "native-parity.json").write_text(json.dumps(report, indent=2) + "\n") | |
| print(json.dumps({"native_parity": report}, indent=2), flush=True) | |
| if not args.convert: | |
| return | |
| import coremltools as ct | |
| install_trace_compatibility() | |
| with finite_fp16_mask(): | |
| patched_report, _ = native_report(backend, decision) | |
| patched_error = max( | |
| abs(before - after) | |
| for baseline, patched in zip(report, patched_report) | |
| for before, after in zip(baseline["decision_logits"], patched["decision_logits"]) | |
| ) | |
| if patched_error > 1e-3: | |
| raise AssertionError(f"tracing-only mask/attention scale changes native logits by {patched_error}") | |
| print(f"Patched mask/attention max native logit error: {patched_error:.8f}", flush=True) | |
| traced = torch.jit.trace(decision, first, check_trace=False).eval() | |
| traced.save(str(out / "jeff-decision-L128.pt")) | |
| with torch.inference_mode(): | |
| expected = decision(*first).detach().numpy() | |
| actual = traced(*first).detach().numpy() | |
| trace_error = float(np.max(np.abs(expected - actual))) | |
| if trace_error > 1e-3: | |
| raise AssertionError(f"TorchScript trace mismatch: {trace_error}") | |
| print(f"TorchScript trace max logit error: {trace_error:.8f}", flush=True) | |
| mlmodel = ct.convert( | |
| traced, | |
| convert_to="mlprogram", | |
| minimum_deployment_target=ct.target.macOS15, | |
| compute_precision=ct.precision.FLOAT16 if args.precision == "fp16" else ct.precision.FLOAT32, | |
| inputs=[ | |
| ct.TensorType(name="input_ids", shape=(1, BUCKET), dtype=np.int32), | |
| ct.TensorType(name="attention_mask", shape=(1, BUCKET), dtype=np.int32), | |
| ct.TensorType(name="parent_position", shape=(1, 1), dtype=np.int32), | |
| ct.TensorType(name="category_positions", shape=(1, MAX_CATEGORIES), dtype=np.int32), | |
| ], | |
| ) | |
| package = out / f"JeffDecision-L128-{args.precision.upper()}.mlpackage" | |
| mlmodel.save(str(package)) | |
| print(f"Saved {package}", flush=True) | |
| if __name__ == "__main__": | |
| main() | |