Files
2026-07-13 13:24:13 +08:00

171 lines
6.4 KiB
Python

from post_processors.openai_api_callback import OpenAICallBack, majority_voting_predict
import collections
import json
import os
import re
from typing import Dict, Any, List, Tuple, Union
import numpy as np
import vllm
from data.qwen25math.parser import extract_answer, strip_string, STRIP_EXCEPTIONS
from data.qwen25math.grader import math_equal_process, math_equal
from concurrent.futures import ThreadPoolExecutor, as_completed
from tqdm import tqdm
from omegaconf import ListConfig
from general_util.logger import get_child_logger
logger = get_child_logger(__name__)
def _annotate(param):
return param[0], math_equal(param[-2], param[-1])
class Qwen25MathCallBack(OpenAICallBack):
def __init__(self, *args, num_workers: int = 16, **kwargs):
super().__init__(*args, **kwargs)
self.num_workers = num_workers
def __call__(self, meta_data: Dict[str, Any], batch_model_outputs: Dict[str, Any], **kwargs):
text = meta_data["text"]
if self.label_field in meta_data:
label = meta_data[self.label_field]
else:
label = -1
index = meta_data[self.index_field]
response = batch_model_outputs["response"]
if isinstance(response, vllm.RequestOutput):
if response.finished:
response = [o.text for o in response.outputs]
if len(response) == 1:
response = response[0]
else:
response = ""
if isinstance(response, str):
pred_clean = extract_answer(response, data_name="math")
pred_clean = strip_string(pred_clean, skip_unit="math" in STRIP_EXCEPTIONS)
if pred_clean is None:
pred_clean = ""
sc_pred = pred_clean
elif isinstance(response, list):
pred_clean = []
for resp in response:
tmp_pred_clean = extract_answer(resp, data_name="math")
tmp_pred_clean = strip_string(tmp_pred_clean, skip_unit="math" in STRIP_EXCEPTIONS)
if tmp_pred_clean is None:
tmp_pred_clean = ""
pred_clean.append(tmp_pred_clean)
sc_pred = majority_voting_predict(pred_clean)
else:
raise ValueError(f"Unknown type of response: {type(response)}")
out_item = {
"text": text,
"label": label,
"response": response,
"pred": pred_clean,
"id": index,
"sc_pred": sc_pred,
}
if self.saved_keys is not None:
for key in self.saved_keys:
out_item[key] = meta_data[key]
self.predictions.append(out_item)
self.fw.write(json.dumps(self.predictions[-1]) + "\n")
self.fw.flush()
def get_results(self):
save_dir = os.path.dirname(self.output_file)
if not os.path.exists(save_dir):
os.makedirs(save_dir, exist_ok=True)
self.fw.close()
_mp_inputs = []
for i, item in enumerate(self.predictions):
if not isinstance(item["pred"], list):
preds = [item["pred"]]
else:
preds = item["pred"]
for j, pred in enumerate(preds):
_mp_inputs.append(((i, j), pred, str(item["label"])))
pbar = tqdm(_mp_inputs, total=len(_mp_inputs), desc="Submitting eval task", dynamic_ncols=True)
outputs = collections.defaultdict(dict)
timeout_cnt = 0
with ThreadPoolExecutor(max_workers=self.num_workers) as executor:
futures = []
for _input in pbar:
future = executor.submit(_annotate, _input)
futures.append(future)
pbar.update()
for future in tqdm(as_completed(futures), total=len(futures), desc="Collecting results"):
try:
idx, result = future.result()
outputs[idx[0]][idx[1]] = result
except StopIteration:
break
except TimeoutError as error:
print(error)
outputs[idx[0]][idx[1]] = False
timeout_cnt += 1
except Exception as error:
print(error.traceback)
exit()
for i, item in enumerate(self.predictions):
if not isinstance(item["pred"], list):
preds = [item["pred"]]
else:
preds = item["pred"]
all_res = outputs[i]
assert len(all_res) == len(preds)
pred2res = {pred: all_res[j] for j, pred in enumerate(preds)}
sc_res = pred2res[item["sc_pred"]]
item["res"] = [pred2res[pred] for pred in preds]
item["sc_res"] = sc_res
if not isinstance(item["pred"], list):
assert len(item["res"]) == 1
item["res"] = item["res"][0]
cnt = 0
pass_at_k = 0
sc = 0
acc_data_topic = collections.Counter()
cnt_data_topic = collections.Counter()
for item in self.predictions:
if not isinstance(item["res"], list):
res = [item["res"]]
else:
res = item["res"]
if res[0]:
cnt += 1
if "data_topic" in item:
if "." in item["data_topic"]:
item["data_topic"] = item["data_topic"].split(".")[0]
acc_data_topic[item["data_topic"]] += int(res[0])
cnt_data_topic[item["data_topic"]] += 1
if any(res):
pass_at_k += 1
if item["sc_res"]:
sc += 1
assert pass_at_k <= len(self.predictions)
json.dump(self.predictions, open(self.output_file, "w"), indent=2)
if len(self.predictions) == 0:
metrics = {"acc": 0, "pass@k": 0, "maj@k": 0, "correct": 0, "total": 0}
else:
metrics = {"acc": cnt / len(self.predictions), "pass@k": pass_at_k / len(self.predictions), "maj@k": sc / len(self.predictions),
"correct": cnt, "total": len(self.predictions)}
if len(acc_data_topic) > 0:
for key in acc_data_topic:
metrics[f"acc_{key}"] = acc_data_topic[key] / cnt_data_topic[key]
json.dump(metrics, open(self.output_file.replace(".json", ".metrics.json"), "w"), indent=2)
return metrics, []