Resumen de la memoria GPU y la utilización del aprendizaje profundo

Creo que mucha gente, incluyéndome a mí, tenemos muchas quejas sobre la memoria de la GPU, nos han estado molestando problemas como CUDA sin memoria, el artículo de hoy es para analizarlo, tal vez te sea útil.

En primer lugar , hablemos brevemente sobre la importancia de la GPU.Tomando Pytorch como ejemplo, utiliza CUDA y cuDNN para convertir varios cálculos realizados durante la inferencia del modelo de aprendizaje profundo en multiplicación de matrices para acelerar, a fin de lograr la operación desde el año de el mono al presente Diez a cien veces más rápido.
En cuanto a la memoria de video que amamos y odiamos, el cambio de la memoria de ejecución de la GPU cuando se leen los datos se usa como referencia. El mecanismo de implementación específico generalmente es a través del proceso de trabajo + cola, lo que permite que varios trabajadores lean y preprocesen los datos de forma asincrónica. , y luego el supervisor entrena el proceso principal para obtener datos del otro extremo de la cola. Si la cola está llena y el proceso de entrenamiento no tiene tiempo para obtener datos, el proceso de trabajo se bloqueará y la memoria en ejecución no crecerá sin límite.

torch.utils.data.DataLoader(datasets[x], batch_size=batch_size, shuffle=True, num_workers=8, pin_memory=True)

Nota: Las operaciones de cálculo y procesamiento de datos no relacionadas, como la impresión de registros y el cálculo de eta, también consumirán mucho tiempo de procesamiento de la CPU.
Hablemos brevemente sobre la memoria de video de control de tamaño de lote (no completamente proporcional, los parámetros del modelo en sí y los datos extendidos también ocupan la memoria de video), la utilización de GPU de control de num_workers (para que los datos de carga de CPU y la velocidad de procesamiento de GPU puedan coincidir, El sistema operativo de Lenovo se puede entender), se mencionarán los dos últimos

Obviamente, la carga de datos, el preprocesamiento, el posprocesamiento, etc. se colocan en la CPU, es decir, otras tareas de IO para lectura y escritura. Y es una buena manera de ajustar la utilización de GPU ajustando el número de num_workers. Es mejor establecer la cantidad en un rango relativamente grande (se puede considerar de 4 a 8), pero no cuanto mayor sea, mejor. Porque cuanto más grande es, aunque hay más subprocesos, el consumo de cada subproceso también es grande, por lo que aumentará la carga en la CPU, lo que reducirá la utilización de la GPU. El número de num_workers generalmente se usa junto con el número de batch_size.
Además , cuando su servidor o computadora tiene mucha memoria y buen rendimiento, se recomienda activar pin_memory , lo que ahorra la necesidad de transferir datos de la CPU a la caché RAM y luego transferirlos a la GPU; para When True, se asigna directamente al bloque de memoria correspondiente de la GPU, lo que ahorra un poco de tiempo de transmisión de datos.
Tenga en cuenta aquí que al cargar datos, se recomienda cargar todos los datos en el __init__ del conjunto de datos o en la canalización llamando al método de importación de datos en lugar de escribir en el método getitem.

Por lo tanto, para esto, un método aproximado es adoptar el modo ddp (no demasiados subprocesos) cuando hay múltiples GPU, y usar el método de memoria compartida para permitir que el proceso de rank0 arroje estos datos de anotación en la memoria compartida, y luego todos otro El proceso es de solo lectura y mapea esta memoria, realizando copia cero.

Si la tasa de utilización de la GPU es baja , lo primero es aumentar el tamaño del lote, aumentar el uso de la memoria de la GPU e intentar agotar la memoria en lugar de dejar la mitad. La memoria vacía es utilizada por otros programas, y la eficiencia de ambas tareas será muy baja. En segundo lugar, al cargar datos, establezca el número de subprocesos num_workers un poco más grandes, se recomiendan 8, 16, etc., y habilite pin_memory=True. No coloque toda la tarea en el proceso principal, ya que esto consume CPU y la velocidad y el rendimiento son extremadamente bajos .

En segundo lugar , considere la influencia del modelo en sí, es decir, los parámetros del modelo y la respuesta de cada capa, que están relacionados con la complejidad del modelo.Tomando Resnet50 como ejemplo, hay 26 millones de parámetros.Si la precisión de punto flotante de 32 bits se utiliza, entonces la memoria ocupada es: 26M * 32 bits = 99MB . Al mismo tiempo, tiene 16 millones de respuestas, ocupando memoria: 16M*32bit = 64MB .
La respuesta no está directamente relacionada con el número de parámetros del modelo. Las capas convolucionales pueden tener respuestas muy grandes pero pocos parámetros; las capas de activación pueden incluso no tener parámetros.

Tal vez sientas que en este punto es solo un poco más de 100 millones y que el impacto no es muy grande. No, este es el momento para que debute el tamaño del lote.

Para utilizar eficazmente el mecanismo SIMD de la GPU, los datos deben ingresarse en la red en forma de minilotes.
Si desea completar la ruta común de 1024 bits con números de coma flotante de 32 bits, necesita calcular 32 muestras al mismo tiempo.
Cuando se usa el mini lote, solo se guarda una copia de los parámetros del modelo , pero las respuestas de cada capa deben duplicarse según el tamaño del mini lote.

ResNet con 50 capas, mini lote = 32, cada respuesta de capa ocupa memoria: 64 MB * 32 = 2 GB

Sin embargo , 2G no es el final. En la biblioteca de aprendizaje profundo, el método de reducción generalmente se usa para convertir el cálculo de convolución en multiplicación de matriz **. Al calcular este tipo de convolución, la respuesta de la capa frontal X XX debe ampliarse K^2 veces.

ResNet de 50 capas, al considerar el efecto de reducción, la respuesta de cada capa ocupa 7,5 GB de memoria

Otra cosa a la que debe prestar atención es cuál es el estándar para el final de su capacitación en red. ¿Se basa en la época o la iteración? Si la iteración se usa como estándar, entonces si aumenta el tamaño del lote, el tiempo de entrenamiento de la red aumentará naturalmente. En este momento, la iteración debe descontarse a la mitad cuando el tamaño del lote se duplique. Si la época se usa como estándar, no se requieren otros cambios al cambiar el tamaño del lote.

Uso de memoria de video ≈ uso de memoria de video modelo + tamaño_lote × uso de memoria de video de cada muestra, se puede ver de una manera tan simple

ResNet50 se usó como ejemplo anteriormente, y el siguiente reemplazo con GPT-2 puede estar más cerca de la estructura de red actual.

introducción

Para un modelo GPT-2 con 1.5B de parámetros, se requieren 3 GB de memoria para almacenar sus pesos (o parámetros) con una precisión de 16 bits, pero no se puede entrenar en una GPU con 32 GB de memoria de video. Durante el entrenamiento del modelo, la mayor parte de la sobrecarga de memoria se usa para los estados del modelo , como los estados del optimizador, los gradientes y los parámetros. Además del estado del modelo, la sobrecarga de memoria restante proviene de estados residuales , como los valores de activación, la memoria caché del área de ensayo y la fragmentación de la memoria.

Estados modelo

Tomando a Adam como ejemplo, necesita almacenar dos partes del estado del optimizador : el impulso promediado en el tiempo y la varianza de los gradientes. Por lo tanto, cuando se utiliza Adam para el entrenamiento del modelo, debe haber suficiente espacio en la memoria para almacenar la estimación del impulso y el valor de copia de la varianza del gradiente. Además, debe haber suficiente espacio para almacenar el gradiente y el peso del propio modelo . El estado del optimizador suele ocupar una gran parte de la memoria, especialmente en el entrenamiento de precisión mixta.

En el entrenamiento de precisión mixta (fp16/32), los parámetros del modelo y los valores de activación se guardan en formato fp16, y los pesos y valores de activación de fp16 también se utilizan para el cálculo en la propagación hacia adelante y hacia atrás. Sin embargo, para calcular de manera más eficiente y garantizar la corrección de las actualizaciones de gradiente (se producirán errores de redondeo en el entrenamiento de precisión mixta), generalmente se copiará una copia de los pesos fp32 y el estado del optimizador al mismo tiempo.

Ejemplo: para un modelo con un tamaño de parámetro de γ, use Adam y precisión mixta para el entrenamiento. En primer lugar, requiere 2 γ de memoria para almacenar los parámetros y pesos copiados por fp16, y 4 γ de memoria para almacenar los parámetros, la estimación de impulso y la varianza de gradiente de las copias de fp32, respectivamente. K se usa aquí para representar el múltiplo de sobrecarga de memoria adicional requerido para almacenar el estado del optimizador, y K = 12 para Adam de precisión mixta. En resumen, la sobrecarga de memoria requerida para entrenar dicho modelo es 2γ + 2γ + Kγ = 16γ. Para un modelo GPT-2 con un tamaño de parámetro de 1,5 B, el consumo de memoria de entrenamiento requerido es de al menos 24 GB, que es mucho mayor que el tamaño de parámetro de 3 GB del propio modelo en formato fp16.

Estados residuales

Activación

Los valores de activación pueden ocupar una gran parte de la memoria durante el entrenamiento. La memoria ocupada por el valor de activación del modelo de estructura del transformador es proporcional al tamaño de transformers_layers * hidden_dimensions * sequence_length * batch_size Para el modelo GPT-2, la memoria del valor de activación es 12 * hidden_dim * bsz * seq_length * transformer_layers . Por lo tanto, para un modelo GPT-2 con 1.5B de parámetros, longitud de secuencia de 1K y tamaño de lote de 32, la memoria requerida es de 60 GB.

Aunque la tecnología de puntos de control de activación puede reducir la memoria ocupada por el valor de activación en tiempos de raíz aproximadamente a costa de volver a calcular el valor de activación en un 33 % (esto puede reducir la memoria del valor de activación requerida por el modelo anterior a aproximadamente 8 GB), para el modelo, el efecto sigue siendo limitado (el modelo GPT con parámetros 100B todavía necesita 60G de memoria sobre la base de puntos de control de activación).

Búferes temporales

Caché de área temporal para almacenar resultados de cálculos intermedios, como reducción total de gradientes o cálculo de norma de gradiente (cálculo de norma de gradiente) que fusiona todos los resultados de gradiente en un solo búfer aplanado antes de reducción total para mejorar el rendimiento. Para un modelo con parámetros de 1.5B, el uso de memoria de búfer aplanado en fp32 alcanzará los 6 GB.

Fragmentación de memoria

Incluso si el tamaño de la memoria es mayor que la sobrecarga de memoria requerida para el entrenamiento del modelo en teoría, debido a la existencia de fragmentación de la memoria (la memoria libre aparece en diferentes ubicaciones de manera discontinua, lo que da como resultado que no haya memoria continua para satisfacer las necesidades del entrenamiento del modelo) , todavía puede haber un error de memoria insuficiente. Al entrenar un modelo súper grande, en algunos casos extremos, habrá un problema OOM (memoria insuficiente) cuando todavía haya un 30 % de memoria restante.

Una forma sencilla de usar la memoria de video en la hoja

1. Para usar SIMD de manera efectiva, si se duplica la precisión, se debe duplicar el tamaño del lote. No se puede reducir el consumo de memoria. Pero puede usar la aceleración de precisión mixta del ápice de Nvidia, y la memoria pytorch se reduce directamente a la mitad .
2. Operación en el lugar : reescribe directamente la respuesta original sin abrir nueva memoria. Muchas funciones de activación pueden hacer esto. Como nn.ReLU(inplace=True), es decir, use el indicador de operación en el lugar tanto como sea posible .
Es un poco más complicado Al analizar todo el gráfico de la red, puede encontrar respuestas que solo deben usarse una vez, que pueden compartir memoria con respuestas posteriores. Por ejemplo, el mecanismo de uso compartido de memoria de MxNet .
3. Cálculo para almacenamiento : descubra los resultados de respuesta que son fáciles de calcular (como la salida de la capa de función de activación) en lugar de almacenamiento, y calcule temporalmente cuando sea necesario. Con este enfoque, el ejemplo de MxNet pudo reducir el consumo de memoria de una red ResNet de 50 capas en un factor de cuatro.

También echa un vistazo a las más de 10 formas de hacer trucos aquí

Finalmente, si puede optimizar la operación del algoritmo y optimizar el uso de la memoria por tamaño de lote en la estructura del gráfico de red, naturalmente puede instalar más tamaños de lote para aprovechar al máximo el núcleo de la GPU.

referencia

  1. https://www.zhihu.com/question/476086761
  2. https://blog.csdn.net/shenxiaolu1984/article/details/71522141
  3. https://zhuanlan.zhihu.com/p/362556069
  4. https://blog.csdn.net/qq_45756171/article/details/122910838
  5. https://zhuanlan.zhihu.com/p/348122818
  6. https://blog.csdn.net/qq_34405401/article/details/108519823
  7. https://zhuanlan.zhihu.com/p/520898642
  8. https://zhuanlan.zhihu.com/p/31558973
  9. https://www.cnblogs.com/jiangkejie/p/10098995.html

Supongo que te gusta

Origin blog.csdn.net/weixin_42455006/article/details/127653740
Recomendado
Clasificación