이전 포스팅인 MLOps에서도 말했는데 예측 모델이 사업적 성과를 만들기 위해서, 실제 운영 서비스에 적용되어야합니다.
DevOps의 마이크로 서비스 아키텍처와 마찬가지로 MLOps 에서는 모델의 예측값을 만들어내는 마이크로 서비스가 필요합니다.
서비스의 목적에 따라 아키텍처는 다양한 방법으로 설계할 수 있습니다. 서비스 운영 시나리오는 다양하므로, 하나의 정답이 존재하지 않습니다.
예를 들어 실시간에 가까운 데이터를 예측해야 한다면, 많은 컴퓨팅 자원이 필요하고 이에 따른 운영 비용은 중요한 문제입니다. 반대로 하루에 한 번 1,000개의 레코드만 처리하고, 응답시간이 중요하지 않을 수 있습니다. 이땐 간단히 linux crontab를 활용해도 됩니다.
기계 학습 모델을 학습하고 평가하는 일련의 과정은 많은 발전이 이루어졌습니다.
하지만 실질적으로 모델의 예측값을 사용자에게 전달하는 “모델 서빙”은 최근에서야 주목받기 시작하였습니다. 모델 서빙은 Tensorflow Serving[1], Clipper[2], Sagemaker 등 많은 곳에서 연구되고 서비스화되고 있습니다.
이번 글에서는 모델 서빙 시 고려해야 하는 것들을 알아보고, 어떤 식으로 해결할 수 있는지 살펴보겠습니다.
MLOps에서도 말했지만 모델의 성능은 외부 요인에 변화가 큽니다.
테스트에 사용된 데이터가 그 당시의 현실을 잘 대표한다고 하더라도, 시간에 따라 테스트 데이터 세트는 현실과 거리가 멀어지게 됩니다. 테스트를 아무리 꼼꼼히 하더라도 결국에는 모델의 성능과 데이터는 현실을 제대로 반영하지 못하게 됩니다.
즉, 모델의 정확도는 시간이 지남에 따라 떨어집니다.
따라서 우리는 모델의 정확도를 지속적으로 체크하고 최신의 데이터를 구해 정제하여 모델을 다시 학습하고 업데이트하는 작업을 주기적으로 진행해야합니다.
그런데 모델 변경내용을 관리하지 않는다면, 어떤 모델로 교체할지 판단하기 어렵습니다.
그리고 소스 코드를 형상 관리하듯이 모델에 대한 형상관리가 필요합니다.
모델의 형상 관리란 이처럼 모델의 변경 이력과 성능에 대한 정보를 관리하는 것입니다.
모델 서빙은 일반적인 DevOps의 애플리케이션 운영과 거의 같습니다. 마이크로 서비스로 나누어 장애가 발생하더라도 피해를 최소화하고, 버전 업데이트 역시 서비스의 끊김 없이 가능해야 합니다. 모델 서빙에서는 DevOps의 애플리케이션이 예측 모델이라는 것과 GPU와 같이 높은 사양이 필요하다는 것이 차이점입니다.
가용성이 고려되어야 하는 지점은 크게 장애 발생 시 대처 방법과 새로운 모델 적용 2가지입니다. 여기서 말하는 장애는 일반적인 서버의 비정상 동작뿐만 아니라 모델의 성능, 데이터의 극적인 변화와 같은 MLOps 만의 장애가 포함됩니다. 장애 발생 후 새로운 모델을 만들었을 때, 새로운 모델을 교체하는 것을 사용자 경험이 끊기지 않도록 하는 것도 고가용성의 중요한 부분입니다.
MLOps 중 모델 서빙은 활발한 연구가 이루어지고 있습니다.
대표적인 예로 [1]Tensorflow-Serving, [2] Clipper 논문이 있습니다. 이 두 논문에서 공통으로 설명하는 기본적인 개념에 대해서만 정리해보겠습니다.
도커 컨테이너는 다양한 형태의 애플리케이션의 실행환경을 규격화된 개념으로 추상화하여 어떠한 운영환경에서든 동작할 수 있도록 합니다. 운영환경이 무엇이든지 도커 컨테이너라는 규격화된 개념으로 개발자가 애플리케이션 개발에 집중할 수 있도록 해줍니다.
모델 컨테이너 역시 데이터 과학자가 모델 개발에 집중할 수 있도록 모델 운영을 추상화한 개념입니다.
예를 들어 딥러닝 모델을 운영한다고 가정해보겠습니다.
만들어진 모델을 사용하기 위해서는 서버에 GPU가 장착되어있어야 하고, 운영체제가 GPU를 사용할 수 있도록 그래픽 드라이버가 설치되어야 합니다. 이 드라이버 위에 CUDA Toolkit과 같은 도구들이 설치되어야 하고, Caffe, TensorFlow, theano 와 같은 프레임워크가 설치되어 있어야 합니다. 그 위에 다시 모델을 읽어와 예측값을 응답해주는 애플리케이션을 작성해야 합니다.
위 그림과 같이 도커 컨테이너를 사용하면 반복적인 인프라 설정 없이 애플리케이션만을 수정하여 필요한 서비스를 만들 수 있습니다.
DevOps에서 도커와 오케스트레이션 도구를 활용하여 애플리케이션을 효율적으로 전달하는 시스템과 유사합니다.
운영환경은 도커로 잘 해결할 수 있습니다.
또 다른 문제는 애플리케이션입니다. 일반적인 소프트웨어 엔지니어라면 Spring, Django 와 같은 도구들을 활용해 API 코드를 작성하고 nginx 나 tomcat 같은 서버를 사용해 실제 애플리케이션을 만들 수 있습니다.
데이터 과학자는 위와 같은 도구들에 익숙하지 않아 애플리케이션을 만드는 일이 어려울 수 있습니다.
모델 컨테이너는 더 나아가 데이터 과학자가 최소한의 개입으로 모델을 배포할 수 있도록 해야 합니다.
예를 들어 AWS의 Sagemaker 는 아래와 같이 모델 서빙을 제공합니다.
xgb_predictor = xgb_model.deploy(initial_instance_count=1,
content_type='text/csv',
instance_type='ml.t2.medium'
)
Sagemaker에서 제공되는 알고리즘들은 복잡한 환경설정 없이 빠르게 HTTP Endpoint를 만들 수 있습니다.
모델 서빙하는 것은 단순히 HTTP Endpoint를 만드는 것이 아닙니다.
모델은 자주 교체가 이루어지고, 교체 과정은 서비스 중지 시간을 최소화해야 합니다.
AWS Sagemaker 는 훌륭한 서비스이지만, 세부적인 모델 서빙 기능을 만들기 위해선 추가적인 기능이 필요합니다. 기능의 예를 들면 교체할 모델을 선택하면 기존 인스턴스를 종료하고 새 인스턴스를 띄우고, 이 변경 내용을 관찰할 수 있는 최소한의 UI입니다.
소프트웨어 엔지니어라면 최소한의 기능을 만들어 팀 내부에서 사용하도록 공유할 수 있습니다. 하지만 데이터 과학자가 웹, 서버, 데이터베이스와 같은 애플리케이션 개발 방법을 학습해야 합니다. 데이터 과학자에게는 충분히 부담스러울 수 있습니다.
모델 컨테이너는 [1]Tensorflow-Serving에서 “Serving Container”라는 개념으로, [2] Clipper에서는 “Model Container”라는 개념으로 자세히 설명되어있습니다.
카나리는 옛날 석탄 광산에서 유독가스의 누출 위험을 알려주는 새입니다. 일반적인 소프트웨어 개발 방법에서 카나리 테스트는 일부 트래픽을 새로운 버전의 애플리케이션에 흘려보냄으로써 새로운 버전의 문제점을 파악하는 데 사용됩니다.
MLOps에서는 특히 새로운 모델을 서빙하는 건 상당히 두려운 일입니다.
이 문제를 해결하기 위해 카나리 모델은 새로운 모델을 완전히 올리기 전 일부 트래픽을 새로운 모델에 흘려보냅니다. 카나리 모델이 어떻게 반응하는지 살펴보고 서빙을 할지 말지 판단하는 데 도와줍니다.
앞서 설명해 드린 모델 컨테이너라는 규격화된 애플리케이션이 있다면 이 안에 모델을 바꾸는 것만으로도 간단히 카나리 모델을 적용할 수 있습니다. 모델 컨테이너가 없다면 카나리 모델을 서빙하는 또 다른 애플리케이션이 담긴 도커 이미지를 반복적으로 빌드 해야 합니다.
위와 같이 카나리 테스트를 적용한다고 하더라도 모델은 시간이 흐르며 성능은 변합니다. 현재 운영 중인 모델이 기준 성능을 만족하지 못하는 상황은 언제든지 발생할 수 있습니다. 이러한 상황이 발생한다면 MLOps가 해야 하는 작업은 크게 (1) 서비스 중지, (2) 특정 모델로 교체입니다.
(1) 서비스 중지는 잘못된 예측값을 주느니 차라리 예측하지 않는 것이 좋은 경우입니다. (2) 특정 모델로 교체하는 것을 롤백이라고 합니다.
모델 컨테이너라는 규격이 있다면 내부 모델 바이너리 파일을 교체하는 것으로 간단히 제공할 수 있습니다. 모델 바이너리 교체를 Rolling Update 나 Recreate 와 같은 전략을 취할 수 있습니다만, 서빙되어야 하는 서비스의 특징에 따라 장단점이 명확합니다.
예를 들어 비싼 GPU 클러스터를 사용한다면 Rolling Update 전략 선택 시 새로운 GPU 클러스터에 애플리케이션이 준비되면 기존의 애플리케이션이 중지됩니다. 운영비용이 부담스럽다면 약간의 중지 시간을 감수하고 Recreate 전략을 취할 수 있습니다.
위 롤백을 설명하며 모델 성능이 문제가 있다면 MLOps가 해야 하는 작업은 크게 (1) 서비스 중지, (2) 특정 모델로 교체라고 했습니다.
(2) 특정 모델로 교체의 상황이라면 어떤 모델로 교체할 것인지 정의해야 합니다. 여기서 말하는 특정한 모델이란 이전에 운영했던 모델일 수도 있고, 그럭저럭 성능이 나왔었던 모델일 수 있습니다. 안타깝게도 돌아가 모델을 찾는 일 역시 매우 까다롭습니다.
예를 들어 위와 같이 Feature A, B, C를 사용해 만든 모델 A와 B가 있다고 가정해보겠습니다. 더 좋은 모델을 만들기 위해 Feature A, B의 계산방식을 바꾸고 Feature C를 제거하여 모델 C를 새로 만들었습니다. 이 경우 모델 C에 문제가 생긴다면 모델 A, B로 변경해도 될까요?
새로운 모델 C를 만들기 위해 Feature Store 란? 글에서 설명해 드린 것처럼 Data Lake에서 데이터를 가공해 학습 데이터를 만들어 냅니다.
모델 A, B와 모델 C는 이 학습 데이터가 완전히 달라졌고, 심지어 입력 형태도 변경되었습니다. 모델 C를 서빙하기 위해 사용자 클라이언트가 요청하는 데이터 형태도 바뀌었을 것이기 때문에 모델을 단순히 특정 시점에 만든 것으로 변경하는 것은 상당히 위험합니다. 마찬가지로 같은 숫자 Feature A, B, C를 사용한 모델 A와 B 라 하더라도, 그 계산 방식이 완전히 다를 수 있으므로 과감히 모델을 변경하는 것은 위험합니다.
모델의 형상 관리는 단순히 모델 학습 소스 코드, 알고리즘, 하이퍼 파라미터를 관리하는 것이 아닙니다.
더 나아가 Feature Store에 저장된 데이터를 어떤 식으로 가공하여 학습, 평가 데이터가 만들어졌는지, 모델은 서빙을 하며 어떤 성능을 보여왔는지 MLOps의 End-to-end 파이프라인의 버전입니다.
이 버전을 관리하는 것이 모델 저장소이며, [3] Versioning for End-to-End Machine Learning Pipelines에서 그 개념을 이해할 수 있습니다.
모델 서빙이란 사용자에게 모델 예측 결과를 효율적으로 전달하는 방식입니다.
서비스 수준에 따라 모델 서빙의 시스템 구성이 매우 다양해집니다. 모델 서빙이 실시간에 가까워질수록, 운영 중인 모델의 수가 많아질수록 데이터 과학자가 해결해야 할 문제는 눈덩이처럼 늘어납니다. 이러한 문제는 크게 모델의 버전 관리, 운영환경 구축, 고가용성과 확장성입니다. 이 문제를 해결하기 위해 모델 컨테이너, 카나리 테스트, 롤백, 모델 저장소와 같은 개념이 필요합니다.