| import os |
| import argparse |
|
|
| import pandas as pd |
| import re |
| from datasets import load_dataset |
|
|
| import json |
|
|
| from .grader import grade_answer |
|
|
| def extract_all_boxed_content(text): |
| results = [] |
| start = 0 |
|
|
| while True: |
| |
| start = text.find(r"\boxed{", start) |
| if start == -1: |
| break |
|
|
| brace_count = 0 |
| result = [] |
| i = start |
|
|
| while i < len(text): |
| char = text[i] |
| result.append(char) |
|
|
| if char == '{': |
| brace_count += 1 |
| elif char == '}': |
| brace_count -= 1 |
|
|
| |
| if brace_count == 0 and result[-1] == '}': |
| break |
|
|
| i += 1 |
|
|
| |
| results.append(''.join(result)) |
| start = i + 1 |
|
|
| return results |
|
|
| def extract_boxed_text(solution): |
| strict_prediction, soft_prediction = None, None |
| prediction_match = extract_all_boxed_content(str(solution)) |
| |
| if len(prediction_match) > 0: |
| strict_prediction = prediction_match[-1] |
| if strict_prediction is not None and '\\boxed' in strict_prediction: |
| strict_prediction = strict_prediction.replace('\\boxed{', '')[:-1] |
| else: |
| patterns = [ |
| r"<answer>(.*?)</answer>", |
| r"</answer>(.*?)</answer>", |
| r"<answer>(.*?)<answer>", |
| r"\*\*Answer:\*\* ([\d\.]+)", |
| |
| r"[-+]?\d*\.\d+|\d+", |
| ] |
| for pattern in patterns: |
| prediction_match = re.findall(pattern, str(solution)) |
| if len(prediction_match) > 0: |
| break |
| |
| if len(prediction_match) > 0: |
| soft_prediction = prediction_match[-1] |
| else: |
| soft_prediction = None |
|
|
| return strict_prediction, soft_prediction |
|
|
|
|
|
|
| def process_target(target, dataset): |
| if dataset == 'gsm8k': |
| target = int(target.split('#### ')[-1].replace(',','')) |
| return target |
| if dataset == 'math500': |
| return target |
| elif dataset == 'aime': |
| return target |
| else: |
| raise ValueError('Unknown dataset') |
| |
| def time_evaluation(result_file): |
| results = [] |
| f = open(result_file, 'r') |
| for line in f: |
| results.append(json.loads(line)) |
| f.close() |
|
|
| total_time, total_token = 0, 0 |
| total = 0 |
| |
| for idx, problem in enumerate(results): |
| total += 1 |
| total_time += problem['time'] |
| total_token += problem['tokens'] |
| |
| return { |
| 'total': total, |
| 'total_time': total_time, |
| 'total_token': total_token, |
| 'token/s': total_token / total_time, |
| } |
|
|
| def evaluation(result_file): |
| results = [] |
| f = open(result_file, 'r') |
| for line in f: |
| results.append(json.loads(line)) |
| f.close() |
|
|
| total, s_correct, f_correct = 0, 0, 0 |
| total_time, total_token = 0, 0 |
|
|
| total_steps = 0 |
| total_experts = 0 |
| |
| for idx, problem in enumerate(results): |
|
|
| answer = problem['answer'] |
| strict_predict_ans, flexible_predict_ans = extract_boxed_text(answer) |
|
|
| target = problem['task']['answer'] |
| target = process_target(target, 'math500') |
|
|
| total += 1 |
| total_time += problem['time'] |
| total_token += problem['tokens'] |
|
|
| total_steps += problem['steps'] |
| total_experts += problem.get('unique_experts_count', 0) |
| |
| if strict_predict_ans is not None or flexible_predict_ans is not None: |
| try: |
| if grade_answer(strict_predict_ans, target): |
| s_correct += 1 |
| f_correct += 1 |
| elif grade_answer(flexible_predict_ans, target): |
| f_correct += 1 |
| |
| except: |
| print('Error in extracting answers: ', strict_predict_ans, flexible_predict_ans) |
| pass |
| else: |
| print('No answer found: ', idx, target) |
| pass |
|
|
| print(f"Strict Match Accuracy = {s_correct}/{total} = {s_correct/total}") |
| print(f"Soft Match Accuracy = {f_correct}/{total} = {f_correct/total}") |
| return { |
| 'strict_accuracy': s_correct / total, |
| 'soft_accuracy': f_correct / total, |
| 'strict_match': s_correct, |
| 'soft_match': f_correct, |
| 'total': total, |
| 'total_time': total_time, |
| 'total_token': total_token, |
| 'token/s': total_token / total_time, |
| 'avg_steps': total_steps / total if total > 0 else -1, |
| 'unique_experts': total_experts / total if total > 0 else -1, |
| } |
|
|
| def passk_evaluation(result_files): |
| """ |
| Pass@k evaluation for MATH500. |
| A question is counted as correct if ANY of the result files answers it correctly. |
| |
| result_files: list[str], each file is a JSONL result file. |
| """ |
|
|
| |
| all_results = [] |
| for file in result_files: |
| with open(file, "r") as f: |
| results = [json.loads(line) for line in f] |
| all_results.append(results) |
|
|
| num_files = len(all_results) |
| num_questions = len(all_results[0]) |
|
|
| strict_correct = 0 |
| soft_correct = 0 |
|
|
| for qid in range(num_questions): |
|
|
| |
| target = all_results[0][qid]['task']['answer'] |
| target = process_target(target, 'math500') |
|
|
| strict_hit = False |
| soft_hit = False |
|
|
| |
| for fidx in range(num_files): |
|
|
| answer = all_results[fidx][qid]['answer'] |
| strict_ans, flexible_ans = extract_boxed_text(answer) |
|
|
| try: |
| |
| if grade_answer(strict_ans, target): |
| strict_hit = True |
| soft_hit = True |
| break |
|
|
| |
| if grade_answer(flexible_ans, target): |
| soft_hit = True |
| break |
|
|
| except Exception as e: |
| print(f"[Warning] Error grading answer in file {fidx}, qid {qid}: {e}") |
| continue |
|
|
| |
| if strict_hit: |
| strict_correct += 1 |
| soft_correct += 1 |
| elif soft_hit: |
| soft_correct += 1 |
|
|
| |
| return { |
| "strict_accuracy": strict_correct / num_questions, |
| "soft_accuracy": soft_correct / num_questions, |
| "strict_match": strict_correct, |
| "soft_match": soft_correct, |
| "total": num_questions, |
| } |
|
|
| if __name__ == '__main__': |
| args = argparse.ArgumentParser() |
| args.add_argument('--result_file', type=str, default='results.csv') |
| args.add_argument('--dataset', type=str, default='math500') |
| args.add_argument('--split', type=str, default='test') |
| args.add_argument('--num-samples', type=int, default=None) |
|
|
| args.add_argument('--relax', action='store_true') |
| args = args.parse_args() |
|
|
| result_file = args.result_file |
| if args.dataset == 'gsm8k': |
| dataset = load_dataset('openai/gsm8k', 'main') |
| dataset_type = args.split |
| elif args.dataset =='math500': |
| dataset = load_dataset("HuggingFaceH4/MATH-500") |
| dataset_type = 'test' |
| elif args.dataset == 'aime': |
| dataset = load_dataset('AI-MO/aimo-validation-aime') |
| dataset_type = 'train' |
| else: |
| raise ValueError('Unknown dataset') |
| |
| results = json.load(open(result_file, 'r')) |
|
|
| total, s_correct, f_correct = 0, 0, 0 |
| correct_length, wrong_length, all_length = [], [], [] |
| max_length, min_length = 0, 100000 |
| for idx, (ans, prob) in enumerate(zip(results, dataset[dataset_type])): |
| if args.num_samples is not None and idx >= args.num_samples: |
| break |
| |
| if args.dataset == 'aime' and idx < 60: |
| continue |
|
|
| strict_predict_ans, flexible_predict_ans = extract_boxed_text(ans['answer']) |
| target = prob['answer'] |
|
|
| target = process_target(target, args.dataset) |
|
|
| total += 1 |
| |
| if strict_predict_ans is not None or flexible_predict_ans is not None: |
| try: |
| correct_flag = False |
| if grade_answer(strict_predict_ans, target): |
| s_correct += 1 |
| f_correct += 1 |
| correct_flag = True |
| elif grade_answer(flexible_predict_ans, target): |
| f_correct += 1 |
| correct_flag = True |
|
|
| |
| except: |
| |
| pass |
| else: |
| |
| pass |
|
|
| |
| |
|
|
| print(f"Strict Match Accuracy = {s_correct}/{total} = {s_correct/total}") |
| print(f"Soft Match Accuracy = {f_correct}/{total} = {f_correct/total}") |
|
|
|
|