책 리뷰/101가지 문제로 배우는 딥러닝 허깅페이스 트랜스포머

02장. DistilBERT 파인튜닝 및 평가 (문제8~11)

조조링 2024. 11. 24. 02:48
728x90
반응형

문제8. 데이터세트 클래스 생성

Torch.utils.data.Dataset을 상속하는 IMDbFDataset이라는 클래스를 작성하세요. 그리고 문제 7의 IMDB 데이터세트에서 생성한 학습, 검증, 테스트 데이터세트를 입력해서 이 클래스를 인스턴스화합니다. 클래스 및 인스턴스화 개념은 문제2를 참고하세요. 

 

상속이란? 
클래스를 생성할 때 다른 클래스의 기능을 가져다 쓰는 것을 상속이라고 합니다. 예를 들어 20층 건물을 지을 때, 타 고층 빌딩의 설계도를 참고하면 도움이 됩니다. 엘리베이터 기능, 공조 기능, 지진 설계 등을 빌려와 쓸 수 있다면 시간과 노력이 절감됩니다. 즉, 객체 A를 짓는 설계도 클래스의 기능을 물려받아 객체 B를 짓는 설계도 클래스에서 그대로 사용한다는 개념이 바로 상속입니다. 

 

Dataset 클래스를 상속하는 IDMbDataset 클래스를 정의해보자. __init__은 인스턴스화에 쓰이는 생성자이다. 

생성자는 클래스로 객체를 생성할 때 자동으로 호출되는 메서드로, 객체를 미리 설정한 값으로 초기화합니다. 생성자 역시 클래스 내 메서드이므로 항상 자신을 가리키는 매개변수 self를 포함합니다.

 

import torch

# Dataset 클래스를 상속하는 IMDBDataset 클래스를 정의
class IMDbDataset(torch.utils.data.Dataset):

    # 생성자 __init__()
    # 자신을 가르키는 매개변수 self 포함
    # 변수를 저장하기 위해 self.변수명을 사용
    def __init__(self, encodings, labels):
        self.encodings = encodings
        self.labels = labels

    # 자신을 가르키는 매개변수 self 포함
    def __getitem__(self, idx):
        # self.encoding에 담긴 키(key)와 키값(value)을 items()로 추출
        # 이 값을 key와 val 변수에 담아 새로운 키(key)와 키값(torch.tensor(val[idx]))를 갖는 딕셔너리 생성
        # 딕셔너리는 {"key1:value1", "key2:value2", ,,,} 형태를 지닌 파이썬 데이터 구조
        # val[idx]에 담긴 데이터를 torch.tensor()를 통해 파이토치 텐서로 변환
        item = {key: torch.tensor(val[idx]) for key, val in self.encodings.items()}

        # self.lables[idx]에 담긴 데이터를 torch.tensor()를 통해 파이토치 텐서 변환
        item['labels'] = torch.tensor(self.labels[idx])
        return item

    # 자신을 가르키는 매개변수 self 포함
    def __len__(self):
        return len(self.labels)

train_dataset = IMDbDataset(train_encodings, train_labels)
val_dataset = IMDbDataset(val_encodings, val_labels)
test_dataset = IMDbDataset(test_encodings, test_labels)

 

  • __getitem__()은 대괄호 [] 안에 표기된 인덱스와 결합된 객체를 얻어 오는 파이썬 메서드이다.
  • 메서드는 클래스 내에 포함되어 있는 함수이다.
  • 얻어 온 결과물을 torch.tensor() 처리를 거친 레이블 값을 출력한다.
  • __len()__는 객체를 입력하면 len() 값인 입력 데이터의 개수를 출력하는 파이썬 메서드이다.
  • 위 코드의 각 데이터세트 train_dataset, val_dataset, test_dataset은 반복 가능(iterable)타입이다. 

 

# train_dataset 확인
for i in train_dataset:
  print(i)
  break

# {'input_ids': tensor([  101,  4937, 11350,  2038,  2048,  1000,  7592, 14433,  1000,  1011,
#          2828, 18401,  2015, 28866,  2075,  2006,  1037, 13576,  4440,  2083,
#          1996, 25115,  1010,  2073,  2505,  2064,  4148,  1010,  1998,  2515,
#          1012,  2023,  2568,  1011,  4440,  4691,  4004,  2460,  3594,  2053,
#         13764,  8649,  1010,  4942, 21532,  2773, 22163,  2612,  1012,  2045,
#          2003,  2053,  2126,  1997,  7851,  2023, 17183, 14088,  9476,  3272,
#          2000,  2425,  2017,  2000,  2156,  2009,  2005,  4426,  1012,  1998,
#          2191,  2469,  2053,  2028,  2104,  2184,  2003,  1999,  1996,  2282,
#          1012,  4487,  6491,  6633,  5677,  3672,  1998,  2064,  3490, 10264,
#          2964,  1998, 18186,  1998,  9576,  2854,  1998,  5573,  2331,  1998,
#          2655,  3560, 27770,  2005,  2500,  2024,  2691,  6991,  1012,  7481,
#          1012,  3383,  1996,  2087, 13432,  3746,  2003,  2008,  1997,  2019,
#         10777,  3605,  1997,  2300,  2008,  1996,  8934,  7368,  9880,  2083,
#          1998,  1999,  1010,  1998,  2036,  4536,  1012,  2021,  2066,  8134,
#          2673,  2842,  1999,  2023,  2143,  1010,  2008, 10021,  1010, 27263,
#         17933,  4226, 25347,  2574,  3310,  2000,  1037,  9202,  2203,  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,     0,     0,     0,     0,     0,     0,     0,     0,     0,
#             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
#             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
#             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
#             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
#             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
#             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
#             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
#             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
#             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
#             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
#             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
#             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
#             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
#             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
#             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
#             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
#             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
#             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
#             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
#             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
#             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
#             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
#             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
#             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
#             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
#             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
#             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
#             0,     0,     0,     0,     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,
#         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, 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, 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, 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, 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, 0, 0, 0, 0, 0, 0, 0,
#         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
#         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
#         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
#         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
#         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
#         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
#         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
#         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
#         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
#         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
#         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
#         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
#         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
#         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
#         0, 0, 0, 0, 0, 0, 0, 0]), 'labels': tensor(1)}

 

  • 결과를 보면 키로 input_ids, attention_mask, labels의 값이 딕셔너리 타입으로 반환되었고, 이 값은 torch.tensor 타입이다.
  • input_ids: 단어 ID이고, 숫자로 작성되며 0부터 시작한다.
  • attention_mask: 패딩 위치를 나타낸다. 실제 입력 토큰과 패딩 토큰을 구분하기 위해 사용된다.
  • labels: 인코딩된 torch.tensor의 레이블 값이다. 

 

 

문제9. 사전학습 모델 불러오기

DistilBertForSequenceClassification 클래스를 사용해서 사전학습 모델을 불러오세요. 

 

  • from_pretrained를 사용해서 distilbert-base-uncased 모델을 불러온다. 
from transformers import DistilBertForSequenceClassification

# distilbert 모델 불러오기
model = DistilBertForSequenceClassification.from_pretrained("distilbert-base-uncased")
model
# DistilBertForSequenceClassification(
#   (distilbert): DistilBertModel(
#     (embeddings): Embeddings(
#       (word_embeddings): Embedding(30522, 768, padding_idx=0)
#       (position_embeddings): Embedding(512, 768)
#       (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
#       (dropout): Dropout(p=0.1, inplace=False)
#     )
#     (transformer): Transformer(
#       (layer): ModuleList(
#         (0-5): 6 x TransformerBlock(
#           (attention): DistilBertSdpaAttention(
#             (dropout): Dropout(p=0.1, inplace=False)
#             (q_lin): Linear(in_features=768, out_features=768, bias=True)
#             (k_lin): Linear(in_features=768, out_features=768, bias=True)
#             (v_lin): Linear(in_features=768, out_features=768, bias=True)
#             (out_lin): Linear(in_features=768, out_features=768, bias=True)
#           )
#           (sa_layer_norm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
#           (ffn): FFN(
#             (dropout): Dropout(p=0.1, inplace=False)
#             (lin1): Linear(in_features=768, out_features=3072, bias=True)
#             (lin2): Linear(in_features=3072, out_features=768, bias=True)
#             (activation): GELUActivation()
#           )
#           (output_layer_norm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
#         )
#       )
#     )
#   )
#   (pre_classifier): Linear(in_features=768, out_features=768, bias=True)
#   (classifier): Linear(in_features=768, out_features=2, bias=True)
#   (dropout): Dropout(p=0.2, inplace=False)
# )

 

 

문제10. TrainingArguments 설정

TrainArguments 클래스를 호출해서, 파인튜닝에 사용하기 위한 하이퍼파라미터를 정의하세요

 

  • 대표적인 하이퍼파라미터들의 값을 다음과 같이 설정합니다. 
    • 학습 에포크: 8
    • 학습 시 미니 배치 사이즈: 16
    • 평가 시 미니 배치 사이즈: 64
    • 최적화 함수: Adam
    • 특정 스텝을 넘기면 학습률을 증가시키기 위해 웜업 스텝을 500으로 설정
    • 딥러닝 모델에서 신경망층을 많이 배치할수록 과적합 발생 경향이 있는데 가중치 감소는 이를 상쇄하기 위해 도입된 장치
from transformers import TrainingArguments

training_args = TrainingArguments(
    output_dir='./results',          # 출력 디렉토리 경로
    num_train_epochs=8,              # 학습 에포크 수
    per_device_train_batch_size=16,  # 학습시 (디바이스 별) 미니 배치 수
    per_device_eval_batch_size=64,   # 평가시 (디바이스 별) 미니 배치 수
    warmup_steps=500,                # 학습률 스케줄링용 웜업 스텝 수

    weight_decay=0.01,               # 가중치 감쇄 강도
    logging_dir='./logs',            # 로그 디렉토리 경로
    logging_steps=10,
)

 

 

문제11. GPU로 전송

문제9에서 불러온 사전학습 모델을 코랩의 GPU 디바이스로 전달합니다. 

 

  • torch.tensors 타입의 데이터는 model.to(device) 메서드를 통해 GPU로 전달된다. 
import torch

# GPU 사용이 가능한 경우 텐서 타입 데이터를 GPU로 전달
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = model.to(device)

 

 

 

 

 

728x90
반응형