Les repères GPU en deep Learning ont révolutionné la façon dont nous résolvons des problèmes complexes, de la reconnaissance d'image au traitement du langage naturel. Cependant, alors que la formation de ces modèles s'appuie souvent sur des GPU haute performance, les déploier efficacement dans des environnements liés aux ressources tels que les appareils Edge ou les systèmes avec du matériel limité présente des défis uniques. Les CPU, étant largement disponibles et rentables, servent souvent de squelette pour l'inférence dans de tels scénarios. Mais comment pouvons-nous nous assurer que les modèles déployés sur des processeurs offrent des performances optimales sans compromettre la précision?
Cet article plonge dans la comparaison de l'inférence du modèle d'apprentissage en profondeur sur les processeurs, en se concentrant sur trois mesures critiques: latence, l'utilisation du processeur et l'utilisation de la mémoire. En utilisant un exemple de classification des spam, nous explorons comment les cadres populaires comme Pytorch, Tensorflow, Jax et ONNX Runtime gèrent les charges de travail d'inférence. À la fin, vous aurez une compréhension claire de la façon de mesurer les performances, d'optimiser les déploiements et de sélectionner les bons outils et cadres pour l'inférence basée sur le processeur dans des environnements limités aux ressources.
Impact: L'exécution optimale de l'inférence peut économiser une somme d'argent importante et libérer des ressources pour d'autres charges de travail.
Cet article a été publié dans le cadre du Blogathon de la science des données.
La vitesse d'inférence est essentielle pour l'expérience utilisateur et l'efficacité opérationnelle dans les applications d'apprentissage automatique. L'optimisation d'exécution joue un rôle clé dans l'amélioration de cela en rationalisant l'exécution. L'utilisation de bibliothèques accélérées par le matériel comme l'ONNX Runtime tire parti des optimisations adaptées à des architectures spécifiques, réduisant la latence (temps par inférence).
De plus, des formats de modèle léger tels que ONNX minimisent les frais généraux, permettant une charge et une exécution plus rapides. Optimisé les temps d'exécution exploitent le traitement parallèle pour distribuer le calcul sur les cœurs de processeur disponibles et améliorer la gestion de la mémoire, garantissant de meilleures performances, en particulier sur les systèmes avec des ressources limitées. Cette approche rend les modèles plus rapides et plus efficaces tout en maintenant la précision.
Pour évaluer les performances de nos modèles, nous nous concentrons sur trois métriques clés:
Pour maintenir cette étude d'analyse comparative ciblée et pratique, nous avons fait les hypothèses suivantes et fixé quelques limites:
Ces hypothèses garantissent que les repères restent pertinents pour les développeurs et les équipes travaillant avec du matériel limité aux ressources ou qui ont besoin de performances prévisibles sans la complexité supplémentaire des systèmes distribués.
Nous explorerons les outils et les cadres essentiels utilisés pour comparer et optimiser l'inférence du modèle d'apprentissage en profondeur sur les processeurs, fournissant des informations sur leurs capacités d'exécution efficace dans des environnements limités aux ressources.
Nous utilisons GitHub Codespace (machine virtuelle) avec la configuration ci-dessous:
Les versions des packages utilisés sont les suivants et cette primaire comprend cinq bibliothèques d'inférence d'apprentissage en profondeur: TensorFlow, Pytorch, ONNX Runtime, Jax et OpenVino:
! pip install numpy == 1.26.4 ! pip install torch == 2.2.2 ! pip install tensorflow == 2.16.2 ! PIP Installer Onnx == 1.17.0 ! PIP Install onnxruntime == 1.17.0! PIP Installer Jax == 0.4.30 ! Pip installer jaxlib == 0.4.30 ! PIP Installer OpenVino == 2024.6.0 ! PIP installer matplotlib == 3.9.3 ! PIP Installer Matplotlib: 3.4.3 ! Pip Installer Oreiller: 8.3.2 ! PIP Installer PSUtil: 5.8.0
Étant donné que l'inférence du modèle consiste à effectuer quelques opérations matricielles entre les poids du réseau et les données d'entrée, il ne nécessite pas de formation de modèle ou de jeux de données. Pour notre exemple le processus d'analyse comparative, nous avons simulé un cas d'utilisation de classification standard. Cela simule les tâches de classification binaire communes comme la détection de spam et les décisions de demande de prêt (approbation ou refus). La nature binaire de ces problèmes les rend idéaux pour comparer les performances du modèle dans différents cadres. Cette configuration reflète les systèmes du monde réel mais nous permet de nous concentrer sur les performances d'inférence entre les cadres sans avoir besoin de grands ensembles de données ou de modèles pré-formés.
La tâche de l'échantillon consiste à prédire si un échantillon donné est un spam ou non (approbation du prêt ou déni), sur la base d'un ensemble de fonctionnalités d'entrée. Ce problème de classification binaire est efficace sur le calcul, permettant une analyse ciblée des performances d'inférence sans la complexité des tâches de classification multi-classes.
Pour simuler les données d'e-mail du monde réel, nous avons généré une entrée au hasard. Ces intérêts imitent le type de données qui pourraient être traitées par des filtres de spam mais évitent la nécessité d'éseils de données externes. Ces données d'entrée simulées permettent une analyse comparative sans compter sur des ensembles de données externes spécifiques, ce qui le rend idéal pour tester les temps d'inférence du modèle, l'utilisation de la mémoire et les performances du CPU. Alternativement, vous pouvez utiliser la classification d'images, la tâche NLP ou toute autre tâche d'apprentissage en profondeur pour effectuer ce processus d'analyse comparative.
La sélection du modèle est une étape critique dans l'analyse comparative car elle influence directement les performances et les idées d'inférence obtenus du processus de profilage. Comme mentionné dans la section précédente, pour cette étude d'analyse comparative, nous avons choisi un cas d'utilisation de classification standard, qui implique d'identifier si un e-mail donné est un spam ou non. Cette tâche est un problème de classification à deux classes simple qui est efficace sur le calcul mais fournit des résultats significatifs pour la comparaison entre les cadres.
Le modèle de la tâche de classification est un réseau neuronal à action directe (FNN) conçu pour la classification binaire (spam vs non spam). Il se compose des couches suivantes:
self.fc1 = torch.nn.linear (200,128)
self.fc2 = torch.nn.linear (128, 64) self.fc3 = torch.nn.linear (64, 32) self.fc4 = torch.nn.linear (32, 16) self.fc5 = torch.nn.linear (16, 8) self.fc6 = torch.nn.linear (8, 1)
self.sigmoïd = torch.nn.sigmoïd ()
Le modèle est simple mais efficace pour la tâche de classification.
Le diagramme d'architecture du modèle utilisé pour l'analyse comparative dans notre cas d'utilisation est illustré ci-dessous:
Ce flux de travail vise à comparer les performances d'inférence de plusieurs cadres d'apprentissage en profondeur (TensorFlow, Pytorch, Onnx, Jax et OpenVino) à l'aide de la tâche de classification. La tâche consiste à utiliser des données d'entrée générées de manière aléatoire et à analyser chaque cadre pour mesurer le temps moyen pris pour une prédiction.
Pour commencer avec les modèles d'apprentissage en profondeur, nous devons d'abord importer les packages Python essentiels qui permettent l'intégration transparente et l'évaluation des performances.
heure d'importation Importer un système d'exploitation Importer Numpy comme NP Importer une torche Importer TensorFlow comme TF à partir de Tensorflow.keras Importation importer onnxruntime comme ort Importer Matplotlib.pyplot en tant que plt à partir de l'image d'importation PIL Importer Putil Importer Jax importer jax.numpy en tant que JNP De OpenVino.Runtime Import Core Importer CSV
os.environ ["CUDA_VISIBLE_DEVICES"] = "-1" # Disable GPU os.environ ["tf_cpp_min_log_level"] = "3" #Suppress TensorFlow Journal
Dans cette étape, nous générons des données d'entrée au hasard pour la classification des spams:
Nous générons des données Randome à l'aide de Numpy pour servir de fonctionnalités d'entrée pour les modèles.
# GENERETER DONNÉES MANDIQUES input_data = np.random.rand (1000, 200) .astype (np.float32)
Dans cette étape, nous définissons l'architecture NetWrok ou configurons le modèle à partir de chaque cadre d'apprentissage en profondeur (Tensorflow, Pytorch, ONNX, Jax et OpenVino). Chaque cadre nécessite des méthodes spécifiques pour charger des modèles et les configurer pour l'inférence.
classe pytorchmodel (torch.nn.module): def __init __ (soi): super (pytorchmodel, self) .__ init __ () self.fc1 = torch.nn.linear (200, 128) self.fc2 = torch.nn.linear (128, 64) self.fc3 = torch.nn.linear (64, 32) self.fc4 = torch.nn.linear (32, 16) self.fc5 = torch.nn.linear (16, 8) self.fc6 = torch.nn.linear (8, 1) self.sigmoïd = torch.nn.sigmoïd () Def en avant (self, x): x = torch.relu (self.fc1 (x)) x = torch.relu (self.fc2 (x)) x = torch.relu (self.fc3 (x)) x = torch.relu (self.fc4 (x)) x = torch.relu (self.fc5 (x)) x = self.sigmoïd (self.fc6 (x)) retour x # Créer un modèle Pytorch pytorch_model = pytorchmodel ()
Tensorflow_model = tf.keras.Sesedentiel ([ Entrée (forme = (200,)), tf.keras.layers.dense (128, activation = 'relu'), tf.keras.layers.dense (64, activation = 'relu'), tf.keras.layers.dense (32, activation = 'relu'), tf.keras.layers.dense (16, activation = 'relu'), tf.keras.layers.dense (8, activation = 'relu'), tf.keras.layers.dense (1, activation = 'sigmoïd') ]) Tensorflow_Model.Compile ()
def jax_model (x): x = jax.nn.relu (jnp.dot (x, jnp.ones ((200, 128))))) x = jax.nn.relu (jnp.dot (x, jnp.ones ((128, 64))))) x = jax.nn.relu (jnp.dot (x, jnp.ones ((64, 32))))) x = jax.nn.relu (jnp.dot (x, jnp.ones ((32, 16))))) x = jax.nn.relu (jnp.dot (x, jnp.ones ((16, 8))))) x = jax.nn.sigmoïd (jnp.dot (x, jnp.ones ((8, 1))))) retour x
# Convertir le modèle pytorch en onnx dummy_input = torch.randn (1, 200) onnx_model_path = "modèle.onnx" torch.onnx.export ( pytorch_model, dummy_input, onnx_model_path, export_params = true, opset_version = 11, input_names = ['input'], Output_Names = ['Output'], dynamic_axes = {'input': {0: 'batch_size'}, 'out': {0: 'batch_size'}} ) onnx_Session = Ort.InferencesSession (onnx_model_path)
# Définition du modèle OpenVino Core = Core () openVino_Model = core.read_model (modèle = "modèle.onnx") compilé_model = core.compile_model (openvino_model, device_name = "cpu")
Cette fonction exécute des tests d'analyse comparative sur différents cadres en prenant trois arguments: Predict_function, Input_data et num_runs. Par défaut, il exécute 1 000 fois mais il peut être augmenté selon les exigences.
def benchmark_model (Predict_function, input_data, num_runs = 1000): start_time = time.time () process = pUtil.process (os.getpid ()) cpu_usage = [] Memory_Usage = [] pour _ dans la plage (num_runs): Predict_function (Input_data) cpu_usage.append (process.cpu_percent ()) mémoire_usage.append (process.memory_info (). rss) end_time = time.time () avg_latency = (end_time - start_time) / num_runs avg_cpu = np.mean (cpu_usage) avg_memory = np.mean (mémoire_usage) / (1024 * 1024) # converti en MB retourner avg_latency, avg_cpu, avg_memory
Maintenant que nous avons chargé les modèles, il est temps de comparer les performances de chaque cadre. Le processus d'analyse comparative effectue une inférence sur les données d'entrée générées.
# Modèle de pytorch de référence def pytorch_predict (input_data): pytorch_model (torch.tensor (input_data)) pytorch_latency, pytorch_cpu, pytorch_memory = benchmark_model (lambda x: pytorch_predict (x), input_data)
# Modèle de TensorFlow de référence def Tensorflow_Predict (Input_data): TensorFlow_Model (Input_data) TENSORFLOW_LATENCY, TENSORFLOW_CPU, TENSORFLOW_MEMORY = BENCHMARK_MODEL (Lambda X: Tensorflow_Predict (x), Input_data)
# Benchmark Jax modèle def jax_predict (input_data): jax_model (jnp.array (input_data)) jax_latency, jax_cpu, jax_memory = benchmark_model (lambda x: jax_predict (x), input_data)
# Benchmark ONNX Modèle def onnx_predict (input_data): # Processus des entrées par lots pour i dans la plage (input_data.shape [0]): single_input = input_data [i: i 1] # Extraire une entrée unique onnx_session.run (aucun, {onnx_session.get_inputs () [0] .name: single_input}) onnx_latency, onnx_cpu, onnx_memory = benchmark_model (lambda x: onnx_predict (x), input_data)
# Modèle OpenVino Benchmark def openVino_Predict (input_data): # Processus des entrées par lots pour i dans la plage (input_data.shape [0]): single_input = input_data [i: i 1] # Extraire une entrée unique compilé_model.infer_new_request ({0: single_input}) OpenVino_Latency, OpenVino_CPU, OpenVino_Memory = Benchmark_Model (Lambda X: OpenVino_Predict (x), Input_data)
Ici, nous discutons des résultats de l'analyse comparative des performances des cadres d'apprentissage en profondeur mentionnés précédemment. Nous les comparons sur - latence, l'utilisation du processeur et l'utilisation de la mémoire. Nous avons inclus des données tabulaires et un tracé pour une comparaison rapide.
Cadre | Latence (MS) | Latence relative (contre Pytorch) |
Pytorch | 1.26 | 1.0 (ligne de base) |
Tensorflow | 6.61 | ~ 5,25 × |
Jax | 3.15 | ~ 2,50 × |
Onnx | 14.75 | ~ 11,72 × |
Openvino | 144.84 | ~ 115 × |
Connaissances:
Cadre | Utilisation du processeur (%) | Utilisation relative du processeur 1 |
Pytorch | 99,79 | ~ 1,00 |
Tensorflow | 112.26 | ~ 1.13 |
Jax | 130.03 | ~ 1.31 |
Onnx | 99,58 | ~ 1,00 |
Openvino | 99.32 | 1,00 (ligne de base) |
Connaissances:
Cadre | Mémoire (MB) | Utilisation relative de la mémoire (vs Pytorch) |
Pytorch | ~ 959,69 | 1.0 (ligne de base) |
Tensorflow | ~ 969.72 | ~ 1,01 × |
Jax | ~ 1033.63 | ~ 1,08 × |
Onnx | ~ 1033,82 | ~ 1,08 × |
Openvino | ~ 1040.80 | ~ 1,08–1,09 × |
Connaissances:
Voici l'intrigue comparant les performances des frameworks d'apprentissage en profondeur:
Dans cet article, nous avons présenté un flux de travail complexe pour évaluer les performances d'inférence de grands cadres d'apprentissage en profondeur - Tensorflow, Pytorch, ONNX, Jax et OpenVino - en utilisant une tâche de classification de spam comme référence. En analysant les mesures clés telles que la latence, l'utilisation du processeur et la consommation de mémoire, les résultats ont mis en évidence les compromis entre les cadres et leur pertinence pour différents scénarios de déploiement.
Pytorch a démontré les performances les plus équilibrées, excellant dans une faible latence et une utilisation efficace de la mémoire, ce qui le rend idéal pour des applications sensibles à la latence comme les prédictions et les systèmes de recommandation en temps réel. TensorFlow a fourni une solution du sol moyen avec une consommation de ressources modérément plus élevée. Jax a présenté un débit de calcul élevé, mais au prix de l'augmentation de l'utilisation du processeur, ce qui pourrait être un facteur limitant pour les environnements limités aux ressources. Pendant ce temps, ONNX et OpenVino ont pris du retard de latence, les performances d'OpenVino sont particulièrement entravées par l'absence d'accélération matérielle.
Ces résultats soulignent l'importance d'aligner la sélection du cadre avec les besoins de déploiement. Que l'optimisation de la vitesse, de l'efficacité des ressources ou du matériel spécifique de comprendre les compromis est essentiel pour un déploiement de modèle efficace dans des environnements réels.
A. Le graphique de calcul dynamique de Pytorch et le pipeline d'exécution efficace permettent une inférence à faible latence (1,26 ms), ce qui le rend bien adapté à des applications telles que les systèmes de recommandation et les prédictions en temps réel.
Q2. Qu'est-ce qui a affecté les performances d'OpenVino dans cette étude?A. Les optimisations d'OpenVino sont conçues pour le matériel Intel. Sans cette accélération, sa latence (144,84 ms) et l'utilisation de la mémoire (1040,8 Mo) étaient moins compétitives par rapport à d'autres cadres.
Q3. Comment choisir un cadre pour les environnements liés aux ressources?A. Pour les configurations CPU uniquement, Pytorch est le plus efficace. TensorFlow est une alternative forte pour les charges de travail modérées. Évitez les cadres comme JAX à moins que l'utilisation plus élevée du processeur soit acceptable.
Q4. Quel rôle le matériel joue-t-il dans les performances du framework?A. Les performances du cadre dépend fortement de la compatibilité matérielle. Par exemple, OpenVino excelle sur les CPU Intel avec des optimisations spécifiques au matériel, tandis que Pytorch et TensorFlow fonctionnent de manière cohérente sur des configurations variées.
Q5. Les résultats d'analyse comparative peuvent-ils diffuser avec des modèles ou des tâches complexes?A. Oui, ces résultats reflètent une simple tâche de classification binaire. Les performances pourraient varier avec des architectures complexes comme Resnet ou des tâches comme la NLP ou d'autres, où ces cadres peuvent tirer parti d'optimisations spécialisées.
Les médias présentés dans cet article ne sont pas détenus par l'analytique vidhya et sont utilisés à la discrétion de l'auteur.
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!