Buckets:
| # 토크나이저[[tokenizers]] | |
| 토크나이저는 자연어처리 파이프라인의 핵심 요소 중 하나입니다. 토크나이저의 역할은 텍스트를 모델이 처리할 수 있는 데이터로 변환하는 것입니다. 모델은 숫자만 처리할 수 있기 때문에 토크나이저는 텍스트 입력을 수치형 데이터로 변환해야 합니다. 이 장에서는 토큰화 파이프라인에서 정확히 어떤 일이 일어나고 있는지 알아볼 것입니다. | |
| 자연어처리 태스크에서 처리되는 데이터는 일반적으로 원시 텍스트입니다. 아래는 원시 텍스트의 예시입니다. | |
| ``` | |
| Jim Henson was a puppeteer | |
| ``` | |
| 그러나 모델은 숫자만 처리할 수 있기 때문에 우리는 원시 텍스트를 숫자로 바꿀 방법을 찾아야 합니다. 그게 바로 토크나이저가 하는 일이며 이 문제를 해결할 수 있는 다양한 방법이 있습니다. 목표는 모델에 가장 적합하면서 간결한 표현을 찾는 것입니다. | |
| 토큰화 알고리즘의 몇 가지 예시를 살펴보고 당신이 토큰화에 대해 가지는 궁금증에 대한 해답을 찾아봅시다. | |
| ## 단어 기반 토큰화[[word-based]] | |
| 가장 먼저 떠오르는 토큰화 유형은 _단어 기반_입니다. 몇 가지 규칙만으로도 설정 및 사용이 매우 쉽고 종종 괜찮은 결과를 출력합니다. 예를 들어, 아래 보이는 사진에서 목표는 원시 텍스트를 단어로 나누고 단어 각각에 대한 수치 표현을 찾는 것입니다. | |
| 텍스트를 나누는 방법은 다양합니다. 예를 들면 파이썬의 `split()` 함수를 이용해 공백 기준으로 텍스트를 나눌 수 있습니다. | |
| ```py | |
| tokenized_text = "Jim Henson was a puppeteer".split() | |
| print(tokenized_text) | |
| ``` | |
| ```python out | |
| ['Jim', 'Henson', 'was', 'a', 'puppeteer'] | |
| ``` | |
| 구두점을 위한 규칙이 추가된 토크나이저도 있습니다. 이러한 종류의 토크나이저를 사용하면 꽤 큰 "단어 사전"을 얻을 수 있는데, 이 때 단어 사전은 우리가 가진 말뭉치(corpus) 내 존재하는 고유한 토큰 수에 의해 정의됩니다. | |
| 각 단어에는 0부터 시작해서 단어 사전 크기까지의 ID가 할당됩니다. 모델은 각 단어를 구별하기 위해 이 ID들을 사용합니다. | |
| 단어 기반 토크나이저로 언어를 완벽하게 처리하고 싶다면 언어의 각 단어에 대한 식별자가 있어야 하며, 이는 엄청난 양의 토큰을 생성할 것입니다. 예를 들어, 영어에 500,000개가 넘는 단어가 있다고 한다면, 각 단어에 입력 ID를 매핑시키기 위해 많은 ID를 추적해야 합니다. 게다가 "dog"와 같은 단어는 "dogs"처럼 다르게 표현되어 모델이 처음에 "dog"와 "dogs"가 유사하다는 것을 알 방법이 없을 것입니다. 모델은 두 단어를 관련이 없다고 인식할 것입니다. "run"과 "running" 같은 다른 유사한 단어에도 적용되는 것으로, 모델은 처음에 두 단어를 유사한 것으로 보지 않습니다. | |
| 마지막으로, 단어 사전에 없는 단어를 표현하기 위한 커스텀 토큰이 필요합니다. "unknown" 토큰으로 알려진 이 토큰은 "[UNK]"이나 ""로 표현됩니다. 토크나이저가 unknown 토큰을 많이 만드는 것은 단어에 적합한 표현을 찾지 못해 정보를 잃어가고 있는 것이기 때문에 일반적으로 좋지 않은 신호입니다. 단어 사전을 생성할 때의 목표는 토크나이저가 단어를 unknown 토큰으로 가능한 한 적게 토큰화하는 것입니다. | |
| Unknown 토큰 수를 줄이기 위한 한 가지 방법은 한 단계 더 들어가 _문자 기반_ 토크나이저를 사용하는 것입니다. | |
| ## 문자 기반 토큰화[[character-based]] | |
| 문자 기반 토크나이저는 텍스트를 단어가 아닌 문자 단위로 나눕니다. 이 방법은 두 가지 장점이 있습니다. | |
| - 단어 사전이 간결해진다. | |
| - 모든 단어는 문자로 이루어졌기 때문에 out-of-vocabulary (unknown) 토큰의 수가 훨씬 적다. | |
| 하지만 여기에서도 공백과 구두점에 대한 몇 가지 궁금증을 가질 수 있습니다. | |
| 이 방법 또한 완벽하지 않습니다. 이제 표현은 단어가 아닌 문자에 기반을 두고 있기 때문에 누군가는 문자의 의미가 적다고 주장할 수 있습니다. 각각의 단어는 그 자체로 큰 의미가 있지만 문자는 그렇지 않습니다. 그러나 이 점은 다시 언어에 따라 달라집니다. 예를 들어, 중국어에서는 각 문자가 라틴어보다 더 많은 정보를 전달합니다. | |
| 또 다른 고려 사항은 모델이 처리해야 하는 매우 많은 양의 토큰이 생기게 된다는 것입니다. 단어 기반 토크나이저에서 단어는 단 하나의 토큰인 반면, 문자로 변환하면 10개 이상의 토큰으로 쉽게 바뀔 수 있습니다. | |
| 장점만을 최대한 활용하기 위해 두 가지 방법을 결합한 세 번째 기법인 *서브워드 토큰화*를 사용할 것입니다. | |
| ## 서브워드 토큰화[[subword-tokenization]] | |
| 서브워드 토큰화 알고리즘은 자주 사용되는 단어는 더 작은 서브워드로 나누면 안되지만, 희귀한 단어는 의미 있는 서브워드로 나눠야 한다는 규칙에 기반합니다. | |
| 예를 들면 "annoyingly"는 흔하지 않은 단어로 여겨질 수 있고 "annoying"과 "ly"로 분해할 수 있을 것입니다. 둘다 독립적인 서브워드로 자주 등장할 가능성이 있는 반면에 "annoyingly"는 "annoying"과 "ly"의 합성으로만 의미가 유지됩니다. | |
| 다음 예시는 서브워드 토큰화 알고리즘이 "Let's do tokenization!"이라는 문장을 어떻게 토큰화하는지 보여줍니다. | |
| 서브워드는 많은 양의 의미론적 정보를 제공합니다. 위의 예시에서 "tokenization"은 의미론적인 정보를 갖는 두 개의 토큰 "token"과 "ization"으로 분할되었고 긴 단어를 두 단어만으로 표현할 수 있어 공간 효율적입니다. 이 방식을 통해 크기가 작은 단어 사전으로도 많은 토큰을 표현할 수 있고 unknown 토큰도 거의 없습니다. | |
| 서브워드 토큰화를 이용한 접근법은 터키어와 같은 교착어에서 유용합니다. 서브워드를 함께 묶어 길고 복잡한 단어를 형성할 수 있습니다. | |
| ### 더 알아보기![[and-more]] | |
| 다양한 토큰화 기법이 존재하는데 몇 가지만 나열해보겠습니다. | |
| - GPT-2에서 사용된 Byte-level BPE | |
| - BERT에서 사용된 WordPiece | |
| - 여러 다언어 모델에서 사용되는 SentencePiece 또는 Unigram | |
| API를 알아보기 위해 토크나이저가 작동하는 과정에 대해 충분히 알고 있어야 합니다. | |
| ## 불러오기 및 저장[[loading-and-saving]] | |
| 토크나이저를 불러오고 저장하는 것은 모델에서 했던 것만큼 간단합니다. 사실, 모델과 동일하게 `from_pretrained()`과 `save_pretrained()` 두 메서드에 기반합니다. 이 메서드들은 토크나이저가 사용하는 알고리즘(모델의 *구조*와 약간 유사)과 단어 사전(모델 *가중치*와 약간 유사)을 불러오거나 저장합니다. | |
| BERT와 동일한 체크포인트로 학습된 BERT 토크나이저를 불러오는 것은 `BertTokenizer` 클래스를 사용하는 것만 제외하면 모델과 동일한 방식으로 수행됩니다. | |
| ```py | |
| from transformers import BertTokenizer | |
| tokenizer = BertTokenizer.from_pretrained("bert-base-cased") | |
| ``` | |
| `AutoModel`과 마찬가지로 `AutoTokenizer` 클래스는 체크포인트 이름에 기반해 적절한 토크나이저 클래스를 가져오고 어떤 체크포인트와도 함께 직접 사용할 수 있습니다. | |
| ```py | |
| from transformers import AutoTokenizer | |
| tokenizer = AutoTokenizer.from_pretrained("bert-base-cased") | |
| ``` | |
| 이제 이전 섹션에서 본 것처럼 토크나이저를 사용할 수 있습니다. | |
| ```python | |
| tokenizer("Using a Transformer network is simple") | |
| ``` | |
| ```python out | |
| {'input_ids': [101, 7993, 170, 11303, 1200, 2443, 1110, 3014, 102], | |
| 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0], | |
| 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1]} | |
| ``` | |
| 토크나이저 저장은 모델 저장과 동일합니다. | |
| ```py | |
| tokenizer.save_pretrained("directory_on_my_computer") | |
| ``` | |
| 위 출력 결과에서 `token_type_ids`는 [제3단원](/course/chapter3)에서 이야기할 것이고 `attention_mask` 키는 잠시 뒤에 설명할 것입니다. 그 전에 먼저 `input_ids`가 어떻게 만들어지는지 알아봅시다. 이를 위해 토크나이저의 중간 메서드를 들여다봐야 합니다. | |
| ## 인코딩[[encoding]] | |
| 텍스트를 숫자로 바꾸는 것은 _인코딩_으로 알려져 있습니다. 인코딩은 토큰화, 토큰을 입력 ID로 바꾸는 두 단계에 걸쳐 수행됩니다. | |
| 첫 번째 단계는 이미 봤던 것처럼 텍스트를 흔히 *토큰*이라고 부르는 단어(또는 단어의 일부, 구두점 기호 등)로 나누는 것입니다. 토크나이저를 초기화할 때 모델의 이름을 사용하는 것도 텍스트를 토큰으로 바꾸는 여러 방법 중 사전 학습에 사용한 것과 같은 방법을 사용하기 위함입니다. | |
| 두 번째 단계는 생성된 토큰들을 숫자로 변환해 텐서로 만들고 모델로 넘겨주는 것입니다. 이 과정을 위해 토크나이저는 `from_pretrained()` 메서드로 인스턴스화할 때 다운로드한 *단어 사전*을 가지고 있습니다. 여기서도 모델이 사전학습될 때 사용한 것과 동일한 단어 사전을 사용해야 합니다. | |
| 각 단계별로 더 자세히 알아봅시다. 여기서는 학습을 위해 토큰화 파이프라인의 부분적인 역할을 하는 함수들을 사용해 중간 결과물을 보여주지만 실전에서는 "2장, 파이프라인 내부 동작 과정"에서와 같이 토크나이저를 직접 사용하시기 바랍니다. | |
| ### 토큰화[[tokenization]] | |
| 토큰화 과정은 토크나이저의 `tokenize()` 메서드를 통해 작동합니다. | |
| ```py | |
| from transformers import AutoTokenizer | |
| tokenizer = AutoTokenizer.from_pretrained("bert-base-cased") | |
| sequence = "Using a Transformer network is simple" | |
| tokens = tokenizer.tokenize(sequence) | |
| print(tokens) | |
| ``` | |
| 이 메서드의 출력 결과는 문자열 리스트이거나 토큰입니다. | |
| ```python out | |
| ['Using', 'a', 'transform', '##er', 'network', 'is', 'simple'] | |
| ``` | |
| 이 토크나이저는 서브워드 토크나이저입니다. 단어 사전으로 표현할 수 있는 토큰을 얻을 때까지 단어를 분할합니다. `transformer`의 경우 `transform`과 `##er`로 나눠집니다. | |
| ### 토큰에서 입력 ID까지[[from-tokens-to-input-ids]] | |
| 토크나이저의 `convert_tokens_to_ids()` 메서드를 이용해 토큰을 입력 ID로 변환합니다. | |
| ```py | |
| ids = tokenizer.convert_tokens_to_ids(tokens) | |
| print(ids) | |
| ``` | |
| ```python out | |
| [7993, 170, 11303, 1200, 2443, 1110, 3014] | |
| ``` | |
| 적절한 프레임워크의 텐서로 변환되고 나면 이 출력 결과는 이전 장에서 본 것처럼 모델 입력으로 사용될 수 있습니다. | |
| > [!TIP] | |
| > ✏️ **직접 해보세요!** 2장에서 사용한 입력 문장("I've been waiting for a HuggingFace course my whole life."과 "I hate this so much!")을 이용해 두 단계(토큰화와 입력 ID로의 변환)를 수행해보세요. 위에서 얻은 결과와 당신이 얻은 결과가 동일한지 확인해보세요! | |
| ## 디코딩[[decoding]] | |
| 단어 사전의 인덱스로부터 문자열을 얻고 싶기 때문에 *디코딩*은 인코딩과 반대로 진행됩니다. 아래처럼 `decode()` 메서드를 이용할 수 있습니다. | |
| ```py | |
| decoded_string = tokenizer.decode([7993, 170, 11303, 1200, 2443, 1110, 3014]) | |
| print(decoded_string) | |
| ``` | |
| ```python out | |
| 'Using a Transformer network is simple' | |
| ``` | |
| `decode` 메서드는 인덱스를 토큰으로 바꿀 뿐만 아니라, 읽기 좋은 문장을 만들기 위해 같은 단어의 일부인 토큰을 그룹화합니다. 이 과정은 새 텍스트(프롬프트에서 생성된 텍스트 또는 번역이나 요약과 같은 시퀀스 간 문제)를 예측하는 모델을 쓸 때 매우 유용합니다. | |
| 이제 토크나이저가 수행하는 토큰화, ID로의 변환, ID를 다시 문자열로 변환하는 과정을 이해할 수 있어야 합니다. 그러나 이는 빙산의 일각입니다. 다음 장에서는 이 접근법의 한계를 알아보고, 이를 극복하는 방법을 알아볼 것입니다. | |
Xet Storage Details
- Size:
- 12.6 kB
- Xet hash:
- e00693e67784a07e3959df088fe5c29d10238b1ed12ef45813609b98a98888d8
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.