금융 데이터 분석가/text-analysis

[NLP] tokenizer 학습 모델 비교 (BPE, WordPiece, Unigram)

리치즈 2023. 11. 7. 06:09
728x90

Tokenizer는 크게 4단계로 구성되어 있습니다. 

 

1. Nomalization : 불필요한 문자 제거, 유니코드 정규화 등 텍스트 정리

2. Pre-Tokenization : 단어 단위로 분해

3. Model : 사전에 토큰화된(pre-tokinzed) 단어로 토큰 시퀀스 생성

4. Postprocessor : 스페셜 토큰 삽입, attention mask와 token type id 생성

 

각 단계를 예시를 들며 살펴보겠습니다.

 

1. Normalization

from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
print(type(tokenizer.backend_tokenizer))
print(tokenizer.backend_tokenizer.normalizer.normalize_str("Héllò hôw are ü?"))

(참고) uncased는 대소문자, 발음기호를 삭제하지만, cased는 삭제하지 않습니다.

 

2. Pre-Tokenizer

예시 문장 "Hello, My  friend?"를 여러 토크나이저로 pre-tokenization하며 비교해보겠습니다. 문장에는 구두점(,)과 이중공백이 포함되어 있습니다.

BERT 토크나이저는 구두점을 구분하지만 이중 공백은 무시했습니다.

tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, My  friend?")
# [('Hello', (0, 5)), (',', (5, 6)), ('My', (7, 9)), ('friend', (11, 17)), ('?', (17, 18))]

 

gpt 기반 토크나이저는 이중 공백을 무시하지 않고, 'Ġ'라는 특수토큰으로 대체했습니다.

gpt_tokenizer = AutoTokenizer.from_pretrained("gpt2")
gpt_tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, My  friend?")
# [('Hello', (0, 5)), (',', (5, 6)), ('ĠMy', (6, 9)), ('Ġ', (9, 10)), ('Ġfriend', (10, 17)), ('?', (17, 18))]

 

SentencePiece 알고리즘을 기반으로 하는 T5 토크나이저는 공백을 유지하지만, 구두점과 물음표는 구분하지 않습니다.

t5_tokenizer = AutoTokenizer.from_pretrained("t5-small")
t5_tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, My  friend?")
# [('▁Hello,', (0, 6)), ('▁My', (7, 9)), ('▁friend?', (11, 18))]

 

SentencePiece

SentencePiece를 사용하면 Normalization과 Pre-tokenizer 단계가 필요하지 않을 수 있습니다.

SentencePiece는 텍스트 전처리를 위한 토큰화 알고리즘인데, 텍스트를 일련의 유니코드 문자로 간주하고 공백은 _로 바꿔줍니다. Unigram 알고리즘과 함께 사용하면 pre-tokenization 단계도 필요하지 않습니다. 이는 공백 문자가 사용되지 않는 언어(중국어, 일본어)에 매우 유용합니다.

주요 특징은 reversible tokenization입니다. 공백에 특별한 처리를 하지 않기 때문에, 토큰을 연결하고 _를 공백으로 바꾸는 것만으로 디코딩이 수행됩니다.- 정규화된 텍스트 생성. 앞에서 본 것처럼 BERT 토크나이저는 반복되는 공백을 제거하므로 토큰을 되돌릴 수 없습니다.

Model for Subword Splitting and Merging

SentencePiece에는 다양한 서브워드 분할 및 병합 전략을 사용할 수 있습니다. 그중 가장 유명한 세가지 방법을 소개합니다.

모델 BPE WordPiece Unigram
Training 작은 vocabulary부터 시작하고, 토큰을 병합하는 규칙을 학습
모든 문자를 서브워드로 처리하고, 가장 빈도가 높은 바이트 페어를 병합하면서 새로운 서브워드 집합을 생성
작은 vocabulary부터 시작하고 토큰을 병합하는 규칙을 학습
단어 목록을 가지고 시작하며, 주어진 텍스트에 있는 모든 단어를 자주 나오는 부분어로 분할하고, 이들을 병합하여 새로운 서브워드를 생성
large vocabulary부터 시작하고 토큰을 삭제하는 규칙을 학습
모든 단어를 하나의 서브워드로 처리하고, 빈도에 따라 새로운 서브워드 생성. 빈도에 따라 샘플링되는데, 효율적인 학습을 위해 랜덤성을 추가.
Training step 가장 일반적인 쌍에 해당하는 토큰을 병합 가장 빈번한 쌍에 대한 스코어를 기반으로 토큰을 병합함 전체 corpus에서 loss를 최소화 하는 단어의 모든 토큰을 제거
Learns 병합 규칙, vocab vocab 각 토큰의 점수와 vocab
Encoding 단어를 문자로 분할하고 학습 중에 학습된 병합을 적용 어휘의 처음부터 시작하여 가장 긴 하위 단어를 찾은 다음 나머지 단어에 대해서도 동일한 작업 수행 훈련 중에 학습한 점수를 사용하여 토큰으로 분할될 가능성이 가장 높은 항목을 찾음

 

지금부터는 각 모델을 예시를 들어 설명하겠습니다. 예시에는 아래 다섯가지 단어를 사용합니다.

"hug", "pug", "pun", "bun", "hugs"

 

BPE(Byte Pari Encoding)

텍스트를 압축하는 알고리즘으로 개발된 후 OpenAI에서 GPT 모델을 사전 학습할 때 토큰화를 위해 사용되었습니다.

GPT, GPT-2, RoBERTa, BART, DeBERTa에서 사용되었습니다.

 

예시에 대한 기본 vocabulary는 ["b", "g", "h", "n", "p", "s", "u"]가 됩니다. 기본 vocabulary에는 최소한 모든 ASCII 문자가 포함되어 있습니다. 하지만 토큰화 하는 예제가 훈련 코퍼스에 없는 문자를 사용할 경우, 해당 문자는 [UNK] 토큰으로 변환됩니다. NLP 모델이 이모티콘이 포함된 글을 분석하는데 안좋은 결과를 보이는 이유 중 하나입니다.(참고) GPT-2와 RoBERTa 토크나이저는 byte-level BPE라는 트릭을 사용합니다. 즉, 단어가 유니코드가 아니라 바이트로 작성된 것으로 간주합니다. vocabularty 크기 (256)는 작지만 모든 문자가 포함되며, UNK로 변환되지 않습니다.

 

기본 vocabulary를 얻은 후, 기존 어휘의 두 요소를 병합하는 규칙을 학습하여, 원하는 크기의 vocabulary가 될 때까지 새 토큰을 추가합니다. 

# 1. 빈도 계산
("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)
# 2. 각 단어를 토큰으로 변환
("h" "u" "g", 10), ("p" "u" "g", 5), ("p" "u" "n", 12), ("b" "u" "n", 4), ("h" "u" "g" "s", 5)
# 3. 가장 빈번하게 발생하는 쌍 찾고 병합
("u", "g") -> "ug"
# Vocabulary: ["b", "g", "h", "n", "p", "s", "u", "ug"]
# Corpus: ("h" "ug", 10), ("p" "ug", 5), ("p" "u" "n", 12), ("b" "u" "n", 4), ("h" "ug" "s", 5)
# 4. 원하는 vocabulary 크기가 될 때까지 반복

 

아래와 같은 규칙을 학습했을 때, "bug"는 ["b", "ug"]로 토큰화됩니다. 하지만 "mug"는 "m"이 vocabulary에 없기 때문에 ["[UNK]", "ug"]로 토큰화 됩니다.

("u", "g") -> "ug"
("u", "n") -> "un"
("h", "ug") -> "hug"

 

 

WordPiece

Google에 BERT를 pretraining하기위해 개발한 토큰화 알고리즘입니다.

이후 DistilBERT, MobileBERT, Funnel Transformers 및 MPNET 등 BERT 기반의 모델에서 사용되었습니다.

 

BPE와 유사하지만 실제 토큰화는 다르게 수행됩니다.

BPE와 마찬가지로 병합 규칙을 학습합니다. BPE는 가장 빈번한 쌍을 병합하지만, WordPiece는 스코어에 따라 병합합니다.

score = ( freq_of_pair ) / ( freq_of_first_element x freq_of_second_element )

쌍의 빈도를 각 부분의 빈도의 곱으로 나누어서, 개별 요소의 빈도 대비 쌍의 빈도가 높은 쌍을 우선적으로 병합합니다.

# 1. 빈도 계산
("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)
# 2. 나누기
("h" "##u" "##g", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("h" "##u" "##g" "##s", 5)
# 3. 스코어 계산후 병합
# Vocabulary: ["b", "h", "p", "##g", "##n", "##s", "##u", "##gs"]
# Corpus: ("h" "##u" "##g", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("h" "##u" "##gs", 5)
# 4. 원하는 크기의 vocabulary가 될 때까지 반복

 

 

Unigram

AlBERT, T5, mBART, Big Bird 및 XLNet에서 사용되고, SentencePiece에 자주 사용됩니다.

 

Unigram은 BPE, WordPiece와 다르게 작동합니다.

기본 large vocabulary에서 시작해서 원하는 어휘 크기에 도달할 때까지 토큰을 제거합니다.

기본 vocabulary를 구축하는데는 여러 옵션이 있는데, 예를 들면 pre-tokenized vocabulary에서 가장 일반적인 하위 문자열을 사용하거나 어휘 크기가 큰 초기 코퍼스에 BPE를 적용할 수 있습니다.

 

모든 학습 단계에서 현재 주어진 vocabulary에 대한 코퍼스의 loss를 계산합니다.

vocabulary 안에 있는 symbol을 제거했을 때, 전체 loss가 얼마나 증가하는지 계산하여 가장 작게 loss가 증가되는 symbol을 제거합니다.

# 1. large vocabulary 구성(예시에서는 모든 substring)
["h", "u", "g", "hu", "ug", "p", "pu", "n", "un", "b", "bu", "s", "hug", "gs", "ugs"]
# 2. 각 빈도 count, 총 합 계산
("h", 15) ("u", 36) ("g", 20) ("hu", 15) ("ug", 20) ("p", 17) ("pu", 17) ("n", 16)
("un", 16) ("b", 4) ("bu", 4) ("s", 5) ("hug", 15) ("gs", 5) ("ugs", 5)
# -> total : 210
# 3. 확률 계산(ex. "pug" → "p", "u", "g")
P(["p", "u", "g"]) = P(["p"]) x P(["u"]) x P(["g"]) = 5/210 x 36/210 x 20/210 = 0.000389

 

Unigram에는 매우 많은 계산이 필요합니다.

# 스코어
"hug": ["hug"] (score 0.071428)
"pug": ["pu", "g"] (score 0.007710)
"pun": ["pu", "n"] (score 0.006168)
"bun": ["bu", "n"] (score 0.001451)
"hugs": ["hug", "s"] (score 0.001701)

# loss 계산
10 * (-log(0.071428)) + 5 * (-log(0.007710)) + 12 * (-log(0.006168)) + 4 * (-log(0.001451)) + 5 * (-log(0.001701)) = 169.8

 

 

 

 

본 포스트는 Hugging Face의 NLP course를 참고하여 작성했습니다.

 

Introduction - Hugging Face NLP Course

2. Using 🤗 Transformers 3. Fine-tuning a pretrained model 4. Sharing models and tokenizers 5. The 🤗 Datasets library 6. The 🤗 Tokenizers library 9. Building and sharing demos new

huggingface.co

 

728x90
LIST