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

8장. PEFT와 LoRA 학습

Qwen 에 LoRA 붙이기와 하이퍼파라미터

안경점에서 렌즈를 맞출 때는 "어느 눈에(어디에), 도수를 얼마로(얼마나 강하게)"를 정합니다. LoRA 도 붙이기 전에 "어느 층에(target_modules), 필름 두께를 얼마로(r), 얼마나 세게(lora_alpha)"를 설정표(LoraConfig)에 적습니다. 그다음 get_peft_model 이 모델 안의 해당 층마다 필름을 한 번에 붙입니다.

Qwen 한 층의 Attention 네 곳, FFN 세 곳, 24층을 나란히 놓은 비교 그림

그림의 왼쪽 칸이 Attention 의 선형층 네 곳, 가운데 칸이 FFN 의 선형층 세 곳입니다. 1일차에 본 Qwen 은 이 일곱 곳이 한 층에 있고, 층이 24개 쌓여 있습니다. 오른쪽 칸처럼 한 층에 붙는 LoRA 파라미터를 모두 더하면 366,592 개이고, 24층이면 8,798,208 개입니다. 실습 1의 print_trainable_parameters() 가 찍는 값이 바로 이것입니다.

LoRA 를 붙이는 일곱 곳

이름위치1일차 개념입력 → 출력r = 16 일 때 LoRA 수
q_projAttentionW_Q896 → 89616 × (896 + 896) = 28,672
k_projAttentionW_K896 → 12816 × (896 + 128) = 16,384
v_projAttentionW_V896 → 12816 × (896 + 128) = 16,384
o_projAttentionW_O896 → 89628,672
gate_projFFNGated FFN896 → 486416 × (896 + 4864) = 92,160
up_projFFNGated FFN896 → 486492,160
down_projFFNGated FFN4864 → 89692,160

k_proj 와 v_proj 의 출력이 128 인 것은 Qwen 이 Key·Value 머리를 2개(2 × 64)만 두고 여럿이 나눠 쓰기(GQA) 때문입니다. 5장에서 본 구조입니다.

LoRA 하이퍼파라미터 가이드

하이퍼파라미터(hyperparameter)는 학습으로 정해지지 않고 사람이 미리 정하는 설정값입니다.

설정뜻흔히 쓰는 값조정 방향
rLoRA 랭크(필름 두께)8, 16, 32과제가 복잡하거나 데이터가 많으면 키운다
lora_alpha배율(alpha ÷ r)r 의 1~2배r 을 바꿀 때 함께 바꾼다
lora_dropoutLoRA 길의 dropout0.0~0.1데이터가 적어 과적합되면 키운다
target_modules붙일 층모든 선형층("all-linear")파라미터를 줄이려면 Attention 만
학습률한 걸음의 보폭1e-4 ~ 3e-4Full Fine-tuning(1e-5 수준)보다 10배쯤 크게
epoch문제집 바퀴 수1~5검증 손실이 다시 오르기 시작하면 멈춘다

처음에는 "모든 선형층 + r = 16 + alpha = 32 + 학습률 2e-4"로 시작하고, 검증 결과를 보며 고치는 것이 무난합니다. 실습도 이 값을 씁니다.

짧은 예시

Python
# Qwen 한 층에서 LoRA 가 붙는 일곱 곳의 (입력 칸, 출력 칸)
shapes = {
    "q_proj": (896, 896), "k_proj": (896, 128), "v_proj": (896, 128), "o_proj": (896, 896),
    "gate_proj": (896, 4864), "up_proj": (896, 4864), "down_proj": (4864, 896),
}
r, layers = 16, 24

# 층마다 r × (입력 + 출력)을 더한다
per_layer = sum(r * (d_in + d_out) for d_in, d_out in shapes.values())
total = per_layer * layers
print(f"한 층: {per_layer:,}개, 24층: {total:,}개")   # 결과: 한 층: 366,592개, 24층: 8,798,208개

# 기반 모델 494,032,768개에 LoRA 를 더한 것이 all params
all_params = 494_032_768 + total
print(f"all params: {all_params:,}, trainable%: {total / all_params * 100:.4f}")
# 결과: all params: 502,830,976, trainable%: 1.7497

이 코드가 뜻하는 것

안경 렌즈 일곱 장의 넓이를 하나씩 더해 한 층의 필름 넓이를 구하고, 24층을 곱해 전체를 구한 코드입니다. 여기서 렌즈 크기표가 shapes, 필름 두께가 r 입니다.

줄하는 일쉽게 말하면
r * (d_in + d_out)한 곳의 A·B 칸 수렌즈 한 장의 넓이
sum(... for ... in shapes.values())일곱 곳을 더한다한 층의 렌즈 넓이
per_layer * layers24층을 곱한다모델 전체
total / all_params * 100학습 비율(%)전체 중 몇 % 를 고치나

손계산 8,798,208 과 1.7497% 가 실습 1의 print_trainable_parameters() 출력과 한 자리도 다르지 않게 같습니다. 모델을 불러오지 않고도 층 크기만 알면 LoRA 크기를 미리 알 수 있습니다.

주의할 점

  • r 을 두 배로 키우면 LoRA 파라미터도 정확히 두 배가 됩니다. 프로젝트에서 r = 4, 8, 16, 32 를 바꿔 가며 확인합니다.
  • lora_alpha 를 그대로 두고 r 만 키우면 배율(alpha ÷ r)이 작아집니다. 그래서 r 을 바꿀 때 alpha 도 함께 바꿉니다.
  • 같은 모델에 get_peft_model 을 또 부르면 필름 위에 필름을 겹치게 됩니다. 실습 1처럼 확인이 끝나면 unload() 로 떼어 둡니다.
← 이전 절8장 · LoRA 의 원리다음 절 →8장 · 학습 흐름과 SFTConfig