# Course

## Docs

- [학습 곡선 이해하기[[understanding-learning-curves]]](https://huggingface.co/learn/course/ko/chapter3/5.md)
- [Trainer API로 모델 미세 조정하기[[fine-tuning-a-model-with-the-trainer-api]]](https://huggingface.co/learn/course/ko/chapter3/3.md)
- [데이터 처리[[processing-the-data]]](https://huggingface.co/learn/course/ko/chapter3/2.md)
- [좋은 이슈를 작성하는 법](https://huggingface.co/learn/course/ko/chapter8/5.md)
- [단원 소개](https://huggingface.co/learn/course/ko/chapter8/1.md)
- [포럼에서 도움 요청하기](https://huggingface.co/learn/course/ko/chapter8/3.md)
- [2단원 완료!](https://huggingface.co/learn/course/ko/chapter8/6.md)
- [학습 파이프라인 디버깅](https://huggingface.co/learn/course/ko/chapter8/4.md)
- [단원 마무리 퀴즈](https://huggingface.co/learn/course/ko/chapter8/7.md)
- [학습 파이프라인 디버깅](https://huggingface.co/learn/course/ko/chapter8/4_tf.md)
- [에러가 발생했을 때 대응 방법](https://huggingface.co/learn/course/ko/chapter8/2.md)
- [단원 소개[[introduction]]](https://huggingface.co/learn/course/ko/chapter5/1.md)
- [필요한 데이터셋이 Hub에 없다면 어떻게 할까요?[[what-if-my-dataset-isnt-on-the-hub]]](https://huggingface.co/learn/course/ko/chapter5/2.md)
- [다중 시퀀스 처리[[handling-multiple-sequences]]](https://huggingface.co/learn/course/ko/chapter2/5.md)
- [단원 소개[[introduction]]](https://huggingface.co/learn/course/ko/chapter2/1.md)
- [Models](https://huggingface.co/learn/course/ko/chapter2/3.md)
- [한 번에 실행하기[[putting-it-all-together]]](https://huggingface.co/learn/course/ko/chapter2/6.md)
- [단원 마무리 퀴즈[[end-of-chapter-quiz]]](https://huggingface.co/learn/course/ko/chapter2/8.md)
- [토크나이저[[tokenizers]]](https://huggingface.co/learn/course/ko/chapter2/4.md)
- [기본 사용 완료![[basic-usage-completed]]](https://huggingface.co/learn/course/ko/chapter2/7.md)
- [파이프라인 내부 동작 과정[[behind-the-pipeline]]](https://huggingface.co/learn/course/ko/chapter2/2.md)
- [강의 소개](https://huggingface.co/learn/course/ko/chapter0/1.md)
- [단원 정리](https://huggingface.co/learn/course/ko/chapter1/9.md)
- [인코더 모델](https://huggingface.co/learn/course/ko/chapter1/5.md)
- [단원 소개](https://huggingface.co/learn/course/ko/chapter1/1.md)
- [트랜스포머로 무엇을 할 수 있나요?](https://huggingface.co/learn/course/ko/chapter1/3.md)
- [디코더 모델](https://huggingface.co/learn/course/ko/chapter1/6.md)
- [단원 마무리 퀴즈](https://huggingface.co/learn/course/ko/chapter1/10.md)
- [편향과 한계](https://huggingface.co/learn/course/ko/chapter1/8.md)
- [트랜스포머는 어떻게 동작하나요?](https://huggingface.co/learn/course/ko/chapter1/4.md)
- [시퀀스-투-시퀀스 모델](https://huggingface.co/learn/course/ko/chapter1/7.md)
- [자연어 처리(Natural Language Processing)](https://huggingface.co/learn/course/ko/chapter1/2.md)

### 학습 곡선 이해하기[[understanding-learning-curves]]
https://huggingface.co/learn/course/ko/chapter3/5.md

# 학습 곡선 이해하기[[understanding-learning-curves]]

<CourseFloatingBanner chapter={3}
  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/chapter3/section7.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter3/section7.ipynb"},
]} />

이제 `Trainer` API와 사용자 정의 훈련 루프를 모두 사용하여 미세 조정을 구현하는 방법을 배웠으므로, 결과를 해석하는 방법을 이해하는 것이 중요합니다. 학습 곡선은 훈련 중 모델의 성능을 평가하고 성능 저하를 일으킬 수 있는 잠재적 문제를 미리 식별하는 데 도움이 되는 매우 유용한 도구입니다.

이 섹션에서는 정확도와 손실 곡선을 읽고 해석하는 방법을 탐구하고, 다양한 곡선 패턴이 모델 동작에 대해 무엇을 의미하는지 파악하며, 일반적인 훈련 문제를 해결하는 방법을 배웁니다.

## 학습 곡선이란 무엇인가요?[[what-are-learning-curves]]

학습 곡선은 훈련 중에 시간에 따른 모델의 성능 지표를 시각적으로 나타낸 것입니다. 모니터링해야 할 가장 중요한 두 곡선은 다음과 같습니다.

- **손실 곡선**: 훈련 단계나 에폭에 따라 모델의 오류(손실)가 어떻게 변하는지 보여줍니다.
- **정확도 곡선**: 훈련 단계나 에폭에 따른 정확한 예측의 백분율을 보여줍니다.

이러한 곡선을 통해 모델의 학습 효과를 파악하고, 성능 향상을 위한 조정 방향을 제시할 수 있습니다. Transformers에서는 이러한 지표가 각 배치에 대해 개별적으로 계산된 다음 디스크에 기록됩니다. 그런 다음 [Weights & Biases](https://wandb.ai/)와 같은 라이브러리를 사용하여 이러한 곡선을 시각화하고 시간에 따른 모델의 성능을 추적할 수 있습니다.

### 손실 곡선[[loss-curves]]

손실 곡선은 시간에 따라 모델의 오류가 어떻게 감소하는지 보여줍니다. 성공적인 훈련에서는 보통 다음과 같은 곡선 패턴을 관찰할 수 있습니다.

![Loss Curve](https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter3/1.png)

- **높은 초기 손실**: 모델이 최적화 없이 시작하므로 초기 예측이 좋지 않습니다.
- **감소하는 손실**: 훈련이 진행됨에 따라 손실이 일반적으로 감소해야 합니다.
- **수렴**: 최종적으로 손실이 낮은 수준에서 안정되면서 모델이 데이터 패턴을 성공적으로 학습했음을 보여줍니다.

이전 챕터에서와 같이 `Trainer` API를 사용하여 이러한 지표를 추적하고 대시보드에서 시각화할 수 있습니다. 다음은 Weights & Biases로 이를 수행하는 예시입니다.

```python
# Trainer로 훈련 중 손실을 추적하는 예시
from transformers import Trainer, TrainingArguments
import wandb

# 실험 추적을 위해 Weights & Biases 초기화
wandb.init(project="transformer-fine-tuning", name="bert-mrpc-analysis")

training_args = TrainingArguments(
    output_dir="./results",
    eval_strategy="steps",
    eval_steps=50,
    save_steps=100,
    logging_steps=10,  # 10단계마다 지표 로그
    num_train_epochs=3,
    per_device_train_batch_size=16,
    per_device_eval_batch_size=16,
    report_to="wandb",  # Weights & Biases로 로그 전송
)

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation"],
    data_collator=data_collator,
    processing_class=tokenizer,
    compute_metrics=compute_metrics,
)

# 훈련 및 자동 지표 로그
trainer.train()
```

### 정확도 곡선[[accuracy-curves]]

정확도 곡선은 시간에 따른 정확한 예측의 백분율을 보여줍니다. 손실 곡선과 달리 정확도 곡선은 모델이 학습함에 따라 일반적으로 증가해야 하며, 손실 곡선보다 더 많은 단계를 포함할 수 있습니다.

![Accuracy Curve](https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter3/2.png)

- **낮게 시작**: 모델이 아직 데이터의 패턴을 학습하지 않았으므로 초기 정확도는 낮아야 합니다.
- **훈련과 함께 증가**: 모델이 데이터의 패턴을 학습할 수 있다면 학습함에 따라 정확도가 일반적으로 향상되어야 합니다.
- **고원 현상을 보일 수 있음**: 모델이 실제 레이블에 가까운 예측을 만들어내므로, 정확도는 부드럽게 상승하기보다는 계단식으로 점프하는 경우가 많습니다. 

<Tip>

💡 **정확도 곡선이 "계단식"인 이유**: 연속적인 손실과 달리 정확도는 이산적인 예측을 실제 레이블과 비교하여 계산됩니다. 모델 신뢰도의 작은 개선은 최종 예측을 변경하지 않을 수 있어서 임계값을 넘을 때까지 정확도가 평평하게 유지됩니다.

</Tip>

### 수렴[[convergence]]

수렴은 모델의 성능이 안정화되고 손실 및 정확도 곡선이 평평해질 때 발생합니다. 이는 모델이 데이터의 패턴을 학습했으며 사용할 준비가 되었다는 신호입니다. 간단히 말해서, 우리는 모델을 훈련할 때마다 안정적인 성능으로 수렴하는 것을 목표로 합니다.

![Convergence](https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter3/4.png)

모델이 수렴하면 새로운 데이터에 대한 예측을 만들고 평가 지표를 참조하여 모델의 성능을 이해할 수 있습니다.

## 학습 곡선 패턴 해석하기[[interpreting-learning-curve-patterns]]

곡선의 다양한 패턴은 모델 훈련의 여러 측면을 보여줍니다. 가장 일반적인 패턴과 그 의미를 살펴보겠습니다.

### 건전한 학습 곡선[[healthy-learning-curves]]

잘 작동하는 훈련 실행은 일반적으로 아래와 같은 곡선 모양을 보여줍니다.

![Healthy Loss Curve](https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter3/5.png)

위의 그림은 손실 곡선(왼쪽)과 해당 정확도 곡선(오른쪽)을 함께 보여줍니다. 두 곡선은 뚜렷한 특성을 가지고 있습니다.

손실 곡선은 시간에 따른 모델 손실의 값을 보여줍니다. 처음에는 손실이 높다가 점차 감소하여 모델이 개선되고 있음을 나타냅니다. 손실 값의 감소는 손실이 예측된 출력과 실제 출력 간의 오류를 나타내므로 모델이 더 나은 예측을 하고 있음을 시사합니다.

다음으로 정확도 곡선을 살펴보겠습니다. 이는 시간에 따른 모델의 정확도를 나타냅니다. 정확도 곡선은 낮은 값에서 시작하여 훈련이 진행됨에 따라 증가합니다. 정확도는 올바르게 분류된 인스턴스의 비율을 측정합니다. 따라서 정확도 곡선이 상승함에 따라 모델이 더 정확한 예측을 하고 있음을 의미합니다.

곡선 간의 주목할 만한 차이점 중 하나는 정확도 곡선의 부드러움과 "고원"의 존재입니다. 손실이 부드럽게 감소하는 동안 정확도 곡선의 고원은 연속적인 증가 대신 정확도의 이산적인 점프를 나타냅니다. 이러한 현상은 정확도 측정 방식의 특성 때문입니다. 최종 예측이 여전히 틀렸더라도 모델의 출력이 목표에 가까워지면 손실이 개선될 수 있습니다. 그러나 정확도는 예측이 정확한 임계값을 넘어야만 향상됩니다.

예를 들어, 고양이(0)와 개(1)를 구별하는 이진 분류기에서 모델이 개 이미지(실제 값 1)에 대해 0.3을 예측하면 이는 0으로 반올림되어 잘못된 분류입니다. 다음 단계에서 0.4를 예측하면 여전히 틀렸습니다. 0.4가 0.3보다 1에 더 가깝기 때문에 손실은 감소했지만 정확도는 변하지 않아 고원을 만듭니다. 정확도는 모델이 0.5보다 큰 값을 예측하여 1로 반올림될 때만 점프합니다.

<Tip>

**건전한 곡선의 특성**
- **손실의 부드러운 감소**: 훈련 및 검증 손실이 모두 꾸준히 감소
- **훈련/검증 성능이 근접**: 훈련 및 검증 지표 간의 작은 격차
- **수렴**: 곡선이 평평해져서 모델이 패턴을 학습했음을 나타냄

</Tip>

### 실용적인 예시[[practical-examples]]

학습 곡선의 몇 가지 실용적인 예시를 살펴보겠습니다. 먼저 훈련 중 학습 곡선을 모니터링하는 몇 가지 접근 방식을 강조하겠습니다. 아래에서는 학습 곡선에서 관찰할 수 있는 다양한 패턴을 살펴보겠습니다.

#### 훈련 중[[during-training]]

훈련 과정 중(`trainer.train()`을 실행한 후) 다음과 같은 주요 지표를 모니터링할 수 있습니다.

1. **손실 수렴**: 손실이 여전히 감소하고 있는가, 아니면 고원에 도달했는가?
2. **과적합 신호**: 훈련 손실은 감소하는 동안 검증 손실이 증가하기 시작하는가?
3. **학습률**: 곡선이 너무 불안정한가(LR이 너무 높음) 아니면 너무 평평한가(LR이 너무 낮음)?
4. **안정성**: 문제를 나타내는 급격한 스파이크나 드롭이 있는가?

#### 훈련 후[[after-training]]

훈련 과정이 완료된 후 전체 곡선을 분석하여 모델의 성능을 이해할 수 있습니다.

1. **최종 성능**: 모델이 허용 가능한 성능 수준에 도달했는가?
2. **효율성**: 더 적은 에폭으로 동일한 성능을 달성할 수 있었는가?
3. **일반화**: 훈련과 검증 성능이 얼마나 가까운가?
4. **경향**: 추가 훈련이 성능을 향상시킬 가능성이 있는가?

<Tip>

🔍 **W&B 대시보드 기능**: Weights & Biases를 사용하면 학습 곡선을 보기 좋고 상호작용 가능한 그래프로 자동으로 만들 수 있습니다.
- 여러 실행을 나란히 비교
- 사용자 정의 지표 및 시각화 추가
- 이상 동작에 대한 알림 설정
- 팀과 결과 공유

[Weights & Biases 문서](https://docs.wandb.ai/)에서 자세히 알아보세요.
</Tip>

#### 과적합[[overfitting]]

과적합은 모델이 훈련 데이터에서 너무 많이 학습하여 다른 데이터(검증 세트로 표현됨)로 일반화할 수 없을 때 발생합니다.

![Overfitting](https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter3/10.png)

**증상**

- 훈련 손실은 계속 감소하지만 검증 손실은 증가하거나 고원에 머무름
- 훈련과 검증 정확도 간의 큰 격차
- 훈련 정확도가 검증 정확도보다 훨씬 높음

**과적합 해결책**
- **정규화**: 드롭아웃, 가중치 감쇠 또는 기타 정규화 기법 추가
- **조기 중단**: 검증 성능이 개선을 멈출 때 훈련 중단
- **데이터 증강**: 훈련 데이터 다양성 증가
- **모델 복잡도 감소**: 더 작은 모델이나 더 적은 매개변수 사용

아래 샘플에서는 과적합을 방지하기 위해 조기 중단을 사용합니다. `early_stopping_patience`를 3으로 설정하여 검증 손실이 3번의 연속적인 에폭 동안 개선되지 않으면 훈련이 중단됩니다.

```python
# 조기 중단으로 과적합을 감지하는 예시
from transformers import EarlyStoppingCallback

training_args = TrainingArguments(
    output_dir="./results",
    eval_strategy="steps",
    eval_steps=100,
    save_strategy="steps",
    save_steps=100,
    load_best_model_at_end=True,
    metric_for_best_model="eval_loss",
    greater_is_better=False,
    num_train_epochs=10,  # 높게 설정하지만 조기에 중단할 예정
)

# 과적합을 방지하기 위해 조기 중단 추가
trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation"],
    data_collator=data_collator,
    processing_class=tokenizer,
    compute_metrics=compute_metrics,
    callbacks=[EarlyStoppingCallback(early_stopping_patience=3)],
)
```

#### 2. 과소적합[[underfitting]]

과소적합은 모델이 데이터의 기본 패턴을 포착하기에는 너무 단순할 때 발생합니다. 이는 여러 가지 이유로 발생할 수 있습니다.

- 모델이 너무 작거나 패턴을 학습할 용량이 부족함
- 학습률이 너무 낮아서 학습이 느림
- 데이터 세트가 너무 작거나 문제를 대표하지 못함
- 모델이 제대로 정규화되지 않음

![Underfitting](https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter3/7.png)

**증상**
- 훈련 및 검증 손실이 모두 높게 유지됨
- 모델 성능이 훈련 초기에 고원에 도달
- 훈련 정확도가 예상보다 낮음

**과소적합 해결책**
- **모델 용량 증가**: 더 큰 모델이나 더 많은 매개변수 사용
- **더 오래 훈련**: 에폭 수 증가
- **학습률 조정**: 다른 학습률 시도
- **데이터 품질 확인**: 데이터가 적절히 전처리되었는지 확인

아래 샘플에서는 모델이 데이터의 패턴을 학습할 수 있는지 확인하기 위해 더 많은 에폭 동안 훈련합니다.

```python
from transformers import TrainingArguments

training_args = TrainingArguments(
    output_dir="./results",
    -num_train_epochs=5,
    +num_train_epochs=10,
)
```

#### 3. 불안정한 학습 곡선[[erratic-learning-curves]]

불안정한 학습 곡선은 모델이 효과적으로 학습하지 않을 때 발생합니다. 이는 여러 가지 이유로 발생할 수 있습니다.

- 학습률이 너무 높아서 모델이 최적 매개변수를 지나치게 됨
- 배치 크기가 너무 작아서 모델이 천천히 학습함
- 모델이 제대로 정규화되지 않아서 훈련 데이터에 과적합됨
- 데이터 세트가 제대로 전처리되지 않아서 모델이 노이즈에서 학습함

![Erratic Learning Curves](https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter3/3.png)

**증상**
- 손실이나 정확도에서 빈번한 변동
- 곡선이 높은 분산이나 불안정성을 보임
- 성능이 명확한 경향 없이 진동함

훈련과 검증 곡선 모두 불안정한 동작을 보입니다.

![Erratic Learning Curves](https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter3/9.png)

**불안정한 곡선의 해결책**
- **학습률 낮추기**: 더 안정적인 훈련을 위해 단계 크기 줄이기
- **배치 크기 증가**: 더 큰 배치가 더 안정적인 그레이디언트 제공
- **그레이디언트 클리핑**: 그레이디언트 폭발 방지
- **더 나은 데이터 전처리**: 일관된 데이터 품질 보장

아래 샘플에서는 학습률을 낮추고 배치 크기를 증가시킵니다.

```python
from transformers import TrainingArguments

training_args = TrainingArguments(
    output_dir="./results",
    -learning_rate=1e-5,
    +learning_rate=1e-4,
    -per_device_train_batch_size=16,
    +per_device_train_batch_size=32,
)
```

## 핵심 요점[[key-takeaways]]

학습 곡선을 이해하는 것은 효과적인 기계학습 전문가가 되기 위해 중요합니다. 이러한 시각적 도구는 모델의 훈련 진행 상황에 대한 즉각적인 피드백을 제공하고 언제 훈련을 중단하거나 하이퍼파라미터를 조정하거나 다른 접근 방식을 시도할지에 대한 정보에 기반한 결정을 내리는 데 도움이 됩니다. 연습을 통해 건전한 학습 곡선이 어떤 모습인지, 그리고 문제가 발생했을 때 어떻게 해결할지에 대한 직관적인 이해를 개발할 수 있습니다.

<Tip>

💡 **핵심 요점**
- 학습 곡선은 모델 훈련 진행 상황을 이해하는 데 필수적인 도구입니다.
- 손실과 정확도 곡선을 모두 모니터링하되, 서로 다른 특성을 가지고 있음을 기억하세요.
- 과적합은 훈련/검증 성능의 분기로 나타납니다.
- 과소적합은 훈련과 검증 데이터 모두에서 성능이 좋지 않은 것으로 나타납니다.
- Weights & Biases와 같은 도구는 학습 곡선을 쉽게 추적하고 분석할 수 있게 해줍니다.
- 조기 중단과 적절한 정규화는 대부분의 일반적인 훈련 문제를 해결할 수 있습니다.

🔬 **다음 단계**: 자신의 미세 조정 실험에서 학습 곡선을 분석해보세요. 다양한 하이퍼파라미터를 시도하고 곡선 모양에 어떤 영향을 미치는지 관찰하세요. 이러한 실습 경험이 훈련 진행 상황을 읽는 직관을 개발하는 가장 좋은 방법입니다.

</Tip>

## 섹션 퀴즈[[section-quiz]]

학습 곡선과 훈련 분석에 대한 이해를 테스트해보세요.

### 1. 훈련 손실은 감소하지만 검증 손실이 증가하기 시작할 때 일반적으로 무엇을 의미하나요?

<Question
	choices={[
		{
			text: "모델이 성공적으로 학습하고 있으며 계속 향상될 것입니다.",
			explain: "검증 손실이 증가하는 동안 훈련 손실이 감소한다면 이는 성공이 아니라 문제를 나타냅니다."
		},
		{
			text: "모델이 훈련 데이터에 과적합되고 있습니다.",
			explain: "정답입니다! 이는 과적합의 전형적인 신호입니다 - 모델이 훈련 데이터에서는 잘 수행하지만 보지 못한 검증 데이터에서는 성능이 떨어집니다.",
            correct: true
		},
		{
			text: "학습률이 너무 낮습니다.",
			explain: "낮은 학습률은 느린 학습을 야기하지만 훈련과 검증 성능 간의 분기는 아닙니다."
		},
        {
			text: "데이터 세트가 너무 작습니다.",
			explain: "작은 데이터 세트가 과적합에 기여할 수 있지만, 이 특정 패턴은 데이터 세트 크기에 관계없이 과적합의 정의입니다."
		}
	]}
/>

### 2. 정확도 곡선이 부드러운 증가보다는 "계단식" 또는 고원과 같은 패턴을 보이는 이유는 무엇인가요?

<Question
	choices={[
		{
			text: "정확도 계산에 오류가 있습니다.",
			explain: "계단식 패턴은 정상적이고 예상되는 것이며 오류가 아닙니다."
		},
		{
			text: "정확도는 예측이 결정 경계를 넘을 때만 변하는 이산적인 지표입니다.",
			explain: "정답입니다! 손실과 달리 정확도는 이산적인 예측 결정에 의존하므로 신뢰도의 작은 개선이 임계값을 넘을 때까지 최종 정확도를 변경하지 않을 수 있습니다.",
            correct: true
		},
		{
			text: "모델이 효과적으로 학습하지 않고 있습니다.",
			explain: "계단식 정확도 곡선은 모델이 잘 학습하고 있을 때도 정상입니다."
		},
        {
			text: "배치 크기가 너무 작습니다.",
			explain: "배치 크기는 훈련 안정성에 영향을 미치지만 정확도 지표의 본질적으로 이산적인 특성을 설명하지는 않습니다."
		}
	]}
/>

### 3. 불안정하고 심하게 변동하는 학습 곡선을 관찰할 때 가장 좋은 접근법은 무엇인가요?

<Question
	choices={[
		{
			text: "수렴을 가속화하기 위해 학습률을 증가시킵니다.",
			explain: "학습률을 증가시키면 변동이 더 심해질 가능성이 높습니다."
		},
		{
			text: "학습률을 낮추고 가능하면 배치 크기를 증가시킵니다.",
			explain: "정답입니다! 낮은 학습률과 큰 배치 크기는 일반적으로 더 안정적인 훈련으로 이어집니다.",
			correct: true
		},
		{
			text: "모델이 개선되지 않을 것이므로 즉시 훈련을 중단합니다.",
			explain: "불안정한 곡선은 종종 하이퍼파라미터 조정으로 수정할 수 있습니다."
		},
        {
			text: "완전히 다른 모델 아키텍처로 바꿉니다.",
			explain: "이는 성급합니다 - 불안정한 곡선은 보통 하이퍼파라미터 튜닝으로 수정 가능합니다."
		}
	]}
/>

### 4. 언제 조기 중단 사용을 고려해야 하나요?

<Question
	choices={[
		{
			text: "항상, 어떤 형태의 과적합도 방지하기 때문입니다.",
			explain: "조기 중단은 유용하지만 다른 정규화 방법이 작동하고 있다면 항상 필요한 것은 아닙니다."
		},
		{
			text: "검증 성능이 개선을 멈추거나 악화되기 시작할 때입니다.",
			explain: "정답입니다! 조기 중단은 모델이 더 이상 더 잘 일반화하지 않을 때 훈련을 중단하여 과적합을 방지하는 데 도움이 됩니다.",
            correct: true
		},
		{
			text: "훈련 손실이 여전히 빠르게 감소하고 있을 때만입니다.",
			explain: "훈련 손실이 빠르게 감소하고 검증 성능이 좋다면 훈련을 계속하고 싶을 수 있습니다."
		},
        {
			text: "절대로, 모델이 완전한 잠재력에 도달하는 것을 방해하기 때문입니다.",
			explain: "조기 중단은 과적합을 방지하여 최종 모델 성능을 향상시키는 경우가 많은 유용한 기법입니다."
		}
	]}
/>

### 5. 모델이 과소적합일 수 있음을 나타내는 것은 무엇인가요?

<Question
	choices={[
		{
			text: "훈련 정확도가 검증 정확도보다 훨씬 높습니다.",
			explain: "이는 과소적합이 아니라 과적합을 설명합니다."
		},
		{
			text: "훈련과 검증 성능이 모두 좋지 않고 조기에 고원에 도달합니다.",
			explain: "정답입니다! 과소적합은 모델이 패턴을 학습할 용량이 부족할 때 발생하여 훈련과 검증 데이터 모두에서 성능이 좋지 않습니다.",
            correct: true
		},
		{
			text: "학습 곡선이 변동 없이 매우 부드럽습니다.",
			explain: "부드러운 곡선은 일반적으로 좋은 것이며 과소적합을 나타내지 않습니다."
		},
        {
			text: "검증 손실이 훈련 손실보다 빠르게 감소합니다.",
			explain: "이는 실제로 긍정적인 신호이며 문제가 아닙니다."
		}
	]}
/>

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

### Trainer API로 모델 미세 조정하기[[fine-tuning-a-model-with-the-trainer-api]]
https://huggingface.co/learn/course/ko/chapter3/3.md

# Trainer API로 모델 미세 조정하기[[fine-tuning-a-model-with-the-trainer-api]]

<CourseFloatingBanner chapter={3}
  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/chapter3/section3.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter3/section3.ipynb"},
]} />

<Youtube id="nvBXf7s7vTI"/>

🤗 Transformers는 `Trainer` 클래스를 제공합니다. 이 클래스를 사용하면 사전 학습된 모델을 여러분의 데이터셋에 맞춰 최신 기법으로 쉽게 미세 조정할 수 있습니다. 이전 섹션에서 데이터 전처리 작업을 모두 마쳤다면 이제 몇 단계만 거치면 `Trainer`를 정의할 수 있습니다. 가장 어려운 부분은 `Trainer.train()`을 실행할 환경을 준비하는 과정일 수 있습니다. 이 작업은 CPU에서 매우 느리게 실행되기 때문입니다. 만약 GPU가 없다면 [Google Colab](https://colab.research.google.com/)에서 무료로 제공하는 GPU나 TPU를 이용할 수 있습니다.

<Tip>

📚 **훈련 리소스**: 훈련을 시작하기 전에 포괄적인 [🤗 Transformers 훈련 가이드](https://huggingface.co/docs/transformers/main/en/training)를 숙지하고 [미세 조정 쿡북](https://huggingface.co/learn/cookbook/en/fine_tuning_code_llm_on_single_gpu)의 실용적인 예제를 살펴보세요.

</Tip>

아래 코드 예시는 이전 섹션의 코드를 모두 실행했다는 가정하에 작동합니다. 즉, 다음 사항들이 필요합니다.

```py
from datasets import load_dataset
from transformers import AutoTokenizer, DataCollatorWithPadding

raw_datasets = load_dataset("glue", "mrpc")
checkpoint = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)


def tokenize_function(example):
    return tokenizer(example["sentence1"], example["sentence2"], truncation=True)


tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
```

### 훈련[[training]]

`Trainer`를 정의하기 전 첫 번째 단계는 `Trainer`가 훈련 및 평가에 사용할 모든 하이퍼파라미터를 담을`TrainingArguments` 클래스를 정의하는 것입니다. 필수로 제공해야 하는 유일한 인수는 훈련된 모델과 중간 체크포인트가 저장될 디렉토리입니다. 나머지는 기본값으로 둘 수 있으며, 기본적인 미세 조정 작업에는 충분한 설정입니다.

```py
from transformers import TrainingArguments

training_args = TrainingArguments("test-trainer")
```

훈련 중에 모델을 Hub에 자동으로 업로드하려면 `TrainingArguments`에서 `push_to_hub=True`를 전달하세요. 이 기능에 대해서는 [Chapter 4](/course/chapter4/3)에서 자세히 알아보겠습니다.

<Tip>

🚀 **고급 설정**: 사용 가능한 모든 훈련 인수와 최적화 전략에 대한 자세한 정보는 [TrainingArguments 문서](https://huggingface.co/docs/transformers/main/en/main_classes/trainer#transformers.TrainingArguments)와 [훈련 구성 쿡북](https://huggingface.co/learn/cookbook/en/fine_tuning_code_llm_on_single_gpu)을 참고하세요.

</Tip>

두 번째 단계는 모델을 정의하는 것입니다. [이전 챕터](/course/chapter2)에서와 같이 두 개의 라벨과 함께 `AutoModelForSequenceClassification` 클래스를 사용하겠습니다.

```py
from transformers import AutoModelForSequenceClassification

model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
```

[Chapter 2](/course/chapter2)와 달리 이 사전 훈련된 모델을 인스턴스화하면  경고 메시지가 나타나는 것을 확인할 수 있습니다. 이는 BERT가 문장 쌍 분류를 위해 사전 훈련되지 않았기 때문에, 사전 훈련된 모델의 헤드가 제거되고 시퀀스 분류에 적합한 새로운 헤드가 추가되었기 때문입니다. 경고는 일부 가중치(제거된 사전 훈련 헤드에 해당하는 가중치)가 사용되지 않았고, 일부 다른 가중치(새로운 헤드용)가 무작위로 초기화되었다는 것을 나타냅니다. 마지막으로 모델을 훈련시키라는 메시지가 나오는데, 바로 지금부터 그 작업을 시작하겠습니다.

모델이 준비되면, 지금까지 구성한 모든 객체(`model`, `training_args`, 훈련 및 검증 데이터셋, `data_collator`, `processing_class`)를 전달하여 `Trainer`를 정의할 수 있습니다. `processing_class` 매개변수는 비교적 최근에 추가된 기능으로, `Trainer`에게 어떤 토크나이저를 사용해 데이터를 처리할지 알려줍니다.

```py
from transformers import Trainer

trainer = Trainer(
    model,
    training_args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation"],
    data_collator=data_collator,
    processing_class=tokenizer,
)
```

`processing_class`에 토크나이저를 전달하면, `Trainer`가 기본적으로 `DataCollatorWithPadding`을 `data_collator`로 사용합니다. 따라서 이 경우에는 `data_collator=data_collator` 줄을 생략할 수 있지만, 데이터 처리 파이프라인의 중요한 부분을 보여드리기 위해 코드에 포함했습니다.

<Tip>

📖 **더 자세히 알아보기**: Trainer 클래스와 그 매개변수에 대한 자세한 내용은 [Trainer API 문서](https://huggingface.co/docs/transformers/main/en/main_classes/trainer)를 방문하고 [훈련 쿡북 레시피](https://huggingface.co/learn/cookbook/en/fine_tuning_code_llm_on_single_gpu)에서 고급 사용 패턴을 살펴보세요.

</Tip>

데이터셋에서 모델을 미세 조정하려면 `Trainer`의 `train()` 메소드를 호출하기만 하면 됩니다.

```py
trainer.train()
```

이렇게 하면 미세 조정이 시작됩니다(GPU에서는 몇 분 정도 소요됩니다). 500단계마다 훈련 손실이 출력되지만, 모델의 성능이 얼마나 좋은지(또는 나쁜지)는 알려주지 않습니다. 그 이유는 다음과 같습니다.

1. `TrainingArguments`에서 `eval_strategy`를 `"steps"` (매 `eval_steps`마다 평가) 또는 `"epoch"` (각 에포크 종료 시 평가)로 설정하지 않았습니다.
2. 평가 중에 메트릭을 계산하기 위한 `compute_metrics()` 함수를 `Trainer`에 제공하지 않았습니다. 이 함수가 없으면 평가에서 손실 값만 출력되는데, 이 값만으로는 성능을 파악하기 어렵습니다.


### 평가[[evaluation]]

이제 유용한 `compute_metrics()` 함수를 어떻게 만들고 다음 훈련 시 사용할 수 있는지 알아보겠습니다. 이 함수는 `EvalPrediction` 객체(`predictions` 필드와 `label_ids` 필드를 갖는 명명된 튜플)를 입력받습니다. 그리고 각 메트릭의 이름을 키(문자열)로, 성능을 값(부동소수점)으로 갖는 딕셔너리를 반환해야 합니다. 모델의 예측값을 얻기 위해 `Trainer.predict()`를 사용할 수 있습니다.

```py
predictions = trainer.predict(tokenized_datasets["validation"])
print(predictions.predictions.shape, predictions.label_ids.shape)
```

```python out
(408, 2) (408,)
```

`predict()` 메소드의 출력은 `predictions`, `label_ids`, `metrics` 세 개의 필드가 있는 또 다른 명명된 튜플입니다. `metrics` 필드에는 전달된 데이터셋에 대한 손실 값과 시간 관련 메트릭(총 예측 시간, 평균 예측 시간)만 포함됩니다. 하지만 `compute_metrics()` 함수를 완성하여 `Trainer`에 전달하면, 이 필드에 `compute_metrics()`가 반환하는 메트릭들도 함께 포함됩니다.

보시다시피, `predictions`는 408 x 2 모양의 2차원 배열입니다 (408은 predict()에 전달한 데이터셋의 샘플 개수입니다). 이 값들은 `predict()`에 전달한 데이터셋의 각 샘플에 대한 로짓입니다 ([이전 챕터](/course/chapter2)에서 보았듯이 모든 Transformer 모델은 로짓을 반환합니다). 이 로짓을 우리가 가진 레이블과 비교할 수 있는 예측값으로 변환하려면, 두 번째 축에서 최댓값을 가진 인덱스를 구해야 합니다.

```py
import numpy as np

preds = np.argmax(predictions.predictions, axis=-1)
```

이제 이 `preds`를 라벨과 비교할 수 있습니다. `compute_metric()` 함수를 빌드하기 위해 🤗 [Evaluate](https://github.com/huggingface/evaluate/) 라이브러리의 메트릭을 활용하겠습니다. 데이터셋을 로드했던 것처럼, MRPC 데이터셋과 관련된 메트릭도 `evaluate.load()` 함수로 쉽게 로드할 수 있습니다. 반환된 객체의 `compute()` 메서드를 사용해 메트릭을 계산할 수 있습니다.

```py
import evaluate

metric = evaluate.load("glue", "mrpc")
metric.compute(predictions=preds, references=predictions.label_ids)
```

```python out
{'accuracy': 0.8578431372549019, 'f1': 0.8996539792387542}
```

<Tip>

다양한 평가 메트릭과 전략에 대해 알아보려면 [🤗 Evaluate 문서](https://huggingface.co/docs/evaluate/)를 참고하세요.

</Tip>

모델 헤드의 가중치가 무작위로 초기화되기 때문에 얻게 되는 결과는 조금씩 다를 수 있습니다. 결과를 보면 우리 모델이 검증 세트에서 85.78%의 정확도와 89.97%의 F1 점수를 달성했음을 볼 수 있습니다. 이 두 가지는 GLUE 벤치마크의 MRPC 데이터셋에서 결과를 평가하는 데 사용되는 메트릭입니다. [BERT 논문](https://arxiv.org/pdf/1810.04805.pdf)에서는 기본 모델의 F1 점수를 88.9로 보고하였습니다. 당시에는 `uncased` 모델을 사용했지만, 우리는 현재 `cased` 모델을 사용하고 있기 때문에 더 나은 결과가 나온 것입니다.

이 모든 것을 종합하면, 다음처럼 `compute_metrics()` 함수를 다음과 같이 정의할 수 있습니다.

```py
def compute_metrics(eval_preds):
    metric = evaluate.load("glue", "mrpc")
    logits, labels = eval_preds
    predictions = np.argmax(logits, axis=-1)
    return metric.compute(predictions=predictions, references=labels)
```

각 에폭이 끝날 때마다 메트릭이 출력되도록, 이 `compute_metrics()` 함수가 포함하여 `Trainer`를 새로 정의해 보겠습니다.

```py
training_args = TrainingArguments("test-trainer", eval_strategy="epoch")
model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)

trainer = Trainer(
    model,
    training_args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation"],
    data_collator=data_collator,
    processing_class=tokenizer,
    compute_metrics=compute_metrics,
)
```

참고로, 우리는 `eval_strategy`를 `"epoch"`으로 설정한 새로운 `TrainingArguments`와 새로운 모델을 생성합니다. 이렇게 하지 않으면 이미 훈련된 모델의 훈련을 계속하게 될 겁니다. 새로운 훈련을 시작하려면 다음을 실행하세요.

```py
trainer.train()
```

이번에는 훈련 손실 외에도 각 에폭이 끝날 때마다 검증 손실과 메트릭이 함께 출력될 겁니다. 앞서 말했듯이 모델 헤드의 무작위 초기화 때문에 여러분이 얻는 정확도/F1 점수는 우리가 얻은 결과와 약간 다를 수 있지만, 비슷한 범위에 있을 겁니다.

### 고급 훈련 기능[[advanced-training-features]]

`Trainer`는 현대 딥러닝의 모범 사례들을 쉽게 활용할 수 있도록 다양한 내장 기능을 제공합니다.

**혼합 정밀도 훈련**: 더 빠른 훈련과 메모리 사용량 감소를 위해 훈련 인수에서 `fp16=True`를 설정하세요.

```py
training_args = TrainingArguments(
    "test-trainer",
    eval_strategy="epoch",
    fp16=True,  # 혼합 정밀도 활성화
)
```

**그레이디언트 누적**: GPU 메모리가 부족할 때 더 큰 배치 크기로 학습하는 효과를 낼 수 있습니다.

```py
training_args = TrainingArguments(
    "test-trainer",
    eval_strategy="epoch",
    per_device_train_batch_size=4,
    gradient_accumulation_steps=4,  # 유효 배치 크기 = 4 * 4 = 16
)
```

**학습률 스케줄링**: Trainer는 기본적으로 선형 감소 방식을 사용하지만, 사용자 맞춤 설정이 가능합니다.

```py
training_args = TrainingArguments(
    "test-trainer",
    eval_strategy="epoch",
    learning_rate=2e-5,
    lr_scheduler_type="cosine",  # 다른 스케줄러 시도
)
```

<Tip>

🎯 **성능 최적화**: 분산 훈련, 메모리 최적화, 하드웨어별 최적화를 포함한 고급 훈련 기술에 대해서는 [🤗 Transformers 성능 가이드](https://huggingface.co/docs/transformers/main/en/performance)를 살펴보세요.

</Tip>

`Trainer`는 여러 GPU 또는 TPU에서 즉시 작동하며 분산 훈련을 위한 많은 옵션을 제공합니다. 이와 관련된 모든 내용은 Chapter 10에서 다루겠습니다.

이것으로 `Trainer` API를 사용한 미세 조정 소개를 마칩니다. 대부분의 일반적인 NLP 작업에 대한 예제는 [Chapter 7](/course/chapter7)에서 다룰 예정이며, 다음으로는 순수 PyTorch 코드로 동일한 작업을 수행하는 방법을 살펴보겠습니다.

<Tip>

📝 **더 많은 예제**: [🤗 Transformers 노트북](https://huggingface.co/docs/transformers/main/en/notebooks)에 있는 방대한 자료를 확인해 보세요.

</Tip>

## 섹션 퀴즈[[section-quiz]]

Trainer API와 미세 조정 개념에 대한 이해를 테스트해보세요.

### 1. `Trainer`에서 <code>processing_class</code> 매개변수의 목적은 무엇인가요?

<Question
	choices={[
		{
			text: "사용할 모델 아키텍처를 지정합니다.",
			explain: "모델 아키텍처는 모델을 로드할 때 지정되며, Trainer에서 지정하지 않습니다."
		},
		{
			text: "데이터 처리에 사용할 토크나이저를 Trainer에 알려줍니다.",
			explain: "processing_class 매개변수는 사용할 토크나이저를 Trainer가 알 수 있도록 도와주는 최신 추가 사항입니다.",
            correct: true
		},
		{
			text: "훈련을 위한 배치 크기를 결정합니다.",
			explain: "배치 크기는 processing_class가 아닌 TrainingArguments에서 설정됩니다."
		},
        {
			text: "평가 빈도를 제어합니다.",
			explain: "평가 빈도는 TrainingArguments의 eval_strategy로 제어됩니다."
		}
	]}
/>

### 2. 훈련 중 평가가 얼마나 자주 발생하는지를 제어하는 TrainingArguments 매개변수는 무엇인가요?

<Question
	choices={[
		{
			text: "eval_frequency",
			explain: "TrainingArguments에는 eval_frequency 매개변수가 없습니다."
		},
		{
			text: "eval_strategy",
			explain: "eval_strategy는 평가 타이밍을 제어하기 위해 'epoch', 'steps', 또는 'no'로 설정할 수 있습니다.",
            correct: true
		},
		{
			text: "evaluation_steps",
			explain: "eval_steps는 평가 사이의 단계 수를 설정하지만, eval_strategy가 평가 발생 여부/시기를 결정합니다."
		},
        {
			text: "do_eval",
			explain: "최신 TrainingArguments에는 do_eval 매개변수가 없습니다."
		}
	]}
/>

### 3. TrainingArguments에서 <code>fp16=True</code>는 무엇을 활성화하나요?

<Question
	choices={[
		{
			text: "더 빠른 훈련을 위한 16비트 정수 정밀도",
			explain: "fp16은 정수 정밀도가 아닌 부동소수점 정밀도를 의미합니다."
		},
		{
			text: "더 빠른 훈련과 메모리 사용량 감소를 위한 16비트 부동소수점 수를 사용한 혼합 정밀도 훈련",
			explain: "혼합 정밀도 훈련은 순전파에는 16비트 플로트를, 그레이디언트에는 32비트를 사용하여 속도를 향상시키고 메모리 사용량을 줄입니다.",
            correct: true
		},
		{
			text: "정확히 16 에포크 동안 훈련",
			explain: "fp16은 에포크 수와 관련이 없습니다."
		},
        {
			text: "분산 훈련을 위한 16개 GPU 사용",
			explain: "GPU 수는 fp16 매개변수로 제어되지 않습니다."
		}
	]}
/>

### 4. Trainer에서 <code>compute_metrics</code> 함수의 역할은 무엇인가요?

<Question
	choices={[
		{
			text: "훈련 중 손실을 계산합니다.",
			explain: "손실 계산은 compute_metrics가 아닌 모델에서 자동으로 처리됩니다."
		},
		{
			text: "로짓을 예측으로 변환하고 정확도 및 F1과 같은 평가 메트릭을 계산합니다.",
			explain: "compute_metrics는 예측과 라벨을 받아서 평가를 위한 메트릭을 반환합니다.",
            correct: true
		},
		{
			text: "사용할 옵티마이저를 결정합니다.",
			explain: "옵티마이저 선택은 compute_metrics로 처리되지 않습니다."
		},
        {
			text: "훈련 데이터를 전처리합니다.",
			explain: "데이터 전처리는 훈련 전에 수행되며, 평가 중 compute_metrics로 수행되지 않습니다."
		}
	]}
/>

### 5. Trainer에 <code>eval_dataset</code>을 제공하지 않으면 어떻게 되나요?

<Question
	choices={[
		{
			text: "훈련이 오류와 함께 실패합니다.",
			explain: "eval_dataset 없이도 훈련을 진행할 수 있지만, 평가 메트릭은 얻을 수 없습니다."
		},
		{
			text: "Trainer가 자동으로 훈련 데이터를 평가용으로 분할합니다.",
			explain: "Trainer는 자동으로 검증 분할을 생성하지 않습니다."
		},
		{
			text: "훈련 중 평가 메트릭을 얻을 수 없지만 훈련은 여전히 작동합니다.",
			explain: "평가는 선택사항입니다 - 평가 없이도 훈련할 수 있지만 검증 메트릭은 볼 수 없습니다.",
            correct: true
		},
        {
			text: "모델이 평가를 위해 훈련 데이터를 사용합니다.",
			explain: "Trainer는 자동으로 평가를 위해 훈련 데이터를 사용하지 않습니다 - 단순히 평가하지 않습니다."
		}
	]}
/>

### 6. 그레이디언트 누적이란 무엇이며 어떻게 활성화하나요?

<Question
	choices={[
		{
			text: "그레이디언트를 디스크에 저장하는 것으로, save_gradients=True로 활성화됩니다.",
			explain: "그레이디언트 누적은 그레이디언트를 디스크에 저장하는 것과 관련이 없습니다."
		},
		{
			text: "업데이트 전에 여러 배치에 걸쳐 그레이디언트를 누적하는 것으로, gradient_accumulation_steps로 활성화됩니다.",
			explain: "이를 통해 여러 순전파에 걸쳐 그레이디언트를 누적하여 더 큰 배치 크기를 시뮬레이션할 수 있습니다.",
            correct: true
		},
		{
			text: "그레이디언트 계산을 가속화하는 것으로, fp16과 함께 자동으로 활성화됩니다.",
			explain: "fp16이 훈련을 가속화할 수 있지만, 그레이디언트 누적은 별도의 기술입니다."
		},
        {
			text: "그레이디언트 오버플로우를 방지하는 것으로, gradient_clipping=True로 활성화됩니다.",
			explain: "이는 그레이디언트 누적이 아닌 그레이디언트 클리핑을 설명합니다."
		}
	]}
/>

<Tip>

💡 **핵심 요점:**
- `Trainer` API는 대부분의 훈련 복잡성을 처리하는 높은 수준의 인터페이스를 제공합니다.
- `processing_class`는 적절한 데이터 처리를 위해 토크나이저를 저장하는 데 사용됩니다.
- `TrainingArguments`는 학습률, 배치 크기, 평가 전략, 최적화 등 훈련의 모든 측면을 제어합니다.
- `compute_metrics`를 사용하면 훈련 손실 외에 사용자 정의 평가 메트릭을 활용할 수 있습니다.
- 혼합 정밀도(`fp16=True`)와 그레이디언트 누적과 같은 최신 기능은 훈련 효율성을 크게 향상시킬 수 있습니다.

</Tip>



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

### 데이터 처리[[processing-the-data]]
https://huggingface.co/learn/course/ko/chapter3/2.md

# 데이터 처리[[processing-the-data]]

<CourseFloatingBanner chapter={3}
  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/chapter3/section2.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter3/section2.ipynb"},
]} />

[이전 챕터](/course/chapter2)의 예제에 이어서, 한 배치에서 시퀀스 분류기를 훈련하는 방법은 다음과 같습니다.

```python
import torch
from torch.optim import AdamW
from transformers import AutoTokenizer, AutoModelForSequenceClassification

# 이전과 동일
checkpoint = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
sequences = [
    "I've been waiting for a HuggingFace course my whole life.",
    "This course is amazing!",
]
batch = tokenizer(sequences, padding=True, truncation=True, return_tensors="pt")

# 여기가 새로운 부분
batch["labels"] = torch.tensor([1, 1])

optimizer = AdamW(model.parameters())
loss = model(**batch).loss
loss.backward()
optimizer.step()
```

물론 두 문장만으로 모델을 훈련하는 것으로는 매우 좋은 결과를 얻을 수 없습니다. 더 나은 결과를 얻으려면 더 큰 데이터 세트를 준비해야 합니다.

이 섹션에서는 William B. Dolan과 Chris Brockett의 [논문](https://www.aclweb.org/anthology/I05-5002.pdf)에서 소개된 MRPC(Microsoft Research Paraphrase Corpus) 데이터 세트를 예제로 사용하겠습니다. 이 데이터 세트는 5,801개의 문장 쌍으로 구성되어 있으며, 각 쌍이 패러프레이즈인지 아닌지를 나타내는 레이블이 있습니다(즉, 두 문장이 같은 의미인지). 이 챕터에서 이 데이터 세트를 선택한 이유는 작은 데이터 세트이므로 훈련 실험을 하기에 쉽기 때문입니다.

### Hub에서 데이터 세트 가져오기[[loading-a-dataset-from-the-hub]]

<Youtube id="_BZearw7f0w"/>

Hub에는 모델뿐만 아니라 다양한 언어로 된 여러 데이터 세트도 있습니다. [여기](https://huggingface.co/datasets)에서 데이터 세트를 찾아볼 수 있으며, 이 섹션을 완료한 후에는 새로운 데이터 세트를 로드하고 처리해보는 것을 권장합니다([여기](https://huggingface.co/docs/datasets/loading)에서 일반적인 문서를 참조하세요). 하지만 지금은 MRPC 데이터 세트에 집중해보겠습니다! 이것은 [GLUE 벤치마크](https://gluebenchmark.com/)를 구성하는 10개 데이터 세트 중 하나로, 10개의 서로 다른 텍스트 분류 작업에 걸쳐 ML 모델의 성능을 측정하는 데 사용되는 학술적 벤치마크입니다.

🤗 Datasets 라이브러리는 Hub에서 데이터 세트를 다운로드하고 캐시하는 매우 간단한 명령을 제공합니다. MRPC 데이터 세트를 다음과 같이 다운로드할 수 있습니다.

<Tip>

💡 **추가 자료**: 더 많은 데이터 세트 로딩 기법과 예제를 보려면 [🤗 Datasets 문서](https://huggingface.co/docs/datasets/)를 확인하세요.

</Tip> 

```py
from datasets import load_dataset

raw_datasets = load_dataset("glue", "mrpc")
raw_datasets
```

```python out
DatasetDict({
    train: Dataset({
        features: ['sentence1', 'sentence2', 'label', 'idx'],
        num_rows: 3668
    })
    validation: Dataset({
        features: ['sentence1', 'sentence2', 'label', 'idx'],
        num_rows: 408
    })
    test: Dataset({
        features: ['sentence1', 'sentence2', 'label', 'idx'],
        num_rows: 1725
    })
})
```

보시다시피, 훈련 세트, 검증 세트, 테스트 세트가 포함된 `DatasetDict` 객체를 얻습니다. 각각은 여러 열(`sentence1`, `sentence2`, `label`, `idx`)과 가변적인 행 수를 포함하며, 이는 각 세트의 요소 수입니다(따라서 훈련 세트에는 3,668개의 문장 쌍이, 검증 세트에는 408개가, 테스트 세트에는 1,725개가 있습니다).

<Tip>

이 명령은 기본적으로 *~/.cache/huggingface/datasets*에 데이터 세트를 다운로드하고 캐시합니다. 2장에서 언급했듯이 `HF_HOME` 환경 변수를 설정하여 캐시 폴더를 맞춤 설정할 수 있습니다.

</Tip>

딕셔너리처럼 인덱싱하여 `raw_datasets` 객체의 각 문장 쌍에 접근할 수 있습니다.

```py
raw_train_dataset = raw_datasets["train"]
raw_train_dataset[0]
```

```python out
{'idx': 0,
 'label': 1,
 'sentence1': 'Amrozi accused his brother , whom he called " the witness " , of deliberately distorting his evidence .',
 'sentence2': 'Referring to him as only " the witness " , Amrozi accused his brother of deliberately distorting his evidence .'}
```

레이블이 이미 정수로 되어 있으므로 여기서 전처리를 할 필요가 없습니다. 어떤 정수가 어떤 레이블에 해당하는지 알아보려면 `raw_train_dataset`의 `features`를 검사하면 됩니다. 이것은 각 열의 타입을 알려줍니다.

```py
raw_train_dataset.features
```

```python out
{'sentence1': Value(dtype='string', id=None),
 'sentence2': Value(dtype='string', id=None),
 'label': ClassLabel(num_classes=2, names=['not_equivalent', 'equivalent'], names_file=None, id=None),
 'idx': Value(dtype='int32', id=None)}
```

내부적으로 `label`은 `ClassLabel` 타입이며, 정수와 레이블 이름의 매핑이 *names* 폴더에 저장되어 있습니다. `0`은 `not_equivalent`에, `1`은 `equivalent`에 해당합니다.

<Tip>

✏️ **직접 해보기!** 훈련 세트의 15번째 요소와 검증 세트의 87번째 요소를 살펴보세요. 그들의 레이블은 무엇인가요?

</Tip>

### 데이터 세트 전처리[[preprocessing-a-dataset]]

<Youtube id="0u3ioSwev3s"/>

데이터 세트를 전처리하려면 텍스트를 모델이 이해할 수 있는 숫자로 변환해야 합니다. [이전 챕터](/course/chapter2)에서 보았듯이, 이는 토크나이저로 수행됩니다. 토크나이저에 한 문장이나 문장 목록을 입력할 수 있으므로, 다음과 같이 각 쌍의 모든 첫 번째 문장과 모든 두 번째 문장을 직접 토큰화할 수 있습니다.

```py
from transformers import AutoTokenizer

checkpoint = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
tokenized_sentences_1 = tokenizer(raw_datasets["train"]["sentence1"])
tokenized_sentences_2 = tokenizer(raw_datasets["train"]["sentence2"])
```

<Tip>

💡 **심화 학습**: 더 고급 토큰화 기법과 다양한 토크나이저가 작동하는 방식을 이해하려면 [🤗 Tokenizers 문서](https://huggingface.co/docs/transformers/main/en/tokenizer_summary)와 [쿡북의 토큰화 가이드](https://huggingface.co/learn/cookbook/en/advanced_rag#tokenization-strategies)를 살펴보세요.

</Tip>

하지만 두 시퀀스를 모델에 전달하기만 해서는 두 문장이 패러프레이즈인지 아닌지 예측할 수 없습니다. 두 시퀀스를 쌍으로 처리하고 적절한 전처리를 적용해야 합니다. 다행히 토크나이저는 한 쌍의 시퀀스를 받아서 BERT 모델이 기대하는 방식으로 준비할 수도 있습니다.

```py
inputs = tokenizer("This is the first sentence.", "This is the second one.")
inputs
```

```python out
{ 
  'input_ids': [101, 2023, 2003, 1996, 2034, 6251, 1012, 102, 2023, 2003, 1996, 2117, 2028, 1012, 102],
  'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1],
  'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
}
```

[2장](/course/chapter2)에서 `input_ids`와 `attention_mask` 키에 대해 논의했지만, `token_type_ids`에 대한 이야기는 미뤄두었습니다. 이 예제에서 이것은 입력의 어느 부분이 첫 번째 문장이고 어느 부분이 두 번째 문장인지 모델에 알려주는 역할을 합니다.

<Tip>

✏️ **직접 해보기!** 훈련 세트의 15번째 요소를 가져와서 두 문장을 따로따로 토큰화하고 쌍으로도 토큰화해보세요. 두 결과의 차이점은 무엇인가요?

</Tip>

`input_ids` 안의 ID를 다시 단어로 디코딩하면

```py
tokenizer.convert_ids_to_tokens(inputs["input_ids"])
```

다음을 얻습니다.

```python out
['[CLS]', 'this', 'is', 'the', 'first', 'sentence', '.', '[SEP]', 'this', 'is', 'the', 'second', 'one', '.', '[SEP]']
```

따라서 모델은 두 문장이 있을 때 입력이 `[CLS] sentence1 [SEP] sentence2 [SEP]` 형태이기를 기대한다는 것을 알 수 있습니다. 이를 `token_type_ids`와 맞춰보면

```python out
['[CLS]', 'this', 'is', 'the', 'first', 'sentence', '.', '[SEP]', 'this', 'is', 'the', 'second', 'one', '.', '[SEP]']
[      0,      0,    0,     0,       0,          0,   0,       0,      1,    1,     1,        1,     1,   1,       1]
```

보시다시피, `[CLS] sentence1 [SEP]`에 해당하는 입력 부분은 모두 토큰 타입 ID가 `0`이고, `sentence2 [SEP]`에 해당하는 다른 부분들은 모두 토큰 타입 ID가 `1`입니다.

다른 체크포인트(checkpoint)를 선택하면 토큰화된 입력에 `token_type_ids`가 반드시 있지는 않다는 점에 주의하세요(예를 들어, DistilBERT 모델을 사용하면 반환되지 않습니다). 모델이 사전 훈련 중에 이를 본 적이 있어서 무엇을 해야 할지 알 때만 반환됩니다.

여기서 BERT는 토큰 타입 ID로 사전 훈련되었으며, [1장](/course/chapter1)에서 이야기한 마스크드 언어 모델링 목표 외에도 _다음 문장 예측_이라는 추가 목표를 가지고 있습니다. 이 작업의 목표는 문장 쌍 간의 관계를 모델링하는 것입니다.

다음 문장 예측에서는 모델에 문장 쌍(무작위로 마스킹된 토큰과 함께)이 제공되고 두 번째 문장이 첫 번째 문장을 따르는지 예측하도록 요청받습니다. 작업을 쉽지 않게 만들기 위해, 절반의 경우에는 문장들이 추출된 원본 문서에서 서로를 따르고, 나머지 절반의 경우에는 두 문장이 서로 다른 문서에서 나옵니다.

일반적으로 토큰화된 입력에 `token_type_ids`가 있는지 여부에 대해 걱정할 필요는 없습니다. 토크나이저와 모델에 동일한 체크포인트(checkpoint)를 사용하는 한, 토크나이저가 모델에 제공해야 할 것을 알고 있으므로 모든 것이 잘 될 것입니다.

이제 토크나이저가 한 쌍의 문장을 어떻게 처리할 수 있는지 보았으므로, 이를 사용하여 전체 데이터 세트를 토큰화할 수 있습니다: [이전 챕터](/course/chapter2)에서처럼, 첫 번째 문장 목록과 두 번째 문장 목록을 제공하여 토크나이저에 문장 쌍 목록을 입력할 수 있습니다. 이는 [2장](/course/chapter2)에서 본 패딩과 생략 옵션과도 호환됩니다. 따라서 훈련 데이터 세트를 전처리하는 한 가지 방법은

```py
tokenized_dataset = tokenizer(
    raw_datasets["train"]["sentence1"],
    raw_datasets["train"]["sentence2"],
    padding=True,
    truncation=True,
)
```

이것은 잘 작동하지만, 딕셔너리(키는 `input_ids`, `attention_mask`, `token_type_ids`이고 값은 목록의 목록)를 반환한다는 단점이 있습니다. 또한 토큰화 중에 전체 데이터 세트를 메모리에 저장할 수 있는 충분한 RAM이 있는 경우에만 작동합니다(🤗 Datasets 라이브러리의 데이터 세트는 디스크에 저장된 [Apache Arrow](https://arrow.apache.org/) 파일이므로, 요청한 샘플만 메모리에 로드됩니다).

데이터를 데이터 세트로 유지하려면 [`Dataset.map()`](https://huggingface.co/docs/datasets/package_reference/main_classes#datasets.Dataset.map) 메소드를 사용하겠습니다. 이는 토큰화 이상의 전처리가 필요한 경우 추가적인 유연성도 제공합니다. `map()` 메소드는 데이터 세트의 각 요소에 함수를 적용하여 작동하므로, 입력을 토큰화하는 함수를 정의해보겠습니다.

```py
def tokenize_function(example):
    return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
```

이 함수는 딕셔너리(데이터 세트의 항목과 같은)를 받아서 `input_ids`, `attention_mask`, `token_type_ids` 키가 있는 새 딕셔너리를 반환합니다. `example` 딕셔너리에 여러 샘플이 포함되어 있어도(각 키가 문장 목록으로) 작동한다는 점에 주목하세요. 앞서 본 것처럼 `tokenizer`는 문장 쌍의 목록에서 작동하기 때문입니다. 이를 통해 `map()` 호출에서 `batched=True` 옵션을 사용할 수 있으며, 이는 토큰화를 크게 가속화할 것입니다. `tokenizer`는 [🤗 Tokenizers](https://github.com/huggingface/tokenizers) 라이브러리의 Rust로 작성된 토크나이저로 뒷받침됩니다. 이 토크나이저는 매우 빠를 수 있지만, 한 번에 많은 입력을 제공해야만 그렇습니다.

지금은 토큰화 함수에서 `padding` 인수를 빼둔 것에 주목하세요. 모든 샘플을 최대 길이로 패딩하는 것은 효율적이지 않기 때문입니다. 배치를 만들 때 샘플을 패딩하는 것이 더 좋습니다. 그러면 해당 배치의 최대 길이까지만 패딩하면 되고, 전체 데이터 세트의 최대 길이까지 패딩할 필요가 없기 때문입니다. 입력의 길이가 매우 다양할 때 많은 시간과 처리 능력을 절약할 수 있습니다!

<Tip>

📚 **성능 팁**: 효율적인 데이터 처리 기법에 대한 자세한 내용은 [🤗 Datasets 성능 가이드](https://huggingface.co/docs/datasets/about_arrow)에서 배울 수 있습니다.

</Tip>

다음은 모든 데이터 세트에 토큰화 함수를 한 번에 적용하는 방법입니다. `map` 호출에서 `batched=True`를 사용하므로 함수가 데이터 세트의 각 요소에 개별적으로가 아니라 여러 요소에 한 번에 적용됩니다. 이를 통해 더 빠른 전처리가 가능합니다.

```py
tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
tokenized_datasets
```

🤗 Datasets 라이브러리가 이 처리를 적용하는 방식은 전처리 함수가 반환하는 딕셔너리의 각 키에 대해 데이터 세트에 새 필드를 추가하는 것입니다.

```python out
DatasetDict({
    train: Dataset({
        features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
        num_rows: 3668
    })
    validation: Dataset({
        features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
        num_rows: 408
    })
    test: Dataset({
        features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
        num_rows: 1725
    })
})
```

`num_proc` 인수를 전달하여 `map()`으로 전처리 함수를 적용할 때 멀티프로세싱을 사용할 수도 있습니다. 🤗 Tokenizers 라이브러리가 이미 여러 스레드를 사용하여 샘플을 더 빠르게 토큰화하므로 여기서는 이를 사용하지 않았지만, 이 라이브러리가 뒷받침하는 빠른 토크나이저를 사용하지 않는다면 전처리 속도를 높일 수 있습니다.

우리의 `tokenize_function`은 `input_ids`, `attention_mask`, `token_type_ids` 키가 있는 딕셔너리를 반환하므로, 이 세 필드가 데이터 세트의 모든 분할에 추가됩니다. 전처리 함수가 `map()`을 적용한 데이터 세트의 기존 키에 대한 새 값을 반환한다면 기존 필드를 변경할 수도 있었을 것입니다.

마지막으로 해야 할 일은 요소들을 배치로 묶을 때 모든 예제를 가장 긴 요소의 길이로 패딩하는 것입니다 — 이 기법을 *동적 패딩*이라고 합니다.

##### 동적 패딩[[dynamic-padding]]

<Youtube id="7q5NyFT8REg"/>

배치 내에서 샘플들을 함께 배치하는 역할을 하는 함수를 *collate function*이라고 합니다. 이는 `DataLoader`를 구축할 때 전달할 수 있는 인수로, 기본값은 샘플을 PyTorch 텐서로 변환하고 연결하는 함수입니다(요소가 목록, 튜플 또는 딕셔너리인 경우 재귀적으로). 우리의 경우 입력이 모두 같은 크기가 아니므로 이것은 불가능할 것입니다. 우리는 의도적으로 패딩을 연기하여 각 배치에서만 필요에 따라 적용하고 많은 패딩이 있는 지나치게 긴 입력을 피했습니다. 이것은 훈련을 상당히 가속화할 것이지만, TPU에서 훈련하는 경우 문제를 일으킬 수 있다는 점에 주의하세요 — TPU는 추가 패딩이 필요하더라도 고정된 모양을 선호합니다.

<Tip>

🚀 **최적화 가이드**: 패딩 전략과 TPU 고려사항을 포함한 훈련 성능 최적화에 대한 자세한 내용은 [🤗 Transformers 성능 문서](https://huggingface.co/docs/transformers/main/en/performance)를 참조하세요.

</Tip>

실제로 이를 수행하려면 함께 배치하려는 데이터 세트 항목에 적절한 양의 패딩을 적용할 collate function을 정의해야 합니다. 다행히 🤗 Transformers 라이브러리는 `DataCollatorWithPadding`을 통해 이러한 함수를 제공합니다. 인스턴스화할 때 토크나이저를 받아서(어떤 패딩 토큰을 사용할지, 모델이 입력의 왼쪽 또는 오른쪽에 패딩을 기대하는지 알기 위해) 필요한 모든 것을 수행합니다.

```py
from transformers import DataCollatorWithPadding

data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
```

이 새로운 도구를 테스트하기 위해, 함께 배치하고 싶은 훈련 세트에서 몇 개의 샘플을 가져와보겠습니다. 여기서는 `idx`, `sentence1`, `sentence2` 열을 제거합니다. 이들은 필요하지 않고 문자열을 포함하고 있으며(문자열로는 텐서를 만들 수 없음), 배치의 각 항목 길이를 살펴보겠습니다.

```py
samples = tokenized_datasets["train"][:8]
samples = {k: v for k, v in samples.items() if k not in ["idx", "sentence1", "sentence2"]}
[len(x) for x in samples["input_ids"]]
```

```python out
[50, 59, 47, 67, 59, 50, 62, 32]
```

당연히 32부터 67까지 다양한 길이의 샘플을 얻습니다. 동적 패딩은 이 배치의 샘플들이 모두 배치 내 최대 길이인 67로 패딩되어야 함을 의미합니다. 동적 패딩이 없다면, 모든 샘플이 전체 데이터 세트의 최대 길이나 모델이 받을 수 있는 최대 길이로 패딩되어야 할 것입니다. `data_collator`가 배치를 동적으로 올바르게 패딩하는지 다시 확인해보겠습니다.

```py
batch = data_collator(samples)
{k: v.shape for k, v in batch.items()}
```

```python out
{'attention_mask': torch.Size([8, 67]),
 'input_ids': torch.Size([8, 67]),
 'token_type_ids': torch.Size([8, 67]),
 'labels': torch.Size([8])}
```

좋아 보입니다! 이제 원시 텍스트에서 모델이 처리할 수 있는 배치까지 만들었으므로, 미세 조정할 준비가 되었습니다!

<Tip>

✏️ **직접 해보기!** GLUE SST-2 데이터 세트에서 전처리를 복제해보세요. 쌍이 아닌 단일 문장으로 구성되어 있어 약간 다르지만, 나머지는 동일하게 보일 것입니다. 더 어려운 도전을 위해서는 GLUE 작업 중 어떤 것에서도 작동하는 전처리 함수를 작성해보세요.

📖 **추가 연습**: [🤗 Transformers 예제](https://huggingface.co/docs/transformers/main/en/notebooks)에서 이러한 실습 예제들을 확인해보세요.

</Tip>

완벽합니다! 이제 🤗 Datasets 라이브러리의 최신 모범 사례로 데이터를 전처리했으므로, 최신 Trainer API를 사용하여 모델을 훈련할 준비가 되었습니다. 다음 섹션에서는 Hugging Face 생태계에서 사용할 수 있는 최신 기능과 최적화를 사용하여 모델을 효과적으로 미세 조정하는 방법을 보여드리겠습니다.

## 섹션 퀴즈[[section-quiz]]

데이터 처리 개념에 대한 이해도를 테스트해보세요.

### 1. `batched=True`와 함께 `Dataset.map()`을 사용하는 주요 장점은 무엇인가요?

<Question
	choices={[
		{
			text: "메모리를 덜 사용합니다.",
			explain: "더 메모리 효율적일 수 있지만, 이것이 주요 장점은 아닙니다."
		},
		{
			text: "여러 예제를 한 번에 처리하여 토큰화를 훨씬 빠르게 만듭니다.",
			explain: "정답입니다! 배치로 처리하면 빠른 토크나이저가 여러 예제를 동시에 작업할 수 있어 속도가 크게 향상됩니다.",
            correct: true
		},
		{
			text: "자동으로 패딩을 처리해줍니다.",
			explain: "배치 처리가 자동으로 패딩을 처리하지는 않습니다 - 그것은 데이터 콜레이터가 수행합니다."
		},
        {
			text: "데이터를 PyTorch 텐서로 변환합니다.",
			explain: "텐서 변환은 형식을 설정할 때 발생하며, 배치 매핑 중에는 발생하지 않습니다."
		}
	]}
/>

### 2. 데이터 세트의 최대 길이로 모든 시퀀스를 패딩하는 대신 동적 패딩을 사용하는 이유는 무엇인가요?

<Question
	choices={[
		{
			text: "동적 패딩이 모델 아키텍처에 의해 요구됩니다.",
			explain: "아니요, 모델은 고정 패딩과 동적 패딩을 모두 처리할 수 있습니다."
		},
		{
			text: "각 배치의 최대 길이까지만 패딩하여 계산 오버헤드를 줄입니다.",
			explain: "정답입니다! 동적 패딩은 데이터 세트 최대값이 아닌 배치 최대값까지만 패딩하여 패딩 토큰에 대한 불필요한 계산을 피합니다.",
            correct: true
		},
		{
			text: "모델 정확도를 향상시킵니다.",
			explain: "패딩 전략은 모델 정확도에 직접적인 영향을 주지 않습니다."
		},
        {
			text: "DataCollatorWithPadding을 사용할 때 필수입니다.",
			explain: "DataCollatorWithPadding이 동적 패딩을 가능하게 하지만, 원한다면 여전히 고정 패딩을 사용할 수 있습니다."
		}
	]}
/>

### 3. BERT 토큰화에서 `token_type_ids` 필드는 무엇을 나타내나요?

<Question
	choices={[
		{
			text: "시퀀스에서 각 토큰의 위치입니다.",
			explain: "그것은 위치 임베딩이지, token_type_ids가 아닙니다."
		},
		{
			text: "문장 쌍을 처리할 때 각 토큰이 어느 문장에 속하는지를 나타냅니다.",
			explain: "정답입니다! token_type_ids는 문장 쌍 작업에서 첫 번째 문장(0)과 두 번째 문장(1)을 구분합니다.",
            correct: true
		},
		{
			text: "각 토큰의 어텐션 마스크입니다.",
			explain: "어텐션 마스크는 어떤 토큰에 주의를 기울일지 나타내는 별도의 필드입니다."
		},
        {
			text: "각 토큰의 어휘 ID입니다.",
			explain: "그것은 token_type_ids가 아닌 input_ids 필드입니다."
		}
	]}
/>

### 4. `load_dataset('glue', 'mrpc')`로 데이터 세트를 로딩할 때 두 번째 인수는 무엇을 지정하나요?

<Question
	choices={[
		{
			text: "로딩할 데이터 세트의 버전입니다.",
			explain: "버전 지정은 다른 매개변수를 사용합니다."
		},
		{
			text: "GLUE 벤치마크 내의 특정 작업 또는 하위 집합입니다.",
			explain: "정답입니다! MRPC는 더 큰 GLUE 벤치마크 컬렉션 내의 특정 작업 중 하나입니다.",
            correct: true
		},
		{
			text: "데이터 세트의 분할(train/validation/test)입니다.",
			explain: "분할은 로딩 후에 접근하며, load_dataset 호출에서 지정하지 않습니다."
		},
        {
			text: "데이터를 반환할 형식입니다.",
			explain: "형식은 로딩 후 set_format() 메소드를 사용하여 설정합니다."
		}
	]}
/>

### 5. 훈련 전에 'sentence1'과 'sentence2' 같은 열을 제거하는 목적은 무엇인가요?

<Question
	choices={[
		{
			text: "훈련 중 메모리를 절약하기 위해서입니다.",
			explain: "약간의 메모리는 절약되지만, 이것이 주된 이유는 아닙니다."
		},
		{
			text: "모델이 이러한 원시 텍스트 열을 예상하지 않고 오류가 발생할 수 있기 때문입니다.",
			explain: "정답입니다! 모델은 원시 텍스트 문자열이 아닌 수치적 텐서를 예상합니다. 텍스트 열을 유지하면 오류가 발생할 것입니다.",
            correct: true
		},
		{
			text: "이러한 열들이 평가에 필요하지 않기 때문입니다.",
			explain: "사실이긴 하지만, 주된 이유는 모델이 원시 텍스트를 처리할 수 없다는 것입니다."
		},
        {
			text: "훈련 속도를 크게 향상시키기 때문입니다.",
			explain: "호환되지 않는 데이터 타입으로 인한 오류 방지에 비해 속도 향상은 미미합니다."
		}
	]}
/>

<Tip>

💡 **핵심 요점**
- 전처리를 훨씬 빠르게 하려면 `Dataset.map()`에서 `batched=True`를 사용하세요
- `DataCollatorWithPadding`을 사용한 동적 패딩이 고정 길이 패딩보다 효율적입니다
- 모델의 추론 결과물(수치적 텐서, 올바른 열 이름)에 맞게 항상 데이터를 전처리하세요
- 🤗 Datasets 라이브러리는 대규모 효율적인 데이터 처리를 위한 강력한 도구를 제공합니다

</Tip>

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

### 좋은 이슈를 작성하는 법
https://huggingface.co/learn/course/ko/chapter8/5.md

# 좋은 이슈를 작성하는 법

<CourseFloatingBanner chapter={8}
  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/chapter8/section5.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter8/section5.ipynb"},
]} />

Hugging Face 라이브러리 중에서 이상한 것을 발견하면 고칠 수 있도록 반드시 알려주셔야 합니다(해당 문제에 대해서는 모든 오픈 소스 라이브러리가 동일). 버그가 자신의 코드에 있는지 또는 라이브러리에 있는지 확실하지 않은 경우 가장 먼저 [포럼](https://discuss.huggingface.co/)에서 확인하세요. 커뮤니티에서 이를 파악하는 데 도움을 줄 것이며 Hugging Face 팀도 그곳에서 토론을 면밀히 관찰합니다.

<Youtube id="_PAli-V4wj0"/>

버그가 있다고 확신하는 경우 첫 번째 단계는 최소한의 재현 가능한 예제를 빌드하는 것입니다.

## 재현 가능한 최소한의 예제 만들기

버그를 생성하는 코드 조각을 분리하는 것이 매우 중요합니다. Hugging Face 팀의 어느 누구도 (아직) 마술사가 아니며 볼 수 없는 것을 고칠 수 없기 때문입니다. 최소한의 재현 가능한 예는 이름에서 알 수 있듯이 재현 가능해야 합니다. 즉, 가지고 있는 외부 파일이나 데이터에 의존해서는 안 됩니다. 사용 중인 데이터를 실제 값처럼 보이지만 여전히 동일한 오류를 생성하는 일부 더미 값으로 바꾸세요.

<Tip>

🚨 🤗 Transformers 저장소의 많은 문제는 트랜스포머를 재현하는 데 사용된 데이터에 액세스할 수 없기 때문에 해결되지 않습니다.

</Tip>

코드가 준비되었더라도 더 적은 수의 코드로 줄여서 우리가 _최소 재현 가능한 예제_라고 부르는 것을 만들 수 있습니다. 여기에는 약간의 추가 작업이 필요하지만 멋지고 짧은 버그 재현 코드를 제공하면 코드 수정에 대한 도움이 보장됩니다.

어렵지 않다면 버그가 발생한 소스 코드를 검사해보세요. 문제에 대한 해결책을 찾을 수 있지만(이 경우 문제를 해결하기 위해 직접 풀 요청을 제안할 수도 있음) 일반적으로 이것은 소스코드 메인테이너가 보고서를 읽을 때 소스를 더 잘 이해하는 데 도움을 줄 수 있습니다.

## 이슈 템플릿 작성하기

이슈를 제출하면 작성할 템플릿이 있음을 알 수 있습니다. 여기에서 [🤗 Transformers issues](https://github.com/huggingface/transformers/issues/new/choose)에 대한 정보를 따르지만 다른 프로젝트의 저장소에서 문제를 보고하는 경우에도 동일한 종류의 정보가 필요합니다. 템플릿을 비워두지 마세요. 시간을 들여 템플릿을 작성하면 답을 얻고 문제를 해결할 가능성이 극대화됩니다.

일반적으로 문제를 제기할 때는 항상 정중하게 행동하세요. 이 프로젝트는 오픈소스 이므로 여러분은 무료 소프트웨어를 사용하고 있으며 아무도 여러분을 도울 의무는 없습니다. 이슈에 정당한 비판이라고 생각하는 것을 포함할 수 있지만, 그러면 메인테이너는 그것을 나쁘게 받아들이고 서두르지 않을 것입니다. 프로젝트의 [행동 강령](https://github.com/huggingface/transformers/blob/master/CODE_OF_CONDUCT.md)을 반드시 읽어보세요.

### 개발 환경 정보 포함

🤗 Transformers는 사용자 환경에 대해 필요한 모든 정보를 얻을 수 있는 유틸리티를 제공합니다. 터미널에 다음을 입력하기만 하면 됩니다.:

```
transformers-cli env
```

그러면 아래와 같은 정보를 얻게 됩니다.:

```out
Copy-and-paste the text below in your GitHub issue and FILL OUT the two last points.

- `transformers` version: 4.12.0.dev0
- Platform: Linux-5.10.61-1-MANJARO-x86_64-with-arch-Manjaro-Linux
- Python version: 3.7.9
- PyTorch version (GPU?): 1.8.1+cu111 (True)
- Tensorflow version (GPU?): 2.5.0 (True)
- Flax version (CPU?/GPU?/TPU?): 0.3.4 (cpu)
- Jax version: 0.2.13
- JaxLib version: 0.1.65
- Using GPU in script?: <fill in>
- Using distributed or parallel set-up in script?: <fill in>
```
`transformers-cli env` 명령 시작 부분에 `!`를 추가하여 주피터 노트북 셀에서 실행한 다음 문제 시작 부분에 결과를 복사하여 붙여넣을 수도 있습니다.

### 사람 태깅하기

`@`를 입력하여 사람들을 태그하면 깃허브를 통해 알림이 전송되어 문제를 확인하고 더 빨리 응답할 수 있습니다. 당신이 태그한 사람들이 직접적인 링크가 없는 것에 대해 알림을 받는 것을 달가워하지 않을 수 있기 때문에 이것을 적당히 사용하세요. 버그와 관련된 소스 파일을 살펴본 경우 문제의 원인이라고 생각되는 줄에서 마지막으로 변경한 사람을 태그해야 합니다(이 정보는 GitHub에서 해당 줄을 보고 선택한 다음 "View git blame" 클릭).

아니면 템플릿에서 태그를 지정할 사람에 대한 제안을 받을 수 있습니다. 일반적으로 3명 이상 태그하지 마세요!

### 재현 가능한 예제 포함하기

버그를 생성하는 예제를 생성했다면 이제 버그를 포함할 때입니다! 다음과 같이 백틱 세 개 뒤에 `python`이 오는 행을 입력하세요.:

```
```python
```

그런 다음 최소한의 재현 가능한 예를 붙여넣고 세 개의 백틱이 있는 새 줄을 입력합니다. 이렇게 하면 코드의 형식이 올바르게 지정됩니다.

재현 가능한 예제를 생성하지 못한 경우 문제가 발생하는 방법을 명확한 단계로 설명하세요. 가능한 경우 오류가 발생한 Google Colab 노트북에 대한 링크를 포함합니다. 더 많은 정보를 공유할수록 메인테이너가 더 잘 답변할 수 있습니다.

모든 경우에 표시되는 전체 오류 메시지를 복사하여 붙여넣어야 합니다. Colab에서 작업하는 경우 일부 프레임이 Traceback에서 자동으로 축소될 수 있으므로 복사하기 전에 프레임을 확장했는지 확인하세요. 코드 샘플과 마찬가지로 세 개의 백틱이 있는 두 줄 사이에 해당 오류 메시지를 넣어 형식이 올바르게 지정되도록 합니다.

### 기대하는 동작 설명하기

메인테이너가 문제를 완전히 이해할 수 있도록 기대했던 것을 몇 줄로 설명하세요. 이 부분은 일반적으로 꽤 뻔하기 때문에 한 문장에 정리가 되어야 겠지만 경우에 따라 할 말이 많을 수도 있습니다.

## 그 다음?

이슈를 제출하고나서 모든 것이 괜찮은지 간단하게 확인하세요. 실수를 한 경우 이슈를 편집하거나 문제가 처음 생각한 것과 다르다는 것을 알게 된 경우 제목을 변경할 수도 있습니다.

답을 얻지 못하면 사람들에게 핑을 날릴 이유가 없습니다. 며칠 동안 아무도 당신을 도와주지 않는다면 아무도 이 문제를 이해할 수 없다는 이야기 입니다. 재현 가능한 예로 돌아가는 것을 주저하지 마십시오. 더 짧고 더 요점을 만들 수 있습니까? 일주일 안에 답변을 받지 못한 경우, 특히 문제에 대한 자세한 정보를 포함하도록 문제를 편집한 경우 부드럽게 도움을 요청하는 메시지를 남길 수 있습니다.

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

### 단원 소개
https://huggingface.co/learn/course/ko/chapter8/1.md

# 단원 소개

<CourseFloatingBanner
    chapter={8}
    classNames="absolute z-10 right-0 top-0"
/>

🤗 Transformers를 활용해 가장 일반적인 NLP 과제를 해결하는 방법을 배웠기 때문에, 이제 스스로 프로젝트를 진행할 차례입니다! 이번 단원에서는 문제가 생겼을 때 해야할 일을 살펴보겠습니다. 이 단원을 읽고 나면 학습 코드를 완벽하게 디버그 하는 방법과, 혼자서 해결할 수 없는 문제에 대해서 커뮤니티에 도움을 청하는 법을 배울 수 있으며, 만약 Huggingface 라이브러리에서 버그를 발견했을 시 해당 버그를 빠르게 해결 하기 위해 리포트하는 가장 좋은 방법을 제시하고자 합니다.

이번 단원에서 배우게 될 상세한 목차입니다.

- 에러가 발생했을 때 처음으로 할 일
- [포럼](https://discuss.huggingface.co/)에 도움을 요청하는 방법
- 학습 파이프라인을 디버그 하는 방법
- 이슈 리포트 작성하는 법

위 내용은 🤗 Transformers나 Huggingface 에코시스템과 특별히 관련 있는 것은 아니지만 대부분의 오픈 소스 프로젝트에 적용할 수 있습니다


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

### 포럼에서 도움 요청하기
https://huggingface.co/learn/course/ko/chapter8/3.md

# 포럼에서 도움 요청하기

<CourseFloatingBanner chapter={8}
  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/chapter8/section3.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter8/section3.ipynb"},
]} />

<Youtube id="S2EEG3JIt2A"/>

[HuggingFace 포럼](https://discuss.huggingface.co)은 오픈 소스 팀과 더 거대한 Hugging Face 커뮤니티의 도움을 받을 수 있는 좋은 공간입니다. 특정 날짜의 메인 페이지는 다음과 같습니다:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter8/forums.png" alt="The Hugging Face forums." width="100%"/>
</div>

왼쪽에는 그룹화된 다양한 주제의 카테고리가 ​있으며 오른쪽에는 가장 최근 주제가 표시됩니다. 주제는 제목, 카테고리 및 설명이 포함된 게시물입니다. 이는 [단원 5](/course/chapter5)에서 자체 데이터 세트를 생성할 때 본 GitHub 문제 형식과 매우 유사합니다. 이름에서 알 수 있듯이 [초보자](https://discuss.huggingface.co/c/beginners/5) 카테고리는 주로 HuggingFace 라이브러리 및 에코시스템을 처음 시작하는 사람들을 대상으로 합니다. 라이브러리에 대한 모든 질문은 환영하는 곳이며 일부 코드를 디버그하거나 무언가를 수행하는 방법에 대한 도움을 요청하는 곳 입니다. (즉, 질문이 특히 한 라이브러리에 관한 것이라면 포럼에서 해당 라이브러리 카테고리로 이동해야 할 것입니다.)

마찬가지로 [중급](https://discuss.huggingface.co/c/intermediate/6) 및 [연구](https://discuss.huggingface.co/c/research/7) 카테고리는 어려운 질문용입니다. 멋진 새 NLP 연구나 라이브러리들에 대해 토론하고 싶을 때의 내용을 예로 들 수 있습니다.

이어서 허깅페이스 코스와 관련된 질문을 할 수 있는 [코스](https://discuss.huggingface.co/c/course/20) 카테고리도 언급 안할 수가 없네요!!

카테고리를 선택하고나면 첫 번째 주제를 작성할 준비가 되었습니다. 이 포스트를 작성하는 방법은 포럼의 [가이드라인](https://discuss.huggingface.co/t/how-to-request-support/3128)에서 찾을 수 있으며 이 장에서는 좋은 주제를 작성하는 몇 가지 방법을 알아보겠습니다. 

## 포럼에 포스팅 잘쓰는 법

실습 예제로 Wikipedia 기사에서 임베딩을 생성하여 커스텀 검색 엔진을 만든다고 가정해 보겠습니다. 평소처럼 토크나이저와 모델을 다음과 같이 로드합니다:

```python
from transformers import AutoTokenizer, AutoModel

model_checkpoint = "distilbert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
model = AutoModel.from_pretrained(model_checkpoint)
```

이제 [Wikipedia 기사](https://en.wikipedia.org/wiki/Transformers)의 전체 섹션을 Transformers(라이브러리가 아니라 프랜차이즈!)에 임베딩 한다고 가정합니다:

```python
text = """
Generation One is a retroactive term for the Transformers characters that
appeared between 1984 and 1993. The Transformers began with the 1980s Japanese
toy lines Micro Change and Diaclone. They presented robots able to transform
into everyday vehicles, electronic items or weapons. Hasbro bought the Micro
Change and Diaclone toys, and partnered with Takara. Marvel Comics was hired by
Hasbro to create the backstory; editor-in-chief Jim Shooter wrote an overall
story, and gave the task of creating the characthers to writer Dennis O'Neil.
Unhappy with O'Neil's work (although O'Neil created the name "Optimus Prime"),
Shooter chose Bob Budiansky to create the characters.

The Transformers mecha were largely designed by Shōji Kawamori, the creator of
the Japanese mecha anime franchise Macross (which was adapted into the Robotech
franchise in North America). Kawamori came up with the idea of transforming
mechs while working on the Diaclone and Macross franchises in the early 1980s
(such as the VF-1 Valkyrie in Macross and Robotech), with his Diaclone mechs
later providing the basis for Transformers.

The primary concept of Generation One is that the heroic Optimus Prime, the
villainous Megatron, and their finest soldiers crash land on pre-historic Earth
in the Ark and the Nemesis before awakening in 1985, Cybertron hurtling through
the Neutral zone as an effect of the war. The Marvel comic was originally part
of the main Marvel Universe, with appearances from Spider-Man and Nick Fury,
plus some cameos, as well as a visit to the Savage Land.

The Transformers TV series began around the same time. Produced by Sunbow
Productions and Marvel Productions, later Hasbro Productions, from the start it
contradicted Budiansky's backstories. The TV series shows the Autobots looking
for new energy sources, and crash landing as the Decepticons attack. Marvel
interpreted the Autobots as destroying a rogue asteroid approaching Cybertron.
Shockwave is loyal to Megatron in the TV series, keeping Cybertron in a
stalemate during his absence, but in the comic book he attempts to take command
of the Decepticons. The TV series would also differ wildly from the origins
Budiansky had created for the Dinobots, the Decepticon turned Autobot Jetfire
(known as Skyfire on TV), the Constructicons (who combine to form
Devastator),[19][20] and Omega Supreme. The Marvel comic establishes early on
that Prime wields the Creation Matrix, which gives life to machines. In the
second season, the two-part episode The Key to Vector Sigma introduced the
ancient Vector Sigma computer, which served the same original purpose as the
Creation Matrix (giving life to Transformers), and its guardian Alpha Trion.
"""

inputs = tokenizer(text, return_tensors="pt")
logits = model(**inputs).logits
```

```python output
IndexError: index out of range in self
```

에고, 문제가 발생했습니다. 오류 메시지는 [2장](/course/chapter8/section2)에서 본 것보다 훨씬 더 어렵네요! 우리는 전체 traceback을 이해할 수 없으므로 HuggingFace 포럼에 도움을 요청하기로 결정합니다. 주제를 어떻게 만들 수 있을까요?

주제를 작성하려면 오른쪽 상단 모서리에 있는 "새 주제" 버튼을 클릭해야 합니다(주제를 만들려면 로그인해야 함):

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter8/forums-new-topic.png" alt="Creating a new forum topic." width="100%"/>
</div>

그러면 주제의 제목과 카테고리, 콘텐츠를 입력할 수 있는 화면이 나타나게 됩니다:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter8/forum-topic01.png" alt="The interface for creating a forum topic." width="100%"/>
</div>

에러는 🤗 Transformers에 대한 것이므로 Transformers 카테고리로 선택하겠습니다. 문제를 설명하는 우리의 첫 시도는 아마 아래와 같을 겁니다:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter8/forum-topic02.png" alt="Drafting the content for a new forum topic." width="100%"/>
</div>

이 주제는 도움에 필요한 오류 메시지가 포함되어 있지만 작성 방식에 몇 가지 문제가 있습니다:

1. 제목이 그다지 기술적이지 않으므로 포럼을 탐색하는 사람은 본문을 읽지 않고는 주제가 무엇인지 알 수 없습니다.
2. 본문이 오류가 _어디에서_ 발생하고 _어떻게_ 에러를 재현하는지에 대한 충분한 정보를 제공하지 않습니다.
3. 주제는 다소 요구하는 어조로 몇몇 사람들을 직접적으로 태그합니다.

이와 같은 주제는 빠른 답변을 얻지 못할 것이므로(적어도 하나라도 받을 경우) 이를 개선할 수 있는 방법을 살펴보겠습니다. 좋은 타이틀을 선택하기 위해 첫번째 이슈글 부터 보겠습니다.

### 기술적인 제목 선택하기

코드의 버그에 대한 도움을 받으려는 경우 다른 사람들이 질문에 답할 수 있는지 여부를 빠르게 결정할 수 있도록 제목에 충분한 정보를 포함하는 것이 좋습니다. 실습 예제에서 발생하는 예외의 이름을 알고 있으며 `model(**inputs)`을 호출하는 모델의 forward 과정에서 발생한다는 힌트를 얻었습니다. 이를 소통하기 위해 다음과 같은 제목이 가능하겠네요:

> Source of IndexError in the AutoModel forward pass?

이 제목은 읽는 사람에게 _어디에서_ 버그가 발생했다고 생각하는지 알려주고, 이전에 'IndexError'를 본 적이 있다면 디버그 방법을 알고 있을 가능성이 큽니다. 물론 제목은 원하는 대로 지정할 수 있으며 다음과 같은 변형도 가능합니다:

> Why does my model produce an IndexError?

와 같은 제목도 괜찮을 수 있습니다. 기술적인 제목을 만들었으니 본문 개선 방법을 살펴보겠습니다.

### 코드 스니펫 형식 지정하기

IDE에서 소스 코드를 읽는 것은 충분히 어렵지만 코드를 일반 텍스트로 복사하여 붙여넣으면 훨씬 더 어렵습니다! 다행히 HuggingFace 포럼은 Markdown 사용을 지원하므로 코드 블록을 항상 세 개의 백틱(```)으로 묶어서 더 쉽게 읽을 수 있도록 해야 합니다. 에러 메시지를 보기 좋게 만들기 위해 백틱안에 코드를 넣으면서 원본 보다 본문을 좀 더 정중하게 만들어 보겠습니다:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter8/forum-topic03.png" alt="Our revised forum topic, with proper code formatting." width="100%"/>
</div>

스크린샷에서 볼 수 있듯이 코드 블록을 백틱으로 묶으면 일반 텍스트가 코드로 변환되고 컬러 스타일링이 완성됩니다! 또한 `distilbert-base-uncased`에 했던 것처럼 하나의 백틱을 사용해 인라인 변수의 형식을 지정할 수 있습니다. 이 주제는 훨씬 나아지고 있으며 운이 좋으면 커뮤니티에서 에러가 무엇인지 추측할 수 있는 사람을 찾을 수 있습니다. 그러나 운에 맡기기 보단 traceback을 철저하고 세부적으로 포함해서 삶을 더 쉽게 만들도록 하죠!

### 전체 traceback 포함하기

traceback의 마지막 줄은 종종 코드를 디버그하기에 충분하기 때문에 주제에 "공간 절약"을 하고 싶을 수도 있습니다. 의도는 좋지만 traceback에서 상위에 있는 정보도 정말 유용할 수 있기 때문에 때론 다른 사람들이 문제를 디버그하는 것을 실제로 _더 어렵게_ 합니다. 따라서 좋은 방법은 _전체_ traceback을 복사하여 붙여넣으면서 형식이 제대로 지정되었는지 확인하는 것입니다. 이러한 traceback은 다소 길어질 수 있으므로 일부 사람들은 소스 코드를 설명한 후에 표시하는 것을 선호합니다. 이제 작성해봅시다. 포럼 주제는 다음과 같습니다:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter8/forum-topic04.png" alt="Our example forum topic, with the complete traceback." width="100%"/>
</div>

이게 훨씬 더 많은 정보를 제공하며, 섬세한 독자는 traceback에 있는 특정 행을 보고 긴 입력을 전달하기 때문에 문제가 있는 것으로 보인다는 점을 지적할 수 있습니다:

> Token indices sequence length is longer than the specified maximum sequence length for this model (583 > 512).

하지만 에러를 발생한 실제 코드를 제공해 문제를 훨씬 더 쉽게 만들 수 있습니다. 같이 방법을 알아보죠.

### 재현 가능한 예제코드 제공

다른 사람의 코드를 디버그하려고 시도한 적이 있다면 아마 그 사람이 보고한 문제를 먼저 재현해서 traceback을 통해 오류를 정확하게 찾아내는 작업을 할 수 있습니다. 포럼에서 도움을 받는(또는 제공하는) 경우에도 다르지 않으므로 오류를 재현하는 작은 예시를 제공할 수 있다면 정말 도움이 됩니다. 절반은 이 연습을 진행하는 것만으로도 무엇이 문제인지 파악하는 데 도움이 될 것입니다. 어쨌든 우리 예제에서 누락된 부분은 모델에 입력한 _inputs_를 표시하는 것입니다. 그러면 다음과 같은 완성된 예가 나타납니다:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter8/forum-topic05.png" alt="The final version of our forum topic." width="100%"/>
</div>

이 주제는 이제 상당히 많은 정보가 포함되어 있으며 커뮤니티의 관심을 끌고 유용한 답변을 얻을 가능성이 훨씬 높은 방식으로 작성되었습니다. 이 기본 가이드라인을 통해 이제 멋진 주제를 포스팅해서 🤗 Transformers 질문에 대한 답변을 찾을 수 있습니다!

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

### 2단원 완료!
https://huggingface.co/learn/course/ko/chapter8/6.md

# 2단원 완료!

<CourseFloatingBanner
    chapter={8}
    classNames="absolute z-10 right-0 top-0"
/>
축하합니다. 코스의 두 번째 부분을 통과했습니다! 세 번째 소식을 열심히 준비 중이니 [뉴스레터](https://huggingface.curated.co/)를 구독하여 소식을 놓치지 않도록 하세요.

이제 다양한 NLP 작업을 처리하고 이에 대한 모델을 파인 튜닝하거나 사전 학습할 수 있습니다. [모델 허브](https://huggingface.co/models)에서 커뮤니티와 결과를 공유하는 것을 잊지 마세요.

여러분이 얻은 지식으로 무엇을 구축할지 기대됩니다!

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

### 학습 파이프라인 디버깅
https://huggingface.co/learn/course/ko/chapter8/4.md

# 학습 파이프라인 디버깅

<CourseFloatingBanner chapter={8}
  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/chapter8/section4_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter8/section4_pt.ipynb"},
]} />

[단원 7](/course/chapter7)의 조언을 충실히 따라 주어진 작업에서 모델을 학습하거나 파인튜닝 하는 아름다운 스크립트를 작성했습니다. 하지만 `trainer.train()` 명령을 실행하면 끔찍한 일이 발생합니다. 에러가 발생합니다 😱! 또는 더 나쁜 것은 모든 것이 정상인 것처럼 보이고 학습이 에러 없이 실행되지만 결과 모델은 엉망입니다. 이 섹션에서는 이러한 종류의 문제를 디버그하기 위해 수행할 수 있는 것들을 보여줍니다.

## 학습 파이프라인 디버깅

<Youtube id="L-WSwUWde1U"/>

`trainer.train()`에서 에러가 발생했을 때의 문제는 `Trainer`가 일반적으로 많은 것을 결합하기 때문에 여러 소스에서 올 수 있다는 것입니다. 데이터 세트를 데이터 로더로 변환하므로 데이터 세트에 문제가 있거나 데이터 세트의 요소를 함께 일괄 처리하려고 할 때 문제가 발생할 수 있습니다. 그런 다음 데이터 배치를 가져와 모델에 공급하므로 문제가 모델 코드에 있을 수 있습니다. 그 다음 기울기를 계산하고 최적화 단계를 수행하므로 문제가 옵티마이저에도 있을 수 있습니다. 모든 것이 학습에 적합하더라도 평가 함수에 문제가 있으면 평가 중에 문제가 발생할 수 있습니다.

`trainer.train()`에서 발생하는 오류를 디버그하는 가장 좋은 방법은 이 전체 파이프라인을 직접 살펴보고 문제가 발생한 부분을 확인하는 것입니다. 그러면 에러를 해결하기가 매우 쉬운 경우가 많습니다.

이를 시연하기 위해 [MNLI 데이터 세트](https://huggingface.co/datasets/glue)에서 DistilBERT 모델을 파인튜닝(시도하는)하는 다음 스크립트를 사용합니다.:

```py
from datasets import load_dataset
import evaluate
from transformers import (
    AutoTokenizer,
    AutoModelForSequenceClassification,
    TrainingArguments,
    Trainer,
)

raw_datasets = load_dataset("glue", "mnli")

model_checkpoint = "distilbert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)


def preprocess_function(examples):
    return tokenizer(examples["premise"], examples["hypothesis"], truncation=True)


tokenized_datasets = raw_datasets.map(preprocess_function, batched=True)
model = AutoModelForSequenceClassification.from_pretrained(model_checkpoint)

args = TrainingArguments(
    f"distilbert-finetuned-mnli",
    evaluation_strategy="epoch",
    save_strategy="epoch",
    learning_rate=2e-5,
    num_train_epochs=3,
    weight_decay=0.01,
)

metric = evaluate.load("glue", "mnli")


def compute_metrics(eval_pred):
    predictions, labels = eval_pred
    return metric.compute(predictions=predictions, references=labels)


trainer = Trainer(
    model,
    args,
    train_dataset=raw_datasets["train"],
    eval_dataset=raw_datasets["validation_matched"],
    compute_metrics=compute_metrics,
)
trainer.train()
```

이 코드를 실행하려고 하면 다소 신비한 에러가 발생합니다.:

```python out
'ValueError: You have to specify either input_ids or inputs_embeds'
```

### 데이터 확인

말할 필요도 없이, 데이터가 손상되면 `Trainer`는 모델을 학습시키는 것은 물론 배치를 형성할 수 없습니다. 따라서 먼저 학습 세트 내부에 무엇이 있는지 살펴보아야 합니다.

버그의 원인이 아닌 것을 수정하는 데 수많은 시간을 소비하지 않으려면 체크할 때 `trainer.train_dataset`을 사용하고 다른 것은 사용하지 않는 것이 좋습니다. 아래와 같이 해보세요.:

```py
trainer.train_dataset[0]
```

```python out
{'hypothesis': 'Product and geography are what make cream skimming work. ',
 'idx': 0,
 'label': 1,
 'premise': 'Conceptually cream skimming has two basic dimensions - product and geography.'}
```
뭔가 잘못된 것을 눈치채셨나요? `input_ids`가 누락되었다는 에러 메시지와 함께 모델이 이해할 수 있는 숫자가 아니라 텍스트라는 것을 깨달아야 합니다. 여기서 원래 에러는 `Trainer`가 모델의 입력 파라미터(즉, 모델에서 기대하는 인수)와 일치하지 않는 열을 자동으로 제거하기 때문에 매우 오해의 소지가 있습니다. 즉, 이 예시에서는 레이블을 제외한 모든 것이 제거되었습니다. 따라서 배치를 생성한 다음 모델로 보내는 데 문제가 없었습니다. 그래서 모델은 적절한 입력을 받지 못했다고 불평한 것입니다.

데이터가 처리되지 않은 이유는 무엇일까요? 각 샘플에 토크나이저를 적용하기 위해 데이터셋에 `Dataset.map()` 메서드를 사용했습니다. 그러나 코드를 자세히 보면 훈련 및 평가 세트를 `Trainer`에 전달할 때 실수 한 것을 알 수 있습니다. 여기서는 `tokenized_datasets` 대신 'raw_datasets' 🤦를 사용했습니다. 수정 해봅시다!

```py
from datasets import load_dataset
import evaluate
from transformers import (
    AutoTokenizer,
    AutoModelForSequenceClassification,
    TrainingArguments,
    Trainer,
)

raw_datasets = load_dataset("glue", "mnli")

model_checkpoint = "distilbert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)


def preprocess_function(examples):
    return tokenizer(examples["premise"], examples["hypothesis"], truncation=True)


tokenized_datasets = raw_datasets.map(preprocess_function, batched=True)
model = AutoModelForSequenceClassification.from_pretrained(model_checkpoint)

args = TrainingArguments(
    f"distilbert-finetuned-mnli",
    evaluation_strategy="epoch",
    save_strategy="epoch",
    learning_rate=2e-5,
    num_train_epochs=3,
    weight_decay=0.01,
)

metric = evaluate.load("glue", "mnli")


def compute_metrics(eval_pred):
    predictions, labels = eval_pred
    return metric.compute(predictions=predictions, references=labels)


trainer = Trainer(
    model,
    args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation_matched"],
    compute_metrics=compute_metrics,
)
trainer.train()
```
새로운 코드는 이제 다른 에러(어쨌든 진전!)를 제공합니다.:

```python out
'ValueError: expected sequence of length 43 at dim 1 (got 37)'
```
traceback을 보면 데이터 정렬 단계에서 에러가 발생하는 것을 볼 수 있습니다.:

```python out
~/git/transformers/src/transformers/data/data_collator.py in torch_default_data_collator(features)
    105                 batch[k] = torch.stack([f[k] for f in features])
    106             else:
--> 107                 batch[k] = torch.tensor([f[k] for f in features])
    108 
    109     return batch
```

이제 문제가 있는 곳으로 이동해야합니다. 하지만 그 전에 데이터 검사를 마쳐서 데이터가 100% 정확한지 확인합시다.

학습 세션을 디버깅할 때 항상 수행해야 하는 한 가지는 모델의 입력값을 디코딩해서 살펴보는 것입니다. 모델에게 입력하는 숫자를 이해할 수 없으므로 해당 숫자가 무엇을 의미하는지 살펴봐야 합니다. 예를 들어, 컴퓨터 비전에서 이는 전달한 픽셀의 디코딩된 그림을 보는 것을 의미하고, 음성에서는 디코딩된 오디오 샘플을 듣는 것을 의미하며, 여기 NLP 예제의 경우 입력을 디코딩하기 위해 토크나이저를 사용하는 것을 의미합니다.:

```py
tokenizer.decode(trainer.train_dataset[0]["input_ids"])
```

```python out
'[CLS] conceptually cream skimming has two basic dimensions - product and geography. [SEP] product and geography are what make cream skimming work. [SEP]'
```

확인해보니 맞는 것 같습니다. 입력값의 모든 키에 대해 다음을 수행해야 합니다.:

```py
trainer.train_dataset[0].keys()
```

```python out
dict_keys(['attention_mask', 'hypothesis', 'idx', 'input_ids', 'label', 'premise'])
```

모델에서 허용하지 않는 키값은 자동으로 폐기되므로 여기서는 `input_ids`, `attention_mask` 및 `label`(이름이 `labels`로 변경됨)만 유지합니다. 모델 정보를 확실하게 확인하려면 모델의 클래스를 출력해본 다음 문서를 확인하세요.:

```py
type(trainer.model)
```

```python out
transformers.models.distilbert.modeling_distilbert.DistilBertForSequenceClassification
```

위 코드의 경우 [이 페이지](https://huggingface.co/transformers/model_doc/distilbert.html#distilbertforsequenceclassification)에서 허용되는 파라미터를 확인할 수 있습니다. `Trainer`는 버리는 열도 기록합니다.

input IDs를 디코딩하여 올바른지 확인했습니다. 다음은 `attention_mask` 차례 입니다.:

```py
trainer.train_dataset[0]["attention_mask"]
```

```python out
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
```
전처리에서 패딩을 적용하지 않았기 때문에 위 값은 완벽하게 자연스러워 보입니다. attention mask에 문제가 없는지 확인하기 위해 input IDs 와 길이가 같은지 확인합니다.:

```py
len(trainer.train_dataset[0]["attention_mask"]) == len(
    trainer.train_dataset[0]["input_ids"]
)
```

```python out
True
```

좋습니다! 마지막으로 레이블을 확인해 보겠습니다.:

```py
trainer.train_dataset[0]["label"]
```

```python out
1
```

input IDs 와 마찬가지로 이 숫자만으로는 의미가 없습니다. 이전에 보았듯이 정수와 레이블 이름 사이의 맵은 데이터세트의 해당 *feature*의 `names` 속성 내부에 저장됩니다.:

```py
trainer.train_dataset.features["label"].names
```

```python out
['entailment', 'neutral', 'contradiction']
```

따라서 `1`은 `중립`을 의미합니다. 즉, 위에서 본 두 문장이 모순되지 않고 첫 번째 문장이 두 번째 문장을 의미하지 않습니다. 맞는 것 같습니다!

여기에는 token type IDs 가 없습니다. DistilBERT는 사용하지 않기 때문입니다. token type IDs를 사용하는 모델의 경우 입력에서 첫 번째 및 두 번째 문장이 있는 위치와 올바르게 일치하는지 확인해야 합니다.

<Tip>

✏️ **여러분 차례입니다!** 학습 데이터 세트의 두 번째 원소가 정상적인지 확인해보세요.

</Tip>

여기에선 학습 세트에 대해서만 확인하지만,  동일한 방식으로 검증 및 평가 세트를 다시 확인해야 합니다.

데이터 세트가 문제 없으므로 이제 학습 파이프라인의 다음 단계를 확인할 차례입니다.

### 데이터세트에서 데이터로더까지

학습 파이프라인에서 다음으로 잘못될 수 있는 사항은 `Trainer`가 학습 또는 검증 세트에서 배치를 형성하려고 할 때입니다. `Trainer`의 데이터 세트가 정확하다고 확신하면 다음을 실행하여 직접 배치를 형성할 수 있습니다(검증 데이터 로더의 경우 `train`을 `eval`로 대체).:

```py
for batch in trainer.get_train_dataloader():
    break
```

이 코드는 학습 데이터 로더를 생성한 다음 반복하며 첫 번째 반복에서 중지합니다. 코드가 에러 없이 실행되면 검사할 수 있는 첫 번째 학습 배치를 얻게 되며, 에러가 발생하면 문제가 데이터 로더에 있음을 확실히 알 수 있습니다.:

```python out
~/git/transformers/src/transformers/data/data_collator.py in torch_default_data_collator(features)
    105                 batch[k] = torch.stack([f[k] for f in features])
    106             else:
--> 107                 batch[k] = torch.tensor([f[k] for f in features])
    108 
    109     return batch

ValueError: expected sequence of length 45 at dim 1 (got 76)
```

Traceback의 마지막 프레임을 조사하면 단서를 제공하기 충분할테지만 조금 더 파헤쳐 보겠습니다. 배치 생성 중 대부분의 문제는 예제를 단일 배치로 조합하기 때문에 발생하므로 의심스러운 경우 가장 먼저 확인해야 할 것은 `DataLoader`가 사용하는 `collate_fn`입니다.:

```py
data_collator = trainer.get_train_dataloader().collate_fn
data_collator
```

```python out
<function transformers.data.data_collator.default_data_collator(features: List[InputDataClass], return_tensors='pt') -> Dict[str, Any]>
```

위 코드는 `default_data_collator`이지만, 이 경우에는 우리가 원하는 것이 아닙니다. 'DataCollatorWithPadding' collator에 의해 수행되는 배치에서 가장 긴 문장으로 패딩을 하고 싶습니다. 그리고 이 데이터 콜레이터는 기본적으로 `Trainer`에 의해 사용된다고 하는데 여기에서는 사용하지 않는 이유는 무엇일까요?

그 이유는 우리가 `Trainer`에 `tokenizer`를 전달하지 않았기 때문에 우리가 원하는 `DataCollatorWithPadding`을 생성할 수 없었기 때문입니다. 실제로 이러한 종류의 에러를 방지하기 위해 사용하려는 data collator를 명시적으로 전달하는 것을 주저해선 안 됩니다. 코드를 정확하게 수정해봅시다.:

```py
from datasets import load_dataset
import evaluate
from transformers import (
    AutoTokenizer,
    AutoModelForSequenceClassification,
    DataCollatorWithPadding,
    TrainingArguments,
    Trainer,
)

raw_datasets = load_dataset("glue", "mnli")

model_checkpoint = "distilbert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)


def preprocess_function(examples):
    return tokenizer(examples["premise"], examples["hypothesis"], truncation=True)


tokenized_datasets = raw_datasets.map(preprocess_function, batched=True)
model = AutoModelForSequenceClassification.from_pretrained(model_checkpoint)

args = TrainingArguments(
    f"distilbert-finetuned-mnli",
    evaluation_strategy="epoch",
    save_strategy="epoch",
    learning_rate=2e-5,
    num_train_epochs=3,
    weight_decay=0.01,
)

metric = evaluate.load("glue", "mnli")


def compute_metrics(eval_pred):
    predictions, labels = eval_pred
    return metric.compute(predictions=predictions, references=labels)


data_collator = DataCollatorWithPadding(tokenizer=tokenizer)

trainer = Trainer(
    model,
    args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation_matched"],
    compute_metrics=compute_metrics,
    data_collator=data_collator,
    tokenizer=tokenizer,
)
trainer.train()
```
좋은 뉴스일까요? 이전과 같은 오류가 발생하지 않습니다. 확실히 진행 중이란 뜻이지요. 나쁜 소식은? 대신 악명 높은 CUDA 오류가 발생합니다.:

```python out
RuntimeError: CUDA error: CUBLAS_STATUS_ALLOC_FAILED when calling `cublasCreate(handle)`
```
CUDA 오류는 일반적으로 디버그하기가 매우 어렵기 때문에 좋지 않은 상황입니다. 이 문제를 해결하는 방법을 잠시 후에 살펴보겠지만 먼저 배치 생성에 대한 분석을 마치겠습니다.

data collator가 정상이라고 확신하는 경우 데이터 세트의 몇 가지 샘플에 적용해야 합니다.:

```py
data_collator = trainer.get_train_dataloader().collate_fn
batch = data_collator([trainer.train_dataset[i] for i in range(4)])
```
`train_dataset`에 `Trainer`가 일반적으로 제거하는 문자열 열이 포함되어 있기 때문에 이 코드는 실패합니다. 이 부분을 수동으로 제거하거나 `Trainer`가 무대 뒤에서 수행하는 작업을 정확히 복제하려면 해당 작업을 수행하는 비공개 `Trainer._remove_unused_columns()` 메서드를 호출하면 됩니다.:

```py
data_collator = trainer.get_train_dataloader().collate_fn
actual_train_set = trainer._remove_unused_columns(trainer.train_dataset)
batch = data_collator([actual_train_set[i] for i in range(4)])
```
그런 다음 오류가 지속되는 경우 data collator ​​내부에서 발생하는 일을 직접 디버그할 수 있어야 합니다.

배치 생성 프로세스를 디버깅했으므로 이제 모델을 통해 전달할 차례입니다!

### 모델 살펴보기

아래 명렁어를 실행함으로써 배치를 얻을 수 있어야 합니다.:

```py
for batch in trainer.get_train_dataloader():
    break
```

노트북에서 이 코드를 실행하는 경우 이전에 본 것과 유사한 CUDA 오류가 발생할 수 있습니다. 이 경우 노트북을 다시 시작하고 `trainer.train()` 행 없이 마지막 스니펫을 다시 실행해야 합니다. CUDA 오류에 대해 두 번째로 짜증나는 점은 커널을 복구할 수 없을 정도로 망가뜨리는 것입니다. 가장 짜증나는 점은 디버깅하기 어렵다는 사실입니다.

왜 그럴까요? 이건 GPU 작동 방식과 관련이 있습니다. 많은 작업을 병렬로 실행하는 데 매우 효율적이지만, 이러한 명령 중 하나의 에러가 발생했을 때 이를 즉시 알 수 없다는 단점이 있습니다. 프로그램이 GPU에서 여러 프로세스의 동기화를 호출할 때만 문제가 발생했음을 깨닫게 되므로 실제로 에러를 만든 곳과 관련이 없는 위치에서 오류가 발생합니다. 예를 들어, 이전 Traceback을 보면 역방향 패스 중에 오류가 발생했지만 실제로는 순방향 중 어딘가에서 비롯된 에러임을 곧 알 수 있습니다.

그렇다면 이러한 에러를 어떻게 디버깅할까요? 답은 간단합니다. 하지 않습니다. CUDA 오류가 메모리 부족 에러(GPU에 메모리가 충분하지 않음을 의미)가 아닌 한 항상 CPU로 돌아가서 디버깅해야 합니다.

이러한 경우 모델을 CPU에 다시 놓고 배치에서 호출하면 됩니다. `DataLoader`가 반환한 배치는 아직 GPU로 이동하지 않았습니다.:

```python
outputs = trainer.model.cpu()(**batch)
```

```python out
~/.pyenv/versions/3.7.9/envs/base/lib/python3.7/site-packages/torch/nn/functional.py in nll_loss(input, target, weight, size_average, ignore_index, reduce, reduction)
   2386         )
   2387     if dim == 2:
-> 2388         ret = torch._C._nn.nll_loss(input, target, weight, _Reduction.get_enum(reduction), ignore_index)
   2389     elif dim == 4:
   2390         ret = torch._C._nn.nll_loss2d(input, target, weight, _Reduction.get_enum(reduction), ignore_index)

IndexError: Target 2 is out of bounds.
```
이제 그림이 선명해지네요. CUDA 오류가 발생하는 대신 이제 로스 계산에 'IndexError'가 있습니다(앞서 말했듯이 역방향 패스와 관련이 없습니다). 더 정확하게는 오류를 생성하는 대상 2 임을 알 수 있으므로 모델의 레이블 수를 확인하기에 아주 좋은 순간입니다.:

```python
trainer.model.config.num_labels
```

```python out
2
```
두 개의 레이블을 사용하면 0과 1만 정답으로 허용되지만 에러 메시지에 따르면 2가 있었습니다. 2를 얻는 것은 실제로 일반적입니다. 이전에 추출한 레이블 이름을 기억해보면 3개가 있었으므로 인덱스는 0, 1, 2가 데이터세트에 있습니다. 문제는 세 개의 레이블로 생성되어야 한다는 점을 모델에 알려주지 않았다는 것입니다. 이제 수정해 봅시다!

```py
from datasets import load_dataset
import evaluate
from transformers import (
    AutoTokenizer,
    AutoModelForSequenceClassification,
    DataCollatorWithPadding,
    TrainingArguments,
    Trainer,
)

raw_datasets = load_dataset("glue", "mnli")

model_checkpoint = "distilbert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)


def preprocess_function(examples):
    return tokenizer(examples["premise"], examples["hypothesis"], truncation=True)


tokenized_datasets = raw_datasets.map(preprocess_function, batched=True)
model = AutoModelForSequenceClassification.from_pretrained(model_checkpoint, num_labels=3)

args = TrainingArguments(
    f"distilbert-finetuned-mnli",
    evaluation_strategy="epoch",
    save_strategy="epoch",
    learning_rate=2e-5,
    num_train_epochs=3,
    weight_decay=0.01,
)

metric = evaluate.load("glue", "mnli")


def compute_metrics(eval_pred):
    predictions, labels = eval_pred
    return metric.compute(predictions=predictions, references=labels)


data_collator = DataCollatorWithPadding(tokenizer=tokenizer)

trainer = Trainer(
    model,
    args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation_matched"],
    compute_metrics=compute_metrics,
    data_collator=data_collator,
    tokenizer=tokenizer,
)
```
모든 것이 괜찮은지 확인하기 위해 아직 `trainer.train()` 라인을 포함하지 않았습니다. 배치를 요청하고 모델에 전달하면 이제 에러 없이 작동합니다!

```py
for batch in trainer.get_train_dataloader():
    break

outputs = trainer.model.cpu()(**batch)
```
다음 단계는 GPU로 돌아가 모든 것이 여전히 작동하는지 확인하는 것입니다.:

```py
import torch

device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
batch = {k: v.to(device) for k, v in batch.items()}

outputs = trainer.model.to(device)(**batch)
```
여전히 에러가 발생하면 노트북을 다시 시작하고 스크립트의 마지막 버전만 실행해야 합니다.

### 한번의 최적화 단계 수행

이제 실제로 모델을 통과하는 배치를 빌드할 수 있다는 것을 알았으므로 학습 파이프라인의 다음 단계인 그래디언트 계산 및 최적화 단계를 수행할 준비가 되었습니다.:

첫 번째 부분은 로스에 대해 `backward()` 메서드를 호출하는 것입니다.:

```py
loss = outputs.loss
loss.backward()
```
이 단계에서 에러가 발생하는 것은 매우 드물지만 에러가 발생하면 CPU로 돌아가 유용한 에러 메시지를 받으세요.

최적화 단계를 수행하려면 `optimizer`를 만들고 `step()` 메서드를 호출하기만 하면 됩니다.:

```py
trainer.create_optimizer()
trainer.optimizer.step()
```
다시 말하지만, `Trainer`에서 기본 옵티마이저를 사용하는 경우 이 단계에서 오류가 발생하지 않아야 하지만 사용자 지정 옵티마이저가 있는 경우 여기에서 디버깅에 몇 가지 문제가 있을 수 있습니다. 이 단계에서 이상한 CUDA 오류가 발생하면 CPU로 돌아가는 것을 잊지 마세요. CUDA 오류에 대해 말하자면, 앞서 우리는 특별한 경우를 언급했습니다. 그 부분을 지금부터 살펴보겠습니다.

### CUDA 메모리 부족 오류 다루기

`RuntimeError: CUDA out of memory`로 시작하는 에러 메시지는 GPU 메모리가 부족하다는 의미입니다. 이 부분은 코드에 직접 연결되지 않으며 완벽하게 실행되는 스크립트에서 발생할 수 있습니다. 이 에러는 GPU의 내부 메모리에 너무 많은 것을 넣으려고 해서 에러가 발생했음을 의미합니다. 다른 CUDA 오류와 마찬가지로 학습을 다시 실행할 수 있는 위치에서 커널을 다시 시작해야 합니다.

이 문제를 해결하려면 GPU 공간을 적게 사용하면 됩니다. 먼저 GPU에 동시에 두 개의 모델이 있지 않은지 확인합니다(물론 문제 해결에 필요한 경우 제외). 그런 다음 배치 크기를 줄여야 합니다. 이는 모델의 모든 중간 결과값 크기와 기울기에 직접적인 영향을 미치기 때문입니다. 문제가 지속되면 더 작은 모델 버전을 사용하는 것이 좋습니다.

<Tip>

코스의 다음 부분에서는 메모리 사용량을 줄이고 가장 큰 모델을 파인 튜닝할 수 있는 고급 기술을 살펴보겠습니다.

</Tip>

### 모델 평가하기

이제 코드의 모든 문제를 해결했으므로 모든 것이 완벽하고 학습이 원활하게 실행되어야 합니다. 그렇죠? 그렇게 빠르진 않습니다! `trainer.train()` 명령을 실행하면 처음에는 모든 것이 좋아 보이지만 잠시 후 다음의 출력을 보게 됩니다.:
Now that we've solved all the issues with our code, everything is perfect and the training should run smoothly, right? Not so fast! If you run the `trainer.train()` command, everything will look good at first, but after a while you will get the following:

```py
# This will take a long time and error out, so you shouldn't run this cell
trainer.train()
```

```python out
TypeError: only size-1 arrays can be converted to Python scalars
```
평가 단계에서 이 오류가 나타남을 알게 되었을 건데요, 이건 곧 마지막으로 디버깅해야 할 사항임을 뜻합니다.

다음과 같이 훈련에서 독립적으로 `Trainer`의 평가 루프를 실행할 수 있습니다.:

```py
trainer.evaluate()
```

```python out
TypeError: only size-1 arrays can be converted to Python scalars
```

<Tip>

💡 에러가 발생하기 전에 많은 컴퓨팅 리소스를 낭비하지 않도록 항상 `trainer.train()`을 실행하기 전에 `trainer.evaluate()`를 실행할 수 있는지 확인해야 합니다.

</Tip>

평가 루프에서 문제를 디버깅하기 전에 먼저 데이터를 살펴보았는지, 배치를 적절하게 구성할 수 있는지, 모델을 실행할 수 있는지 확인해야 합니다. 모든 단계를 완료했으므로 다음의 코드를 에러 없이 실행할 수 있습니다.:

```py
for batch in trainer.get_eval_dataloader():
    break

batch = {k: v.to(device) for k, v in batch.items()}

with torch.no_grad():
    outputs = trainer.model(**batch)
```

에러는 나중에 평가 단계가 끝날 때 발생하며 Traceback을 보면 다음과 같이 표시됩니다.:

```python trace
~/git/datasets/src/datasets/metric.py in add_batch(self, predictions, references)
    431         """
    432         batch = {"predictions": predictions, "references": references}
--> 433         batch = self.info.features.encode_batch(batch)
    434         if self.writer is None:
    435             self._init_writer()
```

이건 에러가 `datasets/metric.py` 모듈에서 발생했음을 알려줍니다. 따라서 이것은 `compute_metrics()` 함수의 문제입니다. 로짓과 레이블의 튜플을 NumPy 배열로 사용하므로 다음과 같이 입력해 보겠습니다.:

```py
predictions = outputs.logits.cpu().numpy()
labels = batch["labels"].cpu().numpy()

compute_metrics((predictions, labels))
```

```python out
TypeError: only size-1 arrays can be converted to Python scalars
```
동일한 에러가 발생하므로 문제는 분명히 해당 기능에 있습니다. 코드를 다시 보면 `predictions`와 `labels`를 `metric.compute()`로 전달하고 있음을 알 수 있습니다. 그럼 그 방법에 문제가 있는 걸까요? 설마... 형태을 간단히 살펴보겠습니다.:

```py
predictions.shape, labels.shape
```

```python out
((8, 3), (8,))
```
우리의 예측은 실제 예측값이 아니라 여전히 로짓입니다. 이것이 메트릭이 이 (다소 모호한) 에러를 반환하는 이유입니다. 수정은 매우 쉽습니다. `compute_metrics()` 함수에 argmax를 추가하기만 하면 됩니다.:

```py
import numpy as np


def compute_metrics(eval_pred):
    predictions, labels = eval_pred
    predictions = np.argmax(predictions, axis=1)
    return metric.compute(predictions=predictions, references=labels)


compute_metrics((predictions, labels))
```

```python out
{'accuracy': 0.625}
```
이제 에러가 수정되었습니다! 이게 마지막이었으므로 이제 스크립트가 모델을 제대로 학습시킬 것입니다.

참고로 아래 스크립트는 완전히 수정된 스크립트입니다.:

```py
import numpy as np
from datasets import load_dataset
import evaluate
from transformers import (
    AutoTokenizer,
    AutoModelForSequenceClassification,
    DataCollatorWithPadding,
    TrainingArguments,
    Trainer,
)

raw_datasets = load_dataset("glue", "mnli")

model_checkpoint = "distilbert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)


def preprocess_function(examples):
    return tokenizer(examples["premise"], examples["hypothesis"], truncation=True)


tokenized_datasets = raw_datasets.map(preprocess_function, batched=True)
model = AutoModelForSequenceClassification.from_pretrained(model_checkpoint, num_labels=3)

args = TrainingArguments(
    f"distilbert-finetuned-mnli",
    evaluation_strategy="epoch",
    save_strategy="epoch",
    learning_rate=2e-5,
    num_train_epochs=3,
    weight_decay=0.01,
)

metric = evaluate.load("glue", "mnli")


def compute_metrics(eval_pred):
    predictions, labels = eval_pred
    predictions = np.argmax(predictions, axis=1)
    return metric.compute(predictions=predictions, references=labels)


data_collator = DataCollatorWithPadding(tokenizer=tokenizer)

trainer = Trainer(
    model,
    args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation_matched"],
    compute_metrics=compute_metrics,
    data_collator=data_collator,
    tokenizer=tokenizer,
)
trainer.train()
```
이 경우 더 이상 문제가 없으며 스크립트는 모델을 파인튜닝 할 것이고 합리적인 결과를 제공해줄 것입니다. 그러나 학습이 에러 없이 진행되었고 학습된 모델이 전혀 잘 작동하지 않을 때 우리는 무엇을 할 수 있을까요? 이것이 기계 학습의 가장 어려운 부분이며 도움이 될 수 있는 몇 가지 기술을 보여 드리겠습니다.

<Tip>

💡 수동 학습 루프를 사용하는 경우 학습 파이프라인을 디버그하기 위해 동일한 단계가 적용되지만 더 쉽게 분리할 수 있습니다. 하지만 올바른 위치의 `model.eval()` 또는 `model.train()` 또는 각 단계의 `zero_grad()`를 잊지 않았는지 확인하세요!

</Tip>

## 학습 중 조용한 에러 디버깅

에러 없이 완료되지만 좋은 결과를 얻지 못하는 학습을 디버그하려면 어떻게 해야 할까요? 여기에서 몇 가지 지침을 제공하겠지만 이러한 종류의 디버깅은 기계 학습에서 가장 어려운 부분이며 마법 같은 답은 없다는 점을 기억하세요.

### 데이터 확인(다시!)

모델은 데이터에서 실제로 뭔가 학습할 수 있는 경우에만 학습합니다. 데이터를 손상시키는 버그가 있거나 레이블이 무작위로 지정된 경우 데이터 세트에 대한 학습을 제대로 진행하지 못할 가능성이 매우 높습니다. 따라서 항상 디코딩된 입력과 레이블을 다시 확인하는 것으로 시작하고 다음의 질문을 스스로에게 물어보세요.:

- 디코딩된 데이터가 이해할만한지?
- 레이블에 납득할 수 있는지?
- 다른 레이블보다 정답에 가까운 레이블이 있는지?
- 모델이 무작위 답, 언제나 같은 답을 예측할 경우 어떤 손실 함수와 매트릭을 정해야할지?

<Tip warning={true}>

분산 학습을 수행하는 경우 각 프로세스에서 데이터 세트의 샘플을 출력하고 동일한 결과를 얻었는지 세 번 확인하세요. 한 가지 일반적인 버그는 데이터 생성 시 각 프로세스가 서로 다른 버전의 데이터 세트를 갖도록 만드는 임의성의 원인이 있다는 것입니다.

</Tip>

데이터를 살펴본 후 모델의 몇 가지 예측을 살펴보고 디코딩합니다. 모델이 항상 동일한 것을 예측하는 경우 데이터 세트가 하나의 범주(분류 문제의 경우)로 편향되어 있기 때문일 수 있습니다. 희귀 클래스를 오버샘플링하는 것과 같은 기술이 도움이 될 수 있습니다.

초기 모델에서 얻은 손실값/메트릭값이 무작위 예측에 대해 예상한 손실값/메트릭값과 매우 다른 경우 버그가 있을 수 있으므로 손실값 또는 메트릭값이 계산되는 방식을 다시 확인하세요. 마지막에 추가적인 여러 손실함수를 사용하는 경우 동일한 크기인지 확인하세요.

데이터가 완벽하다고 확신하는 경우, 모델이 학습을 진행할 수 있는지 한번 간단하게 테스트 해보세요. 

### 한번의 배치에 모델 과적합 해보기

과적합은 일반적으로 훈련할 때 피하려고 합니다. 과적합은 모델이 우리가 원하는 일반적인 기능을 인식하는 방법을 배우는 것이 아니라 훈련 샘플을 암기하는 것임을 의미하기 때문입니다. 그러나 하나의 배치에서 반복해서 모델을 훈련시키려는 시도는 훈련하려는 모델에서 설정한 문제를 해결할 수 있는지 확인하는 좋은 테스트입니다. 또한 초기 학습률이 너무 높은지 확인하는 데 도움이 됩니다.

`Trainer`를 정의한 후에는 이 작업을 수행하는 것이 정말 쉽습니다. 학습 데이터 배치를 사용하여 20단계 정도로 작은 수동 학습 루프를 실행해보세요.:

Doing this once you have defined your `Trainer` is really easy; just grab a batch of training data, then run a small manual training loop only using that batch for something like 20 steps:

```py
for batch in trainer.get_train_dataloader():
    break

batch = {k: v.to(device) for k, v in batch.items()}
trainer.create_optimizer()

for _ in range(20):
    outputs = trainer.model(**batch)
    loss = outputs.loss
    loss.backward()
    trainer.optimizer.step()
    trainer.optimizer.zero_grad()
```

<Tip>

💡 학습 데이터가 불균형한 경우 모든 레이블을 포함하는 학습 데이터 배치를 빌드해야 합니다.

</Tip>

결과 모델은 동일한 `batch`에서 완벽에 가까운 결과를 가져야 합니다. 결과 예측에 대한 메트릭을 계산해 보겠습니다.:

```py
with torch.no_grad():
    outputs = trainer.model(**batch)
preds = outputs.logits
labels = batch["labels"]

compute_metrics((preds.cpu().numpy(), labels.cpu().numpy()))
```

```python out
{'accuracy': 1.0}
```

100% 정확도, 과대적합의 좋은 예입니다(즉, 다른 문장에서 모델 예측을 시도하면 잘못된 답을 줄 가능성이 매우 높습니다)!

모델이 이와 같이 완벽한 결과를 얻지 못한다면 문제 또는 데이터를 구성하는 방식에 문제가 있음을 의미하므로 이를 수정해야 합니다. 과적합 테스트를 통과해야만 모델이 실제로 무언가를 배울 수 있다는 것을 확신할 수 있습니다.

<Tip warning={true}>

⚠️ 이 테스트 후에는 모델과 `Trainer`를 다시 만들어야 합니다. 학습한 모델은 전체 데이터 세트에서 유용한 것을 재구성하거나 학습할 수 없기 때문입니다.

</Tip>

### 첫 기준 모델을 만들기전엔 튜닝하지 마세요.

하이퍼파라미터 튜닝은 머신 러닝에서 가장 어려운 부분으로 항상 강조되지만 메트릭에 대해 약간의 정보를 얻는 데 도움이 되는 마지막 단계일 뿐입니다. 대부분의 경우 `Trainer`의 기본 하이퍼파라미터가 잘 작동하여 좋은 결과를 얻을 수 있으므로 데이터 세트에 해당하는 기준 모델을 능가하는 항목을 찾을 때까지 시간과 비용이 많이 드는 하이퍼파라미터 검색을 하지마세요.

적합한 모델이 있으면 약간의 튜닝을 시작할 수 있습니다. 서로 다른 하이퍼파라미터로 수천 번의 실행을 시도하지 말고 하나의 하이퍼파라미터에 대해 서로 다른 값을 가진 두 번의 실행을 비교하여 가장 큰 영향을 미치는 아이디어를 얻어보세요.

모델 자체를 튜닝하는 경우 간단하게 유지하고 합리적으로 정당화할 수 없는 것은 시도하지 마세요. 항상 과적합 테스트로 돌아가 변경 사항이 의도하지 않은 결과를 가져오지 않았는지 확인하세요.

### 도움 요청하기

이 장에서 문제를 해결하는 데 도움이 되는 몇 가지 조언을 찾으셨기를 바랍니다. 그렇지 않은 경우 언제든지 [포럼](https://discuss.huggingface.co/)에서 커뮤니티에 질문할 수 있습니다.

다음은 도움이 될 수 있는 몇 가지 추가 자료입니다.:

- ["Reproducibility as a vehicle for engineering best practices"](https://docs.google.com/presentation/d/1yHLPvPhUs2KGI5ZWo0sU-PKU3GimAk3iTsI38Z-B5Gw/edit#slide=id.p) by Joel Grus
- ["Checklist for debugging neural networks"](https://towardsdatascience.com/checklist-for-debugging-neural-networks-d8b2a9434f21) by Cecelia Shao
- ["How to unit test machine learning code"](https://medium.com/@keeper6928/how-to-unit-test-machine-learning-code-57cf6fd81765) by Chase Roberts
- ["A Recipe for Training Neural Networks"](http://karpathy.github.io/2019/04/25/recipe/) by Andrej Karpathy

물론 신경망을 훈련할 때 발생하는 모든 문제가 자신의 잘못은 아닙니다! 🤗 Transformers 또는 🤗 Datasets 라이브러리에서 이상한 것을 본다면 버그가 발생했을 수 있습니다. 버그를 봤을 경우 우리에게 상세하게 말해야 하며, 다음 섹션에서 그 방법을 정확히 설명할 것입니다.


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

### 단원 마무리 퀴즈
https://huggingface.co/learn/course/ko/chapter8/7.md

# 단원 마무리 퀴즈

<CourseFloatingBanner
    chapter={8}
    classNames="absolute z-10 right-0 top-0"
/>

이 챕터에서 배운 내용을 테스트해 봅시다!

### 1. 파이썬 traceback을 어떤 순서로 읽어야 할까요?

<Question
	choices={[
		{
			text: "위에서 아래로",
			explain: "다시 풀어보세요. -- 대부분의 다른 프로그래밍 언어는 맨 위에 예외를 인쇄하지만 Python은 이 점에서 특별합니다."
		},
		{
			text: "아래에서 위로",
			explain: "정답! 맨 아래에 예외를 표시하는 Python traceback의 장점 중 하나는 터미널에서 작업할 때 디버그하기가 더 쉽고 마지막 줄에 표시된다는 점입니다.",
			correct: true
		}
	]}
/>

### 2. 최소한의 재현 가능한 예는 무엇일까요?

<Question
	choices={[
		{
			text: "리서치 기사를 통해 Transformer 아키텍처를 간단하게 구현하는 것 ",
			explain: "자신만의 Transformer 모델을 처음부터 구현하는 것이 매우 교육적이지만 여기에서 이야기하는 내용은 아닙니다."
		},
		{
			text: "비공개 파일이나 데이터에 대한 외부 종속성 없이 실행할 수 있는 작고 완성된 코드 블록",
			explain: "정답! 최소한의 재현 가능한 예제는 라이브러리 메인테이너가 문제를 재현하는 데 도움이 되므로 솔루션을 더 빨리 찾을 수 있습니다.",
			correct: true
		},
		{
			text: "파이썬 traceback의 스크린 샷",
			explain: "다시 풀어보세요. -- 문제를 제출할 때 직면한 에러의 스크린샷을 포함하고 싶겠지만 이렇게 하면 다른 사람이 에러를 재현하기가 매우 어렵습니다."
		},
		{
			text: "에러와 관련 없는 부분을 포함하여 전체 분석이 포함된 노트북",
			explain: "정답은 아닙니다. 에러를 표시하는 Google Colab 노트북을 공유하는 것이 도움이 될 수 있지만 짧고 관련 코드만 포함해야 합니다."
		}
	]}
/>

### 3. 에러가 발생하는 다음 코드를 실행하려고 한다고 가정합니다.:

```py
from transformers import GPT3ForSequenceClassification

# ImportError: cannot import name 'GPT3ForSequenceClassification' from 'transformers' (/Users/lewtun/miniconda3/envs/huggingface/lib/python3.8/site-packages/transformers/__init__.py)
# ---------------------------------------------------------------------------
# ImportError                               Traceback (most recent call last)
# /var/folders/28/k4cy5q7s2hs92xq7_h89_vgm0000gn/T/ipykernel_30848/333858878.py in <module>
# ----> 1 from transformers import GPT3ForSequenceClassification

# ImportError: cannot import name 'GPT3ForSequenceClassification' from 'transformers' (/Users/lewtun/miniconda3/envs/huggingface/lib/python3.8/site-packages/transformers/__init__.py)
```

다음 중 도움을 요청하기 위한 포럼 게시글의 제목으로 적합한 것은 무엇일까요?

<Question
	choices={[
		{
			text: "<code>ImportError: cannot import name 'GPT3ForSequenceClassification' from 'transformers' (/Users/lewtun/miniconda3/envs/huggingface/lib/python3.8/site-packages/transformers/__init__.py)</code>",
			explain: "Traceback의 마지막 줄을 포함하는 것은 설명적일 수 있지만 주제의 본문에 더 적합합니다. 다시 풀어보세요!"
		},
		{
			text: "문제가 있습니다. <code>from transformers import GPT3ForSequenceClassification</code>",
			explain: "다시 풀어보세요. 유용한 정보를 제공하지만 텍스트의 본문용으로 사용하는 것이 가장 좋습니다.",
		},
		{
			text: "왜 <code>GPT3ForSequenceClassification</code> import가 안될까요? ?",
			explain: "훌륭한 선택입니다! 이 제목은 간결하고 독자에게 무엇이 잘못되었는지에 대한 단서를 제공합니다(GPT-3는 🤗 트랜스포머에서 지원되지 않음).",
			correct: true
		},
		{
			text: "🤗 Transformers에서 GPT-3가 지원되나요?",
			explain: "잘했습니다! 질문을 주제 제목으로 사용하는 것은 문제를 커뮤니티에 전달하는 좋은 방법입니다.",
			correct: true
		}
	]}
/>

### 4. `trainer.train()`을 실행하려고 시도했는데 정확히 어디서 에러가 발생했는지 알려주지 않는 알 수 없는 에러에 직면했다고 가정합니다. 다음 중 학습 파이프라인에서 에러를 찾아야 하는 첫 번째 위치는 어디일까요?

<Question
	choices={[
		{
			text: "그래디언트를 계산하고 역전파를 수행하는 최적화 단계",
			explain: "옵티마이저에 버그가 있을 수 있지만 이것은 일반적으로 학습 파이프라인의 여러 단계이므로 먼저 확인해야 할 다른 사항이 있습니다. 다시 풀어보세요!"
		},
		{
			text: "메트릭을 계산하는 평가 단계",
			explain: "평가는 일반적으로 전체 epoch에 대한 학습 후에 수행하는 작업이므로 학습 파이프라인의 초기 부분에서 먼저 확인해야 합니다.",
		},
		{
			text: "데이터셋",
			explain: "정답! 텍스트가 적절하게 인코딩되었는지, 예상되는 기능이 있는지 등을 확인하기 위해 거의 항상 데이터를 살펴보는 것이 가장 먼저 해야 할 일입니다.",
			correct: true
		},
		{
			text: "데이터 로더",
			explain: "다시 풀어보세요. 가장 먼저 확인해야 할 사항과 매우 비슷합니다. 데이터 로더에 어떤 개체를 전달했는지 기억하시나요?"
		}
	]}
/>

### 5. CUDA 에러를 디버그하는 최고의 방법은 무엇일까요?

<Question
	choices={[
		{
			text: "포럼이나 GitHub에 에러 메시지를 게시하기.",
			explain: "CUDA 오류 메시지는 일반적으로 정보가 매우 없기 때문에 누구에게도 도움이 되지 않습니다."
		},
		{
			text: "CPU에서 같은 코드로 실행하기",
			explain: "정답!, CPU에서 실행하면 더 정확한 에러 메세지를 받을 수 있습니다!",
			correct: true
		},
		{
			text: "Traceback을 읽고 에러의 원인을 찾기.",
			explain: "다른 에러에 대해 수행하는 작업이지만 대부분의 CUDA 작업은 비동기식이므로 CUDA 에러는 일반적으로 해당 위치에서 발생하지 않습니다."
		},
		{
			text: "배치 사이즈 줄여보기",
			explain: "배치 크기를 줄이는 것은 일반적으로 CUDA 메모리 부족 에러를 처리하는 데 좋은 전략이지만 이 특정 문제에는 그렇지 않습니다. 다시 풀어보세요!"
		},
		{
			text: "주피터 커널 재시작하기",
			explain: "다시 풀어보세요. 커널을 다시 시작해도 에러가 사라지지 않습니다!",
		}
	]}
/>

### 6. GitHub에서 문제를 해결하는 가장 좋은 방법은 무엇일까요?

<Question
	choices={[
		{
			text: "버그의 전체 재현 가능한 예를 게시하기.",
			explain: "정답!, 메인테이너가 버그를 찾는 데 도움이 되는 가장 좋은 방법입니다. 또 무엇을 더 할 수 있을까요?",
			correct: true
		},
		{
			text: "매일 업데이트 요청하기",
			explain: "여러분에게 도움을 줄 것 같지 않습니다. 사람들은 아마 당신을 더 무시할 겁니다.",
		},
		{
			text: "버그 주변의 소스 코드를 검사하고 버그가 발생하는 이유를 찾아서 문제의 결과를 게시하기.",
			explain: "분명히 메인테이너에게 도움이 될 것입니다! 그리고 버그의 원인과 수정 사항을 찾으면 풀 리퀘스트를 열 수도 있습니다. 또 무엇을 더 할 수 있을까요?",
			correct: true
		}
	]}
/>

### 7. 하나의 배치에 과적합하는 것이 일반적으로 좋은 디버깅 기술인 이유는 무엇일까요?

<Question
	choices={[
		{
			text: "그렇지 않습니다. 과적합은 항상 좋지 않으며 피해야 합니다.",
			explain: "전체 데이터 세트에 대해 학습할 때 과적합은 실제로 모델이 새로운 예에 잘 일반화되지 않는다는 신호일 수 있습니다. 그러나 디버깅을 위해 일반적으로 전체 데이터 세트에 대해 학습하지는 않습니다. 다시 풀어보세요!"
		},
		{
			text: "모델이 손실을 0으로 줄일 수 있는지 확인할 수 있습니다.",
			explain: "정답! 예가 두 개밖에 안 되는 작은 배치로 모델이 학습할 수 있는지 빠르게 확인할 수 있습니다.",
			correct: true
		},
		{
			text: "이를 통해 입력 및 레이블의 텐서 모양이 올바른지 확인할 수 있습니다.",
			explain: "다시 풀어보세요. 텐서 모양이 잘못 정렬되면 단일 배치에서도 훈련할 수 없습니다.",
		}
	]}
/>

### 8. 🤗 Transformers 저장소에서 새 이슈를 생성할 때 `transformers-cli env`를 사용하여 컴퓨팅 환경에 대한 세부 정보를 포함하는 것이 좋은 이유는 무엇일까요?

<Question
	choices={[
		{
			text: "이를 통해 메인테이너는 사용 중인 라이브러리의 버전을 이해할 수 있습니다.",
			explain: "정답! 라이브러리의 각 주요 버전은 API에 변경 사항이 있을 수 있으므로 사용 중인 특정 버전을 알면 문제를 좁히는 데 도움이 될 수 있습니다. 다른 이점은 무엇이 있을까요?",
			correct: true
		},
		{
			text: "이를 통해 메인테이너는 Windows, macOS 또는 Linux에서 코드를 실행 중인지 여부를 알 수 있습니다.",
			explain: "정답! 사용 중인 특정 운영 체제로 인해 때때로 오류가 발생할 수 있으며 이를 알고 있으면 메인테이너가 에러를 로컬에서 재현하는 데 도움이 됩니다. 하지만 그게 유일한 이유는 아닙니다.",
			correct: true
		},
		{
			text: "이를 통해 메인테이너는 코드를 GPU에서 실행하는지 CPU에서 실행하는지 알 수 있습니다.",
			explain: "정답! 이 장에서 살펴본 것처럼 GPU 또는 CPU에서 실행되는 코드는 다른 결과 또는 오류를 생성할 수 있으며 사용 중인 하드웨어를 알면 메인테이너의 주의를 집중하는 데 도움이 될 수 있습니다. 하지만 이점은 이뿐만이 아닙니다...",
			correct: true
		}
	]}
/>


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

### 학습 파이프라인 디버깅
https://huggingface.co/learn/course/ko/chapter8/4_tf.md

# 학습 파이프라인 디버깅

<CourseFloatingBanner chapter={8}
  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/chapter8/section4_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter8/section4_tf.ipynb"},
]} />

[단원 7](/course/chapter7)의 조언에 충실히 따라 주어진 작업에서 모델을 훈련하거나 파인튜닝하는 멋진 스크립트를 작성했습니다. 하지만 `model.fit()` 명령을 실행하면 끔찍한 일이 발생합니다. 오류가 발생했네요 😱! 또는 더 좋지 못한 일은 모든 것이 정상인 것처럼 보이고 훈련은 오류 없이 실행되었지만 결과 모델이 엉망인 경우입니다. 이 장에서는 이러한 종류의 이슈를 디버그하기 위해 수행할 수 있는 작업을 보여줍니다.

## 학습 파이프라인 디버깅

<Youtube id="N9kO52itd0Q"/>

`model.fit()`에서 오류가 발생했을 때 문제는 여러 곳에서 발생할 수 있습니다. 학습은 일반적으로 시작부터 해당 에러 지점까지 많은 작업이 진행되기 때문인데, 문제는 데이터세트가 뭔가 잘못 되었거나, 데이터세트의 배치 데이터를 함께 일괄 처리하려고 할 때 발생하는 문제일 수 있습니다. 또는 모델 코드, 손실 함수 또는 옵티마이저에 문제가 있을 수 있습니다. 모든 것이 훈련에 적합하더라도 메트릭에 문제가 있는 경우 평가 중에 문제가 발생할 수 있습니다.

`model.fit()`에서 발생하는 오류를 디버그하는 가장 좋은 방법은 이 전체 파이프라인을 직접 살펴보고 문제가 발생한 부분을 확인하는 것입니다. 가끔 에러는 해결하기 쉬울 때도 있습니다.

예시를 위해서 [MNLI 데이터세트](https://huggingface.co/datasets/glue)에서 DistilBERT 모델을 파인튜닝(을 시도하는) 아래 스크립트를 사용합니다.:

```py
from datasets import load_dataset
import evaluate
from transformers import (
    AutoTokenizer,
    TFAutoModelForSequenceClassification,
)

raw_datasets = load_dataset("glue", "mnli")

model_checkpoint = "distilbert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)


def preprocess_function(examples):
    return tokenizer(examples["premise"], examples["hypothesis"], truncation=True)


tokenized_datasets = raw_datasets.map(preprocess_function, batched=True)

train_dataset = tokenized_datasets["train"].to_tf_dataset(
    columns=["input_ids", "labels"], batch_size=16, shuffle=True
)

validation_dataset = tokenized_datasets["validation_matched"].to_tf_dataset(
    columns=["input_ids", "labels"], batch_size=16, shuffle=True
)

model = TFAutoModelForSequenceClassification.from_pretrained(model_checkpoint)

model.compile(loss="sparse_categorical_crossentropy", optimizer="adam")

model.fit(train_dataset)
```

이 코드를 실행하려고 하면 데이터 세트를 변환할 때 'VisibleDeprecationWarning'이 표시될 수 있습니다. 이는 알려진 UX 문제이므로 무시하시면 됩니다. 예를 들어, 2021년 11월 이후에 이 코스를 읽고 있는데 여전히 진행 중이라면 수정 할 때까지 @carrigmat로 분노의 트윗을 보내주세요.

위 문제보단 누가봐도 명백한 에러가 발생한다는 것인데, 정말 끔찍하게 깁니다:

```python out
ValueError: No gradients provided for any variable: ['tf_distil_bert_for_sequence_classification/distilbert/embeddings/word_embeddings/weight:0', '...']
```

이게 무슨 뜻일까요? 데이터로 학습하려고 하는데 그라디언트가 없다니? 꽤나 당황스러운 일입니다; 이럴 경우 어떻게 디버깅을 할 수 있을까요? 에러가 발생했을 때 문제가 어디에 있는지 바로 알 수 없는 경우 가장 좋은 해결책은 각 단계에서 모든 것이 올바르게 보이는지 확인하면서 순서대로 살펴보는 것이 효과적인 경우가 있습니다. 물론 시작하는 곳은 항상...

### 데이터 확인

굳이 언급하자면, 데이터가 손상된 경우 Keras는 스스로 수정할 수 없습니다. 따라서 가장 먼저 해야 할 일은 학습 세트 내부가 어떤지 살피는 것입니다.

`raw_datasets` 및 `tokenized_datasets` 내부를 살펴보면 좋겠지만 왠만하면 모델에 들어가기 전 지점의 데이터를 확인하는 것이 좋습니다. 즉, `to_tf_dataset()` 함수로 생성한 `tf.data.Dataset`에서 출력을 확인해야 합니다! 그럼 어떻게 해야 할까요? `tf.data.Dataset` 객체는 한 번에 전체 배치를 제공하고 인덱싱을 지원하지 않으므로 `train_dataset[0]`의 방식으로 요청할 수 없습니다. 그렇지만 우리는 살며시 배치를 요청할 수 있습니다.:

```py
for batch in train_dataset:
    break
```

`break`는 한 번의 반복 후에 루프를 종료하므로 `train_dataset`에서 나오는 첫 번째 배치를 가져와 `batch`로 저장합니다. 이제 내부를 살펴보겠습니다.:

```python out
{'attention_mask': <tf.Tensor: shape=(16, 76), dtype=int64, numpy=
 array([[1, 1, 1, ..., 0, 0, 0],
        [1, 1, 1, ..., 0, 0, 0],
        [1, 1, 1, ..., 0, 0, 0],
        ...,
        [1, 1, 1, ..., 1, 1, 1],
        [1, 1, 1, ..., 0, 0, 0],
        [1, 1, 1, ..., 0, 0, 0]])>,
 'label': <tf.Tensor: shape=(16,), dtype=int64, numpy=array([0, 2, 1, 2, 1, 1, 2, 0, 0, 0, 1, 0, 1, 2, 2, 1])>,
 'input_ids': <tf.Tensor: shape=(16, 76), dtype=int64, numpy=
 array([[ 101, 2174, 1010, ...,    0,    0,    0],
        [ 101, 3174, 2420, ...,    0,    0,    0],
        [ 101, 2044, 2048, ...,    0,    0,    0],
        ...,
        [ 101, 3398, 3398, ..., 2051, 2894,  102],
        [ 101, 1996, 4124, ...,    0,    0,    0],
        [ 101, 1999, 2070, ...,    0,    0,    0]])>}
```

맞는 것 같지 않나요? 모델에 'labels', 'attention_mask', 'input_ids'를 전달하고 있는데, 이는 출력값과 손실값을 계산하는 데 필요한 모든 것이 있어야 합니다. 그럼 왜 그래디언트가 없는 걸까요? 자세히 보면 단일 Dictionary 구조로 입력값을 전달하지만 학습 배치는 일반적으로 텐서 또는 Dictionary, 그리고 레이블 텐서입니다. Dictionary 입력에서 레이블은 키로 되어있습니다.

이것이 문제일까요? 항상 그런 것은 아닙니다! 그렇지만 TensorFlow로 Transformer 모델을 훈련할 때 접하게 되는 가장 일반적인 문제 중 하나입니다. 우리 모델은 모두 내부적으로 손실값을 계산할 수 있지만 그렇게 하려면 레이블이 입력 Dictionary에 전달되어야 합니다. 이것은 `compile()`에 손실 파라미터 값을 지정하지 않을 때 사용되는 손실값 입니다. 반면에 Keras는 일반적으로 레이블이 입력 Dictionary와 별도로 전달될 것으로 예상하며 그렇게 하지 않으면 일반적으로 손실값 계산이 실패합니다.

문제는 이제 명확해졌습니다. 우리는 'loss' 파라미터값을 설정 했었습니다. 즉, Keras에게 손실값을 계산하도록 요청했지만, Keras가 예상하는 위치가 모델에다가 레이블을 직접 전달했습니다! 따라서 둘 중 하나를 선택해야 합니다. 모델의 내부 손실 함수를 사용하고 레이블을 그대로 유지하거나, Keras 손실함수를 사용하고 레이블을 Keras가 예상하는 위치로 이동해야 합니다. 심플하게 첫 번째 접근 방식을 취하겠습니다. `compile()`에 대한 호출을 다음과 같이 변경합니다.:

```py
model.compile(optimizer="adam")
```

이제 모델의 내부 손실 함수를 사용하게 될 것이고 문제가 해결 될 겁니다!
Now we'll use the model's internal loss, and this problem should be resolved!

<Tip>

✏️ **여러분 차례입니다!** 다른 문제를 해결 후 추가 도전으로, 이 단계로 돌아와 모델이 내부 손실 대신 원래 Keras 손실함수로 작동하도록 할 수 있습니다. 레이블이 올바르게 출력되도록 하려면 `to_tf_dataset()`의 `label_cols` 인수에 `"labels"`를 추가해야 합니다. 그러면 그래디언트가 계산됩니다. 하지만 우리가 지정한 손실함수에는 한 가지 문제가 더 있습니다. 학습은 문제가 있음에도 불구하고 계속 실행되지만 학습이 매우 느리고 높은 train 손실값에서 정체 될 수 있습니다. 왜 그런지 알아 볼까요?
 
만약 어렵다면 ROT13 인코드 방식의 힌트를 보세요 Transformers에서 SequenceClassification 모델의 출력을 보면 첫 번째 출력은 'logits'입니다. logits란 무엇일까요?(살면서 ROT13 인코딩은 처음 봤네요, 궁금하면 영어 원본으로 보세요.)

두번째 힌트: 옵티마이저, 활성함수 또는 손실함수를 문자열로 지정하면 Keras는 해당 함수의 모든 인수 값을 기본값으로 설정합니다. SparseCategoricalCrossentropy에는 어떤 인수가 있으며 기본값은 무엇일까요?

</Tip>

이제 훈련을 해보죠. 그라디언트를 가져와야 하므로 부디(갑자기 불길한 음악이 재생됨) `model.fit()`을 호출하면 모든 것이 잘 작동할 것입니다!

```python out
  246/24543 [..............................] - ETA: 15:52 - loss: nan
```

이런.

`nan`은 썩 내키지 않는 손실값 입니다. 데이터를 확인했었지만 괜찮아 보였는데 말이죠. 이게 문제가 아니라면 다음은 어떤 것을 확인해야할까요? 다음 단계는 분명히...

### 모델 확인

`model.fit()`은 Keras에서 정말 훌륭한 함수이지만 많은 일을 수행하므로 문제가 발생한 위치를 정확히 찾기가 더 어려울 수 있습니다. 모델을 디버깅하는 경우 진짜 도움이 될만한 전략으로 모델에 단일 배치를 전달하고 해당 배치에 대한 출력을 자세히 살펴보는 방법이 있습니다. 모델에서 오류가 발생하는 경우 정말 유용한 또 다른 팁은 `run_eagerly=True`로 모델을 `compile()`하는 것입니다. 이렇게 하면 속도가 훨씬 느려지지만 모델 코드에서 문제가 발생한 위치를 정확히 나타내기 때문에 에러 메시지를 훨씬 더 이해하기 쉽게 만듭니다.

하지만 지금은 아직 `run_eagerly`가 필요하지 않습니다. 모델을 통해 이전에 얻은 `batch`를 실행하고 출력이 어떻게 보이는지 봅시다:
For now, though, we don't need `run_eagerly` just yet. Let's run the `batch` we got before through the model and see what the outputs look like:

```py
model(batch)
```

```python out
TFSequenceClassifierOutput(loss=<tf.Tensor: shape=(16,), dtype=float32, numpy=
array([nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan,
       nan, nan, nan], dtype=float32)>, logits=<tf.Tensor: shape=(16, 2), dtype=float32, numpy=
array([[nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan]], dtype=float32)>, hidden_states=None, attentions=None)
```

흠, 무언가 복잡하네요. 모든 것이 `nan` 입니다! 하지만 이상하지 않나요? 모든 로짓은 어떻게 `nan`이 될까요? `nan`은 "not a number.", 즉 숫자가 아님을 뜻합니다. `nan` 값은 0으로 나누는 것과 같은 금지된 연산을 수행할 때 종종 발생합니다. 그러나 기계 학습에서 `nan`에 대해 알아야 할 매우 중요한 한 가지는 이 값이 *전파*되는 경향이 있다는 것입니다. 숫자에 `nan`을 곱하면 출력도 `nan`이 됩니다. 출력, 손실값 또는 그래디언트의 아무 곳에서나 `nan`을 얻으면 전체 모델에 빠르게 확산됩니다. 왜냐하면 그 `nan` 값이 네트워크를 통해 다시 전파될 때 `nan` 그래디언트를 사용하여 가중치 업데이트를 계산하면 'nan' 가중치를 얻을 수 있으며 이러한 가중치는 더 많은 `nan` 출력을 계산합니다! 곧 전체 네트워크는 하나의 큰 `nan` 블록이 될 것입니다. 그런 일이 발생하면 문제가 어디에서 시작되었는지 확인하기가 매우 어렵습니다. 'nan'이 처음 들어온 곳을 어떻게 분리할 수 있을까요?

정답은 모델을 *재초기화* 하는 것입니다. 한번 훈련을 시작하면서 어딘가에 `nan`이 생겼고 모델 전체로 빠르게 전파되었습니다. 따라서 체크포인트에서 모델을 로드하고 가중치 업데이트를 수행하지 않고 `nan` 값이 생기는 위치를 확인합니다.:

```py
model = TFAutoModelForSequenceClassification.from_pretrained(model_checkpoint)
model(batch)
```

코드를 실행하면 다음의 값이 출력됩니다.:

```py out
TFSequenceClassifierOutput(loss=<tf.Tensor: shape=(16,), dtype=float32, numpy=
array([0.6844486 ,        nan,        nan, 0.67127866, 0.7068601 ,
              nan, 0.69309855,        nan, 0.65531296,        nan,
              nan,        nan, 0.675402  ,        nan,        nan,
       0.69831556], dtype=float32)>, logits=<tf.Tensor: shape=(16, 2), dtype=float32, numpy=
array([[-0.04761693, -0.06509043],
       [-0.0481936 , -0.04556257],
       [-0.0040929 , -0.05848458],
       [-0.02417453, -0.0684005 ],
       [-0.02517801, -0.05241832],
       [-0.04514256, -0.0757378 ],
       [-0.02656011, -0.02646275],
       [ 0.00766164, -0.04350497],
       [ 0.02060014, -0.05655622],
       [-0.02615328, -0.0447021 ],
       [-0.05119278, -0.06928903],
       [-0.02859691, -0.04879177],
       [-0.02210129, -0.05791225],
       [-0.02363213, -0.05962167],
       [-0.05352269, -0.0481673 ],
       [-0.08141848, -0.07110836]], dtype=float32)>, hidden_states=None, attentions=None)
```

*이제* `nan`값의 위치를 알았내요! 우리 로짓값에는 `nan` 값이 없습니다. 그러나 우리는 손실값에서 몇 개의 'nan' 값이 보이네요. 특히 이 문제를 일으키는 샘플에 대해 뭔가가 있는 걸까요? 어떤 것들이 있는지 봅시다(이 코드를 직접 실행하면 데이터세트가 섞였기 때문에 다른 인덱스를 얻을 수 있습니다):

```python
import numpy as np

loss = model(batch).loss.numpy()
indices = np.flatnonzero(np.isnan(loss))
indices
```

```python out
array([ 1,  2,  5,  7,  9, 10, 11, 13, 14])
```

이 인덱스로 불러온 샘플을 살펴보겠습니다.:

```python
input_ids = batch["input_ids"].numpy()
input_ids[indices]
```

```python out
array([[  101,  2007,  2032,  2001,  1037, 16480,  3917,  2594,  4135,
        23212,  3070,  2214, 10170,  1010,  2012,  4356,  1997,  3183,
         6838, 12953,  2039,  2000,  1996,  6147,  1997,  2010,  2606,
         1012,   102,  6838,  2001,  3294,  6625,  3773,  1996,  2214,
         2158,  1012,   102,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0],
       [  101,  1998,  6814,  2016,  2234,  2461,  2153,  1998, 13322,
         2009,  1012,   102,  2045,  1005,  1055,  2053,  3382,  2008,
         2016,  1005,  2222,  3046,  8103,  2075,  2009,  2153,  1012,
          102,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0],
       [  101,  1998,  2007,  1996,  3712,  4634,  1010,  2057,  8108,
         2025,  3404,  2028,  1012,  1996,  2616, 18449,  2125,  1999,
         1037,  9666,  1997,  4100,  8663, 11020,  6313,  2791,  1998,
         2431,  1011,  4301,  1012,   102,  2028,  1005,  1055,  5177,
         2110,  1998,  3977,  2000,  2832,  2106,  2025,  2689,  2104,
         2122,  6214,  1012,   102,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0],
       [  101,  1045,  2001,  1999,  1037, 13090,  5948,  2007,  2048,
         2308,  2006,  2026,  5001,  2043,  2026,  2171,  2001,  2170,
         1012,   102,  1045,  2001,  3564,  1999,  2277,  1012,   102,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0],
       [  101,  2195,  4279,  2191,  2039,  1996,  2181,  2124,  2004,
         1996,  2225,  7363,  1012,   102,  2045,  2003,  2069,  2028,
         2451,  1999,  1996,  2225,  7363,  1012,   102,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0],
       [  101,  2061,  2008,  1045,  2123,  1005,  1056,  2113,  2065,
         2009,  2428, 10654,  7347,  2030,  2009,  7126,  2256,  2495,
         2291,   102,  2009,  2003,  5094,  2256,  2495,  2291,  2035,
         2105,  1012,   102,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0],
       [  101,  2051,  1010,  2029,  3216,  2019,  2503,  3444,  1010,
         6732,  1996,  2265,  2038, 19840,  2098,  2125,  9906,  1998,
         2003,  2770,  2041,  1997,  4784,  1012,   102,  2051,  6732,
         1996,  2265,  2003,  9525,  1998,  4569,  1012,   102,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0],
       [  101,  1996, 10556,  2140, 11515,  2058,  1010,  2010,  2162,
         2252,  5689,  2013,  2010,  7223,  1012,   102,  2043,  1996,
        10556,  2140, 11515,  2058,  1010,  2010,  2252,  3062,  2000,
         1996,  2598,  1012,   102,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0],
       [  101, 13543,  1999,  2049,  6143,  2933,  2443,   102,  2025,
        13543,  1999,  6143,  2933,  2003,  2443,   102,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0]])
```

음, 여기에 많은 것이 있지만 특별한게 없습니다. 레이블을 살펴보겠습니다.:

```python out
labels = batch['labels'].numpy()
labels[indices]
```

```python out
array([2, 2, 2, 2, 2, 2, 2, 2, 2])
```

아! `nan` 샘플은 모두 동일하게 레이블 2를 가지고 있습니다. 이건 강력한 힌트입니다. 레이블이 2일 때 'nan'손실값이 발생한다는 사실은 우리 모델의 레이블 수를 확인하기에 아주 좋은 시기임을 뜻합니다.:

```python
model.config.num_labels
```

```python out
2
```

이제 문제가 보입니다. 모델은 클래스가 두 개뿐이라고 생각하지만 레이블이 최대 2까지 갑니다. 이는 실제로 세 개의 클래스가 있다는 것을 의미합니다(0도 클래스이기 때문에). 이게 `nan`값이 발생한 이유 -- 존재하지 않는 클래스에 대한 손실을 계산하려고 시도 했기 때문입니다! 이를 변경하고 모델을 다시 피팅해 보겠습니다.:

```
model = TFAutoModelForSequenceClassification.from_pretrained(model_checkpoint, num_labels=3)
model.compile(optimizer='adam')
model.fit(train_dataset)
```

```python out
  869/24543 [>.............................] - ETA: 15:29 - loss: 1.1032
```

학습이 진행되는군요! 더이상 `nan` 없으며, 손실값이 감소하는 듯 보입니다... 한참을 보다보면 손실값이 완고하게 높은 상태를 유지하기 때문에 조금 조바심이 나기 시작할지도 모릅니다. 여기서 학습을 중단하고 이 문제의 원인이 무엇인지 생각해 보겠습니다. 이 시점에서 우리는 데이터와 모델이 모두 괜찮다고 확신하지만 우리 모델은 잘 학습하지 못하고 있습니다. 무엇이 더 남았을까요? 그건 바로...

### 하이퍼파라미터 확인

위의 코드를 다시 보면 `batch_size`를 제외하고는 하이퍼파라미터를 전혀 볼 수 없는데, 이것이 범인일 가능성은 없어 보입니다. 하지만 속지 마세요. 항상 하이퍼파라미터가 있어야 하며, 이를 볼 수 없다면 이는 단지 하이퍼파라미터가 무엇으로 설정되어 있는지 모른다는 것을 의미합니다. 특히 Keras에 대한 중요한 점을 기억하세요. 손실 함수, 옵티마이저 함수 또는 활성화 함수를 문자열로 설정하면 _모든 인수가 기본값으로 설정됩니다_. 즉 문자열을 사용하는 것이 매우 편리하더라도 심각한 이슈 쉽게 숨길 수 있으므로 그렇게 할 때 매우 조심해야 함을 의미합니다. (이러한 선택적인 도전을 시도하는 사람은 이 사실을 염두해야 합니다.)

이 경우 문자열로 인수를 설정한 위치가 어디일까요? 처음에는 문자열로 손실 함수를 설정했지만 더 이상 그렇게 하지 않습니다. 다시보니 옵티마이저를 문자열로 설정하고 있습니다. 이게 어떤 것을 숨기고 있을까요? 옵티마이저의 [인수](https://www.tensorflow.org/api_docs/python/tf/keras/optimizers/Adam)를 살펴보겠습니다.

눈에 띄는 것이 있었나요? 맞습니다. 학습률입니다! 문자열 `'adam'`을 사용하면 기본 학습률이 0.001 또는 1e-3이 됩니다. 트랜스포머 모델에게는 상당히 높은 값입니다. 일반적으로 트랜스포머 모델 계열은 1e-5와 1e-4 사이의 학습률을 시도하는 것이 좋습니다. 이 값은 우리가 여기서 실제로 사용한 학습률보다 10배에서 100배 사이에 있습니다. 큰 문제가 될 것 같으니 줄여보겠습니다. 그렇게 하려면 실제 'optimizer' 객체를 가져와야 합니다. 학습 속도가 높은 훈련이 가중치를 손상시키는 경우를 대비하여 체크포인트에서 모델을 다시 초기화 하겠습니다.:

```python
from tensorflow.keras.optimizers import Adam

model = TFAutoModelForSequenceClassification.from_pretrained(model_checkpoint)
model.compile(optimizer=Adam(5e-5))
```

<Tip>

🤗 Transformers에서 `create_optimizer()` 함수를 가져올 수도 있습니다. 이렇게 하면 적합한 가중치 감쇠(weight decay)와 학습률 워밍업과 감쇠가 포함된 AdamW 옵티마이저가 제공됩니다. 이 옵티마이저는 기본 Adam 옵티마이저로 얻은 결과보다 약간 더 나은 결과를 만들어내는 경우가 많습니다.

</Tip>

이제 새롭고, 개선된 학습률로 모델 학습에 도전해보겠습니다.:

```python
model.fit(train_dataset)
```

```python out
319/24543 [..............................] - ETA: 16:07 - loss: 0.9718
```

이제 손실값은 정말로 움직이고 있습니다! 학습이 마침내 효과가 있는 것 같습니다. 여기에는 교훈이 있습니다. 모델이 실행 중이지만 손실값이 감소하지 않고 데이터가 괜찮다고 확신하는 경우 학습률 및 가중치 감쇠와 같은 하이퍼파라미터를 확인하는 것이 좋습니다. 둘 중 하나를 너무 높게 설정하면 학습이 높은 손실 값에서 "정지"될 가능성이 매우 높습니다.


## 다른 잠재적 이슈들

위의 스크립트에서 문제를 다루었지만 직면할 수 있는 여러가지 다른 일반적인 오류가 있습니다. (매우 불완전한) 목록을 살펴보겠습니다.

### 메모리 부족 에러 처리

메모리 부족을 알리는 신호는 "OOM when allocating tensor"와 같은 에러입니다. OOM은 "out of memory"의 줄임말입니다. 이것은 큰 언어 모델을 다룰 때 매우 흔한 위험입니다. 이 문제가 발생하면 배치 크기를 절반으로 줄이고 다시 시도하는 것이 좋습니다. 그러나 일부 모델은 *매우* 큽니다. 예를 들어, 최대크기 GPT-2는 1.5B개의 매개변수가 있습니다. 즉, 모델을 저장하는 데만 6GB의 메모리가 필요하고 그라디언트에는 6GB가 추가로 필요합니다! 최대 크기 GPT-2 모델을 학습하려면 사용하는 배치 크기에 관계없이 일반적으로 20GB 이상의 VRAM이 필요하며, 소수의 GPU만 해당합니다. 'distilbert-base-cased'와 같은 더 가벼운 모델은 실행하기가 훨씬 쉽고 훨씬 빠르게 학습할 수 있습니다.

<Tip>

다음 장에서는 메모리 사용량을 줄이고 가장 큰 모델을 파인튜닝 할 수 있는 고급 기술을 살펴보겠습니다.

</Tip>

### 몹시 배고픈 TensorFlow 🦛

TensorFlow의 특별한 특징 중 하나는 모델을 로드하거나 학습을 수행하는 즉시 GPU 메모리의 *모든*을 자체적으로 할당한 다음 필요에 따라 해당 메모리를 분할한다는 것입니다. 이것은 PyTorch와 같은 다른 프레임워크의 동작과 다른데, 이는 내부적으로 수행하지 않고 CUDA에서 필요에 따라 메모리를 할당합니다. TensorFlow 접근 방식의 한 가지 장점은 메모리가 부족할 때 종종 유용한 오류를 제공할 수 있고 전체 CUDA 커널을 충돌시키지 않고 해당 상태에서 복구할 수 있다는 것입니다. 그러나 중요한 단점도 있습니다. 한 번에 두 개의 TensorFlow 프로세스를 실행하면 **나쁜 시간을 보내게 됩니다**.

Colab에서 실행하는 경우에는 이에 대해 걱정할 필요가 없지만 로컬에서 실행하는 경우에는 확실히 주의해야 합니다. 특히 노트북(주피터 노트북 등) 탭을 닫아도 해당 노트북이 반드시 종료되는 것은 아닙니다. 실행 중인 노트북(녹색 아이콘이 있는 노트북)을 선택하고 디렉토리 목록에서 수동으로 종료해야 할 수도 있습니다. TensorFlow를 사용하는 실행 중인 노트북은 여전히 ​​많은 GPU 메모리를 보유하고 있을 수 있으며, 이는 새로 시작하는 노트북에서 매우 이상한 문제가 발생할 수 있음을 의미합니다.

이전에 작동했던 코드에서 CUDA, BLAS 또는 cuBLAS에 대한 오류가 발생하기 시작하면 이 현상이 범인인 경우가 많습니다. `nvidia-smi`와 같은 명령을 사용하여 현재 노트북을 종료하거나 다시 시작할 때 대부분의 메모리가 사용 가능한지, 아니면 여전히 사용 중인지 확인할 수 있습니다. 아직 사용 중이라면 다른 무언가가 GPU를 붙잡고 있는 것입니다!

### 데이터 확인 (다시!)

모델은 데이터에서 실제로 무엇이든 학습할 수 있는 경우에만 무언가를 학습합니다. 데이터를 손상시키는 버그가 있거나 레이블이 무작위로 지정된 경우 데이터 세트에 대해 모델이 학습을 하지 못할 가능성이 매우 높습니다. 여기에서 유용한 도구 중 하나는 `tokenizer.decode()`입니다. 이렇게 하면 `input_ids`가 다시 문자열로 바뀌므로 데이터를 보고 학습 데이터가 원하는 내용을 가르치는지 확인할 수 있습니다. 예를 들어, 위에서 했던 것처럼 `tf.data.Dataset`에서 `batch`를 얻은 후 다음과 같이 첫 번째 요소를 디코딩할 수 있습니다.:

```py
input_ids = batch["input_ids"].numpy()
tokenizer.decode(input_ids[0])
```

그리고 첫번째 레이블과 함께 다음처럼 비교해볼 수 있습니다.:

```py
labels = batch["labels"].numpy()
label = labels[0]
```

이처럼 데이터를 볼 수 있게 되면 스스로에게 다음과 같은 질문을 할 수 있습니다.:

- 디코드된 데이터가 이해할만한가?
- 디코드된 데이터에 대한 레이블이 적합한가?
- 다른 레이블보다 더 일반적인 레이블이 있는가?
- 모델이 임의의 답변을 내거나, 같은 답변만 한다면 어떤 손실/평가 함수를 사용해야 할까? 

데이터를 살펴본 후 모델의 예측을 몇 가지 살펴보세요. 모델이 토큰을 출력하는 경우 디코딩도 시도해 보세요. 모델이 항상 동일한 것을 예측하는 경우 데이터 세트가 하나의 범주(분류 문제의 경우)로 편향되어 있으므로 희귀 클래스를 오버샘플링하는 것과 같은 기술이 도움이 될 수 있습니다. 또는 잘못된 하이퍼파라미터 설정과 같은 학습 문제로 인해 발생할 수도 있습니다.

학습 전에 초기 모델에서 얻은 손실값/평가값이 무작위 예측에 대해 예상하는 손실값/평가값과 매우 다른 경우 버그가 있을 수 있으므로 손실값 또는 평가값이 계산되는 방식을 다시 확인하세요. 마지막에 여러 손실함수를 추가해 사용하는 경우 동일한 스케일의 값인지 확인하세요.

데이터가 완벽하다고 확신한다면 한 번의 간단한 테스트로 모델이 데이터를 학습할 수 있는지 확인할 수 있습니다.

### 한 배치에서 모델 과대적합

과적합은 일반적으로 학습할 때 피하려고 합니다. 이는 모델이 우리가 원하는 일반적인 기능을 인식하는 것을 학습하지 않고 대신 학습 샘플을 기억한다는 것을 의미하기 때문입니다. 그러나 한 배치에서 모델을 계속해서 훈련시키려고 시도하는 것은 학습하려는 모델로 우리가 구성한 문제를 해결할 수 있는지 확인하는 좋은 테스트입니다. 또한 초기 학습률이 너무 높은지 확인하는 데 도움이 됩니다.

'모델'을 정의한 후에 이 작업을 수행하는 것은 정말 쉽습니다. 학습 데이터 배치를 가져온 다음 해당 `배치`를 전체 데이터 세트로 처리하고 여러 에포크에 걸쳐 학습합니다.:

```py
for batch in train_dataset:
    break

# Make sure you have run model.compile() and set your optimizer,
# and your loss/metrics if you're using them

model.fit(batch, epochs=20)
```

<Tip>

💡 학습 데이터가 불균형한 경우 모든 레이블을 포함하는 학습 데이터 배치를 구성해야 합니다.

</Tip>

결과 모델은 `배치`에서 완벽에 가까운 결과를 가져야 하며 손실은 0(또는 사용 중인 손실의 최소값)으로 빠르게 감소합니다.

모델이 이와 같은 완벽한 결과를 얻도록 관리하지 못한다면 문제 또는 데이터를 구성하는 방식에 문제가 있음을 의미하므로 수정해야 합니다. 과적합 테스트를 통과해야만 모델이 실제로 무언가를 학습할 수 있다고 확신할 수 있습니다.

<Tip warning={true}>

⚠️ 과적합 테스트 후에 모델을 다시 만들고 다시 컴파일해야 합니다. 학습 모델이 전체 데이터 세트에서 유용한 것을 복구하고 학습할 수 없기 때문입니다.

</Tip>

### 첫 번째 기준이 생길 때까지 아무 것도 조정하지 마세요.

강렬한 하이퍼파라미터 조정은 항상 기계 학습의 가장 어려운 부분으로 강조되지만 지표에서 약간의 이득을 얻는 데 도움이 되는 마지막 단계일 뿐입니다. Transformer 모델과 함께 기본 Adam 학습률 1e-3을 사용하는 것과 같이 하이퍼파라미터에 대한 *매우* 나쁜 값은 학습 진행을 매우 느리게 또는 완전히 중단하게 만들지만 대부분의 경우 다음과 같은 "합리적인" 하이퍼파라미터 1e-5에서 5e-5까지의 학습률을 사용하면 좋은 결과를 얻을 수 있습니다. 따라서 데이터 세트의 기준을 능가하는 결과를 얻을 때까지 시간과 비용이 많이 드는 하이퍼파라미터 검색을 시작하지 마세요.

적당한 모델을 갖고나면 약간 조정을 시작할 수 있습니다. 다른 하이퍼파라미터로 천 개의 실행을 시작하려고 하지 말고 하나의 하이퍼파라미터에 대해 다른 값을 가진 몇 개의 실행을 비교하여 가장 큰 영향력을 미치는 아이디어를 얻어보세요.

모델 자체를 조정하는 경우 단순하게 유지하고 합리적으로 정당화할 수 없는 것은 시도하지 마세요. 항상 과적합 테스트로 돌아가서 변경 사항이 의도하지 않은 결과를 초래하지 않았는지 확인하세요.

### 도움 요청하기

이 단원에서 문제를 해결하는 데 도움이 되는 몇 가지 조언을 찾았으면 좋겠지만, 그렇지 않은 경우 [포럼](https://discuss.huggingface.co/)에서 커뮤니티에 언제든지 질문할 수 있음을 기억하세요.

다음은 도움이 될 수 있는 몇 가지 추가 자료입니다.:

- ["Reproducibility as a vehicle for engineering best practices"](https://docs.google.com/presentation/d/1yHLPvPhUs2KGI5ZWo0sU-PKU3GimAk3iTsI38Z-B5Gw/edit#slide=id.p) by Joel Grus
- ["Checklist for debugging neural networks"](https://towardsdatascience.com/checklist-for-debugging-neural-networks-d8b2a9434f21) by Cecelia Shao
- ["How to unit test machine learning code"](https://medium.com/@keeper6928/how-to-unit-test-machine-learning-code-57cf6fd81765) by Chase Roberts
- ["A Recipe for Training Neural Networks"](http://karpathy.github.io/2019/04/25/recipe/) by Andrej Karpathy

물론 신경망을 훈련할 때 발생하는 모든 문제가 자신의 잘못은 아닙니다! 🤗 Transformers 또는 🤗 Datasets 라이브러리에서 이상한 것을 본다면 버그가 발생했을 수 있습니다. 버그를 봤을 경우 우리에게 상세하게 말해야 하며, 다음 섹션에서 그 방법을 정확히 설명할 것입니다.

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ko/chapter8/4_tf.mdx" />

### 에러가 발생했을 때 대응 방법
https://huggingface.co/learn/course/ko/chapter8/2.md

# 에러가 발생했을 때 대응 방법

<CourseFloatingBanner chapter={8}
  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/chapter8/section2.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter8/section2.ipynb"},
]} />

이번 장에서는 Transformer 모델을 새롭게 튜닝 후 예측을 하려고 할 때 발생할 수 있는 몇가지 일반적인 에러를 살펴보겠습니다.

<Youtube id="DQ-CpJn6Rc4"/>

이번 장에서 [모델의 저장소 템플릿](https://huggingface.co/lewtun/distilbert-base-uncased-finetuned-squad-d5716d28)이 준비되어 있습니다. 
만약 이번 단원에서 코드를 실행하려면 모델을 [Huggingface Hub](https://huggingface.co)의 개인 계정에 모델을 복사해야 합니다.
모델을 계정의 저장소에 복제하기 위해 주피터 노트북에서 아래의 코드를 실행하거나:

```python
from huggingface_hub import notebook_login

notebook_login()
```

또는 아래의 스크립트를 원하는 터미널에서 실행합니다:

```bash
huggingface-cli login
```

터미널에서 아이디와 비밀번호를 입력하는 프롬프트가 나타나며, 식별 토큰은 *~/.cache/huggingface/*에 저장됩니다. 한번 로그인 하고 나면 모델의 저장소 템플릿을 아래의 함수를 사용해 복사할 수 있습니다:

```python
from distutils.dir_util import copy_tree
from huggingface_hub import Repository, snapshot_download, create_repo, get_full_repo_name


def copy_repository_template():
    # Clone the repo and extract the local path
    template_repo_id = "lewtun/distilbert-base-uncased-finetuned-squad-d5716d28"
    commit_hash = "be3eaffc28669d7932492681cd5f3e8905e358b4"
    template_repo_dir = snapshot_download(template_repo_id, revision=commit_hash)
    # Create an empty repo on the Hub
    model_name = template_repo_id.split("/")[1]
    create_repo(model_name, exist_ok=True)
    # Clone the empty repo
    new_repo_id = get_full_repo_name(model_name)
    new_repo_dir = model_name
    repo = Repository(local_dir=new_repo_dir, clone_from=new_repo_id)
    # Copy files
    copy_tree(template_repo_dir, new_repo_dir)
    # Push to Hub
    repo.push_to_hub()
```
이제 `copy_repository_template()`를 호출하면 모델 저장소의 템플릿이 계정에 복사 됩니다.

## 🤗 Transformers의 파이프라인 디버깅

Transformer 모델들의 멋진 디버깅 세계로 여정을 떠나기 위해, 다음의 시나리오를 생각해보세요: 여러분은 E-commerce 사이트의 고객이 소비자 상품에 대한 답변을 찾기 위한 질문 및 답변 프로젝트에서 동료와 함께 일하고 있으며, 동료가 당신에게 다음과 같은 메세지를 보냈습니다:

> 안녕하세요! Hugging Face 코스에 있는 [7 단원](/course/chapter7/7)의 기술을 활용해서 실험을 해봤는데, SQuAD에서 좋은 결과를 얻었습니다. 저희 프로젝트를 이 모델로 시작할 수 있다고 생각이 됩니다. 허브에 있는 모델 아이디는 "lewtun/distillbert-base-uncased-finetuned-squad-d5716d28" 입니다. 마음 껏 테스트 해보세요. :)

🤗 Transformers의 `pipeline`을 사용는 모델을 불러오기 위해 우선 고려해야 할 것이 있습니다:

```python
from transformers import pipeline

model_checkpoint = get_full_repo_name("distillbert-base-uncased-finetuned-squad-d5716d28")
reader = pipeline("question-answering", model=model_checkpoint)
```

```python out
"""
OSError: Can't load config for 'lewtun/distillbert-base-uncased-finetuned-squad-d5716d28'. make sure that:

- 'lewtun/distillbert-base-uncased-finetuned-squad-d5716d28'이라는 모델명이 'https://huggingface.co/models'에 존재하는지 확인하거나

'lewtun/distillbert-base-uncased-finetuned-squad-d5716d28'이라는 경로 또는 폴더가 config.json 파일 포함하고 있는지 확인하세요.
"""
```

아 이런, 뭔가 잘못된 것 같네요! 만약 프로그래밍이 처음이라면, 이런 종류의 에러가 처음에는 다소 신비하게(`OSError`란 도대체..) 보일 수도 있습니다. 여기에서 보이는 에러는 파이썬의 traceback(stack trace로 알려져있음)으로 불리는 좀 더 큰 에러 리포트의 마지막 부분 입니다. 예를 들어 이 코드를 Google의 Colab에서 실행한다면 아래와 같은 스크린샷을 보게 될겁니다:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter8/traceback.png" alt="A Python traceback." width="100%"/>
</div>

이 리포트에는 많은 정보를 담고 있으니, 같이 핵심 부분을 살펴보겠습니다. 우선 명심해야할 것은 tracebacks은 _아래부터 위로_ 읽어야 합니다. 이러한 말은 영어 텍스트를 위에서 아래로 읽어오곤 했다면 이상하게 들릴 수 있겠지만 모델과 토크나이저를 다운로드 할 때 `pipeline`이 만드는 함수 호출 순서를 보여주는 traceback을 반영했기 때문입니다. 내부에서 `pipeline`이 작동하는 방식에 대한 자세한 내용은 [단원 2](/course/chapter2)를 참고하세요.

<Tip>

Google Colab의 traceback에서 "6 frames" 주변의 파란 상자를 보셨나요?  traceback을 "frames"로 압축하는 Colab의 특별한 기능입니다. 만약 오류의 원인을 찾을 수 없다면, 두개의 작은 화살표를 클릭해서 전체 traceback을 확장되어 있는지 여부를 확인하세요.

</Tip>

즉 마지막 에러 메시지와 발생한 예외의 이름을 가리키는 traceback의 마지막 줄을 뜻합니다. 이 경우의 예외 유형은 시스템 관련 오류를 나타내는 OS Error 입니다. 첨부된 오류 메시지를 읽으면 모델의 *config.json* 파일에 문제가 있는 것으로 보이며 이를 수정하기 위해 두 가지 선택지가 있습니다:

```python out
"""
make sure that:

- 'lewtun/distillbert-base-uncased-finetuned-squad-d5716d28' is a correct model identifier listed on 'https://huggingface.co/models'

- or 'lewtun/distillbert-base-uncased-finetuned-squad-d5716d28' is the correct path to a directory containing a config.json file
"""
```

<Tip>

💡 이해하기 어려운 에러 메시지를 접하게 된다면, 메세지를 복사해서 Google 또는 [스택오버플로우](https://stackoverflow.com/) 검색창에 붙여 넣기만 하세요(네 진짭니다!). 이는 오류가 발생한 첫 사람이 아닐 가능성이 높을뿐더러, 커뮤니티의 다른 사람들이 게시한 솔루션을 찾는 좋은 방법입니다. 예를 들어, 스택오버플로우에서 'OSError: Can't load config for'를 검색하면 여러 [해답](https://stackoverflow.com/search?q=OSError%3A+Can%27t+load+config+for+)을 제공하며 문제 해결을 위한 출발점으로 사용할 수 있습니다.

</Tip>

첫 번째 제안은 모델 ID가 실제로 정확한지 확인하도록 요청하는 것으로 비즈니스의 첫 순서는 식별자(모델 이름)를 복사하여 Hub의 검색 창에 붙여넣는 것입니다:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter8/wrong-model-id.png" alt="The wrong model name." width="100%"/>
</div>

음, 동료의 모델이 허브에 없는 것 같습니다... 아하, 모델의 이름에 오타가 있었습니다! DistilBERT는 이름에 "l"이 하나만 있으므로 이를 수정하고 대신 "lewtun/distilbert-base-uncased-finetuned-squad-d5716d28"을 찾아보겠습니다:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter8/true-model-id.png" alt="The right model name." width="100%"/>
</div>

좋습니다, 성공했군요. 이제 올바른 모델 ID로 모델을 다시 다운로드 해봅시다:

```python
model_checkpoint = get_full_repo_name("distilbert-base-uncased-finetuned-squad-d5716d28")
reader = pipeline("question-answering", model=model_checkpoint)
```

```python out
"""
OSError: Can't load config for 'lewtun/distilbert-base-uncased-finetuned-squad-d5716d28'. Make sure that:

- 'lewtun/distilbert-base-uncased-finetuned-squad-d5716d28' is a correct model identifier listed on 'https://huggingface.co/models'

- or 'lewtun/distilbert-base-uncased-finetuned-squad-d5716d28' is the correct path to a directory containing a config.json file
"""
```

아오 또 실패입니다. 머신러닝 엔지니어의 일상에 오신 것을 환영합니다! 모델 ID를 수정했으므로 문제는 저장소 자체에 있어야 합니다. 🤗 Hub의 저장소 컨텐츠에 빠르게 액세스하는 방법은 `huggingface_hub` 라이브러리의 `list_repo_files()` 함수를 사용하는 것입니다:

```python
from huggingface_hub import list_repo_files

list_repo_files(repo_id=model_checkpoint)
```

```python out
['.gitattributes', 'README.md', 'pytorch_model.bin', 'special_tokens_map.json', 'tokenizer_config.json', 'training_args.bin', 'vocab.txt']
```

흥미롭네요 -- 이 저장소에는 *config.json*가 보이지 않습니다! 우리의 `pipeline`이 모델을 불러올 수 없는 것이 당연했군요; 동료가 파인튜닝 후에 허브에 푸시하는 것을 잊어버린 모양입니다. 이 경우, 문제는 매우 간단하게 해결할 수 있습니다: 동료에게 파일을 추가하도록 요청하거나, 사전 훈련(pretrained)된 모델이 [`distilbert-base-uncased`](https://huggingface.co/distilbert-base-uncased)인 것을 확인 할 수 있으므로, 이 모델에 대한 config를 다운로드하고 저장소에 푸시하여 문제가 해결되는지 확인할 수 있습니다. 시도 해봅시다. [단원 2](/course/chapter2)에서 배운 기술을 사용해 `AutoConfig` 클래스로 모델의 config 파일을 다운로드할 수 있습니다.

```python
from transformers import AutoConfig

pretrained_checkpoint = "distilbert-base-uncased"
config = AutoConfig.from_pretrained(pretrained_checkpoint)
```

<Tip warning={true}>

🚨 여기에서 하는 접근 방식은 동료가 'distilbert-base-uncased'의 config를 수정했을 수 있으므로 이 접근 방식은 완전하지 않습니다. 우리는 동료에게 먼저 확인하고 싶겠지만, 이번 장에서의 목적상, 동료가 디폴트 config를 사용했다고 가정하겠습니다.

</Tip>

그런 다음 config 클래스의 `push_to_hub()` 기능을 사용해서 config 파일을 모델 저장소로 푸시할 수 있습니다:
We can then push this to our model repository with the configuration's `push_to_hub()` function:

```python
config.push_to_hub(model_checkpoint, commit_message="Add config.json")
```

이제 `main` 브랜치의 최신 커밋에서 모델을 로드해서 작동 여부를 테스트할 수 있습니다:

```python
reader = pipeline("question-answering", model=model_checkpoint, revision="main")

context = r"""
Extractive Question Answering is the task of extracting an answer from a text
given a question. An example of a question answering dataset is the SQuAD
dataset, which is entirely based on that task. If you would like to fine-tune a
model on a SQuAD task, you may leverage the
examples/pytorch/question-answering/run_squad.py script.

🤗 Transformers is interoperable with the PyTorch, TensorFlow, and JAX
frameworks, so you can use your favourite tools for a wide variety of tasks!
"""

question = "What is extractive question answering?"
reader(question=question, context=context)
```

```python out
{'score': 0.38669535517692566,
 'start': 34,
 'end': 95,
 'answer': 'the task of extracting an answer from a text given a question'}
```

유후, 동작하네요! 방금 배운 내용을 요약 해보겠습니다:

- Python의 오류 메시지는 _tracebacks_로 알려져 있으며 아래에서 위로 읽습니다. 오류 메시지의 마지막 줄에는 일반적으로 문제의 원인을 찾는 데 필요한 정보가 포함되어 있습니다.
- 마지막 줄에 충분한 정보가 포함되어 있지 않으면 traceback을 위로 훑어보고 소스 코드에서 오류가 발생한 위치를 식별할 수 있는지 확인합니다.
- 오류 메시지가 문제를 디버그하는 데 도움이 되지 않으면 온라인에서 유사한 문제에 대한 해결책을 검색해 보세요.
- `huggingface_hub` 
// 🤗 Hub?
이 라이브러리는 허브의 저장소와 상호 작용하고 디버그하는 데 사용할 수 있는 툴들을 제공합니다.

이제 파이프라인을 디버깅하는 방법을 알았으니 모델 자체의 forward pass에서 더 까다로운 예를 살펴보겠습니다.

## 모델의 foward pass 디버깅

'pipeline'은 빠르게 예측을 생성해야 하는 대부분의 애플리케이션에 적합하지만 때로는 모델의 logits값에 접근해야 할 수도 있습니다(예: 적용하려는 커스텀 후처리 과정이 있는 경우). 이 경우 무엇이 잘못될 수 있는지 알아보기 위해 먼저 `pipeline`에서 모델과 토크나이저를 가져와 보겠습니다:

```python
tokenizer = reader.tokenizer
model = reader.model
```
다음으로 질문이 필요합니다. 선호하는 프레임워크가 지원되는지 살펴보겠습니다:

```python
question = "Which frameworks can I use?"
```
[단원 7](/course/chapter7)에서 보았듯이 일반적인 단계는 입력을 토큰화하고 시작과 마지막 토큰의 logits를 추출한 다음 응답 부분을 디코딩하는 것입니다:

```python
import torch

inputs = tokenizer(question, context, add_special_tokens=True)
input_ids = inputs["input_ids"][0]
outputs = model(**inputs)
answer_start_scores = outputs.start_logits
answer_end_scores = outputs.end_logits
# Get the most likely beginning of answer with the argmax of the score
answer_start = torch.argmax(answer_start_scores)
# Get the most likely end of answer with the argmax of the score
answer_end = torch.argmax(answer_end_scores) + 1
answer = tokenizer.convert_tokens_to_string(
    tokenizer.convert_ids_to_tokens(input_ids[answer_start:answer_end])
)
print(f"Question: {question}")
print(f"Answer: {answer}")
```

```python out
"""
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
/var/folders/28/k4cy5q7s2hs92xq7_h89_vgm0000gn/T/ipykernel_75743/2725838073.py in <module>
      1 inputs = tokenizer(question, text, add_special_tokens=True)
      2 input_ids = inputs["input_ids"]
----> 3 outputs = model(**inputs)
      4 answer_start_scores = outputs.start_logits
      5 answer_end_scores = outputs.end_logits

~/miniconda3/envs/huggingface/lib/python3.8/site-packages/torch/nn/modules/module.py in _call_impl(self, *input, **kwargs)
   1049         if not (self._backward_hooks or self._forward_hooks or self._forward_pre_hooks or _global_backward_hooks
   1050                 or _global_forward_hooks or _global_forward_pre_hooks):
-> 1051             return forward_call(*input, **kwargs)
   1052         # Do not call functions when jit is used
   1053         full_backward_hooks, non_full_backward_hooks = [], []

~/miniconda3/envs/huggingface/lib/python3.8/site-packages/transformers/models/distilbert/modeling_distilbert.py in forward(self, input_ids, attention_mask, head_mask, inputs_embeds, start_positions, end_positions, output_attentions, output_hidden_states, return_dict)
    723         return_dict = return_dict if return_dict is not None else self.config.use_return_dict
    724
--> 725         distilbert_output = self.distilbert(
    726             input_ids=input_ids,
    727             attention_mask=attention_mask,

~/miniconda3/envs/huggingface/lib/python3.8/site-packages/torch/nn/modules/module.py in _call_impl(self, *input, **kwargs)
   1049         if not (self._backward_hooks or self._forward_hooks or self._forward_pre_hooks or _global_backward_hooks
   1050                 or _global_forward_hooks or _global_forward_pre_hooks):
-> 1051             return forward_call(*input, **kwargs)
   1052         # Do not call functions when jit is used
   1053         full_backward_hooks, non_full_backward_hooks = [], []

~/miniconda3/envs/huggingface/lib/python3.8/site-packages/transformers/models/distilbert/modeling_distilbert.py in forward(self, input_ids, attention_mask, head_mask, inputs_embeds, output_attentions, output_hidden_states, return_dict)
    471             raise ValueError("You cannot specify both input_ids and inputs_embeds at the same time")
    472         elif input_ids is not None:
--> 473             input_shape = input_ids.size()
    474         elif inputs_embeds is not None:
    475             input_shape = inputs_embeds.size()[:-1]

AttributeError: 'list' object has no attribute 'size'
"""
```

이런, 코드에 버그가 있는 것 같네요! 하지만 약간의 디버깅은 두렵지 않습니다. 노트북에서 파이썬 디버거를 사용 할 수 있습니다:

<Youtube id="rSPyvPw0p9k"/>

또는 터미널에서:

<Youtube id="5PkZ4rbHL6c"/>

여기에서 오류 메시지를 읽으면 `'list' 객체에는 'size' 속성이 없으며 `model(**inputs)'에서 문제가 발생한 라인을 가리키는 `-->` 화살표를 볼 수 있습니다. `.Python 디버거를 사용하여 대화식으로 디버그할 수 있지만 지금은 단순히 `inputs` 부분을 슬라이스하여 어떤 값이 있는지 볼 것입니다:

```python
inputs["input_ids"][:5]
```

```python out
[101, 2029, 7705, 2015, 2064]
```

확실히 일반적인 Python `list`처럼 보이지만 타입을 다시 확인합시다:

```python
type(inputs["input_ids"])
```

```python out
list
```

네, 확실히 파이썬의 `list`입니다. 무엇이 잘못되었을까요? [단원 2](/course/chapter2)에서 🤗 Transformers의 `AutoModelForXxx` 클래스는 _tensors_(PyTorch 또는 TensorFlow 포함)에서 작동하며 tensor의 dimensions를 추출하기 위해 일반적인 방법으로 PyTorch의 `Tensor.size()`를 활용합니다. 어떤 라인이 예외를 발생시켰는지 알아보기 위해 traceback을 다시 살펴보겠습니다:

```
~/miniconda3/envs/huggingface/lib/python3.8/site-packages/transformers/models/distilbert/modeling_distilbert.py in forward(self, input_ids, attention_mask, head_mask, inputs_embeds, output_attentions, output_hidden_states, return_dict)
    471             raise ValueError("You cannot specify both input_ids and inputs_embeds at the same time")
    472         elif input_ids is not None:
--> 473             input_shape = input_ids.size()
    474         elif inputs_embeds is not None:
    475             input_shape = inputs_embeds.size()[:-1]

AttributeError: 'list' object has no attribute 'size'
```

코드가 `input_ids.size()`를 호출하려고 하지만, Python `list`에서는 절대 동작하지 않습니다. 이 문제를 어떻게 해결할 수 있을까요? 스택오버플로우에서 오류 메시지를 검색하면 꽤 많은 관련 [해결책](https://stackoverflow.com/search?q=AttributeError%3A+%27list%27+object+has+no+attribute+%27size%27&s=c15ec54c-63cb-481d-a749-408920073e8f)을 제공합니다. 첫 번째 질문을 클릭하면 아래 스크린샷에 표시된 답변과 함께 우리와 유사한 질문이 표시됩니다:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter8/stack-overflow.png" alt="An answer from Stack Overflow." width="100%"/>
</div>

대답은 토크나이저에 `return_tensors='pt'`를 추가할 것을 권장하는데, 이게 작동하는지 확인해 보겠습니다:

```python out
inputs = tokenizer(question, context, add_special_tokens=True, return_tensors="pt")
input_ids = inputs["input_ids"][0]
outputs = model(**inputs)
answer_start_scores = outputs.start_logits
answer_end_scores = outputs.end_logits
# Get the most likely beginning of answer with the argmax of the score
answer_start = torch.argmax(answer_start_scores)
# Get the most likely end of answer with the argmax of the score
answer_end = torch.argmax(answer_end_scores) + 1
answer = tokenizer.convert_tokens_to_string(
    tokenizer.convert_ids_to_tokens(input_ids[answer_start:answer_end])
)
print(f"Question: {question}")
print(f"Answer: {answer}")
```

```python out
"""
Question: Which frameworks can I use?
Answer: pytorch, tensorflow, and jax
"""
```

잘 동작하네요! 이게 바로 스택오버플로우가 얼마나 유용한지 보여주는 좋은 예입니다. 유사한 문제를 식별하여 커뮤니티의 다른 사람들의 경험을 활용할 수 있었습니다. 그러나 이와 같은 검색이 항상 적절한 답변을 제공하는 것은 아닙니다. 이러한 경우에 무엇을 할 수 있을까요? 다행히도 [Hugging Face forums](https://discuss.huggingface.co/)에 여러분을 반기고 도와줄 수 있는 개발자 커뮤니티가 있습니다! 다음 장에서는 답변을 얻을 수 있는 좋은 포럼 질문을 만드는 방법을 살펴보겠습니다.

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

### 단원 소개[[introduction]]
https://huggingface.co/learn/course/ko/chapter5/1.md

# 단원 소개[[introduction]]

<CourseFloatingBanner
    chapter={5}
    classNames="absolute z-10 right-0 top-0"
/>

[챕터 3](/course/chapter3)에서는 🤗 Datasets 라이브러리를 처음 맛보면서 모델을 미세 조정하는 데 필요한 세 가지 주요 단계를 배웠습니다:

1. Hugging Face Hub에서 데이터셋을 로드한다.
2. `Dataset.map()` 함수를 통해 데이터셋을 전처리한다.
3. 평가 메트릭를 로드하고 계산한다.

하지만 지금까지 배운 것은 🤗 Datasets 라이브러리가 할 수 있는 것의 빙산의 일각입니다! 이번 챕터에서는 더 자세히 살펴볼 것이며, 그 과정에서 다음 질문에 대한 답을 찾을 수 있을 것입니다:

* 데이터셋이 Hub에 없을 때는 어떻게 할 것인가?
* 데이터셋을 어떻게 쪼개어 분석할 수 있는가? (그리고 Pandas를 반드시 사용해야만 한다면?)
* 데이터셋이 너무 방대하여 노트북 RAM을 초과하면 어떻게 할 것인가?
* 도대체 "메모리 매핑 (mmap)"과 Apache Arrow는 무엇인가?
* 당신 고유의 데이터셋을 만들어 Hub로 푸시하려면 어떻게 해야 하는가?

여기서 배운 기술을 통해 [챕터 6](/course/chapter6)과 [챕터 7](/course/chapter7)에서 심화 토큰화 기법 및 미세 조정 작업을 하는 데 활용할 예정이므로 커피 한 잔 하고 오신 뒤 시작하도록 하겠습니다!


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

### 필요한 데이터셋이 Hub에 없다면 어떻게 할까요?[[what-if-my-dataset-isnt-on-the-hub]]
https://huggingface.co/learn/course/ko/chapter5/2.md

# 필요한 데이터셋이 Hub에 없다면 어떻게 할까요?[[what-if-my-dataset-isnt-on-the-hub]]

<CourseFloatingBanner chapter={5}
  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/chapter5/section2.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter5/section2.ipynb"},
]} />

[Hugging Face Hub](https://huggingface.co/datasets)를 통해 데이터셋을 다운로드하는 방법은 배웠지만, Hub가 아닌 로컬 환경이나 원격 서버에 저장된 데이터로 작업해야하는 경우도 많이 있을 것입니다. 이번 섹션에서는 🤗 Datasets를 이용하여 Hugging Face Hub에 없는 데이터셋을 로딩하는 방법을 알려드리도록 하겠습니다.

<Youtube id="HyQgpJTkRdE"/>

## 로컬 또는 원격 데이터셋으로 작업하기[[working-with-local-and-remote-datasets]]

🤗 Datasets는 로컬 또는 원격 데이터셋 로딩을 다루기 위한 스크립트를 제공하며 다음과 같이 몇 가지 일반적인 데이터 형식들을 지원합니다:

|    Data format     | Loading script |                         Example                         |
| :----------------: | :------------: | :-----------------------------------------------------: |
|     CSV & TSV      |     `csv`      |     `load_dataset("csv", data_files="my_file.csv")`     |
|     Text files     |     `text`     |    `load_dataset("text", data_files="my_file.txt")`     |
| JSON & JSON Lines  |     `json`     |   `load_dataset("json", data_files="my_file.jsonl")`    |
| Pickled DataFrames |    `pandas`    | `load_dataset("pandas", data_files="my_dataframe.pkl")` |

테이블에서 볼 수 있듯이, 파일에 대한 경로를 지정하는 `data_files` 인수와 함께 `load_dataset()` 함수에 각 데이터 형식에 맞는 로딩 스크립트 유형을 지정해 주기만하면 됩니다. 지금부터 로컬 파일에서 데이터셋을 로딩을 해보는 것으로 시작해서 원격 파일로 동일한 작업을 수행하는 방법을 살펴보도록 하겠습니다.

## 로컬 데이터셋 로딩하기[[loading-a-local-dataset]]

이번 예제에서는 이탈리아어 질의응답 (QA) 대규모 데이터셋인 [SQuAD-it](https://github.com/crux82/squad-it/)를 사용하겠습니다.

훈련 및 테스트 데이터는 Github에 호스팅되어 있으므로, 간단한 `wget` 명령어를 통해 다운로드 할 수 있습니다:

```python
!wget https://github.com/crux82/squad-it/raw/master/SQuAD_it-train.json.gz
!wget https://github.com/crux82/squad-it/raw/master/SQuAD_it-test.json.gz
```

이렇게하면 *SQuAD_it-train.json.gz*, *SQuAD_it-test.json.gz* 두 개의 압축 파일을 다운로드 받을 수 있고, `gzip` 리눅스 명령어를 통해 압축을 풀 수 있습니다:

```python
!gzip -dkv SQuAD_it-*.json.gz
```

```bash
SQuAD_it-test.json.gz:	   87.4% -- replaced with SQuAD_it-test.json
SQuAD_it-train.json.gz:	   82.2% -- replaced with SQuAD_it-train.json
```

명령어를 통해 압축 파일들이 각각 _SQuAD_it-train.json_, _SQuAD_it-test.json_ 로 바뀌어 저장되어 있는 것을 볼 수 있습니다.

<Tip>

✎ 위 쉘 명령어에 `!`가 붙는 이유는 주피터 노트북에서 실행하고 있기 때문입니다. 터미널에서 명령어를 실행한다면 `!`를 없애주시면 됩니다.

</Tip>

`load_dataset()` 함수로 JSON 파일을 로딩할 때, 파일이 일반 JSON (nested dictionary와 유사)으로 형태인 지 혹은 JSON Lines (줄로 구분된 JSON)형태인 지 알아야 합니다. 많은 질의응답 데이터셋처럼, SQuAD-it 데이터셋은 `data` 필드에 텍스트가 모두 저장된 nested 포맷을 사용하며, 이는 다음과 같이 `field` 인수를 지정하여 데이터셋을 로딩할 수 있음을 의미합니다:

```py
from datasets import load_dataset

squad_it_dataset = load_dataset("json", data_files="SQuAD_it-train.json", field="data")
```

디폴트로 로컬 파일을 로딩하면 `train` 스플릿을 가진 `DatasetDict` 객체가 생성됩니다. `squad_it_dataset` 객체를 통해 이를 확인할 수 있습니다:

```py
squad_it_dataset
```

```python out
DatasetDict({
    train: Dataset({
        features: ['title', 'paragraphs'],
        num_rows: 442
    })
})
```

이것은 `train` 스플릿에 대한 행의 수 (num_rows)와 열의 이름 (features)을 보여줍니다. 또한 다음과 같이 `train` 스플릿을 인덱싱하여 한 예제를 볼 수 있습니다:

```py
squad_it_dataset["train"][0]
```

```python out
{
    "title": "Terremoto del Sichuan del 2008",
    "paragraphs": [
        {
            "context": "Il terremoto del Sichuan del 2008 o il terremoto...",
            "qas": [
                {
                    "answers": [{"answer_start": 29, "text": "2008"}],
                    "id": "56cdca7862d2951400fa6826",
                    "question": "In quale anno si è verificato il terremoto nel Sichuan?",
                },
                ...
            ],
        },
        ...
    ],
}
```

훌륭하게 첫 번째 로컬 데이터셋을 로딩했습니다! 하지만 우리가 하고 싶은 것은 `train`과 `test` 스플릿을 모두 지닌 하나의 `DatasetDict` 객체를 만들어 두 스플릿에 동시에 `Dataset.map()` 함수를 적용하는 것입니다. 이를 위해, 각 스플릿 이름을 해당 스플릿과 관련된 파일에 매핑하는 dictionary를 `data_files` 인수로 줄 수 있습니다:

```py
data_files = {"train": "SQuAD_it-train.json", "test": "SQuAD_it-test.json"}
squad_it_dataset = load_dataset("json", data_files=data_files, field="data")
squad_it_dataset
```

```python out
DatasetDict({
    train: Dataset({
        features: ['title', 'paragraphs'],
        num_rows: 442
    })
    test: Dataset({
        features: ['title', 'paragraphs'],
        num_rows: 48
    })
})
```

이것이 바로 우리가 원했던 것입니다. 이제 다양한 전처리 기법을 적용하여 데이터를 정리하고, 리뷰를 토큰화하는 등의 작업을 수행할 수 있습니다.

<Tip>

`load_dataset()` 함수의 `data_files` 인수는 매우 유연하여 하나의 파일 경로 (str), 파일 경로들의 리스트 (list) 또는 스플릿 이름을 각 파일 경로에 매핑하는 dictionary를 값으로 받을 수 있습니다. 또한 Unix 쉘에서 사용하는 규칙에 따라 지정된 패턴과 일치하는 파일들을 glob할 수도 있습니다. (예를 들어, `data_files="*.json"`와 같이 설정하면 한 폴더에 있는 모든 JSON 파일들을 하나의 스플릿으로 glob할 수 있습니다.) 자세한 내용은 🤗 Datasets [documentation](https://huggingface.co/docs/datasets/loading#local-and-remote-files)에서 확인할 수 있습니다.

</Tip>

🤗 Datasets 로딩 스크립트는 입력 파일의 자동 압축 해제를 지원하기 때문에 실제로 사용할 때는 `data_files` 인수로 압축 파일을 입력하면 `gzip` 사용을 생략할 수 있습니다.

```py
data_files = {"train": "SQuAD_it-train.json.gz", "test": "SQuAD_it-test.json.gz"}
squad_it_dataset = load_dataset("json", data_files=data_files, field="data")
```

이는 많은 GZIP 파일들을 수동으로 압축 해제하고 싶지 않을 때 유용하며, ZIP, TAR과 같은 일반적인 포맷들도 지원하므로 `data_files`에 해당 파일을 입력해 주기만 하면 됩니다.

노트북이나 데스크탑에서 로컬 파일을 로딩하는 방법을 알아봤으므로, 이제 원격 파일을 로딩하는 방법을 살펴보겠습니다.

## 원격 데이터셋 로딩하기[[loading-a-remote-dataset]]

만약 당신이 회사에서 데이터 과학자 또는 개발자로 일하고 있다면 분석하려는 데이터셋이 원격 서버에 저장되어 있을 가능성이 큽니다. 다행히, 원격 파일을 로딩하는 것은 로컬 파일을 로딩하는 것만큼 간단합니다! 로컬 파일 경로를 입력하는 대신, `load_dataset()`의 `data_files` 인수에 원격 파일들이 저장되어 있는 하나 또는 그 이상의 URL을 입력해주기만 하면 됩니다. 예를 들어, GitHub에서 호스팅하고 있는 SQuAD-it 데이터셋의 경우, `data_files`에 다음과 같이 _SQuAD_it-*.json.gz_ 을 입력하면 됩니다:

```py
url = "https://github.com/crux82/squad-it/raw/master/"
data_files = {
    "train": url + "SQuAD_it-train.json.gz",
    "test": url + "SQuAD_it-test.json.gz",
}
squad_it_dataset = load_dataset("json", data_files=data_files, field="data")
```

이와 같은 방법을 통해 위에서 얻은 것과 같이 `DatasetDict` 객체를 생성할 수 있으며 수동으로 파일들을 다운로드받고 압축을 푸는 작업을 생략할 수 있습니다. 지금까지 Hugging Face Hub에서 호스팅되어 있지 않은 데이터셋을 로딩하는 다양한 방법을 알아보았습니다. 이제 다뤄볼 데이터셋을 얻었으니 다양한 데이터 전처리 기법을 배워보도록 합시다.

<Tip>

✏️ **시도해 보세요!** GitHub 또는 [UCI Machine Learning Repository](https://archive.ics.uci.edu/ml/index.php)에서 호스팅되는 다른 데이터셋을 선택하여 위에서 배운 기술을 통해 로컬 또는 원격으로 로딩해 보시고, 추가적으로 CSV 또는 텍스트 포맷으로 저장된 데이터셋도 로딩을 시도해 보시길 바랍니다. (이 포맷들에 대한 자세한 정보는 [documentation](https://huggingface.co/docs/datasets/loading#local-and-remote-files)을 참조하세요.)

</Tip>




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

### 다중 시퀀스 처리[[handling-multiple-sequences]]
https://huggingface.co/learn/course/ko/chapter2/5.md

# 다중 시퀀스 처리[[handling-multiple-sequences]]

{#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/section5_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter2/section5_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/section5_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter2/section5_tf.ipynb"},
]} />

{/if}

{#if fw === 'pt'}
<Youtube id="M6adb1j2jPI"/>
{:else}
<Youtube id="ROxrFOEbsQE"/>
{/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
<tf.Tensor: shape=(1, 16), dtype=int32, numpy=
array([[  101,  1045,  1005,  2310,  2042,  3403,  2005,  1037, 17662,
        12172,  2607,  2026,  2878,  2166,  1012,   102]], dtype=int32)>
```
{/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개로 만든 배치입니다!

<Tip>

✏️ **직접 해보세요!** 이 `batched_ids` 리스트를 텐서로 변환하고 모델로 전달해보세요. 이전에 얻은 로짓과 동일한 결과를 얻는지 확인해보세요. (개수는 두 개여야 합니다!)

</Tip>

배치는 여러 개의 문장을 모델로 넘겼을 때도 모델이 작동하게 합니다. 다중 시퀀스를 사용하는 것은 단일 시퀀스로 배치를 만드는 것만큼 간단합니다. 하지만 두 번째 문제가 있습니다. 두 개 이상의 문장을 배치로 만드려고 할 때, 그 문장들은 아마 다른 길이를 가지고 있을 것입니다. 이전에 텐서를 다뤄본 사람이라면, 텐서의 형태가 직사각형이어야 한다는 것을 알고 있습니다. 문장 길이가 다르면 입력 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=<AddmmBackward>)
tensor([[ 0.5803, -0.4125]], grad_fn=<AddmmBackward>)
tensor([[ 1.5694, -1.3895],
        [ 1.3373, -1.2163]], grad_fn=<AddmmBackward>)
```
{: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=<AddmmBackward>)
```
{: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라는 것을 잊지 마세요.

<Tip>

✏️ **직접 해보세요!** 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!

</Tip>

## 길이가 긴 시퀀스[[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]
```


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

### 단원 소개[[introduction]]
https://huggingface.co/learn/course/ko/chapter2/1.md

# 단원 소개[[introduction]]

<CourseFloatingBanner
    chapter={2}
    classNames="absolute z-10 right-0 top-0"
/>

[제1단원](/course/chapter1)에서 보았듯이, 트랜스포머 모델은 대부분 매우 큽니다. 수백만에서 *수백억*개의 파라미터를 가진 모델을 훈련시키고 배포하는 것은 만만치 않은데다가, 하루가 멀다하고 자체적으로 구현된 새로운 모델이 출시되어서, 모두 적용해보려고 한다면 쉽지는 않을 거예요.

🤗 Transformers 라이브러리는 이 문제를 해결하기 위해 만들어졌습니다. Transformer 모델을 가져오고, 훈련시킨 후 저장할 수 있는 단일 API를 제공하는 것이 목표예요. 라이브러리의 주요 기능은 다음과 같습니다.

- **사용 편의성**: 추론하기 위해 최첨단 NLP 모델을 다운로드한 다음 적재시켜 사용하고 싶다면, 단 2줄의 코드만으로 할 수 있어요.
- **유연성**: 기초적으로 보면 모든 모델은 단순한 PyTorch `nn.module` 또는 TensorFlow `tf.keras.Model` 클래스입니다. 각 머신러닝(ML) 프레임워크의 여타 다른 모델이나 마찬가지로 처리할 수 있다는 뜻이에요.
- **단순성**: 라이브러리 위에 추상화를 거의 하지 않았어요. "모든 것을 파일 하나에"가 핵심 개념입니다. 모델의 순전파(forward propagation) 부분이 파일 한 개에 모두 정의되어 있어서, 코드 자체를 이해하고 해킹할 수도 있어요.

마지막 기능은 여타 ML 라이브러리들과는 다른 🤗 Transformers만의 차별점입니다. 모델은 파일 간에 공유되는 모듈로 만들어지지 않고, 모델마다 자체적인 레이어를 쌓습니다. 이렇게 하면 모델을 더 쉽게 보고 이해할 수 있으면서도, 다른 모델과는 상관없이 원하는 모델에서 마음껏 실험해볼 수 있습니다.

이 단원은 모델과 토크나이저로 [제1단원](/course/chapter1)에서 소개된 `pipeline()` 함수를 처음부터 끝까지 만들어보는 것으로 시작합니다. 만들고나면 모델 API를 더 깊게 탐구해봅니다. model과 configuration 클래스를 알아보고, 모델을 적재하는 방법과 수치를 입력으로 제공해서 예측이 출력되는 처리 과정을 보여드리겠습니다.

그런 다음 `pipeline()` 함수의 중요한 구성요소인 tokenizer API를 살펴보겠습니다. tokenizer는 처리의 첫 번째 단계인 텍스트를 신경망의 수치 입력으로 바꾸는 부분과 필요할 때 다시 텍스트로 바꾸는 마지막 단계, 즉 양끝을 다룹니다. 마지막으로 여러 문장을 묶어서 모델에게 제공하는 방법을 알아보고, 기존 `tokenizer()` 함수를 자세히 살펴봄으로써 마무리짓겠습니다.

<Tip>
⚠️ Model Hub와 🤗 Transformers에서 사용할 수 있는 모든 기능을 활용하려면 <a href="https://huggingface.co/join">계정을 만드는 게</a> 좋습니다.
</Tip>


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

### Models
https://huggingface.co/learn/course/ko/chapter2/3.md

# Models

{#if fw === 'pt'}

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

{:else}

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

{/if}

{#if fw === 'pt'}
<Youtube id="AhChOFRegn4"/>
{:else}
<Youtube id="d3JVgghSOew"/>
{/if}

{#if fw === 'pt'}

이번 섹션에서는 모델을 생성하고 사용하는 방법에 대해 자세히 알아보겠습니다. 체크포인트에서 모델을 인스턴스화하는 데 유용한 `AutoModel` 클래스를 사용할 것입니다.

이 `AutoModel` 클래스와 관련 클래스들은 실제로 라이브러리에 있는 다양한 모델들을 감싸고 있는 간단한 래퍼입니다. 이 래퍼는 체크포인트에 적합한 모델 아키텍처를 자동으로 추측하고, 이 아키텍처를 가진 모델을 인스턴스화하는 것도 똑똑하게 처리합니다.

{:else}
이번 섹션에서는 모델을 생성하고 사용하는 방법에 대해 자세히 알아보겠습니다. 체크포인트에서 모델을 인스턴스화하는 데 유용한 `TFAutoModel` 클래스를 사용할 것입니다.

이 `TFAutoModel` 클래스와 관련 클래스들은 실제로 라이브러리에 있는 다양한 모델들을 감싸고 있는 간단한 래퍼입니다. 이 래퍼는 체크포인트에 적합한 모델 아키텍처를 자동으로 추측하고, 이 아키텍처를 가진 모델을 인스턴스화하는 것도 똑똑하게 처리합니다.

{/if}

하지만, 만약 모델의 아키텍처를 직접 정의하고 싶다면, 해당 모델의 클래스를 사용할 수 있습니다. BERT 모델을 예로 들어보겠습니다.

## Creating a Transformer (Transformer 생성하기)

BERT 모델을 초기화 하기 위해서는 먼저 모델의 환경설정을 로드해야 합나다.

{#if fw === 'pt'}
```py
from transformers import BertConfig, BertModel

# Building the config
config = BertConfig()

# Building the model from the config
model = BertModel(config)
```
{:else}
```py
from transformers import BertConfig, TFBertModel

# Building the config
config = BertConfig()

# Building the model from the config
model = TFBertModel(config)
```
{/if}

이 환경설정은 모델을 구축하는데 사용되는 많은 속성들을 포함하고 있습니다:

```py
print(config)
```

```python out
BertConfig {
  [...]
  "hidden_size": 768,
  "intermediate_size": 3072,
  "max_position_embeddings": 512,
  "num_attention_heads": 12,
  "num_hidden_layers": 12,
  [...]
}
```

아직 이 속성들이 무엇을 의미하는지는 모르겠지만, 몇몇은 익숙할 것입니다: `hidden_size` 속성은 `hidden_states` 벡터의 크기를 정의하고, `num_hidden_layers`는 Transformer 모델이 가지고 있는 레이어의 수를 정의합니다.

### Different loading methods (다른 로딩 방법)

무작위 값을 통한 기본 환경설정으로 모델 생성

{#if fw === 'pt'}
```py
from transformers import BertConfig, BertModel

config = BertConfig()
model = BertModel(config)

# Model is randomly initialized!
```
{:else}
```py
from transformers import BertConfig, TFBertModel

config = BertConfig()
model = TFBertModel(config)

# Model is randomly initialized!
```
{/if}

이 모델은 무작위로 초기화되어 있기 때문에, 아직은 아무런 유용한 정보를 포함하고 있지 않습니다. 이 모델을 훈련시키기 위해서는, 먼저 훈련 데이터를 준비해야 합니다. 우리가 바닥부터 학습을 할 수 있지만, 이 과정은 [Chapter 1](/course/chapter1)에서 확인 했듯이, 많은 시간과 많은 데이터가 필요하며, 학습 환경에도 큰 영향을 미칩니다.

이러한 불필요한 중복된 노력을 피하기 위해서, 이미 훈련된 모델을 공유하고 재사용할 수 있어야 합니다.

훈련된 Transformer 모델을 불러오는 것은 매우 간단합니다 - `from_pretrained` 메소드를 사용하면 됩니다.

{#if fw === 'pt'}
```py
from transformers import BertModel

model = BertModel.from_pretrained("bert-base-cased")
```

이전에 봤듯이, `BertModel` 대신 `AutoModel` 클래스를 사용할 수도 있습니다. 이제부터는 체크포인트에 독립적인 코드를 생성하기 위해 `AutoModel`를 사용하겠습니다. 만약 코드가 한 체크포인트에서 잘 동작한다면, 다른 체크포인트에서도 잘 동작해야 합니다. 이는 체크포인트의 아키텍처가 다르더라도, 비슷한 작업(예를 들어, 감성 분석 작업)을 위해 훈련된 경우에도 적용됩니다.

{:else}
```py
from transformers import TFBertModel

model = TFBertModel.from_pretrained("bert-base-cased")
```

이전에 봤듯이, `TFBertModel` 대신 `TFAutoModel` 클래스를 사용할 수도 있습니다. 이제부터는 체크포인트에 독립적인 코드를 생성하기 위해 `TFAutoModel`를 사용하겠습니다. 만약 코드가 한 체크포인트에서 잘 동작한다면, 다른 체크포인트에서도 잘 동작해야 합니다. 이는 체크포인트의 아키텍처가 다르더라도, 비슷한 작업(예를 들어, 감성 분석 작업)을 위해 훈련된 경우에도 적용됩니다.

{/if}

이 코드 샘플에서는 `BertConfig`를 사용하지 않았고, 대신 `bert-base-cased` 식별자를 통해 사전 훈련된 모델을 불러왔습니다. 이는 BERT의 저자들이 직접 훈련시킨 체크포인트입니다. 자세한 내용은 [모델 카드](https://huggingface.co/bert-base-cased)에서 확인할 수 있습니다.

이 모델은 체크포인트의 모든 가중치로 초기화되었습니다. 이 모델은 체크포인트에서 훈련된 작업에 대해 직접 추론에 사용할 수 있으며, 새로운 작업에 대해 미세 조정할 수도 있습니다. 사전 훈련된 가중치로부터 학습을 진행하면, 빈 상태에서 훈련을 시작하는 것보다 빠르게 좋은 결과를 얻을 수 있습니다.

모델을 불러오는 또 다른 방법은 `from_pretrained()` 메서드를 사용하는 것입니다. 이 메서드는 체크포인트를 다운로드하고, 캐시에 저장합니다(이후 `from_pretrained()` 메서드를 호출할 때 다시 다운로드하지 않습니다). 캐시 폴더는 기본적으로 *~/.cache/huggingface/transformers*에 저장됩니다. 캐시 폴더를 사용자 정의하려면 `HF_HOME` 환경 변수를 설정하면 됩니다.

모델을 불러오는 식별자는 BERT 아키텍처와 호환되는 경우 모델 허브의 모든 모델의 식별자가 될 수 있습니다. BERT 체크포인트의 전체 목록은 [여기](https://huggingface.co/models?filter=bert)에서 확인할 수 있습니다.

### Saving methods (저장 방법)

모델을 저장하는 방법은 불러오는 방법처럼 쉽습니다. `save_pretrained()` 메서드를 사용하면 됩니다. 이 메서드는 `from_pretrained()` 메서드와 유사합니다.

```py
model.save_pretrained("directory_on_my_computer")
```

이는 2가지 파일을 저장하게 됩니다:

{#if fw === 'pt'}
```
ls directory_on_my_computer

config.json model.safetensors
```
{:else}
```
ls directory_on_my_computer

config.json tf_model.h5
```
{/if}

*config.json* 파일은 모델 아키텍처를 구축하는 데 필요한 속성을 알려줍니다. 이 파일에는 체크포인트가 어디에서 생성되었는지, 마지막으로 체크포인트를 저장할 때 사용한 🤗 Transformers 버전 등의 메타데이터도 포함되어 있습니다.

{#if fw === 'pt'}
The *model.safetensors* file is known as the *state dictionary*; it contains all your model's weights. The two files go hand in hand; the configuration is necessary to know your model's architecture, while the model weights are your model's parameters.

{:else}
The *tf_model.h5* file is known as the *state dictionary*; it contains all your model's weights. The two files go hand in hand; the configuration is necessary to know your model's architecture, while the model weights are your model's parameters.

{/if}

## Using a Transformer model for inference (Transformer 모델을 추론에 사용하기)

Now that you know how to load and save a model, let's try using it to make some predictions. Transformer models can only process numbers — numbers that the tokenizer generates. But before we discuss tokenizers, let's explore what inputs the model accepts.

이제 모델을 불러오고 저장하는 방법을 알았으니, 모델을 사용하여 예측을 만들어 보겠습니다. Transformer 모델은 토크나이저가 생성하는 숫자만 처리할 수 있습니다. 그러나 토크나이저에 대해 논의하기 전에 모델이 받는 입력에 대해 알아보겠습니다.

토크나이저는 입력을 적절한 프레임워크의 텐서로 변환할 수 있지만, 이해도를 높이기 위해 모델에 입력을 보내기 전 무엇을 반드시 해야 하는지 간단히 살펴보겠습니다.

우리가 여러 시퀀스들이 있다고 가정해 봅시다:

```py
sequences = ["Hello!", "Cool.", "Nice!"]
```

토크나이저는 이를 단어 인덱스로 변환합니다. 이를 *input IDs*라고 합니다. 각 시퀀스는 이제 숫자 목록입니다! 결과는 다음과 같습니다:

```py no-format
encoded_sequences = [
    [101, 7592, 999, 102],
    [101, 4658, 1012, 102],
    [101, 3835, 999, 102],
]
```

이는 인코딩된 시퀀스의 목록입니다. 텐서는 정사각형 모양만 받을 수 있습니다 (행렬을 생각해 보세요). 이 "배열"은 이미 정사각형 모양이므로 텐서로 변환하는 것은 쉽습니다:

This is a list of encoded sequences: a list of lists. Tensors only accept rectangular shapes (think matrices). This "array" is already of rectangular shape, so converting it to a tensor is easy:

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

model_inputs = torch.tensor(encoded_sequences)
```
{:else}
```py
import tensorflow as tf

model_inputs = tf.constant(encoded_sequences)
```
{/if}

### Using the tensors as inputs to the model (텐서를 모델의 입력으로 사용하기)

모델의 텐서를 사용하는 것은 매우 간단합니다. 모델에 입력을 넣기만 하면 됩니다:

```py
output = model(model_inputs)
```

모델이 다양한 어규먼트를 받는 중에, 입력은 input IDs 만 필요합니다. 나머지 어규먼트들은 언제 필요한지, 어떤 역할을 하는지는 나중에 설명하겠습니다. 먼저 토크나이저에 대해 좀 더 자세히 알아보겠습니다.

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

### 한 번에 실행하기[[putting-it-all-together]]
https://huggingface.co/learn/course/ko/chapter2/6.md

# 한 번에 실행하기[[putting-it-all-together]]

{#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/section6_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter2/section6_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/section6_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter2/section6_tf.ipynb"},
]} />

{/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}


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

### 단원 마무리 퀴즈[[end-of-chapter-quiz]]
https://huggingface.co/learn/course/ko/chapter2/8.md

# 단원 마무리 퀴즈[[end-of-chapter-quiz]]

<CourseFloatingBanner
    chapter={2}
    classNames="absolute z-10 right-0 top-0"
/>

### 1. 언어 모델링 파이프라인은 어떤 순서로 진행될까요?

<Question
	choices={[
		{
			text: "먼저, 모델이 텍스트를 처리하고 원시 예측 결과를 출력합니다. 이어서 토크나이저가 이 예측 결과를 이해하고 텍스트로 변환합니다.",
			explain: "모델은 텍스트를 이해할 수 없습니다! 먼저 토크나이저가 텍스트를 토큰화하고 ID로 변환해야만 모델이 이해할 수 있습니다."
		},
		{
			text: "먼저, 토크나이저가 텍스트를 처리하고 ID를 반환합니다. 모델은 ID를 이용해 텍스트로 된 예측 결과를 출력합니다.",
			explain: "모델의 예측 결과가 텍스트 형태로 바로 나올 수는 없습니다. 예측을 텍스트로 변환하기 위해 토크나이저를 사용해야 합니다!"
		},
		{
			text: "토크나이저가 텍스트를 처리하고 ID를 반환합니다. 모델은 ID를 이용해 예측 결과를 출력합니다. 그리고 토크나이저를 다시 사용해 예측 결과를 일부 텍스트로 변환할 수 있습니다.",
			explain: "정답! 토크나이저는 토큰화와 역토큰화에 사용될 수 있습니다.",
            correct: true
		}
	]}
/>

### 2. 기본 Transformer 모델에 의해 만들어지는 텐서의 출력은 몇 차원이며, 각 텐서가 무엇을 의미하나요?

<Question
	choices={[
		{
			text: "2: 문장 길이, 배치 크기",
			explain: "틀렸습니다! 모델에 의한 텐서 출력은 은닉 크기를 포함한 3차원입니다."
		},
		{
			text: "2: 문장 길이, 은닉 크기",
			explain: "틀렸습니다! 모든 Transformer 모델은 단일 시퀀스에서도 크기 1로 배치를 처리합니다!"
		},
		{
			text: "3: 문장 길이, 배치 크기, 은닉 크기",
			explain: "정답!",
            correct: true
		}
	]}
/>

### 3. 서브워드 토큰화 예시에 해당하는 것은 무엇인가요?

<Question
	choices={[
		{
			text: "WordPiece",
			explain: "정답입니다. 서브워드 토큰화 예시 중 하나에 해당합니다!",
            correct: true
		},
		{
			text: "문자 기반 토큰화",
			explain: "문자 기반 토큰화는 서브워드 토큰화 방법이 아닙니다."
		},
		{
			text: "공백과 구두점을 기준으로 나누기",
			explain: "단어 기반 토큰화 방법입니다!"
		},
		{
			text: "BPE",
			explain: "정답입니다. 서브워드 토큰화 예시 중 하나에 해당합니다!",
            correct: true
        },
		{
			text: "Unigram",
			explain: "정답입니다. 서브워드 토큰화 예시 중 하나에 해당합니다!",
            correct: true
        },
		{
			text: "해당하는 것이 없습니다.",
			explain: "오답!"
        }
	]}
/>

### 4. 모델 헤드가 무엇인가요?

<Question
	choices={[
		{
			text: "기본 Transformer 네트워크의 요소로, 텐서를 적합한 레이어로 리디렉션합니다.",
			explain: "오답입니다! 그런 요소는 없습니다."
		},
		{
			text: "셀프 어텐션 메커니즘이라고도 부르며, 시퀀스 내 다른 토큰에 따라 토큰의 표현을 조정합니다.",
			explain: "오답입니다! 셀프 어텐션 레이어는 어텐션 \"헤드,\"를 포함하고 있지만 어텐션 헤드가 적응 헤드는 아닙니다."
		},
		{
			text: "하나 또는 여러 개의 레이어로 이루어진 추가적인 요소로 트랜스포머의 예측 결과를 task-specific한 출력으로 변환합니다.",
			explain: "정답입니다. 헤드라고 알려진 적응 헤드는 언어 모델링 헤드, 질의 응답 헤드, 순차 분류 헤드 등과 같이 다양한 형태로 나타납니다.",
			correct: true
		} 
	]}
/>

{#if fw === 'pt'}
### 5. AutoModel이 무엇인가요?

<Question
	choices={[
		{
			text: "데이터를 자동으로 학습하는 모델입니다.",
			explain: "오답입니다. <a href='https://huggingface.co/autotrain'>AutoTrain</a>과 헷갈린 게 아닐까요?"
		},
		{
			text: "체크포인트에 기반하여 적합한 구조를 반환하는 객체입니다.",
			explain: "정확합니다. <code>AutoModel</code>은 적절한 모델 구조를 초기화할 때 필요한 체크포인트만을 필요로 합니다.",
			correct: true
		},
		{
			text: "적합한 가중치를 불러오기 위해 입력에 사용된 언어를 자동으로 감지하는 모델입니다.",
			explain: "오답입니다. 몇몇 체크포인트와 모델은 다양한 언어를 처리할 수 있지만, 언어에 따라 체크포인트를 자동으로 선택할 수 있도록 내장된 도구는 없습니다. 태스크에 가장 적합한 체크포인트를 찾으려면 <a href='https://huggingface.co/models'>모델 허브</a>에 가보세요!"
		} 
	]}
/>

{:else}
### 5. TFAutoModel이 무엇인가요?

<Question
	choices={[
		{
			text: "데이터를 자동으로 학습하는 모델입니다.",
			explain: "오답입니다. <a href='https://huggingface.co/autotrain'>AutoTrain</a>과 헷갈린 게 아닐까요?"
		},
		{
			text: "체크포인트에 기반하여 적합한 구조를 반환하는 객체입니다.",
			explain: "정확합니다. <code>AutoModel</code>은 적절한 모델 구조를 초기화할 때 필요한 체크포인트만을 필요로 합니다.",
			correct: true
		},
		{
			text: "적합한 가중치를 불러오기 위해 입력에 사용된 언어를 자동으로 감지하는 모델입니다.",
			explain: "오답입니다. 몇몇 체크포인트와 모델은 다양한 언어를 처리할 수 있지만, 언어에 따라 체크포인트를 자동으로 선택할 수 있도록 내장된 도구는 없습니다. 태스크에 가장 적합한 체크포인트를 찾으려면 <a href='https://huggingface.co/models'>모델 허브</a>에 가보세요!"
		} 
	]}
/>

{/if}

### 6. 길이가 다른 시퀀스를 하나의 배치로 만들 때 신경써야 할 부분은 무엇일까요?

<Question
	choices={[
		{
			text: "잘라내기",
			explain: "정답입니다, 잘라내기는 시퀀스 길이를 동일하게 만들어서 텐서가 직사각형 형태를 가질 수 있게 하는 확실한 방법입니다. 하지만 이 방법 밖에 없을까요?",
			correct: true
		},
		{
			text: "텐서 반환하기",
			explain: "직사각형 텐서를 만들 수 있는 방법은 여러가지가 있겠지만, 텐서를 반환하는 것은 시퀀스를 하나의 배치로 만들 때 유용한 방법이 아닙니다."
		},
		{
			text: "패딩 추가하기",
			explain: "정답입니다, 패딩은 시퀀스 길이를 동일하게 만들어서 텐서가 직사각형 형태를 가질 수 있게 하는 확실한 방법입니다. 하지만 이 방법 밖에 없을까요?",
			correct: true
		}, 
		{
			text: "어텐션 마스킹",
			explain: "정확합니다! 어텐션 마스크는 다른 길이의 시퀀스를 처리할 때 매우 중요합니다. 하지만 어텐션 마스킹이 신경써야 하는 유일한 부분은 아닙니다.",
			correct: true
		} 
	]}
/>

### 7. 시퀀스 분류 모델의 로짓 출력 결과에 소프트맥스 함수를 적용하는 핵심적인 이유는 무엇일까요?

<Question
	choices={[
		{
			text: "소프트맥스 함수는 더 신뢰성 있는 로짓 결과를 만들기 위해 정규화를 수행합니다.",
			explain: "오답입니다, 소프트맥스 함수는 결과의 신뢰도에 영향을 주지 않습니다."
		},
		{
			text: "소프트맥스 함수는 상한과 하한을 적용해서 로짓 결과를 이해하기 쉽게 만듭니다.",
			explain: "정답입니다! 소프트맥스 함수를 거친 로짓 값은 0과 1 사이 값으로 바뀌게 됩니다. 하지만 이 이유가 소프트맥스 함수를 사용하는 유일한 이유는 아닙니다.",
            correct: true
		},
		{
			text: "출력 결과의 총 합이 1이므로 확률적으로 해석이 가능합니다.",
			explain: "정답입니다! 하지만 이 이유가 소프트맥스 함수를 사용하는 유일한 이유는 아닙니다.",
            correct: true
		}
	]}
/>

### 8. 토크나이저 API의 가장 핵심적인 메서드는 무엇일까요?

<Question
	choices={[
		{
			text: "텍스트를 ID로 인코딩하고 ID를 예측 결과로 인코딩하는 <code>encode</code>입니다.",
			explain: "틀렸습니다! <code>encode</code> 메서드가 토크나이저에는 있지만, 모델에는 없습니다."
		},
		{
			text: "토크나이저 객체를 바로 호출하는 메서드입니다.",
			explain: "정확합니다! 토크나이저의 <code>__call__</code> 메서드는 거의 모든 것을 처리할 수 있는 강력한 메서드입니다. 이 메서드는 또한 모델로부터 예측 결과를 탐색하는 데에 사용되기도 합니다.",
			correct: true
		},
		{
			text: "<code>pad</code> 메서드입니다.",
			explain: "틀렸습니다! 패딩은 매우 유용한 방법이지만, 토크나이저 API가 제공하는 기능 중 하나일 뿐입니다."
		},
		{
			text: "<code>tokenize</code> 메서드입니다.",
			explain: "<code>tokenize</code> 메서드가 유용한 것은 틀림없지만, 토크나이저 API의 핵심은 아닙니다."
		}
	]}
/>

### 9. 아래 코드 예시에서 `result` 변수가 포함하고 있는 것은 무엇일까요?

```py
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
result = tokenizer.tokenize("Hello!")
```

<Question
	choices={[
		{
			text: "문자열 리스트로, 각 문자열은 토큰입니다.",
			explain: "아주 정확합니다! 토큰을 ID로 변환하고 모델로 넘겨 보세요!",
            correct: true
		},
		{
			text: "ID 리스트입니다.",
			explain: "오답입니다. ID 리스트를 만드는 메서드는 <code>__call__</code> 또는 <code>convert_tokens_to_ids</code> 입니다!"
		},
		{
			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)
```

<Question
	choices={[
		{
			text: "아니요, 모두 제대로 되어 있습니다.",
			explain: "안타깝게도, 서로 다른 체크포인트로 학습된 토크나이저와 모델을 함께 사용하는 것은 좋은 방법이 아닙니다. 모델은 토크나이저의 출력 결과를 이해할 수 있도록 학습되지 않았기 때문에 모델이 동작하더라도 모델의 출력을 이해할 수 없을 것입니다."
		},
		{
			text: "토크나이저와 모델은 같은 체크포인트를 사용해야 합니다.",
			explain: "정답입니다!",
            correct: true
		},
		{
			text: "토크나이저를 이용해 패딩을 추가하거나 시퀀스 길이를 잘라내는 것은 모든 입력이 배치로 처리될 때 좋은 방법입니다.",
			explain: "모든 모델의 입력이 배치로 들어가야 하는 것은 맞습니다. 하지만 주어진 시퀀스가 하나뿐이기 때문에 이 예시에서 필수적인 부분은 아닙니다. 시퀀스를 잘라내거나 시퀀스에 패딩을 추가하는 것은 여러 개의 문장으로 배치를 만들 때 필요한 기술입니다."
		}
	]}
/>

{: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)
```

<Question
	choices={[
		{
			text: "아니요, 모두 제대로 되어 있습니다.",
			explain: "안타깝게도, 서로 다른 체크포인트로 학습된 토크나이저와 모델을 함께 사용하는 것은 좋은 방법이 아닙니다. 모델은 토크나이저의 출력 결과를 이해할 수 있도록 학습되지 않았기 때문에 모델이 동작하더라도 모델의 출력을 이해할 수 없을 것입니다."
		},
		{
			text: "토크나이저와 모델은 같은 체크포인트를 사용해야 합니다.",
			explain: "정답입니다!",
            correct: true
		},
		{
			text: "토크나이저를 이용해 패딩을 추가하거나 시퀀스 길이를 잘라내는 것은 모든 입력이 배치로 처리될 때 좋은 방법입니다.",
			explain: "모든 모델의 입력이 배치로 들어가야 하는 것은 맞습니다. 하지만 주어진 시퀀스가 하나뿐이기 때문에 이 예시에서 필수적인 부분은 아닙니다. 시퀀스를 잘라내거나 시퀀스에 패딩을 추가하는 것은 여러 개의 문장으로 배치를 만들 때 필요한 기술입니다."
		}
	]}
/>

{/if}


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

### 토크나이저[[tokenizers]]
https://huggingface.co/learn/course/ko/chapter2/4.md

# 토크나이저[[tokenizers]]

{#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/section4_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter2/section4_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/section4_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter2/section4_tf.ipynb"},
]} />

{/if}

<Youtube id="VFp38yj8h3A"/>

토크나이저는 자연어처리 파이프라인의 핵심 요소 중 하나입니다. 토크나이저의 역할은 텍스트를 모델이 처리할 수 있는 데이터로 변환하는 것입니다. 모델은 숫자만 처리할 수 있기 때문에 토크나이저는 텍스트 입력을 수치형 데이터로 변환해야 합니다. 이 장에서는 토큰화 파이프라인에서 정확히 어떤 일이 일어나고 있는지 알아볼 것입니다. 

자연어처리 태스크에서 처리되는 데이터는 일반적으로 원시 텍스트입니다. 아래는 원시 텍스트의 예시입니다.

```
Jim Henson was a puppeteer
```

그러나 모델은 숫자만 처리할 수 있기 때문에 우리는 원시 텍스트를 숫자로 바꿀 방법을 찾아야 합니다. 그게 바로 토크나이저가 하는 일이며 이 문제를 해결할 수 있는 다양한 방법이 있습니다. 목표는 모델에 가장 적합하면서 간결한 표현을 찾는 것입니다.

토큰화 알고리즘의 몇 가지 예시를 살펴보고 당신이 토큰화에 대해 가지는 궁금증에 대한 해답을 찾아봅시다.

## 단어 기반 토큰화[[word-based]]

<Youtube id="nhJxYji1aho"/>

가장 먼저 떠오르는 토큰화 유형은 _단어 기반_입니다. 몇 가지 규칙만으로도 설정 및 사용이 매우 쉽고 종종 괜찮은 결과를 출력합니다. 예를 들어, 아래 보이는 사진에서 목표는 원시 텍스트를 단어로 나누고 단어 각각에 대한 수치 표현을 찾는 것입니다.

<div class="flex justify-center">
  <img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter2/word_based_tokenization.svg" alt="An example of word-based tokenization."/>
  <img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter2/word_based_tokenization-dark.svg" alt="An example of word-based tokenization."/>
</div>

텍스트를 나누는 방법은 다양합니다. 예를 들면 파이썬의 `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]"이나 "<UNK>"로 표현됩니다. 토크나이저가 unknown 토큰을 많이 만드는 것은 단어에 적합한 표현을 찾지 못해 정보를 잃어가고 있는 것이기 때문에 일반적으로 좋지 않은 신호입니다. 단어 사전을 생성할 때의 목표는 토크나이저가 단어를 unknown 토큰으로 가능한 한 적게 토큰화하는 것입니다.

Unknown 토큰 수를 줄이기 위한 한 가지 방법은 한 단계 더 들어가 _문자 기반_ 토크나이저를 사용하는 것입니다.

## 문자 기반 토큰화[[character-based]]

<Youtube id="ssLq_EK2jLE"/>

문자 기반 토크나이저는 텍스트를 단어가 아닌 문자 단위로 나눕니다. 이 방법은 두 가지 장점이 있습니다.

- 단어 사전이 간결해진다.
- 모든 단어는 문자로 이루어졌기 때문에 out-of-vocabulary (unknown) 토큰의 수가 훨씬 적다.

하지만 여기에서도 공백과 구두점에 대한 몇 가지 궁금증을 가질 수 있습니다.

<div class="flex justify-center">
  <img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter2/character_based_tokenization.svg" alt="An example of character-based tokenization."/>
  <img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter2/character_based_tokenization-dark.svg" alt="An example of character-based tokenization."/>
</div>

이 방법 또한 완벽하지 않습니다. 이제 표현은 단어가 아닌 문자에 기반을 두고 있기 때문에 누군가는 문자의 의미가 적다고 주장할 수 있습니다. 각각의 단어는 그 자체로 큰 의미가 있지만 문자는 그렇지 않습니다. 그러나 이 점은 다시 언어에 따라 달라집니다. 예를 들어, 중국어에서는 각 문자가 라틴어보다 더 많은 정보를 전달합니다.

또 다른 고려 사항은 모델이 처리해야 하는 매우 많은 양의 토큰이 생기게 된다는 것입니다. 단어 기반 토크나이저에서 단어는 단 하나의 토큰인 반면, 문자로 변환하면 10개 이상의 토큰으로 쉽게 바뀔 수 있습니다.

장점만을 최대한 활용하기 위해 두 가지 방법을 결합한 세 번째 기법인 *서브워드 토큰화*를 사용할 것입니다. 

## 서브워드 토큰화[[subword-tokenization]]

<Youtube id="zHvTiHr506c"/>

서브워드 토큰화 알고리즘은 자주 사용되는 단어는 더 작은 서브워드로 나누면 안되지만, 희귀한 단어는 의미 있는 서브워드로 나눠야 한다는 규칙에 기반합니다.

예를 들면 "annoyingly"는 흔하지 않은 단어로 여겨질 수 있고 "annoying"과 "ly"로 분해할 수 있을 것입니다. 둘다 독립적인 서브워드로 자주 등장할 가능성이 있는 반면에 "annoyingly"는 "annoying"과 "ly"의 합성으로만 의미가 유지됩니다.

다음 예시는 서브워드 토큰화 알고리즘이 "Let's do tokenization!"이라는 문장을 어떻게 토큰화하는지 보여줍니다.

<div class="flex justify-center">
  <img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter2/bpe_subword.svg" alt="A subword tokenization algorithm."/>
  <img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter2/bpe_subword-dark.svg" alt="A subword tokenization algorithm."/>
</div>

서브워드는 많은 양의 의미론적 정보를 제공합니다. 위의 예시에서 "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]]

<Youtube id="Yffk5aydLzg"/>

텍스트를 숫자로 바꾸는 것은 _인코딩_으로 알려져 있습니다. 인코딩은 토큰화, 토큰을 입력 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]
```

적절한 프레임워크의 텐서로 변환되고 나면 이 출력 결과는 이전 장에서 본 것처럼 모델 입력으로 사용될 수 있습니다.

<Tip>

✏️ **직접 해보세요!** 2장에서 사용한 입력 문장("I've been waiting for a HuggingFace course my whole life."과 "I hate this so much!")을 이용해 두 단계(토큰화와 입력 ID로의 변환)를 수행해보세요. 위에서 얻은 결과와 당신이 얻은 결과가 동일한지 확인해보세요!

</Tip>

## 디코딩[[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를 다시 문자열로 변환하는 과정을 이해할 수 있어야 합니다. 그러나 이는 빙산의 일각입니다. 이어지는 섹션에서는 이 접근법의 한계를 알아보고, 이를 극복하는 방법을 알아볼 것입니다.


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

### 기본 사용 완료![[basic-usage-completed]]
https://huggingface.co/learn/course/ko/chapter2/7.md

# 기본 사용 완료![[basic-usage-completed]]

<CourseFloatingBanner
    chapter={2}
    classNames="absolute z-10 right-0 top-0"
/>

여기까지 오느라 수고하셨습니다! 이 단원에서 배운 것을 되돌아봅시다.

- Transformer 모델의 기본 구성 요소에 대해 배웠습니다.
- 토큰화 파이프라인의 구성 요소에 대해 배웠습니다.
- Transformer 모델을 사용하는 방법을 알아보았습니다.
- 토크나이저를 이용해 텍스트를 모델이 이해할 수 있는 텐서로 변환하는 방법을 배웠습니다.
- 원시 텍스트에서 예측 결과를 얻기 위해 토크나이저와 모델을 함께 사용하는 법을 배웠습니다.
- 입력 ID의 한계와 어텐션 마스크에 대해 배웠습니다.
- 다양하고 설정 가능한 토크나이저 메서드를 사용해보았습니다.

이제부터는 🤗 Transformers 문서 안에서 자유롭게 항해할 수 있어야 합니다. 단어는 익숙하게 들릴 것이고, 사용법을 익히는 데에 이미 충분한 시간을 들였습니다.


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

### 파이프라인 내부 동작 과정[[behind-the-pipeline]]
https://huggingface.co/learn/course/ko/chapter2/2.md

# 파이프라인 내부 동작 과정[[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" />

### 강의 소개
https://huggingface.co/learn/course/ko/chapter0/1.md

# 강의 소개

Hugging Face 강의에 오신 여러분들 환영합니다! 이번 강의 소개에서는 작업 환경 설정에 대해 안내드리겠습니다. 방금 막 이번 과정을 시작하셨다면 먼저 [Chapter 1](/course/chapter1) 내용을 살펴보고 돌아오신 뒤, 환경을 설정하여 코드를 직접 실행해보시길 추천드립니다.

이번 과정에서 사용할 모든 라이브러리는 파이썬 패키지를 통해 사용할 수 있으므로 여기서는 파이썬 환경 설정 방법 및 필요한 라이브러리 설치 방법을 보여드리겠습니다.

작업 환경 설정 방법으로 Colab 노트북을 이용한 방법과 파이썬 가상 환경을 이용한 방법, 두 가지를 다룰 것이고 둘 중 더 마음이 가는 방식을 자유롭게 선택하셔도 됩니다. 입문자의 경우 Colab 노트북을 이용하시길 강력하게 추천합니다.

여기서 Windows 환경에 대해서는 다루지 않기 때문에 Windows에서 실행 중이시면 Colab 노트북을 이용해 아래 과정을 따라가 주시길 권장드립니다. Linux 혹은 macOS를 실행 중이시라면 어떤 방식을 택해도 무방합니다.

대부분의 강의는 여러분이 Hugging Face 계정이 있다는 것을 전제로 하기 때문에 지금 바로 계정을 생성하시길 추천드립니다: [계정 생성하기](https://huggingface.co/join)

## Google Colab 노트북 사용하기

Colab 노트북은 가장 쉬운 설정 방식입니다. 브라우저에 Colab 노트북을 켜고 바로 코딩을 시작하시면 됩니다!

Colab에 익숙하지 않으시다면 [introduction](https://colab.research.google.com/notebooks/intro.ipynb) 링크를 따라 시작하시길 권장드립니다. Colab에서는 GPU, TPU와 같은 가속 하드웨어를 사용할 수 있으며 적은 양의 워크로드에 대해서는 무료입니다.

Colab과 친숙해 지셨다면 새로운 노트북을 생성하여 아래와 같이 시작합니다:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter0/new_colab.png" alt="An empty colab notebook" width="80%"/>
</div>

다음으로, 이번 강의에서 사용할 라이브러리를 설치합니다. 설치에는 파이썬 패키지 관리자인 `pip` 를 사용하도록 하겠습니다. 노트북 파일에서는 시스템 명령어 앞에 `!` 를 붙여 실행시킬 수 있으므로, 아래와 같이 🤗 Transformers 라이브러리를 설치할 수 있습니다:

```
!pip install transformers
```

이제 파이썬 런타임에 패키지를 가져와 패키지가 제대로 설치되었는지 확인해보겠습니다:

```
import transformers
```

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter0/install.gif" alt="A gif showing the result of the two commands above: installation and import" width="80%"/>
</div>

위의 방식으로는 아주 가벼운 버전의 🤗 Transformers가 설치되고, 이는 PyTorch나 TensorFlow와 같은 특정 기계학습 프레임워크를 포함하지 않습니다. 하지만 본 강의에서는 이 라이브러리의 아주 다양한 기능들을 사용할 예정이므로, 아래의 명령어를 통해 대부분의 예제에 필요한 종속성(dependency)을 제공하는 개발 버전을 설치하시길 바랍니다:

```
!pip install transformers[sentencepiece]
```

설치에 시간이 조금 걸리지만 곧 강의를 위한 준비가 모두 끝납니다!

## 파이썬 가상 환경 사용하기

파이썬 가상 환경 사용을 원하신다면 먼저 파이썬을 설치해야 합니다. 이 [가이드](https://realpython.com/installing-python/)를 따라 설치를 진행하실 수 있습니다.

파이썬 설치가 완료되면 터미널에서 파이썬 명령어를 실행할 수 있습니다. 다음 단계로 넘어가기 전에, 다음과 같은 명령어를 실행하여 설치가 잘 되었는지 확인하세요: `python --version`. 이 때 시스템에 사용할 수 있는 파이썬 버전을 출력되어야 합니다.

터미널에서 `python --version` 과 같은 파이썬 명령어를 실행하면, 명령어를 실행하는 프로그램을 시스템의 “메인(main)” 파이썬으로 생각해야 합니다. 이 메인 파이썬은 어떤 패키지도 설치하지 않은 상태로 유지하면서, 작업 중인 각 어플리케이션마다 별도의 환경을 생성하여 이용하는 것을 권장합니다. 이렇게 하면, 각 어플리케이션은 각각의 의존성 및 패키지를 갖게 되어 다른 어플리케이션과의 잠재적 호환성 문제를 피할 수 있습니다.

파이썬에서 이는 [*가상 환경*](https://docs.python.org/3/tutorial/venv.html)을 통해 완수됩니다. 가상 환경은 자체 포함 디렉토리 트리로, 각 트리는 어플리케이션에게 필요한 모든 패키지와 함께 특정 파이썬 버전에 대한 파이썬 설치를 포함합니다. 이러한 가상 환경을 생성하는 방법은 여러 툴을 통해 할 수 있지만, 여기서는 공식 파이썬 패키지인 `[venv](https://docs.python.org/3/library/venv.html#module-venv)` 를 통해 생성해 보겠습니다.

먼저, 어플리케이션을 넣어줄 디렉토리를 생성합니다. 예를 들어, 홈 디렉토리의 *transformers-course*와 같은 이름의 디렉토리를 만들어 봅시다:

```
mkdir ~/transformers-course
cd ~/transformers-course
```

디렉토리 내부에서, 파이썬 `venv` 모듈을 사용하여 가상 환경을 생성합니다:

```
python -m venv .env
```

원래 아무것도 없던 빈 폴더에 *.env*라는 디렉토리가 생기게 됩니다:

```
ls -a
```

```out
.      ..    .env
```

`activate` 스크립트를 통해 가상 환경으로 접속할 수 있고, `deactivate` 를 통해 가상 환경 밖으로 나올 수 있습니다:

```
# Activate the virtual environment
source .env/bin/activate

# Deactivate the virtual environment
deactivate
```

환경이 제대로 활성화 되었는지 `which python` 명령어를 실행하여 확인해 봅시다. 아래와 같이 가상 환경을 보여준다면 제대로 활성화가 것입니다!

```
which python
```

```out
/home/<user>/transformers-course/.env/bin/python
```

### 의존성(dependencies) 설치하기

Google Colab 사용법에서와 마찬가지로 다음 단계로 넘어가기 위해 패키지를 설치해야 합니다. 여기서도, `pip` 패키지 관리자를 통해  🤗 Transformers 개발 버전을 설치할 수 있습니다:

```
pip install "transformers[sentencepiece]"
```

이제 모든 환경 설정을 마치고 시작할 준비가 되었습니다!


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

### 단원 정리
https://huggingface.co/learn/course/ko/chapter1/9.md

# 단원 정리

<CourseFloatingBanner
    chapter={1}
    classNames="absolute z-10 right-0 top-0"
/>

이번 단원에서는 🤗 Transformers의 하이레벨 함수인 `pipeline()` 를 사용하여 다양한 NLP 문제에 대한 접근 방식을 배웠습니다. 그리고 Hub에서 모델을 검색하여 사용하는 방법, 추론 API를 이용해 브라우저 상에서 바로 모델을 테스트 하는 방법 또한 알아보았습니다.

지금까지 트랜스포머 모델의 대략작인 동작 방식과, 전이 학습(transfer learning) 및 미세 조정(fine-tuning)의 중요성에 알아보았습니다. 핵심은 어떤 문제를 풀고싶냐에 따라 전체 모델 구조를 다 사용하거나 인코더, 디코더만 사용할 수도 있다는 것입니다. 아래 표는 이를 요약해서 보여주고 있습니다:

| Model | Examples | Tasks |
| --- | --- | --- |
| 인코더 | ALBERT, BERT, DistilBERT, ELECTRA, RoBERTa | 문장 분류, 개체명 인식, 추출 질의 응답 |
| 디코더 | CTRL, GPT, GPT-2, Transformer XL | 텍스트 생성 |
| 인코더-디코더 | BART, T5, Marian, mBART | 요약, 번역, 생성 질의 응답 |


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

### 인코더 모델
https://huggingface.co/learn/course/ko/chapter1/5.md

# 인코더 모델

<CourseFloatingBanner
    chapter={1}
    classNames="absolute z-10 right-0 top-0"
/>

<Youtube id="MUqNwgPjJvQ" />

인코더 모델(Encoder models)은 트랜스포머 모델의 인코더만 사용합니다. 각각의 단계에서 어텐션 레이어는 초기 문장의 모든 단어에 액세스 할 수 있습니다. 이러한 모델은 “양방향성(bi-directional)” 어텐션을 지닌 특성이 있다고도 하며 *자동 인코딩(auto-enoding) 모델*이라고 부릅니다.

이러한 모델은 주어진 문장을 훼손시킨 후(랜덤으로 단어에 마스킹을 하는 방식 등으로) 모델이 원본 문장을 찾아 재구성하게끔 하는 과정을 반복시키는 방식으로 사전 학습을 진행 합니다.

인코더 모델은 문장 분류, 개체명 인식(더 넓은 범위에서 단어 분류), 추출 질의 응답 등과 같이 전체 문장에 대한 이해를 요구하는 작업에 특화되어 있습니다.

이 계열을 대표하는 모델들은 아래와 같습니다:

- [ALBERT](https://huggingface.co/transformers/model_doc/albert.html)
- [BERT](https://huggingface.co/transformers/model_doc/bert.html)
- [DistilBERT](https://huggingface.co/transformers/model_doc/distilbert.html)
- [ELECTRA](https://huggingface.co/transformers/model_doc/electra.html)
- [RoBERTa](https://huggingface.co/transformers/model_doc/roberta.html)


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

### 단원 소개
https://huggingface.co/learn/course/ko/chapter1/1.md

# 단원 소개

<CourseFloatingBanner
    chapter={1}
    classNames="absolute z-10 right-0 top-0"
/>

## 🤗 강의 수강생 여러분 환영합니다!

<Youtube id="00GKzGyWFEs" />

이번 강의에서는 [Hugging Face](https://huggingface.co/) 환경의 라이브러리([🤗 Transformers](https://github.com/huggingface/transformers), [🤗 Datasets](https://github.com/huggingface/datasets), [🤗 Tokenizers](https://github.com/huggingface/tokenizers), [🤗 Accelerate](https://github.com/huggingface/accelerate))와 [Hugging Face Hub](https://huggingface.co/models) 를 이용해 자연어 처리(NLP)에 대해 배워보겠습니다. (무료 강의에 광고도 없는건 비밀입니다!)

## 무엇을 배우나요?

강의 개요 훑어보기:

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/summary.svg" alt="Brief overview of the chapters of the course.">
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/summary-dark.svg" alt="Brief overview of the chapters of the course.">
</div>

- 챕터 1~4에서는 🤗 Transformers 라이브러리의 핵심 개념에 대해 소개합니다. 이 부분을 마치면 트랜스포머 모델의 동작 원리를 이해하실 수 있고, [Hugging Face Hub](https://huggingface.co/models)에서 모델을 사용하여 데이터셋으로 미세 조정(fine-tune)한 후 Hub에 모델을 공유하는 방법까지 터득하게 될 것입니다!
- 챕터 5~8은 본격적으로 고전 NLP 업무를 수행하기 앞서, 🤗 Datasets와 🤗 Tokenizers의 기초에 대해 알아봅니다. 이 부분을 모두 학습하시면 일반적인 NLP 문제를 스스로 해낼 수 있게 됩니다.
- 챕터 9~12에서는 트랜스포머 모델이 NLP 문제를 넘어, 음성 처리 및 컴퓨터 비전에 어떻게 활용되는지 탐구합니다. 이 과정에서 모델 데모를 구축하고 공유하는 방법과 이를 프로덕션 환경에 최적화하는 방법을 공부합니다. 이러한 과정을 거쳐서, 여러분들은 거의 모든 기계 학습(머신 러닝) 문제에 🤗 Transformers를 적용할 준비를 갖추게 됩니다!

이번 강의는:

* 파이썬에 대한 기초 지식이 필요합니다
* [DeepLearning.AI](https://www.deeplearning.ai/) 의 프로그램이나  [fast.ai's](https://www.fast.ai/) [Practical Deep Learning for Coders](https://course.fast.ai/) 와 같은 딥러닝에 대한 기초 강의를 듣고 수강하면 더욱 효과적입니다
* [PyTorch](https://pytorch.org/) , [TensorFlow](https://www.tensorflow.org/) 에 대한 선수 지식이 필요하지는 않지만, 이에 익숙하시다면 도움이 될 것입니다

본 강의를 모두 수강한 후, DeepLearning.AI의 [Natural Language Processing Specialization](https://www.coursera.org/specializations/natural-language-processing?utm_source=deeplearning-ai&utm_medium=institutions&utm_campaign=20211011-nlp-2-hugging_face-page-nlp-refresh)을 학습하시길 권장드립니다. 해당 과정에서는 Naive Bayes, LSTM과 같은 알아두면 너무나 유용한 더 넓은 범위의 전통 NLP 모델에 대해 학습할 수 있습니다!

## 우리가 누구일까요?

저자 소개:

**Matthew Carrigan**은 Hugging Face의 머신 러닝 엔지니어입니다. 현재 아일랜드 더블린에 살고 있으며, 이전에는 [Parse.ly](http://parse.ly/) 에서 ML 엔지니어로, 그 전에는 Trinity Collge Dublin에서 박사 과정 이후 연구원으로 근무했습니다. 사람이 기존 인공지능 아키텍쳐를 확장하여 사람 수준에는 도달하지 못할거라고 생각하지만, 그럼에도 불멸 로봇(immortality robot)에 대해 큰 기대를 갖고 있습니다.

**Lysandre Debut**는 Hugging Face의 머신 러닝 엔지니어이며 초창기부터 🤗 Transformers 라이브러리 작업을 함께 했습니다. 아주 사용하기 쉬운 API를 개발하여 모두가 NLP를 쉽게 사용할 수 있도록 하는 목표를 갖고 있습니다.

**Sylvain Gugger**는 Hugging Face의 리서치 엔지니어로 🤗 Transformers 라이브러리의 주요 관리자 중 한명입니다. 이전에 [fast.ai](http://fast.ai/) 에서 리서치 사이언티스트로 있었으며 Jeremy Howard와 함께  *[Deep Learning for Coders with fastai and PyTorch](https://learning.oreilly.com/library/view/deep-learning-for/9781492045519/)* 를 저술했습니다. 적은 리소스에서도 모델이 빠르게 학습되도록 기술을 디자인하고 개선하여 딥러닝에 보다 쉽게 접근할 수 있도록 하는 것을 리서치의 가장 큰 목표로 삼고 있습니다.

**Merve Noyan**은 Hugging Face의 개발자 애드보케이트로, 모두에게 평등한 민주적인 머신 러닝 생태계를 만드는 목표를 갖고 있으며, 개발툴 작업 및 주변 컨텐츠 구축 작업을 담당하고 있습니다.

**Lucile Saulnier**은 Hugging Face의 ML 엔지니어로 오픈 소스 툴 사용에 대한 개발 및 지원을 담당합니다. 자연어 처리 분야에서 협업 학습, BigScience등과 같은 다양한 리서치 프로젝트에도 활발히 참여하고 있습니다.

**Lewis Tunstall**는 Hugging Face의 ML 엔지니어로 오픈 소스 툴을 개발하여 더 많은 커뮤니티에 상용화되도록 하는 데에 초점을 맞추고 있습니다. 곧 출간되는 [O’Reilly book on Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/)의 공저자이기도 합니다.

**Leandro von Werra**는 Hugging Face 오픈소스 팀의 머신 러닝 엔지니어이자 곧 출간될 [O’Reilly book on Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/)의 공동 저자입니다.  모든 머신 러닝 스택에서의 작업을 통해 수 년간 NLP 프로젝트를 프로덕션으로 들여온 경력자입니다.

시작할 준비가 되셨나요? 이번 챕터에서 다룰 내용은 다음과 같습니다:

- 텍스트 생성 및 분류와 같은 NLP 문제를 푸는 `pipeline()` 함수 사용법
- 트랜스포머 모델 구조
- 인코더(encoder), 디코더(decoder), 인코더-디코더(encoder-decoder)의 구조와 용례

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

### 트랜스포머로 무엇을 할 수 있나요?
https://huggingface.co/learn/course/ko/chapter1/3.md

# 트랜스포머로 무엇을 할 수 있나요?

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

이번 장에서는 트랜스포머(Transformer) 모델을 사용해 무엇을 할 수 있는지 같이 살펴보고,  🤗 Transformers 라이브러리 툴의 첫 사용을 `pipeline()` 함수와 함께 시작하겠습니다.

<Tip>
👀 오른쪽 상단에 <em>Open in Colab</em> 버튼이 보이시나요? 버튼을 클릭하면 이번 장에서 사용한 모든 코드 샘플들을 Google Colab notebook을 통해 열 수 있습니다. 이런 버튼을 예제 코드를 포함하는 모든 단원에서 발견하실 수 있습니다.

로컬 환경에서 예제 코드를 실행하려면 <a href="/course/chapter0">setup</a>을 살펴보세요.
</Tip>

## 트랜스포머는 어디에나 있어요!

트랜스포머 모델은 이전 단원에서 언급한 작업과 같은 모든 NLP 문제를 해결하기 위해 사용됩니다. 아래와 같이 Hugging Face와 트랜스포머 모델을 이용하고 다시 모델을 공유하여 커뮤니티에 기여하는 많은 기업과 기관이 있습니다:

<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/companies.PNG" alt="Companies using Hugging Face" width="100%">

[🤗 Transformers 라이브러리](https://github.com/huggingface/transformers)는 이렇게 공유한 모델을 사용하고 구축하는 기능들을 제공합니다. [Model Hub](https://huggingface.co/models)에서는 모두가 다운로드 받아 쓸 수 있는 수 천 개의 사전 학습된 모델들이 여러분을 기다리고 있습니다. 여러분만의 모델을 Hub에 업로드하는 것 또한 가능합니다!

<Tip>
⚠️ The Hugging Face Hub에는 트랜스포머 모델만 있지 않아요. 누구든지 어떠한 종류의 모델이나 데이터를 공유할 수 있습니다! <a href="https://huggingface.co/join">Create a huggingface.co</a> 링크에서 계정을 만들고 모든 기능을 사용해보세요!
</Tip>

트랜스포머 모델 안에서 무슨 일이 벌어지는지 알아보기 전에, 트랜스포머가 NLP 문제 해결에 어떻게 사용되는지 몇 가지 흥미로운 예시들을 살펴보겠습니다.

## 파이프라인으로 작업하기

<Youtube id="tiZFewofSLM" />

🤗 Transformers 라이브러리의 가장 기본 객체는 `pipeline()` 함수입니다. 이 함수는 모델에 있어서 필수 과정인 전처리와 후처리 과정을 모델과 연결하고, 우리가 바로 어떠한 텍스트 입력을 넣든 원하는 답을 얻을 수 있도록 합니다:

```python
from transformers import pipeline

classifier = pipeline("sentiment-analysis")
classifier("I've been waiting for a HuggingFace course my whole life.")
```

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

아래와 같이 여러 문장을 함께 넣을 수도 있습니다!

```python
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}]
```

기본적으로 이 파이프라인은 영어 감정 분석에 미세 조정(fine-tune)된 사전 학습 모델을 선택하여 넣게 됩니다. 여기서 `classifier` 객체를 생성할 때 모델이 다운로드 되며 캐싱(caching)이 이루어지때문에, 재실행 시에는 캐싱된 모델을 사용하게 되어 모델을 다시 다운로드 하지 않습니다.

텍스트를 파이프라인에 넣을 때 다음과 같은 세 가지 주요 과정을 거칩니다:

1. 텍스트가 모델이 이해할 수 있는 형태로 전처리 과정을 거칩니다.
2. 전처리된 입력이 모델 입력으로 들어갑니다.
3. 모델의 예측값이 후처리를 거쳐, 사람이 이해할 수 있는 형태로 반환됩니다.


현재까지 사용할 수 있는 파이프라인([available pipelines](https://huggingface.co/transformers/main_classes/pipelines.html))은 다음과 같습니다:

- `feature-extraction` : 특징 추출 (텍스트에 대한 벡터 표현 추출)
- `fill-mask` : 마스크 채우기
- `ner` : 개체명 인식 (named entity recognition)
- `question-answering` : 질의 응답
- `sentiment-analysis` : 감정 분석
- `summarization` : 요약
- `text-generation` : 텍스트 생성
- `translation` : 번역
- `zero-shot-classification` : 제로샷 분류

이 중 몇 가지를 같이 살펴보도록 하겠습니다!

## 제로샷 분류(Zero-shot classification)

레이블이 없는 텍스트를 분류하는 더 까다로운 과제부터 시작하겠습니다. 텍스트에 레이블을 다는 것은 시간이 많이 소요되고 도메인 지식이 필요하기 때문에 이러한 작업은 실제 프로젝트에서 아주 흔한 상황입니다. 이러한 상황에서 `zero-shot-classification` 파이프라인은 매우 유용합니다. 제로샷 파이프라인은 사전 학습된 모델에 의존하지 않고도 분류 작업에 사용할 레이블을 특정할 수 있도록 합니다. 위의 예시에서 모델이 긍정(positive)과 부정(negative)의 두 레이블을 분류하는 샘플을 살펴보았는데, 제로샷 파이프라인을 통해서는 어떠한 레이블 세트에 대해서도 분류 작업을 수행할 수 있습니다.

```python
from transformers import pipeline

classifier = pipeline("zero-shot-classification")
classifier(
    "This is a course about the Transformers library",
    candidate_labels=["education", "politics", "business"],
)
```

```python out
{'sequence': 'This is a course about the Transformers library',
 'labels': ['education', 'business', 'politics'],
 'scores': [0.8445963859558105, 0.111976258456707, 0.043427448719739914]}
```

이러한 파이프라인이 제로샷(zero-shot)이라 불리는 이유는 여러분의 데이터에 맞춰 미세 조정(fine-tune)하지 않고도 바로 작업에 사용할 수 있기 때문입니다. 제로샷은 여러분이 원하는 어떠한 분류 레이블에 대해서도 확률 점수를 즉시 반환합니다.

<Tip>

✏️ **직접 해보기!** 여러분이 직접 작성한 시퀀스와 레이블을 사용해 모델이 어떻게 동작하는지 확인해보세요.

</Tip>


## 텍스트 생성(Text generation)

지금부터 파이프라인을 사용해 텍스트를 생성하는 방법을 알아보겠습니다. 여기서의 핵심은 프롬트를 모델에 제공하면 모델이 나머지 텍스트를 생성하여 이를 자동으로 완성하는 것입니다. 이는 스마트폰의 텍스트 자동 완성 기능과 유사합니다. 텍스트 생성에는 랜덤하게 결과를 생성하는 과정이 포함되어 있어서 여러분이 아래와 같이 동일하게 입력을 넣어도 매번 다른 결과가 나올 수 있습니다.

```python
from transformers import pipeline

generator = pipeline("text-generation")
generator("In this course, we will teach you how to")
```

```python out
[{'generated_text': 'In this course, we will teach you how to understand and use '
                    'data flow and data interchange when handling user data. We '
                    'will be working with one or more of the most commonly used '
                    'data flows — data flows of various types, as seen by the '
                    'HTTP'}]
```

`num_return_sequences`라는 인자(argument)를 통해 몇 개의 서로 다른 출력 결과를 생성할지 정할 수 있고, `max_length` 인자를 통해 출력 텍스트의 총 길이를 설정할 수 있습니다.

<Tip>

✏️ **직접 해보기!** `num_return_sequences` 와 `max_length` 인자를 설정해 15개의 단어를 가진 서로 다른 두 개의 문장을 출력해보세요.

</Tip>


## 파이프라인에 Hub의 모델 적용하기

지금까지 예제들은 해당 작업에 대해 기본 모델들을 사용했지만, 특정 모델을 Hub에서 선택해 텍스트 생성과 같은 특정 작업에 대한 파이프라인에서도 사용할 수 있습니다. [Model Hub](https://huggingface.co/models) 페이지의 화면 왼쪽에 태그를 클릭하여 태그명에 해당하는 작업을 지원하는 모델을 확인할 수 있습니다. 이 때, [다음](https://huggingface.co/models?pipeline_tag=text-generation)과 같은 페이지로 이동하게 됩니다.

함께 [`distilgpt2`](https://huggingface.co/distilgpt2) 모델을 사용해보겠습니다! 위 예제에서 사용한 파이프라인에서 모델을 아래와 같이 로드 할 수 있습니다:

```python
from transformers import pipeline

generator = pipeline("text-generation", model="distilgpt2")
generator(
    "In this course, we will teach you how to",
    max_length=30,
    num_return_sequences=2,
)
```

```python out
[{'generated_text': 'In this course, we will teach you how to manipulate the world and '
                    'move your mental and physical capabilities to your advantage.'},
 {'generated_text': 'In this course, we will teach you how to become an expert and '
                    'practice realtime, and with a hands on experience on both real '
                    'time and real'}]
```

언어 태그를 클릭하여 해당 언어를 지원하고 생성하는 모델을 보다 구체적으로 검색할 수 있습니다. Model Hub에는 다양한 언어를 처리하는 다국어 모델의 체크포인트(모델의 파라미터 값) 또한 포함하고 있습니다.

모델을 클릭하면 온라인상에서 바로 사용 가능한 위젯을 확인할 수 있고, 이를 통해 모델을 직접 다운로드 받기 전에 모델의 기능을 빠르게 테스트 해볼 수 있습니다.

<Tip>

✏️ **직접 해보기!** 영어를 제외한 다른 언어를 생성하는 모델을 검색해보세요. 위젯을 자유롭게 다뤄 보시고 파이프라인을 사용해보세요!

</Tip>

### 추론(Inference) API

모든 모델들은 [Hugging Face 웹사이트](https://huggingface.co/)에서 제공하는 추론 API를 통해 여러분의 브라우저상에서 직접 테스트할 수 있습니다. 이 페이지 링크로 접속해 직접 작성하신 텍스트를 입력하시면 모델의 입력 데이터를 처리 결과를 확인할 수 있습니다.

위젯을 구동하는 추론 API는 간편한 워크플로우를 가능하게 하는 유료 버전의 제품으로도 이용 가능합니다. 자세한 사항은 [가격 정책 페이지](https://huggingface.co/pricing)를 참고해주세요.

## 마스크 채우기(Mask filling)

다음으로 사용해볼 파이프라인은 마스크 채우기(`fill-mask`)입니다. 이 작업의 핵심 아이디어는 주어진 텍스트의 빈칸을 채우기입니다:

```python
from transformers import pipeline

unmasker = pipeline("fill-mask")
unmasker("This course will teach you all about <mask> models.", top_k=2)
```

```python out
[{'sequence': 'This course will teach you all about mathematical models.',
  'score': 0.19619831442832947,
  'token': 30412,
  'token_str': ' mathematical'},
 {'sequence': 'This course will teach you all about computational models.',
  'score': 0.04052725434303284,
  'token': 38163,
  'token_str': ' computational'}]
```

상위 몇 개의 높은 확률을 띠는 토큰을 출력할지 `top_k` 인자를 통해 조절합니다. 여기서 모델이 특이한 `<mask>` 단어를 채우는 것을 주목하세요. 이를 마스크 토큰(mask token)이라고 부릅니다. 다른 마스크 채우기 모델들은 다른 형태의 마스크 토큰을 사용할 수 있기 때문에 다른 모델을 탐색할 때 항상 해당 모델의 마스크 단어가 무엇인지 확인해야 합니다. 위젯에서 사용되는 마스크 단어를 보고 이를 확인할 수 있습니다.

<Tip>

✏️ **직접 해보기!** Hub에서 `bert-base-cased`를 검색해 보고 추론 API 위젯을 통해 모델의 마스크 단어가 무엇인지 확인해 보세요. 이 모델이 위의 `pipeline` 예제에서 사용한 문장에 대해 어떻게 예측하나요?

</Tip>

## 개체명 인식(Named entity recognition)

모델이 입력 텍스트의 어느 부분이 사람, 장소, 기관 등과 같은 개체에 해당하는지 찾는 작업을 개체명 인식(NER)이라고 합니다. 예제를 통해 확인해 봅시다:

```python
from transformers import pipeline

ner = pipeline("ner", grouped_entities=True)
ner("My name is Sylvain and I work at Hugging Face in Brooklyn.")
```

```python out
[{'entity_group': 'PER', 'score': 0.99816, 'word': 'Sylvain', 'start': 11, 'end': 18},
 {'entity_group': 'ORG', 'score': 0.97960, 'word': 'Hugging Face', 'start': 33, 'end': 45},
 {'entity_group': 'LOC', 'score': 0.99321, 'word': 'Brooklyn', 'start': 49, 'end': 57}
]
```

모델이 정확하게 Sylvain을 사람(PER)으로, Hugging Face를 기관(ORG)으로, Brooklyn을 장소(LOC)으로 예측했네요!

파이프라인을 생성하는 함수에  `grouped_entities=True` 옵션을 전달하면 파이프라인이 같은 개체에 해당하는 문장 부분을 다시 그룹화합니다. 이 옵션을 설정하면 모델은 여러 단어로 구성된 단어임에도 “Hugging”과 “Face”를 하나의 기관으로 정확히 분류하게 됩니다. 다음 챕터에서도 확인하겠지만, 놀랍게도 전처리 과정에서 각 단어들은 더 작은 부분으로 쪼개집니다. 예를 들어 `Sylvain` 이라는 단어는 `S`, `##yl`, `##va`, `##in` 이렇게 네 조각으로 쪼개집니다. 후처리 단계에서 파이프라인은 이 조각들을 멋지게 재그룹화합니다.

<Tip>

✏️ **직접 해보기!** Model Hub에서 영어 품사 태깅(part-of-speech tagging, 줄여서 POS)이 가능한 모델을 찾아보세요. 이 모델이 위의 예시 문장으로 무엇을 예측하나요?

</Tip>

## 질의 응답(Question-answering)

질의 응답(`question-answering`) 파이프라인은 주어진 지문(context)의 정보를 활용하여 질문에 대한 답을 하는 태스크입니다:

```python
from transformers import pipeline

question_answerer = pipeline("question-answering")
question_answerer(
    question="Where do I work?",
    context="My name is Sylvain and I work at Hugging Face in Brooklyn",
)
```

```python out
{'score': 0.6385916471481323, 'start': 33, 'end': 45, 'answer': 'Hugging Face'}
```

본 파이프라인은 답을 새롭게 생성하는 방식이 아닌, 주어진 지문 내에서 정답을 추출하는 방식임을 잘 기억하세요.

## 요약(Summarization)

요약(Summarization)은 참조 텍스트의 모든(혹은 대부분의) 중요한 특징을 그대로 유지한 채 텍스트를 짧게 줄이는 작업입니다. 아래 예제를 확인하세요:

```python
from transformers import pipeline

summarizer = pipeline("summarization")
summarizer(
    """
    America has changed dramatically during recent years. Not only has the number of
    graduates in traditional engineering disciplines such as mechanical, civil,
    electrical, chemical, and aeronautical engineering declined, but in most of
    the premier American universities engineering curricula now concentrate on
    and encourage largely the study of engineering science. As a result, there
    are declining offerings in engineering subjects dealing with infrastructure,
    the environment, and related issues, and greater concentration on high
    technology subjects, largely supporting increasingly complex scientific
    developments. While the latter is important, it should not be at the expense
    of more traditional engineering.

    Rapidly developing economies such as China and India, as well as other
    industrial countries in Europe and Asia, continue to encourage and advance
    the teaching of engineering. Both China and India, respectively, graduate
    six and eight times as many traditional engineers as does the United States.
    Other industrial countries at minimum maintain their output, while America
    suffers an increasingly serious decline in the number of engineering graduates
    and a lack of well-educated engineers.
"""
)
```

```python out
[{'summary_text': ' America has changed dramatically during recent years . The '
                  'number of engineering graduates in the U.S. has declined in '
                  'traditional engineering disciplines such as mechanical, civil '
                  ', electrical, chemical, and aeronautical engineering . Rapidly '
                  'developing economies such as China and India, as well as other '
                  'industrial countries in Europe and Asia, continue to encourage '
                  'and advance engineering .'}]
```

텍스트 생성에서와 마찬가지로 `max_length` 와 `min_length` 를 미리 설정할 수 있습니다.

## 번역(Translation)

번역(Translation)의 경우 `"translation_en_to_fr"` 와 같이 태스크명에 해당하는 언어 쌍을 넣어준다면 기본 모델을 사용할 수 있지만, 더 간단하게 [Model Hub](https://huggingface.co/models)에서 원하는 모델을 선택해 사용하는 방법이 있습니다. 아래에서는 프랑스어에서 영어로 번역을 시도해 보겠습니다:

```python
from transformers import pipeline

translator = pipeline("translation", model="Helsinki-NLP/opus-mt-fr-en")
translator("Ce cours est produit par Hugging Face.")
```

```python out
[{'translation_text': 'This course is produced by Hugging Face.'}]
```

텍스트 생성 및 요약에서와 마찬가지로, `max_length` 혹은 `min_length` 를 지정하여 결과를 출력할 수 있습니다.

<Tip>

✏️ **직접 해보기!** 다른 언어를 지원하는 번역 모델을 검색해보고 위의 문장을 몇 가지 다른 언어들로 번역해 봅시다.

</Tip>

지금까지 보여드린 파이프라인들은 대부분 특정 작업을 위해 프로그래밍된 데모용 파이프라인으로, 여러 태스크를 동시에 지원하지는 않습니다. 다음 단원에서는  `pipeline()` 함수 내부를 살펴보고 그 동작 방식을 직접 설계하는 방법에 대해 다루겠습니다.

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

### 디코더 모델
https://huggingface.co/learn/course/ko/chapter1/6.md

# 디코더 모델

<CourseFloatingBanner
    chapter={1}
    classNames="absolute z-10 right-0 top-0"
/>

<Youtube id="d_ixlCubqQw" />

디코더 모델(Decoder models)은 트랜스포머 모델의 디코더만 사용합니다. 각각의 단계마다, 어텐션 레이어는 주어진 단어에 대해 문장 내에서 해당 단어 앞에 위치한 단어들에 대해서만 액세스 할 수 있습니다. 이러한 모델을 *자동 회귀(auto-regressive) 모델*이라고 부릅니다.

디코더 모델의 사전 학습은 보통 문장 내 다음 단어 예측을 반복하는 방식으로 이루어집니다.

이러한 모델은 텍스트 생성에 특화되어 있습니다.

디코더 모델 계열의 대표 주자들은 다음과 같습니다:

- [CTRL](https://huggingface.co/transformers/model_doc/ctrl.html)
- [GPT](https://huggingface.co/docs/transformers/model_doc/openai-gpt)
- [GPT-2](https://huggingface.co/transformers/model_doc/gpt2.html)
- [Transformer XL](https://huggingface.co/transformers/model_doc/transformerxl.html)


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

### 단원 마무리 퀴즈
https://huggingface.co/learn/course/ko/chapter1/10.md

# 단원 마무리 퀴즈

<CourseFloatingBanner
    chapter={1}
    classNames="absolute z-10 right-0 top-0"
/>

이번 챕터에서는 정말 많은 내용들을 다뤘습니다! 그러니 모든 세부 사항을 다 이해하지 못했다고 해서 좌절하지 마세요. 다음 챕터에서 다루는 내용은 내부 작동 방식을 이해하는 데에 도움이 될거에요.

그래도 우선, 이번 챕터에서 배운 내용에 대해 확인해보는 시간을 갖도록 하겠습니다!


### 1. Hub에서 `roberta-large-mnli` 체크포인트를 검색해 보세요. 이 모델은 어떤 작업을 수행하나요?


<Question
    choices={[
        {
            text: "요약",
            explain: "이 페이지를 다시 확인하세요 <a href=\"https://huggingface.co/roberta-large-mnli\">roberta-large-mnli page</a>."
        },
        {
            text: "텍스트 분류",
            explain: "더 정확하게 말하면, 이 모델은 두 문장이 논리적으로 타당한지 세 가지 레이블(모순, 함의, 중립)로 분류합니다. 이러한 문제를 <em>자연어 추론(natural language inference)</em>이라고 부릅니다.",
            correct: true
        },
        {
            text: "텍스트 생성",
            explain: "이 페이지를 다시 확인하세요 <a href=\"https://huggingface.co/roberta-large-mnli\">roberta-large-mnli page</a>."
        }
    ]}
/>

### 2. 다음 코드는 무엇을 반환하나요?

```py
from transformers import pipeline

ner = pipeline("ner", grouped_entities=True)
ner("My name is Sylvain and I work at Hugging Face in Brooklyn.")
```

<Question
    choices={[
        {
            text: "문장에 대해 \"positive\" 혹은 \"negative\" 로 분류한 레이블과 함께 분류 점수를 반환합니다.",
            explain: "오답입니다 — 이는 <code>sentiment-analysis</code> 파이프라인에 대한 설명입니다."
        },
        {
            text: "이 문장을 완성할, 생성 텍스트를 반환합니다.",
            explain: "오답입니다 — 이는 <code>text-generation</code> 파이프라인에 대한 설명입니다.",
        },
        {
            text: "사람, 기관, 장소 등을 나타내는 단어들을 반환합니다.",
            explain: "이 뿐만 아니라, <code>grouped_entities=True</code>를 사용해 \"Hugging Face\"와 같이 같은 개체에 해당하는 단어들을 그룹화해줍니다.",
            correct: true
        }
    ]}
/>

### 3. 다음 예제 코드에서 ... 대신 무엇이 들어가야 할까요?

```py
from transformers import pipeline

filler = pipeline("fill-mask", model="bert-base-cased")
result = filler("...")
```

<Question
    choices={[
        {
            text: "<mask>",
            explain: "오답입니다. 여기 <code>bert-base-cased</code> 모델 카드를 보시고 다시 도전해보세요."
        },
        {
            text: "[MASK]",
            explain: "정답! 이 모델의 마스크 토큰은 [MASK]입니다.",
            correct: true
        },
        {
            text: "man",
            explain: "오답입니다. 이 파이프라인은 마스킹된 단어를 채워야하기니까 어딘가에는 마스크 토큰이 있어야겠죠?"
        }
    ]}
/>

### 4. 다음 코드가 실행되지 않는 이유는 무엇일까요?

```py
from transformers import pipeline

classifier = pipeline("zero-shot-classification")
result = classifier("This is a course about the Transformers library")
```

<Question
    choices={[
        {
            text: "해당 텍스트를 분류하기 위해서는 파이프라인에 레이블을 넣어주어야 합니다.",
            explain: "맞습니다 — 제대로 작동시키기 위해 다음 코드가 필요합니다. <code>candidate_labels=[...]</code>.",
            correct: true
        },
        {
            text: "한 문장이 아니라, 여러 문장을 파이프라인에 넣어주어야 합니다.",
            explain: "틀렸지만, 다른 파이프라인과 마찬가지로 제대로 사용한다면 물론 여러 리스트의 문장도 입력으로 넣어줄 수 있습니다."
        },
        {
            text: "늘 그렇듯 🤗 Transformers 라이브러리가 또 고장난거 아닌가요?",
            explain: "못 들은 걸로 하겠습니다!"
        },
        {
            text: "위의 문장은 너무 짧아서, 더 긴 문장을 입력해야 합니다.",
            explain: "오답입니다. 매우 긴 텍스트는 파이프라인에서 처리할 때 잘리게 되는 것을 명심하세요."
        }
    ]}
/>

### 5. "전이 학습(transfer learning)"이란 무엇을 의미하나요?

<Question
    choices={[
        {
            text: "동일한 데이터셋으로 학습할 때, 사전 학습된 모델의 지식이 새로운 모델로 전달되는 것",
            explain: "틀렸습니다, 이런 경우 동일한 모델이 두 개 있는 것과 똑같습니다."
        },
        {
            text: "사전 학습된 모델의 가중치로 새로운 모델을 초기화할 때, 사전 학습된 모델의 지식이 전달되는 것",
            explain: "정답: 사전 학습된 모델이 새로운 모델로 지식을 *전이(transfer)*하여 새로운 과제에 대한 학습이 이루어집니다.",
            correct: true
        },
        {
            text: "사전 학습된 모델과 동일한 구조의 모델을 새로 만들 때, 사전 학습 모델의 지식이 전달되는 것",
            explain: "구조(architecture)는 모델이 디자인되는 방식일 뿐, 이러한 경우 지식이 공유되거나 전이되지 않습니다."
        }
    ]}
/>

### 6. 언어 모델은 일반적으로 사전 학습시에 레이블을 필요로 하지 않습니다. 이 문장은 참일까요 거짓일까요?


<Question
    choices={[
        {
            text: "참",
            explain: "사전 학습 과정은 일반적으로 <em>자가 지도(self-supervised)</em> 방식입니다. 이는 다음 단어 예측 혹은 마스킹 된 단어 채우기 등과 같이 입력으로부터 자동으로 레이블을 생성하는 것을 의미합니다.",
            correct: true
        },
        {
            text: "거짓",
            explain: "정답이 아닙니다."
        }
    ]}
/>

### 7. 다음 중 “모델(model)”, “구조(architecture)”, “가중치(weights)”에 대해 가장 잘 설명한 것을 고르세요.

<Question
    choices={[
        {
            text: "모델이 하나의 빌딩이라면, 구조는 청사진이고 가중치는 그 안에 사는 사람들입니다.",
            explain: "이러한 비유대로라면, 가중치는 건물을 짓는데 사용한 벽돌이나 기타 재료들이 더 어울리겠네요."
        },
        {
            text: "구조는 모델 구축을 위한 일종의 지도이고 가중치는 그 지도에 나타난 도시들에 해당합니다.",
            explain: "이 비유의 문제점은, 지도는 보통 하나의 실제 세상만을 나타낸다는 것입니다(한국에는 서울이 하나뿐이죠). 실제로 동일한 구조에 대해서 수많은 다른 가중치를 가질 수 있습니다."
        },
        {
            text: "구조는 모델을 구축하기 위한 수학적 함수의 연속이고 가중치는 그 함수들의 변수(parameters)입니다.",
            explain: "동일한 셋의 수학적 함수(구조)는 서로 다른 변수(가중치)를 사용해 각기 다른 모델을 만들 수 있습니다.",
            correct: true
        }
    ]}
/>


### 8. 다음 중 어떤 모델이 텍스트를 생성하여 프롬프트(prompt)를 완성시키는 데에 가장 적합할까요?

<Question
    choices={[
        {
            text: "인코더 모델",
            explain: "인코더 모델은 전체 문장에 대한 표현을 생성하므로 분류에 더 적합합니다."
        },
        {
            text: "디코더 모델",
            explain: "디코더 모델은 프롬트로부터 텍스트를 생성하는 데에 최적의 모델입니다.",
            correct: true
        },
        {
            text: "시퀀스-투-시퀀스 모델",
            explain: "시퀀스-투-시퀀스 모델은 주어진 프롬트가 아닌, 입력 문장과 관련 있는 문장을 생성하는 데에 더 적합합니다."
        }
    ]}
/>

### 9. 다음 중 어떤 모델이 텍스트 요약에 가장 적합할까요?

<Question
    choices={[
        {
            text: "인코더 모델",
            explain: "인코더 모델은 전체 문장에 대한 표현을 생성하므로 분류에 더 적합합니다."
        },
        {
            text: "디코더 모델",
            explain: "디코더 모델 또한 (요약과 같이) 출력 텍스트 생성을 잘하지만 전체 맥락과 같은 컨텍스트를 활용하여 요약을 할 수는 없습니다."
        },
        {
            text: "시퀀스-투-시퀀스 모델",
            explain: "시퀀스-투-시퀀스 모델은 요약 작업을 수행하는 데에 최적의 모델입니다.",
            correct: true
        }
    ]}
/>

### 10. 다음 중 어떤 모델이 입력 텍스트를 특정 레이블로 분류하는 데에 가장 적합할까요?

<Question
    choices={[
        {
            text: "인코더 모델",
            explain: "인코더 모델은 전체 문장에 대한 표현을 생성하므로 분류 태스크에 최적의 모델입니다.",
            correct: true
        },
        {
            text: "디코더 모델",
            explain: "디코더 모델은 출력 텍스트 파일 생성을 잘하지만, 문장으로부터 레이블을 잘 추출하진 못합니다."
        },
        {
            text: "시퀀스-투-시퀀스 모델",
            explain: "시퀀스-투-시퀀스 모델은 주어진 입력 문장을 기반으로 레이블이 아니라 텍스트를 생성하는 데에 적합합니다.",
        }
    ]}
/>

### 11. 다음 중 모델이 편향성(bias)을 갖게 되는 데에 가장 가능성 있는 원인을 모두 고르세요.

<Question
    choices={[
        {
            text: "모델은 사전 학습 모델의 미세 조정된 버전이고, 여기서 편향성이 따라오게 됩니다.",
            explain: "전이 학습 시 사용되는 사전 학습 모델의 편향성이 미세 조정된 모델로도 전달됩니다.",
            correct: true
        },
        {
            text: "모델 학습에 사용된 데이터가 편향되어 있습니다.",
            explain: "이는 가장 분명한 편향의 원인이지만, 유일한 원인은 아닙니다.",
            correct: true
        },
        {
            text: "모델이 최적화한 메트릭(metric)이 편향되어 있습니다.",
            explain: "모델 학습 방식은 편향이 발생되는 불분명한 원인 중 하나입니다. 모델은 어떤 메트릭을 고르든 아무 생각 없이 그에 맞춰 최적화를 합니다.",
            correct: true
        }
    ]}
/>


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

### 편향과 한계
https://huggingface.co/learn/course/ko/chapter1/8.md

# 편향과 한계

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

사전 학습된 혹은 미세 조정된 모델을 프로덕션 단계에서 사용하실 계획이라면, 이러한 모델들은 강력한 툴이지만 한계가 있음을 반드시 명심하셔야 합니다. 가장 큰 한계점은 리서처들이 무수히 많은 양의 데이터를 사전 학습에 사용하기 위해, 인터넷상에서 모을 수 있는 양질의 데이터와 함께 그렇지 않은 데이터까지 수집했을 가능성이 있다는 것입니다.

이를 빠르게 보여드리기 위해 `fill-mask` 파이프라인에 BERT 모델을 연결한 예제를 다시 살펴보겠습니다:

```python
from transformers import pipeline

unmasker = pipeline("fill-mask", model="bert-base-uncased")
result = unmasker("This man works as a [MASK].")
print([r["token_str"] for r in result])

result = unmasker("This woman works as a [MASK].")
print([r["token_str"] for r in result])
```

```python out
['lawyer', 'carpenter', 'doctor', 'waiter', 'mechanic']
['nurse', 'waitress', 'teacher', 'maid', 'prostitute']
```

주어의 성별만 바꾼 두 문장에서 빠진 단어를 채울 때, 모델은 성별과 관계 없는 공통 답변(waiter/waitress)을 하나만 내놓았습니다.  다른 답변들은 일반적으로 특정 성별에 편향된 답변이었습니다. 이를테면, 모델은 매춘이라는 단어와 연관된 상위 5개의 단어에 “여성”과 “일”을 포함시켰습니다. BERT가 전체 인터넷 상의 텍스트를 이용하여 사전 학습된 것이 아니라 [English Wikipedia](https://huggingface.co/datasets/wikipedia) 와 [BookCorpus](https://huggingface.co/datasets/bookcorpus) 같이 상당히 중립적인 데이터를 이용해 사전 학습 되었음에도 불구하고 이러한 현상이 일어납니다.

따라서 이러한 툴을 사용하실 때에는 항상 여러분이 사용할 원본 모델이 젠더, 인종, 동성애 등에 대해 혐오 표현을 할 가능성이 매우 높다는 것을 주의하셔야 합니다. 이러한 모델은 미세 조정을 거쳐도 내제된 편향성을 없애지 못합니다.

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

### 트랜스포머는 어떻게 동작하나요?
https://huggingface.co/learn/course/ko/chapter1/4.md

# 트랜스포머는 어떻게 동작하나요?

<CourseFloatingBanner
    chapter={1}
    classNames="absolute z-10 right-0 top-0"
/>

이번 단원에서는 트랜스포머(Transformer) 모델의 대략적인 구조를 알아보겠습니다.

## 트랜스포머 역사 훑어보기

아래에 트랜스포머 모델의 (짧은) 역사 중 주요한 지점을 나타냈습니다:

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/transformers_chrono.svg" alt="A brief chronology of Transformers models.">
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/transformers_chrono-dark.svg" alt="A brief chronology of Transformers models.">
</div>

[Transformer architecture](https://arxiv.org/abs/1706.03762)는 2017년 6월에 처음 소개되었습니다. 처음 연구 목적은 번역 작업 수행이었습니다. 이후로 다음과 같이 줄줄이 막강한 모델들이 세상에 등장했습니다:

- **2018년 6월**: [GPT](https://cdn.openai.com/research-covers/language-unsupervised/language_understanding_paper.pdf), 최초로 사전 학습된 트랜스포머 모델로 다양한 NLP 작업에 미세 조정(fine-tune)되도록 사용되었으며 SOTA(state-of-the-art) 성능 달성

- **2018년 10월**: [BERT](https://arxiv.org/abs/1810.04805), 또 다른 거대 사전 학습 언어 모델로, 더 좋은 문장 요약을 위해 설계 (이번 단원과 다음 단원에서 더 자세히 알아봐요!)

- **2019년 2월**: [GPT-2](https://cdn.openai.com/better-language-models/language_models_are_unsupervised_multitask_learners.pdf), 더 좋은 성능(그리고 더 큰) 버전의 GPT로, 윤리적 문제로 인해 즉시 공개되지 못하였음

- **2019년 10월**: [DistilBERT](https://arxiv.org/abs/1910.01108), BERT의 60% 빠른 속도에 메모리 측면에서 40% 가볍지만 BERT 성능의 97%를 재현하는 경량화 버전의 BERT

- **2019년 10월**: [BART](https://arxiv.org/abs/1910.13461) 와 [T5](https://arxiv.org/abs/1910.10683), 동일한 구조의 (처음으로) 원본 트랜스포머 모델의 구조를 그대로 따른 두 거대 사전학습 언어 모델

- **2020년 5월**, [GPT-3](https://arxiv.org/abs/2005.14165), 미세 조정 없이도 (zero-shot learning이라 부름) 다양한 작업을 훌륭하게 수행하는 GPT-2의 더 큰 버전

위 리스트는 단순히 여러 종류의 트랜스포머 모델들 중 몇몇을 강조하기 위한 목록입니다. 넓은 관점에서 트랜스포머 모델은 아래와 같이 세 개의 카테고리로 묶을 수 있습니다:

- GPT-계열 (*Auto-regressive* 트랜스포머 모델로도 불림)
- BERT-계열 (*Auto-encoding* 트랜스포머 모델로도 불림)
- BART/T5-계열 (*Sequence-to-sequence* 트랜스포머 모델로도 불림)

추후에 각 계열들에 대해 더 자세히 살펴보도록 하겠습니다.

## 트랜스포머는 언어 모델입니다

위에 언급한 모델(GPT, BERT, BART, T5 등)들은 *언어 모델(language model)*로서 학습 되었습니다. 다르게 말하면 이 모델들은 스스로 지도하는 방식으로 수많은 텍스트에 대해 학습된 모델들입니다. 이러한 자가 지도 학습(self-supervised learning)은 학습의 목적이 모델 입력으로부터 자동으로 계산되는 방식을 말합니다. 결국 사람이 데이터에 레이블을 달지 않아도 학습이 가능한 것입니다!

이러한 종류의 모델은 학습한 언어에 대해 통계 기반의 방식으로 이해를 하지만, 이는 몇몇 실생활 문제에 적합하지 않습니다. 그렇기 때문에 사전 학습된 모델은 *전이 학습(transfer learning)*이라 불리는 과정을 거칩니다. 이 과정에서 모델은 특정 작업에 맞춰 지도적(supervised)인 방법, 즉 사람이 레이블을 추가한 데이터를 사용하는 방법으로 미세 조정(fine-tune)이 이루어지는 단계를 거칩니다.

하나의 예시로 문장 내에서 이전 *n*개의 단어를 읽고 다음에 올 단어를 에측하는 문제를 들 수 있습니다. 이를 과거와 현재의 입력 정보를 이용하는 방식(미래에 올 입력 정보는 이용하지 않습니다)이기 때문에 *인과적 언어 모델링(causal language modeling)*이라고 부릅니다.

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/causal_modeling.svg" alt="Example of causal language modeling in which the next word from a sentence is predicted.">
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/causal_modeling-dark.svg" alt="Example of causal language modeling in which the next word from a sentence is predicted.">
</div>

다른 예시로 *마스크 언어 모델링(masked language modeling)*을 들 수 있습니다. 여기서 모델은 문장 내에 마스킹 된 단어를 예측합니다.

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/masked_modeling.svg" alt="Example of masked language modeling in which a masked word from a sentence is predicted.">
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/masked_modeling-dark.svg" alt="Example of masked language modeling in which a masked word from a sentence is predicted.">
</div>

## 트랜스포머는 거대 모델입니다

(DistilBERT와 같은 몇몇 예외를 제외하고) 모델의 성능을 향상시키는 일반적인 전략은 사전 학습에 사용하는 "모델과 데이터의 크기를 늘리기" 입니다.

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/model_parameters.png" alt="Number of parameters of recent Transformers models" width="90%">
</div>

불행히도 아주 아주 큰 모델의 경우, 학습을 위해 어마무시한 양의 데이터를 필요로 하여 시간과 컴퓨팅 리소스 관점에서 비용이 매우 많이 듭니다. 아래 그래프에서 볼 수 있듯이 이는 환경 오염 문제로 이어지기도 합니다.

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/carbon_footprint.svg" alt="The carbon footprint of a large language model.">
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/carbon_footprint-dark.svg" alt="The carbon footprint of a large language model.">
</div>

<Youtube id="ftWlj4FBHTg"/>

위 비디오는 사전 학습 과정이 환경에 끼치는 부정적 영향을 꾸준히 줄이기 위해 노력하는 한 팀의 (초거대) 언어 모델 프로젝트를 소개합니다. 최적의 하이퍼파라미터를 찾기 위한 수많은 시도에는 아직 많은 여정이 남아 있습니다.

매번 리서치 팀, 학생 기관, 기업 등등이 밑바닥부터 모델을 학습시킨다고 생각해보세요. 이는 결국 정말 어마어마한 양의 글로벌 비용으로 이어질 것입니다!

이제 왜 언어 모델을 공유하는 것이 중요한지 아시겠나요? 학습 가중치를 공유하고, 이미 학습시킨 가중치에 이를 차곡차곡 쌓아 올리는 방식으로 커뮤니티의 컴퓨팅 비용과 탄소 발자국을 줄일 수 있기 때문입니다.


## 전이 학습(Transfer Learning)

<Youtube id="BqqfQnyjmgg" />

*사전 학습(Pretraining)*시에는 모델을 밑바닥부터 학습시키게 됩니다. 모델 가중치를 랜덤하게 초기화하고 사전 지식 없이 학습을 시작합니다.

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/pretraining.svg" alt="The pretraining of a language model is costly in both time and money.">
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/pretraining-dark.svg" alt="The pretraining of a language model is costly in both time and money.">
</div>

이러한 사전 학습 과정은 엄청난 양의 데이터로 이루어지기 때문에 방대한 양의 코퍼스 데이터와 수 주 씩 걸리는 학습 시간을 필요로 하기도 합니다.

반면에 *미세 조정(Fine-tuning)*이란 모델이 모두 사전 학습을 마친 **이후에** 하는 학습을 의미합니다. 미세 조정을 하기 위해서 우선 사전 학습된 언어 모델을 가져오고, 여러분이 할 작업에 특화된 데이터셋을 이용해 추가 학습을 수행합니다. 잠깐만요, 그냥 한번에 최종 태스크에 맞춰 학습시키면 안될까요? 이렇게 하는 데에는 몇 가지 이유가 있습니다:

- 사전 학습된 모델은 이미 미세 조정 데이터셋과 유사한 데이터셋으로 학습이 이루어진 상태입니다. 결국 모델이 사전 학습시에 얻은 지식(이를테면, NLP 문제에서 사전 학습된 모델이 얻게 되는 언어의 통계적 이해)을 십분 활용해 미세 조정에 활용할 수 있게 됩니다.
- 사전 학습된 모델은 이미 방대한 데이터로 학습되었기 떄문에 미세 조정에서는 원하는 성능을 얻기까지 적은 양의 데이터만 필요로 하게 됩니다.
- 위와 같은 이유로, 원하는 성능을 얻기까지 적은 시간과 리소스만 필요하게 됩니다.

예시로, 영어로 사전 학습된 모델을 활용해 arXiv 코퍼스로 미세 조정하여 과학/연구 기반 모델을 만들 수 있습니다. 이 때 미세 조정에는 적은 양의 데이터만 필요할 것입니다. 여기서 사전 학습 과정에서 얻은 지식이 “전이”되었다고 하여 *전이 학습(transfer learning)*이라 부릅니다.

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/finetuning.svg" alt="The fine-tuning of a language model is cheaper than pretraining in both time and money.">
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/finetuning-dark.svg" alt="The fine-tuning of a language model is cheaper than pretraining in both time and money.">
</div>

모델의 미세 조정은 이렇게 시간, 데이터, 경제, 그리고 환경 비용 측면에서 훨씬 저렴합니다. 뿐만 아니라 전체 사전 학습 과정보다 제약이 적기 때문에 다양한 방식의 미세 조정을 쉽고 빠르게 반복할 수 있습니다.

이렇듯 엄청나게 많은 데이터를 확보하지 않은 이상 미세 조정 방식이 밑바닥부터 학습시키는 방식보다 더 좋은 결과를 낼 수 있습니다. 따라서 항상 여러분의 지금 작업 내용과 최대한 비슷한 사전 학습 모델을 불러와 미세 조정하세요!

## 일반적인 구조

이번 섹션에서는 전반적인 트랜스포머 모델의 구조에 대해 알아보겠습니다. 설령 몇몇 개념을 이해하지 못하게 되더라도 걱정하지 마세요. 추후에 각 구성 요소를 자세하게 다루는 섹션이 있으니까요.

<Youtube id="H39Z_720T5s" />

## 구조 소개

모델은 기본적으로 두 개의 블럭으로 이루어져 있습니다:

* **인코더 (왼쪽)**: 인코더는 입력을 받아 입력(혹은 입력의 특징)을 구축합니다. 이는 인코더 모델이 입력을 이해하는 데에 최적화되어 있음을 의미합니다.
* **디코더 (오른쪽)**: 디코더는 인코더의 (특징) 표현과 또 다른 입력을 활용해 타겟 시퀀스를 생성합니다. 이는 디코더 모델이 출력을 생성하는 데에 최적화되어 있음을 의미합니다.

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/transformers_blocks.svg" alt="Architecture of a Transformers models">
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/transformers_blocks-dark.svg" alt="Architecture of a Transformers models">
</div>

트랜스포머 모델의 각 파트는 태스크에 따라 다음과 같이 개별적으로 쓰일 수도 있습니다:

* **인코더 모델(Encoder-only models)**: 문장 분류, 개체명 인식과 같이 입력에 대한 높은 이해를 요구하는 작업에 특화
* **디코더 모델(Decoder-only models)**: 텍스트 생성과 같이 생성 관련 작업에 특화
* **인코더-디코더(Encoder-decoder) 모델 또는 시퀀스-투-시퀀스 (Sequence-to-sequence) 모델**: 번역, 요약과 같이 입력을 필요로 하는 생성 관련 작업에 특화

다음 단원들에서 각각의 구조에 대해 더 깊게 살펴보도록 하겠습니다.

## 어텐션 레이어(Attention layers)

트랜스포머 모델의 중요한 특징은 *어텐션 레이어(attention layers)*라 불리는 특수한 레이어로 구성되었다는 점입니다. 사실, 트랜스포머 구조를 처음 소개한 논문 제목마저 ["Attention Is All You Need"](https://arxiv.org/abs/1706.03762)입니다! 어텐션 레이어의 디테일에 대해서는 추후 강의에서 자세히 다루겠지만, 우선은 이 레이어가 단어의 표현을 다룰 때 입력으로 넣어준 문장의 특정 단어에 어텐션(주의)을 기울이고 특정 단어는 무시하도록 알려준다는 사실을 기억하셔야 합니다.

이러한 상황을 설명하기 위해 영어 텍스트를 프랑스어로 번역하는 작업을 생각해 보겠습니다. “You like this course”라는 입력이 주어지면 번역 모델은 “like”라는 단어를 알맞게 번역하기 위해 인접한 단어 “You”에도 주의를 기울입니다. 이는 프랑스어의 동사 “like”가 문맥에 따라 다른 의미로 해석되기 때문입니다. 그러나 문장의 나머지 단어들은 이 단어를 번역하는 데에 크게 유용하지 않습니다. 같은 맥락에서 “this”를 번역할 때, 이 단어는 연결 명사가 남성형이거나 여성형이냐에 따라 다르게 해석될 수 있기 때문에 모델은 “course”라는 단어에 주의를 기울입니다. 여기서도 문장의 나머지 다른 단어들은 “this”의 의미를 해석하는 데에 크게 중요하지 않습니다. 더 복잡한 문법의 문장에서 모델은 문장의 각 단어를 적절히 번역하기 위해 더 멀리 떨어진 단어들에 대해서도 주의를 기울여야 합니다.

이와 동일한 개념이 자연어의 모든 문제에 적용됩니다. 단어는 그 자체로도 의미를 갖지만 문맥상 앞뒤의 다른 단어정보를 알게 되면 의미가 달라지기도 하죠.

지금까지 어텐션 레이어가 무엇인지 알아보았으니 트랜스포머 구조에 대해 살펴보겠습니다.

## 원본 구조

트랜스포머 구조는 처음에 번역을 위해 만들어졌습니다. 학습시에 인코더는 특정 언어의 입력 문장을 받고, 동시에 디코더는 타겟 언어로된 동일한 의미의 문장을 받습니다. 인코더에서 어텐션 레이어는 문장 내의 모든 단어를 활요할 수 있습니다(방금 보았듯이 주어진 단어의 번역은 문장의 전후를 살펴보아야 하니까요). 반면, 디코더는 순차적으로 작동하기 때문에 문장 내에서 이미 번역이 이루어진 부분에만 주의를 기울일 수 밖에 없습니다. 이로 인해 현재 생성(번역)되고 있는 단어의 앞에 단어들만 이용할 수 있죠. 예시로, 번역된 타겟의 처음 세 단어를 예측해 놨을 때, 이 결과를 디코더로 넘기면 디코더는 인코더로부터 받은 모든 입력 정보를 함께 이용해 네 번째 올 단어를 예측하는 것입니다.

모델이 타겟 문장에 대한 액세스(access)가 있는 상황에서, 훈련 속도를 높이기 위해 디코더는 전체 타겟을 제공하지만 뒤에 올 단어들을 사용할 수 없습니다. (모델이 두 번째 올 단어를 예측하기 위해 두 번째 위치 단어를 접근할 수 있다면 예측이 의미없어지겠죠?) 예를 들어, 네 번째 단어를 예측할 때 어텐션 레이어는 1~3 번째 단어에만 액세스하도록 합니다.

원래 처음 트랜스포머의 구조는 아래와 같이 왼쪽에는 인코더, 오른쪽에는 디코더가 있는 형태를 지닙니다:

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/transformers.svg" alt="Architecture of a Transformers models">
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/transformers-dark.svg" alt="Architecture of a Transformers models">
</div>

디코더 블럭의 첫 번째 어텐션 레이어는 모든 이전의 디코더 입력에 대해 주의를 기울이지만, 두 번째 어텐션 레이어는 인코더의 출력만 사용하는 점을 주목하세요. 이로써 디코더는 전체 입력 문장에 액세스하여 현재 올 단어를 잘 예측하게 되는 것입니다. 이는 서로 다른 언어는 서로 다른 어순을 갖거나 문장의 뒷부분에 등장하는 문맥이 주어진 단어의 가장 적합한 번역을 결정할 수 있기 때문에 매우 유용합니다.

*어텐션 마스크(attention mask)*는 또한 모델이 몇몇 특수 단어들에 어텐션을 주는 것을 막는 데에 사용됩니다. 그 예로, 여러 다른 길이의 문장들을 모아 배치를 구성할 때, 패딩이라고 불리는 특수한 단어를 넣어 문장들의 길이를 맞춰주는데 사용하는 경우 있습니다.

## 구조(Architectures) vs. 체크포인트(Checkpoints)

트랜스포머 모델을 본격적으로 공부하기 앞서, 모델(models)과 함께 *구조(architectures)*와 *체크포인트(checkpoints)*라는 단어를 들으시게 될겁니다. 이 셋은 아래와 같이 조금 다른 의미를 갖고 있습니다:

* **구조(Architecture)**: 모델의 뼈대를 의미하는 용어로, 모델 내부의 각 레이어와 각 연산 작용들을 의미합니다.
* **체크포인트(Checkpoints)**: 주어진 구조(architecture)에 적용될 가중치들을 의미합니다.
* **모델(Model)**: 사실 모델은 “구조”나 “가중치”만큼 구체적이지 않은, 다소 뭉뚱그려 사용되는 용어입니다. 이 강의에서는 모호함을 피하기 위해 *구조(architecture)*와 *체크포인트(checkpoint)*를 구분해서 사용하도록 하겠습니다.

예를 들면, BERT는 구조에 해당하고, Google 팀이 최초 공개에서 내놓은 학습 가중치 셋인 `bert-base-cased`는 체크포인에 해당합니다. 그렇지만 “BERT 모델”, “`bert-base-cased` 모델” 등과 같이 구분하지 않고 사용하기도 합니다.


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

### 시퀀스-투-시퀀스 모델
https://huggingface.co/learn/course/ko/chapter1/7.md

# 시퀀스-투-시퀀스 모델

<CourseFloatingBanner
    chapter={1}
    classNames="absolute z-10 right-0 top-0"
/>

<Youtube id="0_4KEb08xrE" />

인코더-디코더 모델(Encoder-decoder models) (*시퀀스-투-시퀀스 모델(sequence-to-sequence models)*로 부르기도 합니다)은 트랜스포머 구조의 인코더, 디코더 둘을 모두 사용합니다. 각 단계마다, 인코더의 어텐션 레이어는 초기 문장의 모든 단어에 액세스 할 수 있는 반면, 디코더의 어텐션 레이어는 주어진 단어 앞에 위치한 단어들에만 액세스 할 수 있습니다.

이러한 모델의 사전 학습은 인코더 혹은 디코더 모델의 방식을 모두 사용할 수 있지만 조금 더 복잡합니다. 이를테면, [T5](https://huggingface.co/t5-base)는 (여러 단어를 포함하기도 하는) 임의의 텍스트 범위를 하나의 특수 마스크 토큰으로 바꾸고 마스크 단어를 대체할 텍스트를 예측하는 방식으로 사전 학습 되었습니다.

시퀀스-투-시퀀스 모델은 요약, 번역, 생성 질의 응답과 같이 주어진 입력을 기반으로 새로운 문장을 생성하는 작업에 가장 적합합니다.

이 계열을 대표하는 모델은 다음과 같습니다:

- [BART](https://huggingface.co/transformers/model_doc/bart.html)
- [mBART](https://huggingface.co/transformers/model_doc/mbart.html)
- [Marian](https://huggingface.co/transformers/model_doc/marian.html)
- [T5](https://huggingface.co/transformers/model_doc/t5.html)

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

### 자연어 처리(Natural Language Processing)
https://huggingface.co/learn/course/ko/chapter1/2.md

# 자연어 처리(Natural Language Processing)

<CourseFloatingBanner
    chapter={1}
    classNames="absolute z-10 right-0 top-0"
/>

트랜스포머 모델을 공부하기에 앞서 자연어 처리(NLP)가 무엇인지, 그리고 왜 NLP가 중요한지 빠르고 간단하게 살펴보겠습니다.

## NLP가 무엇인가요?

NLP(Natural Language Processing)란 사람의 언어와 관련된 모든 것을 이해하는 데에 중점을 둔 언어학 및 기계 학습(머신 러닝) 분야를 말합니다. NLP의 목적은 단순히 하나의 개별 단어를 이해하는 것을 넘어, 해당 단어들의 문맥을 이해하는 것입니다.

아래는 가장 일반적인 NLP 작업과 그 예시입니다:

- **전체 문장 분류**: 리뷰에 드러난 감정 파악하기, 스팸 메일 분류하기, 문장이 문법적으로 올바른지 혹은 문장 쌍이 논리적으로 관련이 있는지 없는지 결정하기
- **문장 내 단어 분류**: 문장 구성 성분(명사, 동사, 형용사 등) 혹은 개체명(사람, 장소, 기관) 식별하기
- **텍스트 컨텐츠 생성**: 자동 생성 텍스트로 프롬프트 작성하기, 텍스트 내 마스킹 된 단어의 빈칸 채우기
- **텍스트 안에서 정답 추출하기**: 지문과 질의가 주어질 때 지문에 주어진 정보를 이용해 질의에 대한 정답 추출하기
- **입력 텍스트로부터 새로운 문장 생성하기**: 입력 텍스트를 다른 언어로 번역하거나, 요약하기

하지만 NLP는 위와 같은 텍스트 처리에만 제한되지 않습니다. NLP에서는 오디오 샘플의 스크립트 생성 및 이미지의 설명문 생성과 같이 음성 인식과 컴퓨터 비전 분야에서의 까다로운 문제 또한 다룹니다.

## 왜 NLP가 어렵나요?

컴퓨터와 사람은 서로 정보를 처리하는 방식이 다릅니다. 이를테면, “나는 배고파”라는 문장을 읽을 때 우리는 바로 그 의미를 이해할 수 있습니다. 마찬가지로 사람은 “나는 배고파”, “나 슬퍼”와 같은 문장 쌍이 주어질 때, 두 문장이 얼마나 유사한지 쉽게 판단할 수 있습니다. 그러나, 기계 학습 모델은 사람만큼 이를 쉽게 할 수 없습니다. 우선 모델이 텍스트를 학습할 수 있도록 텍스트가 처리 과정을 거쳐야 하는데, 사람의 언어 체계는 매우 복잡하기 때문에 이러한 처리가 어떻게 이루어져야 하는지 면밀히 고민해야 합니다. 따라서 텍스트 표현 방법과 관련한 수많은 연구가 진행되어 왔고, 다음 챕터에서 그 중 몇 가지 방법들을 소개해드리겠습니다.

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