---
title: "DeepSeek-V3 Technical Report 해부: 671B인데 토큰마다 37B만 쓰는 MoE 시스템"
slug: "deepseek-v3-technical-report-deep-dive"
category: "AI / LLM"
topic: "ai-llm"
subtopic: "transformer"
tags: ["DeepSeek-V3","MoE","MLA","FP8","LLMOps","Paper Review"]
status: "published"
created: "2026-07-24"
updated: "2026-07-24"
summary: "DeepSeek-V3 Technical Report를 MoE 라우팅, MLA KV cache 압축, MTP, FP8 학습, 분산 통신, 사후학습과 배포까지 연결해 읽는다. 671B/37B라는 숫자가 제품의 비용·지연 시간·운영 복잡도에 뜻하는 바를 직접 계산으로 확인한다."
kind: "Deep Dive"
evidence: "원 논문 arXiv v2·공식 DeepSeek-V3 코드와 config·공식 weight 문서·Python 3.9 직접 계산"
series: "LLM 시스템 지도 — 0단계"
---

> **0단계 두 번째 논문.** 이 글은 [DeepSeek-V3 Technical Report](https://arxiv.org/abs/2412.19437) arXiv v2(2025-02-18)를 읽고 정리한 기록이다. 논문이 보고한 DeepSeek-V3와 당시 비교 모델의 결과를 설명한다. 2026년 현재의 "최신 최고 모델" 순위를 주장하는 글이 아니다.

## 이 글에서 얻을 답과 범위

첫 글의 Llama 3 Herd가 **dense Transformer(밀집 Transformer)를 큰 데이터·큰 규모·안정적 운영으로 만드는 법**을 보여 줬다면, 이 글의 질문은 반대편에 가깝다.

> **671B 파라미터를 저장하면서도, 한 토큰에 약 37B 파라미터만 계산하려면 모델·메모리·네트워크를 어떻게 함께 설계해야 할까?**

이 글을 읽고 다음을 설명할 수 있으면 된다.

1. MoE(Mixture of Experts, 전문가 혼합)에서 `총 파라미터 671B`와 `활성 파라미터 37B`는 무엇이 다른가?
2. router(라우터)는 토큰을 expert(전문가)에게 어떻게 보내며, 왜 load balancing(부하 균형)이 모델 품질 문제이자 분산 시스템 문제인가?
3. MLA(Multi-head Latent Attention, 다중 헤드 잠재 어텐션)는 KV cache(Key-Value cache, 키-값 캐시)를 왜 크게 줄이는가?
4. MTP(Multi-Token Prediction, 다중 토큰 예측)는 실제 답변을 두 토큰씩 항상 출력하는 기능인가?
5. FP8, DualPipe, expert parallelism(전문가 병렬화), redundant expert(중복 배치 전문가)는 왜 논문 바깥의 "구현 세부"가 아니라 모델의 실현 조건인가?
6. RAG·AI 백엔드·데이터 파이프라인을 만드는 나는 이 논문에서 무엇을 가져오고, 무엇은 당장 하지 말아야 하는가?

**범위:** 논문의 언어 모델, 학습, 사후학습, 배포를 다룬다. H800 클러스터를 재현하거나 671B 가중치를 실행하지는 않는다. 이 컴퓨터에는 NVIDIA GPU와 DeepSeek-V3 가중치가 없으므로, 본문 후반의 직접 검증은 공개된 configuration(설정값)으로 MLA cache와 router 원리를 계산한 것이다.

### 먼저 외울 한 문장

> **DeepSeek-V3는 토큰마다 필요한 8개 routed expert(경로 선택 전문가)와 1개 shared expert(공유 전문가)만 실행하는 671B MoE Transformer이며, MLA로 KV cache를 압축하고, router 부하·GPU 통신·FP8 수치 오차를 동시에 다뤄야 실제로 저렴해지는 모델이다.**

## 왜 필요한가: 큰 모델은 계산보다 "놀고 있는 자원" 때문에 비싸진다

LLM(Large Language Model, 대규모 언어 모델)의 비용을 처음 배울 때는 보통 이렇게 생각한다.

```text
파라미터가 671B다
→ 671B만큼 계산하니 매우 느리고 비싸다
```

절반만 맞다. dense 모델에서는 각 토큰이 각 층의 큰 FFN(Feed-Forward Network, 토큰별 비선형 변환 층)을 통과한다. 반면 MoE는 토큰의 성격에 맞는 expert 일부만 통과시킨다. 코드 토큰은 코드에 익숙한 expert 쪽으로, 수학 토큰은 다른 expert 쪽으로 보내는 식이다.

하지만 "몇 개만 고르니 공짜"가 아니다. 다음 실패가 바로 생긴다.

- 대부분의 토큰이 인기 expert 하나에 몰리면 그 GPU만 기다리게 된다. 이것이 **routing collapse(라우팅 붕괴)** 또는 load imbalance(부하 불균형)다.
- expert가 여러 GPU·여러 node(노드, 서버 묶음)에 있으면 토큰 activation(활성값)을 all-to-all(모든 참여자가 서로 데이터를 교환하는 통신)로 보내고 다시 받아야 한다.
- 긴 문맥에서는 모델 가중치보다 KV cache가 GPU 메모리를 먼저 잠식할 수 있다.
- 8-bit 부동소수점(FP8)으로 계산을 줄이면 빠르지만, 숫자 범위가 작아 overflow(표현 가능한 최대값을 넘음)·underflow(너무 작아 0처럼 사라짐)·누적 오차가 생길 수 있다.
- 학습 때의 expert 부하 분포와 실제 서비스 요청 분포가 다르면, 학습에서 균형이던 라우터가 운영에서 특정 expert를 과부하시킬 수 있다.

이 논문의 핵심은 "MoE를 썼다"가 아니다. **희소한 계산(sparse compute), 캐시 압축, 정밀도, 통신, 배포 재배치가 서로 맞물려야 MoE가 비용 이점으로 바뀐다**는 점이다.

## 무엇인가: 671B와 37B를 절대 같은 숫자로 읽지 않기

논문과 공식 [DeepSeek-V3 README](https://github.com/deepseek-ai/DeepSeek-V3/blob/main/README.md)는 다음을 보고한다.

| 수치 | 뜻 | 잘못된 해석 |
|---|---|---|
| 671B total parameters | 모든 expert, attention, embedding, output head를 합친 저장 가중치 수 | 요청마다 671B 전체를 계산한다. |
| 37B activated parameters | 한 토큰 처리에서 실제 경로에 참여하는 가중치의 대략적 규모 | 모델 전체가 37B라서 37B 모델처럼 배포가 쉽다. |
| 256 routed experts | MoE 층에서 router가 후보로 보는 전문가 수 | 매 토큰이 256개를 전부 실행한다. |
| 8 activated routed experts | router가 토큰마다 고르는 routed expert 수 | shared expert도 포함한 숫자다. |
| 1 shared expert | 모든 토큰이 항상 함께 통과하는 expert | router가 선택할 때만 실행된다. |
| 128K context | 한 요청에서 지원하는 최대 문맥 토큰 길이 | 128K 문서의 모든 사실을 정확히 회수한다. |

`37 / 671 = 약 5.51%`다. 이 비율은 **계산에 참여하는 파라미터 비율의 감각**을 줄 뿐, 서비스 비용이 정확히 5.51%라는 뜻은 아니다. 671B 가중치를 GPU 여러 대에 올려 둬야 하고, attention·KV cache·통신·메모리 대역폭·batch 크기·router 편향도 비용에 들어간다.

이 차이를 데이터베이스로 비유해 보자.

```text
671B total
  = 전체 shard와 모든 index를 포함한 데이터 플랫폼 크기

37B activated
  = 특정 요청이 실제 읽은 shard·index·실행 연산의 규모
```

요청이 일부 shard만 읽는다고 해서, 나머지 shard를 저장·복제·장애 복구하지 않아도 되는 것은 아니다. MoE도 같다.

## 선행 개념: Transformer, FFN, expert, router를 12살 말로 풀기

### Transformer block은 두 종류의 일을 반복한다

LLM의 한 Transformer block(변환기 블록)은 크게 두 가지를 한다.

```text
1. attention(어텐션)
   → 지금 토큰이 앞 문맥의 어느 토큰 정보를 얼마나 가져올지 계산한다.

2. FFN(Feed-Forward Network, 피드포워드 네트워크)
   → 그렇게 섞인 정보를 현재 토큰 안에서 비선형으로 변환한다.
```

dense Transformer의 FFN은 모든 토큰에 같은 큰 계산기를 적용한다.

```text
모든 토큰 → 같은 FFN → 출력
```

MoE의 FFN은 여러 작은 계산기를 두고, router가 일부만 고른다.

```text
토큰 → router → 선택된 expert 8개 + 항상 실행되는 shared expert 1개 → 가중합 → 출력
```

**expert**는 마법처럼 "코드만 아는 사람"이라고 미리 지정된 사람이 아니다. 처음에는 모두 비슷한 신경망이며, 데이터·router·학습 과정이 특정 패턴에 더 많이 반응하도록 만들 수 있다. 논문 부록의 관찰처럼 도메인별 사용 비율이 달라질 수 있지만, 사람이 읽을 수 있는 역할이 expert마다 하나씩 고정된다고 단정하면 안 된다.

### 점수와 실제 가중치는 다를 수 있다

router는 입력 토큰 표현 `u_t`와 expert별 학습 벡터 `e_i`의 친화도(잘 맞는 정도)를 만든다.

```text
s(i, t) = sigmoid(u_tᵀ e_i)
```

- `t`: 현재 토큰 위치
- `i`: expert 번호
- `u_t`: 현재 토큰의 숫자 벡터
- `e_i`: i번 expert를 대표하는 학습 벡터
- `u_tᵀ e_i`: 두 벡터가 얼마나 맞는지 나타내는 내적
- `sigmoid`: 값을 0과 1 사이로 누르는 함수

큰 점수의 Top-K(상위 K개) expert만 실행하고, 선택된 expert의 출력은 gate weight(게이트 가중치)로 섞는다. DeepSeek-V3의 중요한 미세 차이는 **부하 조정 bias는 누구를 고를지에만 쓰고, expert 출력의 섞는 비율은 원래 affinity(친화도)에서 계산한다**는 것이다. 이 차이는 뒤의 직접 계산에서 확인한다.

### KV cache와 왜 긴 답변이 비싸지는가

autoregressive generation(자기회귀 생성)은 답을 한 토큰씩 만든다. 새 토큰을 만들 때 이전 토큰의 Key와 Value를 매번 다시 계산하면 너무 느리다. 그래서 과거 Key·Value를 GPU 메모리에 저장하는 것이 KV cache다.

```text
prompt를 읽는다: prefill(프리필)
  → 과거 모든 토큰의 K/V를 cache에 만든다.

답을 한 토큰씩 만든다: decoding(디코딩)
  → 새 토큰의 Q가 cache의 과거 K/V를 참고한다.
```

긴 문맥의 비용은 따라서 두 갈래다.

1. attention 점수 계산이 문맥 길이에 따라 커진다.
2. 각 층·각 요청의 KV cache가 길이에 비례해 커진다.

DeepSeek-V3의 MLA는 두 번째 문제, 특히 **cache에 무엇을 저장할지**를 바꾼다.

## 밑바닥 원리: MoE, MLA, MTP를 수식과 작은 예제로 연결하기

### DeepSeekMoE: shared expert와 routed expert를 함께 둔다

논문 [2.1.2절](https://arxiv.org/pdf/2412.19437v2#page=8)의 MoE 출력은 개념적으로 아래와 같다.

```text
출력
= 입력(잔차 연결)
+ 모든 shared expert 출력의 합
+ 선택된 routed expert 출력의 가중합
```

수식으로 쓰면 다음이다.

```text
h'_t = u_t
     + Σ FFN_shared_i(u_t)
     + Σ g(i, t) × FFN_routed_i(u_t)
```

- `u_t`: FFN으로 들어오는 t번째 토큰 표현
- `h'_t`: FFN을 지난 뒤의 새 표현
- `FFN_shared_i`: 모든 토큰이 실행하는 공유 expert
- `FFN_routed_i`: router가 고른 경우에만 실행하는 expert
- `g(i, t)`: 선택된 expert 출력에 곱하는 가중치

공유 expert는 모든 토큰에 공통으로 필요한 패턴을 처리하게 두고, routed expert는 더 세분화된 패턴에 특화할 여지를 준다. 이것은 역할을 보장하는 규칙이 아니라, specialization(전문화)이 학습될 수 있게 만든 구조 선택이다.

### 부하 균형: "인기 가게" 문제를 loss에만 맡기지 않는다

expert 256개 중 몇 개만 계속 고르면, 인기 expert가 있는 GPU가 병목이 된다. 전통적인 MoE는 auxiliary loss(보조 손실)를 추가해 expert 선택 비율이 균등해지도록 벌점을 준다.

```text
전체 손실 = 언어 모델 손실 + α × 부하 불균형 벌점
```

문제는 `α`를 크게 하면 router가 "토큰에 잘 맞는 expert"보다 "빈 expert"를 고르도록 학습될 수 있다는 점이다. 작으면 부하가 다시 쏠린다.

DeepSeek-V3의 **auxiliary-loss-free load balancing(보조 손실 없는 부하 균형)**은 expert별 routing bias `b_i`를 따로 조정한다.

```text
선택 점수 = 원래 친화도 s(i, t) + expert별 bias b_i
```

- 과부하 expert: 다음 step에서 `b_i`를 `γ`만큼 낮춘다.
- 저부하 expert: 다음 step에서 `b_i`를 `γ`만큼 높인다.
- `γ(gamma)`: bias update speed(편향 갱신 속도)다.

여기서 이름을 정확히 읽어야 한다. 논문은 **전역 batch 단위**의 주요 균형에 보조 손실을 쓰지 않는다는 뜻으로 auxiliary-loss-free라 부른다. 어떤 sequence(시퀀스, 한 입력 묶음) 안에서 너무 극단적으로 몰리는 일을 막기 위해, 아주 작은 `α = 0.0001`의 sequence-wise auxiliary loss(시퀀스 단위 보조 손실)는 여전히 쓴다. "보조 손실이 0개"라는 뜻이 아니다.

### MLA: K와 V 전체 대신 압축 잠재 벡터를 cache한다

일반 Multi-Head Attention(MHA, 다중 헤드 어텐션)에서는 head마다 Key와 Value를 cache한다. DeepSeek-V3 MLA는 현재 토큰의 입력 `h_t`를 먼저 작은 latent vector(잠재 벡터) `c_t^KV`로 줄인다.

```text
c_t^KV = W_D^KV × h_t
```

- `h_t`: attention 입력 벡터
- `W_D^KV`: down-projection(차원을 줄이는 행렬)
- `c_t^KV`: 압축된 Key/Value 공통 잠재 벡터

이 압축 벡터에서 head별 Key·Value를 복원할 수 있다. 하지만 RoPE(Rotary Position Embedding, 회전 위치 임베딩)는 위치 정보를 Query·Key에 회전 형태로 넣으므로, 단순히 모든 것을 한 덩어리로 흡수하기 어렵다. 그래서 MLA는 RoPE가 적용된 별도의 shared key `k_t^R`를 함께 보관한다.

```text
MLA가 generation 중 cache하는 것
  = compressed KV latent c_t^KV
  + position용 shared key k_t^R
```

이렇게 Key와 Value의 원래 head별 전체를 cache하지 않는 것이 핵심이다. [공식 구현의 `MLA` 클래스](https://github.com/deepseek-ai/DeepSeek-V3/blob/9b4e9788e4a3a731f7567338ed15d3ec549ce03b/inference/model.py#L396-L497)도 `attn_impl == "absorb"` 경로에서 `kv_cache`와 `pe_cache`만 저장하고, attention score를 두 항의 합으로 계산한다.

### MTP: 추론 때 두 토큰을 무조건 내보내는 기능이 아니다

일반 next-token prediction(다음 토큰 예측)은 위치 `i`에서 `t_(i+1)`만 맞힌다. DeepSeek-V3는 학습 때 MTP module을 추가해 그보다 한 단계 더 먼 토큰도 맞힌다.

```text
main model: 입력 t_i → 다음 토큰 t_(i+1) 예측
MTP depth 1: main 표현 + 정답 t_(i+1)의 embedding → t_(i+2) 예측
```

논문의 V3 설정에서 MTP depth `D = 1`이다. 즉 "추가로 한 토큰"을 예측한다. 학습 데이터의 실제 다음 토큰을 사용해 다음 단계 예측을 이어 가는 것은 teacher forcing(정답을 앞 입력으로 주는 학습 방식)이다. 미래 답을 몰래 보는 것이 아니라, 정상적인 next-token 학습을 한 단계 더 조밀하게 만든다.

MTP 손실은 각 깊이의 cross-entropy를 평균내고 가중치 `λ`를 곱한 추가 손실이다.

```text
L_total = L_next_token + λ × L_MTP
```

논문은 MTP의 주 목적을 **학습 신호를 더 촘촘하게 해 본체 모델 품질을 올리는 것**으로 둔다. 표 4의 ablation(다른 조건을 고정한 비교)에서는 추론 활성 파라미터 수가 같은 작은·큰 MoE에서 MTP가 여러 평가를 개선했다고 보고한다. 일반 추론에서는 MTP module을 버려도 main model이 독립적으로 동작한다. 다만 speculative decoding(추측 디코딩)에서는 MTP가 다음 토큰 후보를 미리 제안하고 main 검증이 받아들인 부분을 한 번에 확정해 decoding을 빠르게 할 수 있다.

따라서 "MTP가 있으니 답변이 항상 2배 빠르다"는 말은 틀리다. 제안 토큰의 acceptance rate(수용률), serving engine 지원, batch, 메모리, 실제 요청 길이를 측정해야 한다. 논문 5.4.3절은 특정 평가에서 두 번째 토큰의 수용률 85~90%, speculative decoding 결합 시 1.8배 TPS(Tokens Per Second, 초당 토큰 수)를 보고하지만, 이는 그들의 구현·하드웨어 조건의 수치다.

## 내부 구조와 실제 실행 흐름

### 논문 configuration을 숫자로 읽기

논문 [4.2절](https://arxiv.org/pdf/2412.19437v2#page=21)과 공개된 [`config_671B.json`](https://github.com/deepseek-ai/DeepSeek-V3/blob/9b4e9788e4a3a731f7567338ed15d3ec549ce03b/inference/configs/config_671B.json)을 대조하면 핵심 수치는 다음과 같다.

| 항목 | 값 | 의미 |
|---|---:|---|
| Transformer layer | 61 | attention + FFN/MoE block을 61번 쌓는다. |
| hidden dimension | 7,168 | 토큰 표현 벡터 길이다. |
| dense layer | 처음 3개 | 첫 세 FFN만 일반 dense MLP다. |
| MoE layer | 나머지 58개 | 이후 FFN은 expert routing을 쓴다. |
| routed experts | 256 | 각 MoE 층의 선택 후보 수다. |
| shared experts | 1 | 모든 토큰에 항상 적용되는 expert 수다. |
| activated routed experts | 8 | 토큰마다 Top-8 routed expert를 실행한다. |
| node 제한 | 최대 4개 node | 토큰이 너무 많은 서버로 흩어지지 않게 한다. |
| attention heads | 128 | attention의 병렬 관점 수다. |
| MLA KV latent dimension | 512 | KV를 압축해 저장하는 차원이다. |
| RoPE key/query dimension | 64 | 위치 정보를 따로 보존하는 차원이다. |
| MTP depth | 1 | 다음 토큰 뒤의 한 토큰을 추가 학습한다. |

한 가지 숫자 차이도 기록해 둔다. 논문은 tokenizer vocabulary를 128K라고 표현하지만, 공개 config의 `vocab_size`는 `129280`이다. 둘을 같은 정밀도로 단정하지 않는다. 논문은 둥근 단위로 설명하고 config는 구현의 ID 공간을 적는다. 원문이 이 차이를 명시적으로 풀이하지 않으므로, 구현을 읽을 때는 config 값, 논문 개요를 말할 때는 128K라는 범위를 각각 사용한다.

### 코드로 따라가는 한 토큰의 경로

공식 inference demo의 실제 흐름은 다음과 같다.

```text
token IDs
  → ParallelEmbedding
  → Block 0 .. 60
      → RMSNorm → MLA → residual add
      → RMSNorm → dense MLP(처음 3층) 또는 MoE(나머지 층) → residual add
  → RMSNorm
  → vocabulary logits
  → sampling으로 다음 token ID 선택
```

공개 코드에서 이 흐름을 직접 확인했다.

- `Block`은 layer ID가 3보다 작으면 dense `MLP`, 아니면 `MoE`를 고른다. [model.py:715-735](https://github.com/deepseek-ai/DeepSeek-V3/blob/9b4e9788e4a3a731f7567338ed15d3ec549ce03b/inference/model.py#L715-L735)
- `Gate`는 sigmoid score → bias 추가 → group 제한 → Top-K 선택을 하고, 선택된 원래 score를 정규화해 weight로 쓴다. [model.py:535-598](https://github.com/deepseek-ai/DeepSeek-V3/blob/9b4e9788e4a3a731f7567338ed15d3ec549ce03b/inference/model.py#L535-L598)
- `MoE`는 선택된 local expert 출력에 weight를 곱하고, shared expert 출력 `z`를 더한다. 다중 GPU면 expert 결과를 `all_reduce`한다. [model.py:636-693](https://github.com/deepseek-ai/DeepSeek-V3/blob/9b4e9788e4a3a731f7567338ed15d3ec549ce03b/inference/model.py#L636-L693)
- `Transformer.forward`는 causal mask로 미래 토큰을 가리고 61개 block을 순서대로 통과시킨 뒤 logits를 만든다. [model.py:738-798](https://github.com/deepseek-ai/DeepSeek-V3/blob/9b4e9788e4a3a731f7567338ed15d3ec549ce03b/inference/model.py#L738-L798)

공식 코드는 모델 **추론 demo**다. 훈련용 HAI-LLM framework나 DualPipe 전체 구현을 공개했다는 뜻은 아니다. 코드가 보여 주는 범위와 논문이 설명하는 대규모 훈련 인프라 범위를 섞지 않았다.

### 데이터와 장문 문맥의 실행 경로

```text
웹·코드·수학·다국어 원천
  → 중복 최소화·다양성 유지
  → Byte-level BPE tokenizer
  → document packing
  → 14.8T 토큰 사전학습(기본 4K)
  → YaRN으로 4K → 32K → 128K 장문 문맥 확장
  → SFT → RL/GRPO → Chat model
```

논문은 V2보다 수학·프로그래밍 비율과 다국어 범위를 늘렸고, FIM(Fill-in-the-Middle, 중간 채우기)을 PSM(Prefix-Suffix-Middle, 앞-뒤-중간) 형식으로 10% 적용했다고 설명한다.

```text
<|fim_begin|> 앞부분 <|fim_hole|> 뒷부분 <|fim_end|> 정답 중간 <|eos_token|>
```

코드 자동완성처럼 "앞과 뒤를 보고 가운데를 채우기"에 도움이 되는 학습 형식이다. 주의할 차이도 있다. Llama 3 글에서 본 것처럼 packed document 사이 attention을 막는 모델도 있지만, DeepSeek-V3 논문은 data integrity(문서 온전성)를 위해 document packing을 하면서 **cross-sample attention masking을 넣지 않았다**고 쓴다. 같은 "packing"이라도 모델마다 문서 경계를 어떻게 취급하는지 다르다.

긴 문맥은 YaRN(긴 문맥용 RoPE 보정 방법)을 써서 사전학습 4K에서 32K, 다시 128K로 각각 1,000 step 확장했다. YaRN 설정은 MLA의 모든 KV가 아니라 decoupled shared key `k_t^R`에 적용한다. 이 사실은 MLA가 위치 정보를 별도 경로로 분리한 이유와 연결된다.

### 훈련 인프라: MoE는 네트워크 아키텍처이기도 하다

논문은 2,048개의 NVIDIA H800 GPU cluster에서 V3를 학습했다고 보고한다. 구성은 16-way PP(Pipeline Parallelism, 층을 stage로 나누는 병렬화), 64-way EP(Expert Parallelism, expert를 여러 GPU에 나누는 병렬화), ZeRO-1 DP(Data Parallelism, optimizer 상태를 나눠 갖는 데이터 병렬화)다.

```text
토큰 → 현재 GPU의 router
     → 선택 expert가 있는 GPU/node로 dispatch(all-to-all)
     → expert FFN 실행
     → 결과를 원래 흐름으로 combine(all-to-all)
     → 다음 Transformer layer
```

이 경로는 통신이 계산만큼 오래 걸릴 수 있다. 논문은 그 비율을 약 1:1이라고 보고하고, **DualPipe**로 forward·backward 계산과 all-to-all·pipeline 통신을 겹친다. 핵심은 "통신이 없어졌다"가 아니라, 계산 중에 통신을 진행해 사용자가 기다리는 추가 시간을 가능한 한 숨기는 것이다. 네트워크가 느리거나 배치가 작거나 router가 쏠리면 이 겹침이 깨질 수 있다.

### FP8: 숫자를 8비트로 줄여도 기준값을 버리지 않는다

FP8은 숫자 하나를 8비트로 표현하는 저정밀 부동소수점 형식이다. DeepSeek-V3는 많은 GEMM(General Matrix Multiplication, 일반 행렬 곱)을 FP8로 수행해 연산과 activation 저장 공간을 줄이지만, 전부를 8비트로 바꾸지 않는다.

| 구간 | 논문의 선택 | 이유 |
|---|---|---|
| 주요 Linear GEMM forward/backward | FP8 | 계산량과 메모리 절감 |
| embedding, output head, MoE gate, norm, attention | BF16 또는 FP32 유지 | 작은 오차에도 민감하거나 고정밀 비용이 상대적으로 작음 |
| master weight·gradient·optimizer 핵심 상태 | 높은 정밀도 유지 | 장기 학습 안정성 |
| activation | token당 1×128 tile로 scale | 일부 outlier가 전체 tensor scale을 망치지 않게 함 |
| weight | 128×128 block으로 scale | 가중치 블록별 범위 차이를 반영 |

**quantization(양자화)**은 숫자를 적은 비트로 근사 표현하는 일이다. 값 하나를 그냥 8비트로 잘라 저장하면 큰 값 하나가 scale을 독점하거나 작은 값이 사라질 수 있다. 그래서 V3는 작은 tile/block마다 scale factor(확대·축소 기준값)를 따로 두고, FP8 행렬 곱의 중간 합은 CUDA core의 FP32 register에 주기적으로 올려 누적 정밀도를 높였다. 논문은 BF16 baseline 대비 FP8 훈련의 relative loss error가 약 0.25% 아래였다고 보고하지만, 이는 해당 실험·모델·프레임워크에서의 결과다.

## 직접 검증: MLA cache와 router bias를 숫자로 확인하기

### 검증 범위와 환경

**직접 실행 확인:** 2026-07-24, macOS 26.4.1, Python 3.9.6. NVIDIA GPU와 DeepSeek-V3 671B 가중치가 없으므로 실제 모델 추론·FP8 kernel·2,048 GPU 훈련을 실행하지 않았다.

대신 공개 configuration과 공식 구현으로 다음을 계산했다.

1. 61개 층, 128 head, 128K 문맥에서 naive MHA cache와 MLA absorb cache의 이론상 크기
2. 671B 중 37B 활성 파라미터 비율
3. router bias가 선택만 바꾸고, 선택된 출력 weight는 원래 affinity에서 나오는지

```python
# /tmp/verify_deepseek_v3_mechanics.py
LAYERS = 61
HEADS = 128
QK_NOPE_DIM = 128
QK_ROPE_DIM = 64
VALUE_DIM = 128
KV_LATENT_DIM = 512
TOKENS = 131_072
BYTES_PER_BF16 = 2

# 일반 MHA: 각 head의 K(nope+RoPE)와 V를 저장한다.
naive_scalars_per_token = HEADS * (QK_NOPE_DIM + QK_ROPE_DIM + VALUE_DIM)
# MLA absorb cache: 압축 KV와 공유 RoPE key만 저장한다.
mla_scalars_per_token = KV_LATENT_DIM + QK_ROPE_DIM

def cache_gib(scalars_per_token):
    return LAYERS * TOKENS * scalars_per_token * BYTES_PER_BF16 / (1024 ** 3)

print(naive_scalars_per_token, mla_scalars_per_token)
print(round(naive_scalars_per_token / mla_scalars_per_token, 2))
print(round(cache_gib(naive_scalars_per_token), 2))
print(round(cache_gib(mla_scalars_per_token), 2))
print(f"{37 / 671:.2%}")

# 논문 §2.1.2: bias는 Top-K route 선택에만 쓰고,
# 실제 expert output weight는 original affinity에서 계산한다.
affinity = [0.72, 0.64, 0.61, 0.55]
bias = [-0.12, 0.04, 0.06, 0.05]
adjusted = [score + shift for score, shift in zip(affinity, bias)]
selected = sorted(range(4), key=lambda i: adjusted[i], reverse=True)[:2]
raw = [affinity[i] for i in selected]
weights = [score / sum(raw) for score in raw]
print(selected, [round(x, 4) for x in weights])
```

실행 결과다.

```text
naive scalars/token/layer: 40960
MLA scalars/token/layer: 576
compression ratio: 71.11
naive 128K cache, 61 layers: 610.0 GiB
MLA 128K cache, 61 layers: 8.58 GiB
37B / 671B: 5.51%
selected experts: [1, 2]
gate weights from original affinity: [0.512, 0.488]
```

해석의 경계를 분명히 한다.

- `610.0GiB`와 `8.58GiB`는 **batch 1, 128K, BF16/FP16 scalar 2 byte, 61개 층을 모두 합친 모델 전체 cache의 단순 산술**이다. 실제 배포에서는 head와 층을 GPU에 shard(분할)하므로 GPU 한 장이 이 전부를 들고 있지 않는다.
- 가중치, activation, runtime workspace, allocator 여유분, network buffer는 이 계산에 없다.
- MLA의 약 71배는 이 config의 cache scalar 수 비교다. 실제 end-to-end 속도는 메모리 대역폭, prefill, all-to-all, batch, serving engine에 따라 달라진다.
- toy router 예시는 논문의 bias 규칙을 숫자로 보인 것이지, 학습된 expert의 의미나 실제 트래픽 분포를 재현한 것이 아니다.

## 성능과 트레이드오프: 희소 계산은 운영 복잡도와 교환된다

### Llama 3의 dense 선택과 DeepSeek-V3의 MoE 선택

| 질문 | Llama 3 Herd의 dense 경로 | DeepSeek-V3의 MoE 경로 |
|---|---|---|
| 토큰당 FFN 계산 | 일반적으로 모든 dense 가중치 경로 | routed expert 일부 + shared expert |
| 주된 강점 | 구조·통신·부하 관리가 상대적으로 단순 | 큰 총 용량을 두면서 활성 계산을 제한 |
| 주된 병목 | 모델 전체 연산·KV cache·모델 병렬 | router 부하, expert all-to-all, 배포 재배치 |
| 시스템 난이도 | 큰 모델 분산 운영 | 큰 모델 분산 운영 + 동적 expert 부하 운영 |
| 논문의 우선순위 | 안정성·규모·데이터 품질 | 비용 효율·통신 겹침·정밀도·부하 균형 |

어느 쪽이 "더 좋다"가 아니다. 작은 팀이 RAG 서비스를 만들 때는 671B MoE architecture를 재현하는 것보다, 모델 API 또는 검증된 serving engine 뒤에 데이터 계약·검색 평가·권한·관측성을 먼저 세우는 편이 훨씬 현실적이다.

### prefill과 decode를 분리하는 이유

논문은 online SLO(Service-Level Objective, 서비스 목표)와 throughput(처리량)을 함께 맞추기 위해 prefill과 decoding cluster를 분리한다.

| 단계 | 주로 하는 일 | 논문 최소 배포 단위 | 핵심 병목 |
|---|---|---:|---|
| Prefill | 긴 prompt를 읽고 KV cache 생성 | 4 node / 32 GPU | 큰 연산량, attention·MoE 통신 |
| Decode | 토큰을 하나씩 생성 | 40 node / 320 GPU | 작은 batch의 메모리 접근·낮은 지연 시간 |

decode가 더 큰 node 수를 요구하는 이유는 "모델이 더 복잡해서"만이 아니다. 논문은 decoding에서 GPU마다 expert 하나를 두고, 과부하 expert와 shared expert에는 redundant expert를 추가 배치한다. online load 통계를 보고 약 10분 간격으로 고부하 expert를 조정한다. 이는 MoE deployment가 model loading에서 끝나지 않고 **실제 요청 분포를 관찰하는 운영 작업**임을 보여 준다.

### 학습 비용 숫자를 광고 문구로 쓰지 않기

논문 표 1의 총 `2.788M H800 GPU hours`는 pre-training 2.664M, context extension 119K, post-training 5K의 합이다. 논문은 H800 대여를 GPU 시간당 2달러로 가정해 5.576M 달러를 제시했다.

이 숫자는 비교할 때 다음을 빼면 안 된다.

```text
포함: 공식 V3 학습 단계의 GPU 시간
제외: 아키텍처·데이터·알고리즘을 찾기 위한 선행 연구와 ablation 비용
가정: H800 시간당 2달러
```

따라서 이것은 "오늘 누구나 557만 달러면 같은 모델을 만든다"는 견적이 아니다. 하드웨어, 전력, 네트워크, 인력, 데이터 권리, 실패 재시도, 연구 비용이 빠진 해당 보고의 산술이다.

## 실패, 한계, 장애와 운영 기준

### 논문이 명시한 한계

논문 결론은 다음 한계를 직접 든다.

1. 효율적인 추론을 위한 권장 deployment unit이 커서 작은 팀에는 부담이다.
2. V2보다 end-to-end generation speed가 2배 넘게 개선됐다고 해도 더 빠르게 만들 여지가 남아 있다.
3. 고정 benchmark에 과도하게 최적화하면 실제 능력을 오해할 수 있으므로 더 다차원적인 평가가 필요하다.

여기에 운영 관점의 실패를 붙이면 다음과 같다.

| 실패 방식 | 징후 | 먼저 확인할 것 |
|---|---|---|
| expert hot spot | 일부 GPU만 queue·latency가 급증 | expert별 token count, node별 dispatch bytes, redundant expert 상태 |
| router 품질 저하 | 특정 도메인에서 답 품질·전문성 하락 | route 분포와 실패 slice, 학습·운영 데이터 분포 차이 |
| all-to-all 병목 | GPU 사용률은 낮은데 step/요청 시간이 김 | IB/NVLink 대역폭, dispatch/combine trace, micro-batch 크기 |
| FP8 수치 불안정 | loss spike, NaN, 특정 layer 이상 | tile scale 분포, outlier, FP32 master weight/gradient 보존 여부 |
| long-context 착시 | NIAH는 통과하지만 실제 문서 질의 실패 | 문서 위치별·질문 유형별 평가, citation, RAG baseline 비교 |
| MTP 과대평가 | 기대한 decoding 속도가 안 나옴 | acceptance rate, draft/verify 비용, engine 지원, TTFT/TPOT |

### 안전과 권한은 이 논문의 빈칸이다

DeepSeek-V3 논문은 모델 구조·효율·일반 평가에 집중한다. 내가 구축할 RAG·MCP(Model Context Protocol, 모델 문맥 프로토콜) 시스템에서는 이것만으로 충분하지 않다.

```text
모델이 tool call을 잘 만든다
≠ 그 tool call을 실행해도 안전하다.

모델이 128K 문맥을 읽는다
≠ tenant(테넌트) 권한이 없는 문서를 넣어도 된다.
```

AI 백엔드에서는 모델 앞에 ACL(Access Control List, 접근 제어 목록), source provenance(원천·버전 계보), prompt-injection negative test(주입 공격 차단 시험), tool allowlist(허용 도구 목록), human approval(사람 승인) 같은 경계가 필요하다. 이는 논문의 성능을 부정하는 말이 아니라, **모델 논문이 제품 보안 설계서를 대신하지 않는다**는 경계다.

## 대안, 비교와 선택 기준

### 내 상황에서 MoE를 언제 배워야 하나

| 상황 | 우선 판단 | 행동 |
|---|---|---|
| RAG 품질이 아직 불안정 | MoE가 아니라 retrieval·chunk·eval 문제일 가능성이 큼 | BM25/dense/rerank, dev·locked test, citation부터 고정 |
| API 모델을 사용한다 | 내부 MoE 배포를 직접 관리하지 않음 | 모델별 context·비용·지연 시간만 측정하고 application SLO를 설계 |
| open-weight 대형 모델을 직접 serving한다 | VRAM, node 수, network, engine 지원이 핵심 | MLA/MTP 지원 여부, quantization, prefill/decode 분리부터 확인 |
| expert 라우팅을 연구한다 | router 품질과 부하 균형을 동시에 검증해야 함 | 작은 MoE부터 expert load·quality slice·통신 trace를 함께 실험 |
| 데이터 파이프라인 직무를 목표로 한다 | 대형 pretraining보다 운영 가능한 데이터 증거가 더 중요 | manifest, backfill, DQ, index release, rollback, evaluation을 만든다 |

### 지금 당장 하지 않을 일

- 671B MoE를 로컬에서 실행하려고 하드웨어를 무작정 사지 않는다. 공식 demo도 Linux/Python 3.10과 다중 node 실행을 전제로 설명한다.
- 37B active라는 숫자만 보고 37B dense 모델과 같은 배포 난이도라고 가정하지 않는다.
- FP8 변환만 하면 더 빠르고 같은 품질이라고 가정하지 않는다. kernel·GPU·scale·serving engine·정확도 회귀를 함께 본다.
- MTP가 있다고 모델 출력 두 토큰을 검증 없이 사용하지 않는다. speculative decoding은 draft 제안과 main 검증의 계약이다.
- MoE를 쓰면 RAG·데이터 계약·ACL·평가가 필요 없어진다고 생각하지 않는다.

## 흔한 오해와 최초 질문에 대한 답

### 오해 1: "671B인데 토큰마다 37B만 쓰니 37B 모델보다 무조건 싸다"

아니다. FFN 계산은 줄일 수 있지만 671B 가중치 저장, KV cache, attention, expert 통신, batch, GPU 대수, router 편향 비용이 남는다. **활성 파라미터는 계산 경로의 숫자이지 전체 운영비 청구서가 아니다.**

### 오해 2: "expert는 사람이 이름 붙인 전공자다"

아니다. shared/routed라는 구조 역할은 명시하지만, routed expert 1번이 항상 Java만, 2번이 항상 수학만 맡는 식의 고정 의미는 보장하지 않는다. 도메인별 route 통계와 실제 평가로 specialization을 관찰해야 한다.

### 오해 3: "auxiliary-loss-free는 부하 균형 loss가 하나도 없다는 뜻이다"

아니다. 주요 batch-wise 균형은 bias 조정으로 다루지만, 논문은 시퀀스 내부 극단적 불균형을 막는 작은 sequence-wise auxiliary loss도 쓴다.

### 오해 4: "MLA는 attention을 근사해서 품질을 희생한 단순 압축이다"

정확한 표현이 아니다. MLA는 K/V를 shared latent로 압축하고 위치 성분을 분리해 cache 형태를 바꾸는 attention 구조다. 논문은 표준 MHA와 비슷한 성능을 유지하면서 cache를 줄이는 것을 목표로 검증했다. 다만 모든 모델·모든 구현에서 같은 품질·속도를 보장하는 일반 법칙은 아니다.

### 오해 5: "MTP는 ChatGPT처럼 답을 두 단어씩 보여 주는 기능이다"

아니다. V3에서 MTP는 우선 학습 목적이다. 기본 추론에서는 MTP module을 버릴 수 있고, speculative decoding을 구현할 때만 제안 토큰으로 재사용할 수 있다.

### 전문가에게 1분 안에 설명한다면

> DeepSeek-V3는 61층 MoE Transformer로, 총 671B 파라미터 중 토큰마다 8개 routed expert와 1개 shared expert를 거쳐 약 37B만 활성화합니다. 핵심은 MoE 자체보다 세 가지입니다. 첫째, MLA가 128개 head의 K/V 전체 대신 512차원 compressed KV와 64차원 위치 key를 cache해 장문 decode 메모리를 크게 줄입니다. 둘째, expert별 bias를 router 선택에만 적용해 batch 부하를 맞추고, 실제 expert 출력 weight는 원래 affinity로 유지합니다. 셋째, FP8·DualPipe·all-to-all 통신 겹침·prefill/decode 분리·중복 expert 배치가 MoE의 계산 이득을 실제 운영 이득으로 바꿉니다. 그래서 이 논문은 671B 모델 소개라기보다, 희소 모델을 분산 시스템으로 운영하는 설계서로 읽어야 합니다.

## 출처

### 1차 자료

1. DeepSeek-AI, **[DeepSeek-V3 Technical Report, arXiv:2412.19437v2](https://arxiv.org/abs/2412.19437)**, 2025-02-18. 모델 구조, data, FP8, 인프라, post-training, 평가, 한계의 주 근거다.
2. DeepSeek-AI, **[DeepSeek-V3 공식 README](https://github.com/deepseek-ai/DeepSeek-V3/blob/main/README.md)**. 공개 모델 크기·문맥 길이·가중치 형식·로컬 실행 요구 사항을 확인했다.
3. DeepSeek-AI, **[공식 inference model.py, commit `9b4e978`](https://github.com/deepseek-ai/DeepSeek-V3/blob/9b4e9788e4a3a731f7567338ed15d3ec549ce03b/inference/model.py)**. MLA cache, router bias/weight, MoE·shared expert, block 실행 경로를 확인했다.
4. DeepSeek-AI, **[config_671B.json, commit `9b4e978`](https://github.com/deepseek-ai/DeepSeek-V3/blob/9b4e9788e4a3a731f7567338ed15d3ec549ce03b/inference/configs/config_671B.json)** 및 **[weight file documentation](https://github.com/deepseek-ai/DeepSeek-V3/blob/9b4e9788e4a3a731f7567338ed15d3ec549ce03b/README_WEIGHTS.md)**. 61층·256 expert·Top-8·MLA rank·MTP module·FP8 weight 구조를 대조했다.

### 직접 실행한 검증

- 2026-07-24, macOS 26.4.1, Python 3.9.6에서 본문의 순수 Python 계산을 실행했다.
- 실행 범위: MLA/MHA cache scalar·메모리 산술, 활성 파라미터 비율, auxiliary-loss-free router bias의 선택·가중치 분리.
- 미실행 범위: 671B 가중치 추론, FP8 kernel 성능, DualPipe, H800 2,048 GPU 학습, 논문 benchmark 재현. 실행한 것처럼 쓰지 않았다.

### 다음 글

- 0단계 3편: **DeepSeek-R1** — V3의 SFT/RL과 R1의 강화학습 기반 reasoning model을 구분하고, long CoT(긴 사고 과정)를 제품에 언제·어떻게 써야 하는지 정리한다.
