John Ousterhout, professeur à Stanford et inventeur du langage Tcl, a écrit un jour un livre "La philosophie de la conception logicielle", qui discutait systématiquement des principes généraux et des méthodologies de la conception logicielle, le point central de l'ensemble. Le livre est : Conception de logiciels L'essentiel est de réduire la complexité.
En fait, ce point de vue s'applique également à la conception de logiciels impliquant une adaptation matérielle sous-jacente.
Prenons l'exemple du développement de modèles visuels. Dans le passé, lors du développement de modèles visuels, les gens accordaient généralement plus d'attention à l'optimisation du modèle lui-même pour améliorer la vitesse et l'effet. Cependant, les gens accordent peu d’attention aux étapes de pré-traitement (pré-traitement) et de post-traitement de l’image.
Lorsque le calcul du modèle, c'est-à-dire l'étape principale de la formation et de l'inférence du modèle, devient de plus en plus efficace, les étapes de pré et post-traitement des images deviennent de plus en plus le goulot d'étranglement des performances des tâches de traitement d'image.
Plus précisément, dans le processus de traitement d'image traditionnel, les parties de pré- et post-traitement sont généralement gérées par le processeur, ce qui entraînera 50 % à plus de 90 % de la charge de travail dans le l’ensemble du processus. Liés au pré- et post-traitement, ils deviendront le goulot d’étranglement des performances de l’ensemble du processus algorithmique.
La bibliothèque de traitement d'images grand public OpenCV propose un large éventail de scénarios d'application, mais elle est également confrontée à certains problèmes d'utilisation réelle.
Par exemple, lorsque vous utilisez la version CPU d'OpenCV pour effectuer d'abord une formation, puis effectuer une inférence, vous aurez peut-être besoin d'une version avec des performances plus élevées pendant la phase d'inférence. Parce que dans le scénario de formation, le pré- et post-traitement et l'inférence de modèle peuvent être couverts dans le temps, couvrant ainsi le temps de pré-traitement. Cependant, dans le pipeline d'inférence, le modèle ne contient que l'inférence directe et la consommation de temps est considérablement réduite après l'accélération de Tensor RT. À ce stade, la consommation de temps de prétraitement sera très élevée et il est difficile de la couvrir. inférence de modèle.
OpenCV utilise différentes versions d'opérateurs pendant la formation et l'inférence. Le CPU est généralement utilisé pendant la formation car sa couverture d'opérateurs CPU est relativement élevée, et le GPU est généralement utilisé pendant l'inférence car les performances sont meilleures. Par conséquent, cela peut également entraîner des problèmes d’alignement des résultats. En d’autres termes, lors de l’utilisation du CPU pour la formation du modèle et du GPU pour l’inférence du modèle, les résultats finaux ne seront pas alignés.
Deuxièmement, les performances de certains opérateurs GPU seront dégradées. Dans OpenCV, certains opérateurs GPU eux-mêmes prennent relativement beaucoup de temps, ce qui entraîne une régression des performances de l'ensemble de l'opérateur encore pire que la version CPU.
Troisièmement, la couverture des opérateurs GPU d'OpenCV est limitée et certains opérateurs ne disposent que de versions CPU. Il existe également certains opérateurs GPU dont la couverture des paramètres, types de données, etc. n'est pas aussi élevée que celle de la version CPU, ce qui entraîne des limitations d'utilisation.
Enfin, si l'opérateur CPU et l'opérateur GPU sont utilisés de manière interactive pendant l'utilisation, un grand nombre de copies de données et d'opérations de synchronisation entre le CPU et le GPU se produiront, entraînant des performances d'accélération globales insuffisantes.
Une autre bibliothèque de traitement d'image couramment utilisée est TorchVision.
Lorsque TorchVision effectue une inférence de modèle, certains opérateurs manquent d'interfaces C++, ce qui entraîne un manque de flexibilité lors des appels. Si vous souhaitez créer une version C++, vous devez la créer via TorchScript. Cela entraînera de nombreux inconvénients lors de l'utilisation, car l'insertion d'opérateurs d'autres bibliothèques au milieu du processus pour une utilisation interactive entraînera une surcharge et une charge de travail supplémentaires. Un autre inconvénient de TorchVision est que la couverture des opérateurs n'est pas élevée.
Ce qui précède présente les limites des bibliothèques de CV traditionnelles actuelles.
Étant donné que le goulot d'étranglement des performances du pré- et du post-traitement réside principalement dans l'utilisation des calculs CPU, la technologie d'utilisation du GPU dans l'étape de calcul du modèle est devenue de plus en plus importante. plus mature.
Ensuite, une solution naturelle consiste à utiliser le GPU pour accélérer le pré- et le post-traitement, ce qui améliorera considérablement les performances de l'ensemble du pipeline d'algorithmes.
À cette fin, NVIDIA et ByteDance ont conjointement ouvert la bibliothèque d'opérateurs de prétraitement d'image CV-CUDA. CV-CUDA peut fonctionner efficacement sur GPU et la vitesse de l'opérateur peut atteindre environ cent fois celle d'OpenCV.
Le 15 janvier 2023, de 9h30 à 11h30, la « Première classe ouverte CV-CUDA » organisée par NVIDIA a invité 3 experts techniques de NVIDIA, ByteDance et Sina Weibo (Zhang Yi, Sheng Yiyao, Pang Feng ) partagé en profondeur sur des sujets connexes. Cet article résume l’essentiel des discours des trois experts.
Il y a de nombreux avantages à utiliser le GPU au lieu du CPU. Tout d’abord, une fois les opérateurs de pré et post-traitement migrés vers le GPU, l’efficacité de calcul des opérateurs peut être améliorée.
Deuxièmement, puisque tous les processus sont effectués sur le GPU, la copie des données entre le CPU et le GPU peut être réduite.
Enfin, après avoir migré la charge CPU vers le GPU, la charge CPU peut être réduite et le CPU peut être utilisé pour traiter d'autres tâches nécessitant une logique complexe.
Après la migration de l'ensemble du processus vers le GPU, l'ensemble du pipeline peut être amélioré près de 30 fois, économisant ainsi les frais de calcul et réduisant les coûts d'exploitation.
Vous pouvez voir à partir de la comparaison des données sur la figure que sous la même configuration de serveur et de paramètres, pour un flux vidéo 30 ips 1080p, OpenCV peut ouvrir jusqu'à 2-3 flux parallèles et PyTorch (CPU) peut s'ouvrir à 1,5 flux parallèles, et CV-CUDA peut ouvrir jusqu'à 60 flux parallèles. On peut voir que les performances globales ont été grandement améliorées. Les opérateurs de pré-traitement impliqués incluent le redimensionnement, le remplissage, la normalisation, etc., et les opérateurs de post-traitement incluent le recadrage, le redimensionnement, la composition, etc.
Pourquoi le GPU peut-il s'adapter aux besoins d'accélération du pré- et post-traitement ? Bénéficiez de l'asynchrone entre le calcul du modèle et le pré- et post-traitement, et adaptez-vous aux capacités de calcul parallèle du GPU.
Nous expliquerons respectivement l'asynchronisation du prétraitement de la formation du modèle et de l'inférence du modèle.
La formation du modèle peut être divisée en deux parties, la première est la préparation des données et la seconde est le calcul du modèle.
Les frameworks d'apprentissage automatique grand public actuels, tels que PyTorch et TensorFlow, sont asynchrones entre la préparation des données et le calcul du modèle. En prenant PyTorch comme exemple, il lancera plusieurs sous-processus pour la préparation des données.
Comme le montre la figure, il contient deux états, à savoir le calcul du modèle et la préparation des données. Il existe une relation de séquence temporelle entre les deux. Par exemple, lorsque D0 est terminé, B0 peut être effectué, et ainsi de suite. .
Du point de vue des performances, nous nous attendons à ce que la vitesse de préparation des données suive la vitesse de calcul du modèle. Cependant, dans les situations réelles, certains processus de lecture et de prétraitement des données prennent beaucoup de temps, ce qui entraîne une certaine période de fenêtre avant que les calculs du modèle correspondant puissent être effectués, ce qui entraîne une diminution de l'utilisation du GPU.
La préparation des données peut être divisée en lecture des données et prétraitement des données. Ces deux étapes peuvent être exécutées en série ou en parallèle. Par exemple, dans le framework PyTorch, elles sont exécutées en série.
De nombreux facteurs affectent les performances de lecture des données, tels que le support de stockage des données, le format de stockage, le parallélisme, le nombre de processus d'exécution, etc.
En revanche, le facteur qui affecte les performances du prétraitement des données est relativement simple, à savoir le degré de parallélisme . Plus le degré de parallélisme est élevé, meilleures sont les performances du prétraitement des données. En d'autres termes, rendre le prétraitement des données et le calcul du modèle asynchrones et augmenter le parallélisme du prétraitement des données peut améliorer les performances du prétraitement des données.
Dans la phase d'inférence du modèle, ses performances ont deux indicateurs, le premier est le débit et le second est le retard. Dans une certaine mesure, ces deux indicateurs s’excluent mutuellement.
Pour une seule requête, une fois que le serveur a reçu les données, il prétraitera les données puis effectuera l'inférence de modèle. Ainsi, pour une seule requête, il s’agit dans une certaine mesure d’un processus en série.
Mais cela est très inefficace et gaspillera beaucoup de ressources informatiques. Afin d'améliorer le débit, de nombreux moteurs d'inférence utiliseront la même stratégie que la phase de formation pour préparer de manière asynchrone les données et les calculs du modèle. Lors de la phase de préparation des données, un certain nombre de requêtes seront accumulées et combinées en un lot, puis des calculs ultérieurs seront effectués pour améliorer le débit global.
En termes de débit, l'inférence de modèle et la formation de modèle sont relativement similaires. En déplaçant l'étape de prétraitement des données du CPU vers le GPU, des gains de débit peuvent être obtenus.
Dans le même temps, du point de vue du délai, pour chaque instruction de requête, si le temps passé dans le processus de prétraitement peut être réduit, le délai de chaque requête sera raccourci en conséquence.
Une autre caractéristique de l'inférence de modèle est que la quantité de calcul du modèle est relativement faible, car elle implique uniquement un calcul en avant et non un calcul en arrière. Cela signifie que l'inférence de modèle nécessite un prétraitement de données plus élevé.
En supposant qu'il y ait suffisamment de ressources CPU pour le calcul, théoriquement, le prétraitement ne deviendra pas un goulot d'étranglement en termes de performances. Parce qu'une fois que vous constatez que les performances ne peuvent pas suivre, il vous suffit d'ajouter des processus pour effectuer les opérations de prétraitement.
Par conséquent, le prétraitement des données peut devenir un goulot d'étranglement en termes de performances uniquement en cas de concurrence pour les ressources CPU.
Dans les affaires réelles, la concurrence pour les ressources CPU est très courante, ce qui entraînera une réduction de l'utilisation du GPU dans les étapes ultérieures de formation et d'inférence, et donc une vitesse de formation réduite.
À mesure que la puissance de calcul du GPU continue d'augmenter, il est prévisible que les exigences de vitesse pour la phase de préparation des données deviendront de plus en plus élevées.
Pour cette raison, il est un choix naturel de déplacer la partie prétraitement vers le GPU pour atténuer le problème de la concurrence entre les ressources CPU et améliorer l'utilisation du GPU.
En général, cette conception réduit la complexité du système et adapte directement le corps principal du pipeline de modèle au GPU, ce qui peut être d'une grande aide pour améliorer l'utilisation du GPU et du CPU. Dans le même temps, il évite également le problème de l'alignement des résultats entre les différentes versions, réduit les dépendances et est conforme aux principes de conception logicielle proposés par John Ousterhout.
Le déplacement des processus de pré-traitement et de post-traitement vers le GPU doit répondre à plusieurs conditions.
La première est que ses performances devraient être au moins meilleures que celles du CPU. Ceci repose principalement sur les capacités de calcul simultanées élevées des GPU.
La seconde est l'accélération du prétraitement, qui ne peut pas avoir un impact négatif sur d'autres processus tels que l'inférence de modèle. Pour la deuxième exigence, chaque opérateur de CV-CUDA dispose d'une interface entre le flux et la mémoire CUDA, afin que les ressources GPU puissent être allouées de manière plus raisonnable, de sorte que lors de l'exécution de ces opérateurs de prétraitement sur le GPU, cela n'ait pas trop d'impact. au calcul du modèle lui-même.
Troisièmement, les sociétés Internet ont des besoins commerciaux très divers, impliquant de nombreux types de modèles et la logique de prétraitement correspondante. Par conséquent, les opérateurs de prétraitement doivent être développés en opérateurs personnalisés, afin d'avoir une plus grande flexibilité pour mettre en œuvre une logique complexe.
En général, CV-CUDA accélère les étapes de pré- et post-traitement dans le pipeline de modèles sous les aspects matériels, logiciels, algorithmes, langages, etc., et unifie l'ensemble du pipeline.
En termes de matériel, CV-CUDA est basé sur les capacités de calcul parallèle du GPU, ce qui peut considérablement améliorer la vitesse et le débit du pré- et post-traitement, réduire le temps d'attente du modèle calcul et améliorer l’utilisation du GPU.
CV-CUDA prend en charge les modes Lot et Forme variable. Le mode batch prend en charge le traitement par lots et peut tirer pleinement parti des caractéristiques parallèles du GPU. Cependant, OpenCV ne peut appeler qu'une seule image, qu'il s'agisse de la version CPU ou GPU.
Le mode Forme variable signifie que dans un lot, la longueur et la largeur de chaque image peuvent être différentes. La longueur et la largeur des images sur Internet sont généralement incohérentes. La méthode du framework traditionnel consiste à redimensionner respectivement la longueur et la largeur à la même taille, puis à regrouper les images de même longueur et largeur dans un lot, puis à traiter le lot. CV-CUDA peut directement placer des images de différentes longueurs et largeurs dans un lot pour le traitement, ce qui améliore non seulement l'efficacité, mais est également très pratique à utiliser.
Une autre signification de la forme variable est que lors du traitement des images, vous pouvez spécifier certains paramètres de chaque image, tels que la rotation. Pour un lot d'images, vous pouvez spécifier l'angle de rotation de chaque image.
En termes de logiciel, CV-CUDA a développé un grand nombre de méthodes d'optimisation logicielle pour une optimisation plus poussée, y compris l'optimisation des performances (telle que l'optimisation de l'accès à la mémoire) et l'optimisation de l'utilisation des ressources (telle que pré-allocation de mémoire vidéo), afin qu'il puisse fonctionner efficacement dans des scénarios de formation et d'inférence sur le cloud.
Le premier concerne les paramètres de pré-allocation de la mémoire vidéo. Lorsqu'OpenCV appelle la version GPU, certains opérateurs exécuteront cudaMalloc en interne, ce qui entraînera une augmentation significative de la consommation de temps. Dans CV-CUDA, toutes les pré-allocations de mémoire vidéo sont effectuées pendant la phase d'initialisation, tandis que pendant les phases de formation et d'inférence, aucune opération d'allocation de mémoire vidéo n'est effectuée, améliorant ainsi l'efficacité.
Deuxièmement, tous les opérateurs fonctionnent de manière asynchrone. CV-CUDA intègre un grand nombre de noyaux, réduisant ainsi le nombre de noyaux, réduisant ainsi le temps de démarrage du noyau ainsi que la copie et l'effacement des données, et améliorant l'efficacité opérationnelle globale.
Troisièmement, CV-CUDA optimise également l'accès à la mémoire, comme la fusion de l'accès à la mémoire, la lecture et l'écriture vectorisées, etc., pour améliorer l'utilisation de la bande passante, et utilise également la mémoire partagée pour améliorer l'accès à la mémoire, l'efficacité de la lecture et de l'écriture.
Enfin, CV-CUDA a également apporté de nombreuses optimisations dans les calculs, telles que les mathématiques rapides, la réduction de distorsion/réduction de bloc, etc.
En termes d'algorithme, les opérateurs de CV-CUDA sont conçus et personnalisés indépendamment, ce qui peut prendre en charge une implémentation logique très complexe et faciliter l'utilisation et le débogage.
Comment comprendre le design indépendant ? Il existe deux formes d'appel d'opérateur dans la bibliothèque de traitement d'image. L'une est la forme de pipeline globale, qui ne peut obtenir que les résultats du pipeline, comme DALI, et l'autre est la forme d'opérateur modulaire indépendant, qui peut obtenir chaque opérateur. Résultats individuels, comme OpenCV. CV-CUDA utilise le même formulaire d'appel qu'OpenCV, qui est plus pratique à utiliser et à déboguer.
En termes de langage, CV-CUDA prend en charge une API riche, qui peut connecter de manière transparente le pré- et le post-traitement aux scénarios de formation et d'inférence.
Ces API incluent des interfaces C, C++, Python, etc. couramment utilisées, ce qui nous permet de prendre en charge à la fois les scénarios de formation et d'inférence. Elles prennent également en charge les interfaces PyTorch et TensorRT. À l'avenir, CV-CUDA prendra également en charge. Triton, TensorFlow, JAX et autres interfaces.
Dans la phase d'inférence, vous pouvez utiliser directement l'interface Python ou C++ pour l'inférence, à condition de vous assurer que le pré- et post-traitement, le modèle et le GPU sont placés sur le même flux lors de l'inférence.
Le premier est le cas de classification d'images présenté par NVIDIA.
Dans le pipeline de classification d'image, le premier est le décodage JPEG, qui décode l'image ; la partie verte est l'étape de pré-traitement, y compris le redimensionnement, la conversion du type de données, la normalisation et le reformatage ; -étape de traitement à l'aide de PyTorch Pour le processus de raisonnement, les résultats de classification sont finalement notés et triés.
En comparant les performances de la version CPU et de la version GPU de CV-CUDA et OpenCV, on constate que la version GPU d'OpenCV peut obtenir une plus grande amélioration des performances par rapport à la version CPU, et en en appliquant CV -CUDA, ce qui peut doubler les performances. Par exemple, le nombre d'images traitées par l'opérateur CPU d'OpenCV par milliseconde est de 22 et le nombre d'images traitées par l'opérateur GPU par milliseconde est supérieur à 200. CV-CUDA peut traiter plus de 500 images par milliseconde et son débit est celui du CPU d'OpenCV C'est plus de 20 fois celui de la version GPU et deux fois celui de la version GPU. L'amélioration des performances est évidente.
Deuxièmement, il existe trois cas de multimodalité OCR1, OCR2 et vidéo démontrés par ByteDance.
Dans la formation des modèles, vous pouvez voir que sur les trois tâches OCR1, OCR2 et multimodalité vidéo, des gains de performances de 50 % à 100 % ont été obtenus après l'utilisation de CV-CUDA.
Pourquoi y a-t-il un si gros gain de performances ? En fait, une chose que ces trois tâches ont en commun est que leur logique de prétraitement d'image est très complexe, comme le décodage, le redimensionnement, le recadrage, etc., et ce sont encore de grandes catégories. En fait, il peut y en avoir de nombreuses plus petites dans chacune. catégorie d’opérateur. Prétraitement de classe ou de sous-classe. Pour ces trois tâches, il peut y avoir plus d'une douzaine de types d'amélioration des données sur la liaison de prétraitement, donc la pression de calcul sur le CPU est très élevée. Si cette partie du calcul peut être déplacée vers le GPU, la concurrence en ressources du CPU sera. sera considérablement réduit et le débit global sera grandement amélioré.
Enfin, il y a un cas de traitement vidéo présenté sur Sina Weibo.
Pour le processus de traitement vidéo, la méthode traditionnelle consiste d'abord à décoder les images vidéo dans l'environnement CPU, à décoder le flux d'octets d'origine en données d'image, puis à effectuer certaines opérations régulières, telles que redimensionnez, recadrez, etc., puis téléchargez les données sur le GPU pour des calculs de modèle spécifiques.
La méthode de traitement de CV-CUDA consiste à télécharger le flux d'octets décodé par le CPU et placé dans la mémoire vers le GPU, et le prétraitement est également situé sur le GPU, de sorte qu'il est indépendant du calcul du modèle. Connexion transparente, aucune opération de copie entre la mémoire vidéo et la mémoire n'est requise.
La figure montre le temps de traitement d'OpenCV (nombres impairs) et CV-CUDA (nombres pairs). Le bleu fait référence au temps de consommation du modèle, et l'orange fait référence à la consommation de décodage. . Temps, le vert fait référence au temps de consommation du prétraitement.
OpenCV peut être divisé en deux modes : le décodage CPU et le décodage GPU CV-CUDA utilise uniquement le mode de décodage GPU.
On peut voir que pour OpenCV décodé par CPU, le décodage et le prétraitement d'OpenCV prennent beaucoup plus de temps que CV-CUDA.
En regardant OpenCV utilisant le décodage GPU, nous pouvons voir qu'OpenCV et CV-CUDA sont proches en termes de consommation de temps dans les parties de modèle et de décodage, mais il y a encore un grand écart dans le prétraitement .
En termes de comparaison globale du pipeline, CV-CUDA présente également des avantages évidents, d'une part, CV-CUDA économise les ressources CPU, c'est-à-dire que lorsque l'utilisation du GPU est pleinement maximisée, CV. -CUDA CUDA ne nécessite que 10 % de la configuration CPU d'OpenCV ; en même temps, CV-CUDA économise également les ressources GPU sur le pipeline global, l'efficacité de CV-CUDA augmente de 70 %.
CV-CUDA peut résoudre efficacement le problème de la concurrence entre les ressources CPU dans les phases de formation et d'inférence du modèle, améliorant ainsi l'efficacité de la formation et de l'inférence du modèle.
Mais comment bien comprendre les avantages de CV-CUDA ? Il est nécessaire de comprendre le principe fondamental de sa fonction, et ses avantages par rapport au CPU et à OpenCV ne sont pas absolus.
Tout d’abord, CV-CUDA n’est en fait pas une panacée. Par exemple, lors de la phase de formation du modèle, si le goulot d'étranglement ne réside pas dans le prétraitement, mais dans la lecture des données et l'inférence du modèle. À l’heure actuelle, si vous utilisez CV-CUDA pour remplacer la solution de prétraitement d’origine, cela ne sert à rien.
De plus, lors de l'utilisation de CV-CUDA, si la charge de travail du CPU et du GPU est raisonnablement allouée à la logique de prétraitement, de meilleurs résultats de performances peuvent parfois être obtenus.
Par exemple, le processeur peut toujours décoder et redimensionner les images, puis les placer sur le GPU pour les traiter après le redimensionnement.
Pourquoi mettre le décodage et le redimensionnement sur le CPU ? Tout d’abord, pour le décodage d’images, l’unité de décodage matérielle du GPU est en réalité limitée. Deuxièmement, pour le redimensionnement, le redimensionnement convertira généralement une image plus grande en une image plus petite.
Si vous copiez les données sur le GPU avant de les redimensionner, cela peut occuper beaucoup de bande passante pour le transfert de données de la mémoire vidéo.
Bien sûr, la manière de répartir la charge de travail entre le CPU et le GPU doit encore être jugée en fonction de la situation réelle.
Le principe le plus important est de ne pas alterner les calculs entre CPU et GPU, car il y a une surcharge dans la transmission des données entre les appareils. Si l'alternance est trop fréquente, les bénéfices apportés par le calcul lui-même peuvent être aplatis, entraînant une diminution des performances au lieu d'une augmentation.
En décembre 2022, CV-CUDA a publié la version alpha, qui contient plus de 20 opérateurs, tels que Flip, Rotate, Perspective, Resize, etc.
Actuellement, OpenCV compte plus d'opérateurs, avec des milliers d'opérateurs. CV-CUDA n'accélère actuellement que les opérateurs les plus couramment utilisés, et de nouveaux opérateurs seront ajoutés à l'avenir.
CV-CUDA publiera également une version bêta en mars de cette année, qui ajoutera plus de 20 opérateurs à plus de 50 opérateurs. La version bêta inclura certains opérateurs très couramment utilisés, tels que ConvexHull, FindContours, etc.
En regardant la conception de CV-CUDA, nous pouvons constater qu'il n'y a pas de principe trop compliqué derrière cela, et on peut même dire qu'il être clair en un coup d'œil.
Du point de vue de la complexité, cela peut être considéré comme l'avantage de CV-CUDA. « La philosophie de la conception logicielle » mentionne un principe pour juger de la complexité des logiciels : si un système logiciel est difficile à comprendre et à modifier, il est très complexe ; s'il est facile à comprendre et à modifier, il est très simple.
L'efficacité de CV-CUDA peut être comprise comme l'adaptabilité de l'étape de calcul du modèle au GPU, qui détermine l'adaptabilité des étapes de pré- et post-traitement au GPU. Et cette tendance ne fait que commencer.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!