15. 실전사례 — 현장의 목소리, JoyCaption의 DPO 실전기

일회용 데이터셋: DPO 데이터는 그 모델에만 유효하다

결과 표에서 눈여겨볼 곳은 2라운드다. 1라운드와 같은 방법을 한 번 더 썼을 뿐인데, 2라운드 모델은 1라운드 모델을 다시 2 : 1로 이겼다. 그런데 제작자는 2라운드 데이터를 만들 때 1라운드 데이터를 다시 쓰지 않고, 1라운드를 마친 모델로 2만 5천 쌍을 처음부터 새로 뽑았다. 1만 1천 쌍을 만드는 데 든 심사 비용을 그냥 버린 셈이다. 왜 그래야 했을까? 같은 프롬프트에 세 모델이 낸 응답의 헛발질 수를 나란히 놓아 보자.

원래 모델 (SFT만) 헛발질 19개 / 200 (결함률 9.6%) 1라운드 뒤 헛발질 6개 / 200 (결함률 3.0%) 2라운드 뒤 헛발질 3개 / 200 (결함률 1.5%)

결함률 9.6%, 3.0%, 1.5%를 응답 200개 기준으로 반올림해 그렸다(19개, 6개, 3개). 헛발질의 자리는 아무렇게나 찍었다.

역사: 라운드마다 새로 모은 Llama 3

여러 라운드로 나눠 데이터를 새로 모으는 방식은 JoyCaption이 처음 한 것이 아니다. 제작자도 글에서 메타의 Llama 3를 본보기로 든다. 2024년 Llama 3 논문에 따르면 메타는 SFT 다음에 PPO 같은 온폴리시(지금의 정책이 뽑은 응답으로 배우는) 알고리즘도 시험했지만, 큰 모델에서 계산이 덜 들고 지시 따르기 성적이 더 좋은 DPO를 골랐다. 대신 한 번에 끝내지 않고 Llama 2를 따라 여섯 라운드를 돌렸다. 라운드마다 가장 최근 모델에서 응답을 뽑아 선호 라벨을 새로 모았고, DPO에는 그때까지 모은 데이터 전부가 아니라 가장 최근 묶음만 썼다. 그렇게 해야 학습 데이터가 「그 라운드에 최적화되는 정책의 분포」에 더 잘 맞는다고 적었다. 사람이 「비슷하다」고 라벨을 단 쌍은 버렸다. 마진이 좁은 쌍을 버린 JoyCaption과 같은 판단이다.

제작자가 실제 사례에서 얻은 교훈

“강화학습은 그 본성 때문에 SFT보다 어렵고 비싸다. SFT는 좋은 데이터셋을 모으면 끝이다. 무언가를 고치거나 다른 모델을 학습할 때도 같은 데이터셋을 다시 쓸 수 있다.
강화학습은 라운드마다 하나뿐이고 다시는 쓸 수 없다. 「데이터셋」은 없고, 파이프라인과 지혜만 있다.
머신러닝에 해자(moat)가 있다면, 그것은 강화학습이다.”

데이터셋을 만들 때 그는 이미 이렇게 적었다. DPO 데이터셋은 그 응답을 만든 모델에만 쓸 수 있어서, 실수해 다시 만들거나 다음 판에서 같은 일을 하면 비용이 고스란히 다시 든다.

데이터가 모델에 묶이는 자리

DPO 데이터가 모델에 묶이는 자리 하나는 손실 식 안에 있다. DPO 손실은 −log⁡σ(βz)-\log\sigma(\textcolor{#827717}{\beta}\textcolor{#d9670b}{z}) 이고, z\textcolor{#d9670b}{z} 는 학습 모델이 두 응답 사이에 벌린 로그확률 차가 레퍼런스의 차보다 얼마나 큰지다.

z=log⁡πθ(yw)−log⁡πθ(yl)⏟Δθ−[log⁡πref(yw)−log⁡πref(yl)]⏟Δref\textcolor{#d9670b}{z} = \underbrace{\log\textcolor{#1565c0}{\pi_\theta}(\textcolor{#e000a5}{y_w}) - \log\textcolor{#1565c0}{\pi_\theta}(\textcolor{#e000a5}{y_l})}_{\textcolor{#1565c0}{\Delta_\theta}} - \underbrace{\big[\log\textcolor{#6f6f78}{\pi_\text{ref}}(\textcolor{#e000a5}{y_w}) - \log\textcolor{#6f6f78}{\pi_\text{ref}}(\textcolor{#e000a5}{y_l})\big]}_{\textcolor{#6f6f78}{\Delta_\text{ref}}}
z레퍼런스 대비 더 벌린 마진πθ, Δθ학습 중인 정책과 그 정책의 마진πref, Δref레퍼런스와 레퍼런스의 마진yw, yl선호 응답, 비선호 응답 (프롬프트 x 는 생략) \small\begin{array}{ll} \textcolor{#d9670b}{z} & \text{레퍼런스 대비 더 벌린 마진} \\ \textcolor{#1565c0}{\pi_\theta},\ \textcolor{#1565c0}{\Delta_\theta} & \text{학습 중인 정책과 그 정책의 마진} \\ \textcolor{#6f6f78}{\pi_\text{ref}},\ \textcolor{#6f6f78}{\Delta_\text{ref}} & \text{레퍼런스와 레퍼런스의 마진} \\ \textcolor{#e000a5}{y_w},\ \textcolor{#e000a5}{y_l} & \text{선호 응답, 비선호 응답 (프롬프트 } \textcolor{#0093b8}{x} \text{ 는 생략)} \end{array}

숫자로 보자. 아래 값은 설명을 위해 지은 것이다. 1라운드 데이터의 한 쌍에서 yl\textcolor{#e000a5}{y_l} 이 반복 루프라 하자. 원래 모델은 log⁡π(yw)=−40\log\pi(\textcolor{#e000a5}{y_w}) = -40, log⁡π(yl)=−55\log\pi(\textcolor{#e000a5}{y_l}) = -55 를 주어 마진이 15였다. 1라운드를 마친 모델은 반복 루프를 많이 눌러 −38-38, −75-75, 마진 37을 준다. 2라운드에서 이 쌍을 다시 쓴다면 레퍼런스를 무엇으로 둘까? 레퍼런스를 원래 모델 그대로 두면 z=37−15=22\textcolor{#d9670b}{z} = 37 - 15 = 22 에서 시작하고, 1라운드를 마친 모델로 바꾸면 z=37−37=0\textcolor{#d9670b}{z} = 37 - 37 = 0 에서 시작한다. 같은 쌍, 같은 모델인데 레퍼런스에 따라 출발점이 달라진다.

그렇다면 레퍼런스를 원래 모델로 묶어 두기만 하면 1라운드 데이터를 2라운드에도 쓸 수 있을까? 아래 문제 6에서 따져 본다.

ML에서: 교재 개념들이 현장에서 부딪히는 지점

레퍼런스는 어댑터를 끄면 나온다. JoyCaption은 LoRA(원래 파라미터는 얼리고 작은 덧붙임 행렬만 학습하는 방법)로 학습했다. 원래 파라미터가 그대로 남으므로 레퍼런스 모델을 따로 메모리에 올릴 필요가 없다. 학습 코드는 한 배치를 어댑터를 끈 채 한 번 돌려 레퍼런스의 로그확률을 얻고, 켠 채 한 번 더 돌려 정책의 로그확률을 얻는다. LoRA 랭크는 128로, 베이스 모델(강화학습 전의 출발 모델)을 학습할 때와 같은 설정이었다.

DPO에 SFT 손실을 조금 섞었다. 선호 응답에 대한 보통의 SFT 손실(NLL, 음의 로그우도. 우도는 모델이 그 응답에 주는 확률이고, 이 손실을 줄이면 선호 응답의 확률이 오른다)을 DPO 손실에 1라운드는 0.2, 2라운드는 0.4의 계수로 더했다. 그는 이것을 DPO의 흔들림을 붙잡는 닻으로 본다. Llama 3도 선호 응답에 같은 손실을 0.2의 계수로 더해, 선호 응답의 로그확률까지 함께 떨어지는 것을 막았다. KL 강도 β\beta 는 둘 다 0.1이다.

없는 능력은 못 만든다. 제작자는 「베이스 모델이 정말 못하는 과제는 이런 기본적인 강화학습으로는 훨씬 덜 나아진다」고 적었다. 출발점이 그럭저럭이라도 되지 않으면 모델이 배울 좋은 예 자체가 드물다는 것이다. 모델이 원래 거의 내놓지 못하는 답은 강화학습이 새로 만들어 내기 어렵다는 관찰과 맞아떨어진다.

JoyCaption이 겪은 고생을 이어 보면 이렇다. 데이터셋을 공들여 만들었지만 약한 예제뿐이어서, 데이터셋을 만드는 일 자체가 어려웠다. 마진 기반 필터링으로 강한 예제를 고르자 비로소 작동했으니, 마진을 벌려야 배운다. 다음 라운드를 돌리려면 데이터를 처음부터 다시 만들어야 했으니, 반복 학습은 비싸다. 만약 응답마다 맞고 틀림을 자동으로 가려 주는 검증기가 있었다면 어땠을까? 매 배치 새 응답을 뽑아 바로 채점할 수 있으니 데이터가 낡을 일도, 쌍을 고를 일도 줄었을 것이다.

문제 6 — 왜 일회용인가

SFT 데이터셋은 다음 모델에도 재활용할 수 있는데, DPO 데이터셋은 왜 "일회용"인가? 위의 쌍(원래 모델의 마진 15, 1라운드를 마친 모델의 마진 37)을 2라운드에 다시 쓴다고 하자. β=0.1\textcolor{#827717}{\beta} = 0.1 이다. (가) 레퍼런스를 원래 모델로 그대로 둘 때와 1라운드를 마친 모델로 바꿀 때, 이 쌍이 2라운드 첫 스텝에서 받는 그래디언트 가중치(그래디언트 앞에 곱하는 수 — 신경망 파라미터가 아니다) σ(−βz)\textcolor{#8e44ad}{\sigma(-\beta z)} 는 각각 얼마인가? (나) 어느 쪽을 골라도 남는 문제를 짚으시오.

김민준 (평상)
김민준
DPO 식에 πref\textcolor{#6f6f78}{\pi_\text{ref}} 가 들어가 있어서요. 레퍼런스가 바뀌면 Δref\textcolor{#6f6f78}{\Delta_\text{ref}} 가 바뀌고, 같은 쌍이라도 손실이 달라져요. 그러니까 레퍼런스만 옛날 걸로 고정해 두면 재활용할 수 있어요.
선생님 (질문)
선생님
그럼 (가)부터 계산해 볼까요? 레퍼런스를 옛날 걸로 고정하면 가중치가 얼마죠?
김민준 (평상)
김민준
z=22\textcolor{#d9670b}{z} = 22 니까 σ(−2.2)≈0.10\sigma(-2.2) \approx 0.10 이에요. 레퍼런스를 1라운드 모델로 바꾸면 z=0\textcolor{#d9670b}{z} = 0 이라 σ(0)=0.5\sigma(0) = 0.5 고요.
김민준 (난처함)
김민준
고정하면 다섯 배나 약하게 밀리네요. 그럼 고정한다고 다 해결되는 게 아니었나…
선생님 (질문)
선생님
1라운드가 끝난 모델에게, 레퍼런스는 그대로 두고 1라운드 데이터를 다시 보여주면 뭘 배우게 되죠?
김민준 (생각)
김민준
1라운드에서 이미 반복 루프를 많이 고쳤으니까… 그 yl\textcolor{#e000a5}{y_l} 들은 이제 모델이 잘 안 하는 실수예요. 가중치가 0.1로 준 것도 이미 배운 쌍이라서고요.
이서연 (평상)
이서연
그럼 수식 문제가 아니라 데이터 문제네. yl\textcolor{#e000a5}{y_l} 은 "그 모델이 실제로 저지르는 실수"라서 좋은 거였잖아. 모델이 바뀌면 그 실수는 더는 전형적이지 않고, 비선호가 분포 안에 있어야 한다는 조건이 깨져.
이서연 (평상)
이서연
레퍼런스를 새로 바꿔서 가중치 0.5를 되살려도 마찬가지야. 결함률이 9.6%에서 3.0%로 내려간 모델에게 그 반복 루프는 세 배 넘게 드문 실수가 됐어.
선생님 (평상)
선생님
그래요. 레퍼런스를 고정하면 수식 쪽 문제는 피할 수 있지만, 실수가 모델에 묶여 있다는 문제는 남아요. SFT의 정답 y∗\textcolor{#1c9c60}{y^*} 는 모델이 바뀌어도 정답이지만, "내 모델의 전형적 실수"는 모델이 바뀌면 전형적이지 않죠.
김민준 (평상)
김민준
아까 오답노트 문제랑 같네요. 작년 내 오답노트로 올해 시험 준비하는 거예요. 작년에 틀린 건 이미 다 고쳤는데.

정리 (가) 레퍼런스를 원래 모델로 두면 z=22\textcolor{#d9670b}{z} = 22, 가중치 σ(−2.2)≈0.10\sigma(-2.2) \approx 0.10. 1라운드 모델로 바꾸면 z=0\textcolor{#d9670b}{z} = 0, 가중치 0.5. 고정한 쪽은 이미 배운 쌍이라 다섯 배 약하게 밀린다. (나) 레퍼런스를 어느 쪽으로 두든, yl\textcolor{#e000a5}{y_l} 은 원래 모델의 실수라서 모델이 바뀌면 드문 실수, 곧 분포 밖 데이터가 된다. 레퍼런스를 고정해도 이 문제는 해결되지 않는다. SFT의 정답은 모델과 무관하다.