Accelerate documentation

Profiler

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v1.15.0).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Profiler

Profiler es una herramienta que permite recopilar métricas de rendimiento durante el entrenamiento y la inferencia. La API de administrador de contexto de Profiler se puede utilizar para comprender mejor qué operadores del modelo son los más costosos, examinar sus formas de entrada y trazas de pila, estudiar la actividad de los kernels del dispositivo y visualizar la traza de ejecución. Proporciona información sobre el rendimiento de su modelo, lo que le permite optimizarlo y mejorarlo.

Esta guía explica cómo usar PyTorch Profiler para medir el tiempo y el consumo de memoria de los operadores del modelo y cómo integrar esto con Accelerate. Abordaremos varios casos de uso y proporcionaremos ejemplos para cada uno.

Usar el perfilador para analizar el tiempo de ejecución

Profiler permite verificar qué operadores se llamaron durante la ejecución de un rango de código envuelto con un administrador de contexto de profiler.

Veamos cómo podemos usar el profiler para analizar el tiempo de ejecución:

PyTorch
Accelerate
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))

La salida de la tabla resultante (omitimos algunas columnas):

---------------------------------  ------------  ------------  ------------  ------------  
                             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

Para obtener una granularidad más fina de los resultados e incluir las formas de entrada de los operadores, pase group_by_input_shape=True (nota: esto requiere ejecutar el perfilador con record_shapes=True):

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

Usar el perfilador para analizar el consumo de memoria

Profiler también puede mostrar la cantidad de memoria (usada por los tensores del modelo) que se asignó (o liberó) durante la ejecución de los operadores del modelo. Para habilitar la funcionalidad de perfilado de memoria, pase profile_memory=True.

PyTorch
Accelerate
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))

La salida de la tabla resultante (omitimos algunas columnas):

---------------------------------  ------------  ------------  ------------  
                             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

Si necesita uno o dos números agregados para el seguimiento de experimentos en lugar de una tabla completa de operadores, combine el perfilador con las estadísticas de memoria en tiempo de ejecución expuestas por PyTorch. Esto suele ser suficiente para registrar el uso de memoria pico por ejecución:

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())

Este patrón mantiene la salida del perfilador disponible para depuración y, al mismo tiempo, produce valores compactos que pueden registrarse en un rastreador de experimentos.

Exportar la traza de Chrome

Puede examinar la secuencia de operadores y kernels de CUDA perfiles en el visor de trazas de Chrome (chrome://tracing):

profile_export

PyTorch
Accelerate
# 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")

Uso de Profiler para analizar trabajos de larga duración

Profiler ofrece una API adicional para manejar trabajos de larga duración (como bucles de entrenamiento). Rastrear toda la ejecución puede ser lento y dar lugar a archivos de trazas muy grandes. Para evitarlo, utiliza los argumentos opcionales:

  • schedule_option: Las opciones de programación permiten controlar cuándo está activo el perfilado. Esto es útil para trabajos de larga duración para evitar recopilar demasiados datos. Las claves disponibles son wait, warmup, active, repeat y skip_first. El perfilador omitirá los primeros skip_first pasos, luego esperará wait pasos, luego realizará el calentamiento durante los siguientes warmup pasos, luego realizará la grabación activa durante los siguientes active pasos y luego repetirá el ciclo comenzando con wait pasos. El número opcional de ciclos se especifica con el parámetro repeat; el valor cero significa que los ciclos continuarán hasta que finalice el perfilado.
  • on_trace_ready: especifica una función que toma una referencia al perfilador como entrada y es llamada por el perfilador cada vez que la nueva traza está lista.

Para ilustrar cómo funciona la API, considere el siguiente ejemplo:

PyTorch
Accelerate
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()

FLOPS

Use formula para estimar los FLOPs (operaciones de coma flotante) de operadores específicos (multiplicación de matrices y convolución 2D).

Para medir las operaciones de punto flotante (FLOPS):

PyTorch
Accelerate
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))

La salida de la tabla resultante (omitimos algunas columnas):

-------------------------------------------------------  ------------  ------------  ------------  
                                                   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

Conclusión e información adicional

PyTorch Profiler es una herramienta potente para analizar el rendimiento de tus modelos. Al integrarlo con Accelerate, puedes perfilear tus modelos con facilidad y obtener información sobre su rendimiento, lo que te ayuda a optimizarlos y mejorarlos.

Para obtener información más detallada, consulte la documentación del Profiler de PyTorch.

Update on GitHub