Ch13. Custom Data Loader 1 - Pre-load Data

보통의 경우 GPU, RAM, Hard Disk의 크기

  • GPU: rtx3090기준 24GB
  • RAM: 연구실 서버 기준 128GB
  • Hard Disk: 연구실 서버기준 4TB

모델 훈련 관련 사전지식

  • 모델은 GPU위에서 훈련하게 된다.
  • CPU위에서 훈련할 수 있지만, 단순한 Tensor계산의 경우 CPU를 쓰게 되면 자원 낭비가 심하다.
  • RAM은 바로 사용할 수 있도록 불러오는 저장소로, 파이썬에서 변수를 선언하면 이 램에 할당되게 된다. (비싼 메모리라 많이 확보하기 어렵다)
  • Hard Disk는 저렴한 메모리장치로 많은 양의 데이터를 저장할 수 있지만 바로 활용가능은 불가능하다. (램위에 올려야함)

===

모델 훈련시 발생 가능한 문제 해결

  • 이전 ch12에서 진행한 Pre-load의 경우 모든 데이터를 램 위에 올려놓고 GPU에 배치사이즈만큼 할당하는 방식이다.
  • 데이터가 적으면 괜찮지만, 만약 데이터가 너무 커서 램 위에 모든 데이터를 올릴 수 없다면?
  • 이 문제를 해결하기 위해 배치사이즈를 램에 올릴 때도 활용해주는 것이 배치별 로드 후 훈련 기법이다.

배치별 훈련

  1. 데이터의 경로를 먼저 로드한다.(이게 x, y의 기준이 된다.)
  2. 경로 어레이를 기준으로 배치를 만든다.
  3. 해당 배치만큼 램 위로 로드한다 (변수로 불러온다)
  4. 로드된 배치만큼 GPU에 할당하여 훈련을 진행한다.
  • 위의 방식대로 배치별 훈련이 진행된다. ch12에서 진행한 코드를 변경해보자.
from tensorflow.keras.utils import Sequence
class CustomDataloader(Sequence):
	def __init__(self, x_set, y_set, batch_size, shuffle=False):
	    self.x, self.y = x_set, y_set
	    self.batch_size = batch_size
	    self.shuffle=shuffle

    def __len__(self):
        return math.ceil(len(self.x) / self.batch_size)
    
    def on_epoch_end(self):
        self.indices = np.arange(len(self.x))
        if self.shuffle == True:
            np.random.shuffle(self.indices)

    def __getitem__(self, index):
        indices = self.indices[index*self.batch_size:(index+1)*self.batch_size]
        return self.x[indices], self.y[indices]
  • 이전 단원을 잘 진행했다면 위와 같은 데이터로더가 생성되었을 것이다.
  • 여기서 x_set을 데이터가 있는 파일 경로를 담은 넘파이 어레이라 가정해보자. y_set은 실제 라벨 파일이라 가정
class CustomDataloader(Sequence):
	def __init__(self, x_set, y_set, batch_size, shuffle=False):
	    self.x, self.y = x_set, y_set
	    self.batch_size = batch_size
	    self.shuffle=shuffle

    def __len__(self):
        return math.ceil(len(self.x) / self.batch_size)
    
    def on_epoch_end(self):
        self.indices = np.arange(len(self.x))
        if self.shuffle == True:
            np.random.shuffle(self.indices)

    def loader(self, filepath):
        ...
        하나의 이미지, 넘파이 등을 로드하는 함수
        ...
        return file

    def __getitem__(self, index):
        indices = self.indices[index*self.batch_size:(index+1)*self.batch_size]
        batch_x, batch_y = self.x[indices], self.y[indices]
        return np.array([self.loader(x) for x in batch_x]), batch_y
  • 다음과 같은 방식으로 코딩하게 되면, 마지막 __getitem__ 부분에서 배치만큼의 데이터를 램에서 불러와 GPU에 할당한다.

단점

  • 그러나 데이터를 로딩하는 시간이 오래걸려서 pre-load보다 오래걸릴 것이다.
  • 그러므로 이미지의 경우 전처리를 미리 수행하여 로드가 빠른 넘파이를 바꾸어 놓는 등의 기법을 적용해볼 수 있다.
  • 혹은 램이 가능한 만큼의 데이터를 램 배치로 설정하여 로드하고, 해당 배치에서 데이터를 GPU 배치로 할당하고 램 배치가 다 되면 새로 램 배치를 업데이트 하는 식으로 코드를 짜도 효율적일 것이다.

Copyright © 2023 Junseo Ko. 본 내용은 모두 제가 작성한 도큐먼트입니다. 무단 퍼가기와 재생산을 금지합니다. 문의: piglets.frizzle0v@icloud.com