Unigram tokenizer는 BPE와 같이 문장 혹은 단어 안에 있는 글자들을 적절한 단위로 나누는 subword tokenizer의 하나로, 큰 vocabulary로부터 필요하지 않은 토큰을 하나씩 줄여나감으로써 최종적인 token들을 도출하는 알고리즘이다. 이번 글에서는 Unigram tokenizer가 어떻게 토큰들을 만들어내는지에 대해 알아보겠다.
Unigram tokenizer 알고리즘
우선, unigram tokenizer를 한 눈에 볼 수 있게 표현하면 다음과 같다.
Unigram 알고리즘은 BPE와 동일하게 기본적으로 문장이 pre-tokenize 되어 있다고 가정한다. 즉, 하나의 문장이 모두 이어져 있는 것이 아닌 띄어쓰기 등으로 분리되어 있다고 가정하는 것이다. 우리의 예시 문장이 "hug pug pun bun hugs"라고 하자. (아무런 뜻이 없는 그냥 예시 문장이다.) 그러면 이들 문장이 띄어쓰기 기준으로 pre-tokenize된다고 할 때, 우리는 "hug", "pug", "pun", "bun", "hugs"의 다섯 개의 단어들을 가지고 있는 것이다.
이제부터 unigram 알고리즘을 예시와 함께 살펴보겠다. 우리의 예시 문장 집합은 다음과 같다.
("hugs bun", 4)
("hugs pug", 1)
("hug pug pun", 4)
("hug pun", 6)
("pun", 2)
위 예시에서 따옴표 안에 들어가 있는 것은 문장이고, 그 오른쪽에 있는 숫자는 각 문장의 빈도이다.
1. 우선, 각 pre-tokenize로 나누어진 단어들의 빈도를 계산한다. 이를 우리의 예시 데이터에 대해 계산하면 다음과 같다.
("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)
2. 다음으로, 각 단어의 모든 substrring을 활용해서 가장 큰 vocabulary를 만들어낸다. 예를 들어, hug라는 단어에서는 h, u, g, hu, ug, hug의 6개의 substring을 활용해 vocabulary를 만들어낼 수 있는 것이다. 모든 단어에 대해 vocabulary를 만들어내면 다음과 같다.
["h", "u", "g", "hu", "ug", "p", "pu", "n", "un", "b", "bu", "s", "hug", "gs", "ugs"]
3. 위에서 만들어낸 vocabulary 내의 모든 token들의 빈도를 구한다. 예를 들어, h는 hug에서 10번, hugs에서 5번 등장했으므로 h의 빈도는 15가 된다. 이들 모두에 대해 빈도를 구하면 다음과 같다.
("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)
4. 각 단어들에 대해 tokenization들의 probability를 계산한다. 이 때, 각 token 내의 character의 probability는 서로 독립이라고 가정한다. 예를 들어, pug라는 단어에 대해 각 tokenization 방법들의 probability를 구한다고 하자. 우선, p u g tokenization의 probability를 구해보자. 전체 token들의 빈도의 합을 구하면 210이 되고, p의 빈도는 17, u의 빈도는 36, g의 빈도는 20이기 때문에 이의 probability는 다음과 같다.
이러한 방법으로 pu g tokenization의 probability를 구하면 다음과 같다.
이렇게 pug에 대해 가능한 모든 tokenization인 p u g, pu g, p ug의 probability를 각각 구하면 다음과 같다.
["p", "u", "g"] : 0.000389
["p", "ug"] : 0.0022676
["pu", "g"] : 0.0022676
그러므로 pug는 가장 probability가 높은 pu g 혹은 p ug로 tokenize되어야 하는 것이다. 이런 방법으로 모든 단어들에 대해 가장 probability를 높게 만들어주는 tokenization 방법을 찾는다. 그 결과는 다음과 같다.
"hug": ["hug"] (prob 0.071428)
"pug": ["pu", "g"] (prob 0.002267)
"pun": ["pu", "n"] (prob 0.006168)
"bun": ["bu", "n"] (prob 0.001451)
"hugs": ["hug", "s"] (prob 0.001701)
5. 도출된 각 단어의 probability를 바탕으로 현재의 score를 계산한다. 이는 각 단어의 빈도에 probability의 negative log를 곱하여 합한 것과 같다. 우리의 예시에 대해 현재 score를 계산하면 다음과 같다.
10 * (-log(0.071428)) + 5 * (-log(0.002267)) + 12 * (-log(0.006168)) + 4 * (-log(0.001451)) + 5 * (-log(0.001701)) = 169.8
6. 우리의 vocabulary에서 필요하지 않은 p%의 token들을 제거한다. 이 때, 각 token은 해당 token을 없앴을 때에 score에 loss가 작은 순서대로 제거한다. 예를 들어, token pu를 없애는 것은 우리가 4단계에서 보았던 것처럼 p ug로 tokenization을 진행해도 score가 같기 때문에 loss가 0이 될것이다. 또 다른 예시로, 만약 우리가 hug라는 token을 없앤다고 가정하자. 그러면 우리는 다음으로 좋은 score를 가지는 tokenization을 hug와 hugs에 적용할 것이다. 이 때의 loss는 다음과 같이 계산된다.
"hug": ["hu", "g"] (score 0.006802)
"hugs": ["hu", "gs"] (score 0.001701)
위 계산 결과를 바탕으로 hug라는 token을 없앴을 때의 loss의 변화를 계산하면 다음과 같다.
- 10 * (-log(0.071428)) + 10 * (-log(0.006802)) = 23.5
이렇게 pu와 hug의 loss 변화를 비교하면 pu의 경우가 더 작기 때문에, pu를 token에서 제거하게 될 것이다. 이 때, 제거하는 비율인 p%는 hyperparameter로 적절하게 설정하면 된다.
7. 이러한 과정을 우리가 원하는 vocabulary size가 될 때까지 반복한다.
Unigram 장단점
Unigram tokenizer의 장점은 처음에 모든 substring을 다 고려한 vocabulary로부터 시작하기 때문에 모든 가능한 token들을 고려할 수 있다는 점이다. 하지만 이에 반해 단점은, 모든 가능한 substring을 다 vocabulary에 넣고 시작하기 때문에 계산 과정이 많고, 시간이 오래 걸린다는 것이다. 또한 각 character가 independent하다고 가정하는데, 이러한 가정은 현실에서 맞지 않는 경우 (예를 들어, 영어에서는 대부분의 경우에 q 다음에 u가 와야 하므로 이들은 독립이라고 할 수 없다.) 가 있을 수 있다.
이번 글에서는 unigram tokenizer가 무엇인지에 대해 알아보았다. 이번주는 간만에 아주 조금 여유로워서 평일 저녁에도 이렇게 글을 써 보았다 ㅎㅎ 올해 200개 채우기 목표가 가능할지..
References
Hugging face https://huggingface.co/course/chapter6/7?fw=pt
최근댓글