Views
No views yet

1import torch
2from transformers import AutoModelForCausalLM, AutoTokenizer, GenerationConfig
3
4model = AutoModelForCausalLM.from_pretrained("tomg-group-umd/huginn-0125", torch_dtype=torch.bfloat16, trust_remote_code=True)
5tokenizer = AutoTokenizer.from_pretrained("tomg-group-umd/huginn-0125")num_steps, the model will execute a forward pass with that amount of compute:1input_ids = tokenizer.encode("The capital of Westphalia is", return_tensors="pt", add_special_tokens=True).to(device)
2model.eval()
3model.to(device)
4
5model(input_ids, num_steps=32)num_steps * 1.5B + 2B. Playing with this parameter is what makes this model interesting, and different from fixed-depth transformers!
The model is trained to accept an arbitrary number of steps. However, using fewer than 4 steps will result in very coarse answers. If given enough context to reason about, benchmarks show the model improving up to around num_steps=64. Beyond that, more steps generally do not hurt, but we see no further improvements.bfloat16 to run inference (or AMP bfloat16-mixed precision, if you really want). All benchmarks were evaluated in pure bfloat16.num_steps directly to the generate call, for example:model.eval()
config = GenerationConfig(max_length=256, stop_strings=["<|end_text|>", "<|end_turn|>"],
use_cache=True,
do_sample=False, temperature=None, top_k=None, top_p=None, min_p=None,
return_dict_in_generate=True,
eos_token_id=65505,bos_token_id=65504,pad_token_id=65509)
input_ids = tokenizer.encode("The capital of Westphalia is", return_tensors="pt", add_special_tokens=True).to(device)
outputs = model.generate(input_ids, config, tokenizer=tokenizer, num_steps=16)num_steps and other model arguments CANNOT be included in the GenerationConfig, they will shadow model args at runtime.messages = []
messages.append({"role": "system", "content" : "You are a helpful assistant."})
messages.append({"role": "user", "content" : "What do you think of Goethe's Faust?"})
chat_input = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
print(chat_input)
input_ids = tokenizer.encode(chat_input, return_tensors="pt", add_special_tokens=False).to(device)
model.generate(input_ids, config, num_steps=64, tokenizer=tokenizer)HuginnDynamicCache, otherwise the KV-caches of later calls to the recurrent block will overwrite the earlier ones.
The current implementation will always try to inject this Cache implementation, but that may break with huggingface updates. If you do not use generate, but implement your own generation, use a pattern like this:1# first step:
2past_key_values = None
3outputs = model(input_ids=input_ids, use_cache=True, past_key_values=past_key_values)
4past_key_values = outputs.past_key_values # Should be an instance of HuginnDynamicCache
5# next step
6outputs = model(input_ids=input_ids, use_cache=True, past_key_values=past_key_values)entropy-diff, latent-diff,kl and argmax-stability, via criterion=.... The exit threshold can be modified via exit_threshold=5e-4.
We suggest using kl for interesting exits and argmax-stability for conservative exits. Note that using these variables overrides the default generation function. Not all arguments that are valid for the normal generate call are valid here. To make this more explicit, you can also directly call generate_with_adaptive_compute:1from transformers import TextStreamer
2streamer = TextStreamer(tokenizer)
3
4model.generate_with_adaptive_compute(input_ids, config, num_steps=64, tokenizer=tokenizer, streamer=streamer,
5 continuous_compute=False, criterion="kl", exit_threshold=5e-4, cache_kwargs={"lookup_strategy": "latest-m4"})
6"latest-m4" if using adaptive compute.lookup_strategy to include compress-s16 (where the last number determine the size of the cache).model.generate_with_adaptive_compute(input_ids, config, num_steps=64, tokenizer=tokenizer, streamer=streamer,
continuous_compute=False, cache_kwargs={"lookup_strategy": "compress-s16"})latest-m4-compress-s16.continuous_compute=True, like somodel.generate_with_adaptive_compute(input_ids, config, num_steps=64, tokenizer=tokenizer, streamer=streamer, continuous_compute=True)@article{geiping_scaling_2025,
title = {Scaling up {{Test-Time Compute}} with {{Latent Reasoning}}: {{A Recurrent Depth Approach}}},
shorttitle = {Scaling up {{Test-Time Compute}} with {{Latent Reasoning}}},
author = {Geiping, Jonas and McLeish, Sean and Jain, Neel and Kirchenbauer, John and Singh, Siddharth and Bartoldson, Brian R. and Kailkhura, Bhavya and Bhatele, Abhinav and Goldstein, Tom},
year = {2025},
month = feb,
eprint = {2502.05171},
primaryclass = {cs},
publisher = {arXiv},
doi = {10.48550/arXiv.2502.05171},
url = {http://arxiv.org/abs/2502.05171},
urldate = {2025-02-10},
archiveprefix = {arXiv},
keywords = {Computer Science - Computation and Language,Computer Science - Machine Learning},
journal = {arxiv:2502.05171[cs]}
}