Technique Jedi pour réduire les réseaux convolutifs - élagage
Avant vous est à nouveau la tâche de détecter des objets. La priorité est la rapidité de fonctionnement avec une précision acceptable. Vous prenez l’architecture YOLOv3 et la formez davantage. La précision (mAp75) est supérieure à 0.95. Mais le taux de réussite reste faible. Merde.
Aujourd'hui, nous contournerons la quantification. Et sous la coupe nous regarderons Taille du modèle — supprimer les parties redondantes du réseau pour accélérer l'inférence sans perte de précision. Il est clair où, combien et comment couper. Voyons comment procéder manuellement et où vous pouvez l'automatiser. A la fin il y a un dépôt sur keras.
introduction
Sur mon ancien lieu de travail, Macroscop à Perm, j'ai acquis une habitude : toujours surveiller le temps d'exécution des algorithmes. Et vérifiez toujours le temps d’exécution du réseau via un filtre d’adéquation. Habituellement, l'état de l'art en matière de production ne passe pas ce filtre, ce qui m'a conduit à l'élagage.
L'élagage est un vieux sujet qui a été abordé dans Conférences de Stanford en 2017. L'idée principale est de réduire la taille du réseau formé sans perdre en précision en supprimant divers nœuds. Cela a l'air sympa, mais j'entends rarement parler de son utilisation. Il n'y a probablement pas assez d'implémentations, il n'y a pas d'articles en russe, ou tout simplement tout le monde considère qu'il s'agit d'un savoir-faire d'élagage et reste silencieux.
Mais démontons-le
Un aperçu de la biologie
J'adore quand le Deep Learning examine des idées issues de la biologie. Comme l’évolution, on peut leur faire confiance (saviez-vous que ReLU est très similaire à fonction de l'activation des neurones dans le cerveau?)
Le processus de Model Pruning est également proche de la biologie. La réponse du réseau peut ici être comparée à la plasticité du cerveau. Il y a quelques exemples intéressants dans le livre. Normand Doidge:
Le cerveau d’une femme née avec seulement une moitié s’est reprogrammé pour remplir les fonctions de la moitié manquante.
Le gars a détruit la partie de son cerveau responsable de la vision. Au fil du temps, d’autres parties du cerveau ont pris en charge ces fonctions. (nous n'essayons pas de répéter)
De même, vous pouvez supprimer certaines des convolutions faibles de votre modèle. En dernier recours, les paquets restants permettront de remplacer ceux coupés.
Aimez-vous l’apprentissage par transfert ou apprenez-vous à partir de zéro ?
Option numéro un. Vous utilisez Transfer Learning sur Yolov3. Retina, Mask-RCNN ou U-Net. Mais la plupart du temps, nous n'avons pas besoin de reconnaître 80 classes d'objets comme dans COCO. Dans ma pratique, tout est limité aux niveaux 1 et 2. On pourrait supposer que l’architecture de 80 classes est ici redondante. Cela suggère que l’architecture doit être réduite. De plus, j'aimerais le faire sans perdre les poids pré-entraînés existants.
Option numéro deux. Peut-être disposez-vous de beaucoup de données et de ressources informatiques, ou avez-vous simplement besoin d'une architecture ultra-personnalisée. Cela n'a pas d'importance. Mais vous apprenez le réseau à partir de zéro. La procédure habituelle consiste à examiner la structure des données, à sélectionner une architecture EXCESSIVE en puissance et à repousser les abandons du recyclage. J'ai vu 0.6 abandon, Karl.
Dans les deux cas, le réseau peut être réduit. Motivé. Voyons maintenant quel type de taille de circoncision est
Algorithme général
Nous avons décidé de supprimer les paquets. Cela a l'air assez simple :
La suppression de toute convolution est stressante pour le réseau, ce qui entraîne généralement une augmentation des erreurs. D'une part, cette augmentation de l'erreur est un indicateur de la manière dont nous supprimons correctement les convolutions (par exemple, une augmentation importante indique que nous faisons quelque chose de mal). Mais une petite augmentation est tout à fait acceptable et est souvent éliminée par un léger entraînement supplémentaire ultérieur avec un petit LR. Ajoutez une étape de formation supplémentaire :
Nous devons maintenant déterminer quand nous voulons arrêter notre boucle Learning<->Pruning. Il peut y avoir des options exotiques ici lorsque nous devons réduire le réseau à une certaine taille et vitesse (par exemple, pour les appareils mobiles). Cependant, l’option la plus courante consiste à poursuivre le cycle jusqu’à ce que l’erreur devienne supérieure à l’acceptable. Ajoutez une condition :
Ainsi, l’algorithme devient clair. Reste à savoir comment déterminer les circonvolutions supprimées.
Rechercher des packages supprimés
Nous devons supprimer certaines circonvolutions. Se précipiter et « tirer » sur n’importe qui est une mauvaise idée, même si cela fonctionnera. Mais puisque vous avez une tête, vous pouvez réfléchir et essayer de sélectionner des circonvolutions « faibles » à supprimer. Il existe plusieurs options :
Chacune des options a droit à la vie et ses propres fonctionnalités de mise en œuvre. Nous considérons ici l'option avec la plus petite mesure L1
Processus manuel pour YOLOv3
L'architecture originale contient des blocs résiduels. Mais peu importe à quel point ils sont intéressants pour les réseaux profonds, ils nous gêneront quelque peu. La difficulté est que vous ne pouvez pas supprimer les rapprochements avec des index différents dans ces couches :
Par conséquent, sélectionnons les couches à partir desquelles nous pouvons librement supprimer les rapprochements :
Construisons maintenant un cycle de travail :
Téléchargement d'activations
Déterminer combien couper
Découper
Apprendre 10 époques avec LR=1e-4
Test
Le déchargement des convolutions est utile pour estimer la quantité de partie que nous pouvons supprimer à une certaine étape. Exemples de déchargement :
Nous voyons que presque partout 5 % des circonvolutions ont une norme L1 très basse et nous pouvons les supprimer. A chaque étape, ce déchargement a été répété et une évaluation a été faite pour savoir quelles couches et combien pouvaient être découpées.
L'ensemble du processus s'est déroulé en 4 étapes (des numéros ici et partout pour la RTX 2060 Super) :
Étape
carte75
Nombre de paramètres, millions
Taille du réseau, Mo
À partir de la valeur initiale, %
Temps d'exécution, ms
État de circoncision
0
0.9656
60
241
100
180
-
1
0.9622
55
218
91
175
5% de tous
2
0.9625
50
197
83
168
5% de tous
3
0.9633
39
155
64
155
15 % pour les couches avec plus de 400 convolutions
4
0.9555
31
124
51
146
10 % pour les couches avec plus de 100 convolutions
Un effet positif a été ajouté à l'étape 2 : la taille du lot 4 est entrée en mémoire, ce qui a considérablement accéléré le processus de formation supplémentaire.
À l'étape 4, le processus a été arrêté car même une formation complémentaire à long terme n'a pas élevé mAp75 aux anciennes valeurs.
En conséquence, nous avons réussi à accélérer l’inférence en 15%, réduisez la taille de 35% et ne pas perdre exactement.
Automatisation pour des architectures plus simples
Pour des architectures de réseau plus simples (sans ajout conditionnel, concaténation et blocs résiduels), il est tout à fait possible de se concentrer sur le traitement de toutes les couches convolutives et d'automatiser le processus de suppression des convolutions.
J'ai implémenté cette option ici.
C'est simple : vous n'avez besoin que d'une fonction de perte, d'un optimiseur et de générateurs de lots :
import pruning
from keras.optimizers import Adam
from keras.utils import Sequence
train_batch_generator = BatchGenerator...
score_batch_generator = BatchGenerator...
opt = Adam(lr=1e-4)
pruner = pruning.Pruner("config.json", "categorical_crossentropy", opt)
pruner.prune(train_batch, valid_batch)
Si nécessaire, vous pouvez modifier les paramètres de configuration :
{
"input_model_path": "model.h5",
"output_model_path": "model_pruned.h5",
"finetuning_epochs": 10, # the number of epochs for train between pruning steps
"stop_loss": 0.1, # loss for stopping process
"pruning_percent_step": 0.05, # part of convs for delete on every pruning step
"pruning_standart_deviation_part": 0.2 # shift for limit pruning part
}
De plus, une limitation basée sur l’écart type est implémentée. Le but est de limiter la partie qui est supprimée, en excluant les convolutions avec des mesures L1 déjà « suffisantes » :
Ainsi, nous vous permettons de supprimer uniquement les convolutions faibles des distributions similaires à celle de droite et de ne pas affecter la suppression des distributions similaires à celle de gauche :
Lorsque la distribution s'approche de la normale, le coefficient pruning_standart_deviation_part peut être sélectionné parmi :
Je recommande une hypothèse de 2 sigma. Ou vous pouvez ignorer cette fonctionnalité, en laissant la valeur < 1.0.
Le résultat est un graphique de la taille, de la perte et de la durée d'exécution du réseau pour l'ensemble du test, normalisé à 1.0. Par exemple, ici, la taille du réseau a été réduite de près de 2 fois sans perte de qualité (petit réseau convolutionnel avec des poids de 100 XNUMX) :
La vitesse de déplacement est soumise à des fluctuations normales et reste pratiquement inchangée. Il y a une explication à cela :
Le nombre de circonvolutions passe de pratique (32, 64, 128) à pas le plus pratique pour les cartes vidéo - 27, 51, etc. Je peux me tromper ici, mais cela a très probablement un effet.
L'architecture n'est pas large, mais cohérente. En réduisant la largeur, nous n'affectons pas la profondeur. Ainsi, nous réduisons la charge, mais ne modifions pas la vitesse.
Par conséquent, l'amélioration s'est traduite par une réduction de la charge CUDA pendant l'exécution de 20 à 30 %, mais pas par une réduction du temps d'exécution.
Les résultats de
Réfléchissons. Nous avons envisagé 2 options d'élagage - pour YOLOv3 (quand vous devez travailler de vos mains) et pour les réseaux avec des architectures plus simples. On peut voir que dans les deux cas, il est possible de réduire et d’accélérer la taille du réseau sans perte de précision. Résultats:
Réduction de taille
Course d'accélération
Réduire la charge CUDA
En conséquence, le respect de l'environnement (Nous optimisons l'utilisation future des ressources informatiques. Quelque part on est heureux Greta Tunberg)
Appendice
Après l'étape d'élagage, vous pouvez ajouter une quantification (par exemple, avec TensorRT)