헤파이토스 프로젝트 - HephaestusProject/template GitHub Wiki

헤파이토스 프로젝트는 딥러닝 논문을 구현 및 재현하는 모임입니다

헤파이토스 프로젝트의 목표는 아래와 같습니다

  1. 논문 구현
  2. 성능 재현
  3. OpenAPI를 제공하여 사용자가 바로 사용해볼 수 있게 모델 배포
  4. 사용자가 바로 재현할 수 있도록 가이드 문서 제공
  5. 사용자가 풀고자하는 문제에 바로 이용할 수 있도록 여러 기능을 제공
    5-1. 분리된 Train/Evaluate/Inference 모듈 제공
    5-2. TorchServe로 랩핑된 serving 모델 제공(Dockerfile / Dockerimage 포함)

논문 구현 및 재현 방식에 관하여

  • 논문은 개인이 하고 싶은 논문을 선택하시면 됩니다

  • 구현 및 재현 일정에 대한 스켸쥴링은 개인이 합니다

    1. 초기에 논문을 읽은 후에, 구현할 기능을 정리합니다 (이때, 기능은 원자단위로 쪼갭니다)
    2. 정리된 기능을 기반으로 개발 일정을 산출합니다
    3. 산출된 일정에 2.5배를 곱합니다
    4. 최종 산출된 일정을 데드라인으로 논문 구현 및 재현을 시작합니다
  • 논문 GitHub 저장소는 헤파이토스 조직 내에서 새로운 레파지토를 생성합니다

    • 이때, 저장소는 template저장소를 사용합니다
      • template 저장소를 기반으로 새로운 저장소를 생성했다면, 이를 clone받아 apply.sh를 실행합니다.(commit message에 대한 설정을 적용합니다.)
    • 저장소 이름의 규칙은 -model 입니다.
      • pytorch-<model>
      • tf-<model>
      • mxnet-<model>

운영 방식에 관하여

  • 대부분의 논의는 Slack에서 합니다
  • 작업 가능한 시간에 Slack #telecommuting 채널에 온라인이라고 공지해줍니다. 업무 및 수면으로 작업이 불가능해지면 채널에 오프라인이라고 공지해줍니다
  • 매일 비동기 방식의 Daily Standup Meeting을 수행합니다
    • Standup Meeting에서는 1). 어제 한일, 2). 오늘 할일, 3) 어려운 점, 4). 팀원에게 공유해야할 내용을 간단하게 서술합니다
  • 1주 1회 모두가 오프라인으로 만나서 각자 코딩을 수행합니다
    • 토요일 오전을 고려하고있으며, 점심을 먹고 헤어지는 방식을 고려중입니다
  • 4주에 1회 회고모임을 갖습니다. 이는 오프라인 미팅 때 진행할 예정입니다
  • OpenAPI를 위해 AWS를 사용할 예정이며, 일정 금액의 회비를 걷을 예정입니다

개발에 관하여

README

  • README에 포함되어야할 내용은 아래와 같습니다
  • README는 모두 영어로 작성합니다
  1. Abstract
  • 논문 요약을 적는다
  • 구글 번역 결과를 넣지 않는다
  1. Performance Table
  • 논문에서 제시하는 benchmark dataset을 활용하여 구현
    • benchmark dataset은 하나만 사용한다
    • 논문에서 제시한 hyper-parameter와 architecture로 재현
    • 재현이 안된다면, 본인이 변경한 사항을 서술
  1. Training history
  • tensorboard 또는 weights & biases를 이용, 학습의 로그의 스크린샷 첨부
  1. OpenAPI로 API Call 방법
  2. Usage
    5-1. Environment
    - dockerfile을 이용하여 build하고 run하는 방법
    - install from source code
    5-2. Training & Evaluate
    - ArgumentParser의 command가 code block 형태로 들어가야함
    - single-gpu, multi-gpu 방법 모두 서술
    5-3. Inference
    - ArgumentParser의 command가 code block 형태로 들어가야함
    - single-gpu, multi-gpu 방법 모두 서술
  3. Project structure
  • 터미널에서 tree커맨드 결과 첨부

Dockerfile

  • Nvidia-Docker 사용을 기본적으로 한다.
  • CUDA-10.1, CuDNN 7, ubuntu 18.04를 base image로 설정한다.

Git 브랜치 전략

GitHub Flow

  • 기본적으로 GitHub Flow에서 일부 변경된 브랜치 전략을 사용한다
  • 브랜치는 master, feature, env로 구성
  • master 브랜치로의 Pull Request는 GitHub Action을 통한 PR-test를 통과해야함
  • feature브랜치는 기능개발을 위한 브랜치
  • 작업이 완료된 feature브랜치는 master브랜치에 Pull Request
    • Pull Request는 적어도 한 명 이상의 리뷰를 통해 Approve를 받아야한다
    • 병합은 기본 Merge를 기본으로 한다
  • Master에 병합된 코드는 env 브랜치로 PR한다
    • 이때, env만의 GitHub Action을 통해 E2E(End-to-End Test) 테스트를 수행한다
    • E2E 테스트가 통과된 코드는 CI/CD 도구(Spinnaker etc)를 통해 OpenAPI로 배포된다
    • Pull Request는 적어도 두 명 이상의 리뷰를 통해 Approve를 받아야한다
  • 이전 PR이 master에 병합된게 있다면, feature 브랜치에서 master의 내용을 병합한다
$ git checkout feature
$ git merge master
$ // 기능개발 수행

reference

  1. Git 브랜칭 전략 : Git-flow와 Github-flow
  2. Why ?Branch is Out-of-Date? is Important

코드 컨벤션

구글 스타일 가이드

  • 코드 일관성을 유지하기 위해 Google의 python code style guide를 따른다. (자세한 내용은 reference를 참고한다)
  1. 모듈의 이름은 only 소문자
  2. 클래스는 CamelCase
  3. 함수의 이름은 snake_case
  4. 들여쓰기는 스페이스 4칸

reference

  1. Google Python Style Guide
  2. Google Python Style Guide 한글 번역
  3. 파이썬 (doc) 스타일 가이드에 대한 정리

타입 힌팅 - Typing

  • 모든 코드는 함수, 클래스, 변수의 타입 힌팅을 명시한다.
from typing import Dict, Tuple, Sequence

ConnectionOptions = Dict[str, str]
Address = Tuple[str, int]
Server = Tuple[Address, ConnectionOptions]

def broadcast_message(message: str, servers: Sequence[Server]) -> None:
    ...

# The static type checker will treat the previous type signature as
# being exactly equivalent to this one.
def broadcast_message(
        message: str,
        servers: Sequence[Tuple[Tuple[str, int], Dict[str, str]]]) -> None:
    ...

reference

  1. typing

  2. Example Google Style Python Docstrings


독 스트링 - Docstring

  • 코드의 가독성을 향상시키기 위해서, 기본적으로 아래의 사항에 대해서 docstring을 필수로 작성한다
  • 기본적으로 모든 코드의 docstring은 한글로 적는다
  1. 클래스
  2. 메소드

클래스 docstring

class ExampleError(Exception):
    """예외는 클래스와 같은 방식으로 문서화됩니다

    __init__ 메소드는 클래스 수준 혹은 __init__ 메소드 자체의
    docstring으로 문서화 할 수 있습니다.

    두가지 형태의 docstring은 모두 허용되지만, 혼합해서 사용해선 안됩니다.
    일관성을 유지하려면 한가지만 사용 부탁드립니다.

    Either form is acceptable, but the two should not be mixed. Choose one
    convention to document the __init__ method and be consistent with it.

    Note:
        Args 섹션에 `self`변수를 포함하지 않습니다.

    Args:
        msg (str): 예외를 설명하는 사람이 읽을 수 있는 문자열.
        code (:obj:`int`, optional): 에러 코드.

    Attributes:
        msg (str): 예외를 설명하는 사람이 읽을 수 있는 문자열.
        code (int): 에러 코드.

    """

    def __init__(self, msg: str, code: int) -> None:
        self.msg = msg
        self.code = code

메소드 docstring

def example_method(self, param1: int, param2: int) -> bool:
        """클래스 메소드는 일반적인 함수와 비슷합니다.

        Note:
            Args 섹션에 `self`변수를 포함하지 않습니다.

        Args:
            param1 (int): 첫번째 파라미터
            param2 (int): 두번째 파라미터

        Returns:
            (bool) 성공적이라면 True, 성공이 아니라면 False

        Example:
        >>> r = np.array([1, 1, 1])
        >>> discount_rewards(r, 0.99)
        np.array([1 + 0.99 + 0.99**2, 1 + 0.99, 1])
        """

        return True

reference

  1. Google Python Style Guide
  2. Example Google Style Python Docstrings

Pytest

  • 코드 작성 시, 테스트가 가능한 코드는 모두 test code를 작성한다.
  • 테스트 코드는 저장소의 루트 디렉토리의 tests폴더에 놓는다.

구현부(pytest_sample.py)

def square_10():
    return 10 * 10

테스트부(tests/test_sample.py)

import pytest

from pytest_sample import square_10


def test_square():
    f1 = square_10()
    assert f1 == 100

reference

  1. PyTest 프레임워크 기초 사용법

  • 모든 딥러닝 코드는 Torch-lightning 기반으로 작성한다.
class LitClassifier(pl.LightningModule):

    def __init__(self):
        super().__init__()
        self.l1 = torch.nn.Linear(28 * 28, 10)

    def forward(self, x):
        return torch.relu(self.l1(x.view(x.size(0), -1)))

    def training_step(self, batch, batch_nb):
        x, y = batch
        loss = F.cross_entropy(self(x), y)
        tensorboard_logs = {'train_loss': loss}
        return {'loss': loss, 'log': tensorboard_logs}

    def configure_optimizers(self):
        return torch.optim.Adam(self.parameters(), lr=0.02)

# train!
train_loader = DataLoader(MNIST(os.getcwd(), train=True, download=True, transform=transforms.ToTensor()), batch_size=32)

model = LitClassifier()
trainer = pl.Trainer(gpus=8, precision=16)    
trainer.fit(model, train_loader)

  • 학습 로그는 W&B를 사용한다
  • W&B 사용시 프로세서가 고아 프로세서가 되지 않도록 주의한다
import wandb
# Your custom arguments defined here
args = ...

wandb.init(config=args, project="my-project")
wandb.config["more"] = "custom"

def training_loop():
    while True:
        # Do some machine learning
        epoch, loss, val_loss = ...
        # Framework agnostic / custom metrics
        wandb.log({"epoch": epoch, "loss": loss, "val_loss": val_loss})

  • 하이퍼파라미터 탐색 도구는 ray의 tune을 사용한다.
import torch.optim as optim
from ray import tune
from ray.tune.examples.mnist_pytorch import get_data_loaders, ConvNet, train, test


def train_mnist(config):
    train_loader, test_loader = get_data_loaders()
    model = ConvNet()
    optimizer = optim.SGD(model.parameters(), lr=config["lr"])
    for i in range(10):
        train(model, optimizer, train_loader)
        acc = test(model, test_loader)
        tune.report(mean_accuracy=acc)


analysis = tune.run(
    train_mnist, config={"lr": tune.grid_search([0.001, 0.01, 0.1])})

print("Best config: ", analysis.get_best_config(metric="mean_accuracy"))

# Get a dataframe for analyzing trial results.
df = analysis.dataframe()
⚠️ **GitHub.com Fallback** ⚠️