Tokenization 방법론들에 대한 쉽고 직관적인 이해
(You can find the English version of the post at this link.)
이 글에서는 자연어처리의 가장 기초에 해당하는 Tokenization 에 대해 살펴보고 각 방법론들의 핵심 아이디어들을 살펴보겠습니다.
자연어 처리는 언어를 구성하는 심볼들, 즉 텍스트를 분석하고 이해하는 과정입니다. 이러한 텍스트 데이터를 컴퓨터가 처리할 수 있도록 변환하는 첫 번째 단계를 ‘토크나이제이션(tokenization)’이라고 부릅니다. 토크나이제이션은 문장이나 문서를 모델이 처리할 수 있는 작은 단위, 즉 토큰들로 분할하는 과정을 말합니다. 이 과정은 확률 모델이든 신경망 모델이든 간에, 기계가 이해할 수 있는 형태로 심볼 시퀀스를 입력으로 제공하는 데 필수적입니다.
토크나이제이션을 통해 문장 또는 문서를 어떻게 분절화하여 모델링할지 결정하는 것은 자연어 처리 분야에서 오랫동안 고민되어 온 주제입니다. 이에 따라 여러 가지 토크나이제이션 방법이 개발되었으며, 각 방법은 특정 상황이나 언어에 더 적합할 수 있습니다. 과거부터 사용되어 온 다양한 토크나이제이션 방법들을 간단히 정리하면 아래와 같습니다.
- N-gram: 연속된 n개의 아이템을 하나의 토큰으로 취급합니다. 이는 텍스트 내에서 아이템의 연속성을 포착하는 데 유용합니다.
- Character: 개별 문자를 독립적인 토큰으로 취급합니다. 이 방법은 언어의 구조적 특성을 덜 고려하며, 매우 세밀한 수준의 분석이 가능합니다.
- Word: 공백이나 구두점 등을 기준으로 분리된 ‘단어’를 토큰으로 사용합니다. 가장 일반적이고 직관적인 방법입니다.
- Morpheme: 형태소 분석을 통해 언어의 가장 작은 의미 단위를 토큰으로 취급합니다. 특히 굴절 언어 처리에 적합합니다.
- Subword: ‘Byte Pair Encoding (BPE)’ 또는 ‘WordPiece’와 같은 방법을 사용하여 자주 사용되는 문자의 조합이나 단어의 부분을 토큰으로 분리합니다. 이는 효율적인 어휘 크기 관리와 더 나은 일반화를 가능하게 합니다.
- Hybrid Tokenization: 이 방법은 여러 토크나이제이션 기법을 조합하여 사용합니다. 예를 들어, 어떤 시스템은 먼저 문장을 단어로 분리한 후, 특정 단어 내에서 다시 서브워드 단위로 분리할 수 있습니다. 이는 특히 다양한 언어나 특수 용어가 혼합된 텍스트를 처리할 때 유용합니다.
위 방법론을 간단히 예를 들어보면 아래의 테이블과 같습니다.
각 토크나이제이션 방식은 특정 언어나 문제에 따라 그 효율성이 달라질 수 있으며, 적절한 방식을 선택하는 것이 자연어 처리 성능에 큰 영향을 미칩니다.
과거 연구에서는 나라별, 문화별, 언어권별로 선호되는 토크나이제이션 방식이 다양했습니다:
- 로마 언어권: 단어(word) 기반 토크나이제이션이 일반적으로 사용되었습니다. 이는 라틴계 언어의 구조상 단어 간 구분이 명확하기 때문입니다.
- 중국어 언어권: 문자(character) 기반 토크나이제이션이 주로 사용되었습니다. 중국어는 각 문자가 고유한 의미를 가지고 있기 때문에 이 방식이 효과적입니다.
- 한국어: 형태소(morpheme) 기반 토크나이제이션이 선호되었습니다. 한국어는 굴절 언어의 특성을 가지고 있어 형태소 분석이 중요합니다.
- 일본어: 역시 형태소 기반 토크나이제이션이 흔히 사용되었습니다. 일본어 역시 복잡한 굴절을 가진 언어이기 때문입니다.
- 아랍어 언어권: 스템(stem) 기반 토크나이제이션이 사용되기도 했습니다. 아랍어는 근본적으로 각 단어가 변형되는 규칙적인 패턴을 따르므로, 단어의 근본 형태를 중심으로 토큰화하는 것이 유용합니다.
그러나 최근에는 서브워드(subword) 토크나이제이션, 특히 ‘Byte Pair Encoding (BPE)’ 방식이 다양한 언어에 걸쳐 널리 사용되고 있습니다. 이 방식은 어휘의 크기를 효율적으로 관리하고, 일관된 기준으로 모든 언어를 처리할 수 있기 때문입니다.
이러한 Subword 토크나이제이션 방식을 처음 제안한 것은 Google의 “Neural Machine Translation of Rare Words with Subword Units” 논문에서였습니다. 이 논문에서는 모델 구조는 그대로 유지하면서 토크나이제이션 방식만 BPE 방식으로 바꿔서 성능 향상을 달성한 연구였습니다.
Subword 토크나이제이션 방식 설명 및 예제
Subword 토크나이제이션은 특히 자연어 처리에서 어휘 다양성과 희귀 단어의 변형을 효과적으로 처리할 수 있게 돕는 방법입니다. 이 방법은 전체 단어를 더 작은 의미 단위로 분할하여 자주 발생하지 않는 단어들이 모델에 의해 더 잘 처리될 수 있도록 합니다. 예를 들어, 단어 “unbelievable”은 “un”, “believ”, “able”로 분할될 수 있으며, 이러한 각각의 subword는 다른 단어에서도 재사용될 수 있습니다.
기본적인 원칙은 자주 사용되는 단어는 작은 subword로 분할되지 않고, 희귀 단어는 의미 있는 subword로 분해되어야 한다는 것입니다.
이제 구체적으로 이 subword를 구현하는 방법론에 대해서 살펴보겠습니다. 크게 4가지 방법론이 있습니다.
- Byte-Pair Encoding (BPE)
- Byte-Level BPE
- WordPiece
- Unigram
이 글에서는 위 내용에 대해서 각각 살펴보겠습니다.
BPE(Byte Pair Encoding) 알고리즘
BPE(Byte Pair Encoding) 알고리즘의 핵심 아이디어는 자주 등장하는 문자 쌍을 반복적으로 병합하여 새로운 단어를 생성하는 것입니다. 이를 통해 어휘 크기를 줄이면서도 원본 텍스트의 표현력을 최대한 유지할 수 있습니다.
왜 Byte Pair Encoding이라 불리는가?
BPE는 원래 1994년 Philip Gage가 데이터 압축을 위해 제안한 알고리즘입니다 (Gage, Philip. “A new algorithm for data compression.” C Users Journal 12.2 (1994): 23–38). 이 알고리즘은 데이터 내에서 가장 자주 등장하는 바이트 쌍을 반복적으로 병합하여 압축하는 방식으로 작동합니다. 예를 들어, 데이터 스트림에서 가장 빈번하게 나타나는 바이트 쌍 ‘AB’를 ‘Z’로 치환하고, 다시 가장 빈번한 바이트 쌍을 찾아 치환하는 과정을 반복합니다. 이런 방식을 취하게 되면 다음의 예처럼
- ABABABCCCABDE
- ZZZCCCZDE
이 과정을 통과하면 기존에는 13개의 바이트가 필요한 문서가 10개의 바이트로 압축이 되는 거죠.
이제 BPE를 활용한 텍스트 토크나이제이션 훈련 및 적용 방법을 자세하게 보겠습니다.
Step-by-Step 예제
우리의 데이터가 아래처럼 구성되어 있다고 가정합니다.
low low low low low lower lower
newest newest newest newest
newest newest widest widest widest
이제 이 말뭉치에 나타난 여러 단어들을 ‘문자’ 수준으로 바꾸고, 이를 초기 어휘셋으로 구성합니다. 그리고 각 어휘별로 빈도를 측정하죠.
각 문자별로 가장 많이 나타난 숫자를 보면, e-s
가 9번 나타났습니다. 이러한 조합은 많이 나타나는 조합임을 알았으므로, 이를 새로운 어휘로 등록합니다.
계속 같은 과정을 거쳐 어휘를 등록합니다.
이런 프로세스를 계속 반복하게 되면 최종 어휘를 다음과 같이 얻게 됩니다.
# dictionary
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, er, ne, new, newest, wi, wid, widest
이렇게 얻은 BPE 어휘셋을 이용해서 처음보는 단어인 “lowest”를 처리하게 되면 알고 있는 부분은 기존 어휘를 그대로 쓰고, 모르는 부분은 더 하위 조합으로 내려가면서 tokenization 되는 과정을 거칩니다.
Byte-Level BPE 알고리즘
Byte-Level BPE는 기존 BPE 알고리즘의 확장판입니다. 기존 BPE와의 차이점은 original BPE의 기본 묶음의 최소 단위가 문자(chraracter) 일 때 Byte-Level BPE는 Byte 라는 것이죠.
그렇다면 왜 Byte 수준으로 단위를 낮춰서 묶어나가야 하는 걸까요? 이를 이해하기 위해서는 1) 바이트와 문자표현의 관계, 2) Unicode 어휘 처리 및 3)적절한 수준의 어휘수의 필요성에 대해 이해를 해야 합니다.
바이트와 문자표현의 관계
바이트(Byte)는 디지털 정보의 기본 단위입니다. 1바이트는 8개의 비트(bit)로 구성되며, 각각의 비트는 0 또는 1의 값을 가집니다. 따라서 1바이트는 총 256가지(2⁸=256)의 서로 다른 값을 표현할 수 할 수 있고, 자연스럽게도 과거 컴퓨터에서는 256개의 문자를 표현할 수 있는 Extended ASCII 문자표준을 사용하게 되었습니다. 이 중 128개는 표준 ASCII 문자(영어 대소문자 52개, 숫자 10개, 기호 및 제어 문자 66개)로, 나머지 128개는 추가적인 기호, 특수 문자 및 여러 언어의 문자를 포함하였었죠.
그러나 시대가 흘러 인터넷 시대가 열리고 여러 나라와 여러 문화권의 문자나 기호 등을 문자로 담아야 할 필요성이 생기게 됩니다. 이러한 목적을 달성하기 위해 1991년도에 유니코드(Unicode) 표준이 만들어지게 됩니다. 기존 ASCII와는 다르게 이 방식은 전 세계의 모든 문자를 통합하여 표현할 수 있도록 설계되었으며, 1바이트(8비트)로 표현할 수 있는 문자 수의 제한을 넘어 최대 4바이트(32비트)까지 사용하여 다양한 문자를 포함할 수 있습니다. 유니코드는 초기 16비트로 시작하여 65,536개의 문자를 지원했고, 이후 확장되어 현재는 1,112,064개의 문자를 지원합니다. 이를 통해 다양한 언어와 특수 문자를 통일된 방식으로 표현할 수 있게 되었습니다.
Unicode 어휘 처리
위의 내용을 간단히 정리하면, 전 세계의 전문가들이 모여 “일단 전 세계의 모든 문서에 나타날 법한 ‘기호’를 1:1로 대응하는 코드”를 만들고 이를 “Unicode”라고 정의하고 표준화하는 것까지는 말씀드렸습니다. 그렇다면, 한 발 더 들어가 실제로 이러한 각 코드가 컴퓨터에 저장되고 통신을 통해 다른 기계에게 전송되기 위해 어떤 방식을 써야 ‘효율적인 인코딩’ 방식인지 고민해보아야 하겠죠. 이러한 방식들로 UTF-8, UTF-16, UTF-32 등이 제안되었고, 그중 사실상 가장 많이 쓰이고 있는 인코딩 방식이 UTF-8입니다.
예를 들어, 이모지 ‘😊’ 문자는 유니코드, UTF-8, UTF-16, UTF-32로 다음과 같이 바이트의 시퀀스로 인코딩될 수 있습니다.
문자: ‘😊’
- 유니코드: U+1F60A
- UTF-8: F0 9F 98 8A
- UTF-16: D83D DE0A
- UTF-32: 0001F60A
각 인코딩 방식은 다음과 같은 특징을 가지고 있습니다:
- 유니코드: 문자의 고유 코드 포인트를 정의합니다.
- UTF-8: 가변 길이 인코딩 방식으로, 1~4 바이트로 표현됩니다. ‘😊’는 4바이트로 인코딩됩니다.
- UTF-16: 주로 2바이트로 표현되며, 경우에 따라 4바이트를 사용합니다. ‘😊’는 4바이트로 인코딩됩니다.
- UTF-32: 고정 길이 인코딩 방식으로, 항상 4바이트를 사용합니다. ‘😊’는 4바이트로 인코딩됩니다.
좋습니다. 이제 세상의 어떤 기호든 UTF-8로 인코딩 하면 되는구나라는 것을 알았습니다. 그렇다면 자연어처리에서 쓰이는 인코딩 단위도 기본적으로 UTF-8의 단위를 쓰면 되는 걸까요? 여기서 몇가지 문제가 생깁니다.
적절한 수준의 어휘수의 필요성
트랜스포머같은 자연어처리 모델에 있어서 토크나이제이션은 크게 2가지 목적을 위해 필요합니다.
- [임베딩] 입력된 문서(문장)을 어떻게 분절화해서 모델에 넣어줄 것인가?
- [디코딩] 모델은 어떤 단위로 생성을 수행해야 할 것인가?
임베딩의 경우에는 문서를 핵심 의미를 잘 살릴 수 있는 유의미한 단위로 분절화 해주는 것도 중요합니다만, 더 나아가 Embedding matrix 의 크기를 결정짓기 때문에도 중요합니다. 예를 들어 임베딩 차원이 521이고 어휘수가 10000이라면 임베딩 Matrix 로 [521, 10000] 이 필요하게 되죠.
최초의 의도대로 Unicode 전체 문자를 모두 어휘 수준으로 삼게 되면 최소 문자단위는 모두 어휘에 있어야 하므로 [521, 1,112,064+α] 만큼의 embedding matrix 가 필요하게되죠. 이미 현실성이 너무 없는 이야기입니다.
디코딩 입장에서도 어휘수가 많아지는 것은 너무 부담스러운 일입니다. 디코딩의 본질은 매 타임스탭마다 어휘수 만큼의 분류(Classification)를수행하는 것입니다. 상식적으로 생각해도 1000개중 하나를 맞추는 문제와 1,112,064+α중 하나를 맞추는 문제 중 어떤 것이 쉬운 문제일지 바로 알 수 있습니다.
즉 Unicode 문자를 기본단위로 하는 인코딩 방식은 한계가 있다는 것입니다.
여기서 바로 Byte-Level 로의 전환이라는 아이디어가 나타난 것입니다.
Byte-Level 로의 전환
이 아이디어는 “Neural Machine Translation with Byte-Level Subwords”라는 논문에서 처음 제안되었습니다. 핵심 아이디어는 모든 문자를 바이트 단위로 처리하여 어휘 수를 줄이고, 이를 통해 디코딩 과정에서의 분류 문제를 단순화하는 것입니다. Byte-Level Subwords를 사용하면 유니코드의 복잡한 문자 집합을 단순한 바이트 시퀀스로 변환하여, 언어 모델이 더 효율적으로 작동할 수 있도록 돕습니다.
예를 들어, 아래의 UTF-8 형식의 바이트 표현을 살펴봅시다.
[영어 알파벳 “A”]
- 문자: A
- UTF-8 바이트: 0x41
- 바이트 시퀀스: [01000001]
[한글 “가”]
- 문자: 가
- UTF-8 바이트: 0xEAB080
- 바이트 시퀀스: [11101010 10110000 10000000]
[중국어 “你”]
- 문자: 你
- UTF-8 바이트: 0xE4BDA0
- 바이트 시퀀스: [11100100 10111101 10100000]
[이모지 “😊”]
- 문자: 😊
- UTF-8 바이트: 0xF09F988A
- 바이트 시퀀스: [11110000 10011111 10011000 10001010]
[아랍어 “م”]
- 문자: م
- UTF-8 바이트: 0xD985
- 바이트 시퀀스: [11011001 10000101]
Byte-Level BPE는 사람이 눈으로 인지하는 “문자(Character)” 가 아닌 내부 인코딩 과정에서 사용되는 ‘바이트’ 하나 하나를 병합을 위한 기본 유닛으로 사용하겠다는 것입니다. 예를 들어, “😊”라는 이모지는 네 개의 바이트 [11110000, 10011111, 10011000, 10001010]로 나눠져 있는데, 우리의 데이터에 11110000와 10011111가 많이 나타나서 이를 병합하게 되면 최종적으로 😊를 [11110000–10011111, 10011000, 10001010] 라는 3개 유닛으로 표현하겠다는 것입니다.
Step-by-Step 예제
우리의 데이터가 아래처럼 구성되어 있다고 가정합니다.
low low low low low 🙃 lower lower 😗
newest newest newest newest
newest newest 🤔 widest widest widest 😊
보시다시피 데이터에 이모지 같은 특수 기호들이 포함되어 있는 것을 알 수 있습니다. 이 데이터들을 이제 Byte레벨로 내려서 살펴보죠.
보다시피 이모지 같은 기호들은 4개의 바이트로 구성되어 있는 것을 알 수 있습니다. 한번 더 자세하게 보면, 각 이모지의 바이트 시퀀스에서 \xf0 \x9f
는 모두 공통적으로 많이 나타나고 있다는 것을 볼 수 있죠. 우리는 바이트를 최소 유닛으로 BPE 할 것이기 때문에 이러한 많이 나타나는 바이트 시퀀스는 묶여 나가게 될 것입니다.
WordPiece 알고리즘
WordPiece는 단어를 작은 조각(piece)으로 분할하여 처리하는 방식에서 그 이름이 유래되었습니다. 이는 단어를 더 작은 부분으로 쪼개어 처리함으로써, 어휘 크기를 줄이면서도 다양한 단어를 표현할 수 있게 합니다. 이 방식은 특히 희귀한 단어나 새로운 단어를 처리하는 데 유리하며, 어휘 크기를 줄이면서도 텍스트의 표현력을 유지하는 데 효과적입니다.
알고리즘의 유래
이 알고리즘은 BPE를 활용한 구글의 tokenization 논문보다 오히려 빨리 발표되었습니다. 2012년 구글의 Schuster와 Nakajima에 의해 “Japanese and Korean Voice Search”라는 논문에서 처음 제안되었었죠. 최초에 이 알고리즘은 언어모델의 토크나이제이션에 특화되었다기 보다는, 음성 인식 시스템의 성능을 향상시키기 위해 사용되었습니다.
음성인식 분야에 활용되던 이 테크닉은, 2016년 Sennrich et al.의 논문 “Neural Machine Translation of Rare Words with Subword Units”에서 제안된 BPE (위에서 설명한) 알고리즘이 발표되면서 다시 한번 주목을 받게 되었습니다. BPE와 WordPiece는 자주 등장하는 문자쌍을 ‘병합’ 한다는 아이디어를 공유하지만, 핵심 아이디어는 조금 다릅니다.
- BPE : 빈도 기반으로 문자 쌍을 병합
- WordPiece : 통계적 모델을 사용하여 훈련 데이터에서 가장 자주 나타나는 문자 쌍을 병합
WordPiece 알고리즘의 과정
- 초기 어휘는 모든 단어의 문자로 시작합니다.
- 가능한 모든 문자 쌍의 빈도를 계산합니다.
- 훈련 데이터의 가능도를 최대화하는 문자 쌍을 찾습니다.
- 해당 문자 쌍을 병합하여 새로운 어휘 항목을 만듭니다.
- 이 과정을 반복하여 최종 어휘를 만듭니다.
Step-by-Step 예제
우리의 데이터가 아래처럼 구성되어 있다고 가정합니다.
low low low low low lower lower
newest newest newest newest
newest newest widest widest widest
말뭉치의 데이터를 우선 ‘단어’ 수준으로 모두 분절합니다. 그리고 각 단어들 안에서 다시 문자수준으로 모두 다시 쪼갭니다. 그러면 아래와 같은 초기 분석 상태가 됩니다.
여기서 ##
기호는 각 문자가 처음 시작 문자가 아니라는 것을 의미합니다.
이제 각 단어 안에 있는 조각(지금은 문자) 를 하나씩 묶어 나갈텐데요. 기준이 필요하므로 기준점이 될 수식을 하나 정의합니다.
이 수식을 통해 계산되는 Score는 A-B pair 가 묶여서 어휘로 쓰일 때의 점수를 의미합니다. 높은 점수를 가지는 A-B pair가 어휘로 등록되는 거죠. Score 계산 예를 보겠습니다.
이러한 계산을 모든 단어에 대해 수행하면 다음과 같습니다.
점수들을 기준으로 살펴보면, 모든 문자 간 조합 중에 w##i
가 가장 높으므로, 이 조합을 신규 어휘로 등록하게 됩니다. 이러한 프로세스를 여러번 반복하게 되면 최종적으로 다음의 어휘를 가지게 됩니다.
l, ##o, ##w, ##e, ##r, n, w, ##w, ##s, ##t, ##I, ##d,
wi, wid, lo, ##st, low, ##er, lower, ne, new, newe, wide, newest, widest
즉 단어 경계 안에서 가장 조합의 점수가 높은 것을 묶어 나가는 방법이 바로 Wordpiece 기반 tokenization 인 것입니다.
Unigram 언어 모델
Unigram 토크나이제이션은 크게 두 가지 특징이 있습니다. 첫째는 어느 정도 분절된 상태에서 시작하여 작은 단위로 자른다는 것입니다. 둘째는 어떻게 ‘병합’할 것인지를 unigram 언어 모델(LM)을 통해 계산한다는 것입니다. 각 특징에 대해 살펴보겠습니다.
시작 지점: 분절된 단위
예를 들어, “hello world, this is an example sentence”라는 문장이 있다고 해보겠습니다. ‘공백’을 기준으로 분절하면 (hello, world, this, is, an, example, sentence)로 나뉩니다. 그 후 각 단어 내에서 가능한 많은 조합을 찾습니다. 예를 들어 “hello”의 경우 다음과 같은 조합이 가능합니다.
- h
- e
- l
- l
- o
- he
- hel
- ello
- hello
- …
각 단어를 가능한 모든 조합으로 분절합니다. 이렇게 분절된 단어 조각들을 기반으로 다음 단계로 넘어갑니다.
Step-by-Step 예제
우리의 데이터가 아래처럼 구성되어 있다고 가정합니다.
low low low low low lower lower
newest newest newest newest
newest newest widest widest widest
이 데이터를 우선 단어 단위로 모두 분절합니다. 그리고 각 단어별 다시 문자로 모두 분절시켜서 그것을 초기 어휘셋으로 구성합니다. 여기서 ‘_’ 표시는 단어의 시작을 의미합니다.
위 그림에서 보듯 가능한 모든 조합을 모두 어휘 대상으로 삼기 때문에, 첫 어휘셋는 매우 크게 시작한다는 것에 주목하세요. 향후 이를 줄여나가는 방향으로 개선할 것입니다.
이제 가능한 각 문자들의 조합마다의 빈도수가 구해졌으므로, 확률값도 구할 수 있습니다. 전체 빈도수를 기준으로 타겟 조합이 몇번 나타났는지를 계산하면 일종의 귀납적 확률을 계산해낼 수 있습니다.
확률 계산을 위해 low
라는 단어안에서 조합 가능한 것들에 대한 확률값을 계산해보면 다음과 같습니다.
여기서 바로 이 토크나이제이션 방법론이 Unigram 이라고 불리는지 이유가 나옵니다. P([l,o,w])=P(l)xp(o)xp(w)
의 계산에서 보듯 전체 확률값을 각각의 곱으로 근사화해서 계산하기 때문이죠.
자 이제 확률값을 어떻게 계산해내는지 알았습니다. 이제 이 확률값을 이용해 어휘수를 조금씩 줄여나가는 과정을 진행하겠습니다. 위에서 확률값을 구했으므로 이를 적절히 응용하여 우리의 말뭉치 자체의 확률, 즉 대상 말뭉치의 ‘그럴듯함(Likelihood)’ 을 계산해보겠습니다.
우리가 하려는 것의 핵심은 [A,B,C] 조합으로 말뭉치 전체의 확률값을 구했을 때와 [AB,C] 조합으로 말뭉치 전체의 확률값을 구했을 때 [AB,C] 조합이 더 높은 그럴듯함을 가진다면, [A,B,C] 라는 어휘는 탈락 시키겠다는 아이디어입니다.
여기서 논문과 실제 구현에서는 Negative Log-likehood Loss 를 정의해서 사용합니다.
여기서 P(w_i)는 단어 w_i의 확률입니다. 일반적으로 Loss는 음의 로그 가능도(Negative Log-Likelihood, NLL)로 정의됩니다. 이는 Likelihood를 로그 변환한 뒤, 음수를 취한 값으로, 다음과 같이 정의할 수 있습니다.
결국 위에서 언급한 특정 조합으로 말뭉치의 그럴듯함을 측정하여 그것이 높다는 것은 확률값이 높은 것이고, 손실값(loss)는 낮은 것을 의미합니다. 즉 손실값을 낮게 하는 조합을 취하면서 어휘셋을 조정하는 것이 필요한 거죠.
위의 그림을 보면 같은 문서에 대해서 2개의 조합으로 각각 말뭉치의 그럴듯함을 계산하였습니다. [low]
로 묶어 본 경우와 [lo, w]
로 묶어 본 경우죠. 각각의 loss를 보게 되면 [low]
조합이 108.671로 [lo, w]
조합보다 작습니다. 이렇게 되면 우리 어휘셋에서 [lo, w]
는 제거가 되는 거죠. 이런식으로 계속해서 좋은 조합을 원하는 어휘 (예를 들어 30개) 가 남을 때까지 반복하면서 찾는 것입니다.
이런식으로 여러번 반복해서 얻는 최종 어휘수는 다음과 같습니다.
대표적인 언어모델과 사용된 토큰화 방법
다음은 각 토큰화 알고리즘을 사용하는 대표적인 언어 모델과 각 모델의 어휘 수를 정리한 테이블입니다.
위의 표에서 보다시피 크게 2가지 트렌드를 볼 수 있습니다. 첫 번째는 점점 더 많은 모델이 Byte-Level BPE와 같은 토크나이제이션 방법을 채택하고 있다는 것입니다. 이는 다양한 언어와 특수 문자를 효율적으로 처리하고, 희귀 단어와 신조어에 대한 적응력을 높이기 위함 입니다. Byte-Level BPE는 문자를 바이트 단위로 처리함으로써 다국어 환경에서 특히 유리하며, 복잡한 언어 패턴을 더 잘 캡처할 수 있습니다. 두 번째는 어휘 크기가 다양해지고 있다는 것입니다. 초기 모델들은 주로 30,000에서 50,000 사이의 어휘 크기를 사용했으나, 최근의 모델들은 32,000에서 128,000까지 다양한 어휘 크기를 활용하고 있습니다. 이러한 트렌드는 더 정교한 언어 표현과 다양한 언어 지원을 위한 것으로 해석할 수 있습니다.
Tokenization 관련 도구
Tokenization과 관련해서는 대단히 많은 도구가 있습니다. 크게 대량의 데이터를 활용해 tokenization 어휘셋을 구성해주는 도구, 즉 tokenizer 도구와 이 tokenizer를 이용해서 실질적으로 tokenization을 수행하는 도구로 나눌 수 있습니다. 각 도구들의 목록은 아래와 같습니다.
Tokenizer 도구
[SentencePiece]
- 설명: 구글에서 개발한 오픈 소스 토크나이저로, BPE와 Unigram 모델을 지원합니다. 언어에 상관없이 사용할 수 있으며, 특히 다국어 데이터에 효과적입니다.
- 특징: 통합된 토크나이저, 텍스트 정규화, 다양한 토큰화 옵션.
- URL: SentencePiece GitHub
[Byte-Pair Encoding (BPE)]
- 설명: OpenNMT 및 다른 여러 NLP 라이브러리에서 사용되는 BPE 토크나이저입니다.
- 특징: 어휘 크기 관리가 용이, 희귀 단어 처리에 강점.
- URL: OpenNMT BPE
[Hugging Face Tokenizers]
- 설명: Hugging Face에서 제공하는 고성능 토크나이저 라이브러리로, BERT, GPT-2, RoBERTa 등의 모델을 위한 다양한 토크나이저를 포함합니다.
- 특징: Rust로 구현되어 있어 매우 빠르고, 다양한 토크나이저 알고리즘(BPE, WordPiece, Unigram, SentencePiece 등)을 지원합니다.
- URL: Hugging Face Tokenizer Training
[tiktoken]
- 설명: OpenAI에서 제공하는 토크나이저 라이브러리로, 특히 GPT-3와 같은 대규모 언어 모델을 위해 최적화되어 있습니다.
- 특징: 고성능 토크나이징, 메모리 효율성, 다양한 언어와 특수 문자 지원.
- URL: tiktoken Github
Tokenization 도구
[Hugging Face Transformers]
- 설명: 자연어 처리 모델을 위한 오픈 소스 라이브러리로, 다양한 사전 학습된 토크나이저를 제공합니다.
- 특징: BERT, GPT, RoBERTa 등 다양한 모델 지원, 쉽게 사용 가능한 API.
- URL: Hugging Face Tokenizers
[spaCy]
- 설명: 빠르고 효율적인 자연어 처리 라이브러리로, 텍스트의 토큰화, 구문 분석, 개체 인식 등을 제공합니다.
- 특징: 높은 성능, 쉬운 통합, 사용자 정의 가능.
- URL: spaCy
[NLTK (Natural Language Toolkit)]
- 설명: 교육과 연구를 위한 강력한 파이썬 라이브러리로, 다양한 텍스트 처리 기능을 제공합니다.
- 특징: 토큰화, 형태소 분석, 음소 분석 등 다양한 기능 포함.
- URL: NLTK
[tiktoken]
- 설명: OpenAI에서 제공하는 토크나이저 라이브러리로, 특히 GPT-3와 같은 대규모 언어 모델을 위해 최적화되어 있습니다.
- 특징: 고성능 토크나이징, 메모리 효율성, 다양한 언어와 특수 문자 지원.
- URL: tiktoken GitHub
결론
이 글에서는 자연어처리의 중요한 개념 중 하나인 토크나이제이션에 대해 살펴보았습니다. 토크나이제이션이란 텍스트 데이터를 컴퓨터가 처리할 수 있도록 작은 단위로 분할하는 과정으로, 다양한 기법이 존재합니다. 그 중에서 이번 글에서는 Subword 테크닉들 중 대표적인 BPE(Byte Pair Encoding), Byte-Level BPE, WordPiece, Unigram 등에 대해 자세하게 살펴보았습니다.
각각의 방법론들은 나름의 장단점과 특성을 가지고 있습니다. BPE는 자주 등장하는 문자 쌍을 병합하여 어휘 크기를 줄이면서도 표현력을 유지하는 데 유리합니다. Byte-Level BPE는 바이트 단위의 병합을 통해 다국어 환경에서도 효율적으로 작동합니다. WordPiece는 단어를 기준으로 단어 안에서 병합해야 할 문자 쌍을 빈도 기반으로 병합하는 방식이며, Unigram은 각 서브워드의 출현 확률을 계산하여 전체 모델의 적합성을 최대화하는 방식으로 서브워드를 선택하여 병합합니다.
위에서 살펴본 Tokenization 방법론은 아래 링크에서 직접 구현 및 테스트 해 볼 수 있습니다.
- [Hands-On] BPE(Byte Pair Encoding)를 활용한 토크나이저 구현
- [Hands-On] Wordpiece를 활용한 토크나이저 구현
- [Hands-On] Unigram을 활용한 토크나이저 구현