Personal R&D / Inference Infrastructure

A 237B MoE, Structurally Intact, on One 128 GB Device

expert를 하나도 버리지 않고 텐서 역할별로 정밀도를 배정해 441.63 GiB 모델을 85.56 GiB GGUF로 만들고, 서빙 엔진에 모델 패밀리를 직접 구현해 DGX Spark 한 대에서 262,144 토큰 컨텍스트로 서빙했습니다.

Kept every expert, assigned precision per tensor role to take a 441.63 GiB model down to an 85.56 GiB GGUF, then implemented the model family inside the serving engine to run it on one DGX Spark at a 262,144-token context.

Mixed-quant GGUFllama.cppds4DGX Spark / GB10MTPCUDA sm_121

문제

Problem

237B 파라미터 모델은 통상 멀티 GPU 호스트를 전제합니다. 단일 장비에 올리는 방법은 보통 프루닝이나 distillation이지만, 그러면 원 모델과 다른 모델이 됩니다.

A 237B-parameter model normally implies a multi-GPU host. The usual ways to fit one on a single device — pruning, distillation — produce a different model.

이 작업의 제약은 명확했습니다. 47개 MoE 레이어의 128개 routed expert를 하나도 제거하지 않고, 텐서 개수를 BF16 원본과 동일하게 유지한 채, 128 GB 통합 메모리 한 대에 모델과 최대 컨텍스트를 함께 올린다.

The constraint here was explicit: drop none of the 128 routed experts across 47 MoE layers, keep the tensor count identical to the BF16 source, and still fit both the model and its maximum context into one 128 GB unified-memory device.

접근

Approach

바뀐 것은 구조가 아니라 각 텐서를 몇 비트로 저장하느냐뿐입니다. 전역 비트 예산이 아니라 텐서가 하는 일을 기준으로 정밀도를 배정했습니다.

Nothing about the structure changed — only how many bits each tensor is stored in, assigned by what the tensor does rather than by a global bit budget.

라우터와 모든 norm은 F32로 남겼습니다. 잘못된 expert 선택은 어떤 비트 절약보다 비싸기 때문입니다. 모든 토큰이 지나는 경로 — 임베딩, LM head, attention Q/K/V/O, dense layer 0, shared expert, 그리고 speculative decoding 품질을 좌우하는 MTP 블록 — 는 Q8_0으로 유지했습니다.

Routers and every norm stay F32, because a wrong expert choice costs more than any bit saved. Everything a token always traverses — embeddings, LM head, attention Q/K/V/O, dense layer 0, the shared expert, and the MTP block that drives speculative acceptance — stays at Q8_0.

전체 파라미터의 약 64%를 차지하는 routed expert의 gate/up에서 압축을 회수했고(IQ2_XXS), 가중 누적이 일어나는 down 투영은 Q3_K로 더 보수적으로 두었습니다. 첫·마지막 sparse 블록은 Q4_K로 보호했습니다.

The compression comes from routed expert gate/up projections — about 64% of all parameters — at IQ2_XXS, while the down projections that perform weighted accumulation stay more conservative at Q3_K, and the first and last sparse blocks are protected at Q4_K.

importance matrix는 모델이 서비스하는 6개 언어 코퍼스로 만들되 한국어를 가장 무겁게 실었습니다. 가장 공격적인 양자화가 들어가는 경로에서 한국어 능력을 지키는 것이 이 아티팩트의 목적이기 때문입니다.

The importance matrix was built across all six languages the model serves, weighted heaviest toward Korean, since Korean capacity is exactly what the most aggressive quant in the recipe puts at risk.

서빙 엔진 기여

Serving-engine contribution

타깃 엔진인 ds4는 MLA 전용이었고 K-EXAONE은 일반 GQA라, 이 모델을 서빙하려면 어텐션 경로부터 새로 써야 했습니다. exaone-moe 모델 패밀리를 직접 구현했습니다 — per-head QK-norm이 붙은 GQA, 4개 레이어마다 full attention이 오는 LLLG sliding-window 스케줄, sigmoid top-8 라우팅, 그리고 blk.48 MTP 그래프입니다.

The target engine, ds4, was MLA-only while K-EXAONE is plain GQA, so serving it meant writing the attention path first. I implemented the exaone-moe model family: GQA with per-head QK-norm, the LLLG sliding-window schedule where every fourth layer is full attention, sigmoid top-8 routing, and the blk.48 MTP graph.

여기서 나온 가장 중요한 수정은 KV ring 결함이었습니다. sliding 레이어가 어텐션 윈도우 폭만큼만 KV ring을 잡는데 prefill은 2,048 토큰 청크로 돌아, 청크의 마지막 128 위치만 남고 나머지 행은 이미 덮어써진 슬롯을 참조하고 있었습니다. 48개 중 36개가 sliding 레이어라 긴 프롬프트 이해도가 조용히 무너졌고, 128 토큰 미만 프롬프트는 영향이 없어 API 검증 스위트는 계속 통과하고 있었습니다.

The most consequential fix was a KV ring defect. Sliding layers allocated a ring exactly the width of the attention window while prefill ran in 2,048-token chunks, so only a chunk's last 128 positions survived and every earlier row attended over slots a later position had already overwritten. With 36 of 48 layers sliding, long-prompt comprehension degraded silently — and prompts under 128 tokens were unaffected, which is why the API validation suite kept passing.

멀티턴 prefix 재사용도 다시 만들었습니다. 채팅 클라이언트는 직전 응답을 텍스트로 되돌려 보내므로 재토큰화하면 모델이 실제로 샘플링한 토큰 ID가 재현되지 않습니다. 전체 일치를 요구하던 기존 검사는 98.6%가 겹치는 후속 요청도 전량 재프리필했고, 이를 분기 지점부터 재개하도록 바꿨습니다.

I also rebuilt multi-turn prefix reuse. A chat client replays the assistant's previous reply as text, and re-tokenising it does not reproduce the token IDs the model sampled, so an all-or-nothing checkpoint test re-prefilled everything even when 98.6% of the prompt matched. It now resumes at the divergence point instead.

Measured on GB10

측정된 결과

Measured results

서빙 컨텍스트Context served262,144

모델이 지원하는 최대 컨텍스트를 단일 GB10에서 할당하고 상주시킨 상태로 서빙.

The model's full context length, allocated and resident while serving on a single GB10.

상주 메모리Resident memory103.95 GiB

121.6 GiB 중. 가중치 84.48 GiB + 256K KV 12.30 GiB + 그래프 워크스페이스 1.60 GiB.

Of 121.6 GiB: 84.48 GiB weights, 12.30 GiB of 256K KV, and a 1.60 GiB graph workspace.

KV cost48 KiB/token

48개 중 12개 레이어만 full-context KV를 유지하는 LLLG 스케줄 덕분. 전 레이어 global GQA였다면 약 192 KiB/token.

Because the LLLG schedule keeps full-context KV on only 12 of 48 layers; a fully global GQA stack would need roughly 192 KiB/token.

멀티턴 재사용Multi-turn reuse24×

7K 문서 후속 질문의 first token까지 165.7초 → 5.9초. 무관한 프롬프트는 오탐 없이 그대로 cold.

Time to first token on a follow-up over a 7K document fell from 165.7s to 5.9s, while unrelated prompts correctly stay cold.

구조 검증Structural verification0 errors

781개 텐서를 recipe와 대조한 per-tensor 검증에서 오류·경고 0건, BF16 원본과 텐서 수 일치.

Per-tensor verification against the recipe across all 781 tensors: zero errors, zero warnings, tensor count matching the BF16 source.

한글 품질Korean output quality0.000

384 토큰 한국어 생성에서 자모 깨짐 비율 0.000, 78.1 tok/s.

Broken-jamo ratio of 0.000 across 384 generated Korean tokens at 78.1 tok/s.

Limits

이 결과가 말하지 않는 것

What this result does not claim

262,144 토큰이 들어가고, 할당되고, 상주한다는 것은 메모리 결과이지 처리량 결과가 아닙니다. 이 하드웨어에서 256K cold 프롬프트의 prefill은 시간 단위로 걸리고, 그 깊이에서 decode는 1 tok/s 아래로 떨어집니다. 실제로 쓸 만한 깊이는 GB10 한 대 기준 대략 2K–32K입니다. prefill은 프롬프트 길이에 대해 2차, decode는 컨텍스트 깊이에 대해 선형으로 증가하며, 두 곡선 모두 측정값으로 회귀식을 남겨 두었습니다.

That 262,144 tokens fit, allocate, and stay resident is a memory result, not a throughput result. A cold 256K prefill takes hours on this hardware and decode at that depth runs below 1 token/s, so useful working depths on one GB10 are roughly 2K–32K. Prefill cost is quadratic in prompt length and decode cost linear in context depth; both curves are published as fitted regressions over measured points.