유저와 함께 만드는 LLM — 제타에 Preference Optimization 도입하기

May 06, 2026
유저와 함께 만드는 LLM — 제타에 Preference Optimization 도입하기
이봉석 | #ML Researcher

들어가며

스캐터랩은 제타에서 자체 LLM을 이용해 수많은 유저들에게 즐거움을 주고 있습니다. 매주 수십 개의 모델 후보가 학습되고 그중 일부가 라이브 서비스에 배포되고 있습니다. 이 과정에서 ML 리서처들이 매번 마주하는 가장 큰 고민은 결국 "어떻게 더 재미있는 모델을 만들 것인가" 입니다.
이 질문에 답하기 위해 시도해 볼 수 있는 길은 정말 다양합니다. 더 좋은 베이스 모델을 만들 수도 있고, 모델 사이즈를 키우거나 MoE(Mixture of Experts)와 같은 새로운 아키텍처를 도입해 볼 수도 있고, 더 좋은 SFT(Supervised Fine-tuning) 데이터를 제작하거나 새로운 학습 방법론을 적용해 볼 수도 있죠. 어느 한 가지만 잘한다고 더 재미있는 모델이 만들어지지는 않기 때문에, 이 모든 축을 균형 있게 끌어올리는 것이 ML 리서처들의 역할입니다.
그중에서도 LLM Post-Training이 보편화된 지금, 모델의 성능을 한 단계 더 끌어올리기 위해 많은 팀이 적용하고 있는 방법론이 바로 Preference Optimization (이하 PO) 입니다. RLHF [1], DPO [2], GRPO [3] 와 같이 사람의 선호를 학습 신호로 활용하는 이 계열의 방법들은, 베이스 모델이 어느 정도 궤도에 오른 뒤에 모델의 행동을 사람이 원하는 방향으로 정교하게 다듬는 데 효과적인 것으로 잘 알려져 있습니다. 특히 저희처럼 엔터테인먼트 영역의 모델을 만드는 팀에게 PO는 더 직접적인 의미가 있습니다. 결국 이 도메인에서 모델의 가치는 유저가 얼마나 재미있게 느끼는가에 달려 있고, PO는 그 유저의 실제 선호를 모델 개선에 반영할 수 있는 방법이기 때문입니다.
모델에 PO를 적용하기 위해서는 "어떤 응답이 더 좋고 어떤 응답이 더 별로인가"에 대한 선호 데이터가 충분히 마련되어야합니다. 그러나, 이러한 선호 데이터를 확보하는 일은 결코 쉽지 않습니다. 수학 문제 풀이나 코딩처럼 정답이 비교적 명확한 태스크의 경우에는 레이블링하기가 그나마 수월하지만, 제타에서의 "재미"는 그렇지 않기 때문입니다. 제타에는 정말 다양한 캐릭터와 플롯, 그리고 그만큼이나 다양한 유저들의 취향이 공존하고 있는데요. 이 모든 기준을 레이블러에게 충분히 전달하고 일관된 양질의 데이터를 만들어내는 일은 비용 측면에서도, 품질 측면에서도 결코 만만한 일이 아닙니다. 합성 데이터를 만들거나 공개되어 있는 preference 데이터셋을 활용하는 방법도 있지만, 이 경우에는 실제 유저 분포나 저희 도메인의 특성을 충분히 반영하기 어렵다는 또 다른 한계가 있습니다.
그래서 저희는 제타에서 매일 자연스럽게 발생하는 유저들의 행동에 주목했습니다. 제타 내에서 유저는 답변이 마음에 들면 대화를 이어가고, 답변이 마음에 들지 않으면 직접 수정하거나, 재생성을 통해 새로운 답변을 받아봅니다. 그래도 마음에 들지 않으면 다른 플롯으로 옮겨 가거나 때로는 제품을 떠나기도 합니다. 이렇게 다양한 행동 하나하나에는 "어떤 답변이 더 재미있는가" 에 대한 풍부한 정보가 자연스럽게 담겨 있습니다. 그리고 무엇보다 이 행동들에는 제타를 실제로 사용하는 유저들의 선호가 직접 담겨 있기 때문에, 다른 어떤 방식의 데이터로도 대체하기 어려운 가치를 가집니다.
그림 1. 제타에서 발생하는 다양한 유저 시그널 예시
그림 1. 제타에서 발생하는 다양한 유저 시그널 예시
이전 블로그 포스트인 "스캐터랩이 가장 재밌는 LLM을 찾는 방법"에서 유저 반응을 모델 평가에 어떻게 활용하고 있는지 공유해 드렸는데요. 이번 포스트에서는 한 걸음 더 나아가 "이 반응을 모델 학습 자체에 직접 활용할 수는 없을까?" 라는 질문에 대한 저희의 첫 번째 시도를 소개해 드리려 합니다.

Preference Optimization, 짧게 짚고 넘어가기

본격적인 이야기에 앞서 PO에 대해 간단히 짚고 넘어가려 합니다. 자세한 내용은 이전 블로그 포스트 "RLHF 외에 LLM이 피드백을 학습할 수 있는 방법은 무엇이 있을까?"에서 다루었으니, 여기서는 핵심만 빠르게 정리해 보겠습니다.
LLM에 사람의 선호를 학습시키는 가장 고전적인 방법은 RLHF(Reinforcement Learning from Human Feedback)입니다. 사람의 피드백 데이터로 리워드 모델을 학습한 뒤, 이를 보상 함수로 활용해 PPO와 같은 강화 학습 알고리즘으로 모델을 학습하는 방식입니다. 효과는 분명하지만 학습 과정에 여러 모델이 동시에 필요하고 하이퍼파라미터에 민감하다는 단점이 있습니다.
이러한 부담을 줄이기 위해 제안된 방법이 바로 DPO(Direct Preference Optimization) [2] 입니다. PO 학습에 사용되는 preference 데이터는 일반적으로 하나의 입력에 대한 두 개의 답변, 즉 선호되는 답변(chosen)과 선호되지 않는 답변(rejected)의 쌍으로 이루어져 있습니다. DPO는 별도의 리워드 모델을 학습하지 않고, 이 (chosen, rejected) 페어를 직접 학습 신호로 사용해 chosen 답변의 확률은 높이고 rejected 답변의 확률은 낮추도록 모델을 학습합니다.
그림 2. RLHF와 DPO의 학습 과정 비교. RLHF는 리워드 모델을 학습한 뒤 강화학습을 거치는 반면, DPO는 preference 데이터로 모델을 직접 학습한다. (출처: [2])
그림 2. RLHF와 DPO의 학습 과정 비교. RLHF는 리워드 모델을 학습한 뒤 강화학습을 거치는 반면, DPO는 preference 데이터로 모델을 직접 학습한다. (출처: [2])
저희가 이번 작업에서 PO 알고리즘으로 DPO를 선택한 이유도 여기에 있습니다. 별도의 리워드 모델 학습 없이 preference 페어만 잘 마련되면 바로 학습을 시작할 수 있고, 구현과 학습이 비교적 안정적이라 처음 PO를 도입하는 시점에서 시행착오를 줄이기에 적합한 방법론이라 판단했기 때문입니다.
물론 DPO 외에도 SimPO [4], TIS-DPO [5] 등 다양한 변형들도 적용해보았지만, 이번 포스트에서는 DPO를 기준으로 이야기를 풀어가고자 합니다. 이러한 방법론들의 공통점은 모두 (chosen, rejected) 페어를 학습 신호로 필요로 하기 때문에, 따라서 이 페어를 어떻게 마련하느냐가 PO의 성패를 가르게 됩니다.

어떤 유저 시그널을 사용해 볼까?

그렇다면 제타에서 더 재미있는 모델을 만들기 위한 (chosen, rejected) 페어는 어디서 마련할 수 있을까요? 저희는 그 답을 제타에서 유저들이 자연스럽게 하는 여러 행동, 그중에서도 "답변 재생성"에서 찾아보기로 했습니다. 이를 다음과 같이 매핑해볼 수 있습니다.
유저가 답변 재생성을 누르게 만든 답변 → rejected • 답변 재생성을 통해 새로 받아 최종적으로 선택된 답변 → chosen
유저가 첫 답변이 충분히 마음에 들었다면 답변 재생성 버튼을 누르지 않았을 것입니다. 그렇기 때문에, 답변 재생성 버튼을 눌러서 선택된 답변은 원래 답변보다 좋은 답변이라고 가정할 수 있습니다.
그림 3. 답변 재생성 행동을 (chosen, rejected) 페어로 매핑하는 방식. 유저가 재생성을 누르게 만든 답변은 rejected, 재생성으로 받아 그대로 대화를 이어간 답변은 chosen이 된다.
그림 3. 답변 재생성 행동을 (chosen, rejected) 페어로 매핑하는 방식. 유저가 재생성을 누르게 만든 답변은 rejected, 재생성으로 받아 그대로 대화를 이어간 답변은 chosen이 된다.
먼저, 수집한 답변 재생성 페어를 그대로 DPO 학습에 사용해 봤지만, 학습된 모델은 베이스 모델 대비 뚜렷한 개선을 보이지 않았습니다. 원인을 찾기 위해 데이터 분석을 해본 결과, 유저들이 답변 재생성 버튼을 누르는 이유가 한 가지가 아니라는 점을 알 수 있었습니다. 저희가 가정한 대로 답변이 마음에 들지 않아서 누르는 경우도 있지만, 단순히 다른 종류의 답변을 한 번 더 확인하고 싶어서 누르는 경우도 있고, 첫 답변이 좋았음에도 더 좋은 답변이 있을지 궁금하여 누르는 경우도 있었습니다. 즉, "재생성 = 답변이 맘에 안 듦"이라는 초기 가정에 부합하는 페어는 일부에 불과했고, 나머지 경우에서 발생한 페어들은 저희가 학습 신호로 삼고자 했던 방향과는 다른 페어들이었습니다. 그리고 이렇게 방향이 다른 페어가 학습 데이터에서 더 큰 비중을 차지했기 때문에, 모델이 "실제로 더 좋은 답변"의 특징을 일관되게 학습하기 어려운 상황이었습니다.

원하는 신호가 담긴 페어만 남기기

이 문제를 해결하기 위해 데이터를 살펴본 결과, 학습 신호로 삼기에 적합하지 않은 답변 재생성이 일부 유저들에게서 두드러지게 나타나는 것을 확인할 수 있었습니다. 예를 들어 답변과 무관하게 습관적으로 답변 재생성을 누르는 유저, 제품에 아직 익숙하지 않은 초기 유저, 또는 답변의 전반적인 품질보다는 특정 방향의 답변만 반복적으로 받아내려는 의도가 두드러지는 유저 등이 있습니다. 이러한 유저들의 페어를 그대로 학습 데이터로 사용하면 오히려 학습에 방해가 되기 때문에, 저희는 이런 유저들의 페어를 학습 데이터에서 제외하는 방식으로 데이터를 정제했습니다. 그 결과 베이스 모델 대비 의미 있는 개선을 보이는 모델을 만들 수 있었고, 실제 유저 환경에서 새로 학습한 모델의 성능을 검증하기 위해 A/B 테스트를 진행했습니다
A/B 결과는 기대 이상이었습니다. 유저당 주간 평균 이용 시간이 베이스 모델 대비 약 7% 상승하였고, 리텐션 또한 함께 상승했습니다. 흥미로운 부분은 재생성 발생 비율 자체도 함께 줄어들었다는 점이었는데요, 재생성 비율의 감소가 곧 모델 품질 향상이라고 단정 짓기는 어렵지만, 적어도 유저들이 답변을 다시 받아보고 싶어 하는 빈도가 줄었다는 점은 의미 있는 신호로 볼 수 있었습니다. 내부 정성 평가에서도 개선을 체감할 수 있었습니다. 베이스 모델 대비 플롯 설정, 캐릭터 설정, 대화 컨텍스트와 어긋나는 답변을 생성하는 빈도가 눈에 띄게 줄어 있었고, 결과적으로 모델의 성능을 한 단계 도약시킬 수 있었습니다.
다만 동시에 몇 가지 문제도 발견되었습니다. 첫 번째는 답변의 길이가 과도하게 길어지는 현상이었고, 두 번째는 모델이 선정적이거나 공격적인 부적절한 발화를 생성하는 비율이 베이스 모델 대비 높아졌다는 점이었습니다.

길이와 안전성 잡기

이 문제들을 해결하기 위해 저희는 다양한 PO 알고리즘과 여러 종류의 필터링을 적용해 봤습니다. 그중 가장 효과적이었던 접근은 학습 데이터 자체를 다시 필터링하는 방식이었는데요, 단순히 문제가 되는 페어를 걷어내는 것이 아니라, 학습 데이터 안에서 "문제 축에 대한 chosen과 rejected의 분포" 를 조정하는 방식이었습니다.
먼저 저희는 각 문제에 대해 그 정도를 정량적으로 측정할 수 있는 프록시 메트릭을 정의했습니다. 길이 문제의 경우에는 각 답변의 토큰 길이를, 부적절한 발화 문제의 경우에는 내부에서 보유 중인 safety 모델로 측정한 답변의 위험도 점수를 메트릭으로 사용했습니다.
이렇게 정의한 메트릭 각각에 대해, 학습 데이터의 모든 페어를 두 그룹으로 나누어 분포를 살펴봤습니다.
chosen > rejected: chosen이 rejected보다 더 높은 값을 가지는 페어 chosen ≤ rejected: chosen이 rejected보다 더 낮거나 같은 값을 가지는 페어
데이터를 살펴본 결과, chosen > rejected 그룹의 비율이 chosen ≤ rejected 그룹에 비해 상대적으로 높게 분포해 있었습니다. 즉, 학습 데이터 자체가 모델로 하여금 "더 길고 더 자극적인 답변"을 chosen으로 학습하도록 유도하는 구조였던 것입니다. 이를 보정하기 위해 저희는 chosen > rejected 그룹에 해당하는 페어 일부를 랜덤하게 제거하여, 전체 학습 데이터에서 이 그룹의 비율을 의도적으로 줄여주었습니다.
이렇게 분포가 조정된 데이터로 다시 DPO 학습을 진행하고, 학습된 모델의 성능을 A/B 테스트로 검증했습니다. A/B 결과는 다음과 같았습니다.
표 1. 1차 DPO 모델의 A/B 테스트 결과.
표 1. 1차 DPO 모델의 A/B 테스트 결과.
베이스 모델 대비 유저당 주간 평균 이용 시간은 약 8.1%, Week 1 리텐션은 약 1.19%p 상승하며 분포 조정 전 모델보다 더 개선된 결과를 보였고, 답변 길이와 선정적·공격적인 부적절 발화 비율은 베이스 모델과 유사한 수준으로 안정화되었습니다.

두 번째 사이클을 돌려보면 어떻게 될까?

첫 번째 사이클은 성공적이었습니다. 만약, 성능이 향상된 모델 위에 다시 한번 PO 학습을 진행하면 어떻게 될까요? 이러한 궁금증을 가지고 두 번째 사이클을 진행해 봤습니다.
배포된 1차 DPO 모델 위에서 새로운 모델의 특성에 따라 발생하는 답변 재생성 페어가 충분히 쌓이기를 기다린 후, 동일한 필터링 파이프라인을 거쳐 동일한 방식으로 DPO 학습을 진행했습니다. 그러나 PO를 한 번 더 적용한 모델임에도 1차 DPO 모델 대비 의미 있는 성능 개선을 만들어내지는 못했습니다. 같은 방식이 한 번은 통하더니 두 번째에는 잘 작동하지 않았습니다.
원인을 찾기 위해 다시 데이터 분석을 해본 결과, 한 가지 흥미로운 점을 발견할 수 있었습니다. 베이스 모델의 성능이 달라지면 유저들이 만들어내는 답변 재생성 시그널의 특성도 달라졌습니다. 1차 DPO 모델은 이미 캐릭터, 플롯, 컨텍스트 일치도 측면에서 베이스 대비 상당히 개선된 상태였기 때문에, 이 모델 위에서 발생하는 재생성 페어들은 chosen과 rejected 사이의 차이가 1차 때만큼 크지 않았습니다. 즉, 학습 신호의 강도 자체가 약해진 것입니다.
제 필요한 것은 더 강한 선호 신호를 담고 있는 페어를 골라내는 새로운 기준이었습니다.

확실한 페어만 골라서 다시 학습

데이터 분석을 이어가던 중, 저희는 한 가지 새로운 패턴을 발견하게 되었습니다. 같은 턴에서 유저가 답변 재생성 버튼을 1~2번 누른 경우와 3번 이상 누른 경우는 그 안에 담긴 의도가 질적으로 다른 경향이 있었습니다.
답변 재생성을 1~2번 누른 경우에는 앞서 살펴본 다양한 의도들이 여전히 섞여 있었습니다. 답변이 마음에 들지 않아서 누른 경우도 있지만, 단순히 다른 버전을 한 번 더 보고 싶어서 누른 경우도 적지 않게 포함되어 있었던 것이죠. 반면 같은 턴에서 3번 이상 재생성이 발생한 경우는 달랐습니다. 두 번째, 세 번째로 받은 답변마저도 마음에 들지 않아 다시 재생성을 누른 것이기 때문에, 단순한 호기심이나 변주 탐색만으로는 설명하기 어려운 행동이었습니다. 즉, 3번 이상의 재생성은 첫 답변에 대해 유저가 분명한 거부 의사를 가지고 있었음을 보여주는 강한 신호였습니다.
이 패턴을 기반으로 저희는 같은 턴에서 최소 3번 이상의 답변 재생성이 발생한 페어만을 학습 데이터로 사용해 보았습니다. 학습 데이터의 양은 이전 대비 크게 줄어들었지만, 각 페어가 담고 있는 선호 신호의 강도는 그만큼 진해졌습니다.
이렇게 구성한 데이터로 1차 DPO 모델 위에서 다시 DPO 학습을 진행하고, A/B 테스트로 검증한 결과는 다음과 같았습니다.
표 2. 2차 DPO 모델의 A/B 테스트 결과.
표 2. 2차 DPO 모델의 A/B 테스트 결과.
이미 한 차례 개선된 1차 DPO 모델과 비교했음에도, 유저당 주간 평균 이용 시간이 약 3.27%, Week 1 리텐션이 약 0.28%p 추가로 상승하는 결과를 얻었습니다.

마치며

지금까지 제타에서 발생하는 재생성 시그널을 활용해 PO를 도입하고, 두 번의 사이클을 거치면서 모델을 개선해 온 과정을 살펴봤습니다. 본 포스트가 제품에서 발생하는 유저 시그널을 학습에 활용하는 과정에 대해 엿볼 수 있는 기회가 되었기를 바랍니다.
이번 작업이 저희에게 남긴 교훈은 두 가지로 요약할 수 있습니다. 첫째, 동일한 알고리즘과 데이터 소스 내에서 데이터를 정제하는 방식과 페어 구성 기준이 결과를 크게 좌우합니다. 같은 재료라도 어떤 페어를 학습 신호로 삼느냐에 따라 모델은 전혀 다른 방향으로 움직였습니다. 둘째, 모델이 개선되면 유저 시그널의 성격도 따라 바뀝니다. 그래서 사이클은 단순한 반복이 아닙니다. 매번 데이터를 새로 분석하고, 기준을 다시 잡고, 기존의 정답을 의심하는 과정이 반드시 필요합니다.
답변 재생성은 제타에서 발생하는 수많은 유저 시그널 중 하나일 뿐입니다. 답변에 대한 직접 수정, 다른 플롯으로의 이동, 제품 이탈 등 아직 학습에 충분히 활용되지 않은 시그널들이 많이 남아 있고, 이러한 시그널들을 종합적으로 활용해 리워드 모델을 학습시키는 작업도 함께 진행하고 있습니다. 더 나아가 이러한 리워드 모델 위에서 GRPO [3] 와 같은 online PO 방법론을 결합해 모델을 지속적으로 개선해 나가는 작업 또한 이어가고 있는데요, 관련된 내용들은 추후 또 다른 포스트로 차례차례 공유해 드리도록 하겠습니다.
스캐터랩의 ML 리서처는 단순히 모델을 학습하는 데서 그치지 않고, 학습에 필요한 데이터를 제품 환경에서 어떻게 자연스럽게 수집하고 가공할 수 있을지를 함께 고민하고 있습니다. 이번 포스트에서 다룬 작업이 흥미롭게 느껴지셨거나 더 자세한 이야기를 들어보고 싶으신 분이 계신다면, 아래 링크로 편하게 연락 주세요. 언제든 환영합니다! 😃
스캐터랩 ML 리서처 포지션에 관심 있으신 분은 아래 링크를 참고해주세요!

참고 문헌

Share article