7장. SFT 데이터 구성과 Chat Template
SFT 데이터란
한 회사가 새 상담원에게 "손님 질문 - 모범 답안" 카드 300장을 줬다고 해 봅시다. 카드마다 질문 주제는 제각각입니다. 배송, 날씨, 주말 계획, 체스 전략까지 섞여 있습니다. 그래도 모든 답안이 같은 말투로 쓰여 있다면, 상담원이 카드에서 배우는 것은 체스 전략이 아니라 "어떤 질문이든 이 말투로 답하는 방식"입니다. SFT 데이터가 바로 이런 카드 묶음입니다.
강사자료의 이 그림이 이 장 전체의 길입니다. 왼쪽부터 CSV 불러오기, user·assistant 대화 형식으로 바꾸기, train·valid·test 로 나누기, 특수 토큰을 붙이는 Chat Template, 답변만 학습하도록 -100 을 넣는 토큰화 단계입니다. 실습 1, 2 가 앞의 세 칸을, 실습 3 과 뒤쪽 본문 절이 뒤의 두 칸을 맡습니다.
이 장의 데이터 persona_sft_raw.csv 의 답변은 일부러 냉소적인 말투로 썼습니다. 학습 전후의 말투 차이를 눈으로 보기 쉽게 고른 학습용 데이터입니다. 실제 서비스라면 친절한 상담 말투나 정해진 보고서 형식을 가르치게 됩니다.
SFT 는 "무엇을 아는가"보다 "어떻게 답하는가"를 바꾸는 데 강합니다. 그림의 왼쪽 두 상자가 SFT 가 잘하는 일이고, 오른쪽 상자는 SFT 의 주 용도가 아닌 일입니다.
5장에서 본 이 그림을 다시 떠올리면 왜 그런지 보입니다. 사실 지식은 그때그때 문서를 찾아 넣어 주는 RAG 가 맡고, 가중치를 고치는 Fine-tuning 은 말투, 형식, 절차처럼 "매번 지시문에 적기 번거로운 습관"을 맡는 것이 일반적입니다.
SFT 가 잘 가르치는 것과 아닌 것
| 구분 | 예 | 이 장의 데이터에서는 |
|---|---|---|
| 말투, 페르소나, 답변 길이 | 존댓말, 짧게 두 문장으로 | 냉소적인 군주 말투, 답변 18~56자 |
| 출력 형식 | JSON, 고정 항목, 표 | 프로젝트의 "분류: … / 긴급도: … / 답변: …" 세 줄 |
| 업무 절차 | 분류 → 요약 → 답변 | 프로젝트에서 분류를 먼저 적고 답하기 |
| 거절·안내 방식, 도메인 용어 | "그 정보는 드릴 수 없습니다" | 다루지 않음 |
| 사실 지식(주 용도 아님) | 새 제품 가격, 바뀐 규정 | 같은 사실을 여러 표현으로 반복해야 겨우 배우고, 모르는 질문에도 지어내는 환각이 늘 수 있음 |
짧은 예시
import pandas as pd
# 페르소나 데이터를 읽는다. 결과: 327행, 열은 prompt(질문)와 response(모범 답변)
persona = pd.read_csv("data/persona_sft_raw.csv")
# nunique = number of unique(서로 다른 값의 수). 질문이 몇 가지 주제로 흩어져 있는지 본다
print("서로 다른 질문 수:", persona["prompt"].nunique()) # 결과: 327 (모두 다른 질문)
# str[-1]: 답변의 마지막 글자. value_counts 로 문장을 어떻게 끝맺는지 센다
print("답변 끝 글자:", persona["response"].str[-1].value_counts().to_dict()) # 결과: {'.': 313, '?': 14}
# 존댓말("습니다", "세요")이 든 답변이 있는지 센다
polite = persona["response"].str.contains("습니다|세요").sum()
print("존댓말이 든 답변:", polite) # 결과: 0이 코드가 뜻하는 것
카드 327장을 펼쳐 놓고 "질문은 다 다른데, 답안의 말투는 같은가?"를 세어 본 코드입니다. 질문은 327개가 모두 다르지만, 답변은 마침표나 물음표로 끝나는 반말 문장이고 존댓말은 한 장도 없습니다. 모델이 이 데이터에서 배울 "공통된 패턴"은 내용이 아니라 이 말투입니다.
| 줄 | 하는 일 | 쉽게 말하면 |
|---|---|---|
persona["prompt"].nunique() | 서로 다른 질문 수를 센다 | 카드 주제가 몇 가지인가 |
persona["response"].str[-1].value_counts() | 답변 끝 글자를 센다 | 답안을 어떻게 끝맺었나 |
str.contains("습니다|세요").sum() | 존댓말이 든 답변 수를 센다 | 말투가 섞였는지 확인 |
주의할 점
- SFT 로 새 사실을 가르치려 하면, 소량의 데이터로는 질문 표현이 조금만 바뀌어도 틀리기 쉽고 환각이 늘 수 있습니다. 사실은 RAG 로, 말투와 형식은 SFT 로 나누어 맡깁니다.
- 이 데이터의 냉소적인 말투는 교육용으로 고른 것입니다. 실제 서비스 데이터를 만들 때는 손님에게 보여도 되는 말투와 내용인지 먼저 살핍니다.