본문 바로가기

LLM

LLM 프루닝 완전 정리 — 모델 크기 40% 줄이면서 성능 유지하는 법

반응형

LLM을 로컬에서 돌리거나 서버에 배포할 때 가장 큰 문제는 크기예요.

Llama 3.1 70B:  140GB VRAM 필요 → A100 2장 이상
Llama 3.1 8B:   16GB VRAM 필요  → RTX 4080 1장

근데 70B 성능이 필요하면?
→ 프루닝으로 70B를 40~50B로 줄이면
  성능은 거의 유지하면서 VRAM 40% 절약

프루닝은 모델에서 중요도가 낮은 가중치를 제거하는 기술이에요.


왜 프루닝이 가능한가

LLM의 파라미터 전부가 실제로 중요한 건 아니에요.

연구 결과:
- LLM 가중치의 40~60%는 거의 활성화 안 됨
- 특정 Attention Head는 제거해도 성능 변화 거의 없음
- 일부 FFN 레이어는 중복된 정보를 처리함

→ 이런 불필요한 부분을 제거해도
  모델이 대부분의 능력을 유지함

프루닝 3가지 방식

1. Unstructured Pruning (비구조적 프루닝)

개별 가중치를 0으로 만들어요.

Before:
[0.8, 0.2, 0.9, 0.1, 0.7, 0.05, 0.3]

After (50% sparsity):
[0.8, 0.0, 0.9, 0.0, 0.7, 0.0,  0.3]
장점: 성능 유지가 제일 좋음
단점: 실제 속도 향상이 적음
      → 0인 값도 메모리 차지
      → 특수 하드웨어(Sparse Tensor Core) 필요

2. Structured Pruning (구조적 프루닝)

Attention Head, FFN 레이어, 전체 레이어를 통째로 제거해요.

Before: 32개 레이어, 각 레이어 32개 Attention Head

After: 26개 레이어, 각 레이어 24개 Attention Head
→ 모델 구조 자체가 작아짐
→ 일반 GPU에서 실제 속도 향상
장점: 실제 속도 향상, 일반 하드웨어에서 동작
단점: 성능 손실이 비구조적보다 큼

3. Semi-Structured Pruning (N:M 프루닝)

M개 가중치 중 N개만 유지해요. NVIDIA GPU에 최적화돼 있어요.

2:4 sparsity:
4개 가중치 중 2개만 유지
[0.8, 0.0, 0.9, 0.0] ← 4개 중 2개

→ NVIDIA A100/H100에서 2배 속도 향상
→ 성능은 Unstructured랑 비슷하게 유지

주요 프루닝 알고리즘

Magnitude Pruning (기본)

가장 단순해요. 절대값이 작은 가중치를 제거해요.

import torch
from torch.nn.utils import prune

# 모델의 특정 레이어에서 20% 가중치 제거
prune.l1_unstructured(
    model.layers[0].self_attn.q_proj,
    name='weight',
    amount=0.2  # 20% 제거
)

# 영구 적용
prune.remove(model.layers[0].self_attn.q_proj, 'weight')

빠르지만 성능 손실이 커요.


SparseGPT

2023년 발표된 방법으로 현재 가장 널리 쓰여요.

원리:
단순히 작은 가중치 제거 X
→ "이 가중치 제거했을 때 출력이 얼마나 바뀌는가"를 계산
→ 영향 최소화하면서 가중치 제거
→ 제거 후 나머지 가중치를 보정

결과:
LLaMA 65B를 50% sparsity로 프루닝
→ 성능 거의 유지
→ 4.5시간 안에 완료 (A100 기준)
# SparseGPT 실행 (공식 구현)
git clone https://github.com/IST-DASLab/sparsegpt
cd sparsegpt

python llama.py \
  meta-llama/Llama-2-7b-hf \
  c4 \
  --sparsity 0.5 \       # 50% 가중치 제거
  --prunen 2 \           # 2:4 semi-structured
  --prunem 4

Wanda

SparseGPT보다 빠르고 성능은 비슷해요.

원리:
가중치 크기 × 입력 활성화 크기 = 중요도 점수
→ 점수 낮은 것 제거
→ 2차 미분 계산 없음 → 10배 빠름

결과:
LLaMA 65B 50% sparsity
→ SparseGPT와 성능 거의 동일
→ 7B 모델 10분 안에 프루닝 가능 (H100 기준)

ShortGPT / Sheared LLaMA (레이어 프루닝)

불필요한 레이어 전체를 제거해요.

ShortGPT 원리:
각 레이어가 없어도 출력이 얼마나 바뀌는가 측정
→ 변화 적은 레이어 = 불필요한 레이어
→ 통째로 제거

결과:
LLaMA 2 13B에서 레이어 25% 제거
→ 모델 크기 25% 감소
→ 추론 속도 25% 향상
→ 성능 손실 약 3~5%

NVIDIA Minitron (2024 최신)

NVIDIA가 개발한 방법. 프루닝 + 지식 증류 조합이에요.

결과:
Llama 3.1 8B → 4B로 프루닝
→ 원본 8B 재학습 대비 계산량 40배 절약
→ 같은 크기의 다른 모델들 대비 성능 우세

프루닝 실전 — llama.cpp로 프루닝된 모델 서빙

프루닝된 모델을 로컬에서 실행해요.

# llama.cpp 설치
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
make -j8

# 프루닝된 모델 변환 (GGUF 형식)
python convert_hf_to_gguf.py \
  ./pruned-llama-7b \
  --outtype f16

# 양자화까지 같이 적용 (크기 더 줄이기)
./llama-quantize \
  ./pruned-llama-7b-f16.gguf \
  ./pruned-llama-7b-q4_k_m.gguf \
  Q4_K_M

# 실행
./llama-cli \
  -m ./pruned-llama-7b-q4_k_m.gguf \
  -p "안녕하세요" \
  -n 256

프루닝 + 다른 기법 조합

프루닝만으로는 부족할 때 다른 기법과 같이 써요.

프루닝 단독:        크기 30~50% 감소, 성능 소폭 손실
프루닝 + 양자화:    크기 60~75% 감소, 성능 적당히 손실
프루닝 + 지식 증류: 크기 50% 감소, 성능 손실 최소화
프루닝 + LoRA 파인튜닝: 프루닝 후 성능 회복

실전 조합:

# 1. SparseGPT로 50% 프루닝
# 2. AWQ로 4비트 양자화
# 3. LoRA로 파인튜닝해서 성능 회복

# 결과:
# 원본 70B 모델 → 프루닝+양자화 후
# 메모리: 140GB → 20GB (7배 감소)
# 성능: 원본 대비 약 5~8% 손실
# 속도: 3~4배 향상

언제 뭘 쓰나

상황 1: NVIDIA GPU, 최대 속도 원함
→ 2:4 Semi-structured + SparseGPT
→ Sparse Tensor Core로 2배 속도

상황 2: 일반 GPU, 모델 크기 줄이고 싶음
→ Structured Pruning (레이어/헤드 제거)
→ ShortGPT or Sheared LLaMA

상황 3: 성능 손실 최소화
→ Unstructured + Wanda
→ 이후 LoRA 파인튜닝으로 회복

상황 4: 빠르게 작은 모델 만들기
→ Minitron (프루닝 + 증류 통합)

성능 비교 (LLaMA 2 7B 기준)

방법 Sparsity 메모리 속도 성능 손실

원본 0% 100% 1x 0%
Magnitude 50% 50% 1.1x ~8%
Wanda 50% 50% 1.2x ~3%
SparseGPT 50% 50% 1.6x* ~2%
ShortGPT 25% 75% 1.25x ~4%

*2:4 sparsity + Sparse Tensor Core 기준

 

반응형