Atom2.7-Demo / app.py
ucr-max's picture
Update app.py
28105f9 verified
Raw
History Blame Contribute Delete
4.42 kB
import re
import torch
import gradio as gr
from transformers import AutoModelForCausalLM, AutoTokenizer
MODEL_ID = "UniversalComputingResearch/Atom2.7m"
tokenizer = AutoTokenizer.from_pretrained(
MODEL_ID,
trust_remote_code=True,
)
model = AutoModelForCausalLM.from_pretrained(
MODEL_ID,
trust_remote_code=True,
).eval()
device = "cuda" if torch.cuda.is_available() else "cpu"
model.to(device)
print("Tokenizer class:", type(tokenizer))
print("Model class:", type(model))
print("Device:", device)
def decode_output(output_ids):
"""
First try the official tokenizer.decode path.
If the Space displays raw internal byte-level/LSD tokens, repair display only.
"""
text = tokenizer.decode(output_ids, skip_special_tokens=True)
# If normal decode worked, return it.
if "Ġ" not in text and not re.search(r"\d\s+\d", text):
return text
# Fallback renderer for Atom's internal digit-token representation.
# This does not change model behavior; it only fixes display if the Space
# is not applying the custom decode path correctly.
ids = output_ids.tolist() if hasattr(output_ids, "tolist") else list(output_ids)
ids = [int(x) for x in ids]
digit_id_to_char = {}
for digit in "0123456789":
token_id = tokenizer.convert_tokens_to_ids(digit)
if token_id is not None and token_id != tokenizer.unk_token_id:
digit_id_to_char[int(token_id)] = digit
special_ids = set(tokenizer.all_special_ids)
pieces = []
text_buffer = []
digit_buffer = []
def flush_text():
nonlocal text_buffer
if text_buffer:
part = tokenizer.backend_tokenizer.decode(
text_buffer,
skip_special_tokens=True,
)
part = part.replace("Ġ", " ")
pieces.append(part)
text_buffer = []
def flush_digits():
nonlocal digit_buffer
if digit_buffer:
pieces.extend(reversed(digit_buffer))
digit_buffer = []
for token_id in ids:
if token_id in special_ids:
continue
if token_id in digit_id_to_char:
flush_text()
digit_buffer.append(digit_id_to_char[token_id])
else:
flush_digits()
text_buffer.append(token_id)
flush_text()
flush_digits()
return "".join(pieces).strip()
def generate(prompt, max_new_tokens, temperature, do_sample):
if not prompt.strip():
return "Enter a prompt first."
inputs = tokenizer(
prompt,
return_tensors="pt",
add_special_tokens=False,
)
inputs = {k: v.to(device) for k, v in inputs.items()}
generation_kwargs = {
**inputs,
"max_new_tokens": int(max_new_tokens),
"do_sample": bool(do_sample),
}
if do_sample:
generation_kwargs["temperature"] = float(temperature)
with torch.no_grad():
output_ids = model.generate(**generation_kwargs)
return decode_output(output_ids[0])
examples = [
["12 + 34 =", 3, 1.0, False],
["7 + 8 =", 3, 1.0, False],
["25 - 9 =", 3, 1.0, False],
["5 * 11 =", 3, 1.0, False],
["The capital of France is", 12, 0.8, True],
]
description = """
Atom2.7m is a tiny causal language model for text continuation, with arithmetic-aware handling for numeric spans.
It is not an instruction-tuned chatbot. It works best with short continuation prompts such as `12 + 34 =`.
For arithmetic, greedy decoding with a small number of new tokens usually works best.
"""
demo = gr.Interface(
fn=generate,
inputs=[
gr.Textbox(
label="Prompt",
value="12 + 34 =",
lines=3,
),
gr.Slider(
minimum=1,
maximum=32,
value=3,
step=1,
label="Max new tokens",
),
gr.Slider(
minimum=0.1,
maximum=2.0,
value=1.0,
step=0.1,
label="Temperature",
),
gr.Checkbox(
value=False,
label="Sample instead of greedy decoding",
),
],
outputs=gr.Textbox(
label="Model output",
lines=6,
),
title="Atom2.7m Arithmetic Demo",
description=description,
examples=examples,
allow_flagging="never",
)
if __name__ == "__main__":
demo.launch()