Taimwe commited on
Commit
6e7526a
·
verified ·
1 Parent(s): 3ca4001

Bisect: base+adapter vs merged, to locate the escaped-newline defect

Browse files
Files changed (1) hide show
  1. bisect_adapter.py +79 -0
bisect_adapter.py ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # /// script
2
+ # requires-python = ">=3.10"
3
+ # dependencies = [
4
+ # "torch",
5
+ # "transformers[torch]",
6
+ # "peft",
7
+ # "huggingface_hub",
8
+ # "bitsandbytes",
9
+ # "accelerate",
10
+ # ]
11
+ # ///
12
+ """Bisect: base+adapter (no merge) vs merged checkpoint.
13
+
14
+ If base+adapter emits real newlines, the MERGE is what breaks decoding.
15
+ If it also emits literal backslash-n, the ADAPTER (i.e. the training) is at fault.
16
+ """
17
+ import os
18
+ import re
19
+ import sys
20
+
21
+ import torch
22
+ from peft import PeftModel
23
+ from transformers import (AutoModelForCausalLM, AutoTokenizer,
24
+ BitsAndBytesConfig)
25
+
26
+ BASE = "unsloth/Qwen3-Coder-30B-A3B-Instruct"
27
+ ADAPTER = "Taimwe/securecoder-30b-pro-v2"
28
+
29
+ PROMPTS = [
30
+ "Write a Python function `def is_palindrome(s: str) -> bool:` that checks whether a string reads the same forwards and backwards, ignoring case and non-alphanumeric characters.",
31
+ "Write a Python function `def two_sum(nums: list[int], target: int) -> list[int]:` returning the indices of the two numbers adding to target, or an empty list.",
32
+ "Write a Python function `def flatten(nested: list) -> list:` that flattens an arbitrarily nested list.",
33
+ ]
34
+
35
+ token = os.environ.get("HF_TOKEN")
36
+ tok = AutoTokenizer.from_pretrained(BASE, token=token)
37
+ bnb = BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4",
38
+ bnb_4bit_compute_dtype=torch.bfloat16,
39
+ bnb_4bit_use_double_quant=True)
40
+ model = AutoModelForCausalLM.from_pretrained(
41
+ BASE, token=token, quantization_config=bnb,
42
+ dtype=torch.bfloat16, device_map="auto")
43
+ model = PeftModel.from_pretrained(model, ADAPTER, token=token)
44
+ model.eval()
45
+
46
+ ok = 0
47
+ for i, p in enumerate(PROMPTS, 1):
48
+ text = tok.apply_chat_template([{"role": "user", "content": p}],
49
+ tokenize=False, add_generation_prompt=True)
50
+ ids = tok(text, return_tensors="pt", add_special_tokens=False).input_ids.to(model.device)
51
+ with torch.no_grad():
52
+ gen = model.generate(ids, max_new_tokens=1024, do_sample=False,
53
+ eos_token_id=tok.eos_token_id, pad_token_id=tok.eos_token_id)
54
+ new_ids = gen[0, ids.shape[-1]:]
55
+ reply = tok.decode(new_ids, skip_special_tokens=True)
56
+
57
+ has_real_nl = "\n" in reply
58
+ has_lit = "\\n" in reply
59
+ blocks = re.findall(r"```(?:python)?\s*\n(.*?)```", reply, re.S)
60
+ block = max(blocks, key=len) if blocks else None
61
+ if block is None:
62
+ s = reply.find("def ")
63
+ block = reply[s:] if s >= 0 else None
64
+ err = None
65
+ if block:
66
+ try:
67
+ compile(block, "<t>", "exec")
68
+ except Exception as exc:
69
+ err = "%s: %s" % (type(exc).__name__, exc)
70
+ if not err:
71
+ ok += 1
72
+ print("\n" + "=" * 72)
73
+ print("PROMPT %d new_tokens=%d fenced=%d real_nl=%s literal_backslash_n=%s"
74
+ % (i, len(new_ids), len(blocks), has_real_nl, has_lit))
75
+ print("COMPILE:", err or "OK")
76
+ print("-" * 72)
77
+ print(reply[:1200])
78
+
79
+ print("\n\nSUMMARY base+adapter: %d/%d compiled cleanly" % (ok, len(PROMPTS)))