Comment calculer l'impact de la mémoire mini-batch lors de la formation de modèles d'apprentissage en profondeur?


17

J'essaie de calculer la quantité de mémoire nécessaire à un GPU pour entraîner mon modèle sur la base de ces notes d'Andrej Karphaty: http://cs231n.github.io/convolutional-networks/#computational-considerations

Mon réseau a 532 752 activations et 19 072 984 paramètres (poids et biais). Ce sont toutes des valeurs flottantes de 32 bits, donc chacune prend 4 octets en mémoire.

Mon image d'entrée est 180x50x1 (largeur x hauteur x profondeur) = 9 000 valeurs flottantes 32. Je n'utilise pas d'augmentation d'image, donc je pense que la mémoire diverse ne serait liée qu'à la taille du mini-lot. J'utilise une taille de mini-lot de 128 images.

Sur la base de la recommandation d'Andrej, j'obtiens les tailles de mémoire suivantes:

Activations: 532 752 * 4 / (1024 ^ 2) = 2,03 Mo

Paramètres: 19,072,984 * 4 / (1024 ^ 2) * 3 = 218,27 MB

Divers: 128 * 9,000 * 4 / (1024 ^ 2) = 4,39 Mo

Ainsi , la mémoire totale pour former ce réseau serait 224,69 MB .

J'utilise TensorFlow et je pense qu'il me manque quelque chose. Je n'ai pas encore exécuté la formation, mais je suis assez sûr (sur la base des expériences passées) que la mémoire utilisée sera beaucoup plus élevée que ce que j'ai calculé.

Si pour chaque image du mini-lot, TensorFlow conserve leurs dégradés afin de pouvoir les normaliser plus tard pour une seule étape de mise à jour des poids / biais, je pense que la mémoire devrait prendre en compte 532 752 * 128 valeurs supplémentaires (dégradés pour chaque image dans le mini-lot). Si tel est le cas, il me faudrait plus de 260,13 Mo pour entraîner ce modèle avec 128 images / mini-lot.

Pouvez-vous m'aider à comprendre les considérations de mémoire pour la formation de mon modèle d'apprentissage en profondeur? Les considérations ci-dessus sont-elles justes?


Veuillez voir ma réponse (proposée) à votre question ici .
Adam Hendry

Réponses:


5

Je pense que vous êtes sur la bonne voie.

Oui, vous devrez stocker les dérivées des activations et des paramètres de rétropropagation.

De plus, votre choix d'optimisation peut être important. Vous vous entraînez avec SGD, Adam ou Adagrad? Ceux-ci auront tous des besoins en mémoire différents. Par exemple, vous devrez stocker le cache de taille d'étape pour une méthode basée sur l'élan, bien que cela devrait être secondaire par rapport aux autres considérations de mémoire que vous mentionnez.

Donc, dans l'ensemble, vous semblez avoir calculé les besoins en mémoire pour une passe avant. Andrej Karpathy mentionne que la passe en arrière pourrait occuper jusqu'à 3 fois la mémoire de la passe en avant, donc cela pourrait être la raison pour laquelle vous voyez une telle différence (faites défiler jusqu'à `` Études de cas '' sur le site Web pour voir un exemple pour VGGNet).


5

@StatsSorceress TL; DR:

Je passe par cette activité pour voir si je peux calculer moi-même la mémoire requise:

Activations: 532 752 * 2 * 4 / (1024 ^ 2) = 4,06 Mo

Paramètres: 19,072,984 * 4 / (1024 ^ 2) * 3 = 218,27 MB

Divers: 128 * 9,000 * 4 / (1024 ^ 2) = 4,39 Mo

Mémoire totale: (4,06 * 128 ) + 218,27 + 4,39 = 742,34 Mo

( Quelqu'un, s'il vous plaît, corrigez-moi si je me trompe. Pour info, vous avez déjà multiplié divers par 128, c'est pourquoi je ne l'ai pas multiplié par 128 ci-dessus )


Je voudrais vous signaler cet article et la vidéo correspondante . Ils m'ont aidé à mieux comprendre ce qui se passe.

REMARQUE: la mémoire requise pour utiliser un réseau pour les prévisions est bien inférieure à celle requise pour la formation pour deux raisons:

  • Lors de la prédiction, nous envoyons uniquement une image vers l'avant via le réseau et non vers l'arrière (nous ne multiplions donc pas la mémoire X 3; voir ci-dessous)
  • Il y a une prédiction par image (nous n'avons donc pas besoin de multiplier la mémoire requise pour une image par une taille de lot car nous n'utilisons pas de lots dans la prédiction).

Processus (mémoire à former)

  1. Calculez la mémoire requise pour vous entraîner sur une image
  2. Multipliez ce nombre par le nombre d'images de votre lot

( RAPPELEZ - VOUS: le mini-batch indique que nous prenons un sous-ensemble de nos données, calculons les gradients et les erreurs pour chaque image du sous-ensemble, puis faisons la moyenne de ceux-ci et avançons dans le sens de la moyenne. Pour les convnets, les poids et les biais sont partagés, mais le nombre d'activations est multiplié par le nombre d'images dans le lot. ).

ÉTAPE 1: Mémoire pour 1 image

Pour former une image, vous devez réserver de la mémoire pour:

  • Paramètres du modèle:

    Les poids et les biais à chaque couche, leurs gradients et leurs variables de moment (si Adam, Adagrad, RMSProp, etc., des optimiseurs sont utilisés)

    Pour approximer la mémoire pour cela, calculez la mémoire requise pour stocker les poids et les biais et multipliez cela par 3 (c.-à-d. "Par 3" parce que nous disons que la quantité de mémoire nécessaire pour stocker les poids et les biais est (à peu près) égale à nécessaire pour les gradients et pour les variables de momentum)

    ÉQUATIONS:

    Convolutions:

    poids (n) = profondeur (n) * (kernel_width * kernel_height) * depth (n-1)

    biais (n) = profondeur (n)

    Couches entièrement connectées (denses):

    poids (n) = sorties (n) * entrées (n)

    biais (n) = extrants (n)

n est la couche actuelle et n-1 est la couche précédente, et les sorties sont le nombre de sorties de la couche FC et les entrées sont le nombre d'entrées de la couche FC (si la couche précédente n'est pas une couche entièrement connectée, le nombre d'entrées est égal à la taille de cette couche aplatie).

REMARQUE: La mémoire pour les poids et les biais seuls, plus la mémoire pour les activations pour une image (voir ci-dessous), est la quantité totale de mémoire dont vous avez besoin pour les prédictions (à l'exclusion de certains frais généraux pour la mémoire pour les circonvolutions et d'autres choses).

  • Activations (ce sont des "Blobs" dans Caffe):

(J'utilise des termes librement ici, supporte-moi)

Chaque convolution dans une couche de convolution produit des activations " nombre de pixels dans l'image " (c'est-à-dire que vous passez une image à travers une seule convolution, vous obtenez une seule carte d'entités composée d' activations " m ", où " m " est le nombre de pixels de votre image / entrée).

Pour les couches entièrement connectées, le nombre d'activations que vous produisez est égal à la taille de votre sortie.

Convolutions:

activations (n) = image_width * image_height * image_num_channels

Couches entièrement connectées (denses):

activations (n) = sorties (n)

Notez que votre entrée n'est vraiment qu'une image au début du réseau. Après les circonvolutions, il se transforme en autre chose (cartes de caractéristiques). Remplacez donc vraiment "image_width", "image_height" et "image_num_channels" par "input_width", "input_height" et "layer_depth" pour être plus précis. (C'est plus facile pour moi de penser à ce concept en termes d'images.)

Puisque nous devons également stocker l'erreur pour les activations à chaque couche (utilisée dans la passe arrière), nous multiplions le nombre d'activations par 2 pour obtenir le nombre total d'entités dont nous avons besoin pour faire de la place dans notre espace de stockage. Le nombre d'activations augmente avec le nombre d'images dans le lot, vous multipliez donc ce nombre par la taille du lot.

ÉTAPE 2: Mémoire pour former le lot

Additionnez le nombre de poids et de biais (fois 3) et le nombre d'activations (fois 2 fois la taille du lot). Multipliez cela par 4, et vous obtenez le nombre d'octets requis pour former le lot. Vous pouvez diviser par 1024 ^ 2 pour obtenir la réponse en Go.


Pourquoi dites-vous "nous n'utilisons pas de lots dans la prédiction"? Si un utilisateur doit faire des prédictions sur un grand nombre d'images, il peut être judicieux d'utiliser des lots dans les prédictions.
user3731622

1

Alternativement, je pense que vous pouvez utiliser n'importe quelle bibliothèque de profileur pour analyser la mémoire et l'utilisation du processeur par votre programme. Il existe de nombreuses bibliothèques python qui peuvent vous donner un aperçu de l'utilisation de la mémoire et du processeur par thread ou processus particulier à un intervalle de millisecondes.

Vous pouvez exécuter la partie de votre programme que vous souhaitez surveiller dans un sous-processus différent en utilisant popen et surveiller sa mémoire et l'utilisation du processeur en utilisant son PID.

psutil je trouve bon pour un tel travail. Bien qu'il y en ait beaucoup d'autres.

J'espère que cela vous aidera.


3
Merci pour la réponse, @Anwar. Je recherche un calcul analytique plutôt qu'une observation empirique.
barbolo
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.