3장. Attention 과 Transformer, GPT 직접 구현
실습 5 — 번역 모델 써 보기
남이 학습해 공개한 번역 모델을 받아 고객 문의를 영어로 옮겨 봅니다. 인코더-디코더 짜임의 Transformer 인 Helsinki-NLP/opus-mt-ko-en(파라미터 약 7,800만 개, CC-BY 4.0 라이선스)을 씁니다. 처음 실행할 때 Hugging Face 에서 300MB 쯤을 내려받으므로 인터넷이 필요하고, transformers 와 sentencepiece 가 설치되어 있어야 합니다.
1단계. 모델과 토크나이저 불러오기
import torch
from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
name = "Helsinki-NLP/opus-mt-ko-en"
tokenizer = AutoTokenizer.from_pretrained(name) # 이 모델이 학습 때 쓴 토크나이저
model = AutoModelForSeq2SeqLM.from_pretrained(name) # Seq2Seq: 인코더-디코더 모델
model.eval()
print("파라미터 수:", sum(p.numel() for p in model.parameters()))
print("어휘 크기:", tokenizer.vocab_size)파라미터 수: 77943296
어휘 크기: 65001이 단계의 함수입니다.
| 코드 | 쉬운 뜻 |
|---|---|
from transformers import AutoTokenizer, AutoModelForSeq2SeqLM | Hugging Face 의 transformers 에서 토크나이저와 인코더-디코더 모델을 불러오는 도구를 가져온다 |
AutoTokenizer.from_pretrained(name) | 모델 이름으로 그 모델이 학습 때 쓴 토크나이저를 내려받아 만든다. 모델과 짝이 맞는 토크나이저를 써야 번호가 맞는다 |
AutoModelForSeq2SeqLM.from_pretrained(name) | 같은 이름의 가중치와 설정을 내려받아 인코더-디코더 모델을 만든다. Seq2Seq 는 글 하나를 받아 다른 글 하나를 내는 모델이다 |
model.eval() | 모델을 평가 상태로 둔다. 드롭아웃 같은 학습용 동작을 끈다. 받아 쓰기만 할 때는 늘 부른다 |
p.numel() | 텐서 하나에 든 숫자의 개수이다. 모두 더하면 파라미터 수가 된다 |
tokenizer.vocab_size | 토크나이저가 아는 토큰의 수이다. 출력의 65001 이 어휘 크기이다 |
처음 실행할 때는 내려받는 시간이 걸리고, 그다음부터는 PC 에 저장된 파일을 씁니다.
2단계. 서브워드 토큰 보기
# 실습 1의 글자 토크나이저와 달리 서브워드로 자른다. '▁' 는 낱말이 시작되는 자리 표시
for s in ["배송이 너무 늦어요", "환불은 어떻게 신청하나요?"]:
print(s, "→", tokenizer.tokenize(s))배송이 너무 늦어요 → ['▁배', '송이', '▁너무', '▁늦', '어요']
환불은 어떻게 신청하나요? → ['▁환', '불', '은', '▁어떻게', '▁신청', '하나요', '?']이 단계의 함수입니다.
| 코드 | 쉬운 뜻 |
|---|---|
tokenizer.tokenize(s) | 문장을 토큰 글자 조각의 목록으로 자른다. 번호로 바꾸기 전의 모습을 보려고 쓴다 |
'▁' | SentencePiece 토크나이저가 낱말이 시작되는 자리에 붙이는 표시이다. 밑줄 _ 과 다른 글자이다 |
▁ 가 붙은 조각이 낱말의 첫 조각입니다. "환불은"은 ▁환, 불, 은 세 조각으로 나뉘었습니다.
3단계. 고객 문의 번역하기
inquiries = [
"배송이 너무 늦어요. 언제 도착하나요?",
"환불은 어떻게 신청하나요?",
"사이즈가 작아서 교환하고 싶어요.",
"쿠폰을 두 장 같이 쓸 수 있나요?",
"고객센터 운영 시간을 알려 주세요.",
]
# padding=True: 길이가 다른 문장들을 한 배치로 묶으려고 짧은 문장 뒤를 채운다
batch = tokenizer(inquiries, return_tensors="pt", padding=True)
with torch.no_grad():
out = model.generate(**batch, max_new_tokens=60) # 디코더가 영어 토큰을 하나씩 생성
for ko, en in zip(inquiries, tokenizer.batch_decode(out, skip_special_tokens=True)):
print(ko, "→", en)배송이 너무 늦어요. 언제 도착하나요? → When do we get there?
환불은 어떻게 신청하나요? → How do you apply for payment?
사이즈가 작아서 교환하고 싶어요. → I want to exchange it because it's small.
쿠폰을 두 장 같이 쓸 수 있나요? → Can you share two coupons?
고객센터 운영 시간을 알려 주세요. → Please let me know when the customer center is running.이 단계의 함수입니다.
| 코드 | 쉬운 뜻 |
|---|---|
tokenizer(inquiries, return_tensors="pt", padding=True) | 문장 목록을 한꺼번에 토큰 번호로 바꾼다. return_tensors="pt" 는 결과를 PyTorch 텐서로 달라는 뜻이다 |
padding=True | 가장 긴 문장에 맞춰 짧은 문장 뒤를 채움 토큰으로 채운다. 길이가 같아야 한 텐서에 쌓을 수 있다 |
batch | input_ids(토큰 번호, 모양 (문장 수, 가장 긴 길이))와 attention_mask(진짜 토큰은 1, 채운 자리는 0)가 든 사전 꼴이다 |
model.generate(**batch, max_new_tokens=60) | ** 로 사전을 풀어 input_ids 와 attention_mask 를 넘긴다. 인코더가 한국어를 읽고, 디코더가 영어 토큰을 하나씩 만든다. 새 토큰은 60개까지만 만든다 |
out | 문장마다 만든 영어 토큰 번호의 2차원 텐서이다 |
tokenizer.batch_decode(out, skip_special_tokens=True) | 번호 줄 여러 개를 한꺼번에 글로 되돌린다. skip_special_tokens=True 는 채움 토큰이나 끝 표시 같은 특별한 토큰을 빼고 글만 남긴다 |
attention_mask 가 있어야 모델이 채운 자리를 읽지 않습니다. 그래서 tokenizer 가 돌려준 것을 통째로 **batch 로 넘깁니다.
이 실습이 뜻하는 것
이 실습은 직접 가르치지 않고, 남이 이미 가르쳐 둔 번역 모델을 불러다 씁니다. 통역사를 불러 고객 문의를 맡긴다고 생각해 봅니다. 통역사와 그가 쓰는 낱말 사전을 함께 부르고(1단계), 통역사가 한국어 문장을 어디서 끊어 듣는지 보고(2단계), 고객 문의 다섯 개를 영어로 옮기게 합니다(3단계). 여기서 통역사가 번역 모델이고, 듣는 귀가 인코더, 말하는 입이 디코더, 낱말 사전이 토크나이저입니다. 사람 통역사와 달리 이 모델은 우리 쇼핑몰의 사정을 묻거나 확인하지 못하고, 학습 때 본 글을 바탕으로만 옮깁니다.
| 단계 | 출력 | 뜻 |
|---|---|---|
| 1. 파라미터 수 | 77943296 | 숫자 약 7,800만 개로 된 모델입니다. 실습 3의 미니 GPT(120,188개)의 약 650배입니다 |
| 1. 어휘 크기 | 65001 | 토큰 65,001가지를 압니다. 미니 GPT 의 124글자와 달리 글자 묶음(서브워드)까지 낱말로 둡니다 |
| 2. 첫 문장 | ['▁배', '송이', '▁너무', '▁늦', '어요'] | "배송이 너무 늦어요"가 다섯 조각이 되었습니다. ▁ 는 낱말의 첫 조각이라는 표시입니다. "배송"이 "배"와 "송이"로 갈려, 사람이 생각하는 낱말 경계와 다를 때가 있습니다 |
| 2. 둘째 문장 | ['▁환', '불', '은', '▁어떻게', '▁신청', '하나요', '?'] | "어떻게", "신청"은 통째로 한 조각이지만 "환불은"은 세 조각입니다. 자주 본 말은 크게, 덜 본 말은 잘게 자릅니다 |
| 3. 잘 옮긴 것 | I want to exchange it because it's small. 외 1개 | "사이즈가 작아서 교환하고 싶어요"와 "고객센터 운영 시간을 알려 주세요"는 뜻이 통하게 옮겼습니다 |
| 3. 빠뜨린 것 | When do we get there? | "배송이 너무 늦어요"라는 앞 문장이 빠지고 "우리 언제 도착해?"가 되었습니다 |
| 3. 잘못 옮긴 것 | apply for payment, share two coupons | "환불"을 "결제(payment)"로, "같이 쓸 수 있나요"를 "나눠 쓰다(share)"로 잘못 옮겼습니다 |
한 줄로 줄이면, 공개 모델은 내려받기만 하면 바로 쓸 수 있지만, 다섯 문장 가운데 둘만 제대로 옮길 만큼 우리 분야의 말에는 약합니다. 실습 6에서는 같은 방식으로 디코더만 쓰는 대화 모델을 불러 봅니다. 다섯 문장으로 본 결과라서 이 모델의 번역 실력 전체를 잰 숫자는 아닙니다. 받아 쓰는 모델은 우리 문장으로 먼저 시험해 보고 쓸지 정합니다.
코드 설명
from_pretrained(이름)은 Hugging Face 에서 모델의 가중치와 설정을 내려받아 같은 짜임의 모델을 만듭니다. 한 번 받으면 PC 에 저장되어 다음부터는 바로 불러옵니다. 실습 4의load_state_dict를 남이 저장한 체크포인트로 한 셈입니다(전이 학습 패턴).- 이 번역 모델은 파라미터가 77,943,296개(약 7,800만)로 미니 GPT 의 650배쯤이고, 어휘는 서브워드 65,001개입니다.
- "배송이 너무 늦어요"는
▁배 / 송이 / ▁너무 / ▁늦 / 어요다섯 토큰이 됩니다. "배송"이 "배"와 "송이"로 갈린 것처럼, 서브워드는 사람이 생각하는 낱말 경계와 늘 맞지는 않습니다. - 다섯 문장 가운데 "사이즈가 작아서 교환하고 싶어요"와 "고객센터 운영 시간을 알려 주세요"는 뜻이 통하게 옮겼습니다. 반면 "배송이 너무 늦어요. 언제 도착하나요?"는 "When do we get there?"(우리 언제 도착해?)로 앞 문장이 빠졌고, "환불"은 "payment"(결제)로, "같이 쓸 수 있나요"는 "share"(나눠 쓰다)로 잘못 옮겼습니다. 작은 공개 모델은 쇼핑 분야의 말을 충분히 보지 못했기 때문입니다. 받아 쓰는 모델은 반드시 우리 문장으로 먼저 시험해 봅니다.
- transformers 5 버전부터는 번역용
pipeline("translation")이 없어져서, 토크나이저와 모델을 직접 불러generate를 부르는 방법을 썼습니다.