DeepSeek V4.1
DeepSeek V4.1
이 문서는 DeepSeek의 희소 어텐션 Mixture-of-Experts 모델인 DeepSeek-V4.1 Flash를 SGLang으로 배포하고 호출하는 방법을 설명해요. dsv4 백엔드를 통해 서빙되며, DSpark 자체 3단계 추측 draft를 내장하고 있어요. 원문 페이지에는 하드웨어 플랫폼과 배포 전략을 골라 명령을 자동 생성해 주는 Playground가 포함되어 있어요.
출처: 문서
본문
1. 모델 소개
DeepSeek-V4.1 Flash는 희소 어텐션 Mixture-of-Experts 모델(model_type: deepseek_v4.1)로, SGLang의 dsv4 백엔드를 통해 서빙돼요: 40개 디코더 계층, top-6에서 384개의 routed 전문가 + 하나의 공유 전문가, 32-wide ue8m0 블록 스케일의 fp8 dense 가중치와 fp4 routed 전문가. 또한 DSpark, 자체 3단계 추측 draft를 갖추고 있으며, Low-Latency 레시피가 이를 켜요.
아키텍처. 모든 계층은 위치당 512-wide KV latent 하나를 유지하고 이를 64개 쿼리 헤드 모두의 key와 value로 사용하며 별도의 value projection이 없어요. 각 계층은 반대 방식으로 동작하는 두 개의 저장소를 읽어요: compressed latents — 소스 계층에서만 생성되어 앞으로 공유되며, 계층 2–19에서 2 위치를 1로 축소하고 계층 20부터는 1-to-1; 그리고 128-위치 슬라이딩 윈도우 — 각 계층이 자체 활성화에서 재계산하므로 공유할 수 없지만 컨텍스트에 따라 커지지도 않아요. 소비자는 매핑 테이블 없이 일반 loc // ratio 산술로 소스의 캐시를 주소 지정하므로, 압축 상태는 전체 prefix와 함께 공짜로 살고 죽어요. 잔차 스트림은 토큰별 이중 확률(double stochastic) 매트릭스로 혼합된 네 개의 병렬 복사본이에요; 각 서브계층의 혼합 계수는 다음 서브계층이 소비하며, 이를 통해 해당 투영이 메인 GEMM과 겹칠 수 있어요.
희소 검색 (Sparse retrieval). KV 소스 계층과 index 소스 계층은 서로 다른 목록이에요 — 전자 4개, 후자 8개. 추가 index 계층 4개는 key를 전혀 생성하지 않아요. 그들은 계층 20의 key를 자체 쿼리로 재스코어링하므로, 검색 결정은 key가 저장되는 횟수의 두 배로 내려져요. 각 검색 계층은 top-512 후보 집합을 선택해요.
Engram. 두 계층의 가산 n-gram 해시 메모리. 토큰 id는 해시 전에 정규화되므로 " The", "the", "THE"가 별도 행으로 갈라질 수 없어요. 두 fp8 테이블은 체크포인트에서 가장 큰 단일 가중치 블록이며, 기본적으로 TP 그룹에 걸쳐 행 샤딩으로 로드되어 engram 계층당 all-reduce 한 번이 듭니다. SGLANG_ENABLE_DSV41_ENGRAM_HOST_TABLE=1(opt-in)은 이를 단일 공유 호스트 복사본으로 옮겨요: 두 all-reduce가 모두 사라지고, 비워진 HBM은 KV 풀로 돌아가며, 출력은 bitwise로 동일해요 — 대가로 호스트 RAM, 더 긴 로드, gather를 저렴하게 유지하기 위한 huge-page 백킹이 필요해요.
SWA bounded replay. 윈도우 저장소는 계층별이고 재구축이 저렴하므로 전부 재계산할 필요가 없어요. --enable-decoder-swa-bounded-replay(opt-in)는 초기 계층을 전체 extend에서, 후기 계층을 각 요청의 마지막 128 토큰에서만 실행하며, 후기 계층 KV를 소스 계층 20에서 공유해요. Prefill이 크게 빨라져요. decode 경로에서만 검증되며, 설계상 프롬프트 logprobs를 거부하고, 전체 prefill과 수치적으로 동등하지 않으며, prefill CUDA graph 및 DP attention에서는 실행에서 제외돼요.
커널. 이 토큰 수의 decode에서 이 커널들은 대역폭 결합이 아닌 런치 결합이므로, 작업은 퓨전과 오버랩이에요: compressor projection, ratio-2 pooling, fp4 양자화와 indexer 패킹이 있는 RoPE, 캐시 주소로 직접 해석되는 top-k가 있는 paged fp4 indexer 스코어링, mHC 통계와 Sinkhorn — 그리고 압축·인덱싱·mHC는 전용 스트림에서 attention 및 FFN과 오버랩돼요. 이 모든 것이 자동으로 선택되며, 한 번 뒤에 있던 환경 스위치는 제거됐으므로 켤 것이 없어요. 퓨전보다 우선하는 한 가지 주의점: 현재 출력은 배치 구성에 걸쳐 bitwise로 안정적이지 않아요 — 두 기본 커널이 단일 토큰으로 shape-guard되어 있으며, --enable-deterministic-inference는 이 백엔드에서 거부돼요.
권장 생성: 추론 평가는 reasoning effort max로 temperature=1.0, top_p=0.95에서 실행됐어요(정보용 — 애플리케이션 코드에 하드코딩하지 마세요).
리소스: HuggingFace.
2. 설정 팁
백엔드를 재정의하지 마세요
--attention-backend, --moe-runner-backend, --fp8-gemm-backend은 모델, 하드웨어, forward 모드, 형태에서 자동으로 선택돼요. GB300에서는 dsv4 / flashinfer_mxfp4 / flashinfer_cutedsl로 해석돼요. 전달하지 말고 시작 로그에서 확인하세요.
재정의하는 것은 실망스러운 측정의 가장 흔한 원인이에요: 32-wide ue8m0 블록을 Triton _w8a8_block_fp8_matmul 폴백에 남기는데, 이는 decode 단계를 지배하며 모델 bs=1 처리량의 대부분을 소모해요. decode 속도가 기대의 작은 일부처럼 보이면 먼저 해석된 백엔드를 확인하세요.
3. 고급 사용법
3.1 추론 (Reasoning)
reasoning parser를 활성화하세요 — --reasoning-parser auto는 deepseek-v41로 해석돼요(위 Playground의 Parsers 카드에서 Reasoning Parser 토글) 추론을 최종 답변과 분리. parser는 thinking 블록을 reasoning_content에, 답변을 content에 넣어요; 없으면 둘 다 content에 이어붙여 도착해요.
parser는 모델이 실제로 생성한 thinking 블록만 분리할 수 있으며, thinking은 기본적으로 꺼져 있어요(SGLANG_DEFAULT_THINKING=false). reasoning_effort를 보내면 켜져요: none 외의 값은 chat_template_kwargs.thinking도 켜줘요. 어느 것도 실지 않는 요청은 parser가 어떻게 구성돼 있든 빈 reasoning_content로 돌아와요.
추론 예시 (Python):
from openai import OpenAI
client = OpenAI(base_url="http://localhost:30000/v1", api_key="EMPTY")
resp = client.chat.completions.create(
model="deepseek-ai/DeepSeek-V4.1-Flash",
messages=[{"role": "user", "content": "What is 15% of 240?"}],
reasoning_effort="high",
)
msg = resp.choices[0].message
print("Reasoning:", getattr(msg, "reasoning_content", None))
print("Answer:", msg.content)
요청에서 reasoning_effort는 low, high, xhigh, max 등급을 받거나, [0.0, 0.99]의 float를 받아 모델의 1–100 예산에 매핑해요. 정수 예산은 chat_template_kwargs.reasoning_effort로만 도달 가능하며, 그 경로는 스스로 thinking을 켜지 않아요 — chat_template_kwargs.thinking과 함께 쌍으로 쓰세요. none은 thinking을 꺼요. V4.1에 대응이 없는 나머지 OpenAI 등급(minimal, medium)은 경고를 기록하고 오류 대신 서버 측 기본값으로 폴백해요. 그 기본값은 high이며, SGLANG_DSV41_REASONING_EFFORT가 override해요.
3.2 도구 호출 (Tool Calling)
tool-call parser를 활성화하세요 — --tool-call-parser auto는 deepseekv41로 해석돼요(위 Playground의 Parsers 카드에서 Tool Call Parser 토글) 구조화된 도구 호출을 message.tool_calls로 노출. DeepSeek-V4.1은 spaced DSML 도구 태그를 사용하는데, 기본 DeepSeek-V4 detector는 이를 파싱하지 않아요 — deepseekv41 detector가 필요해요.
도구 호출 예시 (Python):
from openai import OpenAI
client = OpenAI(base_url="http://localhost:30000/v1", api_key="EMPTY")
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather in a city",
"parameters": {
"type": "object",
"properties": {"location": {"type": "string"}},
"required": ["location"],
},
},
}]
resp = client.chat.completions.create(
model="deepseek-ai/DeepSeek-V4.1-Flash",
messages=[{"role": "user", "content": "What's the weather in Beijing?"}],
tools=tools,
reasoning_effort="high",
)
choice = resp.choices[0]
print("finish_reason:", choice.finish_reason)
print("tool_calls:", choice.message.tool_calls)
print("reasoning:", getattr(choice.message, "reasoning_content", None))
성공적인 호출은 요청한 위치를 담은 인자를 가진 단일 get_weather 항목과 함께 finish_reason: "tool_calls"를 반환해요. 대신 content에 원시 DSML 마크업이 보이면 parser 플래그가 빠진 것이에요.
3.3 추측 디코딩 (DSpark)
DSpark는 DeepSeek-V4.1 Flash의 자체 번들 draft예요 — 이 모델에는 EAGLE나 MTP 경로가 없으며, --speculative-num-steps 노브도 없어요(draft 토큰 수는 체크포인트에서 해석됨). --speculative-algorithm DSPARK --speculative-dspark-block-size 5로 켜는데, 이는 Low-Latency 셀이 하는 일이에요.
기대할 것:
- DSpark 단계는 일반 decode 단계보다 의미 있게 비싸므로, bs=1에서 이득은 대략 accept 길이를 고정 단계 비용으로 나눈 값이에요. 대화형 워크로드에서는 실질적인 속도 향상이지만, draft 토큰 수에 비례하지 않아요.
- 수용률은 워크로드에 크게 의존해요 — 수학과 코드에서 가장 높고, 개방형 채팅에서 더 낮으며, agentic 트레이스에서 더욱 낮아요. 실제 실행하는 워크로드에서 기대치를 정하고, 가정하지 말고 측정하세요.
- 단계 비용은 accept 길이나 draft 블록 크기에 따라 움직이지 않으므로
--speculative-dspark-block-size튜닝은 처리량을 사지 못해요. 이것이 High-Throughput 레시피가 추측을 끄는 이유이기도 해요: 큰 배치에서 고정 단계 비용이 스스로를 갚지 못해요.
3.4 PD Disaggregation
Prefill/decode 분리는 32개의 greedy 프롬프트에 걸쳐 단일 서버와 토큰 동일하게 검증됐고, GSM8K가 router를 통해 일치해요. Playground의 PD Disaggregation 카드를 사용해 prefill 역할, decode 역할, router 명령을 생성하세요.
배포 노트 하나: Mooncake는 컨테이너 안에 RDMA 패브릭이 보여야 하므로 --device /dev/infiniband:/dev/infiniband --cap-add IPC_LOCK --ulimit memlock=-1로 실행하세요. 없으면 Mooncake는 자체 allocator의 버퍼만 서빙하고 Requested address ... not found로 실패하는 NVLink 전송을 선택해요. 이 문제가 생기면 MOONCAKE_PROTOCOL=tcp와 MC_FORCE_TCP=1로 TCP를 강제하세요 — Playground의 Mooncake 옵션이 둘 다 설정해요.
PD와 추측 디코딩은 결합할 수 없어요.
3.5 HiCache (계층형 KV 캐싱)
HiCache는 KV cache 계층의 계층 구조로 RadixAttention을 확장해, 장문 맥락 및 다중 턴 시나리오에서 효과적 컨텍스트 용량을 크게 확장해요.
HiCache를 활성화하려면 위 Playground에서 HiCache 카드를 열고 Enable을 켜세요: Playground는 레시피의 플래그 위에 --enable-hierarchical-cache를 내보내고, 콜드 KV 페이지는 GPU 풀에서 버려지는 대신 CPU 고정 메모리로 넘쳐요(L2, GPU + CPU). 호스트 풀은 고정 --hicache-size가 아닌 비율(--hicache-ratio 2)로 크기가 정해져요.
Write policy 노브는 GPU → CPU 쓰기를 제어하며 기본적으로 write_through(업스트림 기본값)이에요: 쓰여질 때마다 모든 페이지가 CPU 계층으로 미러링돼요. write_through_selective는 핫 데이터만 백업하고 write_back은 복사를 퇴출로 지연시켜, 호스트 측 I/O와 캐시 신선도를 맞바꿔요.
이 카드는 MI350X에서는 제공되지 않아요: ROCm 레시피가 --disable-radix-cache를 실행하고, 서버는 --enable-hierarchical-cache와 함께 이를 거부해요.
여기서는 L2 계층만 노출돼요. 스토리지(L3) 계층과 표준 플래그 집합은 HiCache best-practices 레시피와 HiCache 문서를 참고하세요.