일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- ml 웹서빙
- K최근접이웃
- KNN
- DASH
- CUDA
- 3유형
- ㅂ
- Ai
- dl
- 성능
- 공간시각화
- Kaggle
- streamlit
- 1유형
- fastapi
- gradio
- 딥러닝
- 공간분석
- 2유형
- QGIS설치
- qgis
- pytorch
- webserving
- 인공지능
- 머신러닝
- GPU
- 빅데이터분석기사
- 실기
- 예제소스
- 캐글
- Today
- Total
에코프로.AI
[HuggingFace] Fine-tuning - 1 (Processing the data) 본문
Processing the data (데이터 처리)
이전 장의 예를 계속하면 PyTorch에서 하나의 배치에 대한 시퀀스 분류기를 훈련하는 방법은 다음과 같습니다.
import torch
from transformers import AdamW, AutoTokenizer, AutoModelForSequenceClassification
# Same as before
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")
# This is new
batch["labels"] = torch.tensor([1, 1])
optimizer = AdamW(model.parameters())
loss = model(**batch).loss
loss.backward()
optimizer.step()
물론 두 문장으로만 모델을 훈련한다고 해서 좋은 결과를 얻을 수 있는 것은 아닙니다. 더 나은 결과를 얻으려면 더 큰 데이터 세트를 준비해야 합니다.
이 섹션에서는 William B. Dolan and Chris Brockett (윌리엄 B. 돌란과 크리스 브로켓)의 논문에서 소개된 MRPC(Microsoft Research Paraphrase Corpus) 데이터 집합을 예로 들어 설명합니다. 이 데이터 집합은 5,801쌍의 문장으로 구성되어 있으며, 의역인지 아닌지(즉, 두 문장이 같은 의미인지 아닌지)를 나타내는 레이블이 있습니다. 이 장에서 이 데이터 세트를 선택한 이유는 작은 데이터 세트이기 때문에 훈련을 실험하기 쉽기 때문입니다.
Loading a dataset from the Hub (허브에서 데이터 세트 로드)
허브에는 모델만 있는 것이 아니라 다양한 언어로 된 여러 데이터 세트도 있습니다. 여기에서 데이터 세트를 찾아볼 수 있으며, 이 섹션을 살펴본 후 새 데이터 세트를 로드하고 처리하는 것이 좋습니다(일반 설명서는 여기에서 참조). 하지만 지금은 MRPC 데이터 세트에 집중해 보겠습니다! 이것은 10개의 텍스트 분류 작업에서 ML 모델의 성능을 측정하는 데 사용되는 학술적 벤치마크인 GLUE 벤치마크를 구성하는 10개의 데이터 세트 중 하나입니다 .
🤗 Datasets 라이브러리는 허브에 데이터세트를 다운로드하고 캐시하는 매우 간단한 명령을 제공합니다. MRPC 데이터세트는 다음과 같이 다운로드할 수 있습니다.
- datasets 라이브러리를 먼저 설치해 줍니다.
pip install datasets
from datasets import load_dataset
raw_datasets = load_dataset("glue", "mrpc")
raw_datasets
결과 값
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
})
})
보시다시피, 우리는 훈련(train) 세트, 검증(validation) 세트, 테스트(test) 세트를 포함하는 DatasetDict 객체를 얻습니다. 각 세트에는 여러 열( sentence1, sentence2, label, idx)과 가변적인 수의 행이 포함되어 있으며, 이는 각 세트의 요소 수입니다(훈련(train) 세트에는 3,668쌍의 문장이 있고, 검증(validation) 세트에는 408쌍이 있고, 테스트(test) 세트에는 1,725쌍이 있습니다).
이 명령은 기본적으로 ~/.cache/huggingface/datasets에 데이터 세트를 다운로드하여 캐시합니다. 2장에서 HF_HOME 환경 변수를 설정하여 캐시 폴더를 사용자 지정할 수 있다는 점을 기억하세요.
※ 이거 설정하는거 추가 확인해보자!!
사전처럼 인덱싱을 통해 raw_datasets 객체의 각 문장 쌍에 액세스할 수 있습니다:
raw_train_dataset = raw_datasets["train"]
raw_train_dataset[0]
결과 값
{'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의 특징을 검사하면 됩니다. 이렇게 하면 각 열의 유형을 알 수 있습니다:
raw_train_dataset.features
결과 값
{'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)}
뒤에서 레이블은 클래스 레이블 유형이며, 레이블 이름에 대한 정수의 매핑은 이름 폴더에 저장됩니다. 0은 not_equivalent(의미가 다르다)에 해당하고 1은 equivalent(의미가 같다)에 해당합니다.
Preprocessing a dataset (데이터 집합 전처리)
dataset(데이터 집합)을 전 처리하려면 텍스트를 모델이 이해할 수 있는 숫자로 변환해야 합니다. 이전 장에서 보았듯이 토큰화 도구를 사용하면 이 작업을 수행할 수 있습니다. 토큰화 도구에 하나의 문장 또는 문장 목록을 공급할 수 있으므로, 이렇게 각 쌍의 첫 번째 문장과 두 번째 문장을 모두 직접 토큰화할 수 있습니다:
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"])
하지만 두 개의 시퀀스를 모델에 전달하고 두 문장이 의역인지 아닌지를 예측할 수는 없습니다. 두 시퀀스를 한 쌍으로 처리하고 적절한 전처리를 적용해야 합니다. 다행히도 토큰화 도구는 한 쌍의 시퀀스를 가져와서 BERT 모델이 예상하는 방식으로 준비할 수 있습니다:
inputs = tokenizer("This is the first sentence.", "This is the second one.")
inputs
결과 값
{
'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장에서 input_ids와 attention_mask 키에 대해 설명했지만 token_type_ids에 대해서는 뒤로 미뤘습니다. 이 예제에서는 입력의 어느 부분이 첫 번째 문장이고 어느 부분이 두 번째 문장인지 모델에 알려주는 역할을 합니다.
input_ids 내부의 ID를 다시 단어로 디코딩하면 다음과 같습니다:
tokenizer.convert_ids_to_tokens(inputs["input_ids"])
결과 값
['[CLS]', 'this', 'is', 'the', 'first', 'sentence', '.', '[SEP]', 'this', 'is', 'the', 'second', 'one', '.', '[SEP]']
따라서 모델은 두 개의 문장이 있을 때 입력이 [CLS] sentence1 [SEP] sentence2 [SEP] 형식이 될 것으로 예상합니다. 이를 token_type_ids에 맞추면 다음과 같은 결과가 나옵니다:
['[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] 문장1 [SEP]에 해당하는 부분은 모두 token_type_ids 가 0이고, 문장2 [SEP]에 해당하는 다른 부분은 모두 토큰 유형 ID가 1입니다.
다른 체크포인트를 선택하면 토큰화된 입력에 token_type_ids 가 반드시 포함되지는 않습니다(예: DistilBERT 모델을 사용하는 경우 반환되지 않음). 모델이 사전 학습 중에 이를 확인했기 때문에 모델이 이를 어떻게 처리할지 알 때만 반환됩니다.
여기서 BERT는 token type ID 로 사전 학습되며, 1장에서 설명한 마스크 언어 모델링 목표 외에도 다음 문장 예측이라는 추가 목표가 있습니다. 이 작업의 목표는 문장 쌍 간의 관계를 모델링하는 것입니다.
다음 문장 예측에서는 모델에 무작위로 마스킹된 토큰이 포함된 문장 쌍이 제공되고 두 번째 문장이 첫 번째 문장을 따르는지 예측하도록 요청합니다. 이 작업을 어렵지 않게 만들기 위해 절반은 추출된 원본 문서에서 두 문장이 서로 이어지고, 나머지 절반은 두 문장이 서로 다른 두 문서에서 나오도록 합니다.
일반적으로 토큰화된 입력값에 token_type_ids가 있는지 여부는 걱정할 필요가 없습니다. 토큰화 도구와 모델에 동일한 체크포인트를 사용하기만 하면 토큰화 도구가 모델에 무엇을 제공할지 알기 때문에 모든 것이 정상적으로 작동할 것입니다.
이제 토큰화 도구가 한 쌍의 문장을 어떻게 처리하는지 살펴보았으니, 토큰화 도구를 사용해 전체 데이터셋을 토큰화할 수 있습니다. 이전 장에서처럼 토큰화 도구에 첫 문장 목록과 두 번째 문장 목록을 제공함으로써 문장 쌍의 목록을 제공할 수 있습니다. 이는 2장에서 살펴본 패딩(padding) 및 잘라내기(truncation) 옵션과도 호환됩니다. 따라서 학습 데이터 집합을 사전 처리하는 한 가지 방법은 다음과 같습니다:
tokenized_dataset = tokenizer(
raw_datasets["train"]["sentence1"],
raw_datasets["train"]["sentence2"],
padding=True,
truncation=True,
)
이 방법은 잘 작동하지만 사전(keys, input_ids, attention_mask, token_type_ids 및 목록의 목록인 값)을 반환한다는 단점이 있습니다. 또한 토큰화 중에 전체 데이터셋을 저장할 수 있는 충분한 RAM이 있는 경우에만 작동합니다(반면 🤗 데이터셋 라이브러리의 데이터셋은 디스크에 저장된 Apache Arrow 파일이므로 요청하는 샘플만 메모리에 로드된 상태로 유지합니다).
데이터를 데이터셋으로 유지하기 위해 Dataset.map() 메서드를 사용합니다. 이 메서드는 토큰화보다 더 많은 전처리가 필요한 경우 추가적인 유연성을 제공합니다. map() 메서드는 데이터 세트의 각 요소에 함수를 적용하여 작동하므로 입력을 토큰화하는 함수를 정의해 보겠습니다:
def tokenize_function(example):
return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
이 함수는 데이터 세트의 항목과 같은 사전을 가져와 input_ids, attention_mask, token_type_ids 키가 포함된 새 사전을 반환합니다. 토큰화기는 앞서 살펴본 것처럼 문장 쌍의 목록에서 작동하므로 예제 사전에 여러 샘플(각 키가 문장 목록)이 포함되어 있는 경우에도 작동한다는 점에 유의하세요. 이렇게 하면 map() 호출에서 batched=True 옵션을 사용할 수 있으므로 토큰화 속도가 크게 빨라집니다. 이 토큰화 도구는 🤗 토큰화 라이브러리에서 Rust( 성능과 안정성에 중점을 둔 시스템 프로그래밍 언어 )로 작성된 토큰화 도구로 지원됩니다. 이 토큰화 도구는 매우 빠를 수 있지만, 한 번에 많은 입력을 제공할 때만 가능합니다.
지금은 토큰화 함수에서 패딩 인수를 제외했습니다. 이는 모든 샘플을 최대 길이로 패딩하는 것은 효율적이지 않기 때문입니다. 일괄 처리할 때 샘플을 패딩하면 전체 데이터 세트의 최대 길이가 아닌 해당 일괄 처리의 최대 길이로만 패딩하면 되므로 더 좋습니다. 이렇게 하면 입력 길이가 매우 가변적인 경우 많은 시간과 처리 능력을 절약할 수 있습니다!
다음은 모든 데이터 세트에 토큰화 함수를 한 번에 적용하는 방법입니다. 이 함수가 각 요소에 개별적으로 적용되지 않고 데이터 집합의 여러 요소에 한 번에 적용되도록 매핑 호출에 batched=True를 사용하고 있습니다. 이렇게 하면 전처리를 더 빠르게 처리할 수 있습니다.
tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
tokenized_datasets
🤗 데이터세트 라이브러리가 이 처리를 적용하는 방식은 전처리 함수가 반환한 사전의 각 키에 대해 하나씩 데이터세트에 새 필드를 추가하는 것입니다:
결과 값
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
})
})
전처리 함수를 map()에 적용할 때 num_proc 인수를 전달하여 멀티프로세싱을 사용할 수도 있습니다. 여기서는 🤗 토큰화 라이브러리가 이미 여러 스레드를 사용하여 샘플을 더 빠르게 토큰화하기 때문에 이 방법을 사용하지 않았지만, 이 라이브러리가 지원하는 빠른 토큰화 도구를 사용하지 않는다면 전처리 속도를 높일 수 있습니다.
토큰화 함수는 input_ids, attention_mask, token_type_ids 키가 포함된 사전을 반환하므로 이 세 필드가 데이터 세트의 모든 분할에 추가됩니다. 전처리 함수가 map()을 적용한 데이터 세트의 기존 키에 대해 새 값을 반환하는 경우 기존 필드가 변경되었을 수도 있습니다.
마지막으로 해야 할 일은 요소를 일괄 처리할 때 모든 예제를 가장 긴 요소의 길이에 맞춰 패딩하는 것인데, 이를 동적 패딩이라고 합니다.
Dynamic padding (동적 패딩)
배치 내에서 샘플을 조합하는 함수를 collate function (콜레이트 함수)라고 합니다. DataLoader 를 빌드할 때 전달할 수 있는 인자로, 기본값은 샘플을 PyTorch 텐서로 변환하고 연결(요소가 목록, 튜플 또는 사전인 경우 재귀적으로)하는 함수입니다. 우리의 경우 입력값이 모두 같은 크기가 아니기 때문에 이 기능은 불가능합니다. 각 배치에 필요한 만큼만 패딩을 적용하고 패딩이 많은 입력이 지나치게 길어지는 것을 방지하기 위해 의도적으로 패딩을 연기했습니다. 이렇게 하면 훈련 속도가 상당히 빨라지지만, TPU로 훈련하는 경우 문제가 발생할 수 있습니다. TPU는 추가 패딩이 필요한 경우에도 고정된 모양을 선호합니다.
실제로 이 작업을 수행하려면 일괄 처리하려는 데이터 세트의 항목에 정확한 양의 패딩을 적용하는 collate function (콜레이트 함수)를 정의해야 합니다. 다행히도 🤗 트랜스포머 라이브러리는 DataCollatorWithPadding 을 통해 이러한 함수를 제공합니다. 이 함수는 인스턴스화할 때 tokenizer를 필요로 하며(어떤 패딩 토큰을 사용할지, 모델이 입력의 왼쪽에 패딩을 기대하는지 오른쪽에 패딩을 기대하는지 알기 위해), 필요한 모든 작업을 수행합니다:
from transformers import DataCollatorWithPadding
data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
이 새로운 장난감을 테스트하기 위해 훈련(train) 세트에서 함께 일괄 처리할 몇 가지 샘플을 가져와 보겠습니다. 여기에서는 idx, sentence1, sentence2 열은 필요하지 않고 문자열을 포함하므로 제거하고(문자열로 텐서를 만들 수 없으므로) 배치의 각 항목의 길이를 살펴봅니다:
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"]]
결과 값
[50, 59, 47, 67, 59, 50, 62, 32]
당연히 32에서 67까지 다양한 길이의 샘플을 얻게 됩니다. 동적 패딩은 이 배치의 샘플이 모두 배치 내 최대 길이인 67로 패딩되어야 함을 의미합니다. 동적 패딩이 없으면 모든 샘플을 전체 데이터 세트의 최대 길이 또는 모델이 허용할 수 있는 최대 길이로 패딩해야 합니다. data_collator 가 배치에 동적으로 패딩을 제대로 적용하고 있는지 다시 한 번 확인해 보겠습니다:
batch = data_collator(samples)
{k: v.shape for k, v in batch.items()}
결과 값
{'attention_mask': torch.Size([8, 67]),
'input_ids': torch.Size([8, 67]),
'token_type_ids': torch.Size([8, 67]),
'labels': torch.Size([8])}
좋아 보이네요! 이제 원시 텍스트에서 모델이 처리할 수 있는 배치로 전환되었으므로 이제 미세 조정할 준비가 되었습니다!
참고사이트 : https://huggingface.co/learn/nlp-course/chapter3/2?fw=pt
끝~

'AI Tutorial' 카테고리의 다른 글
[HuggingFace] Fine-tuning - 3 (A full training) (0) | 2025.01.09 |
---|---|
[CUDA] 2. pytorch에서 GPU를 사용하는 방법 (0) | 2025.01.08 |
[CUDA] 1. 소개 및 설치 for windows(Feat. GTX 1650) (0) | 2025.01.03 |
[DeepLearning] 사전학습, 전이학습, RAG, 미세조정 (0) | 2025.01.03 |
[Hugging Face - 7] Putting it all together (모든 것을 종합하기) (1) | 2025.01.03 |