본문 바로가기
Machine Learning/개념 정리

[파이토치] 2. 데이터 처리

by W_log 2023. 12. 29.

전체 개요

 

지난 시간에 모델에 대해서 공부했다면 오늘은 모델에 학습시킬 데이터를 처리하는 부분에 대해서 정리해보았습니다.

 

1. 모델 : 우리가 어떤 신경망 구조로 만들지에 대해서 설계하는 방식으로 이건 설계, 창작의 영역이라고 보면 될것 같다.DNN으로 보면, 각 layer 내부 뉴런의 갯수, CNN에서는 Kernel의 갯수 등을 여기서 설정한다.

 

2. 데이터 : 크게 데이터 전처리와 데이터 로더 작업으로 나눌 수 있으며, 전처리는 우리가 학습시키려는 데이터를 모델에 들어갈 수 있게 텐서를 조절하거나 데이터를 클리닝하는 작업에 해당하며, 데이터 로더는 3번의 학습에 맞게 데이터를 배치 형태로 전달하는 역할을 한다.

*데이터 로더가 왜 필요한지? 처음 공부할 때 이게 왜 필요하지? 생각했는데 공부하면서 이렇게 이해했다. 효율적인 관점이 큰데 학습하는 과정에서 병렬 처리, 배치 처리, 셔플링 등의 작업을 진행해준다.

 

3. 학습 : 이제 1과 2에 정의된 모델과 데이터를 정의한 방식대로 학습을 시킨다. 크게 3가지의 학습 관련 함수가 있는데, training(training 측정), evaluation(validation 측정), test(최종 결과 도출) 함수를 각각 정의한다.

 

 

 

DataSet : 학습에 활용할 데이터를 처리하는 코드

 

1. CustomDataset 클래스 정의

파이토치에서는 학습에 활용할 데이터를 Dataset이라는 클래스 형태로 만들어야 처리할 수 있도록 한다. 내부 파이토치에서 제공하고 있는 데이터셋도 있지만 사실 학습에 활용할 대부분의 데이터셋은 각자가 가지고 있는 데이터셋이기 때문에 보통 CustomDataset을 구현한다.

 

1)  __init__(): 여기서는 데이터 전처리 작업들에 필요한 코드들을 작성하는 영역으로 RNN에서 다양한 전처리들이 있어서 해당 코드를 가져와보았다.

 

  def __init__(self, data, vocab, tokenizer, max_len):
    self.data = data
    self.vocab = vocab
    self.max_len = max_len
    self.tokenizer = tokenizer
    seq = self.make_sequence(self.data, self.vocab, self.tokenizer)
    self.seq = self.pre_zeropadding(seq, self.max_len)
    self.X = torch.tensor(self.seq[:,:-1])
    self.label = torch.tensor(self.seq[:,-1])

 

 

2) __len__(self), __getitem__(self, idx) : dataset이라는 클래스를 상속받아서 클래스를 생성할 때 반드시 정의해줘야하는 메소드 2개이다. 사실 메소드 자체는 간단한데 데이터에서 어떤 것을 len으로 측정할지 어떤 것을 조회해야할지에 대해서 정의해주는 메소드이다.

  def __len__(self):
    return len(self.X)

  def __getitem__(self, idx):
    X = self.X[idx]
    label = self.label[idx]
    return X, label

 

 

3) make_sequence, pre_zeropadding : 모델 특성에 맞게 데이터를 처리해주는 함수들을 이 클래스에서 만들어주는데 해당 코드는 문장이 주어졌을 때 다음 문장을 예측하는 함수를 전의해서 init 함수에서 이를 처리할 수 있도록 한다.

 

  def make_sequence(self, data, vocab, tokenizer):
    seq = []
    for i in data:
      token_id = vocab.lookup_indices(tokenizer(i))
      for j in range(1, len(token_id)):
        sequence = token_id[:j+1]
        seq.append(sequence)
    return seq

  def pre_zeropadding(self, seq, max_len):
    return np.array([p[:max_len] if len(p) >= max_len else [0]*(max_len - len(p)) + p for p in seq])

 

4) train_test_split, random_split : 머신러닝에서는 train, validation, test로 데이터셋을 분리하는데 class 생성 후에는 이를 분리해주는데 torch와 sklearn 각각 이를 분리하는 코드가 다르다.

 

#train_test_split 방식

train, test = train_test_split(df, test_size = 0.2, random_state = 42)
test, val = train_test_split(test, test_size = 0.5, random_state = 42)


train_dataset = CustomDataset(train, vocab, tokenizer, max_len)
val_dataset = CustomDataset(val, vocab, tokenizer, max_len)
test_dataset = CustomDataset(test, vocab, tokenizer, max_len)



#random_split 방식
total_size = len(train_dataset)
train_num, val_num = int(total_size * 0.8), int(total_size * 0.2)

train_dataset, val_dataset = torch.utils.data.random_split(train_dataset, [train_num, val_num])

 

 

전체 코드는 아래와 같다.

class CustomDataset(Dataset):
  def __init__(self, data, vocab, tokenizer, max_len):
    self.data = data
    self.vocab = vocab
    self.max_len = max_len
    self.tokenizer = tokenizer
    seq = self.make_sequence(self.data, self.vocab, self.tokenizer)
    self.seq = self.pre_zeropadding(seq, self.max_len)
    self.X = torch.tensor(self.seq[:,:-1])
    self.label = torch.tensor(self.seq[:,-1])

  def __len__(self):
    return len(self.X)

  def __getitem__(self, idx):
    X = self.X[idx]
    label = self.label[idx]
    return X, label

  def make_sequence(self, data, vocab, tokenizer):
    seq = []
    for i in data:
      token_id = vocab.lookup_indices(tokenizer(i))
      for j in range(1, len(token_id)):
        sequence = token_id[:j+1]
        seq.append(sequence)
    return seq

  def pre_zeropadding(self, seq, max_len):
    return np.array([p[:max_len] if len(p) >= max_len else [0]*(max_len - len(p)) + p for p in seq])
    
    
    
#train_test_split 방식

train, test = train_test_split(df, test_size = 0.2, random_state = 42)
test, val = train_test_split(test, test_size = 0.5, random_state = 42)


train_dataset = CustomDataset(train, vocab, tokenizer, max_len)
val_dataset = CustomDataset(val, vocab, tokenizer, max_len)
test_dataset = CustomDataset(test, vocab, tokenizer, max_len)



#random_split 방식
total_size = len(train_dataset)
train_num, val_num = int(total_size * 0.8), int(total_size * 0.2)

train_dataset, val_dataset = torch.utils.data.random_split(train_dataset, [train_num, val_num])

 

 

 

DataLoader : 학습에 활용하기 위해 데이터를 로드하는 코드

이제 위에서 우리가 학습에 활용할 데이터셋을 모델에 학습시키기 위해 DataLoader에 로드하는 코드인데, 꽤 간단하다.

정의해줘야할 건 batch_size 정도인데, train의 경우에는 shuffle 처리를 통해서 학습마다 똑같은 데이터를 처음에 학습시키는 것을 방지한다. 다만 모델에 대한 성과를 평가할 때는 그럴 필요는 없어서 False로 인자를 지정해준다. 

 

batch_size = 32
train_dataloader = DataLoader(train_dataset, batch_size = batch_size, shuffle = True)
val_dataloader = DataLoader(val_dataset, batch_size = batch_size, shuffle = False)
test_dataloader = DataLoader(test_dataset, batch_size = batch_size, shuffle = False)

 

 

이후에 이렇게 지정 후에는 아래와 같이 for문으로 데이터를 넣어주면 된다. 그때마다 하나의 배치만큼의 데이터가 나간다.

for batch_data, batch_labels in train_dataloader:
	output = model(batch_data) # 모델을 통과시켰을 때 나오는 결과값으로 이후 labels와 비교해서 Loss를 계산

'Machine Learning > 개념 정리' 카테고리의 다른 글

[파이토치] 5. Lightning pytorch  (1) 2024.01.01
[파이토치] 4. 여러 모듈 사용  (2) 2023.12.29
[파이토치] 1. 모델 선언  (1) 2023.12.29
Hyperparameter Search Optimization  (1) 2023.11.30
Tree Model 정리  (0) 2023.11.29