본문 바로가기
C.W.K.
Stream
Lesson 05 of 06 · published

가지치기, 지식 증류, 성능 측정

~12 min · pruning, distillation, benchmark

Level 0텐서 탐구자
0 XP0/62 lessons0/13 achievements
0/120 XP to next level120 XP to go0% complete

운영 모델을 더 작고 빠르게 만드는 세 가지 기법

가지치기

가지치기는 중요하지 않다고 판단한 가중치를 크기나 구조적 규칙에 따라 0으로 만들어. 방식은 두 가지야:

  • 비구조적: 개별 가중치를 0으로 만들어. 이론적으로 연산량은 줄지만 대부분의 하드웨어는 희소 행렬곱을 충분히 가속하지 못해서 실제 이득이 작아.
  • 구조적: 채널, 헤드, 블록 전체를 제거해. 매개변수 수, 연산량, 활성화 메모리가 모두 줄어 실제 속도 향상으로 이어지기 쉬워.

가지치기는 양자화와 잘 결합하고, 더 작은 학생 모델을 학습하는 지식 증류와 함께 쓰면 더 효과적일 수 있어.

지식 증류

작은 학생 모델이 큰 교사 모델의 부드러운 예측을 따라 하도록 학습해. 정답 레이블뿐 아니라 교사 모델의 로짓에서 배우므로 '대부분 고양이지만 조금은 여우에 가깝다' 같은 더 풍부한 정보를 얻을 수 있어. 표준 구성은 다음과 같아:

  1. 교사 모델에 입력을 넣어 부드러운 로짓을 받아.
  2. 학생 모델에도 같은 입력을 넣어 학생의 로짓을 받아.
  3. 손실 = α · KL(student / T || teacher / T) · T² + (1 − α) · CE(student, hard_label)

여기서 T는 보통 2~8로 설정하는 온도야. 두 분포를 부드럽게 만들고, α는 부드러운 정답과 확정 정답의 비중을 조절해.

성능 측정: 솔직한 성적표

모든 최적화 주장은 측정으로 확인해야 해. 실제 배포에서 사용할 배치 크기, 하드웨어, 입력 분포로 지연 시간을 재. 한두 번 빨라 보였던 결과는 운영 환경에서 쉽게 사라질 수 있어.

Code

비구조적 가지치기: 크기 기반·python
import torch
import torch.nn as nn
import torch.nn.utils.prune as prune

linear = nn.Linear(100, 50)

# Zero out 30% of weights by magnitude (smallest go to zero)
prune.l1_unstructured(linear, name='weight', amount=0.3)

# Check sparsity
sparsity = (linear.weight == 0).float().mean().item()
print(f"sparsity: {sparsity:.2%}")           # ~30%

# Make pruning permanent (removes the mask, keeps the zeros)
prune.remove(linear, 'weight')
지식 증류: 고전 구성법·python
import torch
import torch.nn as nn
import torch.nn.functional as F

teacher = BigModel().eval()                   # frozen
student = SmallModel().train()
optimizer = torch.optim.AdamW(student.parameters(), lr=1e-3)
T = 4.0                                        # temperature
alpha = 0.7                                    # soft-vs-hard weight

for x, y in train_loader:
    optimizer.zero_grad()

    with torch.inference_mode():
        teacher_logits = teacher(x)

    student_logits = student(x)

    soft = F.kl_div(
        F.log_softmax(student_logits / T, dim=-1),
        F.softmax(teacher_logits / T, dim=-1),
        reduction='batchmean',
    ) * (T ** 2)

    hard = F.cross_entropy(student_logits, y)

    loss = alpha * soft + (1 - alpha) * hard
    loss.backward()
    optimizer.step()
성능 측정 도구: 모두 필요한 표·python
import time
import torch

@torch.inference_mode()
def benchmark(model, input_tensor, num_runs=100, warmup=10, label='model'):
    is_cuda = next(model.parameters()).is_cuda
    if is_cuda: torch.cuda.synchronize()

    for _ in range(warmup):
        model(input_tensor)
    if is_cuda: torch.cuda.synchronize()

    t0 = time.perf_counter()
    for _ in range(num_runs):
        model(input_tensor)
    if is_cuda: torch.cuda.synchronize()
    elapsed = time.perf_counter() - t0

    avg_ms = elapsed / num_runs * 1000
    throughput = input_tensor.size(0) * num_runs / elapsed
    print(f"{label:20s} avg {avg_ms:6.2f} ms/call, {throughput:,.0f} samples/sec")
    return avg_ms

# Use it like:
benchmark(fp32_model, batch, label='fp32 baseline')
benchmark(quant_model, batch, label='int8 quant')
benchmark(compiled_quant_model, batch, label='int8 + compile')

External links

Exercise

세 번째 코드 블록의 성능 측정 도구를 구현하고 작은 분류기의 세 설정에 사용해 봐. (a) fp32 즉시 실행, (b) bf16과 컴파일, (c) int8 동적 양자화를 비교해 표로 출력해. 지연 시간이 얼마나 줄었는지, 거의 줄지 않았는지를 보면 모델을 더 최적화할 가치가 있는지 판단할 수 있어.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.