Views
No views yet
Unbabel/TowerInstruct-7B-v0.2.Unbabel/TowerInstruct-7B-v0.2 (frozen during training)torch.nn.Embedding(vocab_size, hidden_size) applied to the last hidden state[32007, 4096]model.safetensors — the trained head weights (single tensor weight).config.json — SigmoidHeadConfig (vocab/hidden sizes + auto_map).sigmoid_head.py — SigmoidHead(PreTrainedModel) definition; auto-loaded by transformers via trust_remote_code=True.transformers.AutoModel. Pass trust_remote_code=True
so transformers downloads sigmoid_head.py from this repo automatically.1import torch
2from transformers import AutoModel, AutoModelForCausalLM, AutoTokenizer
3
4BASE = "Unbabel/TowerInstruct-7B-v0.2"
5HEAD = "tuanh23/SigmoidHead-TowerInstruct-7B-v0.2"
6device = "cuda" if torch.cuda.is_available() else "cpu"
7
8tokenizer = AutoTokenizer.from_pretrained(BASE)
9base_model = AutoModelForCausalLM.from_pretrained(BASE, torch_dtype=torch.bfloat16).to(device).eval()
10head = AutoModel.from_pretrained(HEAD, trust_remote_code=True).to(device).eval()
11
12# Same chat-template format the head was trained on (see prepare_data.py).
13src_lang, tgt_lang = "English", "German"
14src = "The cat sat on the mat."
15hypothesis = "Die Katze saß auf der Matte."
16user_msg = {"role": "user", "content": f"Translate the following text from {src_lang} into {tgt_lang}.\n{src_lang}: {src}.\n{tgt_lang}: "}
17asst_msg = {"role": "assistant", "content": " " + hypothesis}
18
19# Full conversation -> input_ids for the model
20input_ids = tokenizer.apply_chat_template(
21 [user_msg, asst_msg], tokenize=True, add_generation_prompt=False, return_tensors="pt"
22).to(device)
23# Same encoding but with the generation prompt added after the user turn -> tells us
24# where the assistant content begins inside `input_ids`.
25prompt_len = tokenizer.apply_chat_template(
26 [user_msg], tokenize=True, add_generation_prompt=True, return_tensors="pt"
27).shape[1]
28
29with torch.no_grad():
30 out = base_model(input_ids, output_hidden_states=True)
31 last_hidden = out.hidden_states[-1].float() # [1, T, hidden]
32 conf_full = head.score(last_hidden) # [1, T, vocab] in (0, 1)
33
34# Per-token confidence for the actual next token at each position (shifted by 1)
35target_ids = input_ids[:, 1:]
36conf = conf_full[:, :-1, :].gather(-1, target_ids.unsqueeze(-1)).squeeze(-1) # [1, T-1]
37
38# Confidence over just the assistant span (hypothesis + closing chat tokens):
39hyp_conf = conf[0, prompt_len - 1:]
40hyp_tokens = tokenizer.convert_ids_to_tokens(input_ids[0, prompt_len:].tolist())
41
42print("Hypothesis:", hypothesis)
43for tok, s in zip(hyp_tokens, hyp_conf.tolist()):
44 print(f" {tok!r:>20s} conf={s:.4f}")
45print(f"Sentence-level (mean): {hyp_conf.mean().item():.4f}")
46
47# Expected output:
48# Hypothesis: Die Katze saß auf der Matte.
49# '▁Die' conf=0.9999
50# '▁Kat' conf=0.9995
51# 'ze' conf=0.9992
52# '▁sa' conf=0.9993
53# 'ß' conf=1.0000
54# '▁auf' conf=1.0000
55# '▁der' conf=0.9983
56# '▁Mat' conf=0.9992
57# 'te' conf=0.9999
58# '.' conf=0.9897
59# '<|im_end|>' conf=1.0000
60# '▁' conf=1.0000
61# '<0x0A>' conf=1.0000
62# Sentence-level (mean): 0.9988transformers.generate
already returns when you ask for them. So you can generate with the base LM and
score with the sigmoid head in one forward pass — no re-decoding.1import torch
2from transformers import AutoModel, AutoModelForCausalLM, AutoTokenizer
3
4BASE = "Unbabel/TowerInstruct-7B-v0.2"
5HEAD = "tuanh23/SigmoidHead-TowerInstruct-7B-v0.2"
6device = "cuda" if torch.cuda.is_available() else "cpu"
7
8tokenizer = AutoTokenizer.from_pretrained(BASE)
9base_model = AutoModelForCausalLM.from_pretrained(BASE, torch_dtype=torch.bfloat16).to(device).eval()
10head = AutoModel.from_pretrained(HEAD, trust_remote_code=True).to(device).eval()
11
12src_lang, tgt_lang = "English", "German"
13src = "The cat sat on the mat."
14messages = [{"role": "user", "content": f"Translate the following text from {src_lang} into {tgt_lang}.\n{src_lang}: {src}.\n{tgt_lang}: "}]
15input_ids = tokenizer.apply_chat_template(
16 messages, tokenize=True, add_generation_prompt=True, return_tensors="pt"
17).to(device)
18
19with torch.no_grad():
20 gen = base_model.generate(
21 input_ids=input_ids,
22 max_new_tokens=64,
23 do_sample=False, # greedy
24 output_hidden_states=True,
25 return_dict_in_generate=True,
26 )
27 # Stitch together per-step last-layer hidden states into [B, gen_len, hidden].
28 # Step 0 returns hidden states for the whole prompt — keep only the last position.
29 last_hidden = [step[-1] for step in gen.hidden_states]
30 last_hidden[0] = last_hidden[0][:, -1:, :]
31 last_hidden = torch.cat(last_hidden, dim=1).float() # [B, gen_len, hidden]
32
33 gen_ids = gen.sequences[:, input_ids.shape[1]:] # [B, gen_len]
34 conf_full = head.score(last_hidden) # [B, gen_len, vocab] in (0, 1)
35 conf = conf_full.gather(-1, gen_ids.unsqueeze(-1)).squeeze(-1) # [B, gen_len]
36
37translation = tokenizer.decode(gen_ids[0], skip_special_tokens=True)
38print("Translation:", translation)
39for tok, s in zip(tokenizer.convert_ids_to_tokens(gen_ids[0].tolist()), conf[0].tolist()):
40 print(f" {tok!r:>20s} conf={s:.4f}")
41print(f"Sentence-level (mean): {conf[0].mean().item():.4f}")
42
43# Expected output:
44# Translation: Die Katze saß auf der Matte.
45# '▁Die' conf=0.9999
46# '▁Kat' conf=0.9994
47# 'ze' conf=0.9991
48# '▁sa' conf=0.9993
49# 'ß' conf=1.0000
50# '▁auf' conf=1.0000
51# '▁der' conf=0.9983
52# '▁Mat' conf=0.9992
53# 'te' conf=0.9999
54# '.' conf=0.9900
55# '<|im_end|>' conf=1.0000
56# Sentence-level (mean): 0.9986@article{dinh2026sigmoid,
title = {Sigmoid Head for Quality Estimation under Language Ambiguity},
author = {Dinh, Tu Anh and Niehues, Jan},
journal = {arXiv preprint arXiv:2601.00680},
year = {2026}
}