Accelerate documentation
Profiler
Profiler
Profiler는 학습 및 추론 중에 성능 메트릭을 수집할 수 있는 도구입니다. Profiler의 컨텍스트 매니저 API를 사용하면 가장 비용이 높은 모델 연산자를 파악하고, 해당 연산자의 입력 형식과 스택 트레이스를 검토하며, 디바이스 커널 활동을 분석하고, 실행 트레이스를 시각화할 수 있습니다. 이는 모델의 성능에 대한 통찰력을 제공하여 최적화 및 개선이 가능하도록 합니다.
이 가이드에서는 PyTorch Profiler를 사용하여 모델의 연산자 시간 및 메모리 사용량을 측정하는 방법과 이를 Accelerate에 통합하는 방법을 설명합니다. 다양한 사용 사례를 다루고 각 사례에 대한 예제를 제공합니다.
프로파일러를 사용하여 실행 시간 분석
Profiler를 사용하면 프로파일러 컨텍스트 매니저로 감싸진 코드 구간의 실행 중에 어떤 연산자가 호출되었는지 확인할 수 있습니다.
profiler를 사용하여 실행 시간을 분석하는 방법을 살펴보겠습니다:
import torch
import torchvision.models as models
from torch.profiler import profile, record_function, ProfilerActivity
model = models.resnet18()
inputs = torch.randn(5, 3, 224, 224)
with profile(activities=[ProfilerActivity.CPU], record_shapes=True) as prof:
model(inputs)
print(prof.key_averages().table(sort_by="cpu_time_total", row_limit=10))from accelerate import Accelerator, ProfileKwargs
import torch
import torchvision.models as models
model = models.resnet18()
inputs = torch.randn(5, 3, 224, 224)
profile_kwargs = ProfileKwargs(
activities=["cpu"],
record_shapes=True
)
accelerator = Accelerator(cpu=True, kwargs_handlers=[profile_kwargs])
model = accelerator.prepare(model)
with accelerator.profile() as prof:
with torch.no_grad():
model(inputs)
print(prof.key_averages().table(sort_by="cpu_time_total", row_limit=10))생성된 테이블 출력(일부 열 생략):
--------------------------------- ------------ ------------ ------------ ------------
Name Self CPU CPU total CPU time avg # of Calls
--------------------------------- ------------ ------------ ------------ ------------
aten::conv2d 171.000us 52.260ms 2.613ms 20
aten::convolution 227.000us 52.089ms 2.604ms 20
aten::_convolution 270.000us 51.862ms 2.593ms 20
aten::mkldnn_convolution 51.273ms 51.592ms 2.580ms 20
aten::batch_norm 118.000us 7.059ms 352.950us 20
aten::_batch_norm_impl_index 315.000us 6.941ms 347.050us 20
aten::native_batch_norm 6.305ms 6.599ms 329.950us 20
aten::max_pool2d 40.000us 4.008ms 4.008ms 1
aten::max_pool2d_with_indices 3.968ms 3.968ms 3.968ms 1
aten::add_ 780.000us 780.000us 27.857us 28
--------------------------------- ------------ ------------ ------------ ------------
Self CPU time total: 67.016ms더 세밀한 결과의 세분화 수준을 얻고 연산자 입력 형상을 포함하려면 group_by_input_shape=True를 전달하세요(참고: 이 옵션은 프로파일러를 record_shapes=True로 실행해야 합니다):
print(prof.key_averages(group_by_input_shape=True).table(sort_by="cpu_time_total", row_limit=10))프로파일러를 사용하여 메모리 사용량 분석
Profiler는 모델의 연산자 실행 중에 할당(또는 해제)된 메모리 양(모델의 텐서가 사용하는 메모리)도 표시할 수 있습니다. 메모리 프로파일링 기능을 활성화하려면 profile_memory=True를 전달하세요.
model = models.resnet18()
inputs = torch.randn(5, 3, 224, 224)
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))model = models.resnet18()
inputs = torch.randn(5, 3, 224, 224)
profile_kwargs = ProfileKwargs(
activities=["cpu"],
profile_memory=True,
record_shapes=True
)
accelerator = Accelerator(cpu=True, kwargs_handlers=[profile_kwargs])
model = accelerator.prepare(model)
with accelerator.profile() as prof:
model(inputs)
print(prof.key_averages().table(sort_by="self_cpu_memory_usage", row_limit=10))생성된 테이블 출력(일부 열 생략):
--------------------------------- ------------ ------------ ------------
Name CPU Mem Self CPU Mem # of Calls
--------------------------------- ------------ ------------ ------------
aten::empty 94.85 Mb 94.85 Mb 205
aten::max_pool2d_with_indices 11.48 Mb 11.48 Mb 1
aten::addmm 19.53 Kb 19.53 Kb 1
aten::mean 10.00 Kb 10.00 Kb 1
aten::empty_strided 492 b 492 b 5
aten::cat 240 b 240 b 6
aten::abs 480 b 240 b 4
aten::masked_select 120 b 112 b 1
aten::ne 61 b 53 b 3
aten::eq 30 b 30 b 1
--------------------------------- ------------ ------------ ------------
Self CPU time total: 69.332ms전체 연산자 테이블 대신 실험 추적에 필요한 집계 숫자 1~2개만 필요하시다면, PyTorch가 노출하는 런타임 메모리 통계와 프로파일러를 조합하세요. 이는 실행당 피크 메모리 사용량을 기록하는 데 일반적으로 충분합니다:
import torch
from accelerate import Accelerator, PartialState, ProfileKwargs
# Resolve the device type/module (cuda, xpu, ...) instead of hardcoding CUDA
device_type = PartialState().device.type
device_module = getattr(torch, device_type)
def summarize_memory_stats():
return {
"peak_allocated_mb": device_module.max_memory_allocated() / 1024**2,
"peak_reserved_mb": device_module.max_memory_reserved() / 1024**2,
}
profile_kwargs = ProfileKwargs(
activities=["cpu", device_type],
profile_memory=True,
record_shapes=True,
)
accelerator = Accelerator(kwargs_handlers=[profile_kwargs])
device_module.reset_peak_memory_stats()
with accelerator.profile() as prof:
model(inputs)
print(prof.key_averages().table(sort_by=f"self_{device_type}_memory_usage", row_limit=10))
print(summarize_memory_stats())이 패턴은 프로파일러 출력을 디버깅용으로 유지하면서 실험 트래커에 기록할 수 있는 간결한 값을 생성합니다.
Chrome trace 내보내기
Chrome trace viewer(chrome://tracing)에서 프로파일링된 연산자와 CUDA 커널의 순서를 확인할 수 있습니다:
# Pick the available accelerator (cuda, xpu, ...) instead of hardcoding CUDA
device = torch.accelerator.current_accelerator()
device_activity = getattr(ProfilerActivity, device.type.upper())
model = models.resnet18().to(device)
inputs = torch.randn(5, 3, 224, 224).to(device)
with profile(activities=[ProfilerActivity.CPU, device_activity]) as prof:
model(inputs)
prof.export_chrome_trace("trace.json")model = models.resnet18()
profile_kwargs = ProfileKwargs(
activities=["cpu", PartialState().device.type],
output_trace_dir="trace"
)
accelerator = Accelerator(kwargs_handlers=[profile_kwargs])
model = accelerator.prepare(model)
inputs = torch.randn(5, 3, 224, 224).to(accelerator.device)
with accelerator.profile() as prof:
model(inputs)
# The trace will be saved to the specified directoryProfiler를 사용하여 장기 실행 작업 분석
Profiler는 장시간 실행되는 작업(예: 학습 루프)을 처리하기 위한 추가 API를 제공합니다. 모든 실행을 추적하면 느려질 수 있으며 매우 큰 trace 파일이 생성될 수 있습니다. 이를 피하려면 선택적 인수를 사용하세요:
schedule_option: 스케줄링 옵션을 사용하면 프로파일링이 활성화되는 시점을 제어할 수 있습니다. 이는 데이터 수집량이 과도해지지 않도록 장시간 실행되는 작업에서 유용합니다. 사용 가능한 키는wait,warmup,active,repeat,skip_first입니다. 프로파일러는 처음skip_first스텝을 건너뛰고,wait스텝을 기다린 후, 다음warmup스텝 동안 워밍업을 수행하고, 다음active스텝 동안 활성 기록을 수행한 후,wait스텝으로 시작하여 사이클을 반복합니다. 선택적인 사이클 횟수는repeat파라미터로 지정하며, 0 값은 프로파일링이 완료될 때까지 사이클이 계속됨을 의미합니다.on_trace_ready: 프로파일러의 참조를 입력으로 받는 함수를 지정하며, 프로파일러가 새로운 trace가 준비될 때마다 이 함수를 호출합니다.
API가 어떻게 작동하는지 설명하기 위해 다음 예시를 살펴보세요:
from torch.profiler import schedule
my_schedule = schedule(
skip_first=1,
wait=5,
warmup=1,
active=3,
repeat=2
)
def trace_handler(p):
output = p.key_averages().table(sort_by=f"self_{device.type}_time_total", row_limit=10)
print(output)
p.export_chrome_trace("/tmp/trace_" + str(p.step_num) + ".json")
with profile(
activities=[ProfilerActivity.CPU, device_activity],
schedule=my_schedule,
on_trace_ready=trace_handler
) as p:
for idx in range(8):
model(inputs)
p.step()device_type = PartialState().device.type
def trace_handler(p):
output = p.key_averages().table(sort_by=f"self_{device_type}_time_total", row_limit=10)
print(output)
p.export_chrome_trace("/tmp/trace_" + str(p.step_num) + ".json")
profile_kwargs = ProfileKwargs(
activities=["cpu", device_type],
schedule_option={"wait": 5, "warmup": 1, "active": 3, "repeat": 2, "skip_first": 1},
on_trace_ready=trace_handler
)
accelerator = Accelerator(kwargs_handlers=[profile_kwargs])
model = accelerator.prepare(model)
with accelerator.profile() as prof:
for idx in range(8):
model(inputs)
prof.step()FLOPS
formula를 사용하여 특정 연산자(행렬 곱셈 및 2D 컨볼루션)의 FLOPs(부동소수점 연산)를 추정합니다.
부동소수점 연산(FLOPS)을 측정하려면:
with profile(
activities=[ProfilerActivity.CPU, device_activity],
with_flops=True
) as prof:
model(inputs)
print(prof.key_averages().table(sort_by="flops", row_limit=10))profile_kwargs = ProfileKwargs(
with_flops=True
)
accelerator = Accelerator(kwargs_handlers=[profile_kwargs])
with accelerator.profile() as prof:
model(inputs)
print(prof.key_averages().table(sort_by="flops", row_limit=10))생성된 테이블 출력(일부 열 생략):
------------------------------------------------------- ------------ ------------ ------------
Name Self CPU Self CUDA Total FLOPs
------------------------------------------------------- ------------ ------------ ------------
aten::conv2d 197.000us 0.000us 18135613440.000
aten::addmm 103.000us 17.000us 5120000.000
aten::mul 29.000us 2.000us 30.000
aten::convolution 409.000us 0.000us --
aten::_convolution 253.000us 0.000us --
aten::cudnn_convolution 5.465ms 2.970ms --
cudaEventRecord 138.000us 0.000us --
cudaStreamIsCapturing 43.000us 0.000us --
cudaStreamGetPriority 40.000us 0.000us --
cudaDeviceGetStreamPriorityRange 10.000us 0.000us --
------------------------------------------------------- ------------ ------------ ------------
Self CPU time total: 21.938ms
Self CUDA time total: 4.165ms결론 및 추가 정보
PyTorch Profiler은 모델의 성능을 분석하는 강력한 도구입니다. Accelerate와 통합하면 모델을 쉽게 프로파일링하여 성능에 대한 통찰력을 얻을 수 있으며, 이를 통해 모델을 최적화하고 개선하는 데 도움이 됩니다.
자세한 정보는 PyTorch Profiler 문서를 참조하세요.
Update on GitHub