본문 바로가기
딥러닝

[딥러닝][NLP] Tokenizer 정리

by 방구석 데이터사이언티스트 2023. 1. 13.
728x90
반응형

 

안녕하세요. 오늘은 NLP 파이프라인의 핵심 구성 요소 중 하나인, Tokenizer에서 대해 살펴보고 정리해보겠습니다. 

 

먼저 토크나이저를 정리하려면, 토크나이징에 대한 개념부터 확실히 해야겠군요.

토크나이징(Tokenizing)이란? 의미가 있는 가장 작은 언어단위(토큰)로 텍스트를 전처리하는 과정입니다.

말이 조금 어려운데 쉽게 생각하면, 모델의 입력에 맞게 전처리해준다라고 생각하면 간편할 것 같습니다. 

 

따라서 토크나이징을 위해 모델에 맞는 토크나이저를 사용하게 됩니다. 

왜 모델에 맞는 토크나이저를 사용하냐면, 토크나이징 방식에 따른 차이가 있을 수 있고, 모델의 입력값의 차이도 있기 때문입니다. 예를 들어) Bert의 경우 워드피스 토크나이징 방식을 따르고, 입력으로는 token_ids, token_type_ids, attention_mask 등을 사용하며, 모델마다 조금의 차이는 있습니다.

 

오늘은 크게 세 가지의 토크나이저를 살펴보겠습니다. 

1. BertTokenizer

from transformers import BertTokenizer
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')

 

위와 같은 코드로 사전학습된 토크나이저를 불러올 수 있으며, 위 코드는 허깅페이스를 참고했습니다. 

여기서 저는 'bert-base-uncased' 모델의 토크나이저를 가져왔는데 base는 모델의 사이즈이며, uncased는 대/소문자 구분여부를 나타냅니다.

 

print(tokenizer.cls_token, tokenizer.sep_token, tokenizer.pad_token)
print(tokenizer.cls_token_id, tokenizer.sep_token_id, tokenizer.pad_token_id)
#[CLS] [SEP] [PAD]
# 101 102 0

 

토크나이저의 special token을 살펴보면 [CLS], [SEP], [PAD] 등이 있고 각각 101, 102, 0으로 매핑되어 있군요.

 

res = tokenizer('hello. this is study')
print(res)
res = tokenizer.tokenize('hello. this is study')
print(res)
res = tokenizer.encode('hello. this is study')
print(res)
res=tokenizer.decode(res)
print(res)
# {'input_ids': [101, 7592, 1012, 2023, 2003, 2817, 102], 'token_type_ids': [0, 0, 0, 0, 0, 0, 0], 'attention_mask': [1, 1, 1, 1, 1, 1, 1]}
# ['hello', '.', 'this', 'is', 'study']
# [101, 7592, 1012, 2023, 2003, 2817, 102]
# [CLS] hello. this is study [SEP]

 

한 문장을 넣어 토크나이저의 결과값을 살펴봤습니다.

Bert 입력에 필요한 input_ids, token_type_ids, attention_mask가 출력되는 것을 알 수 있습니다.

그리고 토큰화 과정과 인코딩 과정을 나누어서 살펴보면, 위와 같이 출력되고 디코딩을 해보면 스페셜 토큰까지 잘 concat 된 것을 알 수 있네요.

 

res = tokenizer(['hello. this is study', "what are you doing?"])
print(res)
res = tokenizer(['hello. this is study', "what are you doing?"], add_special_tokens=False)
print(res)
res = tokenizer(['hello. this is study', "what are you doing?"], return_tensors="pt")
print(res)
res = tokenizer(['hello. this is study', "what are you doing?"], padding='max_length', max_length=10)
print(res)
res = tokenizer(['hello. this is study', "what are you doing?"], truncation=True, max_length=10)
print(res)

# {'input_ids': [[101, 7592, 1012, 2023, 2003, 2817, 102], [101, 2054, 2024, 2017, 2725, 1029, 102]], 'token_type_ids': [[0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0]], 'attention_mask': [[1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1]]}
# {'input_ids': [[7592, 1012, 2023, 2003, 2817], [2054, 2024, 2017, 2725, 1029]], 'token_type_ids': [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0]], 'attention_mask': [[1, 1, 1, 1, 1], [1, 1, 1, 1, 1]]}
# {'input_ids': tensor([[ 101, 7592, 1012, 2023, 2003, 2817,  102],
#         [ 101, 2054, 2024, 2017, 2725, 1029,  102]]), 'token_type_ids': tensor([[0, 0, 0, 0, 0, 0, 0],
#         [0, 0, 0, 0, 0, 0, 0]]), 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1],
#         [1, 1, 1, 1, 1, 1, 1]])}
# {'input_ids': [[101, 7592, 1012, 2023, 2003, 2817, 102, 0, 0, 0], [101, 2054, 2024, 2017, 2725, 1029, 102, 0, 0, 0]], 'token_type_ids': [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], 'attention_mask': [[1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 0, 0, 0]]}
# {'input_ids': [[101, 7592, 1012, 2023, 2003, 2817, 102], [101, 2054, 2024, 2017, 2725, 1029, 102]], 'token_type_ids': [[0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0]], 'attention_mask': [[1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1]]}

 

이번에는 리스트 형태로 두 문장을 넣어 보고 토크나이저의 파라미터를 살펴보겠습니다. 

add_special_tokens : 스페셜 토큰 삽입 유무

> "False"를 했을 때, 두 번째 출력에서 101, 102가 빠짐

 

return_tensors : 출력 type 설정

> 대부분 "pt"로 입력해, Tensor 형태로 받는다. 두 가지 타입이 더 있었는데.. 기억안남ㅎㅎ

> 더 궁금한 사항은 허깅페이스를 참고하시길..

padding : pad token 삽입 유무
> max_length 설정없이 True로 입력할 수 도 있고 max_length를 설정하여 그 이상 값은 패딩 처리할 수 도 있다.
 
truncation : 일정 길이 이상의 값을 자름 유무
 
res = tokenizer.encode_plus('hello. this is study')
print(res)
res = tokenizer.batch_encode_plus(['hello. this is study'])
print(res)
res=tokenizer.decode(res['input_ids'][0])
print(res)

{'input_ids': [101, 7592, 1012, 2023, 2003, 2817, 102], 'token_type_ids': [0, 0, 0, 0, 0, 0, 0], 'attention_mask': [1, 1, 1, 1, 1, 1, 1]}
{'input_ids': [[101, 7592, 1012, 2023, 2003, 2817, 102]], 'token_type_ids': [[0, 0, 0, 0, 0, 0, 0]], 'attention_mask': [[1, 1, 1, 1, 1, 1, 1]]}
[CLS] hello. this is study [SEP]

 

다음으로는 encode_plus와 batch_encode_plus에 대해 살펴봤습니다. 

사실 무슨 차이가 있는 지는 잘 모르겠지만, batch_encode_plus는 배치 내에 문장들을 리스트 형태로 받아 인코딩하는 것이라고 생각이 된다. 

 

2. RobertaTokenizer

from transformers import RobertaTokenizer
tokenizer = RobertaTokenizer.from_pretrained('roberta-base')

 

두 번째는 RobertaTokenizer입니다. 

 

print(tokenizer.cls_token, tokenizer.sep_token, tokenizer.pad_token)
print(tokenizer.cls_token_id, tokenizer.sep_token_id, tokenizer.pad_token_id)

# <s> </s> <pad>
# 0 2 1

 

RobertaTokenizer는 cls-0, sep-2, pad-1로 매핑되어 있는 것을 확인 할 수 있습니다.

 

res = tokenizer('hello. this is study')
print(res)

# {'input_ids': [0, 42891, 4, 42, 16, 892, 2], 'attention_mask': [1, 1, 1, 1, 1, 1, 1]}

 

RobertaTokenizer 출력 결과에서 Bert와 조금 차이점이 보입니다.

Bert와 다르게 token_type_ids가 없는 것을 알 수 있으며, 이는 NSP를 제거한 Roberta의 특징입니다. 

 

3. AutoTokenizer

from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("klue/bert-base")

 

세 번째는 AutoTokenizer입니다. 

AutoTokenizer는 사전학습된 Tokenizer를 쉽게 불러와 쓸 수 있도록 도와줍니다.

위에서 본 Bert와 Roberta도 사용할 수 있으며, 이외 다른 사전학습 토크나이저도 사용가능합니다.

 

이번에는 영어가 아닌 한국어로 사전학습된 Klue/bert-base를 사용해봤습니다.

 

print(tokenizer.cls_token, tokenizer.sep_token, tokenizer.pad_token)
print(tokenizer.cls_token_id, tokenizer.sep_token_id, tokenizer.pad_token_id)

# [CLS] [SEP] [PAD]
# 2 3 0

 

Klue/bert-base는 스페셜 토큰이 2,3,0으로 매핑되어있군요.

이러면 1로 매핑되어 있는 토큰이 무엇인지 궁금하실 것 같은데, 참고 1은 UNK Token입니다. 

 

res = tokenizer('나는 공부를 한다.')
print(res)

# {'input_ids': [2, 717, 2259, 4244, 2138, 3605, 18, 3], 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1]}

 

마지막으로 한국어 문장으로 입력하여 토크나이징 결과를 확인해 봤습니다.

bert 입력에 필요한 인풋 값들이 출력되는 것을 확인할 수 있습니다.

 

 

오늘은 Tokenizer에 대해 살펴보고 정리해봤습니다.

감사합니다.

728x90
반응형

댓글