ANKA-50M-RMW3 / inference.py
Melih1234's picture
Release ANKA-50M RMW3 base 5B checkpoint
9204f7b verified
Raw History Blame Contribute Delete
3.95 kB
"""Load ANKA-50M RMW3 and generate text with the released checkpoint."""
from __future__ import annotations
import argparse
import json
from pathlib import Path
import torch
from safetensors.torch import load_file
from tokenizers import Tokenizer
from modeling_anka import RMW3, RMW3Config, count_parameters
def load_model(model_dir: str | Path, device: str | None = None):
model_dir = Path(model_dir)
device = device or ("cuda" if torch.cuda.is_available() else "cpu")
config_data = json.loads((model_dir / "config.json").read_text())
config = RMW3Config(
**{
field: config_data[field]
for field in RMW3Config.__dataclass_fields__
}
)
model = RMW3(config)
dtype = torch.bfloat16 if device.startswith("cuda") else torch.float32
model = model.to(device=device, dtype=dtype)
state_dict = load_file(str(model_dir / "model.safetensors"), device="cpu")
model.load_state_dict(state_dict, strict=True)
model.eval()
if count_parameters(model) != 48_944_657:
raise RuntimeError("Unexpected parameter count")
if model.lm_head.weight.data_ptr() != model.token_embeddings.weight.data_ptr():
raise RuntimeError("Embedding/output weight tying is not active")
tokenizer = Tokenizer.from_file(str(model_dir / "tokenizer.json"))
return model, tokenizer
@torch.inference_mode()
def generate(
model: RMW3,
tokenizer: Tokenizer,
prompt: str,
max_new_tokens: int = 128,
temperature: float = 0.8,
top_p: float = 0.95,
seed: int = 3407,
) -> str:
device = next(model.parameters()).device
token_ids = tokenizer.encode(prompt, add_special_tokens=False).ids or [1]
generated: list[int] = []
generator = torch.Generator(device=device).manual_seed(seed)
for _ in range(max_new_tokens):
active = (token_ids + generated)[-model.config.max_seq_len :]
input_ids = torch.tensor([active], device=device, dtype=torch.long)
with torch.autocast(
device_type=device.type,
dtype=torch.bfloat16,
enabled=device.type == "cuda",
):
logits, _ = model(input_ids)
next_logits = logits[0, -1].float()
if temperature <= 0:
next_id = int(next_logits.argmax())
else:
probabilities = torch.softmax(next_logits / temperature, dim=-1)
if top_p < 1.0:
sorted_probs, sorted_ids = torch.sort(probabilities, descending=True)
remove = (torch.cumsum(sorted_probs, dim=-1) - sorted_probs) > top_p
sorted_probs[remove] = 0
sorted_probs /= sorted_probs.sum().clamp_min(1e-12)
sampled = torch.multinomial(sorted_probs, 1, generator=generator)
next_id = int(sorted_ids[sampled].item())
else:
next_id = int(torch.multinomial(probabilities, 1, generator=generator))
if next_id == 2:
break
generated.append(next_id)
completion = tokenizer.decode(generated, skip_special_tokens=True)
return prompt + completion
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--model-dir", type=Path, default=Path(__file__).parent)
parser.add_argument("--prompt", default="The future of artificial intelligence")
parser.add_argument("--max-new-tokens", type=int, default=128)
parser.add_argument("--temperature", type=float, default=0.8)
parser.add_argument("--top-p", type=float, default=0.95)
parser.add_argument("--seed", type=int, default=3407)
args = parser.parse_args()
model, tokenizer = load_model(args.model_dir)
print(
generate(
model,
tokenizer,
args.prompt,
args.max_new_tokens,
args.temperature,
args.top_p,
args.seed,
)
)
if __name__ == "__main__":
main()