Taimwe commited on
Commit
5c4d18b
·
verified ·
1 Parent(s): 677413b

Fix eval harness: honour --max-new-tokens, exclude truncation from syntax score, fall back across dataset splits

Browse files
Files changed (1) hide show
  1. eval_securecoder.py +37 -10
eval_securecoder.py CHANGED
@@ -79,7 +79,7 @@ def parse_args() -> argparse.Namespace:
79
  p.add_argument("--tool-prompts", type=int, default=80)
80
  p.add_argument("--code-prompts", type=int, default=15)
81
  p.add_argument("--upload-repo", default="Taimwe/securecoder-30b-pro")
82
- p.add_argument("--max-new-tokens", type=int, default=384)
83
  p.add_argument("--seed", type=int, default=3407)
84
  return p.parse_args()
85
 
@@ -297,9 +297,15 @@ def eval_code_sanity(tokenizer, model, args) -> dict:
297
  tokenize=False, add_generation_prompt=True,
298
  )
299
  ids = tokenizer(text, return_tensors="pt", add_special_tokens=False).input_ids.to(model.device)
 
300
  with torch.no_grad():
301
- generated = model.generate(ids, max_new_tokens=384, do_sample=False, eos_token_id=tokenizer.eos_token_id, pad_token_id=tokenizer.eos_token_id)
302
- reply = tokenizer.decode(generated[0, ids.shape[-1]:], skip_special_tokens=True)
 
 
 
 
 
303
  except Exception as exc:
304
  out.append({"prompt": prompt[:60], "error": repr(exc)[:120]})
305
  continue
@@ -336,25 +342,46 @@ def eval_code_sanity(tokenizer, model, args) -> dict:
336
  'prompt': prompt[:60],
337
  'ast_ok': parsed,
338
  'compile_ok': compiles,
 
339
  'reply_first_160': reply[:160],
340
  })
341
  if (i + 1) % 5 == 0:
342
  log.info(" code sanity: %d/%d", i + 1, len(prompts))
343
  n = len(out)
344
- ast_ok = sum(1 for r in out if r.get("ast_ok"))
345
- compile_ok = sum(1 for r in out if r.get("compile_ok"))
 
 
 
 
346
  return {"section": "code_sanity", "n_prompts": n,
347
- "ast_rate": ast_ok / max(n, 1), "compile_rate": compile_ok / max(n, 1),
 
 
 
348
  "details": out}
349
 
350
 
351
 
352
  def eval_security_mcq(tokenizer, model, n_questions: int = 25) -> dict:
353
  import torch
354
- try:
355
- rows = _fetch_first_rows("CyberNative/CyberSecurityEval", None, "train", n_questions * 2)
356
- except Exception as exc:
357
- return {"section": "security_mcq", "error": repr(exc)[:200], "skipped": True}
 
 
 
 
 
 
 
 
 
 
 
 
 
358
  rows = rows[:n_questions]
359
  if not rows:
360
  return {"section": "security_mcq", "skipped": True, "reason": "no rows"}
 
79
  p.add_argument("--tool-prompts", type=int, default=80)
80
  p.add_argument("--code-prompts", type=int, default=15)
81
  p.add_argument("--upload-repo", default="Taimwe/securecoder-30b-pro")
82
+ p.add_argument("--max-new-tokens", type=int, default=1024)
83
  p.add_argument("--seed", type=int, default=3407)
84
  return p.parse_args()
85
 
 
297
  tokenize=False, add_generation_prompt=True,
298
  )
299
  ids = tokenizer(text, return_tensors="pt", add_special_tokens=False).input_ids.to(model.device)
300
+ max_new = int(getattr(args, "max_new_tokens", 384) or 384)
301
  with torch.no_grad():
302
+ generated = model.generate(ids, max_new_tokens=max_new, do_sample=False, eos_token_id=tokenizer.eos_token_id, pad_token_id=tokenizer.eos_token_id)
303
+ new_ids = generated[0, ids.shape[-1]:]
304
+ # Hitting the token cap means TRUNCATED, not "invalid Python". The previous
305
+ # version scored truncation as a syntax error, which made a working model
306
+ # look like it could not code (ast_rate 6.7%).
307
+ truncated = int(len(new_ids) >= max_new)
308
+ reply = tokenizer.decode(new_ids, skip_special_tokens=True)
309
  except Exception as exc:
310
  out.append({"prompt": prompt[:60], "error": repr(exc)[:120]})
311
  continue
 
342
  'prompt': prompt[:60],
343
  'ast_ok': parsed,
344
  'compile_ok': compiles,
345
+ 'truncated': truncated,
346
  'reply_first_160': reply[:160],
347
  })
348
  if (i + 1) % 5 == 0:
349
  log.info(" code sanity: %d/%d", i + 1, len(prompts))
350
  n = len(out)
351
+ n_trunc = sum(1 for r in out if r.get("truncated"))
352
+ # Truncated replies are excluded from the syntax denominator, but if EVERY reply
353
+ # was truncated we still report over all of them rather than dividing by zero.
354
+ scored = [r for r in out if not r.get("truncated")] or out
355
+ ast_ok = sum(1 for r in scored if r.get("ast_ok"))
356
+ compile_ok = sum(1 for r in scored if r.get("compile_ok"))
357
  return {"section": "code_sanity", "n_prompts": n,
358
+ "n_scored": len(scored), "n_truncated": n_trunc,
359
+ "truncated_rate": n_trunc / max(n, 1),
360
+ "ast_rate": ast_ok / max(len(scored), 1),
361
+ "compile_rate": compile_ok / max(len(scored), 1),
362
  "details": out}
363
 
364
 
365
 
366
  def eval_security_mcq(tokenizer, model, n_questions: int = 25) -> dict:
367
  import torch
368
+ rows = None
369
+ last_exc = None
370
+ # CyberNative/CyberSecurityEval only publishes a "test" split. The old code asked
371
+ # for "train", raised ValueError, and the whole security section was skipped as
372
+ # "skipped" - which is why security capability was never measured.
373
+ for split in ("test", "train", "validation"):
374
+ try:
375
+ rows = _fetch_first_rows("CyberNative/CyberSecurityEval", None, split,
376
+ n_questions * 2)
377
+ except Exception as exc: # noqa: BLE001 - try the next split
378
+ last_exc = exc
379
+ rows = None
380
+ if rows:
381
+ break
382
+ if rows is None:
383
+ return {"section": "security_mcq", "error": repr(last_exc)[:200],
384
+ "skipped": True, "reason": "no usable split"}
385
  rows = rows[:n_questions]
386
  if not rows:
387
  return {"section": "security_mcq", "skipped": True, "reason": "no rows"}