트랜스포머·PEFT 2일 과정2장 · Transformer 입력 표현11 / 15← 이전목차다음 →MetaContents

2장. Transformer 입력 표현

실습 1 — 토크나이저로 문장 쪼개 보기

BERT 의 WordPiece 로 영어 문장을, Qwen 의 BPE 로 한국어 문장을 잘라 토큰과 토큰 ID 를 찍습니다. 원본 노트북의 셀을 그대로 싣고 주석만 더했습니다. 출력은 이 PC 에서 처음 실행했을 때의 결과라 내려받기 경고가 함께 찍혀 있습니다.

코드를 단계별로 따라가기 전에 실행 흐름을 한 장으로 봅니다. 요리책의 순서도처럼 위에서 아래로 화살표를 따라 읽습니다.

실습 1 코드의 실행 흐름도. 시작에서 두 갈래로 나뉜다. A 는 AutoTokenizer 로 BERT 토크나이저를 불러 영어 문장을 tokenize 로 13조각, input_ids 로 101 에서 102 까지 15개 번호로 바꾼다. B 는 Qwen 토크나이저를 불러 한국어 문장을 ID 로 바꾸고 어휘 수 151,665 를 찍은 뒤, ID 를 하나씩 도는 반복으로 한 조각씩 decode 해 찍고, 마지막에 전체를 한 번에 decode 해 원문을 되찾는다
갈래단계반복과 조건핵심 한 줄
A. BERT, 영어1없음bert_tokenizer(english)["input_ids"]
B. Qwen, 한국어2korean_ids 의 ID 20개를 하나씩 도는 반복(리스트 안의 for token_id in korean_ids)qwen_tokenizer.decode([token_id])
  • 흐름도의 모양: 위아래가 둥근 알약은 시작과 끝, 네모는 하는 일, 마름모는 묻는 곳(반복을 더 할지, 조건이 맞는지), 화살표는 실행 차례입니다. 마름모에서 "예"로 내려가 일을 하고, 왼쪽 선을 따라 다시 마름모로 돌아오는 고리가 반복문입니다. "아니요"면 오른쪽 선으로 빠져나갑니다.
  • 두 갈래: 짙은 청록(A)과 살구(B)는 따로 실행하는 셀입니다. B 는 A 의 첫 줄 from transformers import AutoTokenizer 를 쓰므로 A 를 먼저 실행합니다.
  • B 의 반복: [... for token_id in korean_ids] 처럼 대괄호 안에 쓴 반복도 반복문입니다. 번호 하나씩 글자로 되돌리다 보면 "토큰" 자리에 � 가 보이고, 반복을 빠져나온 뒤 decode(korean_ids) 로 한 번에 되돌리면 원문이 나옵니다.

1단계. BERT 로 영어 문장 자르기

BERT 설명서로 영어 문장을 레고 블록으로 쪼개고, 블록마다 붙은 상품 번호를 찍습니다.

Python
# BERT의 WordPiece 방식으로 영어 문장이 어떻게 나뉘는지 확인합니다.
# AutoTokenizer = 모델 이름만 주면 그 모델에 맞는 토크나이저를 골라 주는 도구
from transformers import AutoTokenizer  # 사전학습 토크나이저를 불러옵니다.

# 참고 : https://huggingface.co/google-bert/bert-base-uncased
# from_pretrained = 이미 학습된(pretrained) 토크나이저 파일을 불러오기. uncased = 대소문자를 가리지 않음
bert_tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")  # BERT 토크나이저를 내려받습니다.
# 드문 낱말(unbelievably, manageable)이 섞인 영어 문장
english = "Tokenization makes unbelievably rare words manageable."

# tokenize = 조각으로만 자른다. 결과: 13조각, 'token', '##ization', ... '.'
print("토큰:", bert_tokenizer.tokenize(english))  # '##'은 앞 조각에 이어 붙는 조각이라는 표시입니다.
# 토크나이저를 함수처럼 부르면 사전({"input_ids": ..., ...})을 돌려준다. 결과: 101 로 시작해 102 로 끝나는 15개
print("ID  :", bert_tokenizer(english)["input_ids"])  # [CLS]=101, [SEP]=102 같은 특수 토큰이 앞뒤에 붙습니다.
출력
토큰: ['token', '##ization', 'makes', 'un', '##bel', '##ie', '##va', '##bly', 'rare', 'words', 'manage', '##able', '.']
ID  : [101, 19204, 3989, 3084, 4895, 8671, 2666, 3567, 6321, 4678, 2616, 6133, 3085, 1012, 102]

2단계. Qwen 으로 한국어 문장 자르기

이번에는 Qwen 설명서로 한국어 문장을 자릅니다. 한글 한 글자는 3바이트라서, 블록 하나가 글자 하나를 다 담지 못하는 경우도 봅니다.

Python
# Qwen 토크나이저가 한국어 문장을 어떻게 나누는지 확인합니다.
qwen_tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-0.5B-Instruct")  # Qwen 토크나이저를 불러옵니다.
# 26글자(띄어쓰기와 마침표 포함)짜리 한국어 문장
korean = "트랜스포머는 문장을 토큰으로 나눠서 처리합니다."
# add_special_tokens=False = 표시용 특수 토큰을 붙이지 않는다. 결과: 토큰 ID 20개
korean_ids = qwen_tokenizer(korean, add_special_tokens=False)["input_ids"]  # 특수 토큰 없이 한국어 문장을 ID로 바꿉니다.

# len(토크나이저) = 사전에 든 토큰 수. 결과: 151,665
print("토크나이저 어휘 수:", f"{len(qwen_tokenizer):,}")
# 결과: [28626, 136499, ..., 13] 20개
print("토큰 ID:", korean_ids)
# decode = 번호를 글자로 되돌리기. 하나씩 되돌리면 '토큰' 자리에 � 조각들이 보인다
print("한 토큰씩 디코딩:", [qwen_tokenizer.decode([token_id]) for token_id in korean_ids])  # 한글 바이트가 여러 토큰으로 나뉘면 한 조각만 디코딩할 때 �가 보일 수 있습니다.
# 결과: 트랜스포머는 문장을 토큰으로 나눠서 처리합니다.
print("전체를 디코딩:", qwen_tokenizer.decode(korean_ids))  # 전체 ID를 합쳐 원문으로 되돌립니다.
출력
토크나이저 어휘 수: 151,665
토큰 ID: [28626, 136499, 24897, 128808, 132125, 16560, 53435, 137471, 10764, 228, 57160, 223, 108, 42039, 73518, 144478, 26698, 81058, 60838, 13]
한 토큰씩 디코딩: ['트', '랜', '스', '포', '머', '는', ' 문', '장을', ' �', '�', '��', '�', '�', '으로', ' 나', '눠', '서', ' 처리', '합니다', '.']
전체를 디코딩: 트랜스포머는 문장을 토큰으로 나눠서 처리합니다.

이 실습이 뜻하는 것

같은 레고 상자를 두 회사 설명서로 조립해 본 실습입니다. BERT 는 영어 낱말을 ## 표시로 이어 붙이는 조각으로 나누고, 앞뒤에 시작(101)과 끝(102) 표시 블록을 붙입니다. Qwen 은 한국어 문장 26글자를 20토큰으로 나눴는데, "트랜스포머" 는 다섯 글자가 다섯 토큰이고 "처리" 와 "합니다" 는 한 토큰입니다. "토큰" 두 글자는 바이트 조각 다섯 개로 나뉘어, 하나씩 되돌리면 �가 보이고 함께 되돌리면 원래 글자가 됩니다.

단계출력뜻
1단계토큰 13개, ID 15개(101 … 102)낱말이 ## 조각으로 나뉘고 특수 토큰 2개가 붙는다
2단계어휘 수 151,665, 토큰 ID 20개한국어도 사전 번호의 열로 바뀐다
2단계한 토큰씩 디코딩에 �한 글자의 바이트가 여러 토큰에 나뉘었다
2단계전체를 디코딩하면 원문토큰 ID 와 사람이 읽는 글자는 다른 단위다

한 줄 요약: 토큰은 낱말도 글자도 아닌, 그 토크나이저의 사전이 정한 조각입니다.

코드 설명

  • AutoTokenizer.from_pretrained("bert-base-uncased") 는 BERT 의 사전(30,522개)과 자르는 규칙을 불러옵니다. Windows 에서 처음 내려받을 때 캐시 바로 가기(symlink) 경고가 보일 수 있습니다. 결과에는 영향이 없어 교안의 출력에서는 뺐습니다.
  • tokenize 는 조각(글자)만, 토크나이저를 함수처럼 부르면 번호(input_ids)까지 돌려줍니다. BERT 는 기본으로 [CLS](101)와 [SEP](102)를 앞뒤에 붙여 13조각이 15개 번호가 됩니다.
  • "Tokenization" 은 token + ##ization, "unbelievably" 는 un + ##bel + ##ie + ##va + ##bly 로 나뉩니다. 대문자 T 는 uncased 라서 소문자가 됐습니다.
  • Qwen 의 add_special_tokens=False 는 표시용 토큰 없이 문장의 토큰만 받으려는 설정입니다. 사전은 151,665개로 BERT 의 약 5배입니다.
  • decode([token_id]) 로 하나씩 되돌린 리스트에서 ' �', '�', '��', '�', '�' 다섯 개가 "토큰" 두 글자(6바이트)입니다. 바이트 단위로 쪼갠 BPE 라서 생기는 모습이고, 전체를 한 번에 decode 하면 문제없이 원문이 됩니다.
← 이전 절2장 · 실습 지도다음 절 →2장 · 실습 2 — 한국어와 영어 토큰 수 견주기