Ollama 벤치마크
온프레미스로 띄운 사후 훈련 모델이 실제 과제를 얼마나 잘 푸는지는 벤치마크로 잽니다.
Ollama API를 그대로 활용해 서버에 문항을 하나씩 POST /api/chat으로 보내 채점하면, 추가 라이브러리 없이 로컬 모델의 외재적 성능을 바로 확인할 수 있습니다.
Qwen3·Gemma 같은 최신 모델은 추론(thinking)이 기본으로 켜져 있어 답하기 전에 생각을 전개하는데, 이 생각 과정이 점수를 크게 좌우하므로 끄지 않습니다 — 끄면 특히 수학·과학에서 성능이 급락합니다.
긴 추론이 잘리지 않도록 문맥 길이(num_ctx)를 넉넉히 줍니다.
import os, re, requests
from datasets import load_dataset
OLLAMA_BASE_URL = os.environ.get("OLLAMA_HOST", "http://localhost:11434")
def 풀이_요청(모델, 질문, 문맥=32768, 생성=24576):
옵션 = {"num_ctx": 문맥, "num_predict": 생성, "seed": 0}
답 = requests.post(f"{OLLAMA_BASE_URL}/api/chat",
json={"model": 모델,
"messages": [{"role": "user", "content": 질문}],
"stream": False, "options": 옵션},
timeout=1200).json()["message"]
return 답.get("thinking", ""), 답.get("content", "") # 추론 과정, 최종 답think 인자를 따로 주지 않으면 모델의 기본값(추론 켜짐)을 따릅니다.
응답에서 추론은 thinking, 최종 답은 content로 분리되어 오므로, 답만 골라 채점하기 쉽습니다.
GSM8K(초등 수준 수리)로 채점하는 예입니다.
데이터 = load_dataset("gsm8k", "main", split="test").select(range(30))
def 정수(글):
숫자 = re.findall(r"-?\d+", 글.replace(",", ""))
return 숫자[-1] if 숫자 else None
맞힘 = 0
for 문항 in 데이터:
정답 = 정수(문항["answer"].split("####")[-1])
_, 최종답 = 풀이_요청("qwen3:latest",
문항["question"] + "\n\n최종 정수 답을 '#### ' 뒤에 적으세요.")
맞힘 += (정수(최종답) == 정답)
print(f"{맞힘} / {len(데이터)}")30 / 30같은 방식을 더 어려운 벤치마크와 다른 모델에도 적용해, 두 계열의 소형 사후 훈련 모델을 비교합니다.
| 벤치마크 (난이도) | qwen3:latest | gemma4:e4b |
|---|---|---|
| GSM8K — 초등 수리 | 100% | 100% |
| GPQA-Diamond — 전문가급 과학 | 40% | 53% |
| AIME 2025 — 경시 수학 | 60% | 36% |
(GSM8K 15문항, GPQA·AIME 각 30문항, Ollama 양자화 모델, 추론 켜짐, seed 고정 — 절대 수치보다 경향을 봅니다.)
세 가지가 드러납니다. 첫째, GSM8K는 포화됐습니다 — 두 모델 모두 만점이어서 변별이 되지 않습니다. 요즘 모델에는 너무 쉬워, 상위권을 가르려면 GPQA·AIME 같은 더 어려운 과제가 필요합니다. 둘째, 과제 성격에 따라 강자가 갈립니다 — Gemma는 전문가급 과학 추론(GPQA)에서, Qwen은 경시 수학(AIME)에서 앞섭니다. '어느 모델이 더 낫다’는 단일 점수로 답할 수 없고, 쓰려는 과제에 맞춰 골라야 합니다. 셋째, 추론에 들인 시간이 성능과 맞닿습니다 — AIME에서 Qwen은 문항당 평균 130초를 생각해 60%를 맞혔고, Gemma는 48초로 36%였습니다. 어려운 문제일수록 더 길게 생각하는 모델이 유리합니다.
1규모를 키우면 — 매개변수와 벤치마크¶
같은 계열에서 매개변수 수를 키우며 GPQA-Diamond를 재 보면, 규모가 능력으로 이어지는 모습을 볼 수 있습니다.
| 계열 | 모델(크기 증가순) | 정확도 |
|---|---|---|
| Qwen3 | 0.6B | 20% |
| 1.7B | 20% | |
| 4B | 80% | |
8B (qwen3:latest) | 50% | |
| Gemma | e2b | 30% |
| e4b | 55% | |
| 12b | 70% | |
| 26b | 75% | |
| 31b | 80% |
(각 20문항, 모든 모델이 같은 문항, 추론 켜짐, Ollama 양자화.)
두 가지가 보입니다. 첫째, 작은 모델(약 2B 이하)은 GPQA에서 무작위(25%) 수준에 머뭅니다 — 전문가급 과학 추론은 일정 규모를 넘어서야 풀리기 시작합니다. 규모가 커지면 정답률이 뚜렷이 올라, Gemma 계열은 30% → 55% → 70% → 75% → 80%로 다섯 단계에 걸쳐 일관되게 증가합니다. 둘째, 곡선이 늘 매끄럽지는 않습니다 — Qwen3는 4B에서 80%로 치솟지만 8B에서 50%로 도로 내려갑니다. 같은 20문항을 풀었는데도 이런 역전이 나오는 건 표본이 20개로 작아 분산이 크기 때문입니다(한 문항이 5%p). 신뢰할 수 있는 규모 곡선을 그리려면 수백~수천 문항이 필요하며, 이렇게 작은 표본의 절대 수치는 경향의 참고로만 봐야 합니다.
규모와 손실(perplexity)의 매끄러운 거듭제곱 관계는 더 큰 표본으로 규모의 법칙 장에서 다룹니다.
2같은 점수, 다른 비용 — 속도와 메모리¶
품질만 보면 큰 모델을 고르면 그만이지만, 로컬 서빙에서는 속도와 메모리가 똑같이 중요합니다. 앞의 GPQA 점수에 같은 RTX 3090에서 잰 디코드 속도와 VRAM 점유를 나란히 놓으면, 비용의 축이 드러납니다.
| 모델 | 구조 | 디코드(tok/s) | VRAM(GB) | GPQA |
|---|---|---|---|---|
| e2b | 탄력적 (effective ~2B) | 87 | 1.8 | 30% |
| e4b | 탄력적 (effective ~4B) | 128 | 3.3 | 55% |
| 12b | 밀집 (dense) | 31 | 8.1 | 70% |
| 26b | MoE (전문가 128개 중 8개 활성) | 123 | 17.4 | 75% |
| 31b | 밀집 (dense) | 37 | 20.3 | 80% |
(디코드 속도 = 생성 토큰 수 ÷ 생성 시간, Ollama 양자화(Q4_K_M), VRAM은 /api/ps의 적재량. 단발 측정이라 경향으로 봅니다.)
속도가 크기 순서를 따르지 않는 점이 눈에 띕니다 — e4b(128)와 26b(123)가 빠르고, 그 사이의 12b(31)는 오히려 느립니다. gemma4 계열이 세 가지 아키텍처를 섞어 쓰기 때문입니다.
밀집(dense) — 12b·31b. 모든 매개변수가 매 토큰 계산에 참여하므로, 크기가 커질수록 디코드가 느려지고 VRAM도 비례해 늘어납니다.
탄력적(elastic) — e2b·e4b. 전체 가중치는 디스크에 두되 일부만 활성화하는 Gemma 3n 계열 설계로, 적은 VRAM과 빠른 속도를 냅니다.
전문가 혼합(MoE) — 26b. 전문가 128개 중 토큰마다 8개만 활성화(약 4B 활성)해, 밀집 모델(12b·31b)의 3~4배 속도(123 vs 31·37 tok/s)로 31b에 근접한 품질(75% vs 80%)을 냅니다.
핵심은 디코드 속도는 토큰마다 실제로 계산하는 활성(active) 매개변수에, VRAM은 적재해야 하는 전체(total) 매개변수에 좌우된다는 점입니다. MoE인 26b가 그 둘을 분리한 전형입니다 — 4B만큼만 계산해 빠르지만, 전문가 전체를 메모리에 올려야 해 17GB를 씁니다. 따라서 모델 선택은 단일 점수가 아니라 품질·속도·메모리의 삼각 절충입니다 — 에지·실시간이면 e4b, 품질이 최우선이면 31b, 둘의 균형점이면 MoE인 26b가 후보입니다.