https://www.youtube.com/watch?v=kCc8FmEb1nY&t=5065s
이번 글은 이 영상을 따라하는 글이며, 제 생각과 추가 실습 가능한 부분을 같이 써보는 식으로 진행해보려합니다
먼저 tokenizer를 만드는 걸 소개하는데
셰익스피어 글을 다운받아서 커스텀 tokenizer를 만듭니다
chars = sorted(list(set(text)))
vocab_size = len(chars)
string_to_idx = {ch:i for i,ch in enumerate(chars)}
idx_to_string = {i:ch for i,ch in enumerate(chars)}
sti = string_to_idx
its = idx_to_string
encode = lambda s:[sti[c] for c in s]
decode = lambda l: [its[i] for i in l]
print(encode("hii there")) # text -> integer
print(decode(encode("hii there"))) # integer -> text
print("".join(decode(encode("hii there"))))
____결과____
[46, 47, 47, 1, 58, 46, 43, 56, 43]
['h', 'i', 'i', ' ', 't', 'h', 'e', 'r', 'e']
hii there
결과를 보면 단어마다 하나씩 integer가 부여되니까 "hii there"이라는 9글자 단어가 9개의 integer로 표현된 것을 볼 수 있습니다.
시중에 있는 BERT와 OPENAI의 tiktoken과 비교해보면
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
encoded = tokenizer.encode("hii there")
decoded = tokenizer.decode(encoded)
print(encoded)
print(decoded)
____결과___
[101, 7632, 2072, 2045, 102]
[CLS] hii there [SEP]
import tiktoken
model = "gpt-3.5-turbo"
text = "hii there"
encoding = tiktoken.encoding_for_model(model)
tokens = encoding.encode(text)
print(f"Tokens: {tokens}")
num_tokens = len(tokens)
print(f"Number of tokens: {num_tokens}")
decoded_text = encoding.decode(tokens)
print(f"Decoded text: {decoded_text}")
___결과___
Tokens: [71, 3893, 1070]
Number of tokens: 3
Decoded text: hii there
BERT는 cls와 sep을 제외하면 3개,tiktoken도 3개로 커스텀 tokenizer보다 훨씬 시퀀스 길이가 짧은 것을 알 수 있습니다.
이는 의미를 더 구체적으로 반영할 수 있고 학습 효율이 좋다는 장점이 있습니다.
이제 이 3가지 방법으로 처음에 불러온 글을 전부 바꿔보겠습니다
import torch
"""Custom Tokenizer"""
data = torch.tensor(encode(text),dtype = torch.long)
print("""Custom Tokenizer""")
print(data.shape,data.dtype)
"""BERT"""
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
bert_encoded = tokenizer.encode(text)
bert_data = torch.tensor(bert_encoded,dtype = torch.long)
print("""BERT""")
print(data.shape,data.dtype)
"""TIKTOKEN"""
model = "gpt-3.5-turbo"
encoding = tiktoken.encoding_for_model(model)
tiktokens_encoded = encoding.encode(text)
tiktoken_data = torch.tensor(tiktokens_encoded,dtype = torch.long)
print("""TIKTOKEN""")
print(data.shape,data.dtype)
___결과___
Custom Tokenizer
torch.Size([1115394]) torch.int64
Token indices sequence length is longer than the specified maximum sequence length for this model (288721 > 512). Running this sequence through the model will result in indexing errors
BERT
torch.Size([288721]) torch.int64
TIKTOKEN
torch.Size([301829]) torch.int64
결과를 보면 Custom Tokenizer는 크기가 1115394입니다.
소설의 전체 길이와 같은 것을 확인할 수 있습니다
BERT는 경고가 출력되고,288721크기로 출력된 것을 확인할 수 있는데, 이는 BERT가 512개의 input제한이 있기 때문입니다.
BERT는 원래 짧은 문장을 위한 모델이므로 앞으로는 TIKTOKEN과 custom만 비교하도록 하겠습니다
다음으로 TIKTOKEN을 보면 가장 사이즈가 작은 것을 볼 수 있습니다.
이는 토큰 수가 적으니 메모리 효율면에서 가장 좋고, 더 많은 정보를 담을 수 있음을 보여줍니다.
다음은 데이터를 split하고 chunking하는 것입니다
chunking은 긴 시퀀스를 잘게 나누는 것인데 먼저 데이터를 9:1로 train,validation으로 나누겠습니다
n = int(0.9*len(text))
train_data = data[:n]
val_data = data[n:]
n = int(0.9*len(text))
train_data = data[:n]
val_data = data[n:]
AutoRegressive한 작동의 코드를 작성해보겠습니다
torch.manual_seed(1337)
batch_size=4
block_size=8
def get_batch(split):
data = train_data if split=="train" else val_data
xi = torch.randint(0,len(data)-block_size,(batch_size,))
xb = torch.stack([data[x:x+block_size] for x in xi])
yb = torch.stack([data[x+1:x+block_size+1] for x in xi])
return xb,yb
xb,yb = get_batch("train")
for b in range(batch_size):
for t in range(block_size):
context = xb[b,:t+1]
target = yb[b,t]
print(context.tolist(),"->",target.tolist())
_____________________________________________________________________
[24] -> 43
[24, 43] -> 58
[24, 43, 58] -> 5
[24, 43, 58, 5] -> 57
[24, 43, 58, 5, 57] -> 1
[24, 43, 58, 5, 57, 1] -> 46
[24, 43, 58, 5, 57, 1, 46] -> 43
[24, 43, 58, 5, 57, 1, 46, 43] -> 39
[44] -> 53
[44, 53] -> 56
[44, 53, 56] -> 1
[44, 53, 56, 1] -> 58
[44, 53, 56, 1, 58] -> 46
[44, 53, 56, 1, 58, 46] -> 39
[44, 53, 56, 1, 58, 46, 39] -> 58
[44, 53, 56, 1, 58, 46, 39, 58] -> 1
[52] -> 58
[52, 58] -> 1
[52, 58, 1] -> 58
[52, 58, 1, 58] -> 46
[52, 58, 1, 58, 46] -> 39
[52, 58, 1, 58, 46, 39] -> 58
[52, 58, 1, 58, 46, 39, 58] -> 1
[52, 58, 1, 58, 46, 39, 58, 1] -> 46
[25] -> 17
[25, 17] -> 27
[25, 17, 27] -> 10
[25, 17, 27, 10] -> 0
[25, 17, 27, 10, 0] -> 21
[25, 17, 27, 10, 0, 21] -> 1
[25, 17, 27, 10, 0, 21, 1] -> 54
[25, 17, 27, 10, 0, 21, 1, 54] -> 39
위 코드는 배치 단위로 input,target에 대한 코드입니다.
먼저 chunking을 8개로 하고, batch는 4로 하겠습니다.
전체 시퀀스가 [1,2,3,4,5,6,7,8]이라면
x가 [1]이면 예측값은 [2] 여야하고
x가 [1,2]이면 예측값은[3]이어야합니다
이런 흐름을 Autoregressive 하다고 하는 것 같습니다
추가로 tiktokenizer로 똑같은 코드를 실행해본 결과
아래와 같이 출력되었습니다
[74665] -> 374
[74665, 374] -> 45314
[74665, 374, 45314] -> 198
[74665, 374, 45314, 198] -> 791
[74665, 374, 45314, 198, 791] -> 2132
[74665, 374, 45314, 198, 791, 2132] -> 323
[74665, 374, 45314, 198, 791, 2132, 323] -> 279
[74665, 374, 45314, 198, 791, 2132, 323, 279] -> 4948
[11] -> 311
[11, 311] -> 10477
[11, 311, 10477] -> 1077
[11, 311, 10477, 1077] -> 3663
[11, 311, 10477, 1077, 3663] -> 26
[11, 311, 10477, 1077, 3663, 26] -> 369
[11, 311, 10477, 1077, 3663, 26, 369] -> 1077
[11, 311, 10477, 1077, 3663, 26, 369, 1077] -> 8571
[1884] -> 430
[1884, 430] -> 4985
[1884, 430, 4985] -> 198
[1884, 430, 4985, 198] -> 46864
[1884, 430, 4985, 198, 46864] -> 379
[1884, 430, 4985, 198, 46864, 379] -> 12791
[1884, 430, 4985, 198, 46864, 379, 12791] -> 311
[1884, 430, 4985, 198, 46864, 379, 12791, 311] -> 26236
[1440] -> 701
[1440, 701] -> 7438
[1440, 701, 7438] -> 11
[1440, 701, 7438, 11] -> 10868
[1440, 701, 7438, 11, 10868] -> 198
[1440, 701, 7438, 11, 10868, 198] -> 32641
[1440, 701, 7438, 11, 10868, 198, 32641] -> 70879
[1440, 701, 7438, 11, 10868, 198, 32641, 70879] -> 3751
'Karpathy-GPT만들기' 카테고리의 다른 글
| Karpathy - Let's build GPT 실습하기 (2) (0) | 2025.04.25 |
|---|