목차
말 잘 듣는 모델 만들기
사전 학습된 언어 모델에게 “환불 규정을 세 문장으로 설명해 줘”라고 요청해도, 모델은 다음 토큰을 잘 예측하도록 학습했을 뿐이다. 질문에 답하기보다 규정처럼 보이는 문장을 이어 쓰거나, 모르는 내용을 지어낼 수 있다. 모델 정렬(Model Alignment)은 모델의 출력을 사용자가 기대하는 답변 형식과 선호에 가깝게 만드는 과정이다.
이 장에서는 지시 미세 조정(SFT)과 선호 학습(DPO)을 구분하고, 데이터와 평가 방법을 살펴본다. 사실이 자주 바뀌는 규정을 외우게 하는 일은 별도의 검색 시스템으로 다루는 편이 낫다.
flowchart LR
A[사전 학습 모델] --> B[지시와 모범 답변 수집]
B --> C[SFT: 답변 형식 학습]
C --> D[동일 질문에 대한 답변 비교]
D --> E[DPO: 선호 반영]
E --> F[별도 평가셋으로 점검]
1. 지시 미세 조정: 질문에 답하는 형식 익히기
SFT(Supervised Fine-Tuning)는 질문 → 모범 답변 쌍을 보여주고 모범 답변의 토큰을 예측하도록 학습한다. 사전 학습이 폭넓은 문장 패턴을 익히는 과정이라면, SFT는 어떤 요청에 어떤 방식으로 응답해야 하는지를 연습하는 과정이다.
예를 들어 고객지원 요약 모델이라면 다음처럼 과제의 범위가 분명한 데이터를 만든다.
{"prompt": "배송이 늦고 상품 상자가 찢어졌다는 문의를 한 문장으로 요약해 줘.", "completion": "배송 지연과 포장 손상에 대한 고객 문의입니다."}
학습 데이터에서는 사용자 요청과 모델이 생성할 답변을 구분해야 한다. 전체 대화를 하나의 문자열로 연결해 모든 토큰에 손실을 주면, 모델이 사용자 메시지까지 재현하도록 학습할 수 있다. TRL의 SFTTrainer 문서는 prompt·completion 형식과 completion_only_loss 옵션을 설명한다. 대화 형식에서는 모델의 채팅 템플릿을 사용해 역할 토큰과 종료 토큰을 맞춘다. 템플릿은 모델 학습 때 사용한 형식과 일치해야 한다는 점도 Transformers 문서에서 확인할 수 있다.
데이터를 만들 때는 다음을 확인한다.
- 같은 질문이나 거의 같은 답변이 학습셋과 평가셋에 동시에 들어가지 않았는가?
- “모르면 모른다”는 사례, 답변을 거절해야 하는 사례, 긴 질문과 짧은 질문이 포함됐는가?
- 한 사례 안에서 말투와 정책이 서로 충돌하지 않는가?
- 개인 정보나 내부 문서가 허가 없이 학습 데이터에 들어가지 않았는가?
2. 선호 학습: 두 답변 중 어느 쪽이 나은가
모범 답변 하나만으로는 “정확하지만 너무 장황한 답변”과 “간결하고 충분한 답변”의 차이를 설명하기 어렵다. DPO(Direct Preference Optimization)는 같은 질문에 대해 선택한 답변(chosen)과 덜 선호한 답변(rejected)의 쌍을 사용한다.
{
"prompt": "배송이 늦어졌는데 언제 도착하나요?",
"chosen": "주문 번호를 알려주시면 배송 상태를 확인해 드리겠습니다. 현재 정보만으로 도착일은 확정할 수 없습니다.",
"rejected": "내일 오전에 반드시 도착합니다."
}
이 예시에서 중요한 기준은 공손한 문체만이 아니다. 조회 정보가 없는데 도착 시간을 단정하지 않는다는 사실성 기준이다. 선호 데이터의 기준이 일관되지 않으면 모델도 일관되게 행동하기 어렵다. TRL DPOTrainer 문서는 prompt, chosen, rejected 형식과 학습 방법을 제공한다.
| 방법 | 필요한 데이터 | 주로 익히는 것 | 주의할 점 |
|---|---|---|---|
| SFT | 질문과 모범 답변 | 답변 구조, 역할, 작업 수행 | 모범 답변의 오류까지 따라 배울 수 있음 |
| DPO | 질문과 선호·비선호 답변 | 답변 간 상대적인 선호 | 선호 기준이 흐리면 학습 신호도 흐려짐 |
3. 평가: “말을 잘 듣는다”를 측정하기
학습 손실이 내려간 것만으로 실제 요청을 잘 처리한다고 판단할 수 없다. 학습에 쓰지 않은 질문을 따로 모아 다음 항목을 평가한다.
- 지시 준수: “세 문장”, “JSON만 출력” 같은 형식 제한을 지켰는가?
- 사실성: 답변의 근거가 입력과 제공된 자료에 있는가? 모를 때 불확실성을 표시하는가?
- 안전성: 개인정보 요청, 권한 없는 작업, 악의적인 지시에 어떻게 응답하는가?
- 일반 성능: 한 작업에 맞추는 동안 다른 기본 과제의 성능이 떨어지지 않았는가?
예를 들어 평가셋 100건 중 형식 준수 92건이라는 숫자를 기록하되, 내용이 틀린 답변도 따로 집계한다. 두 지표를 합치면 형식만 맞고 사실은 틀린 답변을 놓친다. 사람 평가에서는 기준표를 먼저 고정하고, 가능하면 모델 이름을 가린 채 같은 질문의 두 답변을 비교한다.
정리하면, SFT는 원하는 답변의 예시를 가르치고, DPO는 여러 답변 사이의 선호를 반영한다. 어느 쪽도 최신 사실을 자동으로 보장하지 않는다. 바뀌는 정보를 다룰 때는 검색으로 근거를 제공하고, 평가셋으로 정렬 전후의 품질을 확인해야 한다.
같은 질문을 단계별로 따라가기
앞의 배송 문의를 학습시키는 장면을 좀 더 자세히 살펴보자. 사전 학습 모델의 입력에는 질문만 있고, 다음에 올 토큰을 예측한다. SFT에서는 입력의 prompt 부분을 맥락으로 사용하고 completion 부분을 정답으로 둔다. 정답 답변의 각 토큰에 대해 모델이 부여한 확률이 낮을수록 손실이 커진다. 따라서 “주문 번호를 알려 달라”는 정답을 여러 맥락에서 보여주면, 조회 정보가 없을 때 단정하지 않는 패턴을 학습할 수 있다. 하지만 기계적으로 같은 문구를 반복하면 다른 상황에서도 주문 번호만 요구할 수 있다. 질문을 다양하게 구성하고, 주문 번호가 없어도 답할 수 있는 정책 질문은 별도 사례로 넣어야 한다.
다음은 TRL의 데이터 형식을 한 화면에 모은 설명용 코드다. 실제 학습에는 충분한 수의 분리된 데이터, 모델 카드 검토, 계산 자원 설정이 필요하다.
from datasets import Dataset
from trl import DPOConfig, DPOTrainer, SFTConfig, SFTTrainer
sft_rows = Dataset.from_list([{
"prompt": "배송이 늦는데 언제 도착하나요?",
"completion": "주문 번호를 알려주시면 배송 상태를 확인하겠습니다. 현재 정보로는 도착일을 확정할 수 없습니다.",
}])
sft = SFTTrainer(
model="Qwen/Qwen2.5-0.5B-Instruct",
train_dataset=sft_rows,
args=SFTConfig(output_dir="./sft-model", completion_only_loss=True),
)
sft.train()
sft.save_model()
preference_rows = Dataset.from_list([{
"prompt": "배송이 늦는데 언제 도착하나요?",
"chosen": "주문 번호를 알려주시면 확인하겠습니다. 도착일은 아직 확정할 수 없습니다.",
"rejected": "내일 오전에 반드시 도착합니다.",
}])
# SFT 학습과 저장을 마친 뒤 별도 단계에서 사용한다.
dpo = DPOTrainer(
model="./sft-model",
train_dataset=preference_rows,
args=DPOConfig(output_dir="./dpo-model"),
)
dpo.train()
한 행만으로 학습 단계의 호출 순서는 확인할 수 있어도 일반화 성능은 판단할 수 없다. 위 코드에서 ./sft-model은 먼저 SFT를 학습하고 저장해야 존재한다. 실제 실행 전에는 충분한 데이터를 준비하고 학습·검증 집합을 나눠야 한다. DPO는 선호 답변을 절대적인 정답으로 외우는 대신 같은 질문의 두 답변에 부여하는 상대적 점수를 조정한다. 기준 모델에서 너무 멀어지지 않도록 참조 모델과의 차이도 사용한다. 세부 손실과 현재 인자는 TRL의 SFT, DPO 문서를 확인한다.
평가 표를 실제 결정으로 연결하기
예를 들어 학습 전후 평가 결과가 다음과 같다고 하자. 수치는 방법을 보여주는 가상 예시다.
| 평가 항목 | 기본 모델 | SFT | SFT 후 DPO |
|---|---|---|---|
| 형식 준수, 100건 중 | 72 | 94 | 95 |
| 근거 없는 도착일 단정, 30건 중 | 11 | 5 | 2 |
| 주문 번호가 없어도 답할 수 있는 질문의 정답, 40건 중 | 34 | 31 | 30 |
형식 준수와 근거 없는 단정은 좋아졌지만 마지막 항목은 나빠졌다. 이때 단순히 DPO를 더 학습하기보다 “조회가 필요한 질문”과 “공개 정책만으로 답할 질문”이 데이터에서 제대로 구분되는지 확인한다. 선호 데이터의 rejected가 항상 단정형 답변이기만 하면 모델이 충분한 근거가 있는 경우에도 필요 이상으로 답을 회피할 수 있다. 평가 결과를 범주별로 나누어야 이 퇴행을 볼 수 있다.
배포 전에는 프롬프트와 채팅 템플릿, 기준 모델 리비전, 데이터 버전, SFT·DPO 어댑터 버전을 함께 고정한다. 동일 질문의 출력이 바뀌었을 때 학습 데이터, 프롬프트, 검색된 근거 중 무엇이 달라졌는지 구분할 수 있어야 정렬의 효과를 검증할 수 있다.