dukim's blog

[WK03-Day014][21.08.20.Fri] Multi-GPU, Hyperparameter Tuning, PyTorch Troubleshooting 본문

Boostcamp AI Tech 2th

[WK03-Day014][21.08.20.Fri] Multi-GPU, Hyperparameter Tuning, PyTorch Troubleshooting

eliza.dukim 2021. 8. 20. 20:11

Intro

  • 벌써 3주가 지났다는게 믿기지가 않는다
  • 오늘 내용은 다음주 프로젝트 하면서 자주 쓰이게 될 내용들이었다.

강의 내용 복습

Multi-GPU

  • 3D Parallelism: Parallelism엔 3가지 차원의 Parallel 존재
    • Model parallel, Pipeline parallel, Data parallel

Model Parallel code 소개

  • 모델의 일부를 각각 다른 device에 할당하는 방식
  • forward에서 연산하면서 텐서를 서로 다른 디바이스로 전달하는 상황 발생
  • 이때 한 디바이스에서 연산하는 동안 다른 디바이스가 유휴상태가 되는 문제 -> Pipeline parallel에서 이 문제를 다룸

Data Parallel

  • 각 디바이스에 모델을 복사, 데이터를 분담하여 각자 계산해 결과의 평균을 취하는 방식

  • 직관적으로는 Mini batch 연산을 여러 GPU에서 동시에 수행하는 것

  • PyTorch에서는 두 가지 방식 제공

  • DataParallel: 단순히 데이터 분배 후 평균 -> GPU 사용 불균형 문제, GPU 병목현상, GIL(학습을 관리하는 CPU가 하나. GPU 개수 만큼의 process를 생성하고 실행시키는데, python에서는 한 번에 하나의 process만 실행하므로 multi-processing이 아닌 multi-threading으로 동작-이 부분에 대해선 보강 필요)

    # ref: https://bit.ly/37usURV
    parallel_model = torch.nn.DataParallel(model)
    ...
    preds = parallel_model(inputs)
    loss = loss_function(preds, labels)
    loss.mean().backward() # GPU 개수 만큼 나눠 loss의 평균을 구한 뒤 gradient 계산
    optimizer.step()
    ...
  • DistributedDataParallel: 각 노드의 CPU마다 process 생성하여 개별 GPU에 할당 -> DataParallel과 같은 개념이지만 노드마다 개별 연산을 처리하여 연산의 평균을 냄

    # Distributed Data Parallel을 위한 Train Loader 세팅
    train_sampler = torch.utils.data.distributed.DistributedSampler(train_data)
    suffle = False
    pin_memory = True
    num_workers = 3 # Tip: 보통 GPU 개수의 4배
    
    trainloader = torch.utils.data.DataLoader(train_data, batch_size=20, shuffle=False, pin_memory=pin_memory, num_workers=num_workers, sampler=train_sampler)
    
    # main 함수 세팅
    # ref: https://blog.si-analytics.ai/12
    def main():
        n_gpus = torch.cuda.device_count()
        torch.multiprocessing.spawn(main_worker, nprocs=n_gpus, args=(n_gpus, ))
    
    def main_worker(gpu, n_gpus):
        image_size = 224
        batch_size = 512
        num_worker = 8
        epoch = ...
    
        batch_size = int(batch_size / n_gpus)
        num_worker = int(num_worker / n_gpus)
    
        # 멀티프로세싱 통신 규약 정의
        torch.distributed.init_process_group(backend='nccl', init_method='tcp://127.0.0.1:2568', world_size=n_gpus, rank=gpu)
        model = Model
    
        torch.cuda.set_device(gpu)
        model = model.cuda(gpu)
        model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[gpu])
    
    ########################
    # cf. Python의 멀티프로세싱 코드
    from multiprocessing import Pool
    
    def f(x):
        return x ** x
    
    if __name__ == '__main__':
        with Pool(5) as p:
            print(p.map(f, [1,2,3]))

Hyperparameter Tuning

  • 모델이 스스로 학습하지 않는 요소들을 사람이 지정(learning rate, optimizer, batch size 등)
  • 과거엔 튜닝에 의해 값이 크게 좌우되기도 했었지만 요새는 점점 중요성이 떨어지는 추세
  • 성능에 영향을 끼치는 비율은 데이터 > 모델 > hyperparameter 순. 마지막에 성능을 쥐어짤 때 도전해볼만 하지만 시간대비 효율은 좋지 않다.
  • 기본적으론 grid search vs random search
  • 최근에는 베이지안 방법론(e.g. BOHB(Bayesian Optimization Hyper Band, 2018))

Ray

  • multi-node multi processing 지원 모듈로 ML/DL의 분산 병렬 처리의 표준
  • tune에서 hyperparameter search를 위한 다양한 모듈 제공
  • Spark를 만든 연구실에서 개발

Ray Code Summary

  • 주요 부분만 요약, 어제 학습한 내용에 scheduler가 추가되었다.

      data_dir = os.path.abspath("./data")
      load_data(data_dir)
    
      # search space 설정
      config = {
        "lr":tune.loguniform(1e-4, 1e-2),
        "batch_size": tune.choice([2, 4, 8, 16])
      }
    
      # scheduler algorithm 지정: ASHA는 가능성 없는 후보를 제거하는 방식
      scheduler = ASHAScheduler(
        metric = 'loss', mode='min', max_t=max_num_epochs, grace_period=1, reduction_factor=2)
    
      # 결과 출력 양식 설정
      reporter = CLIReporter(metric_columns=['loss','accuracy', 'training_iteration'])
    
      # 병렬 처리 양식 학습 시행
      result = tune.run(
      partial(train_cifar, data_dir=data_dir),
      resources_per_trial={'cpu':2, 'gpu':gpu_per_trial},
      config=config, num_samples=num_samples,
      scheduler=scheduler,
      progress_reporter=reporter}

PyTorch Troubleshooting

  • Troubleshooting을 위한 도구와 아이디어 소개

1. GPUtil 사용

  • GPU 상태를 보여주는 모듈, iter마다 메모리 늘어나는지 확인
  • 설치
    $pip install GPUtill
  • GPU 상태 확인
    import GPUtil
    GPUtil.showUtilization()

2. torch.cuda.empty_cache()

  • 사용되지 않는 GPU상 cache를 정리해 가용메모리 확보(backprop을 위한 gradient를 담는 메모리 버퍼에 쌓여 있는 것들을 지워 메모리 공간 확보)
  • del은 변수와 메모리 주소 간의 관계를 끊어주는 것, empty_cache()는 메모리에 남아있는 관계가 끊어진 채로 저장되어있는 값을 지우는 것(garbage collecting)
  • 조각코드: GPU memory 비우기
    a = torch.randn(1e7, 1e2).cuda()
    del a
    torch.cuda.empty_cache()

3. training loop에 tensor로 축적되는 변수 체크

  • 학습 로그에 loss 값 기록시 자주하는 실수
  • tensor로 처리된 변수는 GPU 상의 메모리를 차지하기 떄문에 computational graph를 계속해서 이어 그리게됨
  • loss 값 같은 1-d tensor의 경우엔 .item() 메서드를 이용해 python 기본 객체로 변환해 처리할 것

4. del 명령어의 적절한 사용

  • 필요없어진 변수는 적절히 삭제(특히 loop 문의 i 또는 내부에서 계산한 변수들 에서)

5. torch.no_grad() 사용

  • backward pass의 gradient 계산 하지 않아 이와 관련된 메모리가 쌓이지 않음

6. Etc...

  • CNN의 입력 이미지 크기가 달라서 생기는 문제: torchsummary 등으로 사이즈 맞추기(최근 모델은 입력 크기 상관 없이 받아서 문제 없다던데... 확인이 필요한 부분)

피어세션

역할

  • 모더레이터: 대웅
  • 발표자: 한진, 창한
  • 회의록: 한진

    발표

  • 회고록 작성

    한진

  • Raytune을 Template에 적용 (진행 중)

    창한

  • 음성 데이터 Template에 적용
  • 음성 데이터가 임베딩 되는 과정 및 코드 설명

    대웅

    • 단어 임베딩의 역사
    • 단어 임베딩 과정

      이후

    • 금일 스페셜 세션에 대한 감상
    • 대웅님 마이크 이슈
    • 한준님 이슈 발언 추가(공부가 너무 재밌다.

다음 회의

  • 모더레이터: 창한
  • 발표자: 한준, 호영
  • 회의록: 한준

오피스아워: 과제해설

  • gather에 대한 의문이 풀림

학습 회고

  • 첫 주차 만큼이나 힘들지만, 얻은게 많은 한 주였다. 머리보다 손이 바빴던 한 주
  • 최성철 교수님의 ML/DL 스킬셋 조언, PyTorch 세부 기능과 프로젝트 템플릿, Multi GPU 활용과 Troubleshooting 팁은 매우 유용했다.
  • 솔루션 영상 보니 내 코드가 돌아는 가는데 지저분했다. 다시 작성해보자
  • 어느덧 한 달이 다되어간다. 시간 진짜 빠른데, 뽑아갈 수 있는 것은 최대한 뽑아가고 지금까지 배운 것 마지막 정리라 생각하자.

밀린 학습 내용

  • 필수과제 해설영상 다시 보며 gather 정리글 작성하기
  • 공개된 솔루션 코드를 다시 뜯어볼 것(내 코드가 너무 더러웠다. 솔루션 코드보며 깔끔하게 다시 작성해보기)
  • Multi-GPU 실습해보기
  • 운영체제 프로세스와 스레드, 멀티 프로세싱, 멀티 스레드, Python에서의 적용
  • Python GIL
  • 대용량 데이터의 dataloader 만들기
Comments