연속 배칭
연속 배칭 (Continuous batching)
연속 배칭은 매 생성 단계마다 배치를 동적으로 재구성해 GPU 활용률을 최대화해요. 도착하는 요청을 바로 처리하고 완료된 요청은 곧바로 빼내는 방식이에요.
출처: 문서
본문
연속 배칭(continuous batching)은 매 생성 단계마다 배치를 동적으로 재스케줄링해 GPU 활용률을 최대화해요. 요청이 끝나면 전체 배치가 끝날 때까지 기다리는 대신 새 요청이 즉시 합류해요. GPU는 항상 꽉 차 있고 처리량도 높게 유지돼요.
[!TIP] 프로덕션 배포에는 transformers serve를 사용하세요. 이 기능은 ContinuousBatchingManager를 기반으로 하고 OpenAI 호환 HTTP 엔드포인트를 노출해요.
generate_batch
연속 배칭은 generate_batch()로 지원돼요. 토크나이즈된 프롬프트 리스트를 넘기면 모두 끝났을 때 모든 결과를 돌려받아요. generate_batch는 스케줄링을 내부적으로 처리하고 모든 요청이 완료될 때까지 블록(block)해요.
서빙·스트리밍 용도에는 ContinuousBatchingManager를 직접 사용해 요청을 관리해요.
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from transformers.generation import ContinuousBatchingConfig, GenerationConfig
model = AutoModelForCausalLM.from_pretrained(
"Qwen/Qwen3-4B",
attn_implementation="flash_attention_2",
device_map="auto",
dtype=torch.bfloat16,
)
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen3-4B")
prompts = [
"What's up?",
"Name a cat breed.",
"Write a detailed history of quantum mechanics.",
]
inputs = [tokenizer.encode(p) for p in prompts]
generation_config = GenerationConfig(
max_new_tokens=64,
eos_token_id=tokenizer.eos_token_id,
)
outputs = model.generate_batch(inputs=inputs, generation_config=generation_config)
for request_id, output in outputs.items():
text = tokenizer.decode(output.generated_tokens, skip_special_tokens=True)
print(f"[{request_id}] {text}")
ContinuousBatchingManager
ContinuousBatchingManager는 백그라운드 스레드를 실행하며 요청을 제출하고 결과를 독립적으로 가져올 수 있게 해요. 매 생성 단계마다 완료된 요청을 확인하고 배치에 합류할 새 요청을 스케줄링해요. 스트리밍, 실시간 서빙, 또는 도착하는 대로 요청을 제출할 때 유용해요.
continuous_batching_context_manager()를 사용하면 매니저를 안전하게 시작하고 중지할 수 있어요. 아래 예시에는 길이가 다른 입력이 있어요. 가장 짧은 프롬프트가 완료되는 즉시 배치를 떠나고, 긴 프롬프트는 계속 생성해요. 정적 배칭이라면 모두 같은 길이로 패딩해야 해요. 연속 배칭은 완료된 프롬프트를 비워서 다음 프롬프트 처리를 즉시 시작할 수 있게 해줘요.
with model.continuous_batching_context_manager(generation_config=generation_config) as manager:
manager.add_request(
input_ids=tokenizer.encode("Write a detailed history of quantum mechanics."),
request_id="long",
max_new_tokens=512,
)
manager.add_request(
input_ids=tokenizer.encode("What's up?"),
request_id="short_0",
max_new_tokens=32,
)
manager.add_request(
input_ids=tokenizer.encode("Name a cat breed."),
request_id="short_1",
max_new_tokens=32,
)
for result in manager:
text = tokenizer.decode(result.generated_tokens, skip_special_tokens=True)
print(f"[{result.request_id}] {text}")
init_continuous_batching()을 호출해 라이프사이클을 직접 관리할 수도 있어요.
manager = model.init_continuous_batching(generation_config=generation_config)
manager.start()
# submit and retrieve requests...
매니저 종료하기
매니저는 백그라운드 스레드를 실행하고 분산 리소스를 보유해요. 종료는 두 단계로 진행되므로 진행 중인 작업을 어떻게 처리할지 선택할 수 있어요.
stop()을 호출해 백그라운드 스레드를 멈춰요. 기본적으로 매니저는 새 제출을 받지 않고, 스레드가 종료되기 전에 대기·활성 요청이 끝날 때까지 기다려요.
manager.stop()
hard_stop=True를 넘기면 대기 중인 작업을 즉시 포기해요. 대기·활성 요청은 끝까지 실행되는 대신 RuntimeError로 실패 처리돼요.
manager.stop(hard_stop=True)
stop을 호출하면 add_request()와 add_requests()는 새 제출을 버리고 경고를 기록해요. 같은 매니저로 다른 생성 세션을 돌리려면 start를 다시 호출할 수 있어요.
destroy()를 호출해 분산 리소스를 해제해요. destroy는 매니저가 아직 실행 중이면 먼저 멈추고, 이후에는 매니저를 다시 시작할 수 없어요. 프로세스 수명 동안 연속 배칭이 다 끝났을 때 사용해요.
manager.destroy()
continuous_batching_context_manager()가 이 과정을 처리해요. 종료 시 stop을 호출하고, persistent_manager=True로 다음 세션을 위해 매니저를 모델에 캐시하지 않는 한 destroy도 호출해요.
요청 추가하기
add_request()는 단일 요청을 제출해요. request_id를 주거나 매니저가 자동으로 생성하게 둘 수 있어요.
manager.add_request(input_ids=input_ids, request_id="my_request")
add_requests()는 한 번에 배치를 제출해요. 블록 공유(block sharing)가 활성화되면 prefix cache 히트를 최대화하도록 입력을 자동으로 정렬해요.
manager.add_requests(inputs=inputs)
요청은 cancel_request()로 취소할 수 있어요.
manager.cancel_request(request_id="my_request")
요청별 샘플링 파라미터
per_request_processors를 활성화하면 같은 forward pass 안에서 요청별로 temperature, top_k, top_p를 독립적으로 적용할 수 있어요. 그래서 서로 다른 요청에 다른 샘플링 파라미터를 쓸 수 있어요(예: 창의적이고 고온도의 출력 대 정밀하고 저온도의 출력).
cb_config = ContinuousBatchingConfig(per_request_processors=True)
# each request gets its own sampling parameters
manager.add_request(input_ids=inputs_a, temperature=0.9, top_p=0.95)
manager.add_request(input_ids=inputs_b, temperature=0.1, top_k=10)
GenerationConfig의 각 파라미터는 런타임에 해당 logits processor를 만들려면 기본값이 아니어야 해요. 예를 들어 temperature를 None이나 1이 아닌 값으로 설정해야 요청별 temperature 제어를 지원해요. 이후에도 temperature가 1인 요청은 여전히 만들 수 있어요.
결과 가져오기
매니저를 순회(iterate)하면 결과가 도착하는 대로 받아요.
for result in manager:
print(tokenizer.decode(result.generated_tokens, skip_special_tokens=True))
get_result()는 출력 큐에서 다음 결과를 가져와요. request_id를 넘기면 특정 요청으로 필터링할 수 있어요. 큐의 다음 결과가 일치하지 않으면 다시 큐에 넣고 None을 반환해요.
# next available result
result = manager.get_result()
# filter for a specific request
result = manager.get_result(request_id="my_request")
스트리밍
요청에 streaming=True를 설정하고 request_id_iter()를 사용하면 토큰이 생성되는 대로 부분 출력을 순회할 수 있어요.
from transformers.generation.continuous_batching import RequestStatus
manager.add_request(input_ids=input_ids, request_id="streamed", streaming=True)
for chunk in manager.request_id_iter(request_id="streamed"):
token = tokenizer.decode(chunk.generated_tokens[-1:], skip_special_tokens=True)
print(token, end="", flush=True)
if chunk.status == RequestStatus.FINISHED:
break
ContinuousBatchingConfig
ContinuousBatchingConfig는 KV cache, 스케줄링, CUDA graphs, 메모리 사용 등을 제어해요. GenerationConfig와 별도로 generate_batch 또는 init_continuous_batching()에 continuous_batching_config 인자로 넘겨요. 두 config는 서로 다른 것을 설명해요. GenerationConfig는 모델 중심으로 샘플링·정지 파라미터를 담는 반면, ContinuousBatchingConfig는 하드웨어 중심으로 메모리·스케줄링 파라미터를 담아요.
[!WARNING] GenerationConfig에
continuous_batching_config를 설정하는 것은 deprecated이며,FutureWarning을 내보내고 v5.19에서 제거될 예정이에요.
기본적으로 max_batch_tokens는 8192이고, 사용 가능한 GPU 메모리로 제한되며 256 아래로 내려가지 않아요. num_blocks는 남은 메모리를 채워요. 아래 표를 참고해 적절한 기능을 고르세요.
| Feature | Memory | Throughput | Latency |
|---|---|---|---|
max_memory_percent / block_size |
✓ controls KV budget | ||
max_batch_tokens |
↑ larger input buffers | ✓ bigger prefill batches | ✓ TTFT when prefill-bound |
scheduler |
✓ scheduling policy | ✓ TTFT | |
| CUDA graphs | ↑ graph storage | ✓ less dispatch overhead | ✓ |
| Async batching | ↑ ~2× I/O buffers | ✓ overlaps CPU/GPU | |
| Compilation | ↑ warmup-time only | ✓ faster forward passes | ✓ |
| Decode fast path | ↑ block table per request | ✓ faster decode-only steps | ✓ |
| CPU offloading | ↑ pinned CPU memory | ✓ skips some re-prefills | |
| Prefix caching | ↓ shared KV blocks | ✓ skips redundant prefill | ✓ TTFT |
| Paged attention | ↓ no fragmentation | ✓ dynamic batch membership | |
| Sliding window | ↓ bounded KV per layer | ||
| Per-request processors | ✓ mixed sampling params per batch | ||
max_requests_per_batch |
↓ caps logits buffer | ✓ bounds batch size | |
safety_margin |
✓ protects decode latency |
from transformers.generation import ContinuousBatchingConfig
cb_config = ContinuousBatchingConfig(
max_memory_percent=0.8, # fraction of free GPU memory to use for the KV cache
block_size=256, # KV cache block size in tokens
scheduler_type="fifo", # "fifo" or "prefill_first"
)
outputs = model.generate_batch(
inputs=inputs,
generation_config=generation_config,
continuous_batching_config=cb_config,
)
KV cache 블록 크기
block_size는 각 KV cache 블록이 몇 개의 토큰을 담는지 설정해요. 최소 4여야 하고, 더 작은 값이면 generate_batch가 ValueError를 일으켜요. 캐시는 패딩과 sentinel 부기(bookkeeping)를 위해 추가 블록 두 개를 예약하므로, 아주 작은 블록은 이 고정 오버헤드에 메모리의 큰 비율을 쓰게 돼요. 큰 블록은 부기를 줄이지만 시퀀스가 마지막 블록을 채우지 못하면 공간을 낭비해요. 기본값 256은 flash_attn_with_kvcache decode 커널과 일치하며 대부분의 경우 잘 동작해요. 효율적인 캐시를 위해 block_size를 최소값보다 훨씬 위로 유지하세요.
cb_config = ContinuousBatchingConfig(block_size=128)
Prefill 배치 크기
max_batch_tokens는 단일 forward pass의 토큰 예산, 즉 모델이 한 번에 처리하는 최대 쿼리 토큰 수를 설정해요. 예산이 클수록 각 prefill에 더 많은 프롬프트 토큰이 실려 prefill 처리량이 올라가고, 프롬프트가 많은 워크로드에서 첫 토큰까지의 시간(time-to-first-token)이 줄어요. 스케줄러는 예산을 초과하는 프롬프트를 여러 단계로 나눠요. 과도하게 큰 프롬프트가 어떻게 처리되는지는 chunked prefill을 참고하세요.
기본적으로 max_batch_tokens는 8192예요. 배치당 입력 텐서가 KV cache를 밀어내지 않도록 이 값은 사용 가능한 GPU 메모리로 제한되며, 절대 256 아래로 내려가지 않아요. 여유 메모리가 적은 GPU에서는 이 제한이 8192 아래로 낮춰요.
max_batch_tokens와 num_blocks는 같은 메모리 예산을 공유하므로 서로 트레이드오프 관계예요. 토큰 예산이 크면 더 큰 입력 버퍼를 할당하고 동시·긴 요청을 위한 KV cache 블록은 줄어요. 메모리가 더 있을 때 max_batch_tokens를 올려 prefill 처리량을 높이고, 메모리를 비워 KV 블록을 늘리거나 out-of-memory를 피하려면 낮춰요.
cb_config = ContinuousBatchingConfig(max_batch_tokens=16384)
배치·스케줄링 제한
max_requests_per_batch는 단일 forward pass에서 몇 개의 요청이 실행되는지 제한해요. 모델은 배치 토큰 수와 어휘 크기로 크기가 정해진 logits 텐서를 만들고, 샘플링을 위해 fp32로 캐스팅돼 지형이 두 배가 돼요. 요청마다 다음 토큰 예측 하나만 필요하므로 더 작은 제한은 이 텐서를 작게 유지해, 어휘가 큰 prefill 중심 배치에서 out-of-memory를 피하게 해줘요. 기본적으로 제출된 프롬프트 수로 설정되고, 폴백은 1024이며, max_batch_tokens와 num_blocks에 맞게 상한이 정해져요.
cb_config = ContinuousBatchingConfig(max_requests_per_batch=256)
safety_margin은 활성 요청을 위해 KV cache의 일부를 예약해요. 여유 블록이 safety_margin * num_blocks 아래로 떨어지면 스케줄러는 새 prefill 수용을 멈추고 진행 중인 요청만 계속 디코딩해요. 이는 새 요청 시작보다 활성 작업 완료를 우선시해 decode 지연 시간을 보호하고 오프로딩을 지연시켜요. 값은 0과 1 사이여야 하고, 0이면 마진을 비활성화해요.
cb_config = ContinuousBatchingConfig(safety_margin=0.15)
기본값은 스케줄러에 따라 달라요. FIFO 스케줄러에서는 0.15이고 prefill_first에서는 0.0이에요. 마진이 스케줄링과 어떻게 상호작용하는지는 Admission을 참고하세요.
로그 확률 (Log probabilities)
ContinuousBatchingConfig는 return_logprobs=True일 때 생성된 각 토큰의 로그 확률을 반환해요. 이는 logprobs가 일부 학습 루프의 입력이 되는 RL에 유용해요.
cb_config = ContinuousBatchingConfig(return_logprobs=True)
# generate_batch()
for request_id, output in outputs.items():
for token_id, log_prob in zip(output.generated_tokens, output.logprobs):
token = tokenizer.decode([token_id])
print(f"{token} | logprob: {log_prob}")
CUDA graphs
CUDA graphs는 GPU 실행 그래프를 한 번 기록하고 shape이 일치하는 배치에 재생(replay)함으로써 CPU 디스패치 오버헤드를 제거해요. use_cuda_graph=True로 명시적으로 활성화해요.
cb_config = ContinuousBatchingConfig(use_cuda_graph=True)
활성화되면 매니저는 쿼리·KV 길이를 고정 간격으로 패딩해 shape이 반복되고 그래프가 재사용되게 해요. q_padding_interval_size와 kv_padding_interval_size 값이 작을수록 패딩에 낭비되는 계산이 줄지만, 그래프가 기록·저장해야 할 고유 shape이 늘어나 메모리가 더 들어요.
cb_config = ContinuousBatchingConfig(
use_cuda_graph=True,
q_padding_interval_size=64,
kv_padding_interval_size=16384,
)
비동기 배칭 (Async batching)
비동기 배칭은 다음 배치의 CPU 스케줄링을 현재 배치의 GPU 계산과 겹쳐 실행해요. CUDA graphs가 필요하고 입력 텐서에 쓰이는 VRAM이 대략 두 배가 돼요.
cb_config = ContinuousBatchingConfig(
use_cuda_graph=True,
use_async_batching=True,
)
컴파일 (Compilation)
default_compile_level은 모델의 forward pass에 torch.compile을 적용해요. 컴파일은 일회성 워밍업 비용을 생성 중 더 빠른 forward pass와 맞바꿔요. 레벨이 높을수록 더 공격적인 최적화를 실행해 처리량은 좋아지지만 워밍업은 길어져요. 테스트·벤치마크 반복에는 레벨을 낮게 유지하고, 워밍업 비용을 많은 요청에 걸쳐 상쇄할 수 있는 장기 서빙 워크로드에는 높여요.
레벨은 0에서 3까지예요. 레벨 0이 기본이며 컴파일을 완전히 건너뛰어요.
| Level | mode |
dynamic |
Trade-off |
|---|---|---|---|
| 0 | — | — | No compilation (default), fastest startup |
| 1 | default |
True |
Modest speedup, short warmup |
| 2 | max-autotune-no-cudagraphs |
True |
More speedup, longer warmup |
| 3 | max-autotune-no-cudagraphs |
False |
Best throughput, longest warmup |
cb_config = ContinuousBatchingConfig(default_compile_level=1)
레벨은 varlen과 decode 실행 경로에 기본 CompileConfig를 제공해요. 명시적 config가 없는 경로에만 적용되므로, varlen_compile_config와 decode_compile_config가 설정되면 우선해요. FlashAttention에서는 max_seqlen_k가 빈번한 재컴파일을 일으켜 varlen 경로가 컴파일을 건너뛰므로, 그 경우 레벨은 decode 경로에만 영향을 줘요.
Decode 고속 경로
배치에 decode 요청(시퀀스당 쿼리 토큰 하나)만 있으면 매니저는 가변 길이 커널 대신 flash_attn_with_kvcache 커널로 디스패치할 수 있어요. 이 커널은 수동 업데이트 대신 블록 테이블을 통해 페이지드 KV cache를 제자리에서 읽고 쓰므로 varlen 경로보다 빠르답니다. 커널 수준 세부사항은 Paged attention을 참고하세요.
고속 경로는 max_blocks_per_request로 크기가 정해지는데, 이 값이 요청별 블록 테이블을 정형화해요. 기본적으로 자동 추론돼요. 매니저에 max_prompt_length와 max_generated_length가 설정되어 있으면 블록 테이블이 최대 시퀀스 길이에 맞게 정해져요. 그렇지 않으면 폴백 기본값(요청당 32 블록)이 사용돼요.
max_blocks_per_request를 특정 값으로 설정하면 블록 테이블 크기를 명시적으로 정할 수 있어요. 요청별 최대 시퀀스 길이를 알고 블록 테이블 메모리 비용을 제한하고 싶을 때 유용해요.
cb_config = ContinuousBatchingConfig(max_blocks_per_request=64)
max_blocks_per_request=0으로 설정하면 고속 경로를 비활성화하고 모든 배치를 varlen 커널로 강제해요. 이는 기본값 이전의 동작으로 되돌아가며, attention 구현에서 고속 경로를 쓸 수 없을 때 유용해요(매니저는 기본 커널을 쓸 수 없을 때 자동으로 비활성화하기도 해요).
cb_config = ContinuousBatchingConfig(max_blocks_per_request=0)
고속 경로는 flash_attn_with_kvcache 커널에 의존하는데, 이 커널은 두 가지 기기·attention 구현 조합에서 사용 가능해요.
| Device | attn_implementation |
|---|---|
| CUDA | flash_attention_2 or flash_attention_3 |
| XPU | flash_attention_2 |
다른 조합이거나 커널을 가져올 수 없으면 매니저는 varlen 경로로 폴백해요. max_blocks_per_request를 명시적으로 설정했을 때만 경고를 기록해요.
슬라이딩 윈도우 attention은 블록 테이블을 지원하지 않으므로, 슬라이딩 윈도우 레이어가 있는 모델에서는 attention 구현과 무관하게 캐시가 max_blocks_per_request를 0으로 강제해요. 0이 아닌 값을 설정하면 덮어써지고 캐시가 Sliding window attention groups detected: disabling block table support. 로그를 남겨요.
CPU 오프로딩 (CPU offloading)
CPU 오프로딩은 GPU KV cache가 가득 찼을 때 퇴출된(evicted) KV cache 블록을 미리 할당된 pinned CPU 버퍼로 복사해요. 캐시 공간이 생기면 매니저는 블록을 GPU로 다시 복사하고, 프롬프트와 생성 토큰을 다시 계산하지 않고 요청을 재개해요.
cpu_offload_space를 CPU 스왑 공간(GiB)으로 설정해요. 기본값 0.0은 CPU 오프로딩을 비활성화해요.
cb_config = ContinuousBatchingConfig(cpu_offload_space=8.0)
기본적으로 cpu_offload_space_safety_threshold=0.8은 psutil이 설치되어 있을 때 요청된 공간을 사용 가능한 시스템 RAM의 80%로 제한해요. cpu_offload_space=None으로 설정하면 안전 임계값으로 스왑 풀 크기를 정해요.
텐서 병렬화 타임아웃
텐서 병렬화에서 매니저는 rank 간 요청 제출, 취소, 종료를 조정하기 위한 CPU 통신 그룹을 만들어요. cpu_group_timeout은 이 그룹의 collective이 프로세스가 크래시되기 전에 블록할 수 있는 시간을 제한해요. 한 rank가 멈추면 타임아웃이 다른 rank들이 영원히 기다리지 않게 해줘요.
드물게 collective을 발행하는 워크로드에는 더 긴 타임아웃을 설정하거나, None을 넘겨 비활성화해요.
cb_config = ContinuousBatchingConfig(cpu_group_timeout=600.0)
Prefix 캐싱
여러 요청이 시스템 프롬프트 같은 공통 prefix를 공유하면 매니저는 KV cache 블록을 다시 계산하는 대신 재사용해요. 이는 기본적으로 활성화되어 있고 모든 모델 레이어가 full attention을 사용해야 해요(슬라이딩 윈도우 모델에서는 자동 비활성화돼요).
cb_config = ContinuousBatchingConfig(
allow_block_sharing=True, # default
)
Paged attention
연속 배칭은 페이지드 attention 백엔드가 필요해요. 모델을 불러올 때 attn_implementation을 설정해요. 페이지드가 아닌 백엔드("flash_attention_2")로 모델을 불러오면 연속 배칭이 시작될 때 "paged|" 접두사가 자동으로 추가돼요.
| Backend | attn_implementation |
Requirements |
|---|---|---|
| FlashAttention | "paged|flash_attention_2" | flash-attn package |
| SDPA (PyTorch native) | "paged|sdpa" | None |
| Eager | "paged|eager" | None |
model = AutoModelForCausalLM.from_pretrained(
"Qwen/Qwen3-4B",
attn_implementation="paged|flash_attention_2",
device_map="auto",
dtype=torch.bfloat16,
)
또한 연속 배칭은 eager나 SDPA보다 flash attention에서 훨씬 잘 동작하는데, 대부분 Flash는 attention mask가 필요 없기 때문이에요. 따라서 flash attention을 쓸 수 있을 때 모델이 attn_implementation="eager" 또는 attn_implementation="sdpa"를 사용하면 attention 구현이 flash로 교체돼요. 이는 flash_attn 패키지나 kernels 패키지를 통해 flash에 접근할 수 있을 때 작동해요.
이를 피하려면 attn_implementation="paged|eager" 또는 attn_implementation="paged|sdpa"를 설정하면 되는데, 연속 배칭은 이를 사용자가 특정 구현을 요청한 것으로 해석해요. 이는 테스트 상황이나 flash attention을 켜기 어려운 환경에서 유용할 수 있어요(kernels 패키지 덕분에 이제는 드물지만요).
텐서 병렬화 (Tensor parallelism)
단일 GPU에 너무 커서 못 들어가는 모델은 텐서 병렬화로 가중치를 여러 기기에 나눠 담아요. DistributedConfig(tp_size=N)으로 기기 수를 설정해요. 연속 배칭은 모델에서 텐서 병렬화 크기를 읽어 샤드마다 페이지드 KV cache 크기를 정해요. 지원되는 아키텍처 목록과 샤딩 동작 방식은 Tensor parallelism을 참고하세요.
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, DistributedConfig
from transformers.generation import ContinuousBatchingConfig, GenerationConfig
distributed_config = DistributedConfig(tp_size=4)
model = AutoModelForCausalLM.from_pretrained(
"Qwen/Qwen3-32B",
attn_implementation="paged|flash_attention_2",
distributed_config=distributed_config,
)
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen3-32B")
inputs = [tokenizer.encode(p) for p in ["What's up?", "Name a cat breed."]]
generation_config = GenerationConfig(max_new_tokens=64, eos_token_id=tokenizer.eos_token_id)
outputs = model.generate_batch(inputs=inputs, generation_config=generation_config)
스크립트를 torchrun으로 실행하되, --nproc-per-node를 샤딩할 GPU 수로 설정해요.
torchrun --nproc-per-node 4 cb_tp.py
텐서 병렬화 크기는 모델의 num_key_value_heads를 나눌 수 있어야 해요 (모델 config를 확인하세요). 그렇지 않으면 페이지드 캐시가 시작 시 오류를 내므로 적절한 --nproc-per-node를 고르세요.
[!WARNING]
distributed_config와 함께device_map을 설정하지 마세요.device_map은 전체 모듈을 특정 GPU에 배치하는 반면, 텐서 병렬화는 같은 파라미터를 모든 GPU에 샤딩하므로 둘은 충돌해요.
슬라이딩 윈도우 attention (Sliding window attention)
슬라이딩 윈도우 attention이 있는 모델(Mistral, Gemma 2)은 연속 배칭과 함께 동작해요. 파인튜닝이나 커스텀 실험을 위해 슬라이딩 윈도우를 수동으로 구성하려면 로딩 전에 모델 config에 설정해요.
from transformers import AutoConfig, AutoModelForCausalLM
config = AutoConfig.from_pretrained("google/gemma-2-2b")
config.sliding_window = 4096
model = AutoModelForCausalLM.from_pretrained(
"google/gemma-2-2b",
config=config,
attn_implementation="paged|sdpa",
device_map="auto",
dtype=torch.bfloat16,
)
Prefix 캐싱과 decode 고속 경로는 슬라이딩 윈도우 attention이 활성화되면 자동으로 비활성화돼요.
다음 단계 (Next steps)
- Continuous batching 블로그 글은 KV 캐싱, chunked prefill, 동적 스케줄링을 성능 벤치마크 수치와 함께 다뤄요.
- 연속 배칭 시스템이 어떻게 동작하는지 더 깊이 보려면 연속 배칭 아키텍처 문서를 참고하세요.
더 알아보기 (Learn more)
- 연속 배칭 아키텍처 문서
- 채팅 기초 문서