diff --git a/chapters/ko/_toctree.yml b/chapters/ko/_toctree.yml index 54c36a4a9..7a523be1c 100644 --- a/chapters/ko/_toctree.yml +++ b/chapters/ko/_toctree.yml @@ -35,6 +35,17 @@ title: 파이프라인 내부 동작 과정 - local: chapter2/3 title: 모델 + - local: chapter2/4 + title: 토크나이저 + - local: chapter2/5 + title: 다중 시퀀스 처리 + - local: chapter2/6 + title: 한 번에 실행하기 + - local: chapter2/7 + title: 기본 사용 완료! + - local: chapter2/8 + title: 단원 마무리 퀴즈 + quiz: 2 - title: 5. 🤗 Datasets 라이브러리 sections: diff --git a/chapters/ko/chapter2/2.mdx b/chapters/ko/chapter2/2.mdx index 718ea976d..3f246f672 100644 --- a/chapters/ko/chapter2/2.mdx +++ b/chapters/ko/chapter2/2.mdx @@ -344,7 +344,7 @@ model.config.id2label - 첫 번째 문장: NEGATIVE: 0.0402, POSITIVE: 0.9598 - 두 번째 문장: NEGATIVE: 0.9995, POSITIVE: 0.0005 -파이프라인 세 단게-토크나이저를 이용한 전처리, 모델에 입력 넣어주기, 후처리-를 성공적으로 재현했습니다! 이제 각 단계별로 좀 더 깊게 알아보는 시간을 가져봅시다. +파이프라인 세 단계-토크나이저를 이용한 전처리, 모델에 입력 넣어주기, 후처리-를 성공적으로 재현했습니다! 이제 각 단계별로 좀 더 깊게 알아보는 시간을 가져봅시다. diff --git a/chapters/ko/chapter2/4.mdx b/chapters/ko/chapter2/4.mdx new file mode 100644 index 000000000..fed8fc9cf --- /dev/null +++ b/chapters/ko/chapter2/4.mdx @@ -0,0 +1,241 @@ + + +# 토크나이저[[tokenizers]] + +{#if fw === 'pt'} + + + +{:else} + + + +{/if} + + + +토크나이저는 자연어처리 파이프라인의 핵심 요소 중 하나입니다. 토크나이저의 역할은 텍스트를 모델이 처리할 수 있는 데이터로 변환하는 것입니다. 모델은 숫자만 처리할 수 있기 때문에 토크나이저는 텍스트 입력을 수치형 데이터로 변환해야 합니다. 이 장에서는 토큰화 파이프라인에서 정확히 어떤 일이 일어나고 있는지 알아볼 것입니다. + +자연어처리 태스크에서 처리되는 데이터는 일반적으로 원시 텍스트입니다. 아래는 원시 텍스트의 예시입니다. + +``` +Jim Henson was a puppeteer +``` + +그러나 모델은 숫자만 처리할 수 있기 때문에 우리는 원시 텍스트를 숫자로 바꿀 방법을 찾아야 합니다. 그게 바로 토크나이저가 하는 일이며 이 문제를 해결할 수 있는 다양한 방법이 있습니다. 목표는 모델에 가장 적합하면서 간결한 표현을 찾는 것입니다. + +토큰화 알고리즘의 몇 가지 예시를 살펴보고 당신이 토큰화에 대해 가지는 궁금증에 대한 해답을 찾아봅시다. + +## 단어 기반 토큰화[[word-based]] + + + +가장 먼저 떠오르는 토큰화 유형은 _단어 기반_입니다. 몇 가지 규칙만으로도 설정 및 사용이 매우 쉽고 종종 괜찮은 결과를 출력합니다. 예를 들어, 아래 보이는 사진에서 목표는 원시 텍스트를 단어로 나누고 단어 각각에 대한 수치 표현을 찾는 것입니다. + +
+ An example of word-based tokenization. + +
+ +텍스트를 나누는 방법은 다양합니다. 예를 들면 파이썬의 `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) 토큰의 수가 훨씬 적다. + +하지만 여기에서도 공백과 구두점에 대한 몇 가지 궁금증을 가질 수 있습니다. + +
+ An example of character-based tokenization. + +
+ +이 방법 또한 완벽하지 않습니다. 이제 표현은 단어가 아닌 문자에 기반을 두고 있기 때문에 누군가는 문자의 의미가 적다고 주장할 수 있습니다. 각각의 단어는 그 자체로 큰 의미가 있지만 문자는 그렇지 않습니다. 그러나 이 점은 다시 언어에 따라 달라집니다. 예를 들어, 중국어에서는 각 문자가 라틴어보다 더 많은 정보를 전달합니다. + +또 다른 고려 사항은 모델이 처리해야 하는 매우 많은 양의 토큰이 생기게 된다는 것입니다. 단어 기반 토크나이저에서 단어는 단 하나의 토큰인 반면, 문자로 변환하면 10개 이상의 토큰으로 쉽게 바뀔 수 있습니다. + +장점만을 최대한 활용하기 위해 두 가지 방법을 결합한 세 번째 기법인 *서브워드 토큰화*를 사용할 것입니다. + +## 서브워드 토큰화[[subword-tokenization]] + + + +서브워드 토큰화 알고리즘은 자주 사용되는 단어는 더 작은 서브워드로 나누면 안되지만, 희귀한 단어는 의미 있는 서브워드로 나눠야 한다는 규칙에 기반합니다. + +예를 들면 "annoyingly"는 흔하지 않은 단어로 여겨질 수 있고 "annoying"과 "ly"로 분해할 수 있을 것입니다. 둘다 독립적인 서브워드로 자주 등장할 가능성이 있는 반면에 "annoyingly"는 "annoying"과 "ly"의 합성으로만 의미가 유지됩니다. + +다음 예시는 서브워드 토큰화 알고리즘이 "Let's do tokenization!"이라는 문장을 어떻게 토큰화하는지 보여줍니다. + +
+ A subword tokenization algorithm. + +
+ +서브워드는 많은 양의 의미론적 정보를 제공합니다. 위의 예시에서 "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") +``` + +{#if fw === 'pt'} +`AutoModel`과 마찬가지로 `AutoTokenizer` 클래스는 체크포인트 이름에 기반해 적절한 토크나이저 클래스를 가져오고 어떤 체크포인트와도 함께 직접 사용할 수 있습니다. + +{:else} +`TFAutoModel`과 마찬가지로 `AutoTokenizer` 클래스는 체크포인트 이름에 기반해 적절한 토크나이저 클래스를 가져오고 어떤 체크포인트와도 함께 직접 사용할 수 있습니다. + +{/if} + +```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로 바꾸는 두 단계에 걸쳐 수행됩니다. + +첫 번째 단계는 이미 봤던 것처럼 텍스트를 흔히 *토큰*이라고 부르는 단어(또는 단어의 일부, 구두점 기호 등)로 나누는 것입니다. There are multiple rules that can govern that process, which is why we need to instantiate the tokenizer using the name of the model, to make sure we use the same rules that were used when the model was pretrained. + +두 번째 단계는 생성된 토큰들을 숫자로 변환해 텐서로 만들고 모델로 넘겨주는 것입니다. 이 과정을 위해 토크나이저는 `from_pretrained()` 메서드로 인스턴스화할 때 다운로드한 *단어 사전*을 가지고 있습니다. 여기서도 모델이 사전학습될 때 사용한 것과 동일한 단어 사전을 사용해야 합니다. + +두 단계를 더 잘 이해하기 위해, 단계별로 알아봅시다. Note that we will use some methods that perform parts of the tokenization pipeline separately to show you the intermediate results of those steps, but in practice, you should call the tokenizer directly on your inputs (as shown in the section 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] +``` + +적절한 프레임워크의 텐서로 변환되고 나면 이 출력 결과는 이전 장에서 본 것처럼 모델 입력으로 사용될 수 있습니다. + + + +✏️ **직접 해보세요!** 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를 다시 문자열로 변환하는 과정을 이해할 수 있어야 합니다. 그러나 이는 빙산의 일각입니다. 이어지는 섹션에서는 이 접근법의 한계를 알아보고, 이를 극복하는 방법을 알아볼 것입니다. diff --git a/chapters/ko/chapter2/5.mdx b/chapters/ko/chapter2/5.mdx new file mode 100644 index 000000000..faf950b4f --- /dev/null +++ b/chapters/ko/chapter2/5.mdx @@ -0,0 +1,338 @@ + + +# 다중 시퀀스 처리[[handling-multiple-sequences]] + +{#if fw === 'pt'} + + + +{:else} + + + +{/if} + +{#if fw === 'pt'} + +{:else} + +{/if} + +이전 섹션에서, 우리는 가장 간단한 사용 예시를 확인했습니다. 길이가 짧은 단일 시퀀스에 대한 추론을 수행했습니다. 그러나 이미 몇 가지 궁금증이 생깁니다. + +- 여러 개의 시퀀스는 어떻게 처리하나요? +- *서로 다른 길이*를 갖는 다중 시퀀스를 어떻게 처리하나요? +- 단어 사전의 인덱스가 모델이 잘 작동하게 하는 유일한 입력인가요? +- 엄청 긴 시퀀스를 처리하는 방법이 있나요? + +이러한 질문들이 제기하는 문제에 대해 알아보고, 🤗 Transformers API를 이용해 이 문제들을 어떻게 해결할 수 있는지 살펴봅시다. + +## 모델은 배치 입력을 기대합니다[[models-expect-a-batch-of-inputs]] + +우리는 이전 실습에서 시퀀스가 숫자 리스트로 어떻게 바뀌는지 확인했습니다. 이 숫자 리스트를 텐서로 바꾸고 모델로 보내봅시다.: + +{#if fw === 'pt'} +```py +import torch +from transformers import AutoTokenizer, AutoModelForSequenceClassification + +checkpoint = "distilbert-base-uncased-finetuned-sst-2-english" +tokenizer = AutoTokenizer.from_pretrained(checkpoint) +model = AutoModelForSequenceClassification.from_pretrained(checkpoint) + +sequence = "I've been waiting for a HuggingFace course my whole life." + +tokens = tokenizer.tokenize(sequence) +ids = tokenizer.convert_tokens_to_ids(tokens) +input_ids = torch.tensor(ids) +# 이 코드는 실행되지 않을 것입니다. +model(input_ids) +``` + +```python out +IndexError: Dimension out of range (expected to be in range of [-1, 0], but got 1) +``` +{:else} +```py +import tensorflow as tf +from transformers import AutoTokenizer, TFAutoModelForSequenceClassification + +checkpoint = "distilbert-base-uncased-finetuned-sst-2-english" +tokenizer = AutoTokenizer.from_pretrained(checkpoint) +model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint) + +sequence = "I've been waiting for a HuggingFace course my whole life." + +tokens = tokenizer.tokenize(sequence) +ids = tokenizer.convert_tokens_to_ids(tokens) +input_ids = tf.constant(ids) +# 이 코드는 실행되지 않을 것입니다. +model(input_ids) +``` + +```py out +InvalidArgumentError: Input to reshape is a tensor with 14 values, but the requested shape has 196 [Op:Reshape] +``` +{/if} + +이런! 2장의 파이프라인을 순서대로 따라했는데 왜 실행되지 않는 걸까요? + +🤗 Transformers 모델은 기본적으로 여러 개의 문장을 입력으로 받는데 하나의 시퀀스만을 모델에 넘겨줬기 때문에 발생하는 문제입니다. 여기서 우리는 토크나이저를 `시퀀스`에 적용했을 때 뒤에서 일어나고 있는 모든 일을 수행하려고 했습니다. 하지만 자세히 보면, 토크나이저가 입력 ID 리스트를 텐서로 바꿨을 뿐만 아니라 차원도 추가한 것을 알 수 있습니다. + +{#if fw === 'pt'} +```py +tokenized_inputs = tokenizer(sequence, return_tensors="pt") +print(tokenized_inputs["input_ids"]) +``` + +```python out +tensor([[ 101, 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, + 2607, 2026, 2878, 2166, 1012, 102]]) +``` +{:else} +```py +tokenized_inputs = tokenizer(sequence, return_tensors="tf") +print(tokenized_inputs["input_ids"]) +``` + +```py out + +``` +{/if} + +새로운 차원을 추가해서 다시 시도해봅시다. + +{#if fw === 'pt'} +```py +import torch +from transformers import AutoTokenizer, AutoModelForSequenceClassification + +checkpoint = "distilbert-base-uncased-finetuned-sst-2-english" +tokenizer = AutoTokenizer.from_pretrained(checkpoint) +model = AutoModelForSequenceClassification.from_pretrained(checkpoint) + +sequence = "I've been waiting for a HuggingFace course my whole life." + +tokens = tokenizer.tokenize(sequence) +ids = tokenizer.convert_tokens_to_ids(tokens) + +input_ids = torch.tensor([ids]) +print("Input IDs:", input_ids) + +output = model(input_ids) +print("Logits:", output.logits) +``` +{:else} +```py +import tensorflow as tf +from transformers import AutoTokenizer, TFAutoModelForSequenceClassification + +checkpoint = "distilbert-base-uncased-finetuned-sst-2-english" +tokenizer = AutoTokenizer.from_pretrained(checkpoint) +model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint) + +sequence = "I've been waiting for a HuggingFace course my whole life." + +tokens = tokenizer.tokenize(sequence) +ids = tokenizer.convert_tokens_to_ids(tokens) + +input_ids = tf.constant([ids]) +print("Input IDs:", input_ids) + +output = model(input_ids) +print("Logits:", output.logits) +``` +{/if} + +입력 ID와 결과 로짓을 출력한 결과입니다. + +{#if fw === 'pt'} +```python out +Input IDs: [[ 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012]] +Logits: [[-2.7276, 2.8789]] +``` +{:else} +```py out +Input IDs: tf.Tensor( +[[ 1045 1005 2310 2042 3403 2005 1037 17662 12172 2607 2026 2878 + 2166 1012]], shape=(1, 14), dtype=int32) +Logits: tf.Tensor([[-2.7276208 2.8789377]], shape=(1, 2), dtype=float32) +``` +{/if} + +*배치*는 한 번에 여러 개의 문장을 모델로 보내는 방법입니다. 만약 단 한 개의 문장을 가지고 있다면 한 문장을 위한 배치를 만들 수 있습니다. + +``` +batched_ids = [ids, ids] +``` + +동일한 문장 2개로 만든 배치입니다! + + + +✏️ **직접 해보세요!** 이 `batched_ids` 리스트를 텐서로 변환하고 모델로 전달해보세요. 이전에 얻은 로짓과 동일한 결과를 얻는지 확인해보세요. (개수는 두 개여야 합니다!) + + + +배치는 여러 개의 문장을 모델로 넘겼을 때도 모델이 작동하게 합니다. 다중 시퀀스를 사용하는 것은 단일 시퀀스로 배치를 만드는 것만큼 간단합니다. 하지만 두 번째 문제가 있습니다. 두 개 이상의 문장을 배치로 만드려고 할 때, 그 문장들은 아마 다른 길이를 가지고 있을 것입니다. 이전에 텐서를 다뤄본 사람이라면, 텐서의 형태가 직사각형이어야 한다는 것을 알고 있습니다. 문장 길이가 다르면 입력 ID 리스트를 텐서로 바로 변환할 수 없습니다. 이 문제를 해결하기 위해, 일반적으로 입력에 *패드*를 추가합니다. + +## 입력에 패딩 추가하기[[padding-the-inputs]] + +아래 보이는 리스트는 텐서로 변환될 수 없습니다. + +```py no-format +batched_ids = [ + [200, 200, 200], + [200, 200] +] +``` + +*패딩*을 이용해 텐서가 직사각형 형태를 가질 수 있게 하면 이 문제를 해결할 수 있습니다. 패딩은 길이가 짧은 문장에 *패딩 토큰*이라고 불리는 특별한 토큰을 추가함으로써 모든 문장이 같은 길이를 갖게 합니다. 10개의 단어로 이루어진 문장 10개와 20개의 단어로 이루어진 문장 1개를 가지고 있다고 가정한다면, 패딩은 모든 문장이 20개의 단어를 갖게 하는 역할을 합니다. 우리가 사용하는 예시에서 결과 텐서는 다음과 같습니다. + +```py no-format +padding_id = 100 + +batched_ids = [ + [200, 200, 200], + [200, 200, padding_id], +] +``` + +패딩 토큰의 ID는 `tokenizer.pad_token_id` 를 통해 확인할 수 있습니다. 이걸 사용해서 두 문장을 각각 모델로 보내고 하나의 배치로 만들어 봅시다. + +{#if fw === 'pt'} +```py no-format +model = AutoModelForSequenceClassification.from_pretrained(checkpoint) + +sequence1_ids = [[200, 200, 200]] +sequence2_ids = [[200, 200]] +batched_ids = [ + [200, 200, 200], + [200, 200, tokenizer.pad_token_id], +] + +print(model(torch.tensor(sequence1_ids)).logits) +print(model(torch.tensor(sequence2_ids)).logits) +print(model(torch.tensor(batched_ids)).logits) +``` + +```python out +tensor([[ 1.5694, -1.3895]], grad_fn=) +tensor([[ 0.5803, -0.4125]], grad_fn=) +tensor([[ 1.5694, -1.3895], + [ 1.3373, -1.2163]], grad_fn=) +``` +{:else} +```py no-format +model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint) + +sequence1_ids = [[200, 200, 200]] +sequence2_ids = [[200, 200]] +batched_ids = [ + [200, 200, 200], + [200, 200, tokenizer.pad_token_id], +] + +print(model(tf.constant(sequence1_ids)).logits) +print(model(tf.constant(sequence2_ids)).logits) +print(model(tf.constant(batched_ids)).logits) +``` + +```py out +tf.Tensor([[ 1.5693678 -1.3894581]], shape=(1, 2), dtype=float32) +tf.Tensor([[ 0.5803005 -0.41252428]], shape=(1, 2), dtype=float32) +tf.Tensor( +[[ 1.5693681 -1.3894582] + [ 1.3373486 -1.2163193]], shape=(2, 2), dtype=float32) +``` +{/if} + +배치되어 있는 예측 결과의 로짓이 뭔가 잘못된 것 같습니다. 두 번째 행은 두 번째 문장의 로짓과 값이 같아야 하는데 완전히 다른 값을 얻었습니다! + +이것은 Transformer 모델의 핵심 기능이 각 토큰을 *문맥화*하는 어텐션 레이어이기 때문입니다. 어텐션 레이어는 시퀀스 내 모든 토큰을 처리하기 때문에 패딩 토큰도 고려합니다. 서로 다른 길이를 가지는 문장 각각을 모델로 전달했을 때와 패딩이 추가되어 길이가 같아진 문장들을 배치로 전달했을 때의 결과가 같기 위해서는 이 어텐션 레이어들에게 패딩 토큰을 무시하라고 알려주어야 합니다. 이 역할을 어텐션 마스크가 수행합니다. + +## 어텐션 마스크[[attention-masks]] + +*어텐션 마스크*는 입력 ID 텐서와 같은 크기를 같는 텐서로, 0과 1로 이루어져 있습니다. 1은 해당 토큰을 주의 깊게 봐야한다는 것을 의미하고 0은 해당 토큰을 신경 쓰지 않아도 된다는 의미입니다. (다시 말해, 0에 해당하는 토큰은 모델의 어텐션 레이어에서 무시되어야 한다는 뜻입니다.) + +어텐션 마스크와 함께 이전 예시를 완성해봅시다. + +{#if fw === 'pt'} +```py no-format +batched_ids = [ + [200, 200, 200], + [200, 200, tokenizer.pad_token_id], +] + +attention_mask = [ + [1, 1, 1], + [1, 1, 0], +] + +outputs = model(torch.tensor(batched_ids), attention_mask=torch.tensor(attention_mask)) +print(outputs.logits) +``` + +```python out +tensor([[ 1.5694, -1.3895], + [ 0.5803, -0.4125]], grad_fn=) +``` +{:else} +```py no-format +batched_ids = [ + [200, 200, 200], + [200, 200, tokenizer.pad_token_id], +] + +attention_mask = [ + [1, 1, 1], + [1, 1, 0], +] + +outputs = model(tf.constant(batched_ids), attention_mask=tf.constant(attention_mask)) +print(outputs.logits) +``` + +```py out +tf.Tensor( +[[ 1.5693681 -1.3894582 ] + [ 0.5803021 -0.41252586]], shape=(2, 2), dtype=float32) +``` +{/if} + +이제 배치 내 두 번째 문장과 동일한 로짓을 얻었습니다. + +두 번째 문장의 어텐션 마스크에서 마지막 값인 0은 패딩 ID라는 것을 잊지 마세요. + + + +✏️ **직접 해보세요!** 2장에서 사용한 두 개의 문장("I've been waiting for a HuggingFace course my whole life." and "I hate this so much!")을 이용해 직접 토큰화를 적용해보세요. 토큰화 결과를 모델에 넘기고 2장에서 얻은 것과 동일한 로짓을 얻었는지 확인해보세요. 이제 Now batch them together using the padding token, then create the proper attention mask. Check that you obtain the same results when going through the model! + + + +## 길이가 긴 시퀀스[[longer-sequences]] + +Transformer 모델을 사용할 때, 모델에 넘겨줄 수 있는 시퀀스 길이에 제한이 있습니다. 대부분의 모델은 최대 512개나 1024개의 토큰으로 이루어진 시퀀스를 처리할 수 있으며 더 긴 길이의 시퀀스를 처리해달라는 요청을 받으면 작동하지 않습니다. 이 문제에 대한 해결 방법은 2가지가 있습니다. + +- 긴 시퀀스를 처리할 수 있는 모델을 사용하세요. +- 시퀀스 길이를 최대 길이에 맞게 잘라내세요. + +모델별로 지원하는 시퀀스 길이는 다르고 몇 개의 특별한 모델은 엄청나게 긴 시퀀스를 처리할 수 있습니다. [Longformer](https://huggingface.co/transformers/model_doc/longformer.html) 가 그 중 하나이며, [LED](https://huggingface.co/transformers/model_doc/led.html)도 해당합니다. 만약 매우 긴 길이의 시퀀스를 다뤄야 하는 태스크를 진행하고 있다면, 두 모델을 살펴보세요. + +그렇지 않으면 `max_sequence_length` 파라미터를 이용해 시퀀스 길이를 잘라내는 것이 좋습니다. + +```py +sequence = sequence[:max_sequence_length] +``` diff --git a/chapters/ko/chapter2/6.mdx b/chapters/ko/chapter2/6.mdx new file mode 100644 index 000000000..e564a39fa --- /dev/null +++ b/chapters/ko/chapter2/6.mdx @@ -0,0 +1,164 @@ + + +# 한 번에 실행하기[[putting-it-all-together]] + +{#if fw === 'pt'} + + + +{:else} + + + +{/if} + +지난 섹션에서는 대부분의 과정을 하나씩 수행해왔습니다. 토크나이저의 작동 방식을 살펴보고 토큰화, 입력 ID로의 변환, 패딩, 잘라내기 그리고 어텐션 마스크에 대해 알아봤습니다. + +하지만 2장에서 보았듯이 우리는 🤗 Transformers API의 고수준 함수로 이 모든 것을 처리할 수 있습니다. 문장을 이용해 `tokenizer`를 호출하면 모델로 넘겨줄 수 있는 입력을 얻게 됩니다. + +```py +from transformers import AutoTokenizer + +checkpoint = "distilbert-base-uncased-finetuned-sst-2-english" +tokenizer = AutoTokenizer.from_pretrained(checkpoint) + +sequence = "I've been waiting for a HuggingFace course my whole life." + +model_inputs = tokenizer(sequence) +``` + +이제 `model_inputs` 변수는 모델이 잘 동작하기 위해 필요한 모든 것을 가지고 있습니다. DistilBERT는 어텐션 마스크뿐만 아니라 입력 ID도 포함합니다. 추가적인 입력을 받는 다른 모델들도 `tokenizer` 객체에 의해 생기는 결과물을 가지고 있습니다. + +아래의 예시를 보면 tokenizer 메서드는 매우 강력합니다. 먼저, 이 메서드는 단일 시퀀스를 토큰화할 수 있습니다. + +```py +sequence = "I've been waiting for a HuggingFace course my whole life." + +model_inputs = tokenizer(sequence) +``` + +또한 API의 변경 없이 여러 개의 시퀀스를 한 번에 처리할 수 있습니다. + +```py +sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"] + +model_inputs = tokenizer(sequences) +``` + +원하는대로 패딩을 추가할 수 있습니다. + +```py +# 가장 긴 시퀀스의 길이에 맞게 패딩을 추가합니다. +model_inputs = tokenizer(sequences, padding="longest") + +# 모델이 지원하는 최대 시퀀스 길이에 맞게 패딩을 추가합니다. +# (BERT나 DistilBERT의 최대 길이는 512) +model_inputs = tokenizer(sequences, padding="max_length") + +# 지정한 길이에 맞게 패딩을 추가합니다. +model_inputs = tokenizer(sequences, padding="max_length", max_length=8) +``` + +시퀀스 길이를 잘라낼 수도 있습니다. + +```py +sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"] + +# 모델이 지원하는 최대 시퀀스 길이에 맞게 시퀀스 길이를 잘라냅니다. +# (BERT나 DistilBERT의 최대 길이는 512) +model_inputs = tokenizer(sequences, truncation=True) + +# 지정한 최대 길이에 맞게 시퀀스 길이를 잘라냅니다. +model_inputs = tokenizer(sequences, max_length=8, truncation=True) +``` + +`tokenizer` 객체를 이용해 결과를 특정 프레임워크의 텐서로 변환할 수 있으며, 이는 모델에 바로 보내질 수 있습니다. 예를 들어 아래 코드 예시에서 토크나이저가 프레임워크에 따라 다른 텐서를 반환하게 했습니다 - `"pt"`는 PyTorch 텐서를 반환하고 `"tf"`는 TensorFlow 텐서를 반환하며, `"np"`는 NumPy 배열을 반환합니다. + +```py +sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"] + +# PyTorch 텐서를 반환합니다. +model_inputs = tokenizer(sequences, padding=True, return_tensors="pt") + +# TensorFlow 텐서를 반환합니다. +model_inputs = tokenizer(sequences, padding=True, return_tensors="tf") + +# NumPy 배열을 반환합니다. +model_inputs = tokenizer(sequences, padding=True, return_tensors="np") +``` + +## 특수 토큰[[special-tokens]] + +토크나이저가 반환한 입력 ID를 자세히 살펴보면 이전에 봤던 결과와 조금 다르다는 것을 알 수 있습니다. + +```py +sequence = "I've been waiting for a HuggingFace course my whole life." + +model_inputs = tokenizer(sequence) +print(model_inputs["input_ids"]) + +tokens = tokenizer.tokenize(sequence) +ids = tokenizer.convert_tokens_to_ids(tokens) +print(ids) +``` + +```python out +[101, 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012, 102] +[1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012] +``` + +시작과 끝에 추가된 토큰 ID가 있습니다. 두 시퀀스의 ID가 무엇을 의미하는지 확인하기 위해 디코딩해보겠습니다. + +```py +print(tokenizer.decode(model_inputs["input_ids"])) +print(tokenizer.decode(ids)) +``` + +```python out +"[CLS] i've been waiting for a huggingface course my whole life. [SEP]" +"i've been waiting for a huggingface course my whole life." +``` + +토크나이저는 문장이 시작할 떄 `[CLS]`라는 특별한 토큰을 붙이고, 끝날 때는 `[SEP]` 토큰을 붙입니다. 이런 특별한 토큰을 사용하는 이유는 모델이 사전학습될 때 이 토큰들을 사용했기 때문에 추론 시 동일한 결과를 얻기 위함입니다. 참고로 몇몇 모델은 특수 토큰을 추가하지 않아도 되고, 어떤 모델은 다른 토큰을 추가하기도 합니다. 또한, 이러한 특수 토큰을 시작 부분이나 끝 부분에만 추가하는 모델도 있습니다. 어떤 경우든 토크나이저는 토크나이저로 어떤 내용이 들어올지 알고 있고 이 내용을 처리해줄 것입니다. + +## 마무리: 토크나이저에서 모델까지[[wrapping-up-from-tokenizer-to-model]] + +지금까지 `tokenizer` 객체가 텍스트에 적용될 때 거치는 개별적인 단계를 모두 살펴보았습니다. 이제 마지막으로 이 객체가 패딩을 이용해 여러 시퀀스를 어떻게 처리하는지, 잘라내기를 통해 매우 긴 문장을 어떻게 처리하는지, 주요 API에 따라 다양한 텐서를 다루는 법을 알아봅시다. + +{#if fw === 'pt'} +```py +import torch +from transformers import AutoTokenizer, AutoModelForSequenceClassification + +checkpoint = "distilbert-base-uncased-finetuned-sst-2-english" +tokenizer = AutoTokenizer.from_pretrained(checkpoint) +model = AutoModelForSequenceClassification.from_pretrained(checkpoint) +sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"] + +tokens = tokenizer(sequences, padding=True, truncation=True, return_tensors="pt") +output = model(**tokens) +``` +{:else} +```py +import tensorflow as tf +from transformers import AutoTokenizer, TFAutoModelForSequenceClassification + +checkpoint = "distilbert-base-uncased-finetuned-sst-2-english" +tokenizer = AutoTokenizer.from_pretrained(checkpoint) +model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint) +sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"] + +tokens = tokenizer(sequences, padding=True, truncation=True, return_tensors="tf") +output = model(**tokens) +``` +{/if} diff --git a/chapters/ko/chapter2/7.mdx b/chapters/ko/chapter2/7.mdx new file mode 100644 index 000000000..8c6b6349d --- /dev/null +++ b/chapters/ko/chapter2/7.mdx @@ -0,0 +1,18 @@ +# 기본 사용 완료![[basic-usage-completed]] + + + +여기까지 오느라 수고하셨습니다! 이 단원에서 배운 것을 되돌아봅시다. + +- Transformer 모델의 기본 구성 요소에 대해 배웠습니다. +- 토큰화 파이프라인의 구성 요소에 대해 배웠습니다. +- Transformer 모델을 사용하는 방법을 알아보았습니다. +- 토크나이저를 이용해 텍스트를 모델이 이해할 수 있는 텐서로 변환하는 방법을 배웠습니다. +- 원시 텍스트에서 예측 결과를 얻기 위해 토크나이저와 모델을 함께 사용하는 법을 배웠습니다. +- 입력 ID의 한계와 어텐션 마스크에 대해 배웠습니다. +- 다양하고 설정 가능한 토크나이저 메서드를 사용해보았습니다. + +이제부터는 🤗 Transformers 문서 안에서 자유롭게 항해할 수 있어야 합니다. 단어는 익숙하게 들릴 것이고, 사용법을 익히는 데에 이미 충분한 시간을 들였습니다. diff --git a/chapters/ko/chapter2/8.mdx b/chapters/ko/chapter2/8.mdx new file mode 100644 index 000000000..ddc6f4558 --- /dev/null +++ b/chapters/ko/chapter2/8.mdx @@ -0,0 +1,310 @@ + + + + +# 단원 마무리 퀴즈[[end-of-chapter-quiz]] + + + +### 1. 언어 모델링 파이프라인은 어떤 순서로 진행될까요? + + + +### 2. 기본 Transformer 모델에 의해 만들어지는 텐서의 출력은 몇 차원이며, 각 텐서가 무엇을 의미하나요? + + + +### 3. 서브워드 토큰화 예시에 해당하는 것은 무엇인가요? + + + +### 4. 모델 헤드가 무엇인가요? + + + +{#if fw === 'pt'} +### 5. AutoModel이 무엇인가요? + +AutoTrain과 헷갈린 게 아닐까요?" + }, + { + text: "체크포인트에 기반하여 적합한 구조를 반환하는 객체입니다.", + explain: "정확합니다. AutoModel은 적절한 모델 구조를 초기화할 때 필요한 체크포인트만을 필요로 합니다.", + correct: true + }, + { + text: "적합한 가중치를 불러오기 위해 입력에 사용된 언어를 자동으로 감지하는 모델입니다.", + explain: "오답입니다. 몇몇 체크포인트와 모델은 다양한 언어를 처리할 수 있지만, 언어에 따라 체크포인트를 자동으로 선택할 수 있도록 내장된 도구는 없습니다. 태스크에 가장 적합한 체크포인트를 찾으려면 모델 허브에 가보세요!" + } + ]} +/> + +{:else} +### 5. TFAutoModel이 무엇인가요? + +AutoTrain과 헷갈린 게 아닐까요?" + }, + { + text: "체크포인트에 기반하여 적합한 구조를 반환하는 객체입니다.", + explain: "정확합니다. AutoModel은 적절한 모델 구조를 초기화할 때 필요한 체크포인트만을 필요로 합니다.", + correct: true + }, + { + text: "적합한 가중치를 불러오기 위해 입력에 사용된 언어를 자동으로 감지하는 모델입니다.", + explain: "오답입니다. 몇몇 체크포인트와 모델은 다양한 언어를 처리할 수 있지만, 언어에 따라 체크포인트를 자동으로 선택할 수 있도록 내장된 도구는 없습니다. 태스크에 가장 적합한 체크포인트를 찾으려면 모델 허브에 가보세요!" + } + ]} +/> + +{/if} + +### 6. 길이가 다른 시퀀스를 하나의 배치로 만들 때 신경써야 할 부분은 무엇일까요? + + + +### 7. 시퀀스 분류 모델의 로짓 출력 결과에 소프트맥스 함수를 적용하는 핵심적인 이유는 무엇일까요? + + + +### 8. 토크나이저 API의 가장 핵심적인 메서드는 무엇일까요? + +encode입니다.", + explain: "틀렸습니다! encode 메서드가 토크나이저에는 있지만, 모델에는 없습니다." + }, + { + text: "토크나이저 객체를 바로 호출하는 메서드입니다.", + explain: "정확합니다! 토크나이저의 __call__ 메서드는 거의 모든 것을 처리할 수 있는 강력한 메서드입니다. 이 메서드는 또한 모델로부터 예측 결과를 탐색하는 데에 사용되기도 합니다.", + correct: true + }, + { + text: "pad 메서드입니다.", + explain: "틀렸습니다! 패딩은 매우 유용한 방법이지만, 토크나이저 API가 제공하는 기능 중 하나일 뿐입니다." + }, + { + text: "tokenize 메서드입니다.", + explain: "tokenize 메서드가 유용한 것은 틀림없지만, 토크나이저 API의 핵심은 아닙니다." + } + ]} +/> + +### 9. 아래 코드 예시에서 `result` 변수가 포함하고 있는 것은 무엇일까요? + +```py +from transformers import AutoTokenizer + +tokenizer = AutoTokenizer.from_pretrained("bert-base-cased") +result = tokenizer.tokenize("Hello!") +``` + +__call__ 또는 convert_tokens_to_ids 입니다!" + }, + { + text: "모든 토큰을 포함하고 있는 문자열입니다.", + explain: "문자열을 각각의 토큰으로 나누는 것이 목표기 때문에 정확한 정답은 아닙니다." + } + ]} +/> + +{#if fw === 'pt'} +### 10. 아래 코드에서 잘못된 부분이 있을까요? + +```py +from transformers import AutoTokenizer, AutoModel + +tokenizer = AutoTokenizer.from_pretrained("bert-base-cased") +model = AutoModel.from_pretrained("gpt2") + +encoded = tokenizer("Hey!", return_tensors="pt") +result = model(**encoded) +``` + + + +{:else} +### 10. 아래 코드에서 잘못된 부분이 있을까요? + +```py +from transformers import AutoTokenizer, TFAutoModel + +tokenizer = AutoTokenizer.from_pretrained("bert-base-cased") +model = TFAutoModel.from_pretrained("gpt2") + +encoded = tokenizer("Hey!", return_tensors="pt") +result = model(**encoded) +``` + + + +{/if}