나의 코딩 벤치마크에서 7.5B 모델이 24B 모델을 이겼다
요약
작성자가 직접 구축한 56개의 코딩 벤치마크를 통해 로컬 LLM들의 성능을 비교 분석했습니다. 실험 결과, 7.5B 규모의 gemma-4-e4b 모델이 24B 모델보다 높은 코딩 성능을 기록하며 모델 크기와 성능이 반드시 비례하지 않음을 보여주었습니다.
핵심 포인트
- 7.5B 모델이 24B 모델을 능가하는 코딩 성능 기록
- LLM 판정관 대신 컴파일러와 테스트 러너를 통한 객관적 검증 방식 채택
- 모델별 실행 결과의 변동성(noise)이 성능 지표에 미치는 영향 확인
- 작은 GPU 환경에서 양자화 모델의 효율성 입증
16개의 로컬 모델 설정, 56개의 숨겨진 테스트 코딩 작업, 36번의 전체 실행, 16 GB 그래픽 카드 하나
아래의 모든 숫자는 메모를 옮겨 적은 것이 아니라, 확정된 SCORES-*.tsv 파일에서 다시 집계한 것입니다. 각 실행당 하나씩, 전체 실패 목록을 포함한 36개 행의 원시 데이터(raw data)는 RESULTS-q56.csv에 있습니다.
gemma-4-e4b는 4.97 GiB 크기의 파일에 담긴 7.5B 파라미터(parameters) 모델입니다. 이 모델은 42/56점을 기록했습니다.
devstral-small-2-24b는 11.90 GiB 크기의 24B 모델입니다. 이 모델은 40/56점을 기록했습니다. gpt-oss-20b는 38점을 기록했습니다. 두 가지 gemma-4-12b 변형 모델 중 하나는 Q8_0 양자화(quantisation)를 사용하여 정밀도가 두 배 이상 높았음에도 불구하고 각각 42.5점과 41점을 기록했습니다.
각 모델당 3번씩 실행했습니다. 범위가 겹치지 않습니다. 작은 그래픽 카드 환경에서 5 GiB 모델은 타협안이 아닙니다. 그리고 그 어떤 공개 리더보드(public leaderboard)도 당신에게 이 사실을 알려주지 않았을 것입니다.
어떤 로컬 모델로 코딩할지, 어떤 양자화(quantisation)를 실행할지, 그리고 KV 캐시(KV cache) 설정이 어떤 비용을 초래할지에 대해 모두가 의견을 가지고 있습니다. 그러한 의견의 거의 대부분은 공개 리더보드나, 누군가의 컴퓨터에서 특정 설정으로 단 한 번 실행해 본 결과에서 비롯됩니다.
그래서 저는 숨겨진 테스트(hidden tests)로 채점하는 56개 작업의 코딩 벤치마크를 구축했습니다. 그리고 동일한 그래픽 카드에서 동일한 상수(constants)를 사용하여 16개의 모델 설정을 실행했으며, 고정된 모든 상수 값을 실제로 기록했습니다. 그 후, 단일 수치가 얼마나 노이즈(noise)인지 알아내기 위해 여러 모델을 반복해서 실행했습니다 — 총 36번의 전체 실행을 수행했습니다.
순위는 그 어떤 공개 리더보드와도 일치하지 않습니다. 한 경우에는 순위가 완전히 뒤집히기도 합니다. 그리고 순위보다 저를 더 놀라게 한 것은 노이즈 결과였습니다. 오차 범위(error bar)는 벤치마크의 속성이 아니라 모델의 속성입니다. 어떤 모델은 동일한 연속 실행 사이에서 8점의 차이를 보이는 반면, 다른 모델은 비트 단위로 재현 가능(bit-for-bit reproducible)합니다. 그리고 변동이 심한 모델이 반드시 더 약한 모델인 것도 아닙니다.
방법론, 그리고 LLM 판정관(judge)이 없는 이유
실제 에이전트 루프(agent loop)를 통해 실행되는 56개의 중상급(medium-hard) 코딩 작업 - 모델은 파일을 읽고, 수정하고, 테스트를 실행하며, 완료되었다고 말할 때까지 반복합니다. 채점은 숨겨진 검증 (hidden verification) 방식으로 이루어집니다:
- 모델이 보는 피스처(fixture)에는 정상 경로(happy-path)의 가시적인 테스트만 포함되어 있습니다.
- 채점 시점에는 검증기(verifier)가 **숨겨진 리뷰어 테스트 (hidden reviewer tests)**를 주입하고, 모델이 디스크에 실제로 남긴 결과물에 대해 빌드 및 실행을 수행합니다.
- 합격/불합격(Pass/fail) 판정은 컴파일러와 테스트 러너(test runner)가 결정합니다. 어떤 모델도 다른 모델의 출력을 심사하지 않습니다.
마지막 지점은 들리는 것보다 훨씬 더 중요합니다. 이 프로젝트가 조상들로부터 물려받은 교훈은 다음과 같습니다: LLM 판정관(LLM judges)은 수치를 부풀립니다. 결정론적인 정답(deterministic ground truth)은 모델에 의해 낮아질 수는 있어도, 결코 높아질 수는 없습니다. 따라서 여기에는 판정관이 전혀 없으며, 오직 cargo test, pytest, 그리고 node만이 존재합니다.
작업들은 테스트 우선 게이트(test-first gated) 방식을 따릅니다. 작업은 검증기가 수정되지 않은 피스처에서는 _실패_하고, 피스처와 참조 솔루션(reference solution)을 결합했을 때는 _통과_하는 경우에만 코퍼스(corpus)에 포함됩니다. 이를 통해 우연히 이미 통과하고 있는 작업이나 불가능한 작업을 배제합니다.
변별력의 축은 알고리즘적 난이도가 아닌 것으로 밝혀졌습니다. 그것은 바로 **명시되지 않은 정확성 함정 (unstated correctness traps)**입니다. 즉, 퇴화된 입력(degenerate input), 거짓(falsy)과 부재(absent)의 구분, 프롬프트가 암시하지만 결코 열거하지 않는 경계값 등이 그것입니다. 프롬프트는 사용자가 말하듯 목표를 기술하며, 숨겨진 테스트는 리뷰어가 하듯 채점합니다.
코퍼스 (Corpus): 56개 작업 - Python 15개, Rust 14개, JS 10개, TS 9개, Shell 8개. 유형별: 구현 사양(implement-spec) 18개, 경계값(boundary) 7개, 버그 수정(bugfix) 6개, API 오용(api-misuse) 6개, 에러 처리(error-handling) 5개, 리팩터링(refactor) 4개, 성능(perf) 4개, 다중 파일(multi-file) 4개, 동시성(concurrency) 2개.
환경 (Box): RTX 5060 Ti 16 GB, Windows 11, LM Studio. 모든 행에서 일정하게 유지된 조건: 컨텍스트(ctx) 32768, KV 캐시(KV cache) q8_0, numParallelSessions 1, 한 번에 하나의 에이전트 세션, 동일한 에이전트 바이너리. 모든 행의 실제 값은 가정되지 않고 기록되었습니다 - 제가 다른 벤치마커들이 꼭 읽어주었으면 하는 섹션인 "가장 큰 비용을 치른 방법론의 교훈"을 참조하십시오.
표
모든 행에 대해 56개 작업 전체 수행. 낮은 점수는 데이터이지 실패가 아닙니다 - 승자만을 보고하는 벤치마크는 경쟁 구도의 형태에 대해 아무것도 알려주지 않습니다.
| 모델 (model) | 파라미터 (params) | 양자화 (quant) | GiB | 실행 횟수 (runs) | 중앙값 (median) | 범위 (range) | n | 실제 소요 시간 (wall-clock) |
|---|---|---|---|---|---|---|---|---|
| google/gemma-4-26b-a4b-qat | 26B-A4B | Q4_0 | 13.45 | 55, 54, 55 | 55 | 1 | 3 | 71–76 min |
| ... | ||||||||
크기는 벤더의 마케팅 수치나 lms ls(십진법 GB를 보고하며 mmproj 프로젝터를 포함함)가 아니라, 측정된 GiB 단위의 GGUF 파일 크기입니다. 양자화(Quant) 명칭은 정확합니다. 왜냐하면 Q8_0에서의 "gemma-4-12b"와 Q4_0에서의 "gemma-4-12b"는 서로 다른 두 개의 실험이기 때문입니다. |
⚠️1로 표시된 행에 대하여: 단일 실행은 이제 이 벤치마크가 수치화할 수 있는 오차 막대(error bar)를 수반하며, 그 오차는 작지 않습니다 - 노이즈(noise) 섹션을 참조하십시오. 이 글에서 핵심적인 내용은 n=1인 행에 의존하지 않습니다. n=1인 경우(12B에서의 QAT)에는 이를 명시하고 주장을 유보합니다.
한 모델은 점수를 매기는 대신 제외되었습니다: nemotron-3-nano-omni-30b-a3b는 코퍼스(corpus)에서 가장 쉬운 작업에 299초가 소요되었으며(devstral의 18초 대비), 이를 기준으로 계산하면 약 4.5시간이 소요될 것으로 예상되었습니다. 그 정도 속도라면 작업당 타임아웃(timeout)으로 인해 품질이 아닌 시간 제한 때문에 작업이 실패하기 시작하며, 이는 읽을 수 없는 행을 생성합니다. 해당 모델의 설정은 올바른 것으로 확인되었고 템플릿도 정상적이었습니다 - 결론은 오직 속도 때문입니다. 모델이 누락된 _이유_를 기록하는 것이 빈칸으로 남겨두는 것보다 낫습니다.
파트 1 - 모델들이 수행한 것
결과 1: 크기는 아무것도 보장하지 않지만, 이는 평탄한 선이 아니라 절벽이 있는 고원(plateau)이다
로컬 모델 스레드에서 가장 반복되는 주장은 "사용 가능한 가장 큰 모델을 그냥 돌려라"라는 식의 변형된 주장입니다. 여기 gemma-4 제품군이 보여주는 결과가 있습니다. 하나의 아키텍처, 하나의 벤더, 네 가지 크기입니다:
| 모델 (model) | 파라미터 (params) | 양자화 (quant) | GiB | 중앙값 (median) |
|---|---|---|---|---|
| gemma-4-e2b | 4.6B | Q4_K_M | 3.19 | 31 |
| ... |
4.6B → 7.5B +11.0
7.5B → 12B +0.5 ← 고원 (the plateau)
12B → 26B +12.5
7.5B에서 12B로 스케일링 (Scaling)하는 것은 0.5점의 이득만을 가져옵니다. 그것이 바로 평탄한 구간(flat stretch)이며, 이는 12B가 더 나은 양자화 (quantisation)를 사용함에도 불구하고 — 7.5B의 Q4_K_M에 비해 12B는 Q8_0를 사용하여 보수적인 방향으로 설정되었습니다 — 평탄합니다. 작은 모델에 동일한 정밀도 (precision)를 부여한다면 격차는 아마 반대로 뒤집힐 것입니다.
하지만 이 구간은 양쪽 모두에서 실제 절벽(cliffs)에 의해 제한됩니다. 이보다 아래인 4.6B로 떨어지면 11점을 잃습니다. 이보다 위인 26B MoE는 12.5점을 얻습니다. "크기는 아무것도 사지 못한다(Size buys nothing)"는 특정 대역(band) 내에서는 사실이며, 그 대역은 슬로건보다 더 좁습니다.
이것이 제목이 나온 이유입니다. gemma-4-e4b는 매개변수 (parameters) 수는 3분의 1 미만이고 파일 크기는 42%에 불과하면서도 devstral-small-2-24b를 능가합니다. 그리고 이는 단 하나의 운 나쁜 모델을 상대로 한 것이 아니라, 전체 제품군 경계에 걸쳐 수행되었습니다. 즉, gpt-oss-20b (38), north-mini-code (42, n=1), 그리고 정밀도가 두 배인 12B Gemma 모델 두 개 모두를 이깁니다. 복제된 행(rows)에 대해 각각 3회 실행하였으며, 범위는 중복되지 않습니다.
또한 활성 매개변수 (active parameters)로도 이를 설명할 수 없습니다. gemma-4-26b-a4b는 토큰당 약 4B의 매개변수를 활성화하는데, 이는 4.6B e2b가 활성화하는 양과 거의 비슷합니다. 그런데도 55 대 31의 점수를 기록했습니다. 동일한 활성 연산량 (active compute)임에도 24점 차이가 납니다. 여기서 작동하고 있는 것이 무엇이든, 그것은 전체 크기 (total size)도 아니고 활성 크기 (active size) 그 자체도 아닙니다.
결과 2: 공개 리더보드는 이 코퍼스 (corpus)를 순위 매기지 못한다
결과 1의 내용은 리더보드에서 전혀 확인할 수 없으며, 이는 우연이 아닙니다. 리더보드가 생성하는 순위는 표 상단에 있는 제 순위와도 다릅니다.
A 벤더 비교표에 따르면 SWE-bench Verified에서 Gemma-4-31B는 Qwen3.6-35B보다 대략 21점 낮게 나타납니다. 하지만 이 코퍼스에서 Gemma MoE는 Qwen 챔피언을 55 대 50으로 이깁니다.
LiveCodeBench v6는 완전히 역상관 관계 (anti-correlates)를 보입니다: Gemma는 거기서 77.1점을 기록하고 여기서는 55/56점을 기록합니다. 반면 Qwen은 거기서 80.4점을 기록하고 여기서는 50/56점을 기록합니다. 제가 가장 중요하게 생각하는 두 모델 모두에서, 리더보드 점수가 높을수록 제 작업에서는 점수가 낮아집니다. 그리고 이 모델들에 대한 LiveCodeBench 자체 표는 0-검증 (0-verified) 및 53-자가 보고 (53-self-reported) 상태입니다.
이는 단순히 "벤치마크마다 측정하는 것이 다르다"는 수준보다 더 심각한 문제입니다. 왜냐하면 몇몇 명백한 대안들이 더 이상 사용할 수 없게 되었기 때문입니다: BigCodeBench는 2025-04-14 이후로 동결되었으며 (202개 행, 2026년 모델 0개), Aider의 다국어 (polyglot) 벤치마크는 2025-10-04에 중단되었습니다. 한편, 발표된 연구에 따르면 스캐폴드 (scaffold) 선택만으로도 Terminal-Bench에서 동일한 가중치 값이 약 2배나 요동칩니다. 공식 보드에서는 24.6%를 기록한 반면, 동일 모델에 대해 벤더(vendor)가 주장하는 수치는 51.5%였습니다.
마지막 수치가 바로 실제 메커니즘이며, 이것이 제가 제 순위가 LiveCodeBench보다 "더 옳다"고 생각하지 않는 이유입니다. 리더보드 (leaderboard)는 정해진 박스 안에서 답변하는 모델을 측정합니다. 하지만 여러분이 실제로 경험하는 것은 여러분의 도구 스키마 (tool schema), 여러분의 컨텍스트 윈도우 (context window), 여러분의 파일 편집 포맷 (file-edit format)을 사용하는, 여러분의 에이전트 (agent)를 구동하는 모델입니다. 스캐폴드는 모델을 감싸고 있는 단순한 세부 사항이 아닙니다. Terminal-Bench 자체 수치에 따르면, 스캐폴드는 모델 선택만큼이나 큰 가치를 가집니다.
리더보드는 무엇을 다운로드할지 결정하는 데 사용하십시오. 여러분 자신의 하네스 (harness) 내부에서 어떤 일이 일어날지 예측하는 데 절대 사용하지 마십시오. 위의 순위를 발견하기 위해서는 34번의 전체 실행이 필요했으며, 그 어떤 공개 표를 통해서도 추측할 수 없었을 것입니다.
결과 3: 사람들이 논쟁하는 구간에서 양자화 비트 (quantisation bits)는 아무런 이득을 주지 못했다
동일한 MoE 제품군, 동일한 KV 캐시 (KV cache) 및 컨텍스트 조건에서의 네 가지 양자화 (quantisation) 결과입니다:
| 변형 (variant) | bpw | GiB | 점수 (score) |
|---|---|---|---|
| MTP-GPU-2 | 3.06 | 12.67 | 50 (6개의 중앙값) |
| ... |
3.06 → 4.25 bpw 전체 범위에 걸쳐 점수가 평탄하게(Flat) 나타났으며, 그동안 파일 크기는 거의 4 GiB까지 차이 나고 엔드 투 엔드 (end-to-end) 속도는 약 3배까지 차이 났습니다. 네 가지 중 가장 낮은 bpw가 이 하드웨어에서 가장 좋은 선택이며, 이는 근소한 차이가 아닙니다. 해당 모델만이 완전히 VRAM에 상주(resident)할 수 있는 유일한 모델입니다.
솔직하게 주의사항(Caveat)을 말씀드리자면: 이는 하나의 MoE (Mixture-of-Experts) 제품군을 하나의 16GB 그래픽 카드에서 테스트한 결과이며, 네 개의 행 중 세 개는 단일 실행(single run) 결과입니다. 이것이 "양자화 (quantisation)는 전혀 중요하지 않다"는 뜻은 아니며, 양자화에 훨씬 더 민감한 밀집 모델 (dense models)에도 이 결과가 적용될 것이라고 가정하지도 않습니다. 제 주장은 더 좁고 명확한 근거를 가지고 있습니다: 사람들이 실제로 논쟁하는 범위 내에서, 이 정도 크기의 카드에서, 이 제품군을 대상으로 했을 때, 추가적인 비트(bits)를 사용하는 것은 측정 가능한 코드 품질의 향상을 가져오지 못했으며 실제 속도만 희생시켰다.
제가 주장하지 않는 QAT에 관한 내용
양자화 인식 훈련 (Quantisation-aware training, QAT)은 비용이 들지 않는 것처럼 보이며, 저는 이를 보여주는 듯한 두 쌍의 데이터를 가지고 있습니다:
| 쌍 (pair) | 높은 정밀도 (higher precision) | QAT | 차이 (gap) |
|---|---|---|---|
| gemma-4-26b-a4b | UD-Q4_K_M 15.78 GiB → 54 (n=1) | Q4_0 13.45 GiB → 55 (n=3) | Q4_K_M vs Q4_0 |
| gemma-4-12b | Q8_0 11.80 GiB → 42.5 (n=2) | Q4_0 6.50 GiB → 41 (n=1) | ★Q8_0 vs Q4_0 |
이것들은 동일한 실험이 아니며, 이를 하나의 "QAT는 공짜다"라는 문장으로 합치는 것은 양방향 모두에서 잘못된 것입니다. 26B 모델에서는 Q4_K_M과 Q4_0를 비교했는데, 둘 다 4비트급 모델로서 거의 유사한 조건의 교체이므로 "QAT가 이에 필적한다"는 것은 겸손한 주장입니다. 12B 모델에서는 Q8_0와 Q4_0를 비교했는데, 이는 정밀도가 절반이면서 파일 크기는 45% 더 작은 비교로, 만약 사실이라면 진정으로 강력한 결과가 되었을 것입니다.
하지만 12B QAT 그룹은 단 한 번의 실행 (one run) 결과인 반면, 비-QAT 그룹의 두 번의 실행 결과는 42~43 사이에 걸쳐 있습니다. 1점의 차이는 두 모델의 측정된 편차 범위 내에 있습니다. 따라서 저는 이를 주장하지 않습니다. 이 문제를 종결지으려면 gemma-4-12b-qat를 두 번 더 실행해야 하며, 그 결과가 나오기 전까지 정직한 진술은 다음과 같습니다: 26B에서의 QAT는 측정 가능한 비용이 들지 않지만, 12B에서의 QAT는 아직 검증되지 않았다.
제가 이 부분을 길게 언급하는 이유는, 단 한 번의 실행만으로도 영원히 반복되는 주장의 형태가 바로 이것이기 때문이며, 저 또한 하마터면 이대로 발표할 뻔했기 때문입니다.
파트 2 - 벤치마크가 수행한 것
이 부분은 제가 흥미로울 것이라고 예상하지 못했던 부분입니다.
결과 4: 오차 막대 (error bar)는 벤치마크의 속성이 아니라 모델의 속성이다
오랫동안 저는 모든 비교에 대해 "±3"이라는 단일한 전역 노이즈 수치(global noise figure)를 적용해 왔습니다. 그것은 항상 잘못된 방식이었으며, 여러 모델을 복제하여 실행해 본 후에야 비로소 그 사실을 깨달았습니다.
연속된 실행, 동일한 설정(config), 동일한 엔진, 동일한 기기, 그 사이에는 아무것도 변하지 않았습니다:
| model | runs | median | range |
|---|---|---|---|
| gemma-4-e2b | 31, 31, 31 | 31 | 0 |
| ... |
gpt-oss-20b는 세 번의 동일한 실행 과정에서 8포인트의 변동(swings)을 보였습니다. 이 모델의 발표된 단일 실행(single-run) 수치 — 만약 제가 보통 사람들이 하는 것처럼 한 번만 실행했다면 표에 들어갔을 40이라는 수치 — 는 범위의 중심이 아니라 **범위의 상단(top)**입니다. 단일 실행 보고 방식은 이 모델을 2포인트만큼 미화(flatter)시켰으며, 그 실행 과정 중에는 그 어떤 것도 이를 암시하는 신호가 없었습니다.
반면 gemma-4-e2b는 세 번 실행되었고, 단순히 동일한 점수뿐만 아니라 **동일한 실패 목록(identical failure list)**을 생성했습니다. 즉, 동일한 25개의 작업과 동일한 에러 텍스트를 보여주었습니다. 실행 2와 실행 3 사이의 유일한 차이점은 하네스(harness)의 부산물(artifacts)뿐이었습니다: cargo의 병렬 테스트 순서, OS 스레드 ID, ±1초의 타이밍 차이 등입니다.
취약점(Weakness)이 분산(variance)을 유발하는 것이 아닙니다. 실패 모드(failure mode)가 유발하는 것입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기