Subword Tokenizer - BPE, WordPiece
본문은 딥러닝을 이용한 자연어처리 입문과 WordPiece Tokenization 을 참고하여 작성되었습니다.
▶ Introduction
- OOV(Out of Vocabulary)문제나 UNK(Unkown Token) 문제.
- 아무리 많은 단어를 학습시켜도, 세상의 모든 단어를 학습하지는 못함. 만약 모르는 단어가 등장하게 되면 기계가 문제를 풀 때 굉장히 어려워짐. 이와 같이, 모르는 단어로 인해 문제를 해결하는 것이 까다로워지는 상황을 OOV문제라 한다.
- Subword Segmentation - 서브워드 분리 작은, 하나의 단어는 더 작은 단위의 의미있는 여러 서브워드들의 조합으로 구성된 경우가 많기 때문에 하나의 단어를 여러 subword로 분리해서 단어를 인코딩 및 임베딩 하는 전처리 작업을 의미한다. 예를 들면, birthplace라는 단어는 birth와 place의 조합으로 구성되어있기 때문에 이를 분리해서 임베딩 할 수 있다.
▶ 바이트 페어 인코딩 (Byte Pair Encoding, BPE)
- OOV 문제를 완화하는 대표적인 서브워드 분리 알고리즘.
- 초기에는 데이터 압축 알고리즘으로 사용되었으나 자연어처리의 서브워드 분리 알고리즘으로 응용됨.
- BPE는 기본적으로 연속적으로 가장 많이 등장한 글자의 쌍을 찾아 하나의 글자로 병합하는 방식을 수행한다. 아래 예제애서 가장 자주 등장하고 있는 바이트(byte pair)은 'aa'이다. (1) 이 'aa' 바이트의 쌍을 하나의 바이트인 'Z'로 치환해 본다. (2) 이제 가장 많이 등장하는 바이트의 쌍은 'ab'이다. 이를 'Y'로 치환한다. (3) 다음으로 가장 많이 등장하고 있는 바이트의 쌍은 'ZY'이다. 이를 'X'로 치환한다. (4) 더이상 병합할 바이트의 쌍이 없으므로 BPE는 위 결과를 최종으로 종료된다.
aaabdaaabac -> ZabdZabac -> ZYdZYac -> XdXac
- 자연어 처리에서 BPE는 서브워드 분리 알고리즘으로서, 기존에 있던 단어를 분리할 수 있다.
- 요약하면, 글자(character)단위에서 점차적으로 단어집합(vocabulary)을 만들어 내는 Bottom up 방식의 접근을 사용한다. (1) 우선 훈련데이터에 있는 단어들을 모든 글자(characters) 또는 유니코드(unicode) 단위로 단어 집합(vocabulary)를 만들고, 가장 많이 등장하는 유니그램을 하나의 유니그램으로 통합한다.
ex1 ) 어떤 훈련 데이터로부터 각 단어들의 빈도수를 카운트 했다고 하자. 그리고 각 단어와 각 단어의 빈도수가 기록되어있는 해당 결과를 다음과 같은 딕셔너리라 하자.
# dictionary
# 훈련 데이터에 있는 단어와 등장 빈도수
low : 5, lower : 2, newest : 6, widest : 3
기존의 접근으로는 이 훈련데이터 단어 집합에는 'low', 'lower', 'newest', 'widest' 4개의 단어만 존재한다. 이 경우 테스트 과정에서 'lowest'라는 단어가 등장한다면 이에 제대로 대응하지 못하는 OOV 문제가 발생한다.
반면, BPE를 적용할 경우, 우선 딕셔너리의 모든 단어들을 글자(character)단위로 분리한다. 이 딕셔너리는 자기 또한 업데이트 되며, 앞으로 단어집합을 업데이트하기 위해 지속적으로 사용되는 참고자료의 역할을 하게 된다.
# dictionary
l o w : 5, l o w e r : 2, n e w e s t : 6, w i d e s t : 3
딕셔너리를 참고로 한 초기 단어집(vocabulary)는 위와 같다. 즉, 초기 구성은 글자 단위로 분리된 상태이다.
# vocabulary
l, o, w, e, r, n, w, s, t, i, d
BPE의 특징은 알고리즘의 동작을 몇 회 반복(iteration)할 것인지를 사용자가 정한다는 것이다. 여기에서는 총 10회를 반복한다고 가정하자. 즉, 가장 빈도수가 높은 유니그램의 쌍을 하나의 유니그램으로 통합하는 과정을 총 10번 반복한다고 보면 된다. 위 딕셔너리를 보면 빈도수가 가장 높은 유니그램의 쌍은 (e, s)이다.
(1) 1회 - 딕셔너리를 참고로 했을 때, 빈도수가 9로 가장 높은 (e, s) 쌍을 es로 통합한다. =>
# dictionary update!
l o w : 5,
l o w e r : 2,
n e w es t : 6,
w i d es t : 3
# vocabulary update!
l, o, w, e, r, n, w, s, t, i, d, es
(2) 2회 - 빈도수가 9로 가장 높은 (es, t) 쌍을 est로 통합한다.
# dictionary update!
l o w : 5,
l o w e r : 2,
n e w est : 6,
w i d est : 3
# vocabulary update!
l, o, w, e, r, n, w, s, t, i, d, es, est
(3) 3회 - 빈도수가 7로 가장 높은 (l, o)의 쌍을 lo로 통합한다.
# dictionary update!
lo w : 5,
lo w e r : 2,
n e w est : 6,
w i d est : 3
# vocabulary update!
l, o, w, e, r, n, w, s, t, i, d, es, est, lo
본 과정을 10번 반복하였을 때 얻을 수 있는 딕셔너리와 단어 집합은 아래와 같다.
# dictionary update!
low : 5,
low e r : 2,
newest : 6,
widest : 3
# vocabulary update!
l, o, w, e, r, n, w, s, t, i, d, es, est, lo, low, ne, new, newest, wi, wid, widest
이제 테스트 과정에서 'lowest'라는 단어가 등장한다면, BPE 알고리즘을 사용한 위의 단어집합에서 OOV가 아니다. 기계는우선 'lowest'를 전부 글자 단위로 분할한다. 즉, 'l, o, w, e, s, t'가 된다. 그리고 기계는 위의 단어 집합을 참고로 하여 'low'와 'est'를 찾아낸다. 즉, 'lowest'를 ' 'low'와 'est' 두 단어로 인코딩하며 이 두 단어는 모두 단어 집합에 있기 때문에 OOV가 아니다.
▶ WordPiece Tokenizer
- WordPiece Tokenizer는 BPE의 변형 알고리즘이다. 본 알고리즘은 BPE가 빈도수에 기반하여 가장 많이 등장한 쌍을 병합하는 것과는 달리, 병합되었을 때 코퍼스의 우도(Likelihood)를 가장 높이는 쌍을 병합한다. 2016년 "Google’s Neural Machine Translation System: Bridging the Gap between Human and Machine Translation" 논문에서는 구글번역기에서 WordPiece Tokenizer가 수행된 결과에 대해 기술하였다.
- 수행이전의 문장: Jet makes feud over seat width with big orders at stake
WordPiece Tokenizer를 수행한 결과(wordpieces): _J et _makers _fe ud _over _seat _width _with _big _orders _at _stake - Jet -> J / et : 서브워드 구분됨. 이 알고리즘은 BERT를 훈련시기키 위해 사용되기도 함.
- WordPiece Tokenizer는 BPE와 각 candidate token의 score 계산에 있어 차이가 있다.
- BPE 알고리즘이 단순히 쌍의 개수를 가지고 합친다면, WordPiece는 LIkelihood (우도) 기반으로 병합을 진행한다.
- 다음은 "huggingface', 'hugging', 'face', 'hug', 'hugger', 'learning', 'learner', 'learners', 'learn'의 split 구조이다.
compute pair score (h, ##u) : $score = \frac{\textrm{freq of pair}}{\textrm{freq of first element} \times \textrm{freq of second element}} = \frac{4}{4 \times 4} = 0.25$
동일한 방식으로 pair score를 계산하면, 아래 그림과 같다.
따라서 가장 높은 pairs score를 얻는 (h, ##u)를 hu로 병합하고 위의 과정을 반복한다. 최종적으로 아래와 같이 vocabulary를 얻고 'huggingface'를 아래의 4개의 토큰으로 분리할 수 있다.