Pourquoi les pros python évitent les boucles: un doux guide de la pensée vectorisée




Image de l’auteur | Toile
# Introduction
Lorsque vous êtes nouveau sur Python, vous utilisez généralement des boucles «pour» chaque fois que vous devez traiter une collection de données. Besoin de carréner une liste de nombres? Boucler à travers eux. Besoin de les filtrer ou de les résumer? Boucle à nouveau. C’est plus intuitif pour nous en tant qu’êtres humains parce que notre cerveau pense et fonctionne séquentiellement (une chose à la fois).
Mais cela ne signifie pas que les ordinateurs doivent le faire. Ils peuvent profiter de quelque chose appelé pensée vectorisée. Fondamentalement, au lieu de faire une boucle dans chaque élément pour effectuer une opération, vous donnez toute la liste à Python comme, « Hé, voici la liste. Effectuez toutes les opérations à la fois. »
Dans ce tutoriel, je vais vous donner une douce introduction à son fonctionnement, pourquoi cela compte, et nous couvrirons également quelques exemples pour voir à quel point il peut être bénéfique. Alors, commençons.
# Qu’est-ce que la pensée vectorisée et pourquoi ça compte?
Comme discuté précédemment, la pensée vectorisée signifie qu’au lieu de gérer séquentiellement les opérations, nous voulons les exécuter collectivement. Cette idée est en fait inspirée par les opérations matricielles et vectorielles en mathématiques, et cela rend votre code beaucoup plus rapide et plus lisible. Des bibliothèques comme Numpy vous permettent d’implémenter la pensée vectorisée dans Python.
Par exemple, si vous devez multiplier une liste de nombres par 2, alors au lieu d’accéder à chaque élément et de faire l’opération un par un, vous multipliez la liste entière simultanément. Cela présente des avantages majeurs, comme réduire une grande partie des frais généraux de Python. Chaque fois que vous itérez à travers une boucle Python, l’interprète doit faire beaucoup de travail comme vérifier les types, gérer des objets et manipuler les mécanismes de boucle. Avec une approche vectorisée, vous réduisez cela par le traitement en vrac. C’est aussi beaucoup plus rapide. Nous verrons cela plus tard avec un exemple d’impact sur les performances. J’ai visualisé ce que je viens de dire sous la forme d’une image afin que vous puissiez avoir une idée de ce à quoi je fais référence.
Maintenant que vous avez l’idée de ce qu’il est, voyons comment vous pouvez l’implémenter et comment il peut être utile.
# Un exemple simple: conversion de température
Il existe différentes conventions de température utilisées dans différents pays. Par exemple, si vous connaissez l’échelle Fahrenheit et que les données sont données dans Celsius, voici comment vous pouvez la convertir en utilisant les deux approches.
// L’approche de boucle
celsius_temps = (0, 10, 20, 30, 40, 50)
fahrenheit_temps = ()
for temp in celsius_temps:
fahrenheit = (temp * 9/5) + 32
fahrenheit_temps.append(fahrenheit)
print(fahrenheit_temps)
Sortir:
(32.0, 50.0, 68.0, 86.0, 104.0, 122.0)
// L’approche vectorisée
import numpy as np
celsius_temps = np.array((0, 10, 20, 30, 40, 50))
fahrenheit_temps = (celsius_temps * 9/5) + 32
print(fahrenheit_temps) # (32. 50. 68. 86. 104. 122.)
Sortir:
( 32. 50. 68. 86. 104. 122.)
Au lieu de traiter avec chaque élément un à la fois, nous transformons la liste en un tableau Numpy et appliquons la formule à tous les éléments à la fois. Les deux traitent les données et donnent le même résultat. Outre le code Numpy étant plus concis, vous ne remarquez peut-être pas le décalage horaire pour le moment. Mais nous couvrirons cela sous peu.
# Exemple avancé: opérations mathématiques sur plusieurs tableaux
Prenons un autre exemple où nous avons plusieurs tableaux et nous devons calculer le profit. Voici comment vous pouvez le faire avec les deux approches.
// L’approche de boucle
revenues = (1000, 1500, 800, 2000, 1200)
costs = (600, 900, 500, 1100, 700)
tax_rates = (0.15, 0.18, 0.12, 0.20, 0.16)
profits = ()
for i in range(len(revenues)):
gross_profit = revenues(i) - costs(i)
net_profit = gross_profit * (1 - tax_rates(i))
profits.append(net_profit)
print(profits)
Sortir:
(340.0, 492.00000000000006, 264.0, 720.0, 420.0)
Ici, nous calculons manuellement le bénéfice pour chaque entrée:
- Soustraire le coût des revenus (profit brut)
- Appliquer l’impôt
- Ajouter le résultat à une nouvelle liste
Fonctionne bien, mais c’est beaucoup d’indexation manuelle.
// L’approche vectorisée
import numpy as np
revenues = np.array((1000, 1500, 800, 2000, 1200))
costs = np.array((600, 900, 500, 1100, 700))
tax_rates = np.array((0.15, 0.18, 0.12, 0.20, 0.16))
gross_profits = revenues - costs
net_profits = gross_profits * (1 - tax_rates)
print(net_profits)
Sortir:
(340. 492. 264. 720. 420.)
La version vectorisée est également plus lisible et effectue des opérations par élément sur les trois tableaux simultanément. Maintenant, je ne veux pas simplement continuer à répéter «c’est plus rapide» sans preuve solide. Et vous pourriez penser: «De quoi parle Kanwal?» Mais maintenant que vous avez vu comment l’implémenter, regardons la différence de performance entre les deux.
# Performance: les chiffres ne mentent pas
La différence dont je parle n’est pas seulement le battage médiatique ou quelque chose de théorique. C’est mesurable et prouvé. Regardons une référence pratique pour comprendre la quantité d’amélioration que vous pouvez vous attendre. Nous allons créer un très grand ensemble de données de 1 000 000 instances et effectuer l’opération (x ^ 2 + 3x + 1 ) sur chaque élément en utilisant les deux approches et comparer l’heure.
import numpy as np
import time
# Create a large dataset
size = 1000000
data = list(range(size))
np_data = np.array(data)
# Test loop-based approach
start_time = time.time()
result_loop = ()
for x in data:
result_loop.append(x ** 2 + 3 * x + 1)
loop_time = time.time() - start_time
# Test vectorized approach
start_time = time.time()
result_vector = np_data ** 2 + 3 * np_data + 1
vector_time = time.time() - start_time
print(f"Loop time: {loop_time:.4f} seconds")
print(f"Vector time: {vector_time:.4f} seconds")
print(f"Speedup: {loop_time / vector_time:.1f}x faster")
Sortir:
Loop time: 0.4615 seconds
Vector time: 0.0086 seconds
Speedup: 53.9x faster
C’est plus de 50 fois plus rapide !!!
Ce n’est pas une petite optimisation, cela rendra vos tâches de traitement des données (je parle de grands ensembles de données) beaucoup plus réalisables. J’utilise Numpy pour ce tutoriel, mais Pandas est une autre bibliothèque construite sur Numpy. Vous pouvez également l’utiliser.
# Quand ne pas vectoriser
Ce n’est pas parce que quelque chose fonctionne pour la plupart des cas que c’est l’approche. En programmation, votre «meilleure» approche dépend toujours du problème à accomplir. La vectorisation est excellente lorsque vous effectuez la même opération sur tous les éléments d’un ensemble de données. Mais si votre logique implique des conditions complexes, une terminaison précoce ou des opérations qui dépendent des résultats précédents, collez-le à l’approche basée sur la boucle.
De même, lorsque vous travaillez avec de très petits ensembles de données, les frais généraux de la mise en place des opérations vectorielles peuvent l’emporter sur les avantages. Alors utilisez-le là où cela a du sens et ne le forcez pas là où il ne le fait pas.
# Emballage
Alors que vous continuez à travailler avec Python, mettez-vous au défi de repérer les opportunités de vectorisation. Lorsque vous vous retrouvez à chercher une boucle «For», faites une pause et demandez s’il existe un moyen d’exprimer la même opération en utilisant Numpy ou Pandas. Le plus souvent, il y en a, et le résultat sera un code non seulement plus rapide mais aussi plus élégant et plus facile à comprendre.
N’oubliez pas que l’objectif n’est pas d’éliminer toutes les boucles de votre code. C’est pour utiliser le bon outil pour le travail.
Kanwal Mehreen Kanwal est ingénieur d’apprentissage automatique et écrivain technique avec une profonde passion pour la science des données et l’intersection de l’IA avec la médecine. Elle a co-écrit l’ebook « Maximiser la productivité avec Chatgpt ». En tant que Google Generation Scholar 2022 pour APAC, elle défend la diversité et l’excellence académique. Elle est également reconnue comme une diversité de Teradata dans Tech Scholar, le boursier de recherche Mitacs Globalink et le savant de Harvard WECODE. Kanwal est un ardent défenseur du changement, après avoir fondé des femmes pour autonomiser les femmes dans les champs STEM.
Source link