Diffusion NumPy pour des calculs efficaces

NumPyBeginner
Pratiquer maintenant

Introduction

La diffusion (broadcasting) est un concept fondamental dans NumPy qui permet des opérations arithmétiques élément par élément sur des tableaux de formes différentes. Cette fonctionnalité puissante élimine le besoin de boucles explicites, conduisant à un code plus concis et plus efficace sur le plan computationnel. Dans ce laboratoire, vous apprendrez les règles de la diffusion et les appliquerez à des exemples pratiques en écrivant et en exécutant des scripts Python.

Diffusion d'un scalaire vers un tableau

La forme la plus simple de diffusion se produit lorsque vous effectuez une opération entre un tableau et un seul nombre (un scalaire). NumPy "étire" ou "diffuse" automatiquement le scalaire pour qu'il corresponde à la forme du tableau.

Tout d'abord, localisez le fichier broadcasting.py dans l'explorateur de fichiers sur le côté gauche de votre écran. Double-cliquez dessus pour l'ouvrir dans l'éditeur.

Maintenant, remplacez le contenu de broadcasting.py par le code suivant. Ce code crée un tableau NumPy 1D et le multiplie par une valeur scalaire.

import numpy as np

## Create a 1D array
a = np.array([1.0, 2.0, 3.0])

## Define a scalar
b = 2.0

## Multiply the array by the scalar
## The scalar 'b' is broadcast to the shape of 'a'
result = a * b

print("Array 'a':", a)
print("Scalar 'b':", b)
print("Result of a * b:", result)

Pour voir le résultat, vous devez exécuter le script. Ouvrez un terminal en cliquant sur l'icône + dans le panneau du terminal en bas de l'écran et en sélectionnant Terminal. Ensuite, exécutez la commande suivante :

python broadcasting.py

Vous devriez voir la sortie suivante, où chaque élément du tableau a a été multiplié par 2.0.

Array 'a': [1. 2. 3.]
Scalar 'b': 2.0
Result of a * b: [2. 4. 6.]

Diffusion de deux tableaux aux formes compatibles

La diffusion fonctionne également entre deux tableaux si leurs formes sont compatibles. La règle générale de compatibilité est la suivante : lors de la comparaison des dimensions de deux tableaux de droite à gauche, chaque paire de dimensions doit être égale ou l'une d'elles doit être 1.

Voyons un exemple d'ajout d'un tableau 1D à un tableau 2D. Le tableau 1D sera diffusé sur chaque ligne du tableau 2D.

Mettez à jour votre fichier broadcasting.py avec le code suivant :

import numpy as np

## Create a 2D array (shape: 2, 3)
a = np.array([[1.0, 2.0, 3.0],
              [4.0, 5.0, 6.0]])

## Create a 1D array (shape: 3,)
b = np.array([10.0, 20.0, 30.0])

## Add the two arrays
## 'b' is broadcast to shape (2, 3) to match 'a'
## It becomes [[10. 20. 30.], [10. 20. 30.]] internally
result = a + b

print("Array 'a' (shape {}):\n{}".format(a.shape, a))
print("Array 'b' (shape {}): {}".format(b.shape, b))
print("Result of a + b:\n", result)

Exécutez à nouveau le script dans le terminal :

python broadcasting.py

La sortie montre que le tableau 1D b a été ajouté à chaque ligne du tableau 2D a.

Array 'a' (shape (2, 3)):
[[1. 2. 3.]
 [4. 5. 6.]]
Array 'b' (shape (3,)): [10. 20. 30.]
Result of a + b:
 [[11. 22. 33.]
 [14. 25. 36.]]

Comprendre les formes incompatibles

La diffusion échouera si les formes des tableaux ne sont pas compatibles selon les règles. Cela entraîne une ValueError. Comprendre quand cela se produit est crucial pour le débogage.

Essayons une opération avec des formes incompatibles. Ici, nous allons tenter d'ajouter un tableau de forme (2, 3) à un tableau de forme (2,). NumPy compare les dimensions finales (3 et 2), constate qu'elles ne sont pas égales et que ni l'une ni l'autre n'est 1. Cela provoquera une erreur.

Modifiez votre fichier broadcasting.py pour qu'il contienne le code suivant :

import numpy as np

## Create a 2D array (shape: 2, 3)
a = np.array([[1.0, 2.0, 3.0],
              [4.0, 5.0, 6.0]])

## Create an incompatible 1D array (shape: 2,)
b = np.array([1.0, 2.0])

print("Array 'a' (shape {}):\n{}".format(a.shape, a))
print("Array 'b' (shape {}): {}".format(b.shape, b))

## This will raise a ValueError
try:
    result = a + b
except ValueError as e:
    print("\nError:", e)

Exécutez le script depuis le terminal :

python broadcasting.py

Comme prévu, le programme intercepte une ValueError et affiche un message d'erreur expliquant que les formes ne sont pas alignées. C'est le comportement correct et attendu pour des formes incompatibles.

Array 'a' (shape (2, 3)):
[[1. 2. 3.]
 [4. 5. 6.]]
Array 'b' (shape (2,)): [1. 2.]

Error: operands could not be broadcast together with shapes (2,3) (2,)

Exemple pratique - Quantification vectorielle

Appliquons la diffusion à un problème pratique. La Quantification Vectorielle (VQ) est une technique utilisée dans la compression de données et la classification. Une étape clé consiste à trouver le vecteur "code" le plus proche d'un "codebook" (dictionnaire de codes) pour un vecteur "observation" donné. La diffusion rend ce calcul efficace.

Imaginons que nous ayons une observation (par exemple, le poids et la taille d'un athlète) et un codebook de différents types d'athlètes. Nous voulons trouver à quel type notre observation est la plus proche.

Remplacez le code de votre fichier broadcasting.py par le suivant :

import numpy as np

## An observation vector (e.g., weight, height)
observation = np.array([111.0, 188.0])

## A codebook of vectors
codes = np.array([[102.0, 203.0],
                  [132.0, 193.0],
                  [45.0, 155.0],
                  [57.0, 173.0]])

## Use broadcasting to subtract the observation from all codes at once
## observation (2,) is broadcast to (4, 2)
diff = codes - observation

## Calculate the squared Euclidean distance
dist_sq = np.sum(diff**2, axis=-1)

## Find the index of the minimum distance
closest_index = np.argmin(dist_sq)

## Get the closest code from the codebook
closest_code = codes[closest_index]

print("Observation:", observation)
print("Codebook:\n", codes)
print("Distances squared:", dist_sq)
print("Closest code index:", closest_index)
print("Closest code:", closest_code)

Exécutez le script dans votre terminal :

python broadcasting.py

La sortie affichera les distances au carré entre l'observation et chaque code, et identifiera le vecteur code correspondant le plus proche. L'ensemble de ce calcul a été effectué sans aucune boucle Python explicite, grâce à la diffusion.

Observation: [111. 188.]
Codebook:
 [[102. 203.]
 [132. 193.]
 [ 45. 155.]
 [ 57. 173.]]
Distances squared: [ 306.  466. 5445. 3141.]
Closest code index: 0
Closest code: [102. 203.]

Résumé

Dans ce laboratoire, vous avez appris les bases de la diffusion NumPy (broadcasting). Vous avez commencé par le cas simple de la diffusion d'un scalaire vers un tableau, puis vous êtes passé à la diffusion entre deux tableaux compatibles, et vous avez appris à reconnaître les formes incompatibles qui génèrent des erreurs. Enfin, vous avez appliqué la diffusion à un problème pratique de quantification vectorielle, démontrant sa puissance pour écrire du code propre, efficace et sans boucle pour les calculs numériques. Maîtriser la diffusion est une étape clé pour écrire du code NumPy efficace et professionnel.