Instructions to use litert-community/d1-3B-LiteRT with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- LiteRT
How to use litert-community/d1-3B-LiteRT 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
Download examples/run_example.py from litert-community/d1-3B-LiteRT: direct link, hf CLI and curl.
- Browser
- Download file 12.7 kB
-
https://huggingface.co/litert-community/d1-3B-LiteRT/resolve/main/examples/run_example.py
- Command line
-
hf download hf://litert-community/d1-3B-LiteRT/examples/run_example.py
-
curl -L -o run_example.py https://huggingface.co/litert-community/d1-3B-LiteRT/resolve/main/examples/run_example.py
12.7 kB
| """Example: the two example requests of the source model card, answered on the CPU or the GPU, and one refused request. | |
| Run from the repository root: | |
| pip install -r host/requirements-host.txt | |
| python examples/run_example.py --check # CPU (XNNPACK, 8 threads) | |
| python examples/run_example.py --check --accel gpu # GPU at float32 precision (Metal on a Mac) | |
| 1. Text: fixtures/requests_public.json `card_text_001`, one state and three questions (noul, choice, score). | |
| 2. Picture: `card_cats_001`, the card's photo as the whole state and one choice question. The photo (COCO val2017 | |
| 000000039769.jpg, two cats) is not in this repository: the script downloads it from cocodataset.org into memory and | |
| checks its SHA-256 before use. `--image <file>` reads a local copy instead, checked the same way. | |
| 3. A state of more than 4,096 tokens: the host refuses its row (RowTooLong) instead of cutting it. | |
| --check compares the answers with examples/run_example.expected.json, which holds the provider's code in float32 on the | |
| CPU (one row per question). Every probability must be within 1e-5 of it, and the most likely option, the input token | |
| count, the route and the refusal message must be equal. | |
| load_host() builds the host of this repository from contract.json: host/d1_litert.py (D1Host: text and pictures on the | |
| row graphs) under host/d1_shared_state.py (D1SharedHost: the shared-state pairs, picked per request from the measured | |
| call times in the contract). A graph file is compiled when a request needs it. At most `keep` text graphs | |
| (row graphs and pairs) stay compiled; the least recently used one is closed to make room. | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import hashlib | |
| import json | |
| import sys | |
| import time | |
| import urllib.request | |
| from collections import OrderedDict | |
| from pathlib import Path | |
| ROOT = Path(__file__).resolve().parents[1] | |
| sys.path.insert(0, str(ROOT / "host")) | |
| import d1_litert as H # noqa: E402 | |
| import d1_shared_state as S # noqa: E402 | |
| import d1_vision as V # noqa: E402 | |
| # A cold-room log of 900 readings: its row is longer than the largest graph (4,096 tokens). | |
| REFUSED_REQUEST = { | |
| "state": {"room": "cold-room-2", "unit": "celsius", | |
| "readings": [{"minute": i, "value": round(3.0 + (i % 9) * 0.1, 1)} for i in range(900)]}, | |
| "questions": {"breach": {"type": "noul", "instructions": "Did the temperature go above 3.5 degrees?"}}, | |
| } | |
| # ----------------------------------------------------------------------------------------------- the host | |
| class GraphCache: | |
| """Compiled graphs by key; at most `keep` stay compiled, the least recently used is closed to make room.""" | |
| def __init__(self, keep: int): | |
| self.keep, self.items = max(1, int(keep)), OrderedDict() | |
| def get(self, key, make): | |
| if key in self.items: | |
| self.items.move_to_end(key) | |
| return self.items[key] | |
| while len(self.items) >= self.keep: | |
| _, old = self.items.popitem(last=False) | |
| old.close() | |
| self.items[key] = make() | |
| return self.items[key] | |
| def close(self): | |
| while self.items: | |
| self.items.popitem(last=False)[1].close() | |
| class LazyRowGraphs(dict): | |
| """{L: embeds row graph} for D1Host: the keys are the files present; a graph is compiled when it is used.""" | |
| def __init__(self, files: dict, cache: GraphCache, args: tuple): | |
| super().__init__({L: None for L in files}) | |
| self.files, self.cache, self.args = files, cache, args | |
| def __getitem__(self, L): | |
| if L not in self.files: | |
| raise KeyError(L) | |
| return self.cache.get(("row", L), lambda: H.LiteRTEmbedsGraph(self.files[L], *self.args)) | |
| class LazyPair: | |
| """A shared-state pair with the attributes D1SharedHost reads; the file is compiled when it is used.""" | |
| def __init__(self, path: Path, Ls: int, Lq: int, hidden: int, cache: GraphCache, args: tuple, embed_table, pad_id): | |
| self.path, self.Ls, self.Lq, self.hidden_size = Path(path), Ls, Lq, hidden | |
| self.handover, self.pad_id = "direct", pad_id | |
| self.cache, self.args, self.embed_table = cache, args, embed_table | |
| def _pair(self) -> S.SharedStatePair: | |
| return self.cache.get(("pair", self.Ls, self.Lq), lambda: S.SharedStatePair( | |
| self.path, *self.args, pad_id=self.pad_id, embed_table=self.embed_table)) | |
| def run_state(self, state_ids): | |
| return self._pair().run_state(state_ids) | |
| def run_question(self, own_ids): | |
| return self._pair().run_question(own_ids) | |
| def close(self): | |
| pass # the cache closes the compiled file | |
| class LazyGraph: | |
| """One picture graph (tower or projector), compiled when it is called.""" | |
| def __init__(self, path: Path, args: tuple): | |
| self.path, self.args, self.graph = Path(path), args, None | |
| def __call__(self, **feeds): | |
| if self.graph is None: | |
| self.graph = V.LiteRTGraph(self.path, *self.args) | |
| return self.graph(**feeds) | |
| def close(self): | |
| if self.graph is not None: | |
| self.graph.close() | |
| self.graph = None | |
| class LoadedHost: | |
| """decide(request) -> the response; route(request) -> where its questions run; close().""" | |
| def __init__(self, shared: S.D1SharedHost, cache: GraphCache): | |
| self.shared, self.cache = shared, cache | |
| def decide(self, request: dict) -> dict: | |
| return self.shared.decide(request) | |
| def route(self, request: dict) -> dict: | |
| r = self.shared.route(request) | |
| if r["route"] == "pair": | |
| return {"route": "pair", "Ls": r["Ls"], "Lq": r["Lq"]} | |
| rows = self.shared.host.rows(request) | |
| return {"route": "row", "L": max(H.pick_L(len(x.ids), self.shared.host.row_buckets()) for x in rows)} | |
| def close(self): | |
| self.cache.close() | |
| if self.shared.host.vision is not None: | |
| self.shared.host.vision.close() | |
| def __enter__(self): | |
| return self | |
| def __exit__(self, *exc): | |
| self.close() | |
| def load_host(root=ROOT, accelerator: str = "cpu", threads: int = 8, keep: int = 1) -> LoadedHost: | |
| """The host on the files of `root` that are present. accelerator "cpu" (XNNPACK, `threads`) or "gpu" (float32 | |
| precision: GpuOptions(enforce_f32=True)).""" | |
| root = Path(root) | |
| contract = json.loads((root / "contract.json").read_text()) | |
| args = (accelerator, "fp32", threads) | |
| pad = int(contract["token_ids"]["pad"]) | |
| embed_table = H.EmbedTable(root / contract["embeds_graph"]["table"]) | |
| host = H.D1Host(H.D1Tokenizer(root / contract["tokenizer"]["file"]), | |
| table=H.ReadoutTable.from_file(root / contract["readout_table"]["file"]), pad_id=pad, | |
| embed_table=embed_table) | |
| cache = GraphCache(keep) | |
| rows = {int(g["L"]): root / g["file"] for g in contract["embeds_graph"]["buckets"] if (root / g["file"]).is_file()} | |
| host.embeds_graphs = LazyRowGraphs(rows, cache, args) # after __init__, which copies a plain dict | |
| vision = contract["vision"]["real_files"] | |
| tower, projector = (root / vision[g]["v2_fp16fc"]["file"] for g in ("tower", "projector")) | |
| if tower.is_file() and projector.is_file(): | |
| host.vision = V.VisionPath(LazyGraph(tower, args), LazyGraph(projector, args), | |
| V.load_position_table(root / contract["vision"]["position_table"]["file"]), | |
| V.token_ids(contract)) | |
| hidden = int(contract["model"]["hidden_size"]) | |
| pairs = [LazyPair(root / f["file"], int(f["Ls"]), int(f["Lq"]), hidden, cache, args, embed_table, pad) | |
| for f in contract["shared_state"]["files"] if (root / f["file"]).is_file()] | |
| pick = contract["shared_state"]["pick"]["call_ms"] | |
| call_ms = {"row": {int(L): float(ms) for L, ms in pick["row"].items()}, | |
| "pair": {(int(p["Ls"]), int(p["Lq"])): (float(p["state_call_ms"]), float(p["question_call_ms"])) | |
| for p in pick["pair"]}} | |
| return LoadedHost(S.D1SharedHost(host, pairs, call_ms=call_ms), cache) | |
| # ----------------------------------------------------------------------------------------------- the requests | |
| def record(root: Path, rid: str) -> dict: | |
| doc = json.loads((Path(root) / "fixtures/requests_public.json").read_text()) | |
| return next(r for r in doc["records"] if r["id"] == rid) | |
| def text_request(root: Path = ROOT) -> dict: | |
| return record(root, "card_text_001")["request"] | |
| def picture_request(root: Path = ROOT, image: str | None = None) -> dict: | |
| """card_cats_001 with its photo's bytes; the photo's SHA-256 must match the fixture's.""" | |
| req = dict(record(root, "card_cats_001")["request"]) | |
| meta = req["images"][0] | |
| if image: | |
| data = Path(image).read_bytes() | |
| else: | |
| with urllib.request.urlopen(meta["url"], timeout=60) as resp: | |
| data = resp.read() | |
| got = hashlib.sha256(data).hexdigest() | |
| if got != meta["sha256"]: | |
| raise SystemExit(f"the photo's SHA-256 is {got}, expected {meta['sha256']} ({meta['url']}): not used") | |
| req["images"] = [data] | |
| return req | |
| # ----------------------------------------------------------------------------------------------- the check | |
| def probabilities(answer: dict, keys: list) -> list: | |
| if answer["type"] == "noul": | |
| return [answer["noul"], 1.0 - answer["noul"]] | |
| return [answer["probabilities"][k] for k in keys] | |
| def check(results: dict, expected: dict) -> list: | |
| problems, tol = [], float(expected["tolerance_abs_dp"]) | |
| for name in ("text", "picture"): | |
| exp, got = expected["requests"][name], results[name] | |
| if got["response"]["usage"]["input_tokens"] != exp["input_tokens"]: | |
| problems.append(f"{name}: input_tokens {got['response']['usage']['input_tokens']} != {exp['input_tokens']}") | |
| if got["route"] != exp["route"]: | |
| problems.append(f"{name}: route {got['route']} != {exp['route']}") | |
| for q, e in exp["questions"].items(): | |
| p = probabilities(got["response"]["answers"][q], e["keys"]) | |
| dp = max(abs(a - b) for a, b in zip(p, e["probabilities"])) | |
| top = e["keys"][max(range(len(p)), key=p.__getitem__)] | |
| got["max_abs_dp"] = max(got.get("max_abs_dp", 0.0), dp) | |
| if dp > tol or top != e["argmax_key"]: | |
| problems.append(f"{name}/{q}: max |dp| {dp:.2e} (tolerance {tol:.0e}), most likely {top} vs " | |
| f"{e['argmax_key']}") | |
| exp, got = expected["requests"]["refused"], results["refused"] | |
| if (got.get("error"), got.get("message")) != (exp["error"], exp["message"]): | |
| problems.append(f"refused: {got} != {exp}") | |
| return problems | |
| def main() -> int: | |
| ap = argparse.ArgumentParser(description=__doc__.split("\n")[0]) | |
| ap.add_argument("--accel", choices=["cpu", "gpu"], default="cpu", help="cpu (XNNPACK) or gpu (float32 precision)") | |
| ap.add_argument("--threads", type=int, default=8, help="CPU threads (default 8)") | |
| ap.add_argument("--image", help="a local copy of the photo (checked against the fixture's SHA-256)") | |
| ap.add_argument("--check", action="store_true", help="compare with examples/run_example.expected.json") | |
| a = ap.parse_args() | |
| results = {} | |
| with load_host(ROOT, a.accel, a.threads) as host: | |
| for name, request in (("text", text_request()), ("picture", picture_request(ROOT, a.image))): | |
| route = host.route(request) | |
| t = time.perf_counter() | |
| response = host.decide(request) | |
| seconds = time.perf_counter() - t | |
| results[name] = {"route": route, "response": response} | |
| print(json.dumps({"request": name, "route": route, "response": response}, indent=1)) | |
| print(f"# {name}: {seconds:.1f} s, including the compile of its graphs", flush=True) | |
| try: | |
| host.decide(REFUSED_REQUEST) | |
| results["refused"] = {"error": None, "message": "answered"} | |
| except H.RequestError as e: | |
| results["refused"] = {"error": type(e).__name__, "message": str(e)} | |
| print(json.dumps({"request": "refused", **results["refused"]})) | |
| if not a.check: | |
| return 0 | |
| expected = json.loads((ROOT / "examples/run_example.expected.json").read_text()) | |
| problems = check(results, expected) | |
| for name in ("text", "picture"): | |
| print(f"# {name}: max |dp| {results[name]['max_abs_dp']:.2e} against the provider's float32 CPU reference") | |
| if problems: | |
| print("DIFFERS from run_example.expected.json:\n " + "\n ".join(problems)) | |
| return 1 | |
| print(f"matches run_example.expected.json ({a.accel})") | |
| return 0 | |
| if __name__ == "__main__": | |
| sys.exit(main()) | |