[llm-compressor] Basic Pipeline: 한 번의 forward로 끝내는 캘리브레이션
들어가며
BasicPipeline은 네 개의 파이프라인 중 가장 단순하다. 캘리브레이션 데이터로더를 순회하며 모델 전체에 대해 하나의 forward를 수행하고, Modifier의 훅이 필요한 통계를 누적한다. 그게 전부다. src/llmcompressor/pipelines/basic/pipeline.py의 코드를 해부한다.
핵심 구조/코드 분석
BasicPipeline.__call__
@CalibrationPipeline.register("basic")
class BasicPipeline(CalibrationPipeline):
@staticmethod
def __call__(
model: torch.nn.Module, # 캘리브레이션 대상 모델
dataloader: DataLoader, # 캘리브레이션 데이터
dataset_args: Union["DatasetArguments", None],
):
session = active_session()
dispatch_model(model) # 모델을 적절한 디바이스에 자동 배치
model_device = get_execution_device(model)
use_loss_mask = (
getattr(dataset_args, "use_loss_mask", False) if dataset_args else False
)
# AWQ 마스킹 지원을 위한 loss_masks 초기화
if use_loss_mask:
session.state.loss_masks = []
LifecycleCallbacks.calibration_epoch_start() # Modifier 들에게 epoch 시작 알림
with contextlib.ExitStack() as stack:
stack.enter_context(calibration_forward_context(model)) # 캘리브레이션 전용 컨텍스트
for batch_idx, batch in enumerate(
tqdm.tqdm(dataloader, desc="Calibrating")
):
# AWQ 마스킹용 loss mask 수집
if use_loss_mask:
session.state.loss_masks.append(batch.get("loss_mask"))
session.state.current_batch_idx = batch_idx
batch = tensors_to_device(batch, model_device) # 배치를 GPU 로 이동
model(**batch) # forward 실행
LifecycleCallbacks.calibration_epoch_end() # Modifier 들에게 epoch 종료 알림
| 핵심 호출 | 의미 |
|---|---|
dispatch_model(model) |
compressed-tensors의 자동 디바이스 배치. 단일 GPU면 전체를 GPU에, 여러 GPU면 파트별로 나눠 배치 |
get_execution_device(model) |
입력 텐서를 올려야 할 디바이스 조회 |
calibration_forward_context(model) |
Modifier 훅이 forward에 끼어들 수 있도록 하는 컨텍스트 매니저 |
LifecycleCallbacks.calibration_epoch_start/end() |
내부적으로 CompressionLifecycle.event(EventType.CALIBRATION_EPOCH_*) 호출 |
session.state.current_batch_idx = batch_idx |
어떤 배치가 현재 처리 중인지 기록 (Modifier가 배치별 가중치 적용 시 참조) |
model(**batch) |
실제 forward. Modifier 훅이 이 호출 안에서 활성화 통계를 수집 |
run_calibration: 편의 함수
def run_calibration(model: torch.nn.Module, dataloader: DataLoader):
pipeline = BasicPipeline()
pipeline(model, dataloader, None)
dataset_args가 필요 없을 때 쓰는 간편 래퍼다. 테스트나 간단한 사용자 코드에서 from llmcompressor.pipelines.basic import run_calibration 한 줄로 시작할 수 있다.
왜 이 설계인가
1. 단순함의 미덕. 이 파이프라인은 forward 루프 그 이상도 이하도 아니다. Modifier 가 forward 훅에 끼어들어 필요한 통계를 수집하므로, 파이프라인 자체는 배치 순회만 담당하면 된다. 역할이 명확하다.
2. dispatch_model로 멀티 GPU 지원. compressed-tensors의 dispatch_model을 호출하면 모델이 자동으로 여러 GPU에 분산되거나 CPU offload된다. basic 파이프라인은 "생성(generation)처럼 일반 추론과 동일한 디바이스 배치"를 사용한다. 즉 추론에 쓰는 디바이스 구성이 캘리브레이션에도 그대로 적용된다.
3. calibration_forward_context로 훅 활성화. PyTorch forward 호출 밖에서는 Modifier 훅이 비활성화되어 실제 추론에 방해가 되지 않는다. 이 컨텍스트 매니저가 훅을 "이 블록 안에서만" 활성화한다.
4. loss_masks 리스트. AWQ가 캘리브레이션 시 패딩 토큰을 제외하려면 loss mask가 필요하다. 일반 배치 데이터에 loss_mask 필드가 있으면 수집해 session.state.loss_masks에 쌓는다. 없으면 use_loss_mask=False로 스킵.
5. current_batch_idx 전파. GPTQ·AWQ 같은 Modifier는 "어떤 배치의 기여인지"를 알고 있어야 하는 경우가 있다. 세션 상태에 현재 배치 인덱스를 넣으면 Modifier가 session.state.current_batch_idx로 접근할 수 있어, 인자 전달 없이도 문맥을 공유한다.
언제 Basic Pipeline을 쓰는가
Basic 파이프라인은 다음 조건에서만 권장된다.
- 모델이 단일 GPU에 다 올라가는 중소형 모델(< 13B)
- Modifier가 전체 forward를 한 번 본 뒤에 통계를 최종화할 수 있는 것 (예: SmoothQuant)
- GPTQ·SparseGPT·AWQ 처럼 레이어 단위 순차 처리가 필요한 Modifier는 쓰지 말 것
Pipeline Registry의 _infer_pipeline은 basic을 자동 추천하지 않는다. 사용자가 oneshot(..., pipeline="basic")으로 명시해야 한다. 자동 추론은 항상 sequential을 기본값으로 선택한다.
마무리
Basic Pipeline은 "가장 얇은 파이프라인"이다. 50줄 남짓의 코드가 전부다. 하지만 SmoothQuant 같은 단순 PTQ에서는 이 정도면 충분하다. 다음 글은 더 복잡하고 메모리 효율적인 Sequential Pipeline을 본다.
참고 자료
관련 포스트
- [llm-compressor] Sequential Pipeline: 레이어 단위 서브그래프 캘리브레이션
- [llm-compressor] Dataset Calibration: c4/wikitext/ultrachat 로더
- [llm-compressor] Quantization Calibration: update_weight_zp_scale와 observer 등록
- [llm-compressor] Intermediates Cache: 서브그래프 활성화 오프로드 캐시
- [llm-compressor] Data-Free & Independent Pipeline: 데이터 없는 파이프라인과 Modifier별 개별 실행
llm-compressor 의 다른글
- 이전글 [llm-compressor] Pipeline Registry: Modifier 목록을 보고 파이프라인 자동 선택
- 현재글 : [llm-compressor] Basic Pipeline: 한 번의 forward로 끝내는 캘리브레이션
- 다음글 [llm-compressor] Sequential Pipeline: 레이어 단위 서브그래프 캘리브레이션
댓글