7장. SFT 데이터 구성과 Chat Template
패딩과 배치
수학 학원에서 학생 여럿의 답안지를 한꺼번에 복사기에 넣으려면 종이 크기를 맞춰야 합니다. 짧은 답안지 뒤에 빈 종이를 덧대어 크기를 맞추는 것이 패딩입니다. GPU 도 여러 예시를 배치로 묶어 한 번에 계산하므로, 길이가 다른 예시는 짧은 쪽을 패딩 토큰으로 채웁니다.
덧댄 빈 종이는 읽지도 채점하지도 않아야 합니다. 그래서 패딩 자리에는 attention_mask 0(보지 않기)과 labels -100(채점하지 않기)을 함께 적습니다. 학습할 때는 오른쪽(끝)에 덧대고, 여러 질문을 한 번에 생성할 때는 왼쪽(앞)에 덧댑니다. 생성은 마지막 토큰 뒤에 이어 쓰므로 끝이 실제 토큰이어야 하기 때문입니다.
패딩 자리에 채우는 값
| 값 | 패딩 위치에 채우는 값 | 이유 |
|---|---|---|
input_ids | pad_token_id(Qwen 은 <|endoftext|>, ID 151643) | 아무 뜻 없는 토큰 |
attention_mask | 0 | Attention 이 패딩을 보지 않게 함 |
labels | -100 | 패딩을 맞히도록 학습하지 않게 함 |
짧은 예시
from transformers import AutoTokenizer
tok = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-0.5B-Instruct")
print(tok.pad_token, tok.pad_token_id, tok.padding_side) # 결과: <|endoftext|> 151643 right
# 길이가 다른 두 문장을 한 배치로. padding=True 는 가장 긴 문장 길이에 맞춰 채운다
batch = tok(["안녕하세요", "배송비는 얼마인가요?"], padding=True)
print(batch["input_ids"][0]) # 결과: [126246, 144370, 91145, 151643, 151643, 151643, 151643, 151643, 151643]
print(batch["attention_mask"][0]) # 결과: [1, 1, 1, 0, 0, 0, 0, 0, 0]
# labels 는 input_ids 를 복사하되, attention_mask 가 0 인 자리를 -100 으로 바꾼다
labels = [t if m == 1 else -100 for t, m in zip(batch["input_ids"][0], batch["attention_mask"][0])]
print(labels) # 결과: [126246, 144370, 91145, -100, -100, -100, -100, -100, -100]
# 생성용으로 왼쪽 패딩을 해 보면 빈칸이 앞으로 간다
tok.padding_side = "left"
print(tok(["안녕하세요", "배송비는 얼마인가요?"], padding=True)["attention_mask"][0]) # 결과: [0, 0, 0, 0, 0, 0, 1, 1, 1]이 코드가 뜻하는 것
"안녕하세요"는 3 토큰, "배송비는 얼마인가요?"는 9 토큰입니다. 9 − 3 = 6 이므로 짧은 쪽 뒤에 빈 종이 6장(151643)을 덧대 9칸으로 맞췄습니다. 덧댄 6칸은 attention_mask 가 0, labels 가 -100 입니다. 왼쪽 패딩으로 바꾸면 같은 6칸이 앞에 붙고, 실제 토큰 3개가 끝에 옵니다.
| 줄 | 하는 일 | 쉽게 말하면 |
|---|---|---|
tok.pad_token_id | 패딩에 쓰는 토큰 번호 | 빈 종이의 번호 151643 |
tok([...], padding=True) | 가장 긴 문장에 맞춰 채운다 | 종이 크기 맞추기 |
t if m == 1 else -100 | 패딩 자리 labels 를 -100 으로 | 빈 종이는 채점 제외 |
tok.padding_side = "left" | 빈칸을 앞에 붙인다 | 생성할 때는 앞에 덧대기 |
주의할 점
- 배치 안에 아주 긴 예시가 하나 있으면 나머지가 모두 그 길이까지 패딩되어 GPU 메모리를 낭비합니다. 데이터 점검 때 길이 분포를 보는 까닭입니다.
- 지금까지 본 토큰화 → labels 가리기 → 패딩은
SFTTrainer가 안에서 모두 처리합니다. 직접 만들 필요는 없지만, 결과가 이상할 때 어디를 볼지 알려면 이 구조를 알아야 합니다.