# 파이프라인 내부 동작 과정[[behind-the-pipeline]]

{#if fw === 'pt'}

<CourseFloatingBanner chapter={2}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/en/chapter2/section2_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter2/section2_pt.ipynb"},
]} />

{:else}

<CourseFloatingBanner chapter={2}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/en/chapter2/section2_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter2/section2_tf.ipynb"},
]} />

{/if}

<Tip>
PyTorch를 사용하는지, TensorFlow를 사용하는지에 따라 내용이 약간 달라지는 첫 번째 섹션입니다. 제목 상단 스위치를 이용해 선호하는 플랫폼을 선택하세요!
</Tip>

{#if fw === 'pt'}
<Youtube id="1pedAIvTWXk"/>
{:else}
<Youtube id="wVN12smEvqg"/>
{/if}

완전한 예제를 이용해, 아래의 [제1단원](/course/chapter1) 코드를 수행했을 때 뒤에서 어떤 일이 일어나고 있는지 알아봅시다.

```python
from transformers import pipeline

classifier = pipeline("sentiment-analysis")
classifier(
    [
        "I've been waiting for a HuggingFace course my whole life.",
        "I hate this so much!",
    ]
)
```

다음과 같은 출력이 나오게 됩니다.

```python out
[{'label': 'POSITIVE', 'score': 0.9598047137260437},
 {'label': 'NEGATIVE', 'score': 0.9994558095932007}]
```

[제1단원](/course/chapter1)에서 확인했듯이, 이 파이프라인 그룹은 세 단계(전처리, 입력을 모델에 넘겨주는 것, 후처리)를 함께 수행합니다.

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter2/full_nlp_pipeline.svg" alt="The full NLP pipeline: tokenization of text, conversion to IDs, and inference through the Transformer model and the model head."/>
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter2/full_nlp_pipeline-dark.svg" alt="The full NLP pipeline: tokenization of text, conversion to IDs, and inference through the Transformer model and the model head."/>
</div>

각 단계에 대해 빠르게 살펴보겠습니다.

## 토크나이저를 이용한 전처리[[preprocessing-with-a-tokenizer]]

다른 신경망처럼 Transformer 모델도 원시 텍스트를 바로 처리할 수 없기 때문에 파이프라인의 첫 번째 단계는 텍스트 입력을 모델이 이해할 수 있는 숫자로 변환하는 것입니다. 이 과정을 위해 다음 기능들을 수행하는 *토크나이저*를 사용합니다.

- 입력을 *토큰*이라고 부르는 단어나 하위 단어, 또는 심볼(예-구두점)로 분할
- 각 토큰을 하나의 정수에 매핑
- 모델에 유용할 수 있는 부가적인 입력 추가

이 모든 전처리 과정은 모델이 사전학습될 때와 완전히 동일한 방식으로 진행되어야 하기 때문에 [Model Hub](https://huggingface.co/models)에서 정보를 다운로드 해야합니다. 전처리를 위해 `AutoTokenizer` 클래스와 AutoTokenizer의 `from_pretrained()` 메서드를 사용합니다. 모델의 체크포인트 이름을 사용하여 모델의 토크나이저와 연관된 데이터를 자동으로 가져와 저장합니다. (따라서 아래 코드를 처음 실행할 때만 다운로드됩니다.)

`sentiment-analysis` 파이프라인의 기본 체크포인트는 `distilbert-base-uncased-finetuned-sst-2-english`(모델 카드 확인은 [여기](https://huggingface.co/distilbert-base-uncased-finetuned-sst-2-english)서 가능)이므로 아래 코드를 실행합니다.

```python
from transformers import AutoTokenizer

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
```

토크나이저가 있다면 문장을 토크나이저에 입력하여 우리의 모델에 전달할 준비가 된 딕셔너리를 출력으로 얻게 됩니다! 남은 것은 입력 ID 목록을 tensor로 변환하는 것입니다.

여러분은 어떤 ML 프레임워크-PyTorch나 TensorFlow, 또는 몇몇 모델을 위한 Flax-가 백엔드로 사용되는지 걱정하지 않고 🤗 Transformers를 사용할 수 있습니다. 하지만 Transformer 모델은 *tensor*만을 입력으로 받습니다. 만약 tensor에 대해 처음 들어봤다면, NumPy 배열을 생각하면 됩니다. NumPy 배열은 스칼라(0D), 벡터(1D), 행렬(2D) 또는 더 많은 차원을 가질 수 있습니다. 이것은 사실상 텐서입니다. 다른 ML 프레임워크의 텐서도 유사하게 동작하며, NumPy 배열처럼 인스턴스화가 쉽습니다.

얻고자 하는 tensor의 타입(PyTorch, TensorFlow, 일반 NumPy)을 지정하기 위해, `return_tensors` 전달인자를 사용합니다.

{#if fw === 'pt'}
```python
raw_inputs = [
    "I've been waiting for a HuggingFace course my whole life.",
    "I hate this so much!",
]
inputs = tokenizer(raw_inputs, padding=True, truncation=True, return_tensors="pt")
print(inputs)
```
{:else}
```python
raw_inputs = [
    "I've been waiting for a HuggingFace course my whole life.",
    "I hate this so much!",
]
inputs = tokenizer(raw_inputs, padding=True, truncation=True, return_tensors="tf")
print(inputs)
```
{/if}

padding과 truncation에 대해 벌써 걱정하지 마세요. 나중에 설명하도록 하겠습니다. 여기서 기억해야 할 중요한 점은 하나의 문장 또는 여러 개의 문장 리스트를 토크나이저 함수로 전달할 수 있을 뿐만 아니라 얻고 싶은 텐서 유형까지 지정할 수 있다는 것입니다. (텐서 유형이 지정되지 않으면 이중 리스트를 결과로 얻게 됩니다)

{#if fw === 'pt'}

PyTorch tensor로 지정했을 때의 결과입니다.

```python out
{
    'input_ids': tensor([
        [  101,  1045,  1005,  2310,  2042,  3403,  2005,  1037, 17662, 12172, 2607,  2026,  2878,  2166,  1012,   102],
        [  101,  1045,  5223,  2023,  2061,  2172,   999,   102,     0,     0,     0,     0,     0,     0,     0,     0]
    ]), 
    'attention_mask': tensor([
        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
    ])
}
```
{:else}

TensorFlow tensor로 지정했을 때의 결과입니다.

```python out
{
    'input_ids': <tf.Tensor: shape=(2, 16), dtype=int32, numpy=
        array([
            [  101,  1045,  1005,  2310,  2042,  3403,  2005,  1037, 17662, 12172,  2607,  2026,  2878,  2166,  1012,   102],
            [  101,  1045,  5223,  2023,  2061,  2172,   999,   102,     0,     0,     0,     0,     0,     0,     0,     0]
        ], dtype=int32)>, 
    'attention_mask': <tf.Tensor: shape=(2, 16), dtype=int32, numpy=
        array([
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
            [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
        ], dtype=int32)>
}
```
{/if}

출력은 `input_ids`와 `attention_mask` 두 개의 키를 갖는 딕셔너리입니다. `input_ids`는 각 문장 내 토큰의 고유 식별자인 정수로 이루어진 2개의 행(한 행이 하나의 문장)을 가지고 있습니다. `attention_mask`는 이 챕터의 뒤쪽에서 설명할 것입니다. 

## 모델 살펴보기[[going-through-the-model]]

{#if fw === 'pt'}
토크나이저를 다운받은 방식과 동일한 방식으로 사전학습된 모델을 다운받을 수 있습니다. 🤗 Transformers는 `from_pretrained` 메서드를 가진 `AutoModel` 클래스를 제공합니다.

```python
from transformers import AutoModel

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
model = AutoModel.from_pretrained(checkpoint)
```
{:else}
토크나이저를 다운받은 방식과 동일한 방식으로 사전학습된 모델을 다운받을 수 있습니다. 🤗 Transformers는 `from_pretrained` 메서드를 가진 `TFAutoModel` 클래스를 제공합니다.

```python
from transformers import TFAutoModel

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
model = TFAutoModel.from_pretrained(checkpoint)
```
{/if}

이 코드 스니펫에서 이전 파이프라인에서 사용된 것과 동일한 체크포인트를 다운로드(실제로는 이미 저장되어 있어야 합니다.)하고 그것으로 모델을 인스턴스화했습니다.

해당 아키텍처는 기본 Transformer 모듈만 포함하고 있습니다. 입력이 주어지면 *features*라고도 불리는 *hidden states*를 출력합니다. 각 모델의 입력에 대해 **Transformer 모델에 의해 수행된 입력의 문맥적 이해**로 표현할 수 있는 고차원 벡터를 가져옵니다.

이 내용이 이해되지 않더라도 걱정하지 마세요. 뒤에서 설명할 것입니다.

이러한 hidden states는 그 자체로 유용할 수 있지만 일반적으로 *head*라고 알려진 모델의 다른 부분에 입력으로 들어갑니다. [제1단원](/course/chapter1)에서, 동일한 구조로 다른 태스크를 수행할 수 있었는데 이 태스크들은 서로 다른 헤드와 연관되어 있습니다.

### 고차원 벡터란?[[a-high-dimensional-vector]]

Transformer 모듈에 의한 출력 벡터는 일반적으로 크며 보통 3개의 차원을 가집니다.

- **Batch size**: 한 번에 처리되는 시퀀스의 수 (예제에서는 2)
- **Sequence length**: 시퀀스의 숫자 표현 길이 (예제에서는 16)
- **Hidden size**: 각 모델 입력 벡터 차원

위에서 마지막 값으로 인해 "고차원"이라고 불립니다. hidden size는 매우 클 수 있습니다 (작은 모델은 768이 일반적이며 큰 모델은 3072나 그 이상의 값이 될 수 있습니다)

전처리 과정을 거친 입력을 모델로 넘기면 아래 결과를 확인할 수 있습니다.

{#if fw === 'pt'}
```python
outputs = model(**inputs)
print(outputs.last_hidden_state.shape)
```

```python out
torch.Size([2, 16, 768])
```
{:else}
```py
outputs = model(inputs)
print(outputs.last_hidden_state.shape)
```

```python out
(2, 16, 768)
```
{/if}

🤗 Transformers 모델의 출력은 `namedtuple` 또는 딕셔너리 형태입니다. 속성이나 키(`outputs["last_hidden_state"]`)를 이용해 요소에 접근할 수 있고 찾고자 하는 것의 정확한 위치를 안다면 인덱스(`outputs[0]`)도 사용할 수 있습니다.

### 모델 헤드: 숫자로 이해하기[[model-heads-making-sense-out-of-numbers]]

모델 헤드는 hidden state의 고차원 벡터를 입력으로 받아 다른 차원으로 투영합니다. 모델 헤드는 보통 하나 이상의 선형 레이어로 이루어져 있습니다.

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter2/transformer_and_head.svg" alt="A Transformer network alongside its head."/>
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter2/transformer_and_head-dark.svg" alt="A Transformer network alongside its head."/>
</div>

Transformer 모델의 출력은 처리할 모델 헤드로 바로 전달됩니다.

이 다이어그램에서, 모델은 모델의 임베딩 레이어와 후속 레이어로 표현됩니다. 임베딩 레이어는 토큰화된 각각의 입력 ID를 연관된 토큰을 나타내는 벡터로 변환합니다. 후속 레이어는 문장의 최종 표현을 만들기 위해 어텐션 메커니즘을 이용해 이 벡터들을 처리합니다.

🤗 Transformer에는 다양한 아키텍처가 있으며, 각각의 아키텍처는 특정 작업을 처리하도록 설계되었습니다. 아래는 일부 아키텍처입니다.

- `*Model` (retrieve the hidden states)
- `*ForCausalLM`
- `*ForMaskedLM`
- `*ForMultipleChoice`
- `*ForQuestionAnswering`
- `*ForSequenceClassification`
- `*ForTokenClassification`
- 그 외 🤗

{#if fw === 'pt'}
이 예제를 위해서는 문장을 긍정 또는 부정으로 분류할 수 있게 하는 시퀀스 분류 헤드를 가진 모델이 필요합니다. 따라서 `AutoModel` 클래스가 아닌 `AutoModelForSequenceClassification`을 사용할 것입니다.

```python
from transformers import AutoModelForSequenceClassification

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
outputs = model(**inputs)
```
{:else}
이 예제를 위해서는 문장을 긍정 또는 부정으로 분류할 수 있게 하는 시퀀스 분류 헤드를 가진 모델이 필요합니다. 따라서 `AutoModel` 클래스가 아닌 `TFAutoModelForSequenceClassification`을 사용할 것입니다.

```python
from transformers import TFAutoModelForSequenceClassification

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint)
outputs = model(inputs)
```
{/if}

출력 형태를 보면 차원이 훨씬 적은 것을 알 수 있습니다. 모델 헤드는 이전에 봤던 고차원 벡터를 입력으로 받아 2개의 값(레이블 당 하나)으로 이루어진 벡터를 출력합니다.

```python
print(outputs.logits.shape)
```

{#if fw === 'pt'}
```python out
torch.Size([2, 2])
```
{:else}
```python out
(2, 2)
```
{/if}

우리는 2개의 문장과 2개의 레이블만 있기 때문에 모델로부터 얻은 출력 형태는 2 x 2입니다.

## 출력 후처리[[postprocessing-the-output]]

모델의 출력 값이 그 자체로 의미있는 것은 아닙니다. 한 번 보도록 합시다.

```python
print(outputs.logits)
```

{#if fw === 'pt'}
```python out
tensor([[-1.5607,  1.6123],
        [ 4.1692, -3.3464]], grad_fn=<AddmmBackward>)
```
{:else}
```python out
<tf.Tensor: shape=(2, 2), dtype=float32, numpy=
    array([[-1.5606991,  1.6122842],
           [ 4.169231 , -3.3464472]], dtype=float32)>
```
{/if}

모델은 첫 번째 문장에 대해 `[-1.5607, 1.6123]`으로 예측했고 두 번째 문장에 대해 `[ 4.1692, -3.3464]`으로 예측했습니다. 이 값들은 확률이 아니라 모델의 마지막 층에 의해 출력된 정규화되지 않은 점수인 *logits*입니다. 확률로 변환되기 위해 logits은 [SoftMax](https://en.wikipedia.org/wiki/Softmax_function) 층을 거쳐야 합니다. 학습을 위한 손실 함수가 일반적으로 SoftMax와 같은 마지막 활성함수와 교차 엔트로피와 같은 실제 손실 함수를 모두 사용하기 때문에 모든 🤗 Transformers 모델의 출력은 logit입니다.

{#if fw === 'pt'}
```py
import torch

predictions = torch.nn.functional.softmax(outputs.logits, dim=-1)
print(predictions)
```
{:else}
```py
import tensorflow as tf

predictions = tf.math.softmax(outputs.logits, axis=-1)
print(predictions)
```
{/if}

{#if fw === 'pt'}
```python out
tensor([[4.0195e-02, 9.5980e-01],
        [9.9946e-01, 5.4418e-04]], grad_fn=<SoftmaxBackward>)
```
{:else}
```python out
tf.Tensor(
[[4.01951671e-02 9.59804833e-01]
 [9.9945587e-01 5.4418424e-04]], shape=(2, 2), dtype=float32)
```
{/if}

모델은 첫 번째 문장에 대해 `[0.0402, 0.9598]`로 예측했고 두 번째 모델에 대해 `[0.9995,  0.0005]`로 예측했습니다. 이 값들은 확실하게 확률값입니다.

각 위치에 해당하는 레이블을 얻기 위해, 모델 config의 `id2label` 속성을 살펴봅시다. Config에 대한 더 많은 내용은 다음 섹션에서 진행됩니다.

```python
model.config.id2label
```

```python out
{0: 'NEGATIVE', 1: 'POSITIVE'}
```

모델의 예측 결과를 아래처럼 결론 지을 수 있습니다.
 
- 첫 번째 문장: NEGATIVE: 0.0402, POSITIVE: 0.9598
- 두 번째 문장: NEGATIVE: 0.9995, POSITIVE: 0.0005

파이프라인 세 단계-토크나이저를 이용한 전처리, 모델에 입력 넣어주기, 후처리-를 성공적으로 재현했습니다! 이제 각 단계별로 좀 더 깊게 알아보는 시간을 가져봅시다.

<Tip>

✏️ **직접 해보세요!** 2개 이상의 문장을 골라 `sentiment-analysis` 파이프라인을 적용해보세요. 이 챕터에서 본 내용을 그대로 수행해보고 같은 결과가 나오는지 확인해보세요!

</Tip>


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ko/chapter2/2.mdx" />