CSI4506 Introduction à l’intelligence artificielle
Auteur·rice
Affiliations
Marcel Turcotte
École de science informatique et de génie électrique
Université d’Ottawa
Date de publication
13 octobre 2026
Ce carnet exécutable de façon autonome approfondit l’exemple de mise à l’échelle de la leçon 10. Il montre comment l’échelle des attributs modifie la géométrie employée par les méthodes fondées sur les plus proches voisins.
Scénario
Considérons la prédiction du prix d’une maison par régression des k plus proches voisins (KNN) à partir de deux attributs :
x_1 : nombre de pièces (petite échelle)
x_2 : superficie en pieds carrés (grande échelle)
Les trois exemples a, b et c sont choisis de façon à ce que :
sans mise à l’échelle, a soit plus proche de b, car la superficie domine;
avec une standardisation (score z), a devienne plus proche de c, car la différence du nombre de pièces compte une fois les attributs remis à l’échelle.
Données (trois maisons)
import numpy as npimport pandas as pd# Trois exemples (pièces, pieds carrés); prix pour b et c seulementpoint_names = ["a", "b", "c"]X = np.array([ [4, 1500.0], # a (requête) [8, 1520.0], # b (entraînement) [4, 1300.0], # c (entraînement)], dtype=float)prices = pd.Series([np.nan, 520_000, 390_000], index=point_names, name="prix")df = pd.DataFrame(X, columns=["pièces", "pieds_carrés"], index=point_names)display(df)display(prices.to_frame())
pièces
pieds_carrés
a
4.0
1500.0
b
8.0
1520.0
c
4.0
1300.0
prix
a
NaN
b
520000.0
c
390000.0
Les exemples b et c forment l’ensemble d’entraînement. L’exemple a constitue la requête dont nous voulons prédire le prix. Ce jeu de données volontairement minuscule illustre la géométrie, et non l’estimation d’un modèle réaliste.
Distances euclidiennes (sans mise à l’échelle)
La distance euclidienne au carré entre u et v est
\|u-v\|_2^2 = \sum_j (u_j - v_j)^2.
La distance euclidienne est sa racine carrée, \|u-v\|_2; les deux expressions classent les voisins dans le même ordre.
Lorsqu’un attribut possède une échelle beaucoup plus grande, comme la superficie, il peut dominer la somme.
from sklearn.metrics import pairwise_distancesdist_unscaled = pd.DataFrame( pairwise_distances(df.values, metric="euclidean"), index=df.index, columns=df.index)dist_unscaled
a
b
c
a
0.000000
20.396078
200.000000
b
20.396078
0.000000
220.036361
c
200.000000
220.036361
0.000000
print("Plus proche de 'a' (sans mise à l'échelle) :", dist_unscaled.loc["a"].drop("a").idxmin())
Plus proche de 'a' (sans mise à l'échelle) : b
Résultat :a est plus proche de b, car la différence de superficie domine la distance.
Mise à l’échelle appropriée pour la modélisation
Pour éviter toute fuite de données (data leakage), calculons les paramètres de mise à l’échelle à partir des seules données d’entraînement (b et c), puis transformons les données d’entraînement et la requête :
print("Plus proche de 'a' (après mise à l'échelle) :", dist_scaled.loc["a"].drop("a").idxmin())
Plus proche de 'a' (après mise à l'échelle) : c
Nouveau résultat :a est plus proche de c, car la différence du nombre de pièces compte une fois les attributs placés sur des échelles comparables.
Régression KNN : changement de prédiction
Un régresseur 1-NN prédit le prix de l’exemple d’entraînement le plus proche. Comparons le même estimateur avec et sans mise à l’échelle.
from sklearn.neighbors import KNeighborsRegressorfrom sklearn.pipeline import PipelineX_train = df.loc[train_idx].values # b, cy_train = prices.loc[train_idx].values # prix de b et cX_query = df.loc[query_idx].values # a# 1) Sans mise à l'échelleknn_plain = KNeighborsRegressor(n_neighbors=1, metric="euclidean")knn_plain.fit(X_train, y_train)pred_plain = knn_plain.predict(X_query)[0]# 2) Avec mise à l'échelle; le pipeline ajuste le scaler sur l'entraînementknn_scaled = Pipeline([ ("scaler", StandardScaler()), ("knn", KNeighborsRegressor(n_neighbors=1, metric="euclidean"))])knn_scaled.fit(X_train, y_train)pred_scaled = knn_scaled.predict(X_query)[0]pd.DataFrame( {"prédiction (sans mise à l'échelle)": [pred_plain],"prédiction (avec mise à l'échelle)": [pred_scaled],"plus proche voisin (sans mise à l'échelle)": [ train_idx[ knn_plain.kneighbors( X_query, return_distance=False )[0, 0] ] ],"plus proche voisin (avec mise à l'échelle)": [ train_idx[ knn_scaled["knn"].kneighbors( knn_scaled["scaler"].transform(X_query), return_distance=False, )[0, 0] ] ], }, index=["a"])
prédiction (sans mise à l'échelle)
prédiction (avec mise à l'échelle)
plus proche voisin (sans mise à l'échelle)
plus proche voisin (avec mise à l'échelle)
a
520000.0
390000.0
b
c
À retenir :
Sans mise à l’échelle : a ↔︎ b, donc une prédiction d’environ 520 000 $
Avec mise à l’échelle : a ↔︎ c, donc une prédiction d’environ 390 000 $
La cible de la requête est inconnue. Il ne s’agit donc pas d’une comparaison de performance. L’exemple montre que la représentation choisie a modifié le plus proche voisin et, par conséquent, la prédiction.
Pourquoi ce changement?
La distance euclidienne au carré additionne les différences au carré de chaque attribut :
\|u-v\|_2^2 = \sum_j (u_j - v_j)^2.
Un attribut à grande échelle, comme la superficie, peut éclipser les attributs à petite échelle, comme le nombre de pièces. KNN « ignore » alors presque les dimensions à petite échelle.
La standardisation (scores z) ou la mise à l’échelle min-max place les dimensions sur des échelles comparables.
Règle pratique : pour une méthode fondée sur les distances, mettez les attributs à l’échelle, sauf si leurs unités actuelles représentent délibérément l’importance relative que la distance doit leur accorder.
Distances aux exemples d’entraînement
Distances de a à {b, c} avant et après la mise à l’échelle.
def show_pair(name_from, names_to, D):return D.loc[name_from, names_to].to_frame("distance")print("Distances sans mise à l'échelle de a vers {b,c}")display(show_pair("a", ["b", "c"], dist_unscaled))print("Distances après mise à l'échelle de a vers {b,c}")display(show_pair("a", ["b", "c"], dist_scaled))
Distances sans mise à l'échelle de a vers {b,c}
distance
b
20.396078
c
200.000000
Distances après mise à l'échelle de a vers {b,c}
distance
b
2.008247
c
1.818182
Et la distance de Manhattan?
L’échelle importe également avec la distance L_1 :
\|u-v\|_1 = \sum_j |u_j - v_j|.
Les mêmes données d’entraînement peuvent être réutilisées avec metric="manhattan".
# 1) Sans mise à l'échelleknn_manhattan_plain = KNeighborsRegressor( n_neighbors=1, metric="manhattan")knn_manhattan_plain.fit(X_train, y_train)pred_manhattan_plain = knn_manhattan_plain.predict(X_query)[0]# 2) Avec mise à l'échelleknn_manhattan_scaled = Pipeline([ ("scaler", StandardScaler()), ("knn", KNeighborsRegressor(n_neighbors=1, metric="manhattan"))])knn_manhattan_scaled.fit(X_train, y_train)pred_manhattan_scaled = knn_manhattan_scaled.predict(X_query)[0]pd.DataFrame( {"prédiction (sans mise à l'échelle)": [pred_manhattan_plain],"prédiction (avec mise à l'échelle)": [pred_manhattan_scaled], }, index=["a"])
prédiction (sans mise à l'échelle)
prédiction (avec mise à l'échelle)
a
520000.0
390000.0
En résumé
Les modèles fondés sur les distances sont sensibles aux unités et aux échelles relatives de leurs attributs.
Ajustez un outil de mise à l’échelle uniquement sur les données d’entraînement, puis réutilisez-le pour les données de validation, de test et de production.
Un pipeline regroupe la mise à l’échelle et la prédiction et prévient les fuites durant la validation croisée.
La mise à l’échelle modifie la géométrie du modèle et peut donc changer les plus proches voisins et les prédictions.