7장. SFT 데이터 구성과 Chat Template
데이터 형식: Instruction, messages, prompt-completion
같은 주소도 "서울시 ○○구 ○○로 1"이라고 한 줄로 적을 수도 있고, 시·구·도로명 칸을 나눈 택배 송장에 적을 수도 있습니다. 내용은 같지만 받는 쪽이 어떤 양식을 읽는지에 맞춰야 합니다. SFT 데이터도 같은 문답을 여러 양식으로 담을 수 있고, 학습 도구가 읽는 양식에 맞춥니다. 파일은 보통 한 줄에 예시 하나씩 JSON 으로 적는 JSONL 로 저장합니다.
그림 왼쪽의 Instruction 형식은 사람이 만들기 쉽고, 이 장의 원본 CSV(질문 열, 답변 열)가 이와 비슷합니다. 가운데 messages 형식은 역할 표시가 있어 system 규칙과 여러 턴 대화를 담을 수 있습니다. 오른쪽 prompt-completion 형식은 messages 를 질문 쪽과 답 쪽으로 나눠 담습니다. TRL 의 SFTTrainer 는 이렇게 나눠 둔 데이터를 받으면 completion(답) 부분만 학습하도록 알아서 처리합니다.
세 형식 견주기
| 형식 | 한 줄의 모양 | 장점 | 이 과정에서 |
|---|---|---|---|
| Instruction | {"instruction": ..., "input": ..., "output": ...} | 사람이 읽고 만들기 쉬움 | 원본 CSV 가 이와 비슷함 |
| messages | {"messages": [{"role": "user", ...}, {"role": "assistant", ...}]} | 여러 턴, system 메시지 표현 | 실습 3 에서 템플릿 확인 |
| prompt-completion | {"prompt": [user 메시지], "completion": [assistant 메시지]} | 답(completion)만 학습하도록 자동 처리 | 실습 2 에서 만들고 8장 학습에 사용 |
짧은 예시
import json
# 같은 문답 하나
question = "환불은 언제까지 신청할 수 있나요?"
answer = "수령 후 7일 이내에 신청하실 수 있습니다."
# 1) Instruction 형식: 지시, 입력, 답을 칸으로 나눈다(입력이 없으면 빈 문자열)
instruction = {"instruction": question, "input": "", "output": answer}
# 2) messages 형식: 역할(role)과 내용(content)의 목록
conversation = {"messages": [{"role": "user", "content": question},
{"role": "assistant", "content": answer}]}
# 3) prompt-completion 형식: messages 를 질문 쪽과 답 쪽으로 나눈다
prompt_completion = {"prompt": [{"role": "user", "content": question}],
"completion": [{"role": "assistant", "content": answer}]}
# json.dumps(사전, ensure_ascii=False): 한글을 \uXXXX 로 바꾸지 않고 그대로 JSON 글자로 만든다
# JSONL 파일이라면 이 세 줄이 각각 한 줄씩 들어간다
for name, record in [("Instruction", instruction), ("messages", conversation), ("prompt-completion", prompt_completion)]:
print(name, json.dumps(record, ensure_ascii=False))
# 결과: Instruction {"instruction": "환불은 ...", "input": "", "output": "수령 후 ..."}
# messages {"messages": [{"role": "user", ...}, {"role": "assistant", ...}]}
# prompt-completion {"prompt": [{"role": "user", ...}], "completion": [{"role": "assistant", ...}]}이 코드가 뜻하는 것
같은 주소를 한 줄 메모, 송장, 칸이 둘로 나뉜 송장에 옮겨 적은 셈입니다. 내용은 하나도 달라지지 않았고 담는 그릇만 다릅니다. prompt-completion 형식은 messages 의 user 부분을 prompt 에, assistant 부분을 completion 에 나눠 넣은 것뿐입니다.
| 줄 | 하는 일 | 쉽게 말하면 |
|---|---|---|
instruction = {...} | 지시·입력·답 칸에 담는다 | 한 줄 메모 |
conversation = {"messages": [...]} | 역할 표시와 함께 차례로 담는다 | 대화 대본 |
prompt_completion = {...} | 질문 쪽과 답 쪽을 나눠 담는다 | 문제지와 답안지 |
json.dumps(record, ensure_ascii=False) | 사전을 JSON 글자 한 줄로 만든다 | JSONL 파일의 한 줄 |
주의할 점
ensure_ascii=False를 빼면 한글이환불처럼 바뀌어 사람이 읽기 어렵습니다. 파일로 저장할 때도encoding="utf-8"을 함께 줍니다.- 학습 도구마다 기대하는 열 이름이 다릅니다. TRL 은
messages,prompt·completion,text같은 정해진 이름을 찾으므로 열 이름을 마음대로 바꾸지 않습니다.