hku_diffusion_dllm / reference /code /TAD /eval /parse_and_get_acc.py
Ouzhang's picture
Add files using upload-large-folder tool
d91766b verified
Raw
History Blame Contribute Delete
18.1 kB
# Copyright (c) Meta Platforms, Inc. and affiliates.
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
import json
import re
import os
import glob
import tiktoken
from collections import defaultdict
from parser_helper import is_equiv, remove_boxed, last_boxed_only_string
# from .parser_helper import is_equiv, remove_boxed, last_boxed_only_string
def count_effective_tokens(text):
if not text:
return 0
text = text.replace("<|endoftext|>", "")
enc = tiktoken.get_encoding("cl100k_base")
tokens = enc.encode(text)
return len(tokens)
def parse_gsm_answers(json_path=None, json_data=None):
if json_path:
with open(json_path, "r") as file:
data = json.load(file)
else:
data = json_data
total_correct = 0
total_processed = 0
total_effective_tokens = 0
processed_items = []
for item in data.get("generations", []):
total_processed += 1
ground_truth = item.get("ground_truth")
raw_generation = item.get("generations", "")
question = item.get("question", "")
# Count effective tokens
effective_tokens = count_effective_tokens(raw_generation)
total_effective_tokens += effective_tokens
parsed_answer = None
boxed_matches = re.findall(r"\\boxed{(.*?)}", raw_generation)
if boxed_matches:
for boxed_content in boxed_matches:
boxed_content = boxed_content.strip()
if boxed_content and boxed_content != "..." and not re.match(r"^\.+$", boxed_content):
try:
parsed_answer = float(boxed_content)
break
except ValueError:
numbers = re.findall(r"-?\d+\.?\d*", boxed_content)
if numbers:
try:
parsed_answer = float(numbers[0])
break
except ValueError:
pass
if parsed_answer is None:
answer_match = re.search(r"<answer>(.*?)</answer>", raw_generation, re.DOTALL)
if answer_match:
answer_text = answer_match.group(1).strip()
if answer_text:
try:
parsed_answer = float(answer_text)
except ValueError:
numbers = re.findall(r"-?\d+\.?\d*", answer_text)
if numbers:
try:
parsed_answer = float(numbers[-1])
except ValueError:
pass
if parsed_answer is None:
answer = raw_generation.split("<answer>")[-1]
answer = answer.split("</answer>")[0]
answer = answer.strip()
if answer:
try:
parsed_answer = float(answer)
except ValueError:
numbers = re.findall(r"-?\d+\.?\d*", answer)
if numbers:
try:
parsed_answer = float(numbers[-1])
except ValueError:
pass
is_correct = parsed_answer is not None and parsed_answer == ground_truth
if is_correct:
total_correct += 1
processed_items.append(
{
"question": question,
"raw_generation": raw_generation,
"extracted_answer": parsed_answer,
"ground_truth": ground_truth,
"is_correct": is_correct,
"effective_tokens": effective_tokens,
}
)
if "nfes" in item.keys():
processed_items[-1]["nfes"] = item["nfes"]
return (
total_correct,
total_processed,
processed_items,
total_effective_tokens,
)
def parse_math_answers(json_path=None, json_data=None):
if json_path:
with open(json_path, "r") as file:
data = json.load(file)
else:
data = json_data
total_correct = 0
total_processed = 0
total_effective_tokens = 0
processed_items = []
for item in data.get("generations", []):
total_processed += 1
question = item.get("question", "")
ground_truth = item.get("ground_truth", "")
raw_generation = item.get("generations", "")
# Count effective tokens
effective_tokens = count_effective_tokens(raw_generation)
total_effective_tokens += effective_tokens
parsed_answer = None
try:
parsed_answer = remove_boxed(last_boxed_only_string(raw_generation))
except:
parsed_answer = None
if not parsed_answer:
answer_match = re.search(r"<answer>(.*?)</answer>", raw_generation, re.DOTALL)
if answer_match:
parsed_answer = answer_match.group(1).strip()
is_correct = False
if parsed_answer is not None:
is_correct = is_equiv(parsed_answer, ground_truth)
if is_correct:
total_correct += 1
processed_items.append(
{
"question": question,
"raw_generation": raw_generation,
"extracted_answer": parsed_answer,
"ground_truth": ground_truth,
"is_correct": is_correct,
"effective_tokens": effective_tokens,
}
)
return (
total_correct,
total_processed,
processed_items,
total_effective_tokens,
)
def parse_countdown_answers(json_path=None, json_data=None):
if json_path:
with open(json_path, "r") as file:
data = json.load(file)
else:
data = json_data
total_correct = 0
total_processed = 0
total_effective_tokens = 0
processed_items = []
def validate_equation(equation_str, available_numbers):
"""Validate that equation only uses available numbers and each number once."""
try:
numbers_in_eq = [int(n) for n in re.findall(r"\d+", equation_str)]
available_numbers = sorted(available_numbers)
numbers_in_eq = sorted(numbers_in_eq)
return numbers_in_eq == available_numbers
except:
return False
def evaluate_equation(equation_str):
"""Safely evaluate the arithmetic equation."""
try:
allowed_pattern = r"^[\d+\-*/().\s]+$"
if not re.match(allowed_pattern, equation_str):
raise ValueError("Invalid characters in equation.")
result = eval(equation_str.strip(), {"__builtins__": None}, {})
return result
except Exception:
return float("Inf")
for item in data.get("generations", []):
total_processed += 1
question = item.get("question", "")
ground_truth = item.get("ground_truth", [])
generated_text = item.get("generations", "")
# Count effective tokens
effective_tokens = count_effective_tokens(generated_text)
total_effective_tokens += effective_tokens
# Extract available numbers and target from ground_truth
numbers = []
target = None
if isinstance(ground_truth, list) and len(ground_truth) == 2:
numbers = ground_truth[0]
target = ground_truth[1]
else:
# Fallback to parsing from question if ground_truth is not in expected format
numbers_match = re.search(r"Numbers: \[([\d, ]+)\]", question, re.IGNORECASE)
if numbers_match:
numbers_str = numbers_match.group(1)
numbers = [int(num.strip()) for num in numbers_str.split(",")]
target_match = re.search(r"Target: (\d+)", question, re.IGNORECASE)
if target_match:
target = int(target_match.group(1))
equation = ""
try:
equation = remove_boxed(last_boxed_only_string(generated_text))
except:
# Try to extract from answer tags
answer_match = re.search(r"<answer>(.*?)</answer>", generated_text, re.DOTALL)
if answer_match:
equation = answer_match.group(1).strip()
else:
equation = generated_text
# Replace LaTeX operators with Python operators
equation = equation.replace(r"\div", "/").replace(r"\times", "*").replace(r"\cdot", "*")
# Check for equation with equals sign and extract only the expression part
equation_match = re.search(r"([0-9+\-*/() ]+)=[0-9. ]+", equation)
if equation_match:
equation = equation_match.group(1).strip()
is_correct = False
result = None
# Validate and evaluate the equation
is_valid = validate_equation(equation, numbers)
if is_valid:
result = evaluate_equation(equation)
if target is not None and abs(result - target) < 1e-5:
is_correct = True
total_correct += 1
processed_items.append(
{
"question": question,
"extracted_answer": equation,
"evaluation_result": result,
"ground_truth": ground_truth,
"is_correct": is_correct,
"effective_tokens": effective_tokens,
}
)
return (
total_correct,
total_processed,
processed_items,
total_effective_tokens,
)
def parse_sudoku_answers(json_path=None, json_data=None):
if json_path:
with open(json_path, "r") as file:
data = json.load(file)
else:
data = json_data
total_solved = total_processed = 0
total_effective_tokens = 0
processed_items = []
for item in data.get("generations", []):
total_processed += 1
question = item.get("question", "")
ground_truth = item.get("ground_truth", "")
raw_generation = item.get("generations", "")
effective_tokens = count_effective_tokens(raw_generation)
total_effective_tokens += effective_tokens
puzzle_str = ""
if len(question) >= 16 and all(c.isdigit() or c == "0" for c in question[:16]):
puzzle_str = question[:16]
else:
match = re.search(r"Sudoku puzzle: ([0-9]{16})", question)
if match:
puzzle_str = match.group(1)
assert len(puzzle_str) == 16, f"Invalid puzzle string: {puzzle_str}"
empty_indices = [i for i in range(16) if puzzle_str[i] == "0"]
empty_cells = len(empty_indices)
solution_str = ""
patterns = [
r"<answer>.*?```\s*([\d\s]+)```",
r"<answer>(.*?)(?:<\|eot_id\|>|<\|endoftext\|>|</answer>)",
r"</answer>\s*(.*?)(?:<\|eot_id\|>|<\|endoftext\|>|$)",
r".*?(\d{16})\s*</answer>",
r"\b(\d{16})\b",
]
for pattern in patterns:
if solution_str:
break
match = re.search(pattern, raw_generation, re.DOTALL)
if match and match.group(1).strip():
solution_str = match.group(1).strip()
solution_str = re.sub(r"\s", "", solution_str)
if not solution_str:
correct_cells = 0
else:
if len(solution_str) < 16:
solution_str = solution_str + "0" * (16 - len(solution_str))
elif len(solution_str) > 16:
solution_str = solution_str[:16]
correct_cells = sum(1 for i in empty_indices if solution_str[i] == ground_truth[i])
accuracy = correct_cells / empty_cells if empty_cells > 0 else 0.0
solved = (
solution_str
and isinstance(ground_truth, str)
and len(solution_str) == 16
and len(ground_truth) == 16
and empty_cells > 0
and correct_cells == empty_cells
)
if solved:
total_solved += 1
print(f"Puzzle: {puzzle_str}")
print(f"GT : {ground_truth}")
print(f"Pred : {solution_str}")
print("-" * 40)
processed_items.append(
{
"question": question,
"raw_generation": raw_generation,
"extracted_answer": solution_str,
"ground_truth": ground_truth,
"empty_cells": empty_cells,
"correct_cells": correct_cells,
"accuracy": accuracy,
"solved": solved,
"effective_tokens": effective_tokens,
}
)
return (
total_solved,
total_processed,
processed_items,
total_effective_tokens * 8,
)
def extract_setup_name(filename):
"""Extract the setup name from the filename."""
match = re.match(r"(.+)_\d+_generations\.json$", filename)
if match:
return match.group(1)
return None
def aggregate_results(directory="."):
"""Aggregate results from all JSON files and save detailed results."""
# Find all JSON files matching the pattern
json_files = glob.glob(os.path.join(directory, "*_generations.json"))
# Dictionary to store aggregated results by setup
setups = defaultdict(
lambda: {
"correct": 0,
"processed": 0,
"accuracy": 0.0,
"questions": [],
"total_effective_tokens": 0,
}
)
for json_file in json_files:
filename = os.path.basename(json_file)
setup_name = extract_setup_name(filename)
if setup_name:
# print(f"Processing {filename}...")
if "gsm" in setup_name:
(
correct,
processed,
detailed_results,
total_effective_tokens,
) = parse_gsm_answers(json_path=json_file)
elif "math" in setup_name:
(
correct,
processed,
detailed_results,
total_effective_tokens,
) = parse_math_answers(json_path=json_file)
elif "countdown" in setup_name:
(
correct,
processed,
detailed_results,
total_effective_tokens,
) = parse_countdown_answers(json_path=json_file)
elif "sudoku" in setup_name:
(
correct,
processed,
detailed_results,
total_effective_tokens,
) = parse_sudoku_answers(json_path=json_file)
setups[setup_name]["correct"] += correct
setups[setup_name]["processed"] += processed
setups[setup_name]["total_effective_tokens"] += total_effective_tokens
setups[setup_name]["questions"].extend(detailed_results)
# Calculate final accuracy and save results
for setup, results in sorted(setups.items()):
results["accuracy"] = (
results["correct"] / results["processed"] * 100 if results["processed"] > 0 else 0
)
results["avg_effective_tokens"] = (
results["total_effective_tokens"] / results["processed"] if len(results["questions"]) > 0 else 0
)
if "nfes" in results["questions"][0].keys():
results["avg_nfes"] = sum(q["nfes"] for q in results["questions"]) / len(results["questions"])
else:
results["avg_nfes"] = 128
# Header
header_format = "{:<40} {:>12} {:>25} {:>25}"
print(header_format.format("Setup (task_model_seqlen_diffusteps)", "Accuracy", "Avg Effective Tokens", "Avg NFE"))
print("-" * 80)
# Data rows
row_format = "{:<40} {:>11.2f}% {:>25.2f} {:>25.0f}"
for setup, results in sorted(setups.items()):
print(row_format.format(setup, results["accuracy"], results["avg_effective_tokens"], results["avg_nfes"]))
print("=" * 80)
if __name__ == "__main__":
# aggregate_results(directory="save_dir/eval_results/eval_results_math_llada")
# aggregate_results(directory="save_dir/eval_results/elbo_eval_results_math_llada_spg_elbo_20251126_044820")
# aggregate_results(directory="save_dir/eval_results/eubo_eval_results_math_llada_spg_eubo_20251127_215615")
# aggregate_results(directory="save_dir/eval_results/eubo_eval_results_math_llada_2_spg_eubo_20251212_175807")
# aggregate_results(directory="save_dir/eval_results/mix_eval_results_math_llada_spg_mix_20251214_120515")
# aggregate_results(directory="save_dir/eval_results/mix_eval_results_math_llada_spg_mix_20251214_224212")
# aggregate_results(directory="save_dir/eval_results/mix_eval_results_math_llada_spg_mix_20251221_205521")
# aggregate_results(directory="/home/jwliu/dlm/SPG/save_dir/eval_results/elbo_eval_results_math_llada_spg_elbo_20251224_200822")
# aggregate_results(directory="save_dir/eval_results/eubo_eval_results_math_llada_spg_so_elbo_20260105_145030")
# aggregate_results(directory="save_dir/eval_results/mix_eval_results_math_llada_spg_mix_20251221_205521")
# aggregate_results(directory="save_dir/eval_results/eubo_eval_results_math_llada_spg_eubo_20251226_072542")
# aggregate_results(directory="save_dir/eval_results/d1_eval_results_math_llada_d1_20251224_154054")
# aggregate_results(directory="save_dir/eval_results/eubo_eval_results_math_llada_spg_so_early_elbo_20260105_195010")
# aggregate_results(directory="save_dir/eval_results/elbo_eval_results_math_llada_spg_generated_so_elbo_20260109_143814")
# aggregate_results(directory="save_dir/eval_results/elbo_eval_results_math_llada_spg_generated_so_elbo_20260110_104621")
aggregate_results(directory="/home/u2025104115/LLaDA/eval/eval_results/")