AceReason Evaluation Toolkit 완전 분석 - NVIDIA 수학/코딩 평가 시스템 심층 가이드
📋 문서 개요
본 문서는 AceReason Evaluation Toolkit의 전체 동작 과정을 단계별로 상세하게 분석한 자료입니다. 추론부터 채점까지의 전체 파이프라인과 각 컴포넌트의 역할, 채점 방식의 세부사항을 포함합니다.
🎯 시스템 개요
AceReason Evaluation Toolkit은 다음 두 가지 주요 평가를 수행합니다:
- AIME (American Invitational Mathematics Examination): 고난도 수학 문제 추론 평가
- LiveCodeBench: 실시간 코딩 능력 평가
각 평가는 생성(Generation) → 채점(Evaluation) 의 2단계 파이프라인으로 구성됩니다.
🏗️ 전체 아키텍처 플로우
graph TB
A[사용자 실행 요청] --> B{평가 유형 선택}
B -->|AIME| C[run_aime.sh]
B -->|LiveCodeBench| D[run_livecodebench.sh]
C --> E[generate_aime.sh 실행]
D --> F[generate_livecodebench.sh 실행]
E --> G[AIME 다중 시드 병렬 추론]
F --> H[LiveCodeBench 다중 GPU 분할 추론]
G --> I[evaluate_aime.py 채점]
H --> J[evaluate_livecodebench.py 채점]
I --> K[AIME 결과 집계]
J --> L[LiveCodeBench 결과 집계]
K --> M[최종 정확도 리포트]
L --> M
🔄 AIME 평가 프로세스 상세 분석
Phase 1: 추론 생성 단계
1.1 초기 설정 및 시드 분배
# run_aime.sh에서 모델별 시드 설정
if [ "$MODEL_NAME" == "nvidia/AceReason-Nemotron-7B" ]; then
seed_list_aime24=(121 131 141 151 161 171 181 191)
seed_list_aime25=(111 222 333 444 555 666 777 888)
MODEL_TYPE="r1"
elif [ "$MODEL_NAME" == "nvidia/AceReason-Nemotron-14B" ]; then
seed_list_aime24=(111 222 333 444 555 666 777 888)
seed_list_aime25=(111 222 333 444 555 666 777 888)
MODEL_TYPE="r1"
elif [ "$MODEL_NAME" == "nvidia/AceReason-Nemotron-1.1-7B" ]; then
seed_list_aime24=(100 200 300 400 500 600 700 800)
seed_list_aime25=(100 200 300 400 500 600 700 800)
MODEL_TYPE="qwen"
fi
핵심 특징:
- 다중 시드: 8개 서로 다른 시드로 통계적 신뢰성 확보
- 모델별 차별화: 각 모델에 최적화된 시드 및 템플릿 적용
- 데이터셋 분리: AIME 2024, AIME 2025 별도 평가
1.2 병렬 추론 실행
graph LR
A[generate_aime.sh] --> B[GPU 수 설정: 8개]
B --> C[데이터 분할: 30문제 전체]
C --> D[시드별 병렬 실행]
D --> E[GPU 0: seed+0]
D --> F[GPU 1: seed+1]
D --> G[GPU 2: seed+2]
D --> H[GPU 3: seed+3]
D --> I[GPU 4: seed+4]
D --> J[GPU 5: seed+5]
D --> K[GPU 6: seed+6]
D --> L[GPU 7: seed+7]
E --> M[VLLM 추론 엔진]
F --> M
G --> M
H --> M
I --> M
J --> M
K --> M
L --> M
추론 파라미터:
BSZ=30 # 배치 크기: 전체 문제 수
TOTAL=30 # 총 문제 수 (AIME)
GPUS=8 # GPU 수
OUT_SEQ_LEN=32768 # 최대 출력 시퀀스 길이
top_p=0.95 # Nucleus 샘플링
temperature=0.6 # 온도 매개변수
1.3 모델별 템플릿 처리
AceReason r1 모델 (7B, 14B):
def apply_r1_template(problem):
return f"""<|im_start|>user
{problem}
<|im_end|>
<|im_start|>assistant
<think>
{reasoning_process}
</think>
{final_answer}
<|im_end|>"""
AceReason Qwen 모델 (1.1-7B):
def apply_qwen_template(problem):
return f"""<|im_start|>user
{problem}<|im_end|>
<|im_start|>assistant
{response}<|im_end|>"""
Phase 2: 채점 및 평가 단계
2.1 답안 추출 프로세스
graph TD
A[모델 생성 텍스트] --> B[패턴 매칭 시도]
B --> C{\\boxed{} 패턴}
C -->|발견| D[LaTeX 박스 내용 추출]
C -->|미발견| E{**답** 패턴}
E -->|발견| F[강조 텍스트 추출]
E -->|미발견| G{\\[...\\] 패턴}
G -->|발견| H[수식 환경 추출]
G -->|미발견| I{is \\(...\\) 패턴}
I -->|발견| J[인라인 수식 추출]
I -->|미발견| K[추출 실패]
D --> L[답안 정규화]
F --> L
H --> L
J --> L
패턴 매칭 순서:
\\boxed{((?:[^{}]|\\{(?:[^{}]|\\{[^{}]*\\})*\\})*)}
- LaTeX boxed 형식\\*\\*(.*?)\\*\\*
- 마크다운 강조 형식\\\\\\[\\n(.*?)\\n\\\\\\]
- LaTeX 수식 환경is \\\\\\((.*?)\\\\\\)
- 인라인 수식\\\\\\[\\\\n(.*?)\\\\n\\\\\\]
- 대체 수식 환경
2.2 답안 정규화 (math_answer_cleaning)
def math_answer_cleaning(answer):
# 1. \\text{} 래핑 제거
extracted_content = is_completely_wrapped_by_text(answer)
answer = extracted_content if extracted_content else answer
# 2. 수학 표기 정규화
answer = answer.replace(",\\!", "").replace("{,}", "").replace("\\$", "")
answer = answer.replace("dfrac{", "frac{").replace("tfrac{", "frac{")
answer = answer.replace("^\\circ", "").replace("^{\\circ}", "")
# 3. 불필요한 텍스트 제거
answer = answer.replace("\\quad", "")
answer = re.sub(r'\\\\,\\\\text\\{.*?\\}', '', answer)
answer = re.sub(r'\\\\text\\{.*?\\}', '', answer)
# 4. 지수 표기 정규화
answer = re.sub(r'([+-]?\\d*\\.?\\d+)[\\\\]times10\\^{([+-]?\\d+)}', r'\\1e\\2', answer)
answer = re.sub(r'([+-]?\\d*\\.?\\d+)[\\\\]times10\\^([+-]?\\d+)', r'\\1e\\2', answer)
# 5. 함수 표기 처리 (f(x)=ax+b → ax+b)
func_pattern = r'^[a-zA-Z_]\\w*\\([a-zA-Z_]\\w*\\)$'
if "=" in answer and (re.match(func_pattern, answer.split("=")[0]) or len(answer.split("=")[0])<=3):
answer = answer.split("=", 1)[1]
return answer.lower().replace(" ", "").replace("\\n", "").replace(",", "")
2.3 수학적 동치성 검증
graph TD
A[정규화된 답안] --> B{문자열 완전 일치}
B -->|일치| C[True 반환]
B -->|불일치| D{숫자 형식 검사}
D -->|둘 다 숫자| E[수치적 동치성 검사]
D -->|문자 포함| F{SymPy 파싱 가능}
F -->|가능| G[기호적 동치성 검사]
F -->|불가능| H[False 반환]
E --> I{오차 범위 내 일치}
I -->|일치| C
I -->|불일치| H
G --> J[SymPy simplify 적용]
J --> K{차이가 0인가}
K -->|0| C
K -->|0 아님| H
수치적 동치성 검사:
def numeric_equal(prediction: float, reference: float):
return isclose(prediction, reference, rel_tol=1e-4, abs_tol=1e-6)
기호적 동치성 검사:
def symbolic_equal(a, b):
try:
# LaTeX → SymPy 변환
expr_a = latex2sympy(a) if '\\' in a else parse_expr(a)
expr_b = latex2sympy(b) if '\\' in b else parse_expr(b)
# 차이가 0인지 확인
return simplify(expr_a - expr_b) == 0
except:
return False
2.4 다중 시드 결과 집계
def get_answer_by_majority_voting(output_list):
"""다수결 투표로 최종 답안 결정"""
extracted_answers = []
for output in output_list:
answer = extract_answer(output)
if answer: extracted_answers.append(answer)
if not extracted_answers:
return None
# 가장 빈번한 답안 선택
counter = Counter(extracted_answers)
return counter.most_common(1)[0][0]
🚀 LiveCodeBench 평가 프로세스 상세 분석
Phase 1: 추론 생성 단계
1.1 데이터 분할 및 병렬 처리
# generate_livecodebench.sh에서 데이터 분할
BSZ=132 # 배치 크기
TOTAL=1055 # 총 문제 수
GPUS=8 # GPU 수
chunk=$(( (TOTAL + GPUS - 1) / GPUS )) # 132문제씩 분할
for (( gpu=0; gpu<GPUS; gpu++ )); do
start=$(( gpu * chunk ))
end=$(( start + chunk ))
(( end > TOTAL )) && end=$TOTAL
echo "GPU $gpu: processing [$start, $end)..."
# inference.py 실행
done
graph LR
A[1055개 문제] --> B[8개 GPU로 분할]
B --> C[GPU 0: 0-131]
B --> D[GPU 1: 132-263]
B --> E[GPU 2: 264-395]
B --> F[GPU 3: 396-527]
B --> G[GPU 4: 528-659]
B --> H[GPU 5: 660-791]
B --> I[GPU 6: 792-923]
B --> J[GPU 7: 924-1055]
1.2 코드 생성 프로세스
문제 구조:
{
"question_id": "unique_identifier",
"question_content": "문제 설명 및 요구사항",
"starter_code": "def solution():\\n pass",
"private_test_cases": "base64_encoded_test_cases"
}
생성된 코드 형식:
```python
def solution():
# 모델이 생성한 솔루션 코드
return result
```
Phase 2: 코드 검증 및 채점
2.1 코드 추출 프로세스
def has_code(response):
"""생성된 응답에서 Python 코드 추출"""
pattern = r"```python(?:[a-zA-Z0-9]*)\\n(.*?)```"
matches = re.findall(pattern, response, re.DOTALL)
return matches
2.2 테스트 케이스 실행 파이프라인
graph TD
A[생성된 코드] --> B[코드 추출 및 정리]
B --> C[스타터 코드와 병합]
C --> D[안전성 검사]
D --> E{안전한 코드인가}
E -->|위험| F[실행 거부]
E -->|안전| G[샌드박스 환경 구성]
G --> H[테스트 케이스 압축 해제]
H --> I[각 테스트 케이스 실행]
I --> J{시간 제한 내 완료}
J -->|시간 초과| K[타임아웃 처리]
J -->|정상 완료| L[출력 결과 비교]
L --> M{모든 테스트 통과}
M -->|통과| N[정답 처리]
M -->|실패| O[오답 처리]
K --> O
F --> O
2.3 안전성 검증 (code_verifier.py)
def is_safe_code(code):
"""코드 안전성 검사"""
dangerous_patterns = [
r'import\s+os',
r'import\s+subprocess',
r'import\s+sys',
r'__import__',
r'eval\s*\(',
r'exec\s*\(',
r'open\s*\(',
r'file\s*\(',
r'input\s*\(',
r'raw_input\s*\('
]
for pattern in dangerous_patterns:
if re.search(pattern, code, re.IGNORECASE):
return False
return True
2.4 다중 프로세스 테스트 실행
def check_correctness(problem_to_check, timeout=5, debug=False):
"""안전한 멀티프로세싱으로 코드 실행"""
manager = multiprocessing.Manager()
result = manager.list()
metadata_list = manager.list()
total_timeout = (timeout + 1) * len(problem_to_check['input_output']) + 10
p = multiprocessing.Process(
target=_temp_run,
args=(problem_to_check, debug, result, metadata_list, timeout)
)
p.start()
p.join(timeout=total_timeout + 1)
if p.is_alive():
p.kill() # 강제 종료
return bool(result and np.all(np.array(result[0]) > 0))
📊 채점 시스템 상세 분석
AIME 채점 방식
1. 패턴 기반 답안 추출
patterns = [
r"\\\\boxed\\{((?:[^{}]|\\{(?:[^{}]|\\{[^{}]*\\})*\\})*)}", # LaTeX boxed
r"\\*\\*(.*?)\\*\\*", # 마크다운 강조
r"\\\\\\[\\n(.*?)\\n\\\\\\]", # LaTeX 수식 환경
r'is \\\\\\((.*?)\\\\\\)', # 인라인 수식
r"\\\\\\[\\\\n(.*?)\\\\n\\\\\\]" # 대체 수식 환경
]
2. 다단계 정규화 처리
- 1단계: LaTeX 명령어 정규화 (
\\dfrac
→\\frac
) - 2단계: 단위 및 기호 제거 (
^\\circ
,\\text{}
) - 3단계: 공백 및 구두점 정리
- 4단계: 과학적 표기법 통합 (
\\times10^{n}
→en
) - 5단계: 함수 표기 간소화 (
f(x)=y
→y
)
3. 동치성 검증 알고리즘
def math_equal(prediction, reference):
# 1차: 문자열 완전 일치
if str(prediction).lower() == str(reference).lower():
return True
# 2차: 수치적 동치성 (상대오차 1e-4, 절대오차 1e-6)
if is_digit(prediction) and is_digit(reference):
return numeric_equal(parse_digits(prediction), parse_digits(reference))
# 3차: 기호적 동치성 (SymPy를 통한 수학적 검증)
try:
expr1 = latex2sympy(prediction)
expr2 = latex2sympy(reference)
return simplify(expr1 - expr2) == 0
except:
return False
LiveCodeBench 채점 방식
1. 실행 기반 검증
def run_test(problem, timeout=5):
"""실제 코드 실행을 통한 검증"""
test_cases = problem['input_output']
results = []
for test_case in test_cases:
try:
# 사용자 함수 실행
result = exec_with_timeout(
problem['code'],
test_case['input'],
timeout
)
# 출력 비교
if result == test_case['output']:
results.append(1) # 정답
else:
results.append(0) # 오답
except Exception:
results.append(-1) # 실행 오류
return results
2. 보안 샌드박스
- 프로세스 격리: 각 코드 실행을 별도 프로세스에서 수행
- 시간 제한: 테스트 케이스당 5초 제한
- 메모리 제한: 과도한 메모리 사용 방지
- 파일 시스템 접근 차단: 위험한 시스템 호출 차단
⚡ 성능 최적화 전략
1. 병렬 처리 최적화
graph TD
A[마스터 프로세스] --> B[GPU 0 작업자]
A --> C[GPU 1 작업자]
A --> D[GPU 2 작업자]
A --> E[GPU 3 작업자]
A --> F[GPU 4 작업자]
A --> G[GPU 5 작업자]
A --> H[GPU 6 작업자]
A --> I[GPU 7 작업자]
B --> J[VLLM 추론 엔진]
C --> J
D --> J
E --> J
F --> J
G --> J
H --> J
I --> J
J --> K[결과 수집 및 집계]
2. 메모리 효율성
VLLM 최적화:
- PagedAttention: KV 캐시 효율적 관리
- 동적 배치: 가변 길이 시퀀스 최적화
- 메모리 공유: GPU 간 모델 가중치 공유
배치 처리 전략:
# AIME: 전체 문제를 한 번에 처리
BSZ = 30 # = 총 문제 수
# LiveCodeBench: GPU 메모리에 맞춰 조정
BSZ = 132 # 132문제씩 배치 처리
3. 캐싱 메커니즘
결과 캐싱:
- 시드별 결과를 개별 JSONL 파일로 저장
- 중간 결과 체크포인트 지원
- 실패 시 재시작 가능
모델 캐싱:
- 모델 가중치를 GPU 메모리에 상주
- 토크나이저 사전 로드
- 컴파일된 CUDA 커널 재사용
🔍 오류 처리 및 복구
1. 타임아웃 처리
class TimeoutException(Exception):
pass
def timeout_handler(signum, frame):
raise TimeoutException("연산 시간 초과")
signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(5) # 5초 제한
try:
result = compute_math_expression(expr)
finally:
signal.alarm(0) # 타임아웃 해제
2. 메모리 부족 처리
def handle_oom_error():
"""GPU 메모리 부족 시 배치 크기 자동 조정"""
current_batch_size = get_current_batch_size()
new_batch_size = max(1, current_batch_size // 2)
print(f"OOM 감지: 배치 크기 {current_batch_size} → {new_batch_size}")
return new_batch_size
3. 프로세스 복구
def robust_execution(func, max_retries=3):
"""실패 시 재시도 로직"""
for attempt in range(max_retries):
try:
return func()
except Exception as e:
print(f"시도 {attempt + 1} 실패: {e}")
if attempt == max_retries - 1:
raise
time.sleep(2 ** attempt) # 지수 백오프
📈 결과 분석 및 리포팅
1. 통계적 분석
def compute_statistics(results):
"""다중 시드 결과의 통계적 분석"""
accuracies = [result['accuracy'] for result in results]
return {
'mean': np.mean(accuracies),
'std': np.std(accuracies),
'min': np.min(accuracies),
'max': np.max(accuracies),
'confidence_interval': compute_confidence_interval(accuracies)
}
2. 세분화된 분석
AIME 분석:
- 연도별 성능 (2024 vs 2025)
- 문제 난이도별 성능
- 수학 영역별 성능 (대수, 기하, 수론 등)
LiveCodeBench 분석:
- 기간별 성능 (2023.05-2024.05)
- 버전별 성능 (v5 vs v6)
- 월별 성능 트렌드
3. 출력 형식
{
"model": "nvidia/AceReason-Nemotron-1.1-7B",
"dataset": "aime24",
"total_problems": 30,
"seeds": [100, 200, 300, 400, 500, 600, 700, 800],
"results": {
"individual_accuracies": [0.67, 0.70, 0.63, ...],
"mean_accuracy": 0.6625,
"std_accuracy": 0.0234,
"confidence_interval": [0.6421, 0.6829]
},
"detailed_results": [
{
"seed": 100,
"correct": 20,
"total": 30,
"accuracy": 0.6667
}
]
}
🔧 실행 명령어 요약
AIME 평가 실행
# 전체 AIME 평가 (2024 + 2025)
bash run_aime.sh nvidia/AceReason-Nemotron-1.1-7B output_folder
# 개별 시드 실행
bash generate_aime.sh nvidia/AceReason-Nemotron-1.1-7B 100 aime24 output_folder qwen
# 채점만 실행
python evaluate_aime.py --modelfolder output_folder --dataset data/aime24.jsonl
LiveCodeBench 평가 실행
# 전체 LiveCodeBench 평가
bash run_livecodebench.sh nvidia/AceReason-Nemotron-1.1-7B output_folder
# 개별 시드 실행
bash generate_livecodebench.sh nvidia/AceReason-Nemotron-1.1-7B 111 output_folder qwen
# 채점 실행 (자동으로 수행됨)
python evaluate_livecodebench.py
📊 성능 벤치마크
시스템 요구사항
- GPU: 8x NVIDIA H100 80GB (권장), 더 적은 GPU도 가능
- 메모리: 640GB GPU 메모리 총합
- 스토리지: 1TB+ (모델 및 결과 저장)
예상 실행 시간
- AIME 평가: 45-60분 (8개 시드, 60문제)
- LiveCodeBench 평가: 25-35분 (8개 시드, 1055문제)
- 총 평가 시간: 약 1.5-2시간
메모리 사용량
- 모델 로딩: 50-100GB (모델 크기에 따라)
- 추론 과정: 400-500GB (배치 처리 시)
- 결과 저장: 1-5GB (시드별 결과 파일)
🔮 결론
AceReason Evaluation Toolkit은 다음과 같은 특징을 가진 정교한 평가 시스템입니다:
🎯 핵심 강점
- 정확성: 수학적 동치성과 코드 실행 기반의 엄격한 채점
- 신뢰성: 다중 시드를 통한 통계적 신뢰성 확보
- 효율성: VLLM 기반 고성능 병렬 추론
- 안전성: 샌드박스 환경의 보안 코드 실행
- 확장성: 유연한 GPU 구성과 모듈러 아키텍처
🚀 기술적 혁신
- LaTeX2SymPy: 정교한 수학 표기법 파싱
- PagedAttention: 메모리 효율적인 대화형 추론
- 다중 패턴 매칭: robust한 답안 추출
- 프로세스 격리: 안전한 코드 실행 환경
이 시스템은 AI 모델의 복합적 추론 능력을 객관적이고 재현 가능한 방식으로 평가할 수 있는 industry-standard 도구로 자리잡을 수 있는 기술적 기반을 제공합니다.