[부스트캠프 AI tech 4기] week 2 회고 📝 - Pytorch

반응형

BoostCamp AI tech

 

 

Contents

    네이버 부스트캠프 AI tech 4기 2주 차 회고를 정리하겠습니다.

     

    1. 강의 내용

    1) PyTorch

    1️⃣ PyTorch 소개

    1강에서는 딥러닝 프레임워크의 리더가 PyTorch(Facebook)와 TensoFlow(Google) 2가지라는 것과 이 두 프레임워크의 근본적인 차이점에 대해 배웠습니다. 바로, Computational graph를 그리는 방식의 차이입니다. Computational Graph란 연산 과정을 그래프로 표현한 것입니다. PyTorch는 Dynamic graph 방식으로, 실행 시점에 그래프를 그려갑니다. 반면, TensorFlow는 Define and Run 방식으로 그래프를 먼저 정의하고, 실행합니다. 이로 인해, PyTorch의 코드가 더 파이써닉하고 간결하게 짜지며, 중간에 출력 결과를 보며 디버깅하기 좋습니다. 그렇다고 실행속도가 뒤처지는 것도 아닙니다. 오히려 파이 토치가 빠른 경우가 더 많다는 벤치마크 결과가 있습니다. 그래서 요즘 PyTorch 프레임워크가 우상향 하고, TensorFlow가 우하향한 느낌이라고 합니다. 하지만, TensorFlow역시 장점이 많이 있고, 아직 많이 사용되는 프레임워크라고 합니다.

     

    2️⃣ PyTorch 기초

    2강에서는 PyTorch의 기본 자료형인 Tensor 기초와 자동 미분기능인 AutoGrad에 대해 학습했습니다. Tensor는 다차원 Array를 표현하는 PyTorch의 클래스이며, 사실상 ndarray와 비슷합니다. 차이점이 있다면, GPU를 사용하는 tensor를 구분(.cuda)한다는 정도입니다. 그리고, Tensor의 차원을 변환하는 메소드 view(reshape와 동일하지만, 메모리 주소 복사에 차이가 있음), squeeze(차원 중, 1인 차원 삭제), unsqueeze(지정된 차원에 "1차원" 추가)가 있고, 내적(dot)과 행렬곱(mm) 연산을 배웠습니다.

    자동 미분의 대상 Tensor에 requires_grad=True를 지정하고, 목적식에 backward() 연산을 함으로써, Pytorch의 자동 미분 기능을 사용할 수 있습니다. 

     

    3️⃣ PyTorch 프로젝트

    3강에서는 주피터 노트북을 사용하는 것을 넘어서서 PyTorch 프로젝트 구성을 학습합니다. 주어진 데이터에 대해 딥러닝 모델을 만들고, 성능을 테스트하는 것은 초반에는 주피터 노트북이 편하지만, 여러 사람과 협업하며 테스트하고, 그 결과를 공유하며 같이 딥러닝 모델을 발전시켜 나가기에는 노트북 형태가 적합하지 않습니다. 그 이유는 실행 순서도 그렇고, 코드를 모듈화해 import 해서 같이 테스트해보고, 유지보수 향상이 어렵기 때문입니다. PyTorch를 이용해 이렇게 같이 협업하기 좋게 만들어둔 여러 템플릿 프레임워크 중 하나를 학습합니다. 

     

    GitHub - victoresque/pytorch-template: PyTorch deep learning projects made easy.

    PyTorch deep learning projects made easy. Contribute to victoresque/pytorch-template development by creating an account on GitHub.

    github.com

     

    4️⃣ AutoGrad & Optimizer

    4강에서는 nn.Module과 AutoGrad(자동미분)을 이용한 학습의 단계들과 그 의미를 학습했습니다. 딥러닝은 Conv, Linear와 같은 Layer들을 단위로 Neural Network를 구성합니다. 이 Layer들의 base class가 nn.Module 클래스입니다. 이때, 학습의 대상이 되는 웨이트 값들을 정해야 하는데, 이를 nn.Parameter를 이용해 정의합니다. 이는 torch.Tensor를 상속받은 객체이고,  nn.Module내에서 attribute가 될 때는 자동으로 requires_grad=True가 되어 AutoGrad의 대상이 됩니다. 딥러닝 모델(model)에 대해 학습의 필수 5단계는 아래와 같습니다.

    optimizer.zero_grad()             # 1. gradient값 초기화
    outputs = model(inputs)           # 2. 모델에 인풋넣어서 아웃풋 출력
    loss = criterion(outputs, labels) # 3. 로스값 구하기
    loss.backward()                   # 4. 구한 로스값에서 역전파로 미분
    optimizer.step()                  # 5. 미분값 적용

    특히, backward부분이 역전파가 이루어지며, 학습의 핵심 부분입니다. 이 때, loss(결괏값과 실제값 간의 차이 목적식)에 대해 미분이 수행되며, nn.Parameter들이 업데이트됩니다. 그 외에 backward와 optimizer 메서드 부분을 직접 오버라이드 하여 동작원리를 구현해 보았습니다.

     

    5️⃣ Datasets & Dataloaders

    5강에서는 Dataset과 Dataloaders의 정의와 기능에 대해서 배웠습니다. 먼저 데이터들을 한 폴더에 모았다면, 이를 Dataset(Index가 지정된 데이터의 모음)으로 구조화하고, 이를 Batch 별로 나눠 셔플해서 모델에 넣기 위해 DataLoader가 쓰입니다. 즉, Dataset 클래스는 데이터의 입력 형태를 정의하는 클래스로, DataLoader에 들어갈 표준화된 데이터 구조입니다. Dataset 클래스의 기본적인 형태는 아래와 같고, __init__ , __len__, __getitem__ 이 핵심 메서드입니다.

    import torch
    from torch.utils.data import Dataset, DataLoader
    
    class BasicDataset(Dataset):
        def __init__(self, data, labels): # 초기 데이터 생성 방법을 지정
                self.labels = labels
                self.data = data
    
        def __len__(self): # 데이터의 전체 길이
                return len(self.labels)
    
        def __getitem__(self, idx): # index 값을 주었을 때 반환되는 데이터의 형태 (X, y)
                label = self.labels[idx]
                data = self.data[idx]
                sample = {"Data": data, "Class": label}
                return sample

    DataLoader의 주요 목적은 Dataset에 대해 학습 직전(모델에 들어가기 직전)에 데이터의 변환을 책임집니다. 즉, Tensor로 변환 + Batch 처리가 주요 기능입니다. 아래는 DataLoader가 갖는 인자들과 default값입니다.

    DataLoader(dataset, batch_size=1, shuffle=False, sampler=None,
               batch_sampler=None, num_workers=0, collate_fn=None,
               pin_memory=False, drop_last=False, timeout=0,
               worker_init_fn=None, *, prefetch_factor=2,
               persistent_workers=False)

    바로 와닿지 않지만 중요한 argument인 sampler와 collate_fn을 요약해 보겠습니다. sampler는 index를 컨트롤하는 방법입니다. 데이터의 index를 원하는 방식대로 조정합니다. 때문에, sampler를 설정하고 싶다면 shuffle=False 여야 합니다. 그리고, collate_fn은 Data와 Label을 서로 다른 행렬로 분리시켜 줍니다. map-style 데이터셋에서 sample list를 batch 단위로 바꾸기 위해 필요합니다. 직관적인 예시로는 프린터기에서 인쇄할 때, 묶어서 인쇄하기와 같은 기능이라고 생각하면 됩니다. 즉, ((피처 1, 라벨1) (피처2, 라벨 2))와 같은 배치 단위 데이터가 ((피처1, 피처2), (라벨1, 라벨2))와 같이 바뀝니다.

     

    6️⃣ Transfer Learning & Pretrained Model

    6강에서는 이어 학습하기(Transfer Learning) 방법과 학습되어 있는 모델(Pretrained Model) 활용하는 법을 배웠습니다. Transfer Learning은 대부분 기존에 다른 타깃으로 학습된 모델을 가져와서, 내 데이터들로 학습을 시키는 Fine-Tuning 방식이 많이 쓰입니다. 학습된 모델을 torch.save로 저장하고, torch.load로 불러올 수 있습니다.

    # 모델의 architecture 저장
    torch.save(model, "model.pt")
    
    # 모델의 architecture load
    model = torch.load("model.pt")

    torch.save를 활용하여, 모델이 학습을 진행하는 도중에 epoch에 따라 조건을 줘서 checkpoint로 저장할 수 있습니다. 기존에 Pre-trained 된 모델을 불러왔다면 Freezing 기법을 이용해 Fine-tuning을 할 수 있습니다. Freezing 기법이란 내가 원하는 레이어 부분만 required_grad를 True로 줘서 학습을 시키는 방법입니다. 아래 예시 코드를 보면, my_model의 전체 파라미터를 Frozen 시키고, linear_layers 부분(마지막에 final classifier로 추가된 레이어라고 가정)만 Frozen을 풀어 학습 가능한 레이어로 만드는 코드입니다.

    """
    모델의 파라미터들에 대해 requires_grad=False로 해서
    Frozen 시킨다. (파라미터 변경 안되도록 한다.)
    그리고 맨 끝에 final classifier로 내가 추가한 linear 레이어만
    requires_grad=True로 해서 여기만 학습될 수 있도록 한다.
    """
    for param in my_model.parameters():
        param.requires_grad = False
    
    for param in my_model.linear_layers.parameters():
        param.requires_grad = True

     

    7️⃣ Monitoring Tools - Tensorboard, W&B

    7강에서는 학습을 모니터링할 수 있는 도구인 Tensorboard와 W&B에 대해서 배웠습니다. 텐서 보드는 주피터 노트북 상에서 학습을 관찰할 수 있게 라이브러리로 import 해서 사용할 수 있습니다. Tensorboard가 시각화해주는 내용은 Acc, Loss 등 metric의 수치를 epoch 단위로 볼  수 있고, graph와 이미지, 텍스트 등을 쉽게 보여줍니다. Tensorboard를 띄우는 방법은 아래와 같습니다.

    %load_ext tensorboard # tensorboard 로드
    %tensorboard --logdir "logs" # 6006 포트로 텐서보드가 띄워진다.

    주피터 노트북에서 위 코드를 실행하면 아래와 같이 텐서 보드가 띄워집니다.

    텐서보드 띄우기

    그리고, 전문적으로 시각화해주는 Weight and Biases라는 웹사이트에 python api로 연결해서 시각화를 했습니다. W&B 사이트 링크는 아래와 같습니다.

     

    Weights & Biases – Developer tools for ML

    WandB is a central dashboard to keep track of your hyperparameters, system metrics, and predictions so you can compare models live, and share your findings.

    wandb.ai

    먼저, pip insatll wandb를 통해 설치하고, 웹사이트에서 회원 가입한 다음에 튜토리얼을 따라 하면 project와 entity를 생성할 수 있습니다. 아래 코드를 실행하면 Token 키를 입력하라고 나오는데 웹사이트의 토큰 키를 입력해주면 연결이 완료됩니다.

    import wandb
    wandb.init(project="cwj_project", entity="netsus_team")

    실행 결과 화면

    wandb 연결

    그리고, 학습할 때 원하는 파라미터들을 wandb.log에 전달해주면 웹사이트에 로그들이 찍히게 됩니다. 예시 코드는 아래와 같습니다.

    train_loss = epoch_loss/len(train_dataset)
    train_acc = epoch_acc/len(train_dataset)
    wandb.log({'accuracy': train_acc, 'loss': train_loss})

    그 결과 웹사이트에서 아래와 같이 Log를 확인할 수 있습니다.

    wandb 결과화면

     

    8️⃣ Multi GPU - DataParallel

    8강에서는 Model Parallel 개념과 Data Parallel 하는 2가지 방법을 배웠습니다. Model Parallel은 Alexnet이 대표적인 예로 쓰였으며, 모델을 나누어 Forward와 Backward가 병렬적으로 일어나도록 해야 하는 것으로, 파이프라인을 정교하게 잘 짜야만 가능한 고난도 작업입니다. 그래서 아래처럼 Forward와 Backward에 대해서 GPU가 따로 놀면 안 되고,

    잘못된 Model Parallel

    아래처럼 동시적으로 GPU0과 GPU1이 동작하게 파이프라인을 구성하는 게 핵심입니다.

    올바른 Model Parallel

    Data Parallel은 그냥 Data Parallel과 Distributed Data Parallel 2가지로 나뉩니다. Data Parallel은 특정 GPU가 데이터들을 쪼개서 미니 배치로 다른 GPU들에 뿌리고, 모델을 복사해주면 병렬적으로 수행한 뒤, 다시 특정 GPU가 데이터를 모아서 Loss값을 한 번에 구하고, Backward 때에도 마찬가지로 특정 GPU가 Loss기반으로 구한 gradient를 다른 GPU들에 뿌려주면, 전달받은 gradient를 기반으로 병렬적으로 backward 계산해서 Weight의 gradient들을 특정 GPU가 모아 평균 내서 업데이트하는 방식입니다. 사용법은 쉽습니다. model을 정의할 때 아래 코드 한 줄만 추가해주면 됩니다.

    parallel_model = torch.nn.DataParallel(model)
    # 다음에 model 대신 parallel_model로 학습 진행하면 됩니다.

    이는 데이터를 모아주고, 뿌려주고 하는 특정 GPU가 과도하게 쓰이는 근본적인 문제점을 가지고 있습니다. 이를 해결한 게 Distributed Parallel이고 이는 코드적으로 조금 더 복잡합니다. DistributedSampler를 사용해줘야 하고, pin_memory=True로 해주는 게 좋습니다.(메모리에 Data를 바로 올릴 수 있도록 절차 간소화하는 방법입니다.) 코드는 아래와 같습니다.

    # DistributedSampler 생성
    train_sampler = torch.utils.data.distributed.DistributedSampler(train_data)
    
    # DataLoader에 반영
    train_loader = torch.utils.data.DataLoader(train_data, batch_size=30,
    		pin_memory=True, num_workers=3, sampler=train_sampler)

     

    9️⃣ Hyperparameter Tuning - Ray

    9강에서는 Ray를 이용한 하이퍼 파라미터 튜닝에 대해서 배웠습니다. 하이퍼 파라미터란 모델이 스스로 학습하지 않는 값을 의미합니다. 대표적으로 learning_rate, 모델의 크기, optimizer 등이 있습니다. 하이퍼 파라미터 튜닝 방법론에는 GridSearch와 Random, 베이지안 기법으로 나뉘는데, 요즘은 베이지안 기법이 많이 사용됩니다. Ray는 multi-node, multi-processing을 지원해주는 병렬 처리에 특화된 모듈입니다. Ray를 이용해 하이퍼 파라미터 튜닝을 진행할 수 있습니다. 하이퍼 파라미터를 찾을 공간(search space)을 config 변수에 저장하고, 스케쥴러(학습 스케쥴러 알고리즘)를 설정한 뒤에, reporter에 결과 출력 양식을 지정하고, tune.run()을 통해 병렬 처리 양식으로 학습을 수행하는 방식입니다. 개략적인 콘셉트 코드는 아래와 같습니다.

    from ray import tune
    from ray.tune import CLIReporter
    from ray.tune.schedulers import ASHAScheduler
    
    # search space(하이퍼 파라미터 찾을 공간)
    config = { 
        "l1": tune.sample_from(lambda _: 2 ** np.random.randint(2, 9)),
        "l2": tune.sample_from(lambda _: 2 ** np.random.randint(2, 9)),
        "lr": tune.loguniform(1e-4, 1e-1),
        "batch_size": tune.choice([2, 4, 8, 16])
    }
    
    # 스케쥴러 설정
    scheduler = ASHAScheduler(
        metric="loss",
        mode="min",
        max_t=max_num_epochs,
        grace_period=1,
        reduction_factor=2)
    
    # 리포터 설정
    reporter = CLIReporter(
        # parameter_columns=["l1", "l2", "lr", "batch_size"],
        metric_columns=["loss", "accuracy", "training_iteration"])
    
    # 이부분이 실질적으로 병렬처리하는 부분이다.
    result = tune.run(
        partial(train_cifar, data_dir=data_dir),
        resources_per_trial={"cpu": 2, "gpu": gpus_per_trial},
        config=config,
        num_samples=num_samples,
        scheduler=scheduler,
        progress_reporter=reporter)

     

    🔟 Trouble Shooting

    10강에서는 해결이 어려운 GPU OOM 에러를 어떤 식으로 해결할지를 배웠습니다. OOM 에러는 커널이 종료되기 때문에 에러 메시지 확인도 어렵고, 왜 어디서 발생했는지 확인이 어렵습니다. 가장 기초적인 해결방법은 Batch Size를 줄인 다음에 GPU를 clean 하고 실행했을 때 돈다면 코드 자체에 이상이 없는 것이므로, Batch Size를 크게 해서 생긴 문제라고 볼 수 있습니다. 다음으로는 Tool을 활용한 방법이 있습니다. GPUUtil을 활용해 nvidia-smi처럼 GPU 상태를 이터레이션마다 확인할 수 있습니다. 샘플 코드는 아래와 같습니다.

    !pip install GPUtil
    
    import GPUtil
    GPUtil.showUtilization()

    또한, torch.cuda.empty_cache()를 활용해 사용되지 않는 GPU상의 cache를 정리해서 가용 메모리를 확보하는 방법도 있고, gradient 전달이 사용되지 않는 부분에서는 아래 코드처럼 torch.no_grad() Context 안에서 진행하는 방법도 있습니다.

    with torch.no_grad(): # 하위에서는 backward가 일어나지 않기 때문에 메모리가 추가적으로 할당되지 않는다.
        for data, target in test_loader:
            output = network(data)
            test_loss += F.nll_loss(output, target, size_average=False).item()
            pred = output.data.max(1, keepdim=True)[1]
            correct += pred.eq(target.data.view_as(pred)).sum()

    2) 과제

    1️⃣ 기본 과제 1 (부덕이 과제)

    이 과제의 퀄리티는 정말 대박입니다. Pytorch Documentation을 어떻게 학습하고, 공부하는지 이론과 실습을 모두 잡았습니다. torch의 기본 기능들을 알고리즘 문제 풀듯이 풀 수 있었으며, Document에서 원하는 정보를 찾아 학습하는 방법도 친절히 설명되어 있습니다. 심화 문제는 torch의 기능을 깊게 이해해야 하는 문제들로 구성되어있어 정말 알찼습니다. 과제의 양이 워낙 방대하기 때문에 다 설명하는 것은 어렵고, 대략적으로만 설명하겠습니다.

    기본적인 torch를 활용한 연산, 인덱싱, 기본 기능들을 배웠습니다. 특히, gather를 활용한 실습 과제는 난도가 높았습니다. 또한, torch.nn.Module에서 모듈들의 흐름과 파라미터의 흐름을 확인하고, Buffer가 무엇인지 실습하고, 모델을 수정하고 Docstring 작성하는 방법을 실습했습니다. 다음으로, hook과 apply에 대해 세세한 실습 과제로 동작 원리를 배웠고, 심화적으로 apply를 활용한 과제가 있었습니다.

    2️⃣ 기본 과제 2

    Dataset과 DataLoader를 직접 구현하고, 커스텀하여 실제 MNIST와 Titanic 데이터로 모델에 Feeding 하는 방법을 배웠습니다. 세부적인 실습 문제들은 각각 collate_fn을 다루는 문제, torchvision.transforms와 transforms.Compose로 이미지를 변환하는 문제, Dataset 클래스 내부에 tokenizer를 만들고, 정규식으로 문자열을 정제한 뒤에 encoder와 decoder를 구현하는 문제가 있었습니다.

     

    3️⃣ 심화 과제 1

    Ray를 이용해 Fashion Mnist 데이터 학습 분류 모델의 하이퍼 파라미터를 튜닝하고 W&B를 통해 시각화해보는 과제입니다.

     

    2. 피어 세션

    1) 일반

    2주 차인데도 매일 얼굴을 보고 얘기하며 공부하니 뭔가 같은 팀이라고 느껴졌습니다. 서로 모르는 내용을 같이 질문하고, 공부할 분량들을 같이 조절해서 뭔가 같이 장거리 마라톤을 달려 나가는데, 페이스 조절을 함께하는 느낌이 들었습니다. 코딩 테스트 스터디와 면접 질문 스터디도 추가하여 함께 시간을 정해 일주일에 1회 같이 하기로 하였습니다. 처음엔 왜 피어 세션을 하지? 와닿지 않았는데 같이하는 동료가 있으니 쉽게 지치지 않고, 서로 다른 관점으로 해야 할 일들을 같이 결정하고 함께하며 나아가는 느낌이 듭니다.

     

    2) 한 주 회고

    회고록

    3. 그 외 이벤트들

    1️⃣ 멘토링

    추천 시스템 쪽으로 회사에서 일하시다가, 현재는 창업하신 멘토 분과 함께 멘토링을 진행하게 되었습니다. 멘토분의 백그라운드와 우리 피어 세션 팀(8조)의 자기소개를 했고, 멘토님의 석사 졸업부터 회사에 다니시며 겪었던 많은 내용들을 재미있게 이야기해주셨습니다. 그러면서, 비즈니스 문제를 발견하고 어떻게 해결하는지에 대한 인사이트를 주셨습니다.

     

    2️⃣ 오피스아워

    기본 과제에 대한 해설을 진행해주셨습니다.

     

    3️⃣ 마스터 클래스

    이번 주 마스터 클래스는 Pytorch 강의를 해주신 최성철 교수님의 Data Centric AI에 대한 강연이 있었습니다. 정말 마음에 와닿는 강연이었습니다. 실제 산업에서 어떤 이슈가 있는지 Data 종류와 앞으로 알아야 할 것들에 대해 정리해 주셨습니다.

     

     

    이상으로 2주 차 회고를 마치겠습니다.

    읽어주셔서 감사합니다.

     

    Reference)
    1. Data Parallel : http://www.idris.fr/eng/ia/model-parallelism-pytorch-eng.html
    2. 네이버 부스트캠프 AI tech

     

    반응형

    댓글

    Designed by JB FACTORY