처음부터 시작하는 RLHF: SFT, 보상 모델(Reward Model), 그리고 KL 리쉬(KL leash)를 적용한 PPO
요약
RLHF(인간 피드백 기반 강화학습)의 핵심 단계인 SFT, 보상 모델 학습, PPO 최적화 과정을 설명합니다. 모델의 성능 향상과 보상 해킹 방지를 위한 KL 발산 페널티의 역할을 다룹니다.
핵심 포인트
- SFT를 통해 베이스 모델이 인간의 지시사항과 형식을 모방하도록 학습합니다.
- 보상 모델은 인간의 선호도 순위를 Bradley-Terry 손실 함수로 학습하여 구축합니다.
- PPO 단계에서 KL 발산 페널티를 적용해 모델이 참조 모델에서 과도하게 이탈하는 것을 방지합니다.
- 보상 해킹(Reward Hacking)을 막기 위해 KL 리쉬(leash) 개념이 중요하게 작용합니다.
1단계 — SFT: 시연(demonstrations) 모방
먼저, 지도 미세 조정(Supervised Fine-Tuning, SFT)입니다. 인간이 수천 개의 이상적인 프롬프트 → 답변 시연(demonstrations)을 작성하면, 일반적인 다음 토큰 교차 엔트로피(next-token cross-entropy)를 사용하여 베이스 모델(base model)이 이를 복제하도록 미세 조정합니다.
demos = [(prompt, human_written_answer), ...] # 이상적인 행동
sft = finetune(base, demos, loss="cross_entropy")
ref = freeze(copy(sft)) # 이후를 위한 참조 정책(reference policy)
이제 모델은 지시사항을 따르고 올바른 형식을 사용합니다. 결정적으로, SFT 모델의 복사본을 **참조 정책(reference policy)**으로 동결(freeze)해 두는데, 이는 3단계에서 모델의 이탈(drift)을 측정하는 기준이 됩니다.
2단계 — 순위 매기기(ranking)를 통해 학습된 보상 모델(reward model)
SFT는 인간이 직접 작성한 답변만을 모방할 수 있으며, "이것이 조금 더 안전하다"라거나 "이 어조가 더 좋다"와 같은 비교 판단을 표현할 수 없습니다. 따라서 답변 작성을 멈추고 답변의 _순위(ranking)_를 매기기 시작합니다. 프롬프트당 두 개의 완성된 답변을 샘플링하고, 인간이 더 나은 것을 선택하게 합니다. 판단(Judging)은 작성(authoring)보다 훨씬 비용이 저렴하며, 글로 완벽히 다 적을 수 없는 미묘한 차이(nuance)를 포착할 수 있습니다.
그 다음, Bradley–Terry 손실(loss)을 사용하여 이러한 선호도를 보상 모델(reward model)로 증류(distil)합니다. 즉, 선택된 답변의 점수가 거절된 답변의 점수보다 높게 밀어 올리는 방식입니다:
def rm_loss(x, y_chosen, y_rejected):
return -log_sigmoid(rm(x, y_chosen) - rm(x, y_rejected))
train(rm, prefs, loss=rm_loss) # 이제 rm(x,y)는 인간의 선호도를 근사함
이 데모는 이 단계를 직접 체험할 수 있게 합니다. _당신_이 바로 라벨러(labeller)입니다. 당신이 순위를 매기는 각 쌍은 품질 특성(도움이 되는지, 정직한지, 해롭지 않은지)에 대한 작은 가중치 벡터 위에서 실제 Bradley–Terry 경사 하강법(gradient step)을 한 번씩 실행하며, 모델이 당신의 클릭을 통해 당신이 무엇을 가치 있게 여기는지 추론함에 따라 가중치와 손실(loss)이 업데이트되는 것을 지켜보게 됩니다.
3단계 — PPO, 그리고 리쉬(leash)
이제 보상 모델(Reward Model)이 높은 점수를 부여하는 답변을 생성하도록 정책(policy)을 최적화합니다. 하지만 보상 모델은 제한된 데이터로 학습된 하나의 _대리자(proxy)_일 뿐입니다. 만약 너무 강하게 밀어붙이면, 정책이 모델이 이해하는 영역을 벗어나 점수만 높은 헛소리를 생성하게 됩니다. 이것이 바로 **보상 해킹 (reward hacking)**입니다. 해결책은 고정된 참조 모델(reference model)로 돌아가도록 KL 발산(KL-divergence) 페널티를 부여하는 것입니다:
def rlhf_reward(x, y):
r = reward_model(x, y) # 이 값을 높게 유지하고자 함
kl = log pi_theta(y|x) - log pi_ref(y|x) # SFT 참조 모델로부터의 이탈
...
이탈하는 모든 토큰은 보상 비용을 발생시킵니다. beta는 리쉬(leash, 목줄)의 길이를 설정합니다. 큰 beta는 짧은 리쉬(안전하지만 이득이 적음)를 의미하며, 작은 beta는 긴 리쉬(더 높은 대리 보상, 더 높은 리스크)를 의미합니다. 그런 다음 PPO는 클리핑된 업데이트(clipped updates)를 통해 해당 신호를 최적화하여, 정책이 한 번의 단계에서 너무 멀리 급변하지 않도록 합니다.
RLHF에서 가장 중요한 단 하나의 그림
데모의 3단계에서는 beta를 설정하고 PPO 궤적(trajectory)을 실행하여 세 가지 수치를 도식화할 수 있습니다: RM이 부여하는 대리 보상(proxy reward), 참조 모델로부터의 KL 이탈(KL drift), 그리고 실제로 우리가 원하는 진정한(true) 품질입니다. 학습을 강하게 밀어붙일수록 대리 보상은 계속 상승하지만, 진정한 품질은 상승하다가 정점을 찍고 다시 _하락_합니다.
KL이 상승할 때:
대리 보상: 상승, 상승, 상승 (항상 상승함)
진정한 품질: 상승, 정점, 하락 (과최적화)
이것이 RLHF에 적용되는 굿하트의 법칙(Goodhart's law)입니다: 대리 지표를 극대화하면 결국 목표를 해치게 됩니다. 데모에서 beta를 ≈ 0으로 설정하면, 대리 보상이 환상적으로 보이는 와중에도 진정한 품질이 붕괴하는 것을 볼 수 있습니다. 이는 정책이 분포 외(off-distribution) 영역에서 보상 모델을 속이고(gaming) 있기 때문입니다. beta를 적절한 지점(sweet spot)에 유지하면 보상이 상승하면서도 품질이 정점 근처에 머물게 됩니다. KL 리쉬와 조기 종료(early stopping)는 여러분이 그 정점의 좋은 영역에 머물 수 있게 해주는 장치입니다.
전체 레시피
sft = finetune(base, human_demos) # 1) 모방 (imitate)
rm = train(prefs, bradley_terry_loss) # 2) 선호도 점수화 (score preferences)
policy = ppo(sft, reward=rm, ref=sft, beta) # 3) KL 리쉬를 적용한 최적화 (optimize on a KL leash)
세 가지 단계: SFT는 답변하는 법을 가르치고, 보상 모델 (Reward Model)은 인간이 선호하는 것을 포착하며, PPO는 경로를 이탈하지 않으면서 그 보상을 쫓습니다. 이것이 바로 가공되지 않은 GPT 스타일의 사전 학습 (Pretraining)을 사람들이 사용하는 어시스턴트로 탈바꿈시킨 파이프라인 (Pipeline)입니다. 이 방식은 효과적이지만, 세 개의 모델과 불안정한 RL 루프 (RL loop)를 동반하기 때문에 매우 무겁습니다. 이것이 바로 DPO가 별도의 보상 모델이나 PPO 없이 선호도 쌍 (Preference pairs)을 통해 직접 학습하도록 수학적 구조를 재작성한 이유입니다. 그것이 다음 단계입니다.
SFT를 단계별로 살펴보고, 보상 모델을 학습시키기 위해 직접 쌍의 순위를 매긴 다음, KL 리쉬 (KL leash)를 설정하여 보상과 품질 사이의 트레이드오프 (Trade-off)가 어떻게 나타나는지 확인해 보세요:
https://dev48v.infy.uk/ai/days/day34-rlhf.html
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기