| 8 | |
| 9 | |
| 10 | class ChoiceEvaluator(BaseEvaluator): |
| 11 | def __init__(self, prediction_key: str = "model_prediction"): |
| 12 | super().__init__(prediction_key) |
| 13 | |
| 14 | def _build_prompt(self, item: Dict) -> str: |
| 15 | question = item.get("question", "") |
| 16 | choices = item.get("choices", []) |
| 17 | image_paths_field = item.get("image_path") |
| 18 | |
| 19 | option_lines = [f"{chr(65 + i)}. {choice}" for i, choice in enumerate(choices)] |
| 20 | options_block = "\n".join(option_lines) |
| 21 | |
| 22 | text_parts = [ |
| 23 | f"Question: {question}", |
| 24 | "", |
| 25 | "Available options:", |
| 26 | options_block, |
| 27 | "", |
| 28 | "Please analyze the question and options carefully. Your answer must be exactly one of the provided options, and must be copied verbatim from the options above.", |
| 29 | "Return your answer using the format \\answer{...}, where the content inside the braces is exactly the text of your chosen option (not the option letter or number, and do not use \\box{} or any other wrapper).", |
| 30 | "For example, if you choose the option '~1700 cm⁻¹', you should return: \\answer{~1700 cm⁻¹}", |
| 31 | "Do not return just a value like '~1700 cm' or any partial/incomplete answer. The answer must match one of the options exactly.", |
| 32 | "", |
| 33 | "Your response:", |
| 34 | ] |
| 35 | |
| 36 | text_content = "\n".join(text_parts) |
| 37 | |
| 38 | # Check if there are images |
| 39 | image_paths = normalize_image_paths(image_paths_field) |
| 40 | |
| 41 | if image_paths: |
| 42 | assert all( |
| 43 | isinstance(p, str) for p in image_paths |
| 44 | ), f"image_paths should be List[str], got {image_paths}" |
| 45 | # Prepare image data |
| 46 | image_data = prepare_images_for_prompt(image_paths) |
| 47 | |
| 48 | if image_data: |
| 49 | # Return multimodal format |
| 50 | return {"text": text_content, "images": image_data} |
| 51 | |
| 52 | # Return pure text format |
| 53 | return text_content |
| 54 | |
| 55 | def _extract_prediction(self, response: str, item: Dict) -> str: |
| 56 | """只提取 \\answer{...} 内的内容""" |
| 57 | if not response: |
| 58 | return "" |
| 59 | answer_pattern = r"\\answer\{([^}]+)\}" |
| 60 | matches = re.findall(answer_pattern, response) |
| 61 | if matches: |
| 62 | return matches[-1].strip() |
| 63 | return "" |
| 64 | |
| 65 | def _calculate_accuracy(self, answer: str, prediction: str, item: Dict) -> bool: |
| 66 | """Calculate accuracy using string matching from MMAR.""" |
| 67 | choices = item.get("choices", []) |
no outgoing calls