NumPy : penser en tableaux
Étape 1 de ma feuille de route IA : vectorisation, formes, broadcasting et algèbre linéaire, la base de tout l'écosystème.
Tout l’écosystème scientifique de Python repose sur NumPy : pandas stocke ses colonnes dans des tableaux NumPy, scikit-learn attend des tableaux NumPy, et les tenseurs de Keras en sont les cousins directs. Avant tout modèle, il faut donc apprendre à penser en tableaux plutôt qu’en boucles.
Pourquoi pas de simples listes Python ?
Une liste Python contient des objets quelconques, éparpillés en mémoire. Un tableau NumPy (ndarray) contient des valeurs d’un seul type, rangées côte à côte. Résultat : les opérations tournent en C, sur des blocs mémoire contigus, souvent 50 à 100 fois plus vite qu’une boucle Python.
import numpy as np
prix = np.array([12.5, 8.0, 23.9, 4.2])
tva = prix * 0.18 # une opération, appliquée à tout le tableau
print(tva) # [2.25 1.44 4.302 0.756]
Pas de boucle : c’est la vectorisation, le réflexe numéro un à acquérir.
Forme, dimensions, type
Trois attributs à toujours avoir en tête :
x = np.zeros((3, 4)) # 3 lignes, 4 colonnes
x.shape # (3, 4)
x.ndim # 2
x.dtype # float64
En machine learning, la convention est presque toujours la même : une ligne = un exemple, une colonne = une caractéristique. Un jeu de 1 000 maisons décrites par 5 variables est un tableau de forme (1000, 5).
Pour changer de forme sans copier les données :
v = np.arange(12) # [0, 1, ..., 11]
m = v.reshape(3, 4) # 3 x 4
m.T.shape # (4, 3) : la transposée
Indexation et masques
m[0] # première ligne
m[:, 1] # deuxième colonne
m[1:, :2] # lignes 1 et 2, colonnes 0 et 1
notes = np.array([12, 7, 15, 9, 18])
notes[notes >= 10] # [12 15 18] : filtrage par masque booléen
notes[notes < 10] = 10 # remplacement conditionnel, sans boucle
Les masques booléens reviennent partout : filtrer des données, compter des erreurs, sélectionner des classes.
Le broadcasting
NumPy sait combiner des tableaux de formes différentes quand c’est cohérent. Exemple classique : centrer chaque colonne d’un jeu de données.
X = np.random.default_rng(0).normal(size=(100, 3))
X_centre = X - X.mean(axis=0) # (100, 3) - (3,) -> la moyenne est « diffusée »
X_centre.mean(axis=0).round(10) # [0. 0. 0.]
axis=0 agrège le long des lignes (un résultat par colonne), axis=1 le long des colonnes (un résultat par ligne). Se tromper d’axe est l’erreur la plus fréquente au début : vérifiez toujours la forme du résultat.
L’algèbre linéaire, là où tout se joue
Un modèle linéaire, c’est un produit matriciel. Avec de forme et des poids de forme :
rng = np.random.default_rng(42)
X = rng.normal(size=(200, 2))
w_vrai = np.array([3.0, -2.0])
y = X @ w_vrai + 1.0 + rng.normal(scale=0.1, size=200)
# Moindres carrés : on ajoute une colonne de 1 pour le biais
Xb = np.c_[X, np.ones(len(X))]
w, *_ = np.linalg.lstsq(Xb, y, rcond=None)
print(w.round(2)) # ≈ [ 3. -2. 1. ]
C’est exactement la régression linéaire de mon article dédié, écrite en trois lignes.
Exercices pour valider l’étape
- Normaliser un tableau
(n, d)pour que chaque colonne ait une moyenne 0 et un écart-type 1, sans boucle. - Calculer la matrice des distances euclidiennes entre tous les points d’un nuage
(n, 2)avec le broadcasting. - Réimplémenter la descente de gradient de la régression linéaire en NumPy pur.
- Charger une image en tableau
(hauteur, largeur, 3)et la passer en niveaux de gris.
Quand ces quatre exercices se font sans chercher la syntaxe à chaque ligne, l’étape est validée.
Ressources
- NumPy : the absolute basics for beginners, la documentation officielle, excellente.
- Les exercices « NumPy » et « Intro to Machine Learning » de Kaggle Learn.