e93507a09c
Lockfile supply-chain audit / lockfile supply-chain audit (push) Has been cancelled
Windows Studio GGUF CI / GPU prebuilt resolves without Visual Studio (push) Has been cancelled
Windows Studio GGUF CI / setup.ps1 unit tests (VS 2026 / CMake guard) (push) Has been cancelled
Windows Studio GGUF CI / real-VS detection (VS 2022) (push) Has been cancelled
Windows Studio GGUF CI / real-VS detection (VS 2026) (push) Has been cancelled
Windows Studio GGUF CI / VC++ runtime detect + install round-trip (windows-2025-vs2026) (push) Has been cancelled
Windows Studio GGUF CI / VC++ runtime detect + install round-trip (windows-latest) (push) Has been cancelled
Windows Studio Update CI / Studio Updating Tests (push) Has been cancelled
Wheel CI / Wheel build + content sanity + import smoke (push) Has been cancelled
Lint CI / Source lint (Python + shell + YAML + JSON + safety nets) (push) Has been cancelled
MLX CI on Mac M1 / dispatch (push) Has been cancelled
Security audit / advisory audit (pip + npm + cargo) (push) Has been cancelled
Security audit / pip scan-packages :: extras (push) Has been cancelled
Security audit / pip scan-packages :: studio (push) Has been cancelled
Security audit / pip scan-packages :: hf-stack (push) Has been cancelled
Security audit / npm scan-packages (Studio frontend tarballs) (push) Has been cancelled
Security audit / workflow-trigger lint (pull_request_target / cache-poisoning) (push) Has been cancelled
Security audit / pytest tests/security (push) Has been cancelled
Security audit / npm provenance + new install-script diff (push) Has been cancelled
Studio API CI / Studio API & Auth Tests (push) Has been cancelled
Backend CI / (Python 3.10) (push) Has been cancelled
Backend CI / (Python 3.11) (push) Has been cancelled
Backend CI / (Python 3.12) (push) Has been cancelled
Backend CI / (Python 3.13) (push) Has been cancelled
Backend CI / Repo tests (CPU) (push) Has been cancelled
Frontend CI / Frontend build + bundle sanity (push) Has been cancelled
Studio GGUF CI / OpenAI, Anthropic API tests (push) Has been cancelled
Studio GGUF CI / Tool calling Tests (push) Has been cancelled
Studio GGUF CI / JSON, images (push) Has been cancelled
Mac Studio GGUF CI / OpenAI, Anthropic API tests (push) Has been cancelled
Mac Studio GGUF CI / Tool calling Tests (push) Has been cancelled
Mac Studio GGUF CI / JSON, images (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-14) (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-15) (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-26) (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-15-intel) (push) Has been cancelled
Mac Studio API CI / Studio API & Auth Tests (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-26-intel) (push) Has been cancelled
Mac Studio UI CI / Chat UI Tests (push) Has been cancelled
Studio Tauri CI / Tauri Linux debug build (no codesign) (push) Has been cancelled
Mac Studio Update CI / Studio Updating Tests (push) Has been cancelled
Studio UI CI / Chat UI Tests (push) Has been cancelled
Windows Studio API CI / Studio API & Auth Tests (push) Has been cancelled
Windows Studio UI CI / Chat UI Tests (push) Has been cancelled
Studio Update CI / Studio Updating Tests (push) Has been cancelled
Core / Core (HF=default + TRL=default) (push) Has been cancelled
Core / Core (HF=4.57.6 + TRL<1) (push) Has been cancelled
Core / Core (HF=latest + TRL=latest) (push) Has been cancelled
Core / llama.cpp build + smoke (push) Has been cancelled
Windows Studio GGUF CI / OpenAI, Anthropic API tests (push) Has been cancelled
Windows Studio GGUF CI / Tool calling Tests (push) Has been cancelled
Windows Studio GGUF CI / JSON, images (push) Has been cancelled
Windows Studio GGUF CI / Studio install + inference without Visual Studio (push) Has been cancelled
Studio export capability / capability (macos-latest) (push) Has been cancelled
Studio export capability / capability (ubuntu-latest) (push) Has been cancelled
Studio export capability / capability (windows-latest) (push) Has been cancelled
Cross-platform parity / parity (macos-latest) (push) Has been cancelled
Cross-platform parity / parity (windows-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
Studio load-orchestrator CI / test (push) Has been cancelled
494 lines
18 KiB
Python
494 lines
18 KiB
Python
"""
|
|
Evaluate language models on the combined AIME dataset
|
|
(test2024 + test2025-I + test2025-II).
|
|
"""
|
|
|
|
import json
|
|
import requests
|
|
import os
|
|
import re
|
|
import logging
|
|
from typing import List, Dict, Any
|
|
from tqdm import tqdm
|
|
from vllm import SamplingParams
|
|
|
|
|
|
def download_and_combine_aime_datasets(data_dir: str = "./data/aime") -> str:
|
|
"""Download all AIME datasets and combine them into a single file"""
|
|
|
|
datasets = {
|
|
"test2024": "https://raw.githubusercontent.com/GAIR-NLP/AIME-Preview/main/eval/data/aime/test2024.jsonl",
|
|
"test2025-I": "https://raw.githubusercontent.com/GAIR-NLP/AIME-Preview/main/eval/data/aime/test2025-I.jsonl",
|
|
"test2025-II": "https://raw.githubusercontent.com/GAIR-NLP/AIME-Preview/main/eval/data/aime/test2025-II.jsonl",
|
|
}
|
|
|
|
os.makedirs(data_dir, exist_ok = True)
|
|
combined_filepath = os.path.join(data_dir, "aime.jsonl")
|
|
|
|
if os.path.exists(combined_filepath):
|
|
print(f"Combined AIME dataset already exists at {combined_filepath}")
|
|
return combined_filepath
|
|
|
|
print("Downloading and combining AIME datasets...")
|
|
|
|
all_problems = []
|
|
global_id = 0
|
|
|
|
for dataset_name, url in datasets.items():
|
|
print(f" Downloading {dataset_name}...")
|
|
|
|
try:
|
|
response = requests.get(url)
|
|
response.raise_for_status()
|
|
|
|
# Tag each line with its source dataset + global ID
|
|
for line_num, line in enumerate(response.text.strip().split("\n")):
|
|
if line.strip():
|
|
try:
|
|
data = json.loads(line)
|
|
data["source_dataset"] = dataset_name
|
|
data["original_id"] = data.get("id", line_num)
|
|
data["global_id"] = global_id
|
|
global_id += 1
|
|
all_problems.append(data)
|
|
except json.JSONDecodeError as e:
|
|
print(
|
|
f" Warning: Error parsing line {line_num + 1} in {dataset_name}: {e}"
|
|
)
|
|
continue
|
|
|
|
except requests.RequestException as e:
|
|
print(f" Error downloading {dataset_name}: {e}")
|
|
continue
|
|
|
|
if all_problems:
|
|
with open(combined_filepath, "w", encoding = "utf-8") as f:
|
|
for problem in all_problems:
|
|
f.write(json.dumps(problem, ensure_ascii = False) + "\n")
|
|
|
|
print(f"✅ Combined {len(all_problems)} problems from {len(datasets)} datasets")
|
|
print(f" Saved to: {combined_filepath}")
|
|
|
|
for dataset_name in datasets.keys():
|
|
count = sum(1 for p in all_problems if p["source_dataset"] == dataset_name)
|
|
print(f" {dataset_name}: {count} problems")
|
|
|
|
else:
|
|
raise RuntimeError("No problems were successfully downloaded")
|
|
|
|
return combined_filepath
|
|
|
|
|
|
def load_aime_dataset(data_dir: str = "./data/aime") -> List[Dict[str, Any]]:
|
|
"""Load combined AIME dataset and format for evaluation"""
|
|
|
|
filepath = download_and_combine_aime_datasets(data_dir)
|
|
|
|
examples = []
|
|
with open(filepath, "r", encoding = "utf-8") as f:
|
|
for line_num, line in enumerate(f):
|
|
line = line.strip()
|
|
if line:
|
|
try:
|
|
data = json.loads(line)
|
|
|
|
formatted_example = {
|
|
"global_id": data.get("global_id", line_num),
|
|
"original_id": data.get("original_id", data.get("id", line_num)),
|
|
"source_dataset": data.get("source_dataset", "unknown"),
|
|
"problem": data["problem"],
|
|
"answer": str(data["answer"]), # Ensure answer is string
|
|
"solution": data.get("solution", ""),
|
|
"url": data.get("url", ""),
|
|
# Format as chat messages for the model
|
|
"prompt": [
|
|
{
|
|
"role": "system",
|
|
"content": "You are a mathematical problem solver. Solve the given problem step by step and provide your final answer clearly.",
|
|
},
|
|
{
|
|
"role": "user",
|
|
"content": f"Problem: {data['problem']}\n\nSolve this step by step and provide your final numerical answer.",
|
|
},
|
|
],
|
|
}
|
|
examples.append(formatted_example)
|
|
|
|
except json.JSONDecodeError as e:
|
|
print(f"Error parsing line {line_num + 1}: {e}")
|
|
continue
|
|
|
|
print(f"Loaded {len(examples)} problems from combined AIME dataset")
|
|
|
|
source_counts = {}
|
|
for example in examples:
|
|
source = example["source_dataset"]
|
|
source_counts[source] = source_counts.get(source, 0) + 1
|
|
|
|
for source, count in source_counts.items():
|
|
print(f" {source}: {count} problems")
|
|
|
|
return examples
|
|
|
|
|
|
def extract_aime_answer(response: str) -> str:
|
|
"""Extract numerical answer from AIME response"""
|
|
|
|
# AIME answers are integers 0-999; match "The answer is 123" etc.
|
|
patterns = [
|
|
r"(?:the )?(?:final )?answer is (\d{1,3})",
|
|
r"(?:therefore|thus|so),?\s*(?:the )?(?:final )?answer is (\d{1,3})",
|
|
r"\\boxed\{(\d{1,3})\}",
|
|
r"\$\\boxed\{(\d{1,3})\}\$",
|
|
r"(?:answer|result):\s*(\d{1,3})",
|
|
r"(?:^|\n)\s*(\d{1,3})\s*(?:\n|$)", # Standalone number
|
|
]
|
|
|
|
response_lower = response.lower().strip()
|
|
|
|
for pattern in patterns:
|
|
matches = re.findall(pattern, response_lower, re.MULTILINE | re.IGNORECASE)
|
|
if matches:
|
|
answer = matches[-1] # last match = the final answer
|
|
try:
|
|
num = int(answer)
|
|
if 0 <= num <= 999:
|
|
return str(num)
|
|
except ValueError:
|
|
continue
|
|
|
|
# Fallback: any 1-3 digit number, scanning from the end
|
|
numbers = re.findall(r"\b(\d{1,3})\b", response)
|
|
if numbers:
|
|
for num_str in reversed(numbers):
|
|
try:
|
|
num = int(num_str)
|
|
if 0 <= num <= 999:
|
|
return str(num)
|
|
except ValueError:
|
|
continue
|
|
|
|
return ""
|
|
|
|
|
|
def get_num_tokens(text, tokenizer_instance):
|
|
"""Count tokens in text"""
|
|
if not text:
|
|
return 0
|
|
encoding = tokenizer_instance(text, return_tensors = "pt")
|
|
return len(encoding["input_ids"][0])
|
|
|
|
|
|
def evaluate_model_aime(
|
|
model,
|
|
tokenizer,
|
|
model_type = "base",
|
|
lora_request = None,
|
|
temperature = 0.3,
|
|
n_sampling = 8,
|
|
max_tokens = 32768,
|
|
top_p = 0.95,
|
|
seed = 0,
|
|
):
|
|
"""Evaluate model on combined AIME dataset with official configuration"""
|
|
|
|
print(f"\n{'='*70}")
|
|
print(f"🧮 AIME EVALUATION - {model_type.upper()} MODEL")
|
|
print(f"Combined Dataset: test2024 + test2025-I + test2025-II")
|
|
print(f"{'='*70}")
|
|
|
|
try:
|
|
eval_dataset = load_aime_dataset()
|
|
except Exception as e:
|
|
print(f"Error loading dataset: {e}")
|
|
return None
|
|
|
|
if not eval_dataset:
|
|
print("No examples found in dataset")
|
|
return None
|
|
|
|
records = {}
|
|
input_tokens = []
|
|
output_tokens = []
|
|
correct_answers = 0
|
|
|
|
source_stats = {}
|
|
for example in eval_dataset:
|
|
source = example["source_dataset"]
|
|
if source not in source_stats:
|
|
source_stats[source] = {"total": 0, "correct": 0}
|
|
source_stats[source]["total"] += 1
|
|
|
|
sampling_params = SamplingParams(
|
|
temperature = temperature,
|
|
top_p = top_p,
|
|
max_tokens = max_tokens,
|
|
n = n_sampling, # Multiple samples per question
|
|
seed = seed,
|
|
)
|
|
|
|
print(f"\n🔧 Configuration:")
|
|
print(f" Temperature: {temperature}")
|
|
print(f" Samples per question: {n_sampling}")
|
|
print(f" Max tokens: {max_tokens}")
|
|
print(f" Top-p: {top_p}")
|
|
print(f" Seed: {seed}")
|
|
|
|
# Temporarily suppress verbose vllm/ray logging
|
|
original_levels = {}
|
|
loggers_to_suppress = [
|
|
"vllm",
|
|
"vllm.engine",
|
|
"vllm.worker",
|
|
"vllm.model_executor",
|
|
"vllm.executor",
|
|
"ray",
|
|
]
|
|
|
|
for logger_name in loggers_to_suppress:
|
|
logger = logging.getLogger(logger_name)
|
|
original_levels[logger_name] = logger.level
|
|
logger.setLevel(logging.WARNING)
|
|
|
|
try:
|
|
print(f"\n🚀 Evaluating {len(eval_dataset)} problems...")
|
|
|
|
with tqdm(total = len(eval_dataset), desc = "Processing AIME problems", unit = "problem") as pbar:
|
|
for task_id, item in enumerate(eval_dataset):
|
|
try:
|
|
prompt_text = tokenizer.apply_chat_template(
|
|
item["prompt"], add_generation_prompt = True, tokenize = False
|
|
)
|
|
|
|
input_tokens.append(get_num_tokens(prompt_text, tokenizer))
|
|
|
|
outputs = model.fast_generate(
|
|
[prompt_text],
|
|
sampling_params = sampling_params,
|
|
lora_request = lora_request,
|
|
use_tqdm = False,
|
|
)[0].outputs
|
|
|
|
responses = [output.text for output in outputs]
|
|
extracted_answers = [extract_aime_answer(response) for response in responses]
|
|
|
|
total_output_tokens = sum(
|
|
get_num_tokens(response, tokenizer) for response in responses
|
|
)
|
|
output_tokens.append(total_output_tokens)
|
|
|
|
# Correct if any sample matches ground truth
|
|
ground_truth = item["answer"]
|
|
correct_responses = [ans == ground_truth for ans in extracted_answers]
|
|
is_correct = any(correct_responses)
|
|
|
|
if is_correct:
|
|
correct_answers += 1
|
|
source_stats[item["source_dataset"]]["correct"] += 1
|
|
|
|
records[task_id] = {
|
|
"global_id": item["global_id"],
|
|
"original_id": item["original_id"],
|
|
"source_dataset": item["source_dataset"],
|
|
"problem": item["problem"],
|
|
"ground_truth": ground_truth,
|
|
"responses": responses,
|
|
"extracted_answers": extracted_answers,
|
|
"correct_responses": correct_responses,
|
|
"is_correct": is_correct,
|
|
"input_tokens": input_tokens[-1],
|
|
"output_tokens": total_output_tokens,
|
|
"n_correct": sum(correct_responses),
|
|
"n_total": len(responses),
|
|
"solution": item.get("solution", ""),
|
|
"url": item.get("url", ""),
|
|
}
|
|
|
|
current_accuracy = correct_answers / (task_id + 1) * 100
|
|
pbar.set_postfix(
|
|
{
|
|
"accuracy": f"{current_accuracy:.1f}%",
|
|
"correct": correct_answers,
|
|
"total": task_id + 1,
|
|
}
|
|
)
|
|
pbar.update(1)
|
|
|
|
except Exception as e:
|
|
print(f"\nError processing problem {task_id}: {str(e)}")
|
|
records[task_id] = {
|
|
"global_id": item.get("global_id", task_id),
|
|
"original_id": item.get("original_id", task_id),
|
|
"source_dataset": item.get("source_dataset", "unknown"),
|
|
"problem": item["problem"],
|
|
"ground_truth": item["answer"],
|
|
"error": str(e),
|
|
"is_correct": False,
|
|
}
|
|
pbar.update(1)
|
|
continue
|
|
|
|
finally:
|
|
for logger_name, level in original_levels.items():
|
|
logging.getLogger(logger_name).setLevel(level)
|
|
|
|
total_problems = len(eval_dataset)
|
|
accuracy = correct_answers / total_problems * 100
|
|
|
|
# Pass@k: fraction of problems where at least one of k samples is correct
|
|
pass_at_k_scores = []
|
|
for record in records.values():
|
|
if "n_correct" in record and "n_total" in record:
|
|
n_correct = record["n_correct"]
|
|
n_total = record["n_total"]
|
|
if n_correct > 0:
|
|
pass_at_k_scores.append(1.0)
|
|
else:
|
|
pass_at_k_scores.append(0.0)
|
|
|
|
pass_at_k = sum(pass_at_k_scores) / len(pass_at_k_scores) if pass_at_k_scores else 0
|
|
|
|
source_accuracies = {}
|
|
for source, stats in source_stats.items():
|
|
source_accuracies[source] = (
|
|
(stats["correct"] / stats["total"] * 100) if stats["total"] > 0 else 0
|
|
)
|
|
|
|
results = {
|
|
"model_type": model_type,
|
|
"dataset": "aime_combined",
|
|
"total_problems": total_problems,
|
|
"correct_answers": correct_answers,
|
|
"accuracy": accuracy,
|
|
"pass_at_k": pass_at_k * 100,
|
|
"source_stats": source_stats,
|
|
"source_accuracies": source_accuracies,
|
|
"temperature": temperature,
|
|
"n_sampling": n_sampling,
|
|
"max_tokens": max_tokens,
|
|
"top_p": top_p,
|
|
"seed": seed,
|
|
"avg_input_tokens": sum(input_tokens) / len(input_tokens) if input_tokens else 0,
|
|
"avg_output_tokens": sum(output_tokens) / len(output_tokens) if output_tokens else 0,
|
|
"max_input_tokens": max(input_tokens) if input_tokens else 0,
|
|
"max_output_tokens": max(output_tokens) if output_tokens else 0,
|
|
}
|
|
|
|
filename = f"aime_eval_combined_{model_type}_t{temperature}_n{n_sampling}.json"
|
|
with open(filename, "w", encoding = "utf-8") as f:
|
|
json.dump({"results": results, "records": records}, f, indent = 4)
|
|
|
|
print(f"\n{'='*70}")
|
|
print(f"📊 AIME EVALUATION RESULTS - {model_type.upper()}")
|
|
print(f"{'='*70}")
|
|
|
|
print(f"\n🎯 Overall Performance:")
|
|
print(f" Total problems: {total_problems:>6}")
|
|
print(f" Correct answers: {correct_answers:>6}/{total_problems} ({accuracy:>5.1f}%)")
|
|
print(f" Pass@{n_sampling}: {pass_at_k:>10.1f}%")
|
|
|
|
print(f"\n📈 Performance by Dataset:")
|
|
for source, stats in source_stats.items():
|
|
source_acc = source_accuracies[source]
|
|
print(f" {source:>12}: {stats['correct']:>3}/{stats['total']:>3} ({source_acc:>5.1f}%)")
|
|
|
|
print(f"\n🔧 Configuration:")
|
|
print(f" Temperature: {temperature}")
|
|
print(f" Samples per problem: {n_sampling}")
|
|
print(f" Max tokens: {max_tokens}")
|
|
print(f" Top-p: {top_p}")
|
|
print(f" Seed: {seed}")
|
|
|
|
print(f"\n📝 Token Statistics:")
|
|
print(f" Avg input tokens: {results['avg_input_tokens']:>10.1f}")
|
|
print(f" Avg output tokens: {results['avg_output_tokens']:>10.1f}")
|
|
print(f" Max input tokens: {results['max_input_tokens']:>10}")
|
|
print(f" Max output tokens: {results['max_output_tokens']:>10}")
|
|
|
|
if accuracy >= 50:
|
|
tier = "🏆 EXCEPTIONAL"
|
|
elif accuracy >= 30:
|
|
tier = "✅ EXCELLENT"
|
|
elif accuracy >= 20:
|
|
tier = "🎯 VERY GOOD"
|
|
elif accuracy >= 10:
|
|
tier = "⚠️ GOOD"
|
|
elif accuracy >= 5:
|
|
tier = "📈 FAIR"
|
|
else:
|
|
tier = "❌ NEEDS IMPROVEMENT"
|
|
|
|
print(f"\n🎖️ AIME Performance: {tier} ({accuracy:.1f}%)")
|
|
print(f"\n💾 Detailed results saved to: {filename}")
|
|
print(f"\n{'='*70}")
|
|
|
|
return results
|
|
|
|
|
|
def compare_aime_results(all_results):
|
|
"""Generate comprehensive comparison for AIME evaluation results"""
|
|
print(f"\n{'='*80}")
|
|
print("COMPREHENSIVE AIME MODEL COMPARISON")
|
|
print(f"{'='*80}")
|
|
|
|
print(f"{'Model':<15} {'Accuracy %':<12} {'Pass@K %':<10} {'Correct':<8} {'Total':<8}")
|
|
print("-" * 80)
|
|
|
|
for result in all_results:
|
|
print(
|
|
f"{result['model_type']:<15} "
|
|
f"{result['accuracy']:<12.1f} "
|
|
f"{result['pass_at_k']:<10.1f} "
|
|
f"{result['correct_answers']:<8} "
|
|
f"{result['total_problems']:<8}"
|
|
)
|
|
|
|
if len(all_results) > 1:
|
|
print(f"\n{'='*50}")
|
|
print("IMPROVEMENT ANALYSIS")
|
|
print(f"{'='*50}")
|
|
|
|
base_result = all_results[0] # first is the base model
|
|
|
|
for i, result in enumerate(all_results[1:], 1):
|
|
print(f"\n{result['model_type']} vs {base_result['model_type']}:")
|
|
|
|
accuracy_improvement = result["accuracy"] - base_result["accuracy"]
|
|
pass_k_improvement = result["pass_at_k"] - base_result["pass_at_k"]
|
|
|
|
print(f" Accuracy improvement: {accuracy_improvement:+.1f}%")
|
|
print(f" Pass@K improvement: {pass_k_improvement:+.1f}%")
|
|
|
|
print(f"\n{'='*50}")
|
|
print("PERFORMANCE BY DATASET")
|
|
print(f"{'='*50}")
|
|
|
|
if all_results and "source_accuracies" in all_results[0]:
|
|
datasets = list(all_results[0]["source_accuracies"].keys())
|
|
|
|
print(f"{'Model':<15}", end = "")
|
|
for dataset in datasets:
|
|
print(f"{dataset:<15}", end = "")
|
|
print()
|
|
print("-" * (15 + 15 * len(datasets)))
|
|
|
|
for result in all_results:
|
|
print(f"{result['model_type']:<15}", end = "")
|
|
for dataset in datasets:
|
|
accuracy = result["source_accuracies"].get(dataset, 0)
|
|
print(f"{accuracy:<15.1f}", end = "")
|
|
print()
|
|
|
|
comparison_data = {
|
|
"summary": all_results,
|
|
"best_model": max(all_results, key = lambda x: x["accuracy"]),
|
|
}
|
|
|
|
with open("aime_model_comparison.json", "w") as f:
|
|
json.dump(comparison_data, f, indent = 4)
|
|
|
|
print(
|
|
f"\nBest performing model: {comparison_data['best_model']['model_type']} "
|
|
f"({comparison_data['best_model']['accuracy']:.1f}% accuracy)"
|
|
)
|