Accelerating Reinforcement Batch Inference Speed through Unlimited Parallel Processing (AWS, k8s)

스페이스워크는 인공지능.데이터 기술로 최적의 토지 개발시나리오를 구현하는 프롭테크 기업입니다. 건축설계 AI는 강화학습 알고리즘을 기반으로 최적의 건축설계안을 만듭니다. 대표적인 제품으로 랜드북 이 있습니다 [11].

랜드북 — 건축설계 AI [1]

강화학습은 어떤 환경 안에서 정의된 에이전트가 현재의 상태를 인식하여, 선택 가능한 행동들 중 보상을 최대화하는 행동을 선택하는 방법입니다. 딥러닝을 활용한 강화학습에서 에이전트는 다양한 행동을 하고 그에 대한 보상을 얻는 방식으로 학습하게 됩니다.

품질좋은 설계안을 만들기 위해서는 이와 관련된 연구를 신속하고 빠르게 진행해야합니다. 하지만 건축설계 AI의 추론과정은 상당한 시간이 소요되어 개선이 필요했습니다. 본 포스팅에서는 건축설계 AI의 추론시간을 개선한 사례를 공유하겠습니다.

건축설계 AI 추론과정

하나의 데이터배치에 대해서 추론작업은 다음과 같이 진행됩니다. 하나의 추론과정에서 에이전트와 환경이 서로 상호작용합니다.

건축설계 AI 추론과정
  1. 에이전트가 현재의 상태를 기반으로 액션을 생성합니다.
  2. 환경은 에이전트의 액션을 바탕으로 새로운 상태를 에이전트에게 전달합니다.

위의 두 과정을 N번 반복하게 되면 하나의 추론이 끝나게 됩니다.

AS-IS: 환경패키지 사용

기존방법은 환경을 파이썬 패키지로 사용합니다. 즉 에이전트와 환경은 같은 컴퓨팅자원에서 동작합니다. 환경연산의 경우 각 연산은 독립적으로 수행할 수 있습니다. 보통 다수의 코어를 가진 컴퓨팅자원을 사용하기 때문에 사용할 수 있는 코어의 수만큼 환경연산을 병렬처리 합니다.

환경패키지 사용시 병렬처리

학습에 사용하는 컴퓨팅자원의 코어의 수는 직접적으로 학습속도에 영향을 주는 요소입니다. 예를 들어 배치사이즈가 128인 학습을 진행할 때 코어의 수가 4개인 경우와 8개인 경우를 비교해보겠습니다.

코어의 수가 4개인 경우 128개의 환경연산은 4개씩 동시에 이루어집니다. 그렇게 되면 한 코어에서 대략 128 / 4 = 32번 의 환경연산이 이루어지면 128개의 환경연산은 완료됩니다.

4개의 코어로 병렬처리 시 환경연산

반면에 코어의 수가 8라면 128개의 환경연산은 8개씩 동시에 이루어지면 128/ 8 = 16번 의 환경연산이 이루어지면 128개의 환경연산이 완료됩니다. 따라서 코어가 4개인 경우와 비교했을 때 환경연산시간은 2배정도 빠를 것으로 기대할 수 있습니다.

8개의 코어로 병렬처리 시 환경연산

하지만 특정 컴퓨팅자원의 코어의 수는 물리적으로 한정되어 있습니다. 즉 특정 수 이상으로는 코어를 늘릴 수 없습니다. 또한 학습 배치사이즈는 모델이 커지거나 연구방향에 따라서 커질 수 있습니다. 따라서환경연산이 학습을 진행하는 컴퓨팅자원에 의존적이라면 배치사이즈가 커짐에 따라서 학습속도도 느려질 것입니다.

TO-BE: 환경서버 운영

환경연산을 에이전트의 컴퓨팅자원에 독립적으로 만들기 위해서 환경을 패키지 형태가 아닌 서버형태로 변경하였습니다. 즉 에이전트 연산이 이루어지는 컴퓨팅자원과 환경연산이 이루어지는 컴퓨팅자원은 독립적 으로 운영될 수 있습니다.

환경서버 사용시 병렬연산

환경연산을 서버형태로 하게 되면 환경연산을 동시에 처리할 수 있는 수는 제한이 없게됩니다. 기존과 비교하면 다음과 같습니다. 정리해보면 환경서버 형태는 현재 운영중인 환경서버의 수만큼 동시에 환경연산을 처리할 수 있게됩니다. 또한 AWS 자원을 사용하기 때문에 환경서버의 수는 특별히 제한받지 않습니다.

구현

스페이스워크에서 어떻게 환경서버를 운영하는지 소개하겠습니다. 사용하는 기술스택은 다음과 같습니다.

  • FastAPI: 환경서버 구현을 위한 프레임워크
  • AWS: 환경서버에 필요한 컴퓨팅자원
  • Kubernetes: 환경서버를 운영하기 위한 컨터네이너 오케스트레이션 도구
  • Newrelic: 환경서버 모니터링을 위한 도구

FastAPI를 활용한 환경서버 구현

환경서버를 구현하기 위해서 FastAPI 프레임워크를 사용했습니다. FastAPI는 파이썬 기반 프레임워크로 다음과 같은 장점이 있습니다 [1]. 참고로 기존 환경패키지는 파이썬으로 구현되어 있습니다. 구현의 편의성을 위해서 파이썬 언어를 사용하여 환경서버를 구축하였습니다.

  • 빠릅니다. 파이썬 프레임워크중에서는 가장 좋은 성능을 보여줍니다 [9].
  • 개발표준을 채택하고 있습니다. OpenAPI(Swagger UI)를 활용할 수 있습니다.
  • 낮은 개발비용으로 개발할 수 있습니다.

실제로 많은 기업에서 FastAPI에 대해서 상당히 좋은 인상을 받았다는 것을 알 수 있었습니다 [1].

FastAPI 후기 [1]

다음과 같은 코드로 서버를 간단히 구현할 수 있습니다. 환경패키지를 임포트하여 환경서버에서 사용하는 방식으로 구현하였습니다.

동적할당을 위한 AWS 사용

위에서 언급되었듯이 환경서버는 AWS의 자원을 사용하고 있습니다. 건축AI 학습이 항상(24시간, 매일) 발생하는 이벤트가 아니기 때문에 온프라미스 장비를 구매하기에는 다소 불확실성이 큽니다. 따라서 컴퓨팅 자원을 동적으로 사용할 필요가 있었고 대표적인 클라우드 서비스인 AWS를 이용하였습니다.

참고로 AWS의 EKS는 Cluster Autoscaler 기능을 제공하고 있습니다 [3]. 즉 사용량이 많아진다면 EKS를 구성하는 노드(컴퓨팅 리소스)의 수도 늘어나게 됩니다.

실제로 내부에서 환경서버 사용 경향성을 추정해보면 다음과 같습니다. 환경서버는 건축설계 AI를 학습시킬 때활용될 수 있기 때문에 GPU 사용률을 근거로 추정하였습니다.

Newrelic Monitoring: 학습에 사용하는 GPU Utilization

요청이 상시로 구동하는 서비스와 다르게 규칙적으로 발생하지 않으며 요청이 한번 발생하면 그렇지 않을때와 비교했을 때의 차이가 매우 큰 편입니다. 따라서 동적인 자원할당이 합리적이라고 판단했습니다

환경서버 운영을 위한 Kubernetes(EKS)

환경서버를 안정적이고 운영비용을 낮추는 방향으로 관리하기 위해서 Kubernetes를 사용했습니다. Kubernetes에 대해서 알고 싶으신 분들은 Kubernetes의 공식문서[6]를 참고하시는 것을 추천드립니다.

Kubernetes를 사용하면 기존의 사람이 하던 컨테이너 관리를 많은 부분 시스템으로 처리할 수 있습니다. 예를 들어 컨테이너가 다운되면 개발자가 직접 컨테이너를 다시 띄우기 보다는 시스템이 컨테이너 상태를 체크하고 이를 다시 복구하도록 하는 것입니다. 이를 자동화된 복구(Self-Healing)이라고 합니다.

또한 서비스 디스커버리와 로드밸런싱도 손쉽게 구현할 수 있습니다. 에이전트 모델이 환경서버에 접근할 수 있도록 환경서버를 외부로 노출시킬 수 있으며 환경서버에 대한 부하에 대해서 AWS Network Loadbalaner를 이용하여 부하를 분산합니다.

그리고 Kubernetes는 Autoscaling기능도 제공합니다. Autoscaling의 종류에는 Horizontal Pod Autoscaling과 Vertical Pod Autoscaling이 있습니다. Horizontal Pod Autoscaler같은 경우는 동일한 리소스를 가지는 Pod의 수를 늘리거나 줄이는 것이고 Vertical Pod Autoscaling은 Pod의 리소스를 늘리거나 줄이는 기능을 합니다.

Kubernetes Horizontal Pod Autoscaler [6]

앞서 언급했듯이 환경서버 요청이 일정하지 않게 발생하지 않습니다. 따라서 같은 수의 환경서버를 항상 운영하는 것은 비효율적입니다. 환경서버 요청이 있을 때 환경서버의 수를 늘리고 요청이 없을때 환경서버의 수를 줄여주는 것이 필요했고 이를 위해 Kubernets의 HPA를 사용했습니다.

이를 시각화해보면 아래와 같은 구성이 됩니다.

HPA 사용시 다이어그램

환경서버 External Metric 정의를 위한 Newrelic Adapter

위에서 HPA를 사용하여 환경서버의 수를 조절한다고 했습니다. 그렇다면 무엇을 기준으로 환경서버의 수를 조절할 수 있을까요? 가장 기본적으로 Kubernetes는 파드의 리소스 사용량을 기준으로 스케일링 할 수 있습니다 [5].

CPU Utilization 기준으로 스케일링 [5]

하지만 환경서버 HPA 메트릭으로 파드의 리소스 사용량은 아쉬움이 있었습니다.

실제로 CPU 사용량을 바탕으로 스케일링하게되면 다음과 같은 문제가 발생할 수 있습니다. 하나의 요청에 대한 환경서버의 평균 및 최대 CPU Utilization은 90%라고 가정하겠습니다.

  • averageUtilization 90%로 설정: Pod의 수가 늘어나지 않는다. 하나의 환경서버에 요청이 몰릴경우에도 Pod의 수는 늘어나지 않는다. 환경서버는 하나의 요청씩 처리하며 따라서 CPU Utilization은 평균적으로 90%로 유지된다.
  • averageUtilization 90% 미만으로 설정: Pod의 수가 실제로 필요한 수보다 커진다. 배치사이즈가 128인 경우 환경서버의 수는 128개 이상으로 증가하여 averageUtilization 를 90%미만으로 떨어트린다.

보다 세밀한 스케일링을 위해서는 환경서버 파드 당 요청량이 필요했습니다. 예를 들어 적절한 환경서버 파드 당 요청량을 초당 4개로 설정한경우 초당 16개의 요청이 들어오면 환경서버 파드를 1개에서 4개로 스케일링 할 수 있습니다.

하지만 Kubernetes 자체적으로 Pod에 대한 요청량을 구할 수 있는 기능은 없습니다. 대신 커스텀 메트릭을 등록하여 이를 스케일링 매트릭으로 활용할 수 있습니다 [6].

Newrelic 혹은 Prometheus와 같은 Kubernetes에서 사용할 수 있는 모니터링 도구들은 쉽게 커스텀 매트릭을 등록할 수 있게합니다. 사내에서는 Newrelic을 활용하여 배포하고 있는 서비스에 모니터링을 하고 있습니다. 이에 HPA 매트릭으로 Newrelic을 활용하기로 했습니다.

Newrelic은 강력한 모니터링 도구입니다 [7]. 그리고 Newrelic에서는 New Relic Metrics Adapter를 제공합니다 [8]. New Relic Metrics Adapter는 newrelic이 제공하는 다양한 metric을 Kubernetes Metric으로 등록합니다.

Newrelic Metrics Adapter [8]

설치는 다음과 같이 진행할 수 있습니다. 참고로 이미 Newrelic이 Kubernetes Cluster에 설치되어 있다면 New Relic Metrics Adapter를 따로 설치할 수 있습니다.

다음과 같은 Requirement를 모두 충족했다면 Helm으로 간단히 설치할 수 있습니다.

newrelic metric adapter 설치 스크립트

앞서 환경서버의 요청량 기준으로 환경서버를 스케일링하는 것이 필요하다고 했습니다. 이에 다음과 같은 External Metric을 정의했습니다.

  • external_metric_name : env_servier_request_per_seconds
  • NRQL query: FROM Metric SELECT average(k8s.pod.netRxBytesPerSecond) / {하나의 요청당 bytes} / uniqueCount(k8s.podName) SINCE 1 minute AGO WHERE k8s.deploymentName = 'env-service-deployment'

설치가 완료되면 다음과 같은 명령어로 External Metric이 제대로 작동하는지 확인할 수 있습니다.

등록된 Metric 확인

매트릭이 정상작동한 것을 확인했다면 HPA를 배포할 차례입니다. 다음과 같은 YAML로 HPA를 설정할 수 있습니다.

External Metric을 활용한 Autoscaler

이제 HPA까지 모두 배포되었습니다. 전체적인 다이어그램을 그려보면 다음과 같습니다.

Newrelic Adapter를 활용한 다이어그램

실험결과

환경서버 운영시 추론 속도 개선이 얼마나 될 수 있는지 살펴보겠습니다.

64 Batch 추론시 실험결과
1024 Batch 추론시 실험결과
  • 에이전트 서버: c4.xlarge, 시간당 0.227 USD
  • 환경 서버: c4.xlarge/ 4 시간당 0.061 USD
  • AWS 비용계산 = (시간당 에이전트 서버비용 + 시간당 환경서버 비용 * 사용서버 개수) * 사용시간(초) / 3600
  • 참고로 실험에서는 명확한 비교를 위해서 HPA를 적용하지 않았습니다.

참고로 환경패키지-CPU4와 환경서버 4개의 추론시간이 차이가 납니다. CPU를 이용한 추론의 경우 에이전트 모델과 환경패키지를 연산하는 부분이 CPU를 함께 사용합니다. 하지만 환경서버의 경우 에이전트 모델이 사용하는 CPU와 환경패키지를 사용하는 CPU가 분리되어 환경연산 처리 의한 지연현상이 없습니다.

64개의 배치추론에 대한 실험에서 AWS 비용이 세배정도 사용하면 시간을 절반정도 줄일 수 있었습니다. 또한 거의 유사한 비용으로 시간을 기존대비 60%정도 줄일 수 있었습니다.

1024개의 배치추론에 대해서 병렬처리 효과가 더 명확하게 보입니다. 약 7.6배정도의 비용으로 속도를 4.5배정도 향상시킬 수 있었습니다.

결론

이번 포스팅에서는 건축설계 AI의 배치추론을 가속화하는 방법에 대해서 다뤘습니다. 에이전트와 환경이 하나의 호스트에 종속되면 필연적으로 병렬처리할 수 있는 코어의 수가 제한됩니다.

문제를 해결하기 위해서 에이전트와 환경이 각각 독립적인 컴퓨팅 자원에서 운영될 수 있도록 환경을 서버형태로 운영하였습니다. 또한 환경서버의 수요가 일정하지 않으므로 온프라미스 자원을 구축하기보다는 클라우드 서비스를 이용하여 컴퓨팅 자원을 동적으로 활용했습니다. (Cluster Autoscaling) 그리고 kubernetes의 HPA를 활용하여 자동으로 환경서버의 수를 조절하도록 했습니다.

참고로 본 포스팅에서 다룬 배치추론 가속화를 건축설계 AI의 학습 속도를 높이거나 서비스의 속도를 높이는데 활용할 계획입니다.

스페이스워크에서는 다양한 기술스택을 적극적으로 도입하고 있습니다. 앞으로 다른 포스팅을 통해서 다양한 내부사례를 공유하겠습니다.

[1] FastAPI [websites], (2022, Feb, 8)

[2] AWS Elastic Load Balancing 요금 [websites], (2022 ,Feb, 8)

[3] AWS EKS Cluster Autoscaling [websites], (2022, Feb, 8)

[4] Kubernetes Horizonal Pod AutoScaling [websites] (2022, Feb, 8)

[5] Kubernetes Resource Metric [websites], (2022, Feb, 8)

[6] Kubernetes Custom Metric [websites], (2022, Feb, 8)

[7] Newrelic [websites], (2022, Feb, 8)

[8] New Relic Metrics Adapter [websites] (2022, Feb, 8)

[9] Web Framework Benchmark [websites], (2022, Feb, 8)

[10] Spacewalk Demo [websites], (2022, Feb, 8)

[11] Spacewalk Landbook [websites], (2022, Feb, 8)

--

--