| import json |
| import re |
|
|
|
|
| def extract_answer(solution): |
| """ |
| Extract predicted answer letter (A/B/C/D) from model output. |
| Returns (prediction, confidence) where confidence is one of: |
| 'last_line' - last line matches prompt format "Answer: X" |
| 'answer_tag' - "Answer: X" found anywhere (last occurrence) |
| 'boxed' - \boxed{X} |
| 'explicit' - explicit phrases like "the answer is X", "correct answer is X" |
| 'pattern' - weaker patterns like "Option X", "(X)", "**X**" |
| 'last_letter' - last standalone letter A-D in text (lowest confidence) |
| None - no answer found |
| """ |
| solution = str(solution) |
|
|
| |
| last_line = solution.strip().split('\n')[-1] |
| m = re.search(r'(?i)Answer\s*:\s*\**\s*([A-D])\b', last_line) |
| if m: |
| return m.group(1).upper(), 'last_line' |
|
|
| |
| matches = re.findall(r'(?i)Answer\s*:\s*\**\s*([A-D])\b', solution) |
| if matches: |
| return matches[-1].upper(), 'answer_tag' |
|
|
| |
| m = re.search(r'\\boxed\{([^}]*)\}', solution) |
| if m: |
| cm = re.search(r'\b([ABCD])\b', m.group(1)) |
| if cm: |
| return cm.group(1).upper(), 'boxed' |
|
|
| |
| explicit_patterns = [ |
| r'(?i)(?:the\s+)?correct\s+answer\s+is\s*:?\s*\**([A-D])\b', |
| r'(?i)(?:the\s+)?answer\s+is\s*:?\s*\**([A-D])\b', |
| r'(?i)(?:so|thus|therefore)[,\s]+(?:the\s+)?(?:correct\s+)?answer\s+is\s*:?\s*\**([A-D])\b', |
| r'(?i)I\s+(?:would\s+)?(?:choose|select|pick)\s+:?\s*\**([A-D])\b', |
| ] |
| for pattern in explicit_patterns: |
| matches = re.findall(pattern, solution) |
| if matches: |
| return matches[-1].upper(), 'explicit' |
|
|
| |
| weak_patterns = [ |
| r'(?i)is\s+option\s*:?\s*([A-D])\b', |
| r'(?i)\*\*Answer:\*\*\s*([A-D])\b', |
| r'(?i)Option\s+([A-D])\b', |
| r'\(([A-D])\)', |
| r'(?i)\b([A-D])\s+is\s+(?:correct|right)\b', |
| ] |
| for pattern in weak_patterns: |
| matches = re.findall(pattern, solution) |
| if matches: |
| return matches[-1].upper(), 'pattern' |
|
|
| |
| matches = re.findall(r'\b([A-D])\b', solution) |
| if matches: |
| return matches[-1].upper(), 'last_letter' |
|
|
| return None, None |
|
|
|
|
| |
| _STRICT_CONFIDENCE = {'last_line', 'answer_tag', 'boxed', 'explicit'} |
| _FLEXIBLE_CONFIDENCE = {'last_line', 'answer_tag', 'boxed', 'explicit', 'pattern', 'last_letter'} |
|
|
|
|
| def extract_boxed_text_sampling(document): |
| """Returns (strict_prediction, flexible_prediction) for sampling / pass@k use.""" |
| prediction, confidence = extract_answer(document) |
| if confidence in _STRICT_CONFIDENCE: |
| return prediction, prediction |
| elif confidence in _FLEXIBLE_CONFIDENCE: |
| return None, prediction |
| return None, None |
|
|
|
|
| def extract_boxed_text(document, expected_answer): |
| """Returns bool: whether the extracted answer matches expected_answer (0-indexed int).""" |
| prediction, _ = extract_answer(document) |
| if prediction is None: |
| return False |
| try: |
| return prediction.upper() == "ABCD"[expected_answer].upper() |
| except (IndexError, TypeError): |
| return False |
|
|
|
|
| def evaluation(result_file): |
| results = [] |
| with open(result_file, 'r') as f: |
| for line in f: |
| results.append(json.loads(line)) |
|
|
| total = 0 |
| s_correct = 0 |
| f_correct = 0 |
| no_answer = 0 |
|
|
| total_time, total_token = 0, 0 |
| total_steps = 0 |
| total_experts = 0 |
|
|
| confidence_counts = {} |
|
|
| for problem in results: |
| expected_answer = problem['answer_index'] |
| answer_text = problem['answer'] |
|
|
| prediction, confidence = extract_answer(answer_text) |
|
|
| total += 1 |
| total_time += problem['time'] |
| total_token += problem['tokens'] |
| total_steps += problem.get('steps', 0) |
| total_experts += problem.get('unique_experts_count', 0) |
|
|
| confidence_counts[confidence] = confidence_counts.get(confidence, 0) + 1 |
|
|
| if prediction is None: |
| no_answer += 1 |
| continue |
|
|
| correct = prediction.upper() == "ABCD"[expected_answer].upper() |
|
|
| if correct: |
| if confidence in _STRICT_CONFIDENCE: |
| s_correct += 1 |
| f_correct += 1 |
| else: |
| f_correct += 1 |
|
|
| print(f"Strict Accuracy = {s_correct}/{total} = {s_correct/total:.4f} (last_line/answer_tag/boxed/explicit)") |
| print(f"Flexible Accuracy = {f_correct}/{total} = {f_correct/total:.4f} (all patterns)") |
| print(f"No answer found = {no_answer}/{total}") |
| print(f"Confidence breakdown: {confidence_counts}") |
|
|
| return { |
| 'strict_accuracy': s_correct / total, |
| 'flexible_accuracy': f_correct / total, |
| 'strict_match': s_correct, |
| 'flexible_match': f_correct, |
| 'total': total, |
| 'no_answer': no_answer, |
| 'total_time': total_time, |
| 'total_token': total_token, |
| 'token/s': total_token / total_time, |
| 'avg_steps': total_steps / total if total > 0 else 0, |
| 'unique_experts': total_experts / total if total > 0 else 0, |
| 'confidence_breakdown': confidence_counts, |
| } |
|
|
|
|
| def time_evaluation(result_file): |
| results = [] |
| with open(result_file, 'r') as f: |
| for line in f: |
| results.append(json.loads(line)) |
|
|
| total_time, total_token, total = 0, 0, 0 |
| for problem in 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, |
| } |
|
|