mirror of
https://github.com/hanzoai/open-instruct.git
synced 2026-10-30 18:26:32 +00:00
92 lines
3.5 KiB
Python
92 lines
3.5 KiB
Python
import itertools
|
|
import os
|
|
from collections import Counter, defaultdict
|
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
|
|
import numpy as np
|
|
from eval.mbpp.execution import check_correctness
|
|
|
|
# https://github.com/bigcode-project/bigcode-evaluation-harness/blob/main/bigcode_eval/tasks/custom_metrics/code_eval.py#L129
|
|
|
|
_WARNING = """
|
|
################################################################################
|
|
!!!WARNING!!!
|
|
################################################################################
|
|
The "code_eval" metric executes untrusted model-generated code in Python.
|
|
Although it is highly unlikely that model-generated code will do something
|
|
overtly malicious in response to this test suite, model-generated code may act
|
|
destructively due to a lack of model capability or alignment.
|
|
Users are strongly encouraged to sandbox this evaluation suite so that it
|
|
does not perform destructive actions on their host or network. For more
|
|
information on how OpenAI sandboxes its code, see the paper "Evaluating Large
|
|
Language Models Trained on Code" (https://arxiv.org/abs/2107.03374).
|
|
|
|
Once you have read this disclaimer and taken appropriate precautions,
|
|
set the environment variable HF_ALLOW_CODE_EVAL="1". Within Python you can to this
|
|
with:
|
|
|
|
>>> import os
|
|
>>> os.environ["HF_ALLOW_CODE_EVAL"] = "1"
|
|
|
|
################################################################################\
|
|
"""
|
|
|
|
def compute_code_eval(predictions, k=[1, 10, 100], num_workers=4, timeout=3.0):
|
|
"""Returns the scores"""
|
|
|
|
if os.getenv("HF_ALLOW_CODE_EVAL", 0) != "1":
|
|
raise ValueError(_WARNING)
|
|
|
|
if os.name == "nt":
|
|
raise NotImplementedError("This metric is currently not supported on Windows.")
|
|
|
|
with ThreadPoolExecutor(max_workers=num_workers) as executor:
|
|
futures = []
|
|
completion_id = Counter()
|
|
n_samples = 0
|
|
results = defaultdict(list)
|
|
|
|
for sample in predictions:
|
|
test_program = sample['completion'] + "\n" + sample['test_cases']
|
|
args = (test_program, timeout, sample['task_id'], completion_id[sample['task_id']])
|
|
future = executor.submit(check_correctness, *args)
|
|
futures.append(future)
|
|
completion_id[sample['task_id']] += 1
|
|
n_samples += 1
|
|
|
|
for future in as_completed(futures):
|
|
result = future.result()
|
|
results[result["task_id"]].append((result["completion_id"], result))
|
|
total, correct = [], []
|
|
for result in results.values():
|
|
result.sort()
|
|
passed = [r[1]["passed"] for r in result]
|
|
total.append(len(passed))
|
|
correct.append(sum(passed))
|
|
total = np.array(total)
|
|
correct = np.array(correct)
|
|
|
|
ks = k
|
|
if not isinstance(ks, (list, tuple)):
|
|
ks = [ks]
|
|
pass_at_k = {f"pass@{k}": estimate_pass_at_k(total, correct, k).mean() for k in ks if (total >= k).all()}
|
|
|
|
return pass_at_k, results
|
|
|
|
|
|
def estimate_pass_at_k(num_samples, num_correct, k):
|
|
"""Estimates pass@k of each problem and returns them in an array."""
|
|
|
|
def estimator(n: int, c: int, k: int) -> float:
|
|
"""Calculates 1 - comb(n - c, k) / comb(n, k)."""
|
|
if n - c < k:
|
|
return 1.0
|
|
return 1.0 - np.prod(1.0 - k / np.arange(n - c + 1, n + 1))
|
|
|
|
if isinstance(num_samples, int):
|
|
num_samples_it = itertools.repeat(num_samples, len(num_correct))
|
|
else:
|
|
assert len(num_samples) == len(num_correct)
|
|
num_samples_it = iter(num_samples)
|
|
|
|
return np.array([estimator(int(n), int(c), k) for n, c in zip(num_samples_it, num_correct)]) |