아오, 대역폭… MoE는 뭐가 이렇게 다른가
페르소나를 여러 개 만들다가 모델을 여러 개 띄우는 쪽으로 생각이 커졌다.
그때까지만 해도 문제는 prompt와 LoRA라고 생각했다. 그런데 모델을 실제로 올리고 context를 늘려보니 전혀 다른 문제가 나타났다.
가중치를 넣고 나니 context를 얼마 못 뽑았다.
처음에는 단순히 모델이 크기 때문이라고 생각했다. 그런데 Gemma4-26B-A4B와 Qwen3.6-35B-A3B를 번갈아 보면서 dense와 MoE의 차이를 찾아보니, 파라미터 숫자 하나로는 아무것도 설명되지 않았다.
dense는 정직하게 무거웠다
Dense 모델은 토큰을 처리할 때 전체 네트워크가 계산에 참여한다.
token → 전체 주요 파라미터 → 다음 token모델 크기가 커지면 가중치를 보관하는 비용이 커지고, 계산할 때 읽어야 하는 데이터도 많아진다. context를 늘리면 입력 토큰을 처리하는 시간과 KV cache가 같이 늘어난다.
그래서 “양자화했으니 context도 넉넉하게 쓸 수 있겠지”라고 생각하면 안 됐다. NVFP4 같은 양자화는 가중치 쪽 부담을 줄여주지만, 긴 입력과 여러 동시 요청이 만드는 KV cache 문제까지 없애주지는 않는다.
가중치를 줄인 뒤 남은 자원을 context에 쓰고 싶었는데, 실제로는 runtime workspace와 cache, 메모리 이동이 계속 자리를 차지했다.
MoE는 가벼운 dense가 아니었다
MoE는 여러 expert를 두고 router가 토큰마다 일부 expert를 선택한다.
token → router → 선택된 expert 일부 → 다음 token이 설명만 보면 전체 파라미터가 커도 실제 계산은 일부만 하니 훨씬 가벼울 것 같다. 절반은 맞고 절반은 틀렸다.
활성 파라미터 수와 전체 가중치 보관 비용은 다르다. expert가 여러 개면 모델을 올릴 때 필요한 가중치와 메모리 배치가 복잡해진다. router가 선택한 expert로 데이터를 보내고 결과를 합치는 과정도 있다.
즉 MoE는 “dense 모델 여러 개를 동시에 실행하는 것”도 아니고, “큰 모델인데 작은 모델처럼 공짜로 도는 것”도 아니었다.
아오, 대역폭
여기서 대역폭이라는 단어를 자꾸 보게 됐다.
처음에는 네트워크 대역폭만 생각했다. 하지만 실제 병목은 더 넓은 의미의 데이터 이동이었다.
가중치 읽기
+ token과 activation 이동
+ expert 선택과 결과 결합
+ KV cache 읽기·쓰기
+ 여러 sequence의 동시 처리모델이 계산을 잘하는지와 필요한 데이터를 제때 가져오는지는 다른 문제였다. 계산 유닛이 놀고 있는 것처럼 보여도 메모리 계층에서 데이터를 기다릴 수 있었다.
정확한 장비 수치나 특정 환경의 benchmark를 적는 것보다, 요청 길이와 동시성이 바뀔 때 병목이 어디로 이동하는지를 보는 것이 더 유용했다.
context를 키우면 왜 갑자기 힘들어질까
context는 단순히 입력창의 최대 글자 수가 아니었다.
현재 요청의 입력 토큰, 생성할 출력 토큰, 동시에 처리 중인 다른 sequence, 각 sequence의 KV cache가 모두 같은 실행 예산을 나눠 쓴다.
전체 실행 예산
├─ prompt 처리
├─ 생성 토큰
├─ KV cache
└─ 동시 sequence긴 context 하나를 허용하는 것과 긴 context 요청을 여러 개 안정적으로 처리하는 것은 전혀 달랐다. context 상한만 크게 잡아두면 사용자는 긴 요청을 보낼 수 있지만, 다른 요청의 대기 시간과 tail latency가 먼저 망가질 수 있다.
그래서 모델을 비교할 때 이제는 이런 질문을 같이 적는다.
- 긴 입력에서 첫 토큰까지 얼마나 기다리는가
- 출력 상한을 키우면 다른 요청이 얼마나 밀리는가
- 동시 sequence를 늘릴 때 cache가 어디서 한계에 닿는가
- timeout이 나기 전에 어떤 상태를 반환할 것인가
평균 token/s만 봐서는 답이 나오지 않았다.
MoE가 페르소나를 자동으로 나눠주지는 않았다
내 데이터가 디자인 기획, CTO 리뷰, 오버엔지니어링 검사, 실행 계획으로 나뉘어 있다고 해서 MoE expert가 그대로 그 역할을 하나씩 맡아주는 것은 아니었다.
expert routing은 모델 내부의 계산 선택이고, 업무 persona는 애플리케이션이 요구하는 판단 경계다. 둘을 같은 것으로 보면 설계가 금방 꼬인다.
application persona router
→ 어떤 관점으로 검토할지 결정
model expert router
→ 토큰 계산에 참여할 경로를 결정오히려 모델 바깥에서 역할의 순서를 명확히 하고, 모델 안의 routing은 모델이 학습한 계산 구조로 남겨두는 편이 경계가 분명했다.
그래서 LoRA를 붙이기 전에 baseline을 만들었다
모델 구조가 다르고 context 운용 특성도 다른데, adapter까지 한 번에 붙이면 무엇이 결과를 바꿨는지 알 수 없다.
그래서 먼저 base model의 같은 입력을 비교하고, 짧은 context와 긴 context를 분리해서 봤다. 그 다음에야 LoRA를 붙일 준비가 됐다.
여기까지 오고 나니 페르소나 학습은 문체 데이터만의 문제가 아니었다.
어떤 모델 구조를 선택하고, 가중치와 cache 사이에서 context 예산을 어떻게 나누고, 여러 역할을 모델 안팎 어디에 둘지 결정하는 문제였다.
그리고 이제 데이터가 남았다.
내 커밋과 문서와 메시지를 실제로 학습 데이터로 만들려면, 먼저 사람이 읽을 수 있는 기준부터 만들어야 했다.