트랜스포머·PEFT 2일 과정8장 · PEFT와 LoRA 학습3 / 18← 이전목차다음 →MetaContents

8장. PEFT와 LoRA 학습

Full Fine-tuning 이 부담스러운 까닭

이삿짐을 옮긴다고 해 봅시다. 가구만 옮기면 트럭 한 대면 됩니다. 그런데 가구마다 "어느 방향으로 옮길지 적은 쪽지"와 "지난번에 어떻게 옮겼는지 적은 기록장 두 권"까지 함께 실어야 한다면 짐이 네 배가 됩니다. 모델을 학습할 때가 이렇습니다. 답을 만들기만 하는 추론(inference)에는 가중치만 있으면 되지만, 학습할 때는 가중치마다 그래디언트와 옵티마이저 기록이 따라붙습니다.

가중치 4바이트, 그래디언트 4바이트, 옵티마이저 상태 8바이트, 합계 16바이트의 네 칸을 나란히 놓은 비교 그림

그림의 왼쪽 첫 칸(가중치)만 추론에 필요합니다. 학습에서는 둘째 칸(그래디언트)과 셋째 칸(옵티마이저 상태)이 더해져, 넷째 칸처럼 파라미터 하나가 16바이트를 차지합니다. 이 그림은 FP32(숫자 하나에 4바이트)로 모두 저장한다고 가정한 어림값입니다. 실제로는 입력과 답을 처리하는 중간 계산(activation)과, 어휘 151,936 개 전체에 대한 점수표(logits)를 담을 메모리가 더 필요합니다.

파라미터 하나가 차지하는 메모리

항목추론할 때학습할 때하는 일이삿짐으로 치면
가중치4바이트4바이트모델이 학습한 현재 값가구
그래디언트없음4바이트가중치를 어느 방향으로 바꿀지옮길 방향 쪽지
옵티마이저 상태없음8바이트지난 그래디언트를 요약한 숫자 두 개기록장 두 권
합계약 4바이트약 16바이트파라미터와 직접 이어진 메모리만짐이 네 배

손으로 한 번 계산해 봅니다. 메모리는 "파라미터 수 × 바이트 수"입니다.

모델파라미터 수추론(× 4바이트)전체 학습(× 16바이트)
Qwen2.5-0.5B494,032,7681,976,131,072 바이트 ≈ 1,885 MB7,904,524,288 바이트 ≈ 7.36 GB
8B 모델8,000,000,00032,000,000,000 바이트 ≈ 29.8 GB128,000,000,000 바이트 ≈ 119 GB

여기서 MB 와 GB 는 1024 단위입니다(1 MB = 1024 × 1024 바이트). 0.5B 모델도 전체 학습에는 activation 을 빼고도 7GB 넘게 필요하므로, 10GB GPU 로는 빠듯합니다. 8B 모델은 119GB 가까이 필요해 비싼 GPU 여러 장이 있어야 합니다.

부담은 메모리만이 아닙니다. 저장 공간도 있습니다. Full Fine-tuning 은 업무마다 모델 전체를 따로 저장해야 합니다(8B 를 BF16 으로 저장하면 16GB). LoRA 는 업무마다 수십 MB 의 어댑터만 저장합니다.

짧은 예시

Python
# 파라미터 수 × 바이트 수로 메모리를 어림한다
BYTES = {"가중치": 4, "그래디언트": 4, "옵티마이저 상태": 8}  # FP32 기준, 파라미터 하나당

def memory_mb(n_params, training):
    # training=True 면 세 항목을 모두, False(추론)면 가중치만 더한다
    per_param = sum(BYTES.values()) if training else BYTES["가중치"]
    # 1024**2 로 나누어 MB 단위로 바꾼다
    return n_params * per_param / 1024**2

qwen = 494_032_768  # 6차시에서 센 Qwen2.5-0.5B 의 파라미터 수
print(f"추론 : {memory_mb(qwen, training=False):,.0f} MB")      # 결과: 추론 : 1,885 MB
print(f"학습 : {memory_mb(qwen, training=True):,.0f} MB")       # 결과: 학습 : 7,538 MB
print(f"8B 학습: {memory_mb(8e9, training=True) / 1024:,.1f} GB")  # 결과: 8B 학습: 119.2 GB

이 코드가 뜻하는 것

이삿짐 목록표를 만들어 트럭 크기를 미리 재 본 코드입니다. 가구만 싣는 경우(추론)와 쪽지와 기록장까지 싣는 경우(학습)를 견줍니다. 여기서 짐 목록이 BYTES, 가구 수가 n_params 입니다.

줄하는 일쉽게 말하면
BYTES = {...}항목별 바이트 수를 사전에 담는다짐 종류별 부피표
sum(BYTES.values())4 + 4 + 8 = 16학습 때 한 개당 짐
n_params * per_param / 1024**2바이트를 MB 로 바꾼다전체 짐 부피

1,885 MB 는 실습 4에서 어댑터를 저장할 때 원본 코드가 찍는 "기반 모델 가중치(FP32) 크기 약 1,885 MB"와 같은 값입니다. 학습하면 16바이트로 계산해 네 배인 7,538 MB 가 됩니다(1,885 는 반올림한 값이라 4 를 곱하면 7,540 이 됩니다).

주의할 점

  • 표의 16바이트는 파라미터와 직접 이어진 메모리만 센 어림값입니다. 실제 학습에는 activation, logits(배치 × 길이 × 151,936) 같은 메모리가 더해지고, 배치 크기와 문장 길이를 늘리면 더 커집니다.
  • BF16(숫자 하나에 2바이트)으로 계산하면 줄어들지만, 옵티마이저 상태는 보통 FP32 로 둡니다. 정밀도 설정에 따라 실제 값은 달라집니다.
  • GPU 메모리의 실제 사용량은 명령 창에서 nvidia-smi 로 확인합니다.
← 이전 절8장 · 용어 짚고 가기다음 절 →8장 · PEFT, 대부분은 얼리고 일부만 학습하기