헤파이토스 프로젝트 - HephaestusProject/template GitHub Wiki
헤파이토스 프로젝트는 딥러닝 논문을 구현 및 재현하는 모임입니다
헤파이토스 프로젝트의 목표는 아래와 같습니다
- 논문 구현
- 성능 재현
- OpenAPI를 제공하여 사용자가 바로 사용해볼 수 있게 모델 배포
- 사용자가 바로 재현할 수 있도록 가이드 문서 제공
- 사용자가 풀고자하는 문제에 바로 이용할 수 있도록 여러 기능을 제공
5-1. 분리된 Train/Evaluate/Inference 모듈 제공
5-2. TorchServe로 랩핑된 serving 모델 제공(Dockerfile / Dockerimage 포함)
-
논문은 개인이 하고 싶은 논문을 선택하시면 됩니다
-
구현 및 재현 일정에 대한 스켸쥴링은 개인이 합니다
- 초기에 논문을 읽은 후에, 구현할 기능을 정리합니다 (이때, 기능은 원자단위로 쪼갭니다)
- 정리된 기능을 기반으로 개발 일정을 산출합니다
- 산출된 일정에 2.5배를 곱합니다
- 최종 산출된 일정을 데드라인으로 논문 구현 및 재현을 시작합니다
-
논문 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는 모두 영어로 작성합니다
- Abstract
- 논문 요약을 적는다
- 구글 번역 결과를 넣지 않는다
- Performance Table
- 논문에서 제시하는 benchmark dataset을 활용하여 구현
- benchmark dataset은 하나만 사용한다
- 논문에서 제시한 hyper-parameter와 architecture로 재현
- 재현이 안된다면, 본인이 변경한 사항을 서술
- Training history
- tensorboard 또는 weights & biases를 이용, 학습의 로그의 스크린샷 첨부
- OpenAPI로 API Call 방법
- 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 방법 모두 서술
- Project structure
- 터미널에서 tree커맨드 결과 첨부
- Nvidia-Docker 사용을 기본적으로 한다.
- CUDA-10.1, CuDNN 7, ubuntu 18.04를 base image로 설정한다.
- 기본적으로 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
$ // 기능개발 수행
- 코드 일관성을 유지하기 위해 Google의 python code style guide를 따른다. (자세한 내용은 reference를 참고한다)
- 모듈의 이름은 only 소문자
- 클래스는 CamelCase
- 함수의 이름은 snake_case
- 들여쓰기는 스페이스 4칸
- 모든 코드는 함수, 클래스, 변수의 타입 힌팅을 명시한다.
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:
...
- 코드의 가독성을 향상시키기 위해서, 기본적으로 아래의 사항에 대해서 docstring을 필수로 작성한다
- 기본적으로 모든 코드의 docstring은 한글로 적는다
- 클래스
- 메소드
클래스 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
- 코드 작성 시, 테스트가 가능한 코드는 모두 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
- 모든 딥러닝 코드는 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()