Download typed_decisions.py from semgram/decider-75M: direct link, hf CLI and curl.
- Browser
- Download file 2.44 kB
-
https://huggingface.co/semgram/decider-75M/resolve/main/typed_decisions.py
- Command line
-
hf download hf://semgram/decider-75M/typed_decisions.py
-
curl -L -o typed_decisions.py https://huggingface.co/semgram/decider-75M/resolve/main/typed_decisions.py
2.44 kB
| """Load and run the model in this folder: per question, a probability distribution over its options. | |
| from typed_decisions import TypedDecisions | |
| m = TypedDecisions.load(".") # this folder, or a downloaded snapshot of it | |
| m.predict("invoice_processing", state_dict_or_json) | |
| # {"discrepancy_severity": {"0": 0.71, "1": 0.22, ...}, "disposition": {...}, ...} | |
| """ | |
| from __future__ import annotations | |
| import json | |
| from pathlib import Path | |
| import torch | |
| from safetensors.torch import load_file | |
| from torch import nn | |
| from transformers import AutoConfig, AutoModel, AutoTokenizer | |
| class TypedDecisions(nn.Module): | |
| def __init__(self, folder: Path, cfg: dict): | |
| super().__init__() | |
| enc_cfg = AutoConfig.from_pretrained(folder) | |
| self.enc = AutoModel.from_config(enc_cfg) | |
| h = enc_cfg.hidden_size | |
| self.schema: dict[str, dict[str, list[str]]] = cfg["schema"] | |
| self.heads = nn.ModuleDict({f"{wf}/{q}": nn.Linear(h, len(o)) for wf, qs in self.schema.items() for q, o in qs.items()}) | |
| self.max_len = cfg["max_len"] | |
| self.tok = AutoTokenizer.from_pretrained(folder) | |
| def load(cls, folder: str | Path, device: str = "cuda" if torch.cuda.is_available() else "cpu") -> "TypedDecisions": | |
| folder = Path(folder) | |
| m = cls(folder, json.loads((folder / "typed_decisions.json").read_text())) | |
| m.load_state_dict(load_file(str(folder / "model.safetensors"))) | |
| return m.to(device).eval() | |
| def predict(self, workflow: str, state: dict | str) -> dict[str, dict[str, float]]: | |
| """The input is the workflow name, a newline, then the state as compact JSON (as in training).""" | |
| s = json.dumps(json.loads(state) if isinstance(state, str) else state, ensure_ascii=False) | |
| dev = next(self.parameters()).device | |
| enc = self.tok([f"{workflow}\n{s}"], truncation=True, max_length=self.max_len, return_tensors="pt").to(dev) | |
| with torch.autocast(dev.type, dtype=torch.bfloat16, enabled=dev.type == "cuda"): | |
| hs = self.enc(**enc).last_hidden_state | |
| m = enc.attention_mask.unsqueeze(-1).to(hs.dtype) | |
| z = ((hs * m).sum(1) / m.sum(1)).float() | |
| return {q: dict(zip(opts, torch.softmax(self.heads[f"{workflow}/{q}"](z)[0], -1).tolist())) | |
| for q, opts in self.schema[workflow].items()} | |