깃랩 CI/CD 기반의 MLOps 도입기

MarkAny·2022년 3월 8일
2

MLOps란?

MLOps(Machine Learning Operations)란 머신러닝, 딥러닝 솔루션을 개발하는데에 필요한 데이터 엔지니어링, 모델 학습, 배포 등의 작업을 효율적으로 운영하는 분야를 말합니다. 위키피디아에는 아래처럼 정의되어 있습니다.

MLOps or ML Ops is a set of practices that aims to deploy and maintain machine learning models in production reliably and efficiently.

DevOps, ML, DE 사이에 있는 MLOps

MLOps는 최근 머신러닝, 딥러닝 분야에서 굉장히 핫한 분야로,
Kubeflow, Metaflow, MLflow, Airflow 등 다양한 MLOps 플랫폼들이 있습니다.

이번 글에서는 저희 사내에서 구축한 MLOps 구조와 그 개발기를 공유드리겠습니다.

구축 환경

먼저 저희 팀에서는 학습 코드, 모델을 깃랩(Gitlab)을 통해 관리하기 때문에, 깃랩을 최대한 이용한 MLOps 환경이 필요했습니다. 또한 이전에는 모델의 학습, 최적화, 배포 등이 여러 깃랩 프로젝트로 나뉘어 있거나 많은 부분이 수동으로 이루어졌습니다.

그래서 깃랩 CI/CD를 통해 파편화된 작업들을 하나의 파이프라인으로 구축하여 효율성을 높이고자 했습니다.

구축 과정

깃랩 러너(Gitlab runner) 설치

제일 먼저 모델 학습을 진행할 서버에 러너를 설치해줍니다. 저희 팀 같은 경우 NVIDIA A5000와 Titan XP가 장착된 서버 2대에 설치 진행하였고, 러너는 도커 기반으로 설치하였습니다. 설치관련 문서

설치가 완료된 깃랩 러너들

설치가 완료된 이후 파이토치 학습으로 테스트를 진행하였는데 많은 분들이 겪어서 알고계시는 도커 메모리관련 이슈가 러너에서도 발생하여 아래와 같이 러너 config 파일 볼륨부분에 shared memory 관련 정보 ( "/dev/shm:/dev/shm" ) 를 추가해주었습니다.

ERROR: Unexpected bus error encountered in worker. This might be caused by insufficient shared memory (shm)

[[runners]]
	name = "training server"
    url = ...
    token = ...
    executor = "docker"
    [runners.docker]
    	volumes = ["/cache", "/dev/shm:/dev/shm"]

데이터 로드 및 전처리

기본적으로 저희 팀에서 데이터는 나스에서 관리되고 있고, 이번 작업에서는 DVC라는 오픈소스를 통해 데이터 버전관리를 진행했습니다.

DVC를 통한 데이터셋 버전관리 (https://dvc.org 이미지)

그렇기 때문에 아래의 그림처럼 깃랩 CI/CD 과정 중 데이터를 로드하는 job에서는 특정 데이터셋 및 버전 내용을 담고 있는 .dvc 파일을 통해 데이터를 로드하였습니다. (자세한 내용: 링크)

preprocess:
  stage: preprocess
  image: nvcr.io/nvidia/pytorch:20.11-py3
  script:
    - ...
    - dvc pull
    - cp -r dataset copy
    - python3 preprocess.py copy

이후에는 학습에 필요한 사전작업들(이미지 리사이즈, 특정 라벨 삭제 등)을 진행합니다.

모델 학습

학습은 파이토치로 진행하였습니다. 그렇기 때문에 실제 학습을 돌리는 job의 이미지는 파이토치가 설치되어 있어야하고, 그게 아니라면 requirement.txt와 같은 파일을 통해 학습할 환경을 먼저 구축해주어야 합니다.

학습 환경 구축 이후에는 전처리가 끝난 데이터셋을 이용하여 학습을 진행합니다. 테스트 학습의 경우 Pascal VOC 데이터셋을 이용하여 진행하였고, 아래와 같이 학습이 되는 것을 확인할 수 있었습니다.

Pascal VOC 데이터셋 학습 결과

모델 관리 프로젝트와 연동

저희 팀의 경우 배포될 모델을 깃랩의 특정 프로젝트에서 관리하기 때문에, 학습 이후에는 모델 관리 프로젝트와의 연동이 필요했습니다.

깃랩의 경우 트리거를 이용하여 특정 프로젝트의 파이프라인을 실행시킬 수 있기에 저희 팀의 경우 학습 프로젝트 이후 모델 관리 프로젝트 순서로 진행되도록 구성하였습니다.

또한 모델/가중치를 도커 이미지로 관리하기 때문에, 모델 관리 프로젝트에서는 모델/가중치 들어있는 이미지를 불러와 이후의 작업들을 수행합니다.

모델/가중치가 포함된 도커이미지 생성 후 트리거

build_docker:
  script:
    - MY_CURRENT_TAG="weights"
    - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
    - docker build --pull -f Dockerfile.weights -t "$CI_REGISTRY_IMAGE:$MY_CURRENT_TAG" .
    - docker push "$CI_REGISTRY_IMAGE:$MY_CURRENT_TAG"
    - curl -X POST -F token=$WEIGHTS_PROJECT_TOKEN -F ref=$REF_NAME [모델관리 프로젝트 파이프라인]
  tags:
    - linux

TensorRT 변환

저희 팀에서는 모델 성능을 높이기 위해 모델을 TensorRT 엔진으로 빌드 후 배포합니다. 실제 제품이 동작하는 서버들의 GPU가 다양하기 때문에 모델은 ONNX 모델로 관리되었고, 배포되는 서버와 동일한 GPU를 가진 서버에서 TensorRT 변환 후 도커 이미지로 생성하는 단계를 거칩니다.

GPU별 TensorRT 엔진 생성 구조

최종 파이프라인 구조

결과적으로 아래 그림과 같은 파이프라인을 구축할 수 있었습니다. 크게 나누어보면 깃랩 CI/CD 작업 내에서 (1) NAS, DVC를 통한 데이터 로드, (2) 학습 후 ONNX로 도커 이미지 생성, (3) 이후 트리거를 통해 모델관리 파이프라인 실행, (4) 해당 모델 이미지를 불러와 각 GPU별 TensorRT 변환 후 (5) 도커이미지 생성'의 구조를 가집니다.

학습 및 TensorRT 변환 구조

마치며

최종적으로 위와 같은 구조를 설계하고 개발하고나니 확실히 많은 부분들을 세이브할 수 있었고, 효율적인 개발을 할 수 있었습니다. 그리고 학습 모니터링, 하이퍼파라미터 튜닝 등 더 많은 기능들이 필요하다는 생각 또한 들었습니다.

MLOps 플랫폼을 사용한건 아니지만 위와 같은 비슷한 환경에서 개발하고 계시는 개발자분들에게 조금이나마 도움이 되었으면 좋겠습니다:)

MARKANY_둘러보기

profile
마크애니 기술 블로그 입니다.

0개의 댓글