Download scripts/helpfulness_scheme_interface.py from sheng22213/genarm-h0p01-c0175-eval-code: direct link, hf CLI and curl.
- Browser
- Download file 35.3 kB
-
https://huggingface.co/sheng22213/genarm-h0p01-c0175-eval-code/resolve/main/scripts/helpfulness_scheme_interface.py
- Command line
-
hf download hf://sheng22213/genarm-h0p01-c0175-eval-code/scripts/helpfulness_scheme_interface.py
-
curl -L -o helpfulness_scheme_interface.py https://huggingface.co/sheng22213/genarm-h0p01-c0175-eval-code/resolve/main/scripts/helpfulness_scheme_interface.py
35.3 kB
| import json | |
| import math | |
| from dataclasses import dataclass | |
| from pathlib import Path | |
| import torch | |
| import torch.nn.functional as F | |
| from peft import AutoPeftModelForCausalLM | |
| from transformers import AutoModelForCausalLM, AutoTokenizer | |
| ROOT = Path("/scratch/users/nus/e1538883/genarm-exp/temp/pku_safe_rlhf_repro") | |
| FIRST_ENTRIES = ROOT / "gpt-evaluation" / "analysis_helpfulness_len_norm" / "dataset_first_entries.json" | |
| BASE_MODEL = "/scratch/users/nus/e1538883/huggingface/alpaca-7b-reproduced" | |
| OLD_HELPFUL = ROOT / "checkpoints" / "helpfulness" / "arm_beta_0p5" / "final_checkpoint" | |
| NEW_LEN_NORM_HELPFUL = ROOT / "checkpoints" / "helpfulness" / "arm_beta_0p5_len_norm_only" / "final_checkpoint" | |
| class SchemeAConfig: | |
| base_prob_threshold: float = 0.01 | |
| topk: int = 10 | |
| steps: int = 5 | |
| prompt: str | None = None | |
| response: str | None = None | |
| adapter_path: str | None = None | |
| class SchemeBConfig: | |
| topk: int = 10 | |
| steps: int = 5 | |
| prompt: str | None = None | |
| response: str | None = None | |
| adapter_path: str | None = None | |
| class PrefixOnlyConfig: | |
| topk: int = 10 | |
| steps: int = 25 | |
| prompt: str | None = None | |
| response: str | None = None | |
| adapter_path: str | None = None | |
| class X1Config: | |
| tau_p: float = 0.8 | |
| tau_g: float = 0.5 | |
| class SchemeBTempSupportConfig: | |
| support_temperature: float = 2.0 | |
| alpha: float = 1.0 | |
| topk: int = 10 | |
| steps: int = 25 | |
| prompt: str | None = None | |
| response: str | None = None | |
| adapter_path: str | None = None | |
| def load_first_helpfulness_winner_pair() -> dict: | |
| obj = json.load(open(FIRST_ENTRIES, "r", encoding="utf-8")) | |
| return obj["helpfulness_training_first_formatted"] | |
| def load_base_and_helpful_models(adapter_path: str | Path | None = None): | |
| tokenizer = AutoTokenizer.from_pretrained(BASE_MODEL) | |
| if tokenizer.pad_token_id is None: | |
| tokenizer.pad_token = tokenizer.eos_token | |
| base_model = AutoModelForCausalLM.from_pretrained( | |
| BASE_MODEL, | |
| torch_dtype=torch.bfloat16, | |
| device_map="auto", | |
| trust_remote_code=True, | |
| ) | |
| base_model.eval() | |
| arm_model = AutoPeftModelForCausalLM.from_pretrained( | |
| str(adapter_path or OLD_HELPFUL), | |
| torch_dtype=torch.bfloat16, | |
| device_map="auto", | |
| trust_remote_code=True, | |
| ) | |
| arm_model.eval() | |
| return tokenizer, base_model, arm_model | |
| def load_base_and_old_helpful_models(): | |
| return load_base_and_helpful_models(OLD_HELPFUL) | |
| def _resolve_prompt_and_response(prompt: str | None, response: str | None) -> tuple[str, str]: | |
| if prompt is None or response is None: | |
| row = load_first_helpfulness_winner_pair() | |
| return row["prompt"], row["chosen"] | |
| return prompt, response | |
| def _forward_last_logprobs(model, input_ids: list[int]) -> torch.Tensor: | |
| device = next(model.parameters()).device | |
| tensor = torch.tensor([input_ids], device=device) | |
| with torch.no_grad(): | |
| logits = model(input_ids=tensor, use_cache=False).logits[0, -1].float().cpu() | |
| return F.log_softmax(logits, dim=-1) | |
| def _top_rows_from_allowed( | |
| values: torch.Tensor, | |
| allowed_ids: torch.Tensor, | |
| tokenizer, | |
| gold_id: int, | |
| topk: int, | |
| ) -> list[dict]: | |
| allowed_vals = values[allowed_ids] | |
| k = min(topk, allowed_ids.numel()) | |
| top_vals, top_idx = torch.topk(allowed_vals, k=k) | |
| top_ids = allowed_ids[top_idx] | |
| rows = [] | |
| seen_gold = False | |
| for tok_id, val in zip(top_ids.tolist(), top_vals.tolist()): | |
| is_gold = int(tok_id) == int(gold_id) | |
| seen_gold = seen_gold or is_gold | |
| rows.append( | |
| { | |
| "token_id": int(tok_id), | |
| "token_text": tokenizer.decode([int(tok_id)]), | |
| "value": float(val), | |
| "is_gold": bool(is_gold), | |
| "gold_in_scope": True, | |
| } | |
| ) | |
| if not seen_gold: | |
| rows.append( | |
| { | |
| "token_id": int(gold_id), | |
| "token_text": tokenizer.decode([int(gold_id)]), | |
| "value": float(values[int(gold_id)].item()), | |
| "is_gold": True, | |
| "gold_in_scope": False, | |
| } | |
| ) | |
| return rows | |
| def _score_rows_from_allowed( | |
| values: torch.Tensor, | |
| allowed_ids: torch.Tensor, | |
| tokenizer, | |
| gold_id: int, | |
| topk: int, | |
| ) -> list[dict]: | |
| allowed_vals = values[allowed_ids] | |
| k = min(topk, allowed_ids.numel()) | |
| top_vals, top_idx = torch.topk(allowed_vals, k=k) | |
| top_ids = allowed_ids[top_idx] | |
| rows = [] | |
| seen_gold = False | |
| for tok_id, val in zip(top_ids.tolist(), top_vals.tolist()): | |
| is_gold = int(tok_id) == int(gold_id) | |
| seen_gold = seen_gold or is_gold | |
| rows.append( | |
| { | |
| "token_id": int(tok_id), | |
| "token_text": tokenizer.decode([int(tok_id)]), | |
| "value": float(val), | |
| "is_gold": bool(is_gold), | |
| "gold_in_scope": True, | |
| } | |
| ) | |
| if not seen_gold: | |
| rows.append( | |
| { | |
| "token_id": int(gold_id), | |
| "token_text": tokenizer.decode([int(gold_id)]), | |
| "value": float(values[int(gold_id)].item()), | |
| "is_gold": True, | |
| "gold_in_scope": False, | |
| } | |
| ) | |
| return rows | |
| def _top_rows_full_vocab( | |
| values: torch.Tensor, | |
| tokenizer, | |
| gold_id: int, | |
| topk: int, | |
| ) -> list[dict]: | |
| top_vals, top_ids = torch.topk(values, k=min(topk, values.numel())) | |
| rows = [] | |
| seen_gold = False | |
| for tok_id, val in zip(top_ids.tolist(), top_vals.tolist()): | |
| is_gold = int(tok_id) == int(gold_id) | |
| seen_gold = seen_gold or is_gold | |
| rows.append( | |
| { | |
| "token_id": int(tok_id), | |
| "token_text": tokenizer.decode([int(tok_id)]), | |
| "value": float(val), | |
| "is_gold": bool(is_gold), | |
| "gold_in_scope": True, | |
| } | |
| ) | |
| if not seen_gold: | |
| rows.append( | |
| { | |
| "token_id": int(gold_id), | |
| "token_text": tokenizer.decode([int(gold_id)]), | |
| "value": float(values[int(gold_id)].item()), | |
| "is_gold": True, | |
| "gold_in_scope": True, | |
| } | |
| ) | |
| return rows | |
| def _sparsemax(scores: torch.Tensor) -> torch.Tensor: | |
| z = scores.float() | |
| z_sorted, _ = torch.sort(z, descending=True) | |
| z_cumsum = torch.cumsum(z_sorted, dim=0) | |
| ks = torch.arange(1, z.numel() + 1, device=z.device, dtype=z.dtype) | |
| support = 1 + ks * z_sorted > z_cumsum | |
| k = int(torch.sum(support).item()) | |
| tau = (z_cumsum[k - 1] - 1) / k | |
| return torch.clamp(z - tau, min=0.0) | |
| def _compute_allowed_ids_scheme_a(prob_base: torch.Tensor, threshold: float) -> torch.Tensor: | |
| allowed_mask = prob_base >= threshold | |
| allowed_ids = torch.nonzero(allowed_mask, as_tuple=False).squeeze(-1) | |
| if allowed_ids.numel() == 0: | |
| allowed_ids = torch.tensor([int(torch.argmax(prob_base).item())], dtype=torch.long) | |
| return allowed_ids | |
| def _compute_allowed_ids_scheme_b(logp_base: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]: | |
| base_sparse_probs = _sparsemax(logp_base) | |
| allowed_mask = base_sparse_probs > 0 | |
| allowed_ids = torch.nonzero(allowed_mask, as_tuple=False).squeeze(-1) | |
| if allowed_ids.numel() == 0: | |
| allowed_ids = torch.tensor([int(torch.argmax(logp_base).item())], dtype=torch.long) | |
| return allowed_ids, base_sparse_probs | |
| def _compute_allowed_ids_scheme_b_temp(logp_base: torch.Tensor, support_temperature: float) -> tuple[torch.Tensor, torch.Tensor]: | |
| if support_temperature <= 0: | |
| raise ValueError(f"support_temperature must be > 0, got {support_temperature}") | |
| scaled_base_sparse_probs = _sparsemax(logp_base / support_temperature) | |
| allowed_mask = scaled_base_sparse_probs > 0 | |
| allowed_ids = torch.nonzero(allowed_mask, as_tuple=False).squeeze(-1) | |
| if allowed_ids.numel() == 0: | |
| allowed_ids = torch.tensor([int(torch.argmax(logp_base).item())], dtype=torch.long) | |
| return allowed_ids, scaled_base_sparse_probs | |
| def _compute_x1_gate(prob_base: torch.Tensor, x1_config: X1Config) -> dict: | |
| top_vals, top_ids = torch.topk(prob_base, k=2) | |
| p1 = float(top_vals[0].item()) | |
| p2 = float(top_vals[1].item()) | |
| non_forking = (p1 >= x1_config.tau_p) or ((p1 - p2) >= x1_config.tau_g) | |
| return { | |
| "non_forking": bool(non_forking), | |
| "p1": p1, | |
| "p2": p2, | |
| "gap": p1 - p2, | |
| "top1_token_id": int(top_ids[0].item()), | |
| "top2_token_id": int(top_ids[1].item()), | |
| } | |
| def _build_score_with_optional_x1( | |
| logp_base: torch.Tensor, | |
| logp_arm: torch.Tensor, | |
| prob_base: torch.Tensor, | |
| x1_config: X1Config | None = None, | |
| ) -> tuple[torch.Tensor, dict]: | |
| if x1_config is None: | |
| return logp_base + logp_arm, { | |
| "x1_enabled": False, | |
| "non_forking": False, | |
| "score_mode": "base_plus_arm", | |
| } | |
| gate = _compute_x1_gate(prob_base, x1_config) | |
| if gate["non_forking"]: | |
| score_s = logp_base | |
| score_mode = "base_only_non_forking" | |
| else: | |
| score_s = logp_base + logp_arm | |
| score_mode = "base_plus_arm_forking" | |
| gate.update( | |
| { | |
| "x1_enabled": True, | |
| "score_mode": score_mode, | |
| "tau_p": x1_config.tau_p, | |
| "tau_g": x1_config.tau_g, | |
| } | |
| ) | |
| return score_s, gate | |
| def scheme_a_supervised_old_helpful(config: SchemeAConfig) -> dict: | |
| tokenizer, base_model, arm_model = load_base_and_helpful_models(config.adapter_path) | |
| prompt, response = _resolve_prompt_and_response(config.prompt, config.response) | |
| prompt_ids = tokenizer(prompt, add_special_tokens=False)["input_ids"] | |
| response_ids = tokenizer(response, add_special_tokens=False)["input_ids"] | |
| steps = [] | |
| for step_idx, gold_id in enumerate(response_ids[: config.steps]): | |
| prefix_ids = response_ids[:step_idx] | |
| full_prefix = prompt_ids + prefix_ids | |
| logp_base = _forward_last_logprobs(base_model, full_prefix) | |
| logp_arm = _forward_last_logprobs(arm_model, full_prefix) | |
| prob_base = torch.exp(logp_base) | |
| allowed_mask = prob_base >= config.base_prob_threshold | |
| allowed_ids = torch.nonzero(allowed_mask, as_tuple=False).squeeze(-1) | |
| if allowed_ids.numel() == 0: | |
| allowed_ids = torch.tensor([int(torch.argmax(prob_base).item())], dtype=torch.long) | |
| score_s = logp_base + logp_arm | |
| filtered_score = score_s[allowed_ids] | |
| filtered_probs = torch.softmax(filtered_score, dim=-1) | |
| prob_arm = torch.exp(logp_arm) | |
| if int(gold_id) in set(allowed_ids.tolist()): | |
| gold_allowed_index = (allowed_ids == int(gold_id)).nonzero(as_tuple=False).item() | |
| gold_final_prob = float(filtered_probs[gold_allowed_index].item()) | |
| gold_in_scope = True | |
| else: | |
| gold_final_prob = 0.0 | |
| gold_in_scope = False | |
| prob_rows = [] | |
| top_prob_vals, top_prob_idx = torch.topk(filtered_probs, k=min(config.topk, allowed_ids.numel())) | |
| top_prob_ids = allowed_ids[top_prob_idx] | |
| seen_gold_prob = False | |
| for tok_id, val in zip(top_prob_ids.tolist(), top_prob_vals.tolist()): | |
| is_gold = int(tok_id) == int(gold_id) | |
| seen_gold_prob = seen_gold_prob or is_gold | |
| prob_rows.append( | |
| { | |
| "token_id": int(tok_id), | |
| "token_text": tokenizer.decode([int(tok_id)]), | |
| "value": float(val), | |
| "is_gold": bool(is_gold), | |
| "gold_in_scope": True, | |
| } | |
| ) | |
| if not seen_gold_prob: | |
| prob_rows.append( | |
| { | |
| "token_id": int(gold_id), | |
| "token_text": tokenizer.decode([int(gold_id)]), | |
| "value": gold_final_prob, | |
| "is_gold": True, | |
| "gold_in_scope": False, | |
| } | |
| ) | |
| score_rows = _score_rows_from_allowed(score_s, allowed_ids, tokenizer, int(gold_id), config.topk) | |
| steps.append( | |
| { | |
| "step_index": step_idx + 1, | |
| "gold_token_id": int(gold_id), | |
| "gold_token_text": tokenizer.decode([int(gold_id)]), | |
| "prefix_response_token_ids": prefix_ids, | |
| "prefix_response_text": tokenizer.decode(prefix_ids), | |
| "allowed_token_count": int(allowed_ids.numel()), | |
| "gold_token_base_prob": float(prob_base[int(gold_id)].item()), | |
| "gold_token_in_scope": gold_in_scope, | |
| "gold_logp_base": float(logp_base[int(gold_id)].item()), | |
| "gold_logp_arm": float(logp_arm[int(gold_id)].item()), | |
| "gold_score_s": float(score_s[int(gold_id)].item()), | |
| "gold_prob_arm": float(prob_arm[int(gold_id)].item()), | |
| "gold_final_prob_after_filter_softmax": gold_final_prob, | |
| "logp_base_top10_allowed": _top_rows_from_allowed(logp_base, allowed_ids, tokenizer, int(gold_id), config.topk), | |
| "logp_arm_top10_allowed": _top_rows_from_allowed(logp_arm, allowed_ids, tokenizer, int(gold_id), config.topk), | |
| "score_s_top10_allowed": score_rows, | |
| "prob_base_top10_allowed": _top_rows_from_allowed(prob_base, allowed_ids, tokenizer, int(gold_id), config.topk), | |
| "prob_arm_top10_allowed": _top_rows_from_allowed(prob_arm, allowed_ids, tokenizer, int(gold_id), config.topk), | |
| "prob_score_top10_allowed": prob_rows, | |
| } | |
| ) | |
| return { | |
| "scheme": "A", | |
| "threshold": config.base_prob_threshold, | |
| "prompt": prompt, | |
| "response": response, | |
| "response_token_count": len(response_ids), | |
| "steps": steps, | |
| } | |
| def scheme_b_supervised_old_helpful(config: SchemeBConfig) -> dict: | |
| tokenizer, base_model, arm_model = load_base_and_helpful_models(config.adapter_path) | |
| prompt, response = _resolve_prompt_and_response(config.prompt, config.response) | |
| prompt_ids = tokenizer(prompt, add_special_tokens=False)["input_ids"] | |
| response_ids = tokenizer(response, add_special_tokens=False)["input_ids"] | |
| steps = [] | |
| for step_idx, gold_id in enumerate(response_ids[: config.steps]): | |
| prefix_ids = response_ids[:step_idx] | |
| full_prefix = prompt_ids + prefix_ids | |
| logp_base = _forward_last_logprobs(base_model, full_prefix) | |
| logp_arm = _forward_last_logprobs(arm_model, full_prefix) | |
| score_s = logp_base + logp_arm | |
| prob_base = torch.exp(logp_base) | |
| prob_arm = torch.exp(logp_arm) | |
| base_sparse_probs = _sparsemax(logp_base) | |
| allowed_mask = base_sparse_probs > 0 | |
| allowed_ids = torch.nonzero(allowed_mask, as_tuple=False).squeeze(-1) | |
| if allowed_ids.numel() == 0: | |
| allowed_ids = torch.tensor([int(torch.argmax(logp_base).item())], dtype=torch.long) | |
| filtered_score = score_s[allowed_ids] | |
| filtered_probs = torch.softmax(filtered_score, dim=-1) | |
| if int(gold_id) in set(allowed_ids.tolist()): | |
| gold_allowed_index = (allowed_ids == int(gold_id)).nonzero(as_tuple=False).item() | |
| gold_final_prob = float(filtered_probs[gold_allowed_index].item()) | |
| gold_in_scope = True | |
| else: | |
| gold_final_prob = 0.0 | |
| gold_in_scope = False | |
| prob_rows = [] | |
| top_prob_vals, top_prob_idx = torch.topk(filtered_probs, k=min(config.topk, allowed_ids.numel())) | |
| top_prob_ids = allowed_ids[top_prob_idx] | |
| seen_gold_prob = False | |
| for tok_id, val in zip(top_prob_ids.tolist(), top_prob_vals.tolist()): | |
| is_gold = int(tok_id) == int(gold_id) | |
| seen_gold_prob = seen_gold_prob or is_gold | |
| prob_rows.append( | |
| { | |
| "token_id": int(tok_id), | |
| "token_text": tokenizer.decode([int(tok_id)]), | |
| "value": float(val), | |
| "is_gold": bool(is_gold), | |
| "gold_in_scope": True, | |
| } | |
| ) | |
| if not seen_gold_prob: | |
| prob_rows.append( | |
| { | |
| "token_id": int(gold_id), | |
| "token_text": tokenizer.decode([int(gold_id)]), | |
| "value": gold_final_prob, | |
| "is_gold": True, | |
| "gold_in_scope": False, | |
| } | |
| ) | |
| score_rows = _score_rows_from_allowed(score_s, allowed_ids, tokenizer, int(gold_id), config.topk) | |
| steps.append( | |
| { | |
| "step_index": step_idx + 1, | |
| "gold_token_id": int(gold_id), | |
| "gold_token_text": tokenizer.decode([int(gold_id)]), | |
| "prefix_response_token_ids": prefix_ids, | |
| "prefix_response_text": tokenizer.decode(prefix_ids), | |
| "allowed_token_count": int(allowed_ids.numel()), | |
| "gold_token_sparsemax_base_weight": float(base_sparse_probs[int(gold_id)].item()), | |
| "gold_token_in_scope": gold_in_scope, | |
| "gold_logp_base": float(logp_base[int(gold_id)].item()), | |
| "gold_logp_arm": float(logp_arm[int(gold_id)].item()), | |
| "gold_score_s": float(score_s[int(gold_id)].item()), | |
| "gold_prob_base": float(prob_base[int(gold_id)].item()), | |
| "gold_prob_arm": float(prob_arm[int(gold_id)].item()), | |
| "gold_final_prob_after_support_softmax": gold_final_prob, | |
| "logp_base_top10_allowed": _top_rows_from_allowed(logp_base, allowed_ids, tokenizer, int(gold_id), config.topk), | |
| "logp_arm_top10_allowed": _top_rows_from_allowed(logp_arm, allowed_ids, tokenizer, int(gold_id), config.topk), | |
| "score_s_top10_allowed": score_rows, | |
| "prob_base_top10_allowed": _top_rows_from_allowed(prob_base, allowed_ids, tokenizer, int(gold_id), config.topk), | |
| "prob_arm_top10_allowed": _top_rows_from_allowed(prob_arm, allowed_ids, tokenizer, int(gold_id), config.topk), | |
| "prob_score_top10_allowed": prob_rows, | |
| } | |
| ) | |
| return { | |
| "scheme": "B", | |
| "support_rule": "sparsemax(logp_base) > 0", | |
| "prompt": prompt, | |
| "response": response, | |
| "response_token_count": len(response_ids), | |
| "steps": steps, | |
| } | |
| def scheme_a_supervised_old_helpful_x1(config: SchemeAConfig, x1_config: X1Config | None = None) -> dict: | |
| tokenizer, base_model, arm_model = load_base_and_helpful_models(config.adapter_path) | |
| prompt, response = _resolve_prompt_and_response(config.prompt, config.response) | |
| prompt_ids = tokenizer(prompt, add_special_tokens=False)["input_ids"] | |
| response_ids = tokenizer(response, add_special_tokens=False)["input_ids"] | |
| x1_config = x1_config or X1Config() | |
| steps = [] | |
| for step_idx, gold_id in enumerate(response_ids[: config.steps]): | |
| prefix_ids = response_ids[:step_idx] | |
| full_prefix = prompt_ids + prefix_ids | |
| logp_base = _forward_last_logprobs(base_model, full_prefix) | |
| logp_arm = _forward_last_logprobs(arm_model, full_prefix) | |
| prob_base = torch.exp(logp_base) | |
| prob_arm = torch.exp(logp_arm) | |
| allowed_ids = _compute_allowed_ids_scheme_a(prob_base, config.base_prob_threshold) | |
| score_s, gate = _build_score_with_optional_x1(logp_base, logp_arm, prob_base, x1_config) | |
| filtered_score = score_s[allowed_ids] | |
| filtered_probs = torch.softmax(filtered_score, dim=-1) | |
| if int(gold_id) in set(allowed_ids.tolist()): | |
| gold_allowed_index = (allowed_ids == int(gold_id)).nonzero(as_tuple=False).item() | |
| gold_final_prob = float(filtered_probs[gold_allowed_index].item()) | |
| gold_in_scope = True | |
| else: | |
| gold_final_prob = 0.0 | |
| gold_in_scope = False | |
| prob_rows = [] | |
| top_prob_vals, top_prob_idx = torch.topk(filtered_probs, k=min(config.topk, allowed_ids.numel())) | |
| top_prob_ids = allowed_ids[top_prob_idx] | |
| seen_gold_prob = False | |
| for tok_id, val in zip(top_prob_ids.tolist(), top_prob_vals.tolist()): | |
| is_gold = int(tok_id) == int(gold_id) | |
| seen_gold_prob = seen_gold_prob or is_gold | |
| prob_rows.append( | |
| { | |
| "token_id": int(tok_id), | |
| "token_text": tokenizer.decode([int(tok_id)]), | |
| "value": float(val), | |
| "is_gold": bool(is_gold), | |
| "gold_in_scope": True, | |
| } | |
| ) | |
| if not seen_gold_prob: | |
| prob_rows.append( | |
| { | |
| "token_id": int(gold_id), | |
| "token_text": tokenizer.decode([int(gold_id)]), | |
| "value": gold_final_prob, | |
| "is_gold": True, | |
| "gold_in_scope": False, | |
| } | |
| ) | |
| step_payload = { | |
| "step_index": step_idx + 1, | |
| "gold_token_id": int(gold_id), | |
| "gold_token_text": tokenizer.decode([int(gold_id)]), | |
| "prefix_response_token_ids": prefix_ids, | |
| "prefix_response_text": tokenizer.decode(prefix_ids), | |
| "allowed_token_count": int(allowed_ids.numel()), | |
| "gold_token_base_prob": float(prob_base[int(gold_id)].item()), | |
| "gold_token_in_scope": gold_in_scope, | |
| "gold_logp_base": float(logp_base[int(gold_id)].item()), | |
| "gold_logp_arm": float(logp_arm[int(gold_id)].item()), | |
| "gold_score_s": float(score_s[int(gold_id)].item()), | |
| "gold_prob_arm": float(prob_arm[int(gold_id)].item()), | |
| "gold_final_prob_after_filter_softmax": gold_final_prob, | |
| "logp_base_top10_allowed": _top_rows_from_allowed(logp_base, allowed_ids, tokenizer, int(gold_id), config.topk), | |
| "logp_arm_top10_allowed": _top_rows_from_allowed(logp_arm, allowed_ids, tokenizer, int(gold_id), config.topk), | |
| "score_s_top10_allowed": _score_rows_from_allowed(score_s, allowed_ids, tokenizer, int(gold_id), config.topk), | |
| "prob_base_top10_allowed": _top_rows_from_allowed(prob_base, allowed_ids, tokenizer, int(gold_id), config.topk), | |
| "prob_arm_top10_allowed": _top_rows_from_allowed(prob_arm, allowed_ids, tokenizer, int(gold_id), config.topk), | |
| "prob_score_top10_allowed": prob_rows, | |
| } | |
| step_payload.update(gate) | |
| steps.append(step_payload) | |
| return { | |
| "scheme": "A", | |
| "plugin": "X1", | |
| "threshold": config.base_prob_threshold, | |
| "prompt": prompt, | |
| "response": response, | |
| "response_token_count": len(response_ids), | |
| "steps": steps, | |
| } | |
| def scheme_b_supervised_old_helpful_x1(config: SchemeBConfig, x1_config: X1Config | None = None) -> dict: | |
| tokenizer, base_model, arm_model = load_base_and_helpful_models(config.adapter_path) | |
| prompt, response = _resolve_prompt_and_response(config.prompt, config.response) | |
| prompt_ids = tokenizer(prompt, add_special_tokens=False)["input_ids"] | |
| response_ids = tokenizer(response, add_special_tokens=False)["input_ids"] | |
| x1_config = x1_config or X1Config() | |
| steps = [] | |
| for step_idx, gold_id in enumerate(response_ids[: config.steps]): | |
| prefix_ids = response_ids[:step_idx] | |
| full_prefix = prompt_ids + prefix_ids | |
| logp_base = _forward_last_logprobs(base_model, full_prefix) | |
| logp_arm = _forward_last_logprobs(arm_model, full_prefix) | |
| prob_base = torch.exp(logp_base) | |
| prob_arm = torch.exp(logp_arm) | |
| allowed_ids, base_sparse_probs = _compute_allowed_ids_scheme_b(logp_base) | |
| score_s, gate = _build_score_with_optional_x1(logp_base, logp_arm, prob_base, x1_config) | |
| filtered_score = score_s[allowed_ids] | |
| filtered_probs = torch.softmax(filtered_score, dim=-1) | |
| if int(gold_id) in set(allowed_ids.tolist()): | |
| gold_allowed_index = (allowed_ids == int(gold_id)).nonzero(as_tuple=False).item() | |
| gold_final_prob = float(filtered_probs[gold_allowed_index].item()) | |
| gold_in_scope = True | |
| else: | |
| gold_final_prob = 0.0 | |
| gold_in_scope = False | |
| prob_rows = [] | |
| top_prob_vals, top_prob_idx = torch.topk(filtered_probs, k=min(config.topk, allowed_ids.numel())) | |
| top_prob_ids = allowed_ids[top_prob_idx] | |
| seen_gold_prob = False | |
| for tok_id, val in zip(top_prob_ids.tolist(), top_prob_vals.tolist()): | |
| is_gold = int(tok_id) == int(gold_id) | |
| seen_gold_prob = seen_gold_prob or is_gold | |
| prob_rows.append( | |
| { | |
| "token_id": int(tok_id), | |
| "token_text": tokenizer.decode([int(tok_id)]), | |
| "value": float(val), | |
| "is_gold": bool(is_gold), | |
| "gold_in_scope": True, | |
| } | |
| ) | |
| if not seen_gold_prob: | |
| prob_rows.append( | |
| { | |
| "token_id": int(gold_id), | |
| "token_text": tokenizer.decode([int(gold_id)]), | |
| "value": gold_final_prob, | |
| "is_gold": True, | |
| "gold_in_scope": False, | |
| } | |
| ) | |
| step_payload = { | |
| "step_index": step_idx + 1, | |
| "gold_token_id": int(gold_id), | |
| "gold_token_text": tokenizer.decode([int(gold_id)]), | |
| "prefix_response_token_ids": prefix_ids, | |
| "prefix_response_text": tokenizer.decode(prefix_ids), | |
| "allowed_token_count": int(allowed_ids.numel()), | |
| "gold_token_sparsemax_base_weight": float(base_sparse_probs[int(gold_id)].item()), | |
| "gold_token_in_scope": gold_in_scope, | |
| "gold_logp_base": float(logp_base[int(gold_id)].item()), | |
| "gold_logp_arm": float(logp_arm[int(gold_id)].item()), | |
| "gold_score_s": float(score_s[int(gold_id)].item()), | |
| "gold_prob_base": float(prob_base[int(gold_id)].item()), | |
| "gold_prob_arm": float(prob_arm[int(gold_id)].item()), | |
| "gold_final_prob_after_support_softmax": gold_final_prob, | |
| "logp_base_top10_allowed": _top_rows_from_allowed(logp_base, allowed_ids, tokenizer, int(gold_id), config.topk), | |
| "logp_arm_top10_allowed": _top_rows_from_allowed(logp_arm, allowed_ids, tokenizer, int(gold_id), config.topk), | |
| "score_s_top10_allowed": _score_rows_from_allowed(score_s, allowed_ids, tokenizer, int(gold_id), config.topk), | |
| "prob_base_top10_allowed": _top_rows_from_allowed(prob_base, allowed_ids, tokenizer, int(gold_id), config.topk), | |
| "prob_arm_top10_allowed": _top_rows_from_allowed(prob_arm, allowed_ids, tokenizer, int(gold_id), config.topk), | |
| "prob_score_top10_allowed": prob_rows, | |
| } | |
| step_payload.update(gate) | |
| steps.append(step_payload) | |
| return { | |
| "scheme": "B", | |
| "plugin": "X1", | |
| "support_rule": "sparsemax(logp_base) > 0", | |
| "prompt": prompt, | |
| "response": response, | |
| "response_token_count": len(response_ids), | |
| "steps": steps, | |
| } | |
| def prefix_only_old_helpful_winner(config: PrefixOnlyConfig) -> dict: | |
| tokenizer, base_model, arm_model = load_base_and_helpful_models(config.adapter_path) | |
| prompt, response = _resolve_prompt_and_response(config.prompt, config.response) | |
| prompt_ids = tokenizer(prompt, add_special_tokens=False)["input_ids"] | |
| response_ids = tokenizer(response, add_special_tokens=False)["input_ids"] | |
| steps = [] | |
| for step_idx, gold_id in enumerate(response_ids[: config.steps]): | |
| prefix_ids = response_ids[:step_idx] | |
| full_prefix = prompt_ids + prefix_ids | |
| logp_base = _forward_last_logprobs(base_model, full_prefix) | |
| logp_arm = _forward_last_logprobs(arm_model, full_prefix) | |
| score_s = logp_base + logp_arm | |
| prob_base = torch.exp(logp_base) | |
| prob_arm = torch.exp(logp_arm) | |
| prob_score = torch.softmax(score_s, dim=-1) | |
| steps.append( | |
| { | |
| "step_index": step_idx + 1, | |
| "gold_token_id": int(gold_id), | |
| "gold_token_text": tokenizer.decode([int(gold_id)]), | |
| "prefix_response_token_ids": prefix_ids, | |
| "prefix_response_text": tokenizer.decode(prefix_ids), | |
| "gold_logp_base": float(logp_base[int(gold_id)].item()), | |
| "gold_logp_arm": float(logp_arm[int(gold_id)].item()), | |
| "gold_score_s": float(score_s[int(gold_id)].item()), | |
| "gold_prob_base": float(prob_base[int(gold_id)].item()), | |
| "gold_prob_arm": float(prob_arm[int(gold_id)].item()), | |
| "gold_prob_score": float(prob_score[int(gold_id)].item()), | |
| "logp_base_top10": _top_rows_full_vocab(logp_base, tokenizer, int(gold_id), config.topk), | |
| "logp_arm_top10": _top_rows_full_vocab(logp_arm, tokenizer, int(gold_id), config.topk), | |
| "score_s_top10": _top_rows_full_vocab(score_s, tokenizer, int(gold_id), config.topk), | |
| "prob_base_top10": _top_rows_full_vocab(prob_base, tokenizer, int(gold_id), config.topk), | |
| "prob_arm_top10": _top_rows_full_vocab(prob_arm, tokenizer, int(gold_id), config.topk), | |
| "prob_score_top10": _top_rows_full_vocab(prob_score, tokenizer, int(gold_id), config.topk), | |
| } | |
| ) | |
| return { | |
| "mode": "prefix_only_old_helpful_winner", | |
| "prompt": prompt, | |
| "response": response, | |
| "response_token_count": len(response_ids), | |
| "steps": steps, | |
| } | |
| def scheme_b_temp_support_supervised_old_helpful(config: SchemeBTempSupportConfig) -> dict: | |
| tokenizer, base_model, arm_model = load_base_and_helpful_models(config.adapter_path) | |
| prompt, response = _resolve_prompt_and_response(config.prompt, config.response) | |
| prompt_ids = tokenizer(prompt, add_special_tokens=False)["input_ids"] | |
| response_ids = tokenizer(response, add_special_tokens=False)["input_ids"] | |
| steps = [] | |
| for step_idx, gold_id in enumerate(response_ids[: config.steps]): | |
| prefix_ids = response_ids[:step_idx] | |
| full_prefix = prompt_ids + prefix_ids | |
| logp_base = _forward_last_logprobs(base_model, full_prefix) | |
| logp_arm = _forward_last_logprobs(arm_model, full_prefix) | |
| prob_base = torch.exp(logp_base) | |
| prob_arm = torch.exp(logp_arm) | |
| allowed_ids, scaled_base_sparse_probs = _compute_allowed_ids_scheme_b_temp( | |
| logp_base, config.support_temperature | |
| ) | |
| score_s = logp_base + config.alpha * logp_arm | |
| filtered_score = score_s[allowed_ids] | |
| filtered_probs = torch.softmax(filtered_score, dim=-1) | |
| if int(gold_id) in set(allowed_ids.tolist()): | |
| gold_allowed_index = (allowed_ids == int(gold_id)).nonzero(as_tuple=False).item() | |
| gold_final_prob = float(filtered_probs[gold_allowed_index].item()) | |
| gold_in_scope = True | |
| else: | |
| gold_final_prob = 0.0 | |
| gold_in_scope = False | |
| prob_rows = [] | |
| top_prob_vals, top_prob_idx = torch.topk(filtered_probs, k=min(config.topk, allowed_ids.numel())) | |
| top_prob_ids = allowed_ids[top_prob_idx] | |
| seen_gold_prob = False | |
| for tok_id, val in zip(top_prob_ids.tolist(), top_prob_vals.tolist()): | |
| is_gold = int(tok_id) == int(gold_id) | |
| seen_gold_prob = seen_gold_prob or is_gold | |
| prob_rows.append( | |
| { | |
| "token_id": int(tok_id), | |
| "token_text": tokenizer.decode([int(tok_id)]), | |
| "value": float(val), | |
| "is_gold": bool(is_gold), | |
| "gold_in_scope": True, | |
| } | |
| ) | |
| if not seen_gold_prob: | |
| prob_rows.append( | |
| { | |
| "token_id": int(gold_id), | |
| "token_text": tokenizer.decode([int(gold_id)]), | |
| "value": gold_final_prob, | |
| "is_gold": True, | |
| "gold_in_scope": False, | |
| } | |
| ) | |
| steps.append( | |
| { | |
| "step_index": step_idx + 1, | |
| "gold_token_id": int(gold_id), | |
| "gold_token_text": tokenizer.decode([int(gold_id)]), | |
| "prefix_response_token_ids": prefix_ids, | |
| "prefix_response_text": tokenizer.decode(prefix_ids), | |
| "allowed_token_count": int(allowed_ids.numel()), | |
| "support_temperature": float(config.support_temperature), | |
| "alpha": float(config.alpha), | |
| "gold_token_sparsemax_base_weight_scaled": float(scaled_base_sparse_probs[int(gold_id)].item()), | |
| "gold_token_in_scope": gold_in_scope, | |
| "gold_logp_base": float(logp_base[int(gold_id)].item()), | |
| "gold_logp_arm": float(logp_arm[int(gold_id)].item()), | |
| "gold_score_s": float(score_s[int(gold_id)].item()), | |
| "gold_prob_base": float(prob_base[int(gold_id)].item()), | |
| "gold_prob_arm": float(prob_arm[int(gold_id)].item()), | |
| "gold_final_prob_after_support_softmax": gold_final_prob, | |
| "logp_base_top10_allowed": _top_rows_from_allowed(logp_base, allowed_ids, tokenizer, int(gold_id), config.topk), | |
| "logp_arm_top10_allowed": _top_rows_from_allowed(logp_arm, allowed_ids, tokenizer, int(gold_id), config.topk), | |
| "score_s_top10_allowed": _score_rows_from_allowed(score_s, allowed_ids, tokenizer, int(gold_id), config.topk), | |
| "prob_base_top10_allowed": _top_rows_from_allowed(prob_base, allowed_ids, tokenizer, int(gold_id), config.topk), | |
| "prob_arm_top10_allowed": _top_rows_from_allowed(prob_arm, allowed_ids, tokenizer, int(gold_id), config.topk), | |
| "prob_score_top10_allowed": prob_rows, | |
| } | |
| ) | |
| support_counts = [step["allowed_token_count"] for step in steps] | |
| support_counts_sorted = sorted(support_counts) | |
| mid = len(support_counts_sorted) // 2 | |
| if len(support_counts_sorted) % 2 == 1: | |
| median_support = float(support_counts_sorted[mid]) | |
| else: | |
| median_support = 0.5 * (support_counts_sorted[mid - 1] + support_counts_sorted[mid]) | |
| return { | |
| "scheme": "B_temp_support", | |
| "support_rule": "support(sparsemax(logp_base / T_support))", | |
| "support_temperature": float(config.support_temperature), | |
| "alpha": float(config.alpha), | |
| "prompt": prompt, | |
| "response": response, | |
| "response_token_count": len(response_ids), | |
| "support_stats": { | |
| "steps_analyzed": len(steps), | |
| "min": int(min(support_counts)), | |
| "max": int(max(support_counts)), | |
| "mean": float(sum(support_counts) / len(support_counts)), | |
| "median": float(median_support), | |
| }, | |
| "steps": steps, | |
| } | |