MiniMind — 작은 언어 모델을 처음부터 끝까지 직접 학습한다
DPO 는 보상 모델 없이 '이쪽이 더 낫다' 를 가르친다
한 줄 요약
SFT 로 답하는 법을 배운 모델에게 어떤 답이 더 나은지를 가르치는 단계다. DPO(Direct Preference Optimization)는 같은 질문에 대한 좋은 답(chosen)과 나쁜 답(rejected) 쌍만으로, 보상 모델도 강화학습도 없이 분류 손실 하나로 이것을 한다. MiniMind 의 train_dpo.py 는 그 손실을 열 줄로 구현한다. 이 모듈에서는 사실은 같고 말투만 다른 쌍(입니다 대 이다)으로 DPO 를 해서, 말투가 바뀌는지와 내용은 지켜지는지를 함께 잰다.
왜 이게 필요했나
RLHF 는 사람의 선호로 보상 모델을 먼저 학습하고, 그 보상을 최대화하되 원래 모델에서 너무 멀어지지 않게 강화학습(PPO)으로 모델을 민다. 모델을 네 벌(정책·기준·보상·가치) 띄워야 하고 학습이 불안정하다. DPO 논문은 이 목표의 최적해를 닫힌 꼴로 풀어, 같은 문제를 정책과 기준 두 벌과 간단한 분류 손실로 풀 수 있게 했다. MiniMind README 는 DPO 가 정적인 선호 자료를 여러 번 도는 off-policy 방식이라 안정적이지만, 스스로 탐색하지 않으므로 '문제를 맞히는 능력' 보다 선호·안전 같은 정렬에 맞는다고 적는다. 이 모듈의 실험이 바로 그 경계를 보여 준다.
어떻게 동작하나
문장 하나의 로그확률은 답 토큰마다의 log p 를 더한 것이다. MiniMind 는 SFT 와 같은 마스크로 답 토큰만 남긴다.
log_probs = torch.gather(F.log_softmax(logits, dim=2), 2, labels.unsqueeze(2)).squeeze(-1)
seq = (log_probs * mask).sum(dim=1) # 문장마다 답 토큰의 합
chosen, rejected = seq[:B // 2], seq[B // 2:] # 배치 앞 절반이 chosen, 뒤 절반이 rejected
logits = (π_chosen − π_rejected) − (ref_chosen − ref_rejected)
loss = −F.logsigmoid(β * logits).mean()
- 기준 모델. SFT 모델을 한 벌 더 불러 얼린다(
eval(),requires_grad_(False)). 정책이 기준보다 chosen 을 상대적으로 더 좋아하게 될수록 손실이 준다. 정책이 기준과 같으면 괄호 안이 0 이라 손실은 −log σ(0) = ln 2 ≈ 0.693 이다. - β. 기준에서 얼마나 멀어져도 되는지를 정한다. 논문은 β·log(π/π_ref) 를 암묵적 보상으로 해석한다. MiniMind 기본값은 0.15 다.
- 학습률. MiniMind 기본값은 4e-8 이고 코드 주석이 '5e-8 이하 권장, 망각을 피하려고' 라고 적는다. SFT(1e-5)보다도 수백 배 작다. 선호 학습은 모델을 조금만 밀어야 한다.
함정 하나. DPO 손실은 chosen 과 rejected 의 차이만 본다. 그래서 학습률이 크면 rejected 를 크게 끌어내리면서 chosen 까지 함께 끌어내리는 일이 흔하다 — 차이는 벌어져 손실은 줄지만, 모델이 실제로 내는 답은 어느 쪽도 아닌 것이 된다. 이 코스의 모델로 lr 을 1e-4 로 올리면 chosen 의 평균 로그확률이 −3.5 에서 −16.5 로 무너지고, '입니다' 로 답하는 비율은 오히려 떨어졌다. 그래서 손실만 보지 말고 생성 결과와 다른 능력이 지켜졌는지를 함께 봐야 한다.
현장에서 만나는 모습
사내 챗봇이 반말을 섞는다거나, 금지된 주제에 답한다는 피드백이 쌓이면 선호 쌍을 만들어 DPO 를 한다. 이때 흔히 겪는 일이 '말투는 고쳐졌는데 정확도가 떨어졌다' 는 것이다(흔히 alignment tax 라 부른다). 그래서 DPO 전후로 선호 지표(선호 정확도·원하는 말투의 비율)와 원래 능력 지표(사실 정답률)를 둘 다 재는 것이 표준 절차다. 선호 자료가 사실을 가르치지는 못한다는 점도 기억해야 한다 — 모델이 모르는 사실은 chosen 을 아무리 보여 줘도 새로 생기지 않는다. 선호 쌍을 만드는 일 자체도 비용이다. 사람이 두 답을 읽고 고르는 데 시간이 들고, 고르는 사람마다 기준이 달라 자료에 잡음이 섞인다. 그래서 '무엇을 선호하는지' 를 먼저 한 문장으로 정해 두고(예: 존댓말, 근거 없는 단정 금지), 그 기준 하나만 다르게 만든 쌍부터 시작하는 것이 결과를 해석하기 쉽다 — 이 실습의 쌍이 말투 한 곳만 다른 이유다.
MiniMind 원본과 이 코스가 다른 점
MiniMind 의 선호 자료는 DPO-En-Zh-20k 에서 뽑은 실제 사람 선호 쌍이고, 한 쌍의 두 답은 내용이 다르다. 이 코스의 쌍은 내용이 같고 말투 한 곳만 다르게 만들었다. 그래야 '선호 학습이 무엇을 옮기고 무엇을 지키는가' 를 한 변수로 잴 수 있다. 학습률도 다르다 — MiniMind 는 긴 자료를 도는 64M 모델에 4e-8 을 쓰고, 이 코스는 100걸음만 도는 1M 모델에 1e-5 를 쓴다. 숫자는 달라도 원칙은 같다. SFT 학습률보다 훨씬 작게, 그리고 지표가 좋아지는 동안 원래 능력도 함께 재면서.
다음 실습에서 할 것
SFT 기준 모델로 선호 쌍 하나의 로그확률을 재고, DPO 손실을 논문 식대로 구현해 정책이 기준과 같을 때 ln 2 가 나오는지 확인한다. DPO 전의 선호 정확도와 공손 비율을 재고, 100걸음 DPO 뒤에 같은 지표가 어떻게 바뀌는지, 말투를 무시한 정답률이 지켜졌는지 잰다.