Download scripts/eval/internal/prepare_workspace.py from Ulairi/ADSD: direct link, hf CLI and curl.
- Browser
- Download file 18.6 kB
-
https://huggingface.co/spaces/Ulairi/ADSD/resolve/main/scripts/eval/internal/prepare_workspace.py
- Command line
-
hf download hf://spaces/Ulairi/ADSD/scripts/eval/internal/prepare_workspace.py
-
curl -L -o prepare_workspace.py https://huggingface.co/spaces/Ulairi/ADSD/resolve/main/scripts/eval/internal/prepare_workspace.py
18.6 kB
| #!/usr/bin/env python3 | |
| from __future__ import annotations | |
| import argparse | |
| import shutil | |
| from pathlib import Path | |
| PROJECT_ROOT = Path(__file__).resolve().parents[3] | |
| SOURCE_REPO = PROJECT_ROOT / "external" / "hsd" | |
| DEFAULT_WORKSPACE = PROJECT_ROOT / "experiments" / "hsd_inference" / "workspace" | |
| def copy_workspace(src: Path, dst: Path) -> None: | |
| dst.parent.mkdir(parents=True, exist_ok=True) | |
| shutil.copytree( | |
| src, | |
| dst, | |
| dirs_exist_ok=True, | |
| ignore=shutil.ignore_patterns(".git", ".idea", "__pycache__", "*.pyc"), | |
| ) | |
| def replace_once_or_skip(text: str, old: str, new: str, path: Path) -> str: | |
| if new in text: | |
| return text | |
| if old in text: | |
| return text.replace(old, new, 1) | |
| raise RuntimeError(f"Expected text not found while patching {path}: {old}") | |
| def patch_eval_script(path: Path) -> None: | |
| text = path.read_text() | |
| replacements = { | |
| "os.environ['HF_HOME'] = '/home/c02yuzh/CISPA-projects/rectified_softmax_ml-2023/hf_home'": ( | |
| "from pathlib import Path\n" | |
| "PROMPT_DIR = Path(__file__).resolve().parent / 'lib_prompt'\n" | |
| 'os.environ.setdefault("HF_HOME", os.environ.get("SHARED_CACHE_ROOT", "/project/mpese/tigersec/runw/workdirs/speculative-decoding-robustness/cache") + "/huggingface")' | |
| ), | |
| "parser.add_argument('--target-model', default='Qwen/Qwen2.5-72B-Instruct-GPTQ-Int8', help='must be complex or original')": ( | |
| "parser.add_argument('--target-model', default='Qwen/Qwen2.5-72B-Instruct-GPTQ-Int8', help='must be complex or original')\n" | |
| "parser.add_argument('--draft-model', default='Qwen/Qwen2.5-0.5B-Instruct-GPTQ-Int8')\n" | |
| "parser.add_argument('--num-samples', type=int, default=None)\n" | |
| "parser.add_argument('--prompt-suffix', dest='prompt_suffix', default='')\n" | |
| "parser.add_argument('--prompt-tag', default='')\n" | |
| "parser.add_argument('--seed', type=int, default=2027)\n" | |
| "parser.add_argument('--max-new-tokens', type=int, default=512)\n" | |
| "parser.add_argument('--min-new-tokens', type=int, default=0)\n" | |
| "parser.add_argument('--sample-offset', type=int, default=0)" | |
| ), | |
| "args = parser.parse_args()": ( | |
| "args = parser.parse_args()\n" | |
| "random.seed(args.seed)\n" | |
| "np.random.seed(args.seed)\n" | |
| "torch.manual_seed(args.seed)\n" | |
| "if torch.cuda.is_available():\n" | |
| " torch.cuda.manual_seed_all(args.seed)" | |
| ), | |
| 'model1_name = "Qwen/Qwen2.5-0.5B-Instruct-GPTQ-Int8"': "model1_name = args.draft_model", | |
| 'model_size = model2_name.split("/")[1].split("-")[1]': ( | |
| 'model_ref = os.path.basename(model2_name.rstrip("/"))\n' | |
| 'size_match = re.search(r"(\\d+(?:\\.\\d+)?B)", model_ref)\n' | |
| 'if size_match is None:\n' | |
| ' raise ValueError(f"Could not infer model size from target model path: {model2_name}")\n' | |
| 'model_size = size_match.group(1)' | |
| ), | |
| "num_samples = len(gsm8k_test['question'])//5": ( | |
| "num_samples = args.num_samples if args.num_samples is not None else len(gsm8k_test['question'])//5\n" | |
| "if args.sample_offset < 0 or args.sample_offset + num_samples > len(gsm8k_test['question']):\n" | |
| " raise ValueError('sample-offset/num-samples exceed the GSM8K split')" | |
| ), | |
| "for q, a in tqdm(zip(gsm8k_test['question'][:num_samples], gsm8k_test['answer'][:num_samples]),\n" | |
| " total=num_samples):": ( | |
| "sample_end = args.sample_offset + num_samples\n" | |
| " for q, a in tqdm(zip(gsm8k_test['question'][args.sample_offset:sample_end], " | |
| "gsm8k_test['answer'][args.sample_offset:sample_end]),\n" | |
| " total=num_samples):" | |
| ), | |
| 'if args.prompt == "original":\n prompt = open(\'lib_prompt/prompt_original.txt\').read()\nelse:\n prompt = open(\'lib_prompt/prompt_hardest.txt\').read()': ( | |
| 'if args.prompt == "original":\n prompt = (PROMPT_DIR / \'prompt_original.txt\').read_text()\nelse:\n prompt = (PROMPT_DIR / \'prompt_hardest.txt\').read_text()\n\n' | |
| "if args.prompt_suffix:\n" | |
| " print('prompt_suffix:', args.prompt_suffix.encode('unicode_escape').decode('ascii'))" | |
| ), | |
| ' if args.parallel:\n sd += "_parallel"\n': ( | |
| ' if args.parallel:\n sd += "_parallel"\n\n' | |
| ' if args.prompt_tag:\n sd += f"_{args.prompt_tag}"\n' | |
| ), | |
| " input_text = tokenizer2.apply_chat_template(\n" | |
| " messages,\n" | |
| " tokenize=False,\n" | |
| " add_generation_prompt=True\n" | |
| " )\n": ( | |
| " input_text = tokenizer2.apply_chat_template(\n" | |
| " messages,\n" | |
| " tokenize=False,\n" | |
| " add_generation_prompt=True\n" | |
| " )\n" | |
| "\n" | |
| " if args.prompt_suffix:\n" | |
| " input_text += args.prompt_suffix\n" | |
| ), | |
| 'total_counts["time"].append(start-end)': 'total_counts["time"].append(end-start)', | |
| # Flush after every sample so NFS sees partial results and they survive SIGTERM/preemption | |
| " fd.write('Q: %s\\nA_model:\\n%s\\nA:\\n%s\\n\\n' % (q, ans_, a))\n": ( | |
| " fd.write('Q: %s\\nA_model:\\n%s\\nA:\\n%s\\n\\n' % (q, ans_, a))\n" | |
| " fd.flush() # flush after every sample so NFS sees progress and partial results survive SIGTERM\n" | |
| ), | |
| ' "step_back_probs":[], "p_i":[], "q_i":[], "hist_lengths": [], "time":[], "ids":[]}': ( | |
| ' "step_back_probs":[], "p_i":[], "q_i":[], "hist_lengths": [], "time":[], "ids":[],\n' | |
| ' "aggregate_acceptance_per_sample": [], "block_efficiency_per_sample": [],\n' | |
| ' "accepted_draft_tokens_per_sample": [], "drafted_tokens_per_sample": [],\n' | |
| ' "target_steps_per_sample": [], "first_token_acceptance_per_sample": [],\n' | |
| ' "acceptance_trace_per_sample": [], "fallback_triggered_per_sample": [],\n' | |
| ' "fallback_trigger_block_per_sample": [], "fallback_generated_tokens_per_sample": [],\n' | |
| ' "generated_tokens_per_sample": []}' | |
| ), | |
| ' return_probs=args.backward or args.blockwise,': ' return_probs=True,', | |
| ' total_counts["sample_length"].append(len(outputs[0] - len(input_ids[0])))': ( | |
| ' generated_length = int(outputs.shape[-1] - input_ids.shape[-1])\n' | |
| ' total_counts["sample_length"].append(generated_length)\n' | |
| ' total_counts["generated_tokens_per_sample"].append(generated_length)\n' | |
| ' total_counts["target_steps_per_sample"].append(float(generated_length))' | |
| ), | |
| ' total_counts["ids"].append(counts["ids"])\n': ( | |
| ' total_counts["ids"].append(counts["ids"])\n' | |
| '\n' | |
| ' drafted_tokens = float(sum(counts["draft_eval"]))\n' | |
| ' accepted_draft_tokens = float(sum(counts.get("accepted_draft_tokens", [\n' | |
| ' min(int(d), max(int(s) - int(t), 0))\n' | |
| ' for d, s, t in zip(counts["draft_eval"], counts["sample_length"], counts["target_eval"])\n' | |
| ' ])))\n' | |
| ' generated_tokens = float(sum(counts["sample_length"]))\n' | |
| ' target_steps = float(sum(counts["total_step"]))\n' | |
| ' aggregate_acceptance = accepted_draft_tokens / drafted_tokens if drafted_tokens > 0 else None\n' | |
| ' block_efficiency = generated_tokens / target_steps if target_steps > 0 else None\n' | |
| '\n' | |
| ' first_token_acceptance = None\n' | |
| ' if counts["p_i"] and counts["q_i"] and len(counts["p_i"]) > 0 and len(counts["q_i"]) > 0:\n' | |
| ' valid_prob_pairs = []\n' | |
| ' for p_val, q_val in zip(counts["p_i"], counts["q_i"]):\n' | |
| ' if p_val is None or q_val is None:\n' | |
| ' continue\n' | |
| ' try:\n' | |
| ' p_float = float(p_val)\n' | |
| ' q_float = float(q_val)\n' | |
| ' except (TypeError, ValueError):\n' | |
| ' continue\n' | |
| ' valid_prob_pairs.append((p_float, q_float))\n' | |
| ' if valid_prob_pairs:\n' | |
| ' p0, q0 = valid_prob_pairs[0]\n' | |
| ' first_token_acceptance = min(1.0, p0 / max(q0, 1e-12))\n' | |
| '\n' | |
| ' total_counts["accepted_draft_tokens_per_sample"].append(accepted_draft_tokens)\n' | |
| ' total_counts["drafted_tokens_per_sample"].append(drafted_tokens)\n' | |
| ' total_counts["target_steps_per_sample"].append(target_steps)\n' | |
| ' total_counts["aggregate_acceptance_per_sample"].append(aggregate_acceptance)\n' | |
| ' total_counts["block_efficiency_per_sample"].append(block_efficiency)\n' | |
| ' total_counts["first_token_acceptance_per_sample"].append(first_token_acceptance)\n' | |
| ' total_counts["acceptance_trace_per_sample"].append(counts.get("acceptance_trace", []))\n' | |
| ' total_counts["fallback_triggered_per_sample"].append(bool(counts.get("fallback_triggered", False)))\n' | |
| ' total_counts["fallback_trigger_block_per_sample"].append(counts.get("fallback_trigger_block"))\n' | |
| ' total_counts["fallback_generated_tokens_per_sample"].append(int(counts.get("fallback_generated_tokens", 0)))\n' | |
| ), | |
| ' with open(f"{sd}_total_counts.json", "w") as f:\n json.dump(total_counts, f)\n': ( | |
| ' with open(f"{sd}_total_counts.json", "w") as f:\n' | |
| ' valid_acceptance = [x for x in total_counts["aggregate_acceptance_per_sample"] if x is not None]\n' | |
| ' valid_block_eff = [x for x in total_counts["block_efficiency_per_sample"] if x is not None]\n' | |
| ' valid_first_alpha = [x for x in total_counts["first_token_acceptance_per_sample"] if x is not None]\n' | |
| '\n' | |
| ' total_counts["summary_metrics"] = {\n' | |
| ' "aggregate_acceptance_mean": float(np.mean(valid_acceptance)) if valid_acceptance else None,\n' | |
| ' "block_efficiency_mean": float(np.mean(valid_block_eff)) if valid_block_eff else None,\n' | |
| ' "first_token_acceptance_mean": float(np.mean(valid_first_alpha)) if valid_first_alpha else None,\n' | |
| ' }\n' | |
| ' json.dump(total_counts, f)\n' | |
| ), | |
| } | |
| for old, new in replacements.items(): | |
| text = replace_once_or_skip(text, old, new, path) | |
| text = text.replace("max_new_tokens=512", "max_new_tokens=args.max_new_tokens") | |
| text = text.replace( | |
| "max_new_tokens=args.max_new_tokens,", | |
| "max_new_tokens=args.max_new_tokens,\n min_new_tokens=args.min_new_tokens,", | |
| ) | |
| path.write_text(text) | |
| def patch_generation_utils(path: Path) -> None: | |
| text = path.read_text() | |
| fallback_init = ( | |
| ' fallback_threshold_raw = os.environ.get("ADSD_ACCEPTANCE_FALLBACK_THRESHOLD", "").strip()\n' | |
| ' fallback_threshold = float(fallback_threshold_raw) if fallback_threshold_raw else None\n' | |
| ' fallback_window = int(os.environ.get("ADSD_ACCEPTANCE_FALLBACK_WINDOW", "8"))\n' | |
| ' if fallback_threshold is not None and not 0.0 <= fallback_threshold <= 1.0:\n' | |
| ' raise ValueError("ADSD_ACCEPTANCE_FALLBACK_THRESHOLD must be in [0, 1]")\n' | |
| ' if fallback_window <= 0:\n' | |
| ' raise ValueError("ADSD_ACCEPTANCE_FALLBACK_WINDOW must be positive")\n' | |
| ' fallback_active = False\n' | |
| ' fallback_trigger_block = None\n' | |
| ' fallback_generated_tokens = 0\n' | |
| ' acceptance_trace = []\n' | |
| ' acceptance_window = []\n' | |
| '\n' | |
| ' counts = {"draft_eval": [], "target_eval": [], "total_step": [], "sample_length": [],\n' | |
| ' "step_back_probs": [], "p_i": [], "q_i": [], "hist_lengths": [], "ids": [],\n' | |
| ' "accepted_draft_tokens": [], "acceptance_trace": acceptance_trace,\n' | |
| ' "fallback_threshold": fallback_threshold, "fallback_window": fallback_window,\n' | |
| ' "fallback_triggered": False, "fallback_trigger_block": None,\n' | |
| ' "fallback_generated_tokens": 0}' | |
| ) | |
| replacements = { | |
| " if backward:\n": ( | |
| " step_back_probs, p_next, q_next, ids = None, None, None, None\n" | |
| " if backward:\n" | |
| ), | |
| " if not return_probs:\n return valid_tokens, n_matches, ind\n else:\n return valid_tokens, n_matches, None, None, None, None, ind\n": ( | |
| " if not return_probs:\n" | |
| " return valid_tokens, n_matches, ind\n" | |
| " else:\n" | |
| " return valid_tokens, n_matches, None, p_i.cpu().numpy().tolist(), q_i.cpu().numpy().tolist(), " | |
| "new_candidate_input_ids.cpu().numpy().tolist(), ind\n" | |
| ), | |
| ' counts = {"draft_eval": [], "target_eval": [], "total_step": [], "sample_length": [],\n' | |
| ' "step_back_probs": [], "p_i": [], "q_i": [], "hist_lengths": [], "ids": []}': ( | |
| fallback_init | |
| ), | |
| " num_residual_tokens = num_assistant_tokens\n": ( | |
| " if fallback_active:\n" | |
| " candidate_generator.num_assistant_tokens = 0\n" | |
| " num_assistant_tokens = 0\n" | |
| "\n" | |
| " num_residual_tokens = num_assistant_tokens\n" | |
| ), | |
| " candidate_generator.update_candidate_strategy(input_ids, new_logits, n_matches)\n" | |
| " # update the num_assistant_tokens if needed\n" | |
| " num_assistant_tokens = candidate_generator.num_assistant_tokens\n": ( | |
| " candidate_generator.update_candidate_strategy(input_ids, new_logits, n_matches)\n" | |
| " # update the num_assistant_tokens if needed\n" | |
| " num_assistant_tokens = candidate_generator.num_assistant_tokens\n" | |
| " if fallback_active:\n" | |
| " candidate_generator.num_assistant_tokens = 0\n" | |
| " num_assistant_tokens = 0\n" | |
| ), | |
| ' counts["hist_lengths"].append(hist_lengths)\n': ( | |
| ' counts["hist_lengths"].append(hist_lengths)\n' | |
| ' accepted_draft_tokens = min(int(draft_eval), max(int(sample_length) - int(target_eval), 0))\n' | |
| ' counts["accepted_draft_tokens"].append(accepted_draft_tokens)\n' | |
| ' if fallback_active:\n' | |
| ' fallback_generated_tokens += int(sample_length)\n' | |
| ' elif draft_eval > 0:\n' | |
| ' block_acceptance = float(accepted_draft_tokens) / float(draft_eval)\n' | |
| ' acceptance_trace.append(block_acceptance)\n' | |
| ' acceptance_window.append(block_acceptance)\n' | |
| ' if len(acceptance_window) > fallback_window:\n' | |
| ' acceptance_window.pop(0)\n' | |
| ' if (\n' | |
| ' fallback_threshold is not None\n' | |
| ' and len(acceptance_window) == fallback_window\n' | |
| ' and sum(acceptance_window) / len(acceptance_window) < fallback_threshold\n' | |
| ' ):\n' | |
| ' fallback_active = True\n' | |
| ' fallback_trigger_block = len(counts["total_step"])\n' | |
| ' counts["fallback_triggered"] = True\n' | |
| ' counts["fallback_trigger_block"] = fallback_trigger_block\n' | |
| ' candidate_generator.num_assistant_tokens = 0\n' | |
| ' num_assistant_tokens = 0\n' | |
| ' counts["fallback_generated_tokens"] = fallback_generated_tokens\n' | |
| ), | |
| } | |
| for old, new in replacements.items(): | |
| text = replace_once_or_skip(text, old, new, path) | |
| path.write_text(text) | |
| def ensure_outputs_dir(workspace: Path) -> None: | |
| outputs_dir = workspace / "chain-of-thought-hub" / "gsm8k" / "outputs" | |
| outputs_dir.mkdir(parents=True, exist_ok=True) | |
| def main() -> int: | |
| parser = argparse.ArgumentParser(description="Prepare a patched local HSD workspace") | |
| parser.add_argument("--workspace", type=Path, default=DEFAULT_WORKSPACE) | |
| args = parser.parse_args() | |
| if not SOURCE_REPO.exists(): | |
| raise SystemExit(f"Missing source repo: {SOURCE_REPO}") | |
| copy_workspace(SOURCE_REPO, args.workspace) | |
| patch_eval_script(args.workspace / "chain-of-thought-hub" / "gsm8k" / "eval_speculative_decoding_llm.py") | |
| patch_generation_utils(args.workspace / "transformers" / "generation" / "utils.py") | |
| ensure_outputs_dir(args.workspace) | |
| print(args.workspace) | |
| return 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |