AWS Neuron SDK & AWS Inferentia
Introduction
Model serving 시 모델의 입력에 대해 출력을 산출하는 과정인 순전파 (forward propagation)는 model 내부의 layer들을 거치는데, 각 layer에는 수많은 단순 연산들이 존재한다.
이를 병렬로 수행하기 위해 CPU (Central Processing Unit)보다 병렬 계산에 이점이 있는 GPU (Grphic Processing Unit)을 많이 활용하고 있다.
하지만 아래와 같은 한계점이 존재한다.
- 비싸다. (물리적 GPU 비용, 전력 소모, 클라우드 서비스 등 모든 측면에서)
- 물리 코어를 가상 코어로 나눠 사용할 수 없어 자원을 낭비하기 쉽다.
- 연산량이 적은 모델에 대해 추론할 때 CPU와 GPU 간 메모리 통신 오버헤드가 큰 경우 CPU에서 자체적 처리가 더 효율적일 수 있다.
이러한 이유들로 다양한 회사에서 GPU에 비해 저렴하지만 효율적인 ML 연산을 할 수 있는 AI 가속기들을 아래와 같이 개발하고 있다.
- AWS Trainium, Inferentia
- Google TPU (Tensor Processing Unit)
- Apple ANE (Apple Neural Engine)
AI 가속기들은 ASIC (Application-Specific Integrated Circuit, 특정 용도용 집적 회로) 기반으로 개발되고 있다.
ASIC은 공정상의 이유로 소량 생산 시 생산 비용이 비싸고 설계 및 수정도 어려운 한계점들이 있지만, ML 연산은 대부분이 단순하고 수많은 독립적 연산을 처리하는 패턴이기 때문에 model serving을 위한 하드웨어를 생산하기 적합하다.
AI 가속기를 사용한 Amazon EC2 객체는 아래와 같다.
- Amazon EC2 Trn1/Trn1n
- AWS Trainium으로 구동
- 모델 학습에 최적화 (학습 시간 단축, 저렴한 비용)
- Amazon EC2 Inf1/Inf2
- AWS Inferentia로 구동
- 모델 추론에 최적화 (저렴한 비용, 높은 처리량, 낮은 지연 시간)
두 인스턴스 중 모델을 serving 할 수 있는 Amazon EC2 Inf* 인스턴스에 대해 알아보겠다.
Amazon EC2 Inf*는 앞서 설명한 ASIC으로 구동되기 때문에 모델을 AWS Neuron SDK로 모델을 컴파일해야한다.
- AWS Neuron: AWS Trainium 및 AWS Inferentia 기반 인스턴스에서 deep learning workloads를 실행하는데 사용되는 SDK
- End-to-End ML development lifecycle에서 새로운 모델 구축, 학습 및 최적화 후 배포를 위해 사용
- TensorFlow, PyTorch, Apache MXNet에 통합되는 deep learning compiler, runtime, tools 포함
AWS Neuron SDK를 통한 모델 컴파일은 TensorRT 변환과 유사하게 진행된다.
변환된 모델을 Triton Inference Server로 모델을 배포하면 모델 serving에 대한 준비는 모두 완료된다.
Model Compile
앞서 말한 것과 같이 AWS Neuron SDK를 통해 모델을 컴파일하기 위해서는 모델의 구조를 담고 있는 deep learning framework (PyTorch, TensorFlow, …) 기반의 코드와 학습이 완료된 가중치를 준비해야한다.
AWS Neuron SDK 설치
1 | pip config set global.extra-index-url https://pip.repos.neuron.amazonaws.com |
1 | sudo apt-get install aws-neuronx-collectives=2.* -y |
AWS Neuron SDK 기반 모델 컴파일
1 | import torch |
- 컴파일은
torch.neuron.trace()
함수를 사용해 진행할 수 있다.
추론을 통한 결과 비교
1 | model_neuron = torch.jit.load('model_neuron.pt') |
이를 응용하고 시험하기 위해 STD 모델인 CRAFT를 AWS Neuron SDK로 컴파일하기 위해 아래 코드를 개발했다.
1 | import torch |
1 | python torch2neuron.py |
상세한 로그들은 여기에서 확인할 수 있다.
- 결과 비교를 위해서는 Amazon EC2 Inf1 인스턴스를 실행해야하지만 아직은 불가능하기 때문에 일단 여기까지! $\rightarrow$ 차후
model_neuron.pt
를 Python backend 기반 Triton Inference Server로 모델 배포 가능! - 모델 컴파일 시 Dynamic Shapes는 현재까지는 지원 X
하지만 동적인 입력을 위해 아래와 같이 입력 텐서의 크기를 torch.nn.functional.pad
로 조정해줄 수 있다.
1 | import torch |
모든 케이스에 적용이 가능하진 않지만 CNN (Convolution Neural Network)과 같은 계층으로 구성된 신경망에 대해서는 가능하다.
Compile 외않되?
위에서 진행한 것과 같이 Amazon EC2 Inf1 인스턴스에 모델들을 서빙하기위해 AWS Neuron SDK로 모델들을 컴파일하는 도중 STR 모델인 PARSeq가 컴파일 시 에러가 발생함을 확인하였다.
컴파일 에러에 대한 ChatGPT 선생님의 소견은 아래와 같다.
이는 PyTorch의 동적 그래프 실행 방식 때문입니다. PyTorch는 실행 시에 모델의 형태를 동적으로 조정할 수 있기 때문에 오류가 발생하지 않을 수 있습니다. 그러나 Neuron은 컴파일 시에 정적인 그래프를 사용하므로 텐서 형태의 일치가 필요합니다. 그래서 Neuron 변환 과정에서 오류가 발생하는 것입니다.
이러한 문제를 해결하기 위해서는 모델 변환 과정에서aten::view
연산을 수정해야 합니다. 현재 모델 변환에 사용된aten::view
연산에서 잘못된 형태로 텐서를 변경하려고 하기 때문에 오류가 발생한 것입니다.aten::view
연산을 수정하여 입력 텐서의 형태를 목표 형태로 올바르게 변경하도록 해야 합니다.
선생님의 말씀과 같이 Python만을 사용해 동일 모델을 순전파하여 추론하였을 때는 에러가 발생하지 않음을 확인할 수 있었고, 이는 내부에서 동적인 텐서 크기가 존재하여 발생하였음을 추측할 수 있었다.
자세하게는 특정 레이어에서 텐서가 순전파될 때 텐서의 크기가 동적이기 때문에 발생하였음을 하단의 Error Logs를 살펴보면 알 수 있다.
Error Logs
1 | /opt/conda/envs/neuron/lib/python3.8/site-packages/torch/__init__.py:853: TracerWarning: Converting a tensor to a Python boolean might cause the trace to be incorrect. We can't record the data flow of Python values, so this value will be treated as a constant in the future. This means that the trace might not generalize to other inputs! |
비슷한 구조의 Encoder-Decoder 모델의 컴파일 과정 예제를 기반으로 컴파일 에러를 해결해보려 했으나,,, 위에서 기술한 것과 같이 동적 그래프 방식 모델은 AWS Neuron SDK로 컴파일이 불가능하다.
아래와 같이 PARSeq.decode()
메서드에 입력되는 텐서가 슬라이싱되어 입력되기 때문에 매 반복에 따라서 크기가 변하는 것을 확인할 수 있고, 이를 해결하기 위해 동적인 입력 텐서에 대해 가장 큰 차원의 입력 텐서를 기준으로 작은 텐서를 패딩하여 텐서의 차원을 고정하려 했으나 계층의 순전파 이후 결과가 변경되기 때문에 불가능한 것으로 결론지었다.
1 | ... |
하지만 이러한 구조를 정적으로 변경하면 컴파일이 가능하다. (중요한 것은 꺾이지 않는 마음.)
모델의 성능은 감소하겠지만, Amazon EC2 Inf1의 비용이 매우 저렴하기 때문에 경우에 따라서 좋은 선택지가 될 수 있다.
모델을 정적으로 만들기 위해 아래와 같이 변경하였다.
1 | ... |
이렇게 정적으로 변경한 모델을 AWS Neuron SDK로 컴파일하는 코드는 아래와 같다.
1 | import torch |
CRAFT와 다르게 PARSeq는 모델 구조의 이해도가 상대적으로 떨어지기 때문에 컴파일에 어려움이 많았다.
MLOps 개발자의 길은 험난하다…
Refereces
- Hyperconnect: 머신러닝 모델 서빙 비용 1/4로 줄이기
- AWS: Welcome to AWS Neuron
- Inferentia1 Architecture
- Inferentia2 Architecture
- Model Architecture Fit Guidelines
- Amazon EC2 Trn1
- Amazon EC2 Inf2
- Scatter Lab
- AWS Inferentia를 이용한 모델 서빙 비용 최적화: 모델 서버 비용 2배 줄이기 1탄
- AWS Inferentia를 이용한 모델 서빙 비용 최적화: 모델 서버 비용 2배 줄이기 2탄