Download scripts/score_models.py from koushiks29292/ValidButVacuous: direct link, hf CLI and curl.
- Browser
- Download file 3.05 kB
-
https://huggingface.co/koushiks29292/ValidButVacuous/resolve/main/scripts/score_models.py
- Command line
-
hf download hf://koushiks29292/ValidButVacuous/scripts/score_models.py
-
curl -L -o score_models.py https://huggingface.co/koushiks29292/ValidButVacuous/resolve/main/scripts/score_models.py
3.05 kB
| """Score multiple-choice medical questions with a small open model. | |
| Confidence = softmax over the option letters' next-token logits (no temperature). | |
| Each question is scored in its original and in a shuffled option order. | |
| python scripts/score_models.py --model Qwen/Qwen2.5-1.5B-Instruct \ | |
| --questions data/medqa.jsonl --out scores/qwen1.5b_medqa.npz [--nf4] | |
| questions.jsonl, one per line: {"question": str, "options": [str, ...], "answer": int} | |
| Output .npz: conf, correct, pred, conf_shuf, correct_shuf, pred_shuf_in_original_order. | |
| Needs: torch, transformers (+ bitsandbytes for --nf4). Runs on a single 16 GB GPU. | |
| """ | |
| import argparse | |
| import json | |
| import numpy as np | |
| LETTERS = "ABCDEFGHIJ" | |
| def prompt(q, options): | |
| lines = [f"Question: {q}"] + [f"{LETTERS[i]}. {o}" for i, o in enumerate(options)] | |
| return "\n".join(lines) + "\nAnswer:" | |
| def main(): | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--model", required=True) | |
| ap.add_argument("--questions", required=True) | |
| ap.add_argument("--out", required=True) | |
| ap.add_argument("--nf4", action="store_true", help="load in 4-bit NF4") | |
| ap.add_argument("--seed", type=int, default=0) | |
| args = ap.parse_args() | |
| import torch | |
| from transformers import AutoModelForCausalLM, AutoTokenizer | |
| kw = dict(device_map="auto", torch_dtype=torch.float16) | |
| if args.nf4: | |
| from transformers import BitsAndBytesConfig | |
| kw["quantization_config"] = BitsAndBytesConfig( | |
| load_in_4bit=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.float16) | |
| tok = AutoTokenizer.from_pretrained(args.model) | |
| model = AutoModelForCausalLM.from_pretrained(args.model, **kw).eval() | |
| rows = [json.loads(l) for l in open(args.questions)] | |
| rng = np.random.default_rng(args.seed) | |
| def letter_ids(k): | |
| return [tok.encode(" " + LETTERS[i], add_special_tokens=False)[-1] for i in range(k)] | |
| def score(q, opts): | |
| ids = tok(prompt(q, opts), return_tensors="pt").to(model.device) | |
| logits = model(**ids).logits[0, -1].float() | |
| p = torch.softmax(logits[letter_ids(len(opts))], -1).cpu().numpy() | |
| return int(p.argmax()), float(p.max()) | |
| out = {k: [] for k in ["pred", "conf", "correct", "pred_shuf", "conf_shuf", "correct_shuf"]} | |
| for r in rows: | |
| pr, c = score(r["question"], r["options"]) | |
| out["pred"].append(pr); out["conf"].append(c); out["correct"].append(pr == r["answer"]) | |
| perm = rng.permutation(len(r["options"])) | |
| pr2, c2 = score(r["question"], [r["options"][j] for j in perm]) | |
| orig = int(perm[pr2]) | |
| out["pred_shuf"].append(orig); out["conf_shuf"].append(c2) | |
| out["correct_shuf"].append(orig == r["answer"]) | |
| np.savez(args.out, **{k: np.array(v) for k, v in out.items()}) | |
| acc = np.mean(out["correct"]); agree = np.mean(np.array(out["pred"]) == np.array(out["pred_shuf"])) | |
| print(f"{args.model}: accuracy {acc:.3f}, agreement under shuffling {agree:.3f}") | |
| if __name__ == "__main__": | |
| main() | |