목차
메타 설명
PPO의 무거운 Value Model 없이도 생성형 AI를 강화학습할 수 있을까요? Hugging Face TRL의 RLOOTrainer를 이용해 하나의 질문에서 여러 답변을 생성하고, 자기 자신을 제외한 나머지 답변의 평균 Reward를 Baseline으로 사용하는 REINFORCE Leave-One-Out의 원리부터 Advantage, KL Penalty, Policy Clipping, LoRA·QLoRA, Custom Reward, vLLM 가속과 실전 수학 프로젝트까지 알아봅니다.
지난 시간에는 GRPOTrainer를 이용해 하나의 Prompt에서 여러 답변을 생성하고 서로의 Reward를 비교해 Policy를 강화학습하는 방법을 알아보았습니다.
예를 들어 모델에게 다음 문제를 줬습니다.
문제:
15 × 8은?모델이 네 개의 답변을 생성합니다.
답변 A:
120
답변 B:
110
답변 C:
120
답변 D:
130Reward:
A → 1
B → 0
C → 1
D → 0그리고 그룹 안에서 상대적으로 잘한 답변을 강화했습니다.
그런데 오늘은 평균을 계산하는 방법을 살짝 바꿔보겠습니다.
답변 A의 성적을 평가한다고 하겠습니다.
GRPO 스타일의 단순한 그룹 평균을 생각하면:
A
B
C
D
모두 포함할 수 있습니다.
하지만 RLOO는 다릅니다.
A를 평가할 때
A는 평균에서 제외
B
C
D
평균만 계산답변 B를 평가할 때는?
B 제외
A
C
D
평균 계산답변 C는?
C 제외
A
B
D
평균 계산바로 이것이 오늘의 핵심입니다.
Leave One Out한국어로 풀면:
하나를 빼고 계산한다.
이 아이디어가 적용된 강화학습 알고리즘이 바로 RLOO, REINFORCE Leave-One-Out입니다.
현재 Hugging Face TRL의 RLOOTrainer도 하나의 Prompt에서 여러 Completion을 생성한 뒤, 각 Completion을 평가할 때 자기 자신을 제외한 나머지 Completion의 평균 Reward를 Baseline으로 사용합니다. 이 Baseline은 Policy Gradient의 분산을 낮추는 데 사용됩니다.
학생 A:
“우리 반 평균이 몇 점이에요?”
RLOO:
“잠깐.
네 점수는 빼고 계산해야지.”
학생 A:
“왜 저만 빼요?”
RLOO:
“B도 B 계산할 때 빠지고,
C도 C 계산할 때 빠집니다.”
학생 A:
“공평하네요.”오늘은 강화학습 교실에서 본인 제외 평균제를 시행해 보겠습니다. 🤖📊
1. RLOO란?
RLOO는 다음 표현의 약자입니다.
REINFORCE Leave-One-Out이름을 두 부분으로 나눠보겠습니다.
REINFORCE
+
Leave-One-Out즉 REINFORCE Policy Gradient를 기반으로 하면서, 여러 생성 결과 중 현재 Sample 하나를 제외한 나머지 Reward 평균을 Baseline으로 사용하는 방법입니다.
RLOO는 `Back to Basics: Revisiting REINFORCE Style Optimization for Learning from Human Feedback in LLMs` 연구에서 LLM RLHF에 적합한 단순한 REINFORCE 계열 방법으로 연구되었습니다. 해당 연구에서는 복잡한 PPO 구성 요소 중 상당 부분을 줄인 REINFORCE 계열 방법들이 자신들의 실험에서 PPO와 DPO 등 강한 Baseline과 경쟁하거나 능가할 수 있음을 보고했습니다.
전체 구조:
Prompt
↓
Policy Model
↓
여러 Completion 생성
├─ A
├─ B
├─ C
└─ D
↓
각 Completion Reward 계산
↓
A 평가
→ B, C, D 평균
B 평가
→ A, C, D 평균
C 평가
→ A, B, D 평균
D 평가
→ A, B, C 평균
↓
Advantage 계산
↓
Policy 업데이트2. REINFORCE란?
REINFORCE는 가장 기본적인 Policy Gradient 알고리즘 중 하나입니다.
핵심 아이디어는 놀라울 정도로 단순합니다.
좋은 결과를 만든 행동
→ 확률 증가
나쁜 결과를 만든 행동
→ 확률 감소LLM이라면 행동은 생성된 Completion입니다.
Prompt
↓
Completion
↓
Reward예:
Prompt:
Python 리스트란?
Completion A:
리스트는 여러 값을
순서대로 저장하는 자료형입니다.
Reward:
3.0Reward가 좋다면:
A 같은 Completion이 나올 확률
↑반대로:
Completion B:
리스트는 CPU입니다.
Reward:
-2.0이라면:
B 같은 Completion이 나올 확률
↓RLOO는 이 기본적인 REINFORCE 아이디어에 좋은 Baseline을 추가합니다.
3. RLOO가 등장한 이유
PPO를 다시 떠올려 보겠습니다.
PPO에는 일반적으로 다음 구성 요소가 필요했습니다.
Policy Model
Reference Model
Reward Model
Value Model특히 Value Model은:
앞으로 받을 Reward를
얼마나 기대할 수 있는가?를 추정했습니다.
하지만 LLM은 모델 하나만 커도 GPU가 뜨끈해집니다.
여기에 모델을 여러 개 추가하면:
Policy
+
Reference
+
Reward
+
ValueGPU:
“혹시 저한테
개인적인 감정 있으세요?”RLOO 연구는 PPO의 Critic, 즉 Value Function에 의존하지 않는 훨씬 단순한 REINFORCE 계열 RLHF 방법을 탐구했습니다.
RLOO에서는 Value Model 대신 여러 Online Sample의 Reward를 Baseline 계산에 활용합니다.
Value Model:
없음
다른 Completion들의 Reward:
Baseline 역할4. PPO와 RLOO 차이
PPO
Prompt
↓
Response
↓
Reward Model
+
Value Model
↓
Advantage
↓
PPO 업데이트RLOO
Prompt
↓
Response 여러 개
↓
각 Response Reward
↓
나머지 Response 평균
↓
Advantage
↓
REINFORCE 업데이트가장 큰 차이:
PPO
→ Value Model 필요
RLOO
→ Value Model 없음또한 RLOO의 현재 TRL 구현은 Completion 전체의 Sequence Probability를 이용하는 REINFORCE 형태의 목적함수를 사용합니다.
5. GRPO와 RLOO 차이
지난 편의 GRPO와 매우 비슷해 보입니다.
둘 다:
하나의 Prompt
↓
여러 Completion 생성
↓
Reward 비교
↓
Value Model 없이 Advantage 계산합니다.
하지만 Baseline 계산 아이디어가 다릅니다.
GRPO
대표적인 GRPO 구성에서는 그룹 Reward를 중심화하거나 표준화합니다.
현재 답변 Reward
-
그룹 평균여기서 그룹 평균에는 보통 자기 자신도 포함됩니다.
RLOO
현재 답변 Reward
-
나를 제외한
다른 Completion 평균입니다.
GRPO:
“우리 모두의 평균을 봅시다.”
RLOO:
“본인 점수는 평균에서 빼세요.”이름 그대로 Leave One Out입니다.
6. Leave-One-Out이란?
4개의 Reward가 있다고 하겠습니다.
A = 10
B = 8
C = 4
D = 2A의 Baseline은:
B + C + D
─────────
3입니다.
A 자신은 들어가지 않습니다.
B의 Baseline은:
A + C + D
─────────
3입니다.
모든 Sample이 자신의 Baseline 계산에서 제외됩니다.
현재 TRL 공식 문서는 RLOO Baseline을 다음 개념으로 정의합니다.
현재 Completion을 제외한
나머지 G-1개의 평균 Reward그리고 현재 Completion Reward에서 이 Baseline을 빼 Advantage를 계산합니다.
7. RLOO 전체 학습 흐름
현재 TRL 문서는 RLOO를 크게 다음 과정으로 설명합니다.
Completion 생성
↓
Reward 계산
↓
Advantage 계산
↓
KL 추정
↓
RLOO Loss 계산RLOO는 현재 Policy가 생성한 데이터를 이용해 반복 학습하는 Online Learning 알고리즘입니다.
조금 더 자세히 보면:
① Prompt 선택
② 같은 Prompt에서 G개 Completion 생성
③ 각 Completion Reward 계산
④ KL Penalty 계산
⑤ 최종 Reward 구성
⑥ Leave-One-Out Baseline 계산
⑦ Advantage 계산
⑧ Policy Probability 계산
⑨ 필요하면 Importance Ratio 계산
⑩ Clipping
⑪ REINFORCE Loss
⑫ Policy 업데이트8. 여러 Completion 생성
RLOO도 GRPO와 마찬가지로 한 Prompt에서 여러 답변을 생성합니다.
Prompt:
12 × 8은?생성:
A:
96
B:
94
C:
96
D:
108현재 TRL의 설정은:
num_generations=4처럼 지정합니다.
현재 `RLOOConfig`의 기본값은:
num_generations=2입니다.
9. Reward 계산
각 Completion에 Reward를 부여합니다.
정답:
96Reward Function:
정답:
1
오답:
0따라서:
A:
1
B:
0
C:
1
D:
0RLOOTrainer는 현재 다음 형태의 Reward Source를 지원합니다.
Python Reward Function
Async Reward Function
Sequence Classification Reward Model
Hub의 Reward Model
여러 Reward의 조합Custom Reward Function은 Prompt와 Completion뿐 아니라 Dataset에 존재하는 추가 열도 받을 수 있습니다.
10. Baseline이 필요한 이유
가장 단순한 REINFORCE에서는 Reward를 그대로 사용할 수 있습니다.
Reward 10
→ 강하게 증가
Reward 1
→ 약하게 증가하지만 문제마다 Reward Scale과 난이도가 다르면 Gradient의 변동이 매우 커질 수 있습니다.
예를 들어:
문제 A:
Reward 100
문제 B:
Reward 2100점이 무조건 훌륭한 행동이라는 의미일까요?
그 문제에서는 모두가 150점을 받을 수도 있습니다.
RLOO는 다음 질문을 합니다.
“같은 상황에서 나온
다른 Sample들과 비교하면
얼마나 잘한 것인가?”Baseline을 빼면 Reward의 절대적인 크기보다 상대적으로 잘했는지를 볼 수 있습니다.
11. Leave-One-Out Baseline
Completion이 G개 있다면 Sample i의 Baseline은:
나를 제외한
G - 1개 Reward 평균입니다.
TRL 공식 정의:
b_i
=
나머지 Completion Reward의 평균그리고 Advantage는:
A_i
=
r_i - b_i입니다.
쉽게 표현하면:
내 점수
-
나 빼고 반 평균입니다.
12. 실제 숫자로 Baseline 계산
다음 Reward를 사용하겠습니다.
A = 10
B = 8
C = 4
D = 2A의 Baseline
A를 빼고:
8 + 4 + 2
= 14평균:
14 / 3
≈ 4.67A Advantage:
10 - 4.67
≈ +5.33B의 Baseline
B를 제외합니다.
10 + 4 + 2
= 16평균:
16 / 3
≈ 5.33Advantage:
8 - 5.33
≈ +2.67C의 Baseline
10 + 8 + 2
= 20평균:
20 / 3
≈ 6.67Advantage:
4 - 6.67
≈ -2.67D의 Baseline
10 + 8 + 4
= 22평균:
22 / 3
≈ 7.33Advantage:
2 - 7.33
≈ -5.33정리하면:
A:
+5.33
B:
+2.67
C:
-2.67
D:
-5.3313. Advantage 계산
이 Advantage가 Policy 학습 방향을 결정합니다.
Advantage > 0
→ 해당 Completion 확률 증가
Advantage < 0
→ 해당 Completion 확률 감소위 예에서는:
A:
강하게 강화
B:
약하게 강화
C:
약하게 억제
D:
강하게 억제됩니다.
RLOO:
“A는 잘했습니다.
B도 평균 이상입니다.
C는 조금 아쉽고,
D는 상담실로 오세요.”14. RLOO Advantage의 특징
재미있는 특성이 하나 있습니다.
앞 예제의 Advantage를 모두 더해보겠습니다.
+5.33
+2.67
-2.67
-5.33결과:
0RLOO의 Leave-One-Out Baseline은 그룹 내부의 상대적 차이를 중심으로 학습 신호를 만듭니다.
즉 그룹 전체가 무조건 긍정적인 Gradient만 만들거나 부정적인 Gradient만 만들지 않고 상대적인 우열을 반영할 수 있습니다.
15. GRPO 평균과 RLOO 평균 비교
앞선 Reward:
10
8
4
2전체 평균은:
6단순 Group Mean을 사용한다면:
A:
10 - 6 = 4
B:
8 - 6 = 2
C:
4 - 6 = -2
D:
2 - 6 = -4RLOO:
A:
+5.33
B:
+2.67
C:
-2.67
D:
-5.33흥미로운 관계가 있습니다.
Completion 수가 G일 때, 단순 Group Mean을 사용한 Centered Reward와 RLOO Advantage는:
RLOO Advantage
=
G
─────
G - 1
×
Centered Group Reward관계가 됩니다.
G=4라면:
4 / 3배입니다.
다만 현재 TRL의 GRPO 기본 구성은 Group Standard Deviation Scaling 등 추가 처리가 적용될 수 있으므로 실제 GRPOTrainer와 RLOOTrainer의 Advantage가 단순히 상수배 관계라고 보면 안 됩니다.
알고리즘의 Baseline 차이를 이해하기 위한 수학적 비교입니다.
16. 모든 Reward가 같으면?
Reward:
1
1
1
1A의 Baseline:
1Advantage:
1 - 1
= 0다른 Completion도 마찬가지입니다.
A:
0
B:
0
C:
0
D:
0모두 틀렸을 때도:
0
0
0
0Advantage가 모두 0입니다.
즉:
비교할 차이가 없다
→ 학습 신호가 없다는 뜻입니다.
현재 RLOOTrainer도 이 상황을 진단하기 위해 `frac_reward_zero_std`를 기록합니다. 이 값은 동일 Prompt에서 생성된 답변들의 Reward 분산이 0인 비율을 나타냅니다.
17. num_generations가 중요한 이유
Generation 수를 늘리면 Baseline을 계산할 Sample이 많아집니다.
num_generations=2라면:
A의 Baseline:
B 하나
B의 Baseline:
A 하나입니다.
반면:
num_generations=8이면 각 Completion의 Baseline에 다른 7개 Reward가 사용됩니다.
장점:
Baseline 안정성 증가 가능
다양한 답변 탐색
더 많은 비교 정보단점:
Generation 비용 증가
GPU 메모리 증가
학습 시간 증가18. RLOO 최소 Generation 수
Leave-One-Out을 하려면 최소한 다른 Sample 하나가 필요합니다.
따라서:
num_generations=1은 불가능합니다.
현재 TRL 구현에서도 `num_generations`가 2보다 작으면 오류를 발생시킵니다.
학생 수:
1명
RLOO:
“본인을 제외한
반 평균을 계산하세요.”
학생:
“저밖에 없는데요.”
RLOO:
“수업 종료.”19. Reward Function
RLOOTrainer에서는 Custom Reward Function을 매우 쉽게 작성할 수 있습니다.
def reward_func(
completions,
**kwargs
):
return [
1.0
for completion
in completions
]반환값:
Completion 하나당
float Reward 하나입니다.
현재 Reward Function에는 다음 값도 전달할 수 있습니다.
prompts
completions
completion_ids
trainer_state
Dataset 추가 열그리고 동기 함수뿐 아니라 `async def` 형태의 비동기 Reward Function도 지원합니다. 여러 Async Reward Function이 있으면 동시에 Await해 I/O 지연을 겹쳐 처리할 수 있습니다.
20. Accuracy Reward
수학 문제를 예로 들어보겠습니다.
import re
def extract_answer(
text: str
) -> str | None:
match = re.search(
r"<answer>\s*(.*?)\s*</answer>",
text,
flags=re.DOTALL
)
if match is None:
return None
return (
match
.group(1)
.strip()
)Reward Function:
def accuracy_reward(
completions,
ground_truth,
**kwargs
):
rewards = []
for completion, answer in zip(
completions,
ground_truth
):
predicted = extract_answer(
completion
)
rewards.append(
1.0
if predicted == str(answer)
else 0.0
)
return rewards21. Format Reward
정답뿐 아니라 형식도 가르칠 수 있습니다.
원하는 형식:
<answer>84</answer>Reward:
def format_reward(
completions,
**kwargs
):
pattern = re.compile(
r".*<answer>\s*"
r"[-+]?[0-9]+(?:\.[0-9]+)?"
r"\s*</answer>\s*$",
flags=re.DOTALL
)
return [
1.0
if pattern.fullmatch(
completion.strip()
)
else 0.0
for completion
in completions
]22. 여러 Reward 조합
현재 RLOOTrainer는 여러 Reward Function을 함께 사용할 수 있습니다.
reward_funcs=[
accuracy_reward,
format_reward
]Reward:
Accuracy:
1
Format:
1단순 합산하면:
Total Reward:
2현재 여러 Reward Function을 지정하면 결과를 합산하며 `reward_weights`로 중요도를 조정할 수 있습니다.
23. Reward Weight
정답이 형식보다 중요하다면:
reward_weights=[
1.0,
0.2
]로 지정할 수 있습니다.
예:
정답 맞음:
1 × 1.0
Format 맞음:
1 × 0.2
Total:
1.2정답이 틀리고 형식만 맞으면:
0 + 0.2
= 0.2모델:
“정답은 모르겠지만
태그는 예쁘게 썼습니다.”
RLOO:
“0.2점.”24. KL Penalty
RLOO는 단순히 Reward만 최대화하지 않습니다.
Policy가 기존 모델에서 지나치게 멀리 움직이지 않도록 Reference Policy와의 KL Divergence를 Reward에 반영할 수 있습니다.
현재 RLOO의 Reward는 개념적으로:
Reward Model Score
-
beta × KL구조로 설명됩니다.
즉:
Reward는 높지만
기존 모델에서 지나치게 멀어짐
→ Penalty입니다.
25. beta의 의미
현재 RLOOConfig 기본값:
beta=0.05입니다.
Beta가 커지면:
Reference에서
멀리 움직이기 어려움Beta가 작아지면:
Reward 방향으로
더 자유롭게 이동합니다.
특별히:
beta=0.0으로 설정하면 Reference Model 자체를 로드하지 않아 메모리 사용량과 연산량을 줄일 수 있습니다.
26. Reference Model
이전 RLOO API에서는 Reference Policy를 직접 전달하던 시절이 있었습니다.
현재 API에서는 다릅니다.
현재 RLOOTrainer에서는:
ref_policy
직접 전달방식이 제거되고 Policy Model을 바탕으로 Reference Model을 자동 생성합니다.
즉 현재 기본 Trainer:
trainer = RLOOTrainer(
model=MODEL_ID,
reward_funcs=reward_func,
train_dataset=dataset
)만으로도 됩니다.
개발자:
“Reference Model 인자는 어디 있죠?”
현재 RLOOTrainer:
“제가 준비합니다.”27. 현재 RLOO와 GRPO의 KL 차이
흥미로운 차이가 있습니다.
현재 RLOO 구현에서는 KL이 Reward Shaping에 사용됩니다.
즉 KL을 계산한 후 Scalar Reward에 포함하고, 그 Reward를 이용해 Advantage를 계산합니다.
KL 항 자체를 직접 Gradient 경로로 Backpropagation하지 않습니다.
반면 현재 GRPO는 KL 항을 Objective에 직접 포함할 수 있어 Gradient가 KL 항을 통해서도 흐릅니다. TRL 공식 RLOO 문서는 이 차이를 명시하고 있습니다.
쉽게 표현하면:
RLOO:
KL 점수를 계산해서
성적표에서 감점
GRPO:
KL 항 자체도
Loss 계산에 직접 참여 가능28. RLOO Loss
RLOO의 기본 REINFORCE Loss를 직관적으로 표현하면:
- Advantage
×
Completion Log Probability입니다.
TRL 공식 문서에서는 Completion 전체의 확률과 Leave-One-Out Advantage를 이용하는 REINFORCE Loss로 정의합니다.
Positive Advantage:
좋은 Completion
→ Log Probability 증가 방향Negative Advantage:
나쁜 Completion
→ Log Probability 감소 방향29. Importance Sampling Ratio
현재 기본값은:
num_iterations=1입니다.
이 경우 데이터를 현재 Policy로 생성하고 바로 한 번 업데이트하므로 기본적인 REINFORCE와 매우 가깝습니다.
하지만 같은 Completion Batch로 여러 번 업데이트한다면:
num_iterations=2또는:
num_iterations=4처럼 사용할 수 있습니다.
그러면 Generation 시점의 Policy와 현재 업데이트된 Policy가 달라집니다.
그래서:
현재 Policy 확률
──────────────
Sampling Policy 확률인 Importance Sampling Ratio가 필요합니다.
TRL 공식 문서도 여러 Gradient Step을 수행할 경우 Ratio와 Clipping을 적용하고, 기본 `num_iterations=1`에서는 Ratio가 1이 되어 표준 REINFORCE 형태로 축소된다고 설명합니다.
30. Policy Clipping
RLOOTrainer도 Policy Update가 지나치게 커지는 것을 막기 위한 Clipping을 제공합니다.
현재 기본값:
epsilon=0.2입니다.
대칭 Clip이라면:
0.8 ~ 1.2정도의 Ratio 범위를 사용하게 됩니다.
또한 현재:
epsilon_high=0.28처럼 상단 Clip을 별도로 설정하는 것도 가능합니다.
31. num_iterations
현재 기본값:
num_iterations=1입니다.
1
현재 Policy가 생성
↓
한 번 학습가장 Online한 형태입니다.
2 이상
한 번 생성
↓
같은 Rollout으로
여러 번 Gradient UpdateGeneration 비용은 줄일 수 있지만 점점 Off-policy가 되므로 Importance Ratio와 Clipping이 중요해집니다.
32. normalize_advantages
현재 기본값:
normalize_advantages=False입니다.
활성화하면:
normalize_advantages=TrueGeneration Batch의 Advantage를:
평균:
0
표준편차:
1이 되도록 정규화합니다.
Reward Scale이 불안정한 환경에서 실험할 수 있는 옵션입니다.
33. Reward Clipping
Reward가 매우 큰 값을 가지는 경우:
+5000
-3000
+12000Gradient가 불안정해질 수 있습니다.
현재 RLOOConfig에는:
reward_clip_range=(
-5.0,
5.0
)처럼 Reward 자체를 제한하는 기능이 있습니다. 기본값은 `None`이라 별도 Clipping을 하지 않습니다.
34. 현재 TRL 버전
2026년 8월 17일 현재 PyPI의 최신 TRL 안정 버전은 1.10.0이며, 2026년 8월 13일 릴리스로 표시됩니다.
설치 버전 확인:
python -m pip show trlPython:
import trl
print(
trl.__version__
)현재 RLOOTrainer는 일반 TRL API에서 가져옵니다.
from trl import (
RLOOConfig,
RLOOTrainer
)현재 공식 예제 목록에도 RLOOTrainer 기반 수학 Fine-tuning Script가 포함되어 있습니다.
35. RLOOTrainer 기본 사용법
현재 공식 Quick Start는 매우 간단합니다.
from datasets import load_dataset
from trl import RLOOTrainer
from trl.rewards import (
accuracy_reward
)
dataset = load_dataset(
"trl-lib/DeepMath-103K",
split="train"
)
trainer = RLOOTrainer(
model=(
"Qwen/"
"Qwen2.5-0.5B-Instruct"
),
reward_funcs=accuracy_reward,
train_dataset=dataset
)
trainer.train()현재 공식 문서도 Qwen2.5-0.5B-Instruct와 DeepMath-103K 형태의 예제로 RLOOTrainer를 설명합니다.
실행:
accelerate launch train_rloo.py36. RLOOConfig 핵심 설정
예:
from trl import RLOOConfig
training_args = RLOOConfig(
output_dir=(
"outputs/"
"math-rloo"
),
learning_rate=1e-6,
per_device_train_batch_size=1,
gradient_accumulation_steps=4,
num_train_epochs=3,
num_generations=4,
max_completion_length=128,
temperature=0.8,
top_p=0.95,
beta=0.05,
num_iterations=1,
epsilon=0.2,
normalize_advantages=False,
reward_weights=[
1.0,
0.2
],
logging_steps=1,
log_completions=True,
report_to="none"
)현재 RLOOConfig의 주요 기본값은 다음과 같습니다.
learning_rate:
1e-6
num_generations:
2
max_completion_length:
512
temperature:
1.0
beta:
0.05
num_iterations:
1
epsilon:
0.2
normalize_advantages:
False37. Dataset 구성
RLOO Dataset의 핵심 열은:
prompt입니다.
현재 Trainer는 Standard Format과 Conversational Format을 모두 지원하며 추가 Dataset 열은 Custom Reward Function에서 사용할 수 있습니다.
예:
{
"prompt": (
"17 × 8을 계산하세요. "
"최종 답은 "
"<answer>숫자</answer> "
"형식으로 출력하세요."
),
"ground_truth": "136"
}`ground_truth`는 Reward Function에서 사용합니다.
38. LoRA와 RLOO
RLOOTrainer는 현재 `peft_config`를 직접 지원합니다.
from peft import (
LoraConfig,
TaskType
)
lora_config = LoraConfig(
task_type=(
TaskType.CAUSAL_LM
),
r=16,
lora_alpha=32,
lora_dropout=0.05,
target_modules=[
"q_proj",
"k_proj",
"v_proj",
"o_proj"
],
bias="none"
)Trainer:
trainer = RLOOTrainer(
model=MODEL_ID,
args=training_args,
reward_funcs=[
accuracy_reward,
format_reward
],
train_dataset=train_dataset,
peft_config=lora_config
)Policy 전체가 아니라 LoRA Adapter만 Online RL로 업데이트할 수 있습니다.
39. QLoRA와 RLOO
현재 RLOOTrainer에는 `quantization_config`와 `peft_config`가 모두 존재하며, 모델 ID 문자열과 함께 사용하면 QLoRA 형태로 구성할 수 있습니다. 이미 Model Object를 직접 생성해 전달한 경우 Trainer의 `quantization_config`는 적용되지 않습니다.
예:
import torch
from transformers import (
BitsAndBytesConfig
)
quantization_config = (
BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_use_double_quant=True,
bnb_4bit_compute_dtype=(
torch.bfloat16
)
)
)Trainer:
trainer = RLOOTrainer(
model=MODEL_ID,
args=training_args,
reward_funcs=[
accuracy_reward
],
train_dataset=train_dataset,
peft_config=lora_config,
quantization_config=(
quantization_config
)
)BF16은 실제 GPU 지원 여부를 확인해야 합니다.
40. vLLM 가속
RLOO는 Online RL입니다.
즉 계속 새로운 Completion을 생성해야 합니다.
생성
↓
학습
↓
생성
↓
학습
↓
생성Generation이 병목이 되기 쉽습니다.
현재 RLOOTrainer는 vLLM 기반 Generation 가속을 지원합니다.
설치:
python -m pip install "trl[vllm]"활성화:
training_args = RLOOConfig(
use_vllm=True
)현재 기본 Mode는:
vllm_mode="colocate"입니다. Training Model과 같은 GPU를 공유합니다.
별도 GPU를 사용한다면:
vllm_mode="server"를 사용할 수 있습니다. 현재 RLOOConfig의 vLLM Colocate 기본 GPU Memory Utilization은 0.3입니다.
41. RLOO 로그 읽기
현재 RLOOTrainer는 다양한 Metric을 기록합니다.
중요한 항목:
reward
reward_std
frac_reward_zero_std
rewards/<reward_name>/mean
rewards/<reward_name>/std
completions/mean_length
completions/clipped_ratio
entropy
kl
clip_ratio/region_meanreward
전체 Reward Function을 합산한 평균 Reward입니다.
reward_std
전체 Batch Reward의 표준편차입니다.
entropy
생성 Token 분포가 얼마나 다양한지 볼 수 있습니다.
kl
`beta`가 0이 아닐 때 Reference와의 평균 KL을 기록합니다.
42. Reward Std와 Zero Std
특히 중요합니다.
frac_reward_zero_std가 높다면:
같은 Prompt의 답변들이
모두 같은 점수를 받고 있다.는 뜻일 수 있습니다.
예:
Prompt A
Completion 1:
0
Completion 2:
0
Completion 3:
0
Completion 4:
0RLOO:
“나머지 평균도 0.
내 Reward도 0.
Advantage도 0.”학습할 것이 없습니다.
원인:
문제가 너무 어려움
문제가 너무 쉬움
Reward Function이 지나치게 Binary
Temperature가 너무 낮음
모델이 같은 답만 생성현재 Trainer가 이 비율을 직접 기록하는 이유도 이런 Online RL 학습 신호를 진단하기 위해서입니다.
43. 실전 수학 강화학습 프로젝트
이제 직접 만들어보겠습니다.
목표:
산술 문제
↓
한 Prompt에서
4개 답변 생성
↓
Accuracy Reward
+
Format Reward
↓
Leave-One-Out Advantage
↓
LoRA Policy 학습이 예제는 RLOO Pipeline을 이해하기 위한 소규모 실습입니다. 실제 추론 성능 향상을 검증하려면 훨씬 많은 Train·Validation·Test 데이터가 필요합니다.
`train_rloo.py`:
from pathlib import Path
import re
from datasets import Dataset
from peft import (
LoraConfig,
TaskType
)
from transformers import (
AutoTokenizer
)
from trl import (
RLOOConfig,
RLOOTrainer
)
MODEL_ID = (
"Qwen/"
"Qwen2.5-0.5B-Instruct"
)
OUTPUT_DIR = Path(
"outputs/"
"math-rloo"
)
SEED = 2026
RAW_DATA = [
{
"question": "12 × 7",
"ground_truth": "84"
},
{
"question": "25 + 38",
"ground_truth": "63"
},
{
"question": "144 ÷ 12",
"ground_truth": "12"
},
{
"question": "17 × 8",
"ground_truth": "136"
},
{
"question": "93 - 47",
"ground_truth": "46"
},
{
"question": "16 × 9",
"ground_truth": "144"
},
{
"question": "225 ÷ 15",
"ground_truth": "15"
},
{
"question": "128 + 79",
"ground_truth": "207"
},
{
"question": "300 - 128",
"ground_truth": "172"
},
{
"question": "19 × 11",
"ground_truth": "209"
},
{
"question": "256 ÷ 16",
"ground_truth": "16"
},
{
"question": "67 + 89",
"ground_truth": "156"
},
{
"question": "13 × 14",
"ground_truth": "182"
},
{
"question": "500 - 273",
"ground_truth": "227"
},
{
"question": "18 × 12",
"ground_truth": "216"
},
{
"question": "324 ÷ 18",
"ground_truth": "18"
},
{
"question": "45 + 76 - 21",
"ground_truth": "100"
},
{
"question": "8 × 7 + 4",
"ground_truth": "60"
},
{
"question": "(15 + 5) × 3",
"ground_truth": "60"
},
{
"question": "100 ÷ 4 + 17",
"ground_truth": "42"
}
]
SYSTEM_MESSAGE = (
"당신은 정확한 산술 문제 풀이 AI입니다. "
"문제를 계산한 뒤 최종 결과를 반드시 "
"<answer>숫자</answer> 형식으로 출력하세요."
)
def build_prompt(
tokenizer,
question: str
) -> str:
messages = [
{
"role": "system",
"content": SYSTEM_MESSAGE
},
{
"role": "user",
"content": (
f"다음 값을 계산하세요: "
f"{question}"
)
}
]
return tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True
)
def extract_answer(
text: str
) -> str | None:
match = re.search(
r"<answer>\s*(.*?)\s*</answer>",
text,
flags=re.DOTALL
)
if match is None:
return None
return (
match
.group(1)
.strip()
)
def accuracy_reward(
completions,
ground_truth,
**kwargs
):
rewards = []
for completion, answer in zip(
completions,
ground_truth
):
predicted = extract_answer(
completion
)
rewards.append(
1.0
if predicted == str(answer)
else 0.0
)
return rewards
def format_reward(
completions,
**kwargs
):
pattern = re.compile(
r".*<answer>\s*"
r"[-+]?[0-9]+(?:\.[0-9]+)?"
r"\s*</answer>\s*$",
flags=re.DOTALL
)
rewards = []
for completion in completions:
is_valid = bool(
pattern.fullmatch(
completion.strip()
)
)
rewards.append(
1.0
if is_valid
else 0.0
)
return rewards
def main():
tokenizer = (
AutoTokenizer
.from_pretrained(
MODEL_ID,
padding_side="left"
)
)
if tokenizer.pad_token is None:
tokenizer.pad_token = (
tokenizer.eos_token
)
prepared_data = []
for row in RAW_DATA:
prepared_data.append({
"prompt": build_prompt(
tokenizer,
row["question"]
),
"ground_truth": (
row["ground_truth"]
)
})
dataset = Dataset.from_list(
prepared_data
)
split_dataset = (
dataset.train_test_split(
test_size=0.2,
seed=SEED,
shuffle=True
)
)
train_dataset = (
split_dataset["train"]
)
eval_dataset = (
split_dataset["test"]
)
lora_config = LoraConfig(
task_type=(
TaskType.CAUSAL_LM
),
r=16,
lora_alpha=32,
lora_dropout=0.05,
target_modules=[
"q_proj",
"k_proj",
"v_proj",
"o_proj"
],
bias="none"
)
training_args = RLOOConfig(
output_dir=str(
OUTPUT_DIR
),
learning_rate=1e-6,
lr_scheduler_type="linear",
warmup_ratio=0.1,
per_device_train_batch_size=1,
per_device_eval_batch_size=2,
gradient_accumulation_steps=4,
num_train_epochs=3,
num_generations=4,
num_generations_eval=2,
max_completion_length=128,
temperature=0.8,
top_p=0.95,
beta=0.05,
num_iterations=1,
epsilon=0.2,
normalize_advantages=False,
reward_weights=[
1.0,
0.2
],
remove_unused_columns=False,
mask_truncated_completions=True,
eval_strategy="epoch",
save_strategy="epoch",
logging_strategy="steps",
logging_steps=1,
logging_first_step=True,
save_total_limit=2,
gradient_checkpointing=True,
log_completions=True,
num_completions_to_print=2,
report_to="none",
seed=SEED,
data_seed=SEED,
run_name=(
"math-rloo-lora"
)
)
trainer = RLOOTrainer(
model=MODEL_ID,
reward_funcs=[
accuracy_reward,
format_reward
],
args=training_args,
train_dataset=train_dataset,
eval_dataset=eval_dataset,
processing_class=tokenizer,
peft_config=lora_config
)
print(
"[학습 가능한 파라미터]"
)
trainer.model.print_trainable_parameters()
print(
"[RLOO 강화학습 시작]"
)
train_result = (
trainer.train()
)
trainer.log_metrics(
"train",
train_result.metrics
)
trainer.save_metrics(
"train",
train_result.metrics
)
trainer.save_state()
print(
"[Validation 평가]"
)
eval_metrics = (
trainer.evaluate()
)
trainer.log_metrics(
"eval",
eval_metrics
)
trainer.save_metrics(
"eval",
eval_metrics
)
print(
"[모델 저장]"
)
trainer.save_model(
str(
OUTPUT_DIR
)
)
tokenizer.save_pretrained(
OUTPUT_DIR
)
print(
f"저장 완료: "
f"{OUTPUT_DIR.resolve()}"
)
if __name__ == "__main__":
main()현재 RLOOTrainer는 `prompt` 열을 갖는 Dataset을 요구하며 Custom Reward Function이 추가 열을 사용한다면 `remove_unused_columns=False`를 유지해야 합니다. `num_generations`는 Effective Training Batch Size를 정확히 나눌 수 있어야 하며 최소 2 이상이어야 합니다.
44. 학습 결과 저장
trainer.save_model(
"outputs/math-rloo"
)Tokenizer:
tokenizer.save_pretrained(
"outputs/math-rloo"
)Checkpoint에서 다시 시작하려면:
trainer.train(
resume_from_checkpoint=True
)또는:
trainer.train(
resume_from_checkpoint=(
"outputs/"
"math-rloo/"
"checkpoint-100"
)
)현재 RLOOTrainer의 `train()`은 Trainer Checkpoint에서 모델·Optimizer·Scheduler 상태를 복원해 학습을 이어갈 수 있습니다.
45. 학습 전후 평가
다음 Test 문제는 Training에 넣지 않습니다.
23 × 7
31 + 48
400 ÷ 25
18 × 14
500 - 168
(25 + 5) × 4비교:
| 항목 | Base | RLOO |
|---|---|---|
| 정답률 | 측정 | 측정 |
| Format 준수율 | 측정 | 측정 |
| 평균 Reward | 측정 | 측정 |
| 평균 답변 길이 | 측정 | 측정 |
| EOS 비율 | 측정 | 측정 |
RLOO Training Reward만 보고 성공 여부를 판단해서는 안 됩니다.
46. Reward Hacking
RLOO도 Reward Function이 잘못되어 있으면 그 허점을 찾아냅니다.
잘못된 Reward:
def bad_reward(
completions,
**kwargs
):
return [
1.0
if "<answer>" in completion
else 0.0
for completion
in completions
]모델:
<answer>고양이</answer>Reward:
1다음:
<answer>777777777</answer>Reward:
1모델:
“정답은 중요하지 않고
태그만 있으면 되는군요.”개발자:
“아니 그게 아닌데...”강화학습 알고리즘은 개발자의 마음을 읽지 않습니다.
코드로 정의된 Reward
=
실제 목표로 받아들입니다.
47. RLOO 안정화 방법
학습이 불안정하다면 다음 순서로 확인합니다.
Reward Function
가장 먼저 봅니다.
실제 정답과 Reward가 일치하는가?
Reward Scale이 지나치게 큰가?
길이나 특정 표현에 편향되어 있지 않은가?Learning Rate
현재 기본:
learning_rate=1e-6보다 너무 크게 올리지 않았는지 확인합니다.
Beta
KL이 너무 커지면:
beta=0.1처럼 Reference 제약을 강화하는 실험을 할 수 있습니다.
num_iterations
불안정하다면:
num_iterations=1로 돌아갑니다.
Generation Diversity
답변이 모두 같다면:
temperature=1.0등을 검토합니다.
Reward Zero Std
frac_reward_zero_std가 지나치게 높은지 확인합니다.
Truncated Completion
현재 RLOOConfig에는 잘린 Completion을 Loss에서 제외하는:
mask_truncated_completions=True옵션이 있습니다. 현재 문서는 학습 안정성을 위한 실험 옵션으로 이를 제공합니다.
48. PPO·GRPO·RLOO 비교
세 가지 Online RL 방법을 정리해 보겠습니다.
| 항목 | PPO | GRPO | RLOO |
|---|---|---|---|
| Online Generation | O | O | O |
| Value Model | O | X | X |
| 여러 Completion | 가능 | 핵심 | 핵심 |
| Baseline | Value Model | Group Reward | Leave-One-Out |
| 자기 Reward가 Baseline에 포함 | 해당 없음 | 방식에 따라 포함 | X |
| Reference Model | 일반적으로 사용 | beta에 따라 | beta에 따라 |
| KL 기본값 | 사용 | 현재 0 | 현재 0.05 |
| Policy Clipping | O | O | O |
| 구조 복잡도 | 높음 | 중간 | 중간 |
| Custom Reward | 가능 | O | O |
현재 TRL RLOO의 `beta` 기본값은 0.05인 반면, 앞서 살펴본 현재 GRPO 기본 `beta`는 0.0이므로 기본 Reference Model 사용 여부도 차이가 있습니다. RLOO는 `beta=0`으로 설정하면 Reference Model을 생략할 수 있습니다.
한 문장으로 정리하면:
PPO:
“Value Model에게
예상 점수를 물어봅시다.”
GRPO:
“그룹 성적을
상대 비교합시다.”
RLOO:
“내 점수는 빼고
친구들 평균과 비교합시다.”49. 자주 발생하는 오류
오류 1. RLOOTrainer Import 실패
현재 방식:
from trl import (
RLOOConfig,
RLOOTrainer
)TRL 업데이트:
python -m pip install --upgrade trl오류 2. 오래된 rloo_k 예제를 사용함
오래된 코드:
rloo_k=4현재:
num_generations=4로 이름이 변경되었습니다.
오류 3. kl_coef를 사용함
오래된 이름:
kl_coef=0.05현재:
beta=0.05입니다.
오류 4. cliprange 사용
오래된 이름:
cliprange=0.2현재:
epsilon=0.2입니다.
오류 5. num_ppo_epochs 사용
오래된 설정:
num_ppo_epochs=4현재 RLOOConfig에서는:
num_iterations=4로 변경되었습니다.
오류 6. Reference Model을 직접 전달하려 함
과거 API에는 `ref_policy`가 있었지만 현재 Trainer에서는 제거되었습니다.
Reference Model은 Trainer가 자동 생성합니다.
오류 7. num_generations=1
ValueError가 발생합니다.
RLOO는 Leave-One-Out Baseline을 계산해야 하므로 최소 2개의 Generation이 필요합니다.
오류 8. Batch Size가 num_generations로 나누어지지 않음
예:
Effective Batch:
4
num_generations:
3현재 RLOOConfig는 Effective Batch가 `num_generations`로 정확히 나누어져야 합니다.
해결:
num_generations=4오류 9. Eval Batch Size 오류
Evaluation에서도 Global Eval Batch Size와 `num_generations_eval` 관계를 확인해야 합니다.
예:
per_device_eval_batch_size=2
num_generations_eval=21 GPU라면 나누어집니다.
오류 10. Reward가 모두 같음
1
1
1
1또는:
0
0
0
0확인:
frac_reward_zero_stdReward Function과 문제 난이도를 조정합니다.
오류 11. Reward는 오르는데 정답률은 그대로
Format Reward만 최적화하고 있을 수 있습니다.
개별 Metric:
rewards/accuracy_reward/mean
rewards/format_reward/mean을 함께 봅니다. RLOOTrainer는 Reward Function별 평균과 표준편차를 기록합니다.
오류 12. KL이 너무 커짐
klMetric을 확인합니다.
대응 후보:
Learning Rate 감소
beta 증가
num_iterations 감소오류 13. Clip Ratio가 너무 높음
확인:
clip_ratio/region_mean높은 값은 많은 Sequence Probability Ratio가 Trust Region 밖으로 벗어나 Clip되고 있다는 의미입니다.
오류 14. Completion이 계속 잘림
확인:
completions/clipped_ratio대응:
max_completion_length=256또는 Prompt와 답변 형식을 간결하게 만듭니다.
오류 15. CUDA Out of Memory
우선:
Batch Size 감소
num_generations 감소
max_completion_length 감소
LoRA
QLoRA
Gradient Checkpointing을 검토합니다.
오류 16. beta=0인데 KL Metric이 없음
정상입니다.
beta=0.0이면 Reference Model을 로드하지 않으며 KL Metric도 사용되지 않습니다.
오류 17. Reward Model 대신 Custom Function을 쓰고 싶음
가능합니다.
trainer = RLOOTrainer(
model=MODEL_ID,
reward_funcs=[
my_reward_func
],
train_dataset=dataset
)현재 RLOOTrainer는 Dense Reward Model 없이 Custom Reward만으로도 학습할 수 있습니다.
오류 18. 비동기 API를 Reward로 사용하고 싶음
현재 Custom Reward Function은:
async def reward_func(
completions,
**kwargs
):
...형태도 지원합니다. 여러 Async Reward Function은 동시에 처리할 수 있습니다.
오류 19. vLLM OOM
Colocate Mode에서는 Training Model과 vLLM이 GPU를 공유합니다.
다음 값을 조정합니다.
vllm_gpu_memory_utilization=0.2현재 기본값은 0.3입니다.
오류 20. vLLM Server와 Trainer GPU가 같음
Server Mode에서는 별도 GPU 사용을 권장합니다.
같은 GPU를 잘못 사용하면 NCCL 관련 충돌이 발생할 수 있다고 현재 공식 문서가 경고합니다.
50. 연습 문제
문제 1
RLOO의 전체 이름을 작성하세요.
REINFORCE Leave-One-Out문제 2
Reward가 다음과 같습니다.
A = 8
B = 6
C = 4
D = 2A의 Leave-One-Out Baseline을 계산하세요.
문제 3
위 데이터에서 B의 Baseline과 Advantage를 계산하세요.
문제 4
모든 Reward가 다음과 같다면 Advantage는 어떻게 될까요?
1
1
1
1문제 5
RLOO에서 Value Model이 필요하지 않은 이유를 설명하세요.
문제 6
다음 오래된 설정을 현재 API 이름으로 바꾸세요.
rloo_k
cliprange
kl_coef
num_ppo_epochs정답:
num_generations
epsilon
beta
num_iterations문제 7
다음 Reward Function을 작성하세요.
정답:
+1
오답:
0문제 8
JSON Parsing에 성공하면 0.2 Reward를 추가하세요.
문제 9
Accuracy와 Format Reward Weight를 다음처럼 지정하세요.
Accuracy:
1.0
Format:
0.2문제 10
다음 Beta를 비교하세요.
0
0.01
0.05
0.1그리고:
Reward
KL
Test Accuracy를 비교하세요.
문제 11
`num_generations`를 비교하세요.
2
4
8문제 12
각 설정에서 다음을 기록하세요.
reward_std
frac_reward_zero_std
학습 시간
GPU 메모리문제 13
다음 Temperature를 비교하세요.
0.3
0.8
1.2문제 14
`num_iterations`를 비교하세요.
1
2
4문제 15
`normalize_advantages=True`와 `False`를 비교하세요.
문제 16
Reward Clip을 적용하세요.
reward_clip_range=(
-2.0,
2.0
)문제 17
LoRA Rank를 비교하세요.
8
16
32문제 18
QLoRA를 적용해 4비트 RLOO를 구성하세요.
문제 19
vLLM Colocate와 Server Mode의 장단점을 정리하세요.
문제 20
다음 프로젝트로 확장하세요.
산술 문제 5,000개
Train / Validation / Test
Accuracy Reward
Format Reward
Difficulty Reward
RLOO + LoRA
Reward Curve
KL Curve
Zero Std 분석
오답 CSV 저장
Base vs RLOO Accuracy 비교51. 핵심 요약
Import
from trl import (
RLOOConfig,
RLOOTrainer
)Dataset
{
"prompt": (
"12 × 7을 계산하세요."
),
"ground_truth": "84"
}Reward Function
def accuracy_reward(
completions,
ground_truth,
**kwargs
):
return [
1.0
if extract_answer(
completion
) == answer
else 0.0
for completion, answer
in zip(
completions,
ground_truth
)
]LoRA
lora_config = LoraConfig(
task_type=(
TaskType.CAUSAL_LM
),
r=16,
lora_alpha=32,
target_modules=[
"q_proj",
"k_proj",
"v_proj",
"o_proj"
]
)RLOOConfig
training_args = RLOOConfig(
output_dir="outputs/rloo",
learning_rate=1e-6,
per_device_train_batch_size=1,
gradient_accumulation_steps=4,
num_generations=4,
max_completion_length=128,
temperature=0.8,
beta=0.05,
num_iterations=1,
epsilon=0.2,
reward_weights=[
1.0,
0.2
],
log_completions=True,
report_to="none"
)Trainer
trainer = RLOOTrainer(
model=MODEL_ID,
args=training_args,
reward_funcs=[
accuracy_reward,
format_reward
],
train_dataset=train_dataset,
peft_config=lora_config
)학습
trainer.train()저장
trainer.save_model(
"models/rloo-model"
)52. 마무리
이번 시간에는 RLOOTrainer를 이용해 Value Model 없이 생성형 AI를 Online Reinforcement Learning하는 방법을 알아보았습니다.
RLOO의 핵심을 한 장으로 정리하면 다음과 같습니다.
Prompt
↓
여러 Completion 생성
├─ A
├─ B
├─ C
└─ D
↓
Reward 계산
↓
A 평가:
B + C + D 평균
B 평가:
A + C + D 평균
C 평가:
A + B + D 평균
D 평가:
A + B + C 평균
↓
내 Reward
-
나를 제외한 평균
↓
Advantage
↓
REINFORCE Policy UpdateGRPO와 RLOO는 형제처럼 닮았습니다.
둘 다:
Value Model 없음
여러 Completion 생성
Group Reward 활용
Online RL이라는 공통점이 있습니다.
하지만 질문하는 방식이 다릅니다.
GRPO:
“우리 그룹에서
나는 어느 정도인가?”RLOO:
“나를 빼고 계산한
다른 사람들의 평균보다
나는 얼마나 잘했는가?”RLOO에서 이 Leave-One-Out Baseline은 Policy Gradient의 분산을 낮추기 위한 핵심 장치입니다. 현재 TRL 구현도 자기 자신을 제외한 나머지 Reward 평균을 Baseline으로 사용합니다.
PPO와 비교하면 구조도 단순해집니다.
PPO:
Policy
Reference
Reward
Value
RLOO:
Policy
Reference
Reward그리고:
beta=0.0으로 KL Penalty까지 사용하지 않는다면 현재 TRL은 Reference Model도 로드하지 않습니다.
Policy
+
Reward수준으로 더욱 단순한 Online RL 실험도 가능합니다.
하지만 모델 수가 줄었다고 강화학습 자체가 쉬워지는 것은 아닙니다.
여전히 가장 중요한 질문은 이것입니다.
Reward를
무엇으로 정의했는가?정답 Reward가 잘못되어 있다면:
틀린 정답
→ 높은 Reward를 학습합니다.
Format Reward 비중이 너무 크다면:
답은 틀림
하지만 JSON은 예쁨같은 모델이 탄생할 수 있습니다.
Reward가 모두 같다면:
0
0
0
0RLOO는 비교할 것이 없습니다.
RLOO:
“나를 제외한 평균을
계산했습니다.”
모델:
“그래서요?”
RLOO:
“모두 0점입니다.”
모델:
“누가 잘했는데요?”
RLOO:
“...아무도요.”그래서 Online RL에서는 다음 지표가 정말 중요합니다.
reward
reward_std
frac_reward_zero_std
kl
completion length
clip ratio학습 Loss 하나만 보고 강화학습의 건강 상태를 판단하면 안 됩니다. 현재 RLOOTrainer 역시 Reward, Completion Length, Entropy, KL, Clip Ratio 등을 별도 Metric으로 기록합니다.
지금까지 우리는 생성형 AI Post-training의 중요한 도구들을 상당히 많이 모았습니다.
SFT
→ 모범 답안 학습
DPO
→ Preference Pair 학습
Reward Model
→ 답변 채점
PPO
→ Value Model 기반 Online RL
GRPO
→ Group Relative Online RL
RLOO
→ Leave-One-Out Online RL처음에는 단순했던 AI 훈련장이 이제 꽤 그럴듯한 연구소가 되었습니다. 🧪🤖
다음 편 예고
[Python 완전정복 시리즈 #45] KTOTrainer 완벽 이해하기 | Chosen·Rejected 쌍 없이 좋아요와 싫어요 데이터로 AI의 선호도를 학습하는 방법
다음 시간에는 다시 Preference Optimization으로 이동합니다.
DPO에는 항상 다음 두 답변이 한 쌍으로 필요했습니다.
Chosen
vs
Rejected그런데 실제 서비스에서는 사용자에게 이렇게 받는 경우가 훨씬 많습니다.
👍 좋아요또는:
👎 싫어요즉 비교 Pair가 없습니다.
질문 A + 답변 A
👍
질문 B + 답변 B
👎이런 Unpaired Preference Data를 이용해 모델을 학습하는 방법이 KTO입니다.
DPO:
“답변 A와 B 중
어느 쪽이 더 좋습니까?”
KTO:
“이 답변은
좋았습니까, 싫었습니까?”실제 서비스의 좋아요·싫어요 로그를 AI 학습 데이터로 연결하는 방법을 다음 편에서 알아보겠습니다. 👍👎🤖
#Python #파이썬 #Python강좌 #HuggingFace #TRL #RLOO #RLOOTrainer #REINFORCE #LeaveOneOut #강화학습 #ReinforcementLearning #RLHF #OnlineRL #RewardFunction #RewardModel #Advantage #Baseline #KLDivergence #KLPenalty #PolicyGradient #LoRA #QLoRA #PEFT #vLLM #GRPO #PPO #DPO #LLM #생성형AI #AI정렬 #PostTraining #FineTuning #PyTorch #코딩공부 #프로그래밍
