Text Classification
Transformers
Safetensors
English
qwen3
text-generation
qwen
decision-model
finance
wealth-management
sec-filings
news
calibration
lora
jev
text-embeddings-inference
Instructions to use ternary/Arbiter-8B with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use ternary/Arbiter-8B with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-classification", model="ternary/Arbiter-8B")# pip install -U transformers accelerate # Load model directly from transformers import AutoTokenizer, AutoModelForCausalLM tokenizer = AutoTokenizer.from_pretrained("ternary/Arbiter-8B") model = AutoModelForCausalLM.from_pretrained("ternary/Arbiter-8B", device_map="auto") - Notebooks
- Google Colab
- Kaggle
Download arbiter.py from ternary/Arbiter-8B: direct link, hf CLI and curl.
- Browser
- Download file 4.5 kB
-
https://huggingface.co/ternary/Arbiter-8B/resolve/main/arbiter.py
- Command line
-
hf download hf://ternary/Arbiter-8B/arbiter.py
-
curl -L -o arbiter.py https://huggingface.co/ternary/Arbiter-8B/resolve/main/arbiter.py
4.5 kB
| """Arbiter: load the model from this repository and answer typed decisions. | |
| from arbiter import Arbiter | |
| from questions import OUTLOOK | |
| arbiter = Arbiter.from_pretrained("ternary/Arbiter-8B") | |
| answer = arbiter.decide(state="...article or 10-K passage...", question=OUTLOOK, focus="Meta Platforms") | |
| print(answer["choice"], answer["probabilities"]) | |
| One forward pass per decision; the answer carries a probability for every option, calibrated with the | |
| per-type temperatures in calibration.json. Every fallback (state truncated to fit, uncalibrated | |
| temperature) is counted in `arbiter.fallback_counts`. | |
| """ | |
| from __future__ import annotations | |
| import json | |
| from collections import Counter | |
| from pathlib import Path | |
| from typing import Any | |
| from model import QUESTION_TYPES, jev_answer, load_decision_model, option_logits, probabilities, question_type | |
| class Arbiter: | |
| def __init__(self, model, tokenizer, temperatures: dict[str, float], max_length: int, counter: Counter): | |
| self.model, self.tokenizer = model, tokenizer | |
| self.temperatures, self.max_length = temperatures, max_length | |
| self.fallback_counts = counter | |
| def from_pretrained(cls, path_or_repo: str = "ternary/Arbiter-8B", adapter: bool = False, | |
| quantization: str | None = None, max_memory: str | None = None) -> "Arbiter": | |
| """Load from a local folder or a Hugging Face repo id. | |
| Default: the full model (LoRA merged into the weights, fp16/bf16, no base download). | |
| adapter=True: the LoRA in adapter/ on Qwen/Qwen3-8B, 4-bit as trained (6.5 GB GPU memory measured). | |
| quantization overrides the default ('none' or '4bit').""" | |
| from safetensors.torch import load_file | |
| path = Path(path_or_repo) | |
| if not path.exists(): | |
| from huggingface_hub import snapshot_download | |
| path = Path(snapshot_download(path_or_repo)) | |
| meta = json.loads((path / "metadata.json").read_text()) | |
| head_config = json.loads((path / "head_config.json").read_text()) | |
| counter: Counter = Counter() | |
| if (path / "config.json").exists() and not adapter: | |
| # Full model: the LoRA is merged into the weights in this folder; no base download, no peft. | |
| model_id, adapter_dir, default_quantization = str(path), None, "none" | |
| calibration = path / "calibration.json" | |
| else: | |
| # Adapter on the base model, 4-bit as trained. | |
| model_id, adapter_dir, default_quantization = meta["base_model"], path / "adapter", meta["quantization"] | |
| calibration = path / "adapter" / "calibration.json" | |
| if not calibration.exists(): | |
| calibration = path / "calibration.json" | |
| model, tokenizer, letter_ids, _ = load_decision_model( | |
| model_id, quantization=quantization or default_quantization, max_memory=max_memory, | |
| adapter_dir=adapter_dir, counter=counter) | |
| if letter_ids != head_config["letter_ids"]: | |
| raise ValueError("this tokenizer's letter ids differ from the ones the head was trained with") | |
| model.head.load_state_dict(load_file(str(path / "decision_head.safetensors"))) | |
| model.head.to(next(model.backbone.parameters()).device) | |
| temperatures = {kind: 1.0 for kind in QUESTION_TYPES} | |
| if calibration.exists(): | |
| temperatures.update(json.loads(calibration.read_text())["temperatures"]) | |
| else: | |
| counter["uncalibrated_temperature_1"] += 1 | |
| return cls(model, tokenizer, temperatures, meta["max_length"], counter) | |
| def decide(self, state: str, question: dict[str, Any], focus: str | None = None) -> dict[str, Any]: | |
| """question: {"type": "choice"|"noul"|"score", "instructions": str, "criteria": {option_id: definition}}. | |
| noul criteria are {"true": ..., "false": ...}; score criteria are {"0": lowest, "1": ..., ...}.""" | |
| labels = ["true", "false"] if question["type"] == "noul" else list(question["criteria"]) | |
| row = {"state": state, "question": question, "labels": labels, | |
| "focus": {"surface": focus} if focus else {}} | |
| logits, truncated = option_logits(self.model, self.tokenizer, row, self.max_length, self.fallback_counts) | |
| probs = probabilities(logits, self.temperatures[question_type(row)]) | |
| answer = jev_answer(row, probs) | |
| answer["probabilities"] = probs | |
| answer["state_truncated"] = truncated | |
| return answer | |