Downloads · 30 days
7
1% of all-time downloads
center-for-humans-and-machines
center-for-humans-and-machines/end_token
end_token is a machine learning model from center-for-humans-and-machines. Use it for the machine learning task on the model card, and read the license before you ship it in a product. The card lists the license as apache-2.0.
Qwen2.5-3B-Instruct, fully fine-tuned to notice when a conversation has collapsed into repetition, say so by emitting a special token <end, and then change the subject to something substantive.
Downloads · 30 days
7
1% of all-time downloads
All-time downloads
662
Public
Parameters
3.1B
6.2 GB on disk
Likes
0
Public
Click a slice to open those files.
.safetensors6.2 GB · 100%
From the Hugging Face model README
Qwen2.5-3B-Instruct, fully fine-tuned to notice when a conversation has collapsed
into repetition, say so by emitting a special token <end>, and then change the
subject to something substantive.
Built at the Center for Humans and Machines, Max Planck Institute for Human Development.
A language model left generating against its own output stops producing anything new, reliably and quickly.
This matters anywhere model output feeds back into model input: agent loops, synthetic data generation, multi-agent systems. Those setups currently have no signal that they have stopped producing information.
This model provides the signal.
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
tok = AutoTokenizer.from_pretrained("center-for-humans-and-machines/end_token")
model = AutoModelForCausalLM.from_pretrained(
"center-for-humans-and-machines/end_token", dtype=torch.bfloat16).to("cuda")
messages = [ # the last ~5 turns of your conversation
{"role": "user", "content": "..."},
{"role": "assistant", "content": "..."},
# ...
]
ids = tok.apply_chat_template(messages, add_generation_prompt=True,
return_tensors="pt").to(model.device)
out = model.generate(ids, max_new_tokens=200, do_sample=True,
temperature=1.0, top_p=0.8)
# IMPORTANT: skip_special_tokens=False, or <end> is deleted from the output
text = tok.decode(out[0][ids.shape[-1]:], skip_special_tokens=False)
fired = "<end>" in text
1. skip_special_tokens=False is required. <end> is a special token (id
151665). Every decoder strips special tokens by default, so the model fires and
you see nothing. If you serve it with vLLM, pass
extra_body={"skip_special_tokens": False} on the request.
2. Feed it about 5 turns. It was trained on 5-turn windows. A loop is not visible in one turn, and with a much longer window the input drifts out of distribution: in our tests, a 16-turn window dropped firing to 0 out of 17 conversations while the conversations still collapsed.
tokenizer_config.json was written by transformers 5.x, which records added
special tokens under extra_special_tokens as a list. transformers 4.x expects a
dict there and raises AttributeError: 'list' object has no attribute 'keys'.
The file is deliberately left as saved, so that the published weights and config are byte-identical to the artifact that produced the reported results. If you are on 4.x, rewrite the key locally:
import json
from huggingface_hub import snapshot_download
d = snapshot_download("center-for-humans-and-machines/end_token")
p = f"{d}/tokenizer_config.json"
c = json.load(open(p))
c["additional_special_tokens"] = list(c.pop("extra_special_tokens", []))
json.dump(c, open(p, "w"), indent=2)
Verified behaviour-preserving: with and without this change, 60 real training rows
tokenize to identical id sequences, and <end> remains id 151665.
| base | Qwen/Qwen2.5-3B-Instruct |
| method | full fine-tune (not LoRA) |
| data | end_token_data, method_a_v6_L3.jsonl, 8,504 rows |
| epochs / lr | 3 / 1e-5, cosine, warmup 0.03 |
| batch | 2 x 8 grad-accum (effective 16), max length 4096 |
| hardware | one H200, about 30 minutes |
The training signal is a multi-turn loss mask: each example is a whole
conversation arc (collapsed window, then <end> plus a new topic, then several
turns continuing it) and the loss covers only the model's OWN turns. Grading the
other side's turns teaches the model to write both speakers, which produces
turn-boundary leakage.
Data composition: 4,530 collapsed windows confirmed as looping by a separate 31B judge; 2,057 healthy conversations generated by the same base model and judge-verified; 414 human chat logs; 1,503 continuation rows carrying the post-escape stretches.
Healthy examples are generated rather than mined because mined negatives from a different model let an earlier version separate the classes by writing style instead of by looping: a one-character rule scored 86%.
It cannot rescue a conversation indefinitely. Run to 500 turns, every conversation we tested ended in repetition or silence. In one, the model announced a change of subject five times in a row and each time changed to the same subject, then emitted a single full stop for the remaining 424 turns. This is consistent with the theory: a closed loop cannot generate the novelty it needs (see Kong et al. on semantic collapse, and the Data Processing Inequality).