Published on

Pytorch Dataset 클래스(상속) 파악하기!!

Authors
  • avatar
    Name
    Inhwan Cho
    Twitter

BERT_Dataset

  • 데이터를 정제하였다면, 각 데이터가 KoBERT 모델의 입력으로 들어갈 수 있는 형태가 되도록 토큰화, 정수 인코딩, 패딩 등을 해주어야 한다. 아래는 그를 수행할 클래스이다.
  • Dataset을 상속 받는 클래스의 구성
  • 사용자 정의 Dataset 클래스는 반드시 3개 함수를 구현해야 합니다:
    1.__init__,
    2.__len__,
    3.__getitem__
  • __init__ 함수는 Dataset 객체가 생성(instantiate)될 때 한 번만 실행됩니다.
  • __len__ 함수는 데이터셋의 샘플 개수를 반환합니다.
    len()함수를 사용 시 반환값이라고 생각하시면 됩니다.
  • __getitem__ 함수는 클래스의 인덱스에 접근할 때 자동으로 호출되는 메서드(함수)이다.
    쉽게 표현하면 슬라이싱을 구현하려면 필요한 것은 __getitem__라는 메소드!

from torch.utils.data import Dataset
import gluonnlp as nlp

class BERT_Dataset(Dataset):


    # __init__ 함수는 Dataset 객체가 생성(instantiate)될 때 한 번만 실행됩니다. 
    # 여기서는 dataset, sent_idx, label_idx, bert_tokenizer, max_len, pad, pair)를 불러옵니다.
    # 함수를 호출하게되면 nlp.data.BERTSentenceTransform을 하고, sentences와 labels의 리스트를 만들게 됩니다.
    def __init__(self, dataset, sent_idx, label_idx, bert_tokenizer, max_len,
                 pad, pair):
        transform = nlp.data.BERTSentenceTransform(
            bert_tokenizer, max_seq_length = max_len, pad = pad, pair = pair)

        self.sentences = [transform([i[sent_idx]]) for i in dataset]
        self.labels = [np.int32(i[label_idx]) for i in dataset]


    # 인덱스를 기반으로 문장과 라벨을 반환합니다. return에 슬라이싱 "[]"이 필요함.
    def __getitem__(self, i):
        return (self.sentences[i] + (self.labels[i], ))


    # __len__ 함수는 데이터셋의 레이블 개수를 반환합니다.
    def __len__(self):
        return (len(self.labels))

######################################################################

#사용하기
tokenizer = get_tokenizer()
token = nlp.data.BERTSPTokenizer(tokenizer, vocab, lower=False)

# 이런식으로 만든 함수를 사용합니다.
data_train = BERTDataset(dataset = train_set_data, sent_idx = 0, label_idx = 1, bert_tokenizer = token, max_len = 64, pad = True, pair = False)
# 아니면 아래와 같은 형식으로 '=' 기호를 빼고 사용하셔도 결과는 같습니다.
data_train = BERTDataset(train_set_data, 0, 1, token, max_len, True, False)

코드 분해하여 원리 파악하기

from kobert.utils import get_tokenizer
import gluonnlp as nlp
tokenizer = get_tokenizer()


bertmodel, vocab = get_pytorch_kobert_model()
token = nlp.data.BERTSPTokenizer(tokenizer, vocab, lower=False)

data_train = BERTDataset(dataset = train_set_data, sent_idx = 0, label_idx = 1, bert_tokenizer = token, max_len = 64, pad = True, pair = False)

# data를 리스트[0]에는 문장을 넣고, 리스트[1]에는 감정(labels)으로 넣어서 리스트로 만들어줌.
train_set_data = [[i, str(j)] for i, j in zip(train_set['data'], train_set['label'])]


print(train_set_data[0])
# 출력 결과 => ['큰아들이 결혼하는데 집을 사달라고 해서 화가 나.', '4']

print(data_train[0])
# 출력 결과 =>
(array([   2, 4688, 6797, 5940,  950, 7795, 4384, 7088, 2573, 5794, 5439,
        5007, 5112, 5330, 1370,  517,   54,    3,    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], dtype=int32),
 array(18, dtype=int32),
 array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       dtype=int32),
 4)
# array의 첫 번째는 패딩된 시퀀스, 두 번째는 길이와 타입에 대한 내용, 세 번재는 어텐션 마스크 시퀀스이다.
  • 아래는 from kobert.pytorch_kobert import get_pytorch_kobert_modelget_kobert_model함수 형식입니다.
  • bertmodel, vocab = get_pytorch_kobert_model()을 실행 시 에러가 나온다면
    !pip install sentencepiece==0.1.91
    !pip install transformers==4.8.2
    버전을 맞춰주면 해결이 될 수도 있습니다.

def get_kobert_model(model_path, vocab_file, ctx="cpu"):
    bertmodel = BertModel.from_pretrained(model_path)
    device = torch.device(ctx)
    bertmodel.to(device)
    bertmodel.eval()
    vocab_b_obj = nlp.vocab.BERTVocab.from_sentencepiece(vocab_file,
                                                         padding_token='[PAD]')
    return bertmodel, vocab_b_obj

  • KoBERT 함수 출처