
LLM 벤치마크를 6개월 돌린 기록 — 4,791번의 실행이 남긴 것
반년간 모델을 계속 재보며 남은 실행 로그를 시간순으로 정리했다. 문제셋을 33번 고친 이유와, 처음 발행한 수치가 왜 틀렸는지까지 함께 적는다.
정정 (2026-08-27) 이 글의 처음 판에는 “4,725번 중 1,403번만 살아남았다(71%가 버려졌다)“고 썼다. 틀린 숫자다. 실제 유효 실행은 95%였다. 집계 도구가 날짜 필드 하나 때문에 3,102건을 조용히 건너뛰고 있었고, 나는 그 누락을 “품질 게이트에서 걸러진 실행”으로 잘못 읽었다. 아래 본문은 전부 정정된 수치이며, 무엇이 어떻게 틀렸는지는 맨 끝에 따로 적었다.
앞선 글에서 LLM 평가 시스템을 만든 이야기를 썼다. 이번에는 그걸 6개월 굴린 로그를 열어봤다.
실행 기록이 runs.jsonl 한 파일에 쌓여 있었다. 9.6MB, 4,791건이다. 2026년 3월 3일부터 8월 27일까지다.
먼저: 버려진 실행은 5%였다
집계에 쓸 수 있는 실행과 그렇지 않은 실행을 갈라보면 이렇다.
| 건수 | 비율 | |
|---|---|---|
| 전체 실행 | 4,791 | 100% |
| 점수로 쓸 수 있는 실행 | 4,553 | 95.0% |
| 제외 | 238 | 5.0% |
제외된 238건의 사유는 이렇다.
| 사유 | 건수 |
|---|---|
run_status=incomplete (중단됨) |
94 |
run_status=partial (부분 실행) |
72 |
is_partial / is_shard_run (샤드·부분 집계) |
72 |
집계 기준은 코드에 명시적으로 박아뒀다.
완료(
complete) + 품질 게이트 통과(pass) + 부분 실행·샤드 제외
“측정했다”와 “측정값으로 쓸 수 있다”는 다르다. 이걸 코드로 구분해두지 않으면 나중에 자기 데이터에 속는다. (그리고 이 글의 첫 판에서 나는 실제로 속았다. 종류는 좀 달랐지만.)
월별로 본 6개월
유효 실행만 세면 이렇게 흘러갔다.
| 월 | 유효 실행 | 평균 점수 | 그달 처음 등장한 모델 |
|---|---|---|---|
| 2026-03 | 647 | 55.3% | 21종 |
| 2026-04 | 3,521 | 52.2% | 28종 |
| 2026-05 | 60 | 39.1% | 2종 |
| 2026-06 | 41 | 66.8% | 2종 |
| 2026-07 | 110 | 80.0% | 8종 |
| 2026-08 | 174 | 75.6% | 5종 |
6개월 동안 모델 66종이 한 번 이상 올라갔다.
월별 평균 점수는 모델 난이도가 아니라 그달 뭘 테스트했는지를 반영한다. 게다가 문제셋 버전이 계속 바뀌었으니(아래 참고) 달끼리 직접 비교할 수 있는 숫자도 아니다. 이 열을 “모델이 좋아졌다/나빠졌다”로 읽으면 안 된다.
읽을 만한 건 패턴이다.
4월 한 달에 3,521건이 몰려 있다. 3~4월을 합치면 4,168건, 전체 유효 실행의 **91.5%**다. 이때 모델 49종이 처음 등장했다. 시스템을 만들고 손에 잡히는 걸 다 넣어본 시기다.
5월에 뚝 끊긴다. 60건. 이유는 단순하다. 다른 일을 하느라 안 돌렸다.
개인 프로젝트의 로그는 대체로 이렇게 생겼다. 기술적 이유가 아니라 그냥 손이 못 간 구간이 있다. 이걸 “5월에 모델 출시가 없어서” 같은 그럴듯한 설명으로 채우면 그 순간 데이터가 아니라 이야기가 된다. 6월부터 다시 오르지만 이전 규모로는 돌아가지 않는다.
7~8월은 성격이 다르다. 실행 수는 4월의 1/20 수준(110건·174건)인데 신규 모델은 꾸준히 들어온다. 무더기로 훑는 대신 새 모델이 나오면 그것만 재보는 방식으로 바뀌었다. 평균 점수가 3~4월보다 20%p 넘게 높은 것도 같은 이유다. 검증된 모델과 안정된 문제셋만 돌리고 있다.
문제셋을 33번 고쳤다
버전 기록을 보고 놀랐다. 문제셋 버전이 33개다.
| 기간 | 버전 흐름 |
|---|---|
| 3월 초 | 0.1.0 → 0.1.5 (열흘 만에 6단계) |
| 3월 말~4월 초 | 0.2.0 → 0.2.8 |
| 4월 중순 | 0.3.2 → 0.3.5 |
| 4월 하순 | 0.5.0 → 0.5.12 (2주 만에 13단계) |
| 7월~ | 0.6.0 |
이게 무슨 뜻이냐면, 초기에는 모델을 재는 게 아니라 자를 만들고 있었다. 문제를 내보고, 채점이 이상하면 고치고, 정답이 애매하면 다시 정하고. 그 과정이 버전 번호로 남았다.
4월에 실행이 3,521건이나 쌓인 것도 여기서 설명된다. 모델을 3,521번 평가한 게 아니라, 문제셋을 고칠 때마다 갖고 있던 모델을 전부 다시 돌린 것에 가깝다. 버전별로 보면 v0.2.8 492건(4월 68일), 21일), v0.5.1 335건(4월 20v0.3.3 325건(4월 10~14일) 식으로 며칠 단위 뭉치가 반복된다.
가장 오래 쓴 건 v0.5.12(4월 27일7월 11일, 582건)와 , 252건)이다. 자가 안정되고 나서야 모델을 재기 시작한 셈이다.v0.6.0(7월 20일
여기서 배운 게 있다. 벤치마크를 만들 때 초기 결과는 모델에 대한 정보가 아니라 문제셋에 대한 정보다. 점수가 이상하면 대체로 모델이 아니라 문제가 잘못됐다.
속도 차이가 점수 차이보다 컸다
현재 문제셋(v0.6.0, 유효 252건) 기준으로 모델별 성적과 응답 시간을 같이 보면 그림이 달라진다.
| 모델 | 구분 | 실행 | 평균 점수 | 평균 지연 |
|---|---|---|---|---|
| codex:gpt-5.6-sol | 상용 CLI | 12 | 94.6% | 47초 |
| codex:gpt-5.6-terra | 상용 CLI | 12 | 90.8% | 46초 |
| Kimi-K2.6 | 로컬 | 6 | 90.6% | 866초 |
| Qwen3.8-27B-FP8 | 로컬 | 36 | 87.9% | 583초 |
| chatgpt-pro | 상용 | 11 | 87.2% | 1,185초 |
| Qwen3.8-27B-INT4 | 로컬 | 36 | 86.8% | 576초 |
| codex:gpt-5.6-luna | 상용 CLI | 12 | 85.4% | 69초 |
| Kimi-K2.7-Code | 로컬 | 6 | 77.5% | 507초 |
| glm5.2-nvfp4 | 로컬 | 6 | 76.7% | 208초 |
상위 6개 모델이 86.8~94.6% 사이 7.8%p 안에 몰려 있는데, 같은 구간에서 지연은 46초부터 1,185초까지 25배 벌어진다.
- 로컬 최고(Kimi-K2.6 90.6%)와 상용 최고(94.6%)의 차이는 4%p
- 그런데 Kimi 는 866초, 상용 CLI 는 47초다. 18배
chatgpt-pro 가 1,185초로 가장 느린 것도 눈에 띈다. 무거운 추론을 오래 돌리는 모드라 점수는 높지만 대화형으로 쓰기는 어렵다.
“어느 모델이 제일 똑똑한가”보다 “이 작업에 몇 초까지 쓸 수 있나”가 실제 선택을 결정했다. 사람이 기다리는 작업이면 47초와 866초는 전혀 다른 도구다. 배치로 돌리는 작업이면 866초도 상관없다.
양자화 버전을 나란히 재봤다
같은 모델의 양자화 버전을 함께 돌린 기록도 있다. v0.6.0 기준, 각각 36건씩이다.
| 모델 | 평균 점수 | 평균 지연 |
|---|---|---|
| Qwen3.8-27B-FP8 | 87.9% | 583초 |
| Qwen3.8-27B-INT4 | 86.8% | 576초 |
FP8 과 INT4 사이 차이는 1.1%p였다. 지연은 오히려 INT4 가 7초 빨랐는데, 이 정도 차이는 실행 조건 편차 안이라 의미를 두지 않았다.
문제군별로 보면 방향이 한쪽으로 쏠리지도 않는다.
| 문제군 | FP8 | INT4 |
|---|---|---|
| agent | 98.3% | 95.8% |
| instruction-hierarchy | 91.7% | 87.5% |
| korean | 87.9% | 84.3% |
| structured-io | 83.3% | 81.7% |
| coding | 75.0% | 77.0% |
| long-context | 89.2% | 93.3% |
FP8 이 대체로 앞서지만 coding 과 long-context 에서는 INT4 가 높다. 문제군당 5~7건이라 이 역전을 확정적으로 읽을 수는 없다. 다만 “양자화하면 무조건 나빠진다”는 단순한 그림은 아니었다는 정도는 말할 수 있다.
이런 건 공개 벤치마크에서 찾기 어렵다. 양자화 버전은 배포처마다 방식이 다르고, 같은 조건에서 나란히 잰 숫자가 잘 없다. 자기 서버에서 자기 설정으로 재봐야 알 수 있는 종류의 정보다.
진행 중인 실험을 완료된 결과와 나란히 놓으면 안 된다
이 글을 쓰면서 하마터면 잘못 읽을 뻔한 데이터가 있다.
| 모델 | 실행 | 평균 점수 |
|---|---|---|
| Qwen3.8-27B-FP8 | 36 | 87.9% |
| Qwen3.8-27B-INT4 | 36 | 86.8% |
| Qwen3.8-27B | 62 | 70.4% |
같은 모델인데 양자화 버전이 원본보다 16%p 넘게 높게 나온다. 처음엔 “양자화가 성능을 올렸나?” 싶었다. 그럴 리가 없다.
이유는 단순했다. Qwen3.8-27B 는 지금도 계속 돌리는 중이다. 설정을 바꿔가며 탐색하는 과정의 실행이 62건 쌓여 있고, 그중에는 아직 조건이 맞지 않은 것들이 섞여 있다. 반면 FP8·INT4 는 조건이 정해진 뒤 돌린 것들이다.
앞의 품질 게이트를 통과했는데도 이렇다. 게이트는 “이 실행이 정상적으로 끝났는가”를 보지, “이 실행이 그 모델의 최선을 끌어낸 설정인가”를 보지 않는다.
여기서 얻은 게 하나 있다.
평균은 무엇을 평균 냈는지를 같이 봐야 한다. 실행 횟수가 유독 많은 항목은 대체로 아직 탐색 중이라는 신호다.
같은 문제셋에서 다른 모델이 6~36건인데 62건짜리가 하나 튀어나왔다면, 그건 그 모델을 특별히 많이 신뢰해서가 아니라 아직 자리를 못 잡았다는 뜻이었다. 대시보드가 랭킹에 “6개 문제군 전체를 완료한 결과만” 반영하는 이유도 같은 맥락이다.
로그를 남겨두길 잘했다
이 글에 쓴 내용은 전부 runs.jsonl 한 파일에서 나왔다. 실행할 때마다 한 줄씩 append 하도록 만들어둔 것이다.
당시에는 “나중에 볼 일 있겠지” 정도였는데, 6개월이 지나니 그 자체가 데이터가 됐다. 어떤 모델을 언제 처음 써봤는지, 문제셋을 얼마나 자주 고쳤는지, 실행 중 몇 %가 버려졌는지 — 기억으로는 절대 복원할 수 없는 것들이다.
기록할 때 지킨 게 두 가지 있다.
- 한 줄 = 한 실행. JSONL 이라 중간이 깨져도 나머지를 읽을 수 있다
- 성공만 남기지 않는다. 실패와 부분 실행도 같이 남기고, 대신 상태 필드(
run_status,quality_gate,is_partial)로 구분한다
두 번째가 특히 중요했다. 성공만 저장했으면 무엇이 왜 빠졌는지 되짚을 방법이 없었을 것이다. 이 글을 정정할 수 있었던 것도 원본이 그대로 남아 있었기 때문이다.
정정 기록: 내 집계 도구에 속았다
이 글의 첫 판에서 나는 “4,725건 중 1,403건(29%)만 유효하다” 고 썼다. 제목에도 그 숫자를 넣었다. 틀렸다.
원인은 집계 스크립트의 이 세 줄이었다.
ts = str(d.get("timestamp", ""))[:10]
if not ts:
continue # ← 여기서 3,102건이 사라졌다
runs.jsonl 4,791행 중 3,102행은 timestamp 필드가 빈 문자열이다. 대신 같은 값이 run_started_at 에 들어 있다. 두 필드가 모두 채워진 1,565건을 대조해보니 불일치가 0건이었다. 즉 완전히 멀쩡한 기록인데, 시계열 집계용으로 날짜를 뽑는 줄에서 통째로 탈락하고 있었다.
여기까지는 흔한 버그다. 진짜 문제는 그 다음이었다.
나는 줄어든 숫자를 보고 그럴듯한 설명을 붙였다. “벤치마크는 대부분의 실행을 버린다”는 그럴듯한 문장이었고, 품질 게이트라는 실제로 존재하는 장치가 있었으니 더 그럴듯했다. 검증 없이 해석이 먼저 나왔다.
고치면서 도구에 두 가지를 넣었다.
- 날짜는
timestamp가 비면run_started_at으로 대체한다 - 조용히 버리지 않는다. 몇 건이 왜 빠졌는지 사유별 표를 항상 출력한다
두 번째가 핵심이다. 처음부터 “제외: 238건(incomplete 94, partial 72, 샤드 72)“이 출력에 찍혀 있었다면, 3,322건이 사라진 걸 눈치채지 못할 수가 없었다. 필터가 몇 건을 걸렀는지 말하지 않는 집계 도구는 조용히 거짓말을 한다.
바뀐 숫자는 이렇다.
| 항목 | 첫 판 | 정정 |
|---|---|---|
| 유효 실행 | 1,403 (29%) | 4,553 (95%) |
| 3월 | 310 | 647 |
| 4월 | 805 | 3,521 |
| 5월 | 36 | 60 |
| 문제셋 버전 수 | 26 | 33 |
v0.5.12 실행 |
257 | 582 |
| FP8 vs INT4 격차 | 3.1%p | 1.1%p |
속도 비교(로컬 866초 vs 상용 47초)와 진행 중 실험에 대한 결론은 정정 후에도 그대로였다.
정리
- 벤치마크 실행 4,791건 중 쓸 수 있는 건 4,553건(95%)이었다. 버려지는 건 생각보다 적다
- 필터가 몇 건을 왜 걸렀는지 출력하지 않는 집계 도구를 믿지 않는다. 조용한 누락이 가장 위험하다
- 숫자가 예상과 다르면 해석을 붙이기 전에 그 숫자가 어떻게 만들어졌는지 먼저 확인한다
- 초기 결과는 모델이 아니라 문제셋에 대한 정보다. 점수가 이상하면 대개 문제가 잘못됐다
- 자(문제셋)가 안정되기까지 33번 고쳤다. 그 전 숫자는 모델 비교에 쓸 수 없다
- 점수는 몇 %p 안에 몰리는데 응답 시간은 20배 벌어진다. 실제 선택은 여기서 갈린다
- 양자화 격차(FP8 vs INT4)는 1.1%p로 작았고, 문제군에 따라 역전되기도 했다
- 진행 중인 실험의 평균을 완료된 결과와 같은 표에 놓지 않는다. 실행 수가 유독 많으면 탐색 중이라는 신호다
- 로그의 빈 구간을 그럴듯한 설명으로 채우지 않는다. 안 돌린 달은 그냥 안 돌린 것이다
- 실행 로그를 JSONL 로 남기되 실패도 함께 남긴다. 원본이 남아 있어야 틀린 글을 고칠 수 있다