본문 바로가기

DEVELOP_NOTE/ML

[Pytorch Profiler]파이토치 프로파일러를 통해 모델 성능 모니터링하기!

보통 PoC단계의 모델개발에서 실제 프로덕트단으로 넘어가는 과정에서 반드시 고려해야할 사항들이 있다.

바로 모델의 속도리소스이다.

 

프로덕트단에서 서비스로서 제공되는 모델은 정확도는 기본이고,

유저의 이벤트에 빠르게 반응해야하며, 대규모의 트래픽에도 운용될 수 있는 적은 리소스로 운용될 수 있어야한다.

이를 위해서는 모델 개발환경에서부터 모델의 성능, 속도, 리소스에 대한 모니터링과 함께 반복적인 개선작업이 수반되어야한다.

 

오늘은 내 모델의 속도와 리소스에 대해 모니터링 할 수 있는 Pytorch Profiler를 활용하기 위해 진행한 튜토리얼 과정에 대해 

정리해보려 한다.

 

1. Profiler?

먼저 프로파일러의 개념에 대해서 잠깐 이해해보도록 하자.

프로파일러는 "머신러닝 모델의 학습과 실행 과정에서 발생하는 다양한 작업과 리소스 사용을 모니터링하고 분석하는 도구를 의미한다.

특히 대규모 또는 복잡한 머신러닝 모델을 최적화하고 디버깅하는 데 유용하게 사용될 수 있다.

Pytorch에서도 프로파일러를 기본 모듈로 제공하고 있는데, pytorch profiler의 기능에 대해서 조금 더 상세하게 살펴보자.

 

1) 성능분석

프로파일러는 모델의 학습과 추론 과정에서 각 단계별 실행 시간을 측정한다.

이를 통해 계산과정에서 성능 병목이 발생하는 구역을 식별할 수 있도록 한다.

2) 메모리 사용 분석

머신러닝 모델, 특히 딥러닝 모델은 종종 대량의 메모리를 사용하게 되는데, 이때, 프로파일러는 메모리 사용량을 추적하여 메모리 누수 또는 비효율적인 메모리 사용을 찾아낼 수 있는 모니터링 기능을 제공한다.

3) 하드웨어 리소스 사용 분석

현재 모델이 사용중인 하드웨어 리소스, CPU, GPU 자원의 사용률과 효율성을 분석할 수 있다.

수치화 및 시각화를 통해 하드웨어 리소스의 이용도를 최적화할 수 있게된다.

4) 병렬 처리 및 분산 학습 분석

대규모 데이터셋이나 복잡한 모델의 경우, 병렬 처리와 분산 학습이 중요하다.

프로파일러는 이러한 환경에서의 작업 분할과 통신 효율성을 분석할 수 있다.


🙋‍♂️ 자, 이제 프로파일러의 역할과 기능에 대해서는 대략 정리가 되었으니, 실제 튜토리얼을 진행해보도록 하자.

먼저, CPU환경에서 모델의 학습 및 추론에 소요되는 시간을 모니터링 해보도록 하자

import torch
import torchvision.models as models
from torch.profiler import profile, record_function, ProfilerActivity

 

torch와 profiler를 import한 후, 간단한 resnet모델을 load하여 프로파일링을 진행해보록 하겠다.

model = models.resnet18()
inputs = torch.randn(5,3,224,24)
with profile(activities=[ProfilerActivity.CPU], record_shapes=True) as prof:
    with record_function("model_inference_test"): 
        model(inputs)

 

PyTorch 프로파일러는 컨텍스트 관리자를 통해 활성화되며 활용가능한 다양한 parameter들이 있다.

가장 유용한 Parameter 몇가지에 대해서 정리해보자.


1) activities

'activities'은 파이토치 프로파일러에서 프로파일링할 활동의 종류를 지정하는데 사용되는 매개변수이다.
- ProfilerActivity.CPU

   : CPU 기반의 연산과 작업을 프로파일링 하겠다! 라는 의미로, CPU에서 수행되는 시간과 리소스 사용을 측정한다. CPU활동에는       

      tensor연산, 데이터 로딩, 전처리 등이 포함될 수 있다.

- ProfilerActivity.CUDA

   : CUDA를 사용하는 GPU기반의 연산을 프로파일링한다는 의미이고, CUDA에서 이뤄지는 작업은 대표적으로 tensor연산, 뉴럴넷의

     feed-forward 연산과 back-propagation등으로 해당 작업들의 리소스사용량과 속도를 모니터링 할 수 있다.


2) record_shapes

텐서 연산에 사용되는 input shape을 기록하여, 각 여난에서 처리되는 데이터 차원의 크기를 모니터링하고 성능문제의 원인을 좀 더 쉽게 분석할 수 있도록 한다.

 


3) profile_memory

메모리 프로파일링을 활성화하는 옵션으로 이를 사용하면 CPU 및 GPU에서 메모리할당과 해제가 기록된다. 메모리 사용량을 추적해서 메모리관련 성능 문제를 식별하는데 유용한 옵션이다.


4) use_cuda

CUDA를 사용하는 경우에만 관련이 있는 옵션으로, CUDA 기반의 GPU 작업 시간을 측정하기 위해 사용되며, GPU의 성능 분석에 중요한 정보를 모니터링 할 수 있는 옵션이다.


 

자, 다시 코드로 돌아와서 위 파라미터들을 직접 확인해보자!

with profile(activities=[ProfilerActivity.CPU], record_shapes=True) as prof:
# 위 코드는 프로파일러를 시작하는 컨텍스트 매니저이다. 이 안에서 실행되는 코드에 대해 성능을 기록하게 된다.
    with record_function("model_inference_test"): 
    #'model_inference_test'라는 이름으로 모델의 추론 테스트를 기록하며, 아래 코드에서 추론을 시작하게된다.
        model(inputs)

 

위 코드는 CPU활동을 기록하기 위한 프로파일러 사용을 시작하는 내용으로 'record_shapes=True'를 통해 model의 input shape 

또한 기록하는것으로 컨텍스트 매니저를 정의하고 실행하게 된다. (프로파일러는 컨텍스트 매니저('profile')를 통해 실행된다.)

 

그리고, 아래 코드를 통해 프로파일러에서 수집된 성능데이터를 요약하여 표 형태로 출력할 수 있다.

여기서 'prof'는 앞서 프로파일러 컨텍스트 매니저로 생성된 객체를 의미한다.

prof.key_averages().table(sort_by="cpu_time_total", row_limit=10)

# key_averages(): 프로파일링된 데이터의 평균값을 계산한다. 각 종류의 연산이 얼마나 자주 발생했는지, 그리고 평균적으로 얼마나 많은 시간을 차지했는지를 보여준다.
# table(...): 계산된 평균값을 테이블 형식으로 출력한다. 이 테이블은 연산의 종류, 평균 실행 시간, 호출 횟수 등 다양한 성능 메트릭을 포함할 수 있다.

출력결과

 

예상한대로 대부분의 시간이 컨볼루션에서 소비되는것을 확인할 수 있다. 

위 표에서는 'Self CPU'와 'CPU time'이 가장 중요한 정보로 확인해야한다.

 

여기서, 보다 세부적인 결과와 함께 input shape을 함께 포함하여 출력하려면 아래와 같이 'group_by_input_shape=True'를 전달하여 출력하면 된다.

print(prof.key_averages(group_by_input_shape=True).table(sort_by='cpu_time_total', row_limit=10))

출력결과

 

화면과 같이 우측에 input shapes가 상세히 포함되어 출력되고 있다.

 

이번에는 GPU 환경에서의 학습 및 추론에 소요되는 시간을 모니터링 해보자.

device = 'cuda' if torch.cuda.is_available() else 'cpu'

with profile(activities=[
    ProfilerActivity.CPU, ProfilerActivity.CUDA], record_shapes=True) as prof:
    with record_function('model_inference_GPU_TEST'):
        model(inputs)
        
print(prof.key_averages().table(sort_by='cuda_time_total', row_limit=10))

위와 같이 컨텍스트 매니저(profile로 선언된 부분)에서 'ProfilerActivity.CUDA'를 통해 GPU 리소스에 대한 모니터링을 진행할 수 있도록 하고, 마찬가지로 input shape을 출력하게끔 정의한 후 'model_inference_GPU_TEST'라는 이름으로 모델의 추론 테스트를 기록하도록 했다.

출력결과

 

출력결과의 오른쪽에서 CUDA기반의 GPU에 대한 소요시간을 확인할 수 있다.

 

 

Pytorch Profiler는 소요시간뿐 아니라, CPU와 GPU환경에서의 메모리 소비에 대한 모니터링도 제공한다.

아래와 같이, 컨텍스트 매니저에서 간단하게 'profile_memory=True'옵션을 통해 메모리 사용량에 대한 정보를 출력할 수 있다.

inputs = torch.randn(1,32)
model = MLPmodel(inputs.size(1), inputs.size(1))

with profile(activities=[ProfilerActivity.CPU],
            profile_memory=True, record_shapes=True) as prof:
    model(inputs)

print(prof.key_averages().table(sort_by='self_cpu_memory_usage', row_limit=10))

출력결과

 

 

 

위와 같이 프로파일러는 하드웨어 리소스에 대한 상세한 정보를 제공하지만, 매번 일시적인 출력으로만 모니터링을 진행할 수 없다.

당연하게도 Pytorch 프로파일러는 해당 내용을 json 형태의 파일로 적재 또한 가능하다.

 

model = models.resnet18().to(device)
inputs = torch.randn(5, 3, 224, 224).to(device)

with profile(activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA]) as prof:
    model(inputs)
    
prof.export_chrome_trace('/home/jovyan/WIDE_HOME/wontae_kim/Research/trace.json')

위 export_chrome_trace를 통해 모니터링 데이터를 적재할 수 있으며, 

"chrome://tracing/" 을 통해 log화 된 데이터를 시각화로 확인할 수 있다.

 

 

 

 

마치며

오늘은 Pytorch에서 제공되는 Profiler 라이브러리를 사용해 간단한 ResNet모델의 추론과정을 모니터링하는 튜토리얼을 진행해보았다. 간단한 프로파일링만으로 상당히 유용한 정보를 많이 제공하고있는데, 사실 파이토치에서는 TensorBoard에서 프로파일러를 통해 기록한 내용을 좀 더 상세하게 모니터링 할 수 있다.

다음에는 TensorBoard를 통해 모델을 모니터링 하고 분석하여 인사이트를 얻는과정에 대해서 좀 더 상세하게 리뷰할 수 있도록 해보자.

 

 

 

Reference

https://pytorch.org/tutorials/recipes/recipes/profiler_recipe.html

 

PyTorch Profiler — PyTorch Tutorials 2.2.0+cu121 documentation

Note Click here to download the full example code PyTorch Profiler This recipe explains how to use PyTorch profiler and measure the time and memory consumption of the model’s operators. Introduction PyTorch includes a simple profiler API that is useful w

pytorch.org