Aller au contenu
English

Expliquer les prédictions

  • Leçon 22
  • 60 min
  • Module 7 : Comprendre et choisir le modèle
Leçon 22 sur 27 · Apprentissage automatique : du problème à un modèle fiableExpliquer les prédictions

Ce que vous saurez faire

Répondez à deux questions que Grace et Omar posent sur chaque modèle : « Que regarde-t-il? » et « Pourquoi a-t-il donné un score si bas à ce billet de soutien? » Répondez ensuite à une troisième question qu’ils ne posent pas, mais devraient poser : « Est-ce que la variable cause l’escalade? » Non.

Carnet JupyterFaites cette leçon dans un carnet JupyterColab · Kaggle · GitHub · en anglais

Le carnet (notebook) Jupyter contient toutes les cellules, tâches et vérifications de cette leçon, avec les instructions. Ses cellules de préparation installent les paquets et téléchargent les données et le fichier ticket_model.py du cours. Aucune configuration locale n’est nécessaire. Lisez les explications ici et exécutez le code là-bas.

Le carnet est en anglais. Colab et Kaggle ouvrent votre propre copie. Il faut un compte Google ou Kaggle gratuit. Vous êtes bloqué ? Consultez le carnet de solution.

Avant de commencerCe qu’il faut savoir et avoir sous la mainEnviron 60 minutes

Ce que vous devez savoir

Ce qu’il vous faut sur votre ordinateur (seulement pour le parcours local; le carnet n’en a pas besoin)

Testé sur macOS avec Python 3.14.6, pandas 3.0.6 et scikit-learn 1.9.1. Les commandes Windows n’ont pas été testées. Le carnet a été testé dans Jupyter.

Imaginez qu’un site immobilier estime votre appartement à 300 000. Vous posez la question évidente : pourquoi? Un bon site répond en plusieurs parties. La superficie ajoute le plus à la valeur. Le quartier en ajoute un peu. L’âge du bâtiment en enlève un peu. Cette réponse permet de vérifier les faits (« mon appartement fait 70 mètres carrés, pas 90 ») et de comprendre le nombre.

Vous posez ensuite une deuxième question : « Si je peins les murs, est-ce que l’estimation augmentera? » Ici, il faut être prudent. L’estimation augmente seulement si le modèle du site utilise la couleur des murs. Même alors, le modèle a appris ce qui accompagne les prix élevés dans les ventes passées. Il n’a pas appris ce qui rend un prix élevé.

Grace et Omar posent les mêmes questions sur le modèle d’escalade. Grace demande : « Que regarde le modèle? » Omar demande à propos d’un billet : « Pourquoi lui a-t-il donné un score si bas? » Vient ensuite la question qu’ils ne posent pas, mais devraient poser : « Si nous changeons cette variable, l’escalade disparaît-elle? » La réponse à cette dernière question est non. Cette leçon montre pourquoi.

Chaque question a son outil. Les coefficients sont les poids d’une régression logistique. Ils répondent à la question de Priya sur le sens du changement : quelles variables augmentent ou diminuent les scores, toutes choses égales? L’importance par permutation répond à la question de Grace : de quelles variables le modèle dépend-il le plus? Elle fonctionne pour tout modèle. Les contributions répondent à la question d’Omar sur un billet : combien chaque variable ajoute-t-elle ou enlève-t-elle à son score? Le tableau résume ces idées.

Question Qui la pose Outil dans cette leçon Fonctionne pour
Quelles variables augmentent ou diminuent les scores, toutes choses égales? Priya Coefficients Les modèles linéaires, comme la régression logistique
De quelles variables le modèle dépend-il le plus? Grace Importance par permutation Tout modèle
Pourquoi ce billet a-t-il reçu ce score? Omar Contributions d’un billet Les modèles linéaires (les autres modèles exigent d’autres outils)

Les trois décrivent le modèle. Aucun ne décrit le monde. Gardez cette phrase en tête; le cas d’échec à la fin montre ce qui arrive quand une équipe l’oublie.

Le modèle final est une régression logistique. Pour chaque billet, il multiplie chaque valeur préparée par un poids. Il additionne les produits et une ordonnée à l’origine (une valeur de départ fixe). La fonction logistique transforme la somme en un score de 0 à 1. Vous avez vu cela dans Comprendre la classification. Un coefficient est le poids d’une colonne préparée.

Les nombres préparés sont mis à l’échelle : le pipeline soustrait la moyenne d’entraînement et divise par l’écart-type d’entraînement. Une valeur mise à l’échelle de 1 signifie donc « un écart-type au-dessus du billet d’entraînement moyen ». La valeur 0 signifie « moyen ». Voici deux billets avec les vrais coefficients du modèle final pour priority (−0,64) et word_count (+0,40). La moyenne et l’écart-type d’entraînement sont de 2,37 et 0,72 pour priority, et de 106,13 et 70,81 pour word_count.

Billet A Billet B
priority 1 → (1 − 2,37) ÷ 0,72 = −1,91 3 → (3 − 2,37) ÷ 0,72 = +0,88
part de la priorité = −0,64 × valeur mise à l’échelle −0,64 × −1,91 = +1,22 −0,64 × 0,88 = −0,56
word_count 300 → (300 − 106,13) ÷ 70,81 = +2,74 40 → (40 − 106,13) ÷ 70,81 = −0,93
part des mots = 0,40 × valeur mise à l’échelle 0,40 × 2,74 = +1,10 0,40 × −0,93 = −0,37
Somme des deux parts +2,32 −0,94

Le billet A, urgent et accompagné d’un long message, obtient une somme bien plus grande, donc un score plus élevé. Regardez la ligne de priorité. Le coefficient est négatif, mais la part du billet A est positive. La priorité 1 est la plus urgente, et sa valeur mise à l’échelle est sous la moyenne. Un nombre négatif multiplié par un nombre négatif donne un nombre positif. Ne lisez donc jamais un coefficient seul : lisez-le avec la valeur qu’il multiplie.

Voici le sens d’un coefficient en une phrase : si une valeur préparée augmente de 1 et que toutes les autres restent identiques, la somme change du coefficient. Les mots « toutes les autres restent identiques » comptent. Vous verrez pourquoi plus bas.

Exemple détaillé : les coefficients du modèle final

Section intitulée « Exemple détaillé : les coefficients du modèle final »

Priya ajuste le modèle final sur les données d’entraînement, comme dans les leçons précédentes, et calcule les scores du mois de validation :

from ticket_model import FEATURES, TARGET, build_pipeline, load
train, valid = load("train"), load("valid")
pipeline = build_pipeline().fit(train[FEATURES], train[TARGET])
valid["score"] = pipeline.predict_proba(valid[FEATURES])[:, 1]
print("fitted on", len(train), "tickets; scored", len(valid), "validation tickets")

La sortie :

fitted on 16939 tickets; scored 1179 validation tickets

Le pipeline nomme chaque colonne préparée. get_feature_names_out() renvoie les noms, et coef_ contient les coefficients dans le même ordre. Priya les trie du plus négatif au plus positif. Avant de regarder, faites une prédiction : quelle équipe a le coefficient le plus négatif?

names = pipeline.named_steps["prepare"].get_feature_names_out()
coefficients = pd.Series(pipeline.named_steps["model"].coef_[0], index=names).sort_values()
print(coefficients.round(2).to_string())

La sortie :

categories__team_account -1.02
categories__segment_home -0.71
numbers__priority -0.64
categories__channel_web_form -0.51
categories__team_delivery -0.39
categories__team_returns -0.38
categories__region_north -0.37
categories__channel_chat -0.30
categories__channel_email -0.30
numbers__prior_tickets_90d -0.29
categories__segment_trade -0.26
categories__channel_unknown -0.25
categories__region_unknown -0.23
categories__region_south -0.21
categories__region_east -0.17
categories__region_west -0.14
categories__segment_business -0.14
numbers__missingindicator_customer_tenure_days -0.07
numbers__created_hour -0.04
categories__segment_guest -0.02
numbers__customer_tenure_days -0.02
numbers__missingindicator_order_value 0.02
numbers__order_value 0.15
categories__channel_phone 0.23
categories__team_warranty 0.28
categories__team_payment 0.38
numbers__word_count 0.40
numbers__prior_escalations_90d 0.43

Comment lire les deux types de noms :

  • categories__team_account −1,02. Une colonne encodée à chaud vaut 1 quand le billet appartient à cette catégorie, et 0 sinon. Comparez les catégories d’une même colonne : un billet de compte reçoit −1,02, un billet de paiement +0,38. La différence est de 1,40. Un billet de compte obtient une somme inférieure de 1,40 à celle d’un billet de paiement aux autres valeurs identiques. Cela correspond aux sous-groupes de la dernière leçon : les comptes ont le moins d’escalades, les paiements le plus.
  • numbers__prior_escalations_90d +0,43. Un écart-type de plus d’escalades antérieures (2,16 escalades) ajoute 0,43 à la somme, toutes les autres valeurs restant identiques.
  • numbers__priority −0,64. Un numéro de priorité plus grand signifie moins urgent (la priorité 1 est la plus urgente). Un coefficient négatif signifie donc : plus urgent, plus de risque.

Les coefficients concordent avec ce que Grace sait de ses équipes. C’est une vérification utile : un coefficient au signe surprenant justifie un examen plus attentif. L’un d’eux est surprenant.

prior_tickets_90d a un coefficient de −0,29 : plus de billets dans les 90 derniers jours font baisser le score. Tomás le comprend ainsi : « Les clients qui écrivent souvent sont calmes. Ils font moins d’escalades. » Priya vérifie les données d’entraînement avant d’être d’accord. Elle répartit les billets antérieurs en quatre tranches. Pour chaque tranche, elle calcule le taux d’escalade de tous les billets, puis des billets sans escalade dans les 90 jours précédents.

band = pd.cut(train["prior_tickets_90d"], [-1, 0, 2, 5, 1000], labels=["0", "1-2", "3-5", "6 or more"])
none_before = train["prior_escalations_90d"] == 0
print("correlation:", round(train["prior_tickets_90d"].corr(train["prior_escalations_90d"]), 2))
print(pd.DataFrame({
"all tickets": train.groupby(band, observed=True)[TARGET].mean(),
"no escalation before": train[none_before].groupby(band[none_before], observed=True)[TARGET].mean(),
}).round(3))

La sortie :

correlation: 0.73
all tickets no escalation before
prior_tickets_90d
0 0.104 0.104
1-2 0.101 0.092
3-5 0.100 0.082
6 or more 0.137 0.086

Lisez les deux colonnes :

  • Tous les billets : les clients ayant au moins 6 billets antérieurs font plus d’escalades (13,7 %) que ceux sans billet antérieur (10,4 %). Tomás se trompe sur les données brutes.
  • Aucune escalade antérieure : parmi les clients dont les billets antérieurs n’ont pas été escaladés, plus de billets accompagnent moins d’escalades (de 10,4 % à 8,6 %).
  • Corrélation de 0,73 : les clients ayant beaucoup de billets ont aussi beaucoup d’escalades antérieures. Les deux colonnes varient ensemble.

Le coefficient répond à la deuxième question : « Si prior_escalations_90d reste fixe, qu’ajoutent des billets supplémentaires? » Parmi les clients ayant le même historique d’escalades, celui qui écrit souvent pour de petits problèmes présente un risque légèrement moindre. La première question, « les clients ayant beaucoup de billets font-ils plus d’escalades? », a pour réponse oui. Le modèle capte ce risque grâce à prior_escalations_90d (+0,43).

Voici la preuve que le coefficient dépend des autres variables : Priya ajuste prior_tickets_90d dans trois modèles.

Modèle Coefficient de prior_tickets_90d
prior_tickets_90d seul +0,18
Les 10 variables du cours SQL et préparation des données pour l’IA (sans prior_escalations_90d) +0,07
Le modèle final, 11 variables −0,29

La même colonne change de signe quand une colonne liée entre dans le modèle. Un coefficient n’est donc jamais « l’effet d’une variable ». C’est l’effet compte tenu des autres variables de ce modèle.

Comment savoir à quel point une équipe de soccer dépend d’un joueur? Regardez un match sans ce joueur. Si l’équipe joue beaucoup moins bien, elle dépend de lui; si rien ne change, elle n’en dépend pas.

Les coefficients fonctionnent seulement pour les modèles linéaires, et leur taille dépend de l’échelle. La question de Grace, « de quoi le modèle dépend-il? », a un outil qui fonctionne pour tout modèle. Il utilise la même idée que le match de soccer. L’importance par permutation mélange une colonne pour qu’elle ne corresponde plus à ses billets. Elle mesure ensuite à quel point le modèle se dégrade. Une grande perte signifie que le modèle dépend de cette colonne.

D’abord, huit billets et la règle « signaler la priorité 1 », évaluée avec l’exactitude. Pour mélanger à la main, inversez l’ordre d’une colonne.

tiny = pd.DataFrame({"priority": [1, 3, 1, 2, 3, 1, 2, 3],
"word_count": [120, 40, 200, 80, 60, 150, 90, 30],
"escalated": [1, 0, 1, 0, 0, 0, 0, 0]})
def rule_accuracy(df):
"""The share of tickets where the rule "flag priority 1" is right."""
return ((df["priority"] == 1).astype(int) == tiny["escalated"]).mean()
print("as recorded:", rule_accuracy(tiny))
print("priority shuffled:", rule_accuracy(tiny.assign(priority=tiny["priority"].values[::-1])))
print("word_count shuffled:", rule_accuracy(tiny.assign(word_count=tiny["word_count"].values[::-1])))

La sortie :

as recorded: 0.875
priority shuffled: 0.625
word_count shuffled: 0.875

Vérifiez à la main. Dans l’ordre enregistré, la règle signale les billets 1, 3 et 6. Les billets 1 et 3 ont été escaladés, mais pas le billet 6. Les cinq autres sont correctement laissés sans signalement : 7 réponses correctes sur 8. Après inversion, les priorités sont 3, 2, 1, 3, 2, 1, 3, 1. La règle signale maintenant les billets 3, 6 et 8, et manque le billet 1 : 5 réponses correctes sur 8. L’importance de priority est de 0,875 − 0,625 = 0,25. La règle ne lit jamais word_count, donc le mélanger coûte 0.

Sur le vrai modèle, permutation_importance de scikit-learn mélange chacune des 11 colonnes 10 fois au hasard. Il rapporte la moyenne et l’écart-type de la perte. Priya utilise la précision moyenne, la métrique de classement du module Évaluer les modèles supervisés, sur les données de validation. Elle prédit d’abord : la priorité arrivera en tête. Cela prend environ 10 secondes.

from sklearn.inspection import permutation_importance
result = permutation_importance(pipeline, valid[FEATURES], valid[TARGET], scoring="average_precision",
n_repeats=10, random_state=0)
importance = pd.DataFrame({"mean": result.importances_mean, "std": result.importances_std}, index=FEATURES)
print(importance.sort_values("mean", ascending=False).round(3))

La sortie :

mean std
priority 0.111 0.016
prior_escalations_90d 0.075 0.014
team 0.074 0.009
word_count 0.041 0.011
prior_tickets_90d 0.015 0.004
segment 0.014 0.007
order_value 0.013 0.004
channel 0.005 0.008
region 0.002 0.002
created_hour 0.001 0.002
customer_tenure_days -0.002 0.001
Importance par permutation des cinq colonnes les plus importantes du modèle final, sur le mois de validation (moyenne de 10 mélanges). La précision moyenne du modèle est de 0,391.
Description textuelle du graphique

Un diagramme à colonnes montre la précision moyenne perdue après le mélange d’une colonne, pour les cinq colonnes les plus importantes. priority : 0,111. prior_escalations_90d : 0,075. team : 0,074. word_count : 0,041. prior_tickets_90d : 0,015. Les six autres colonnes perdent 0,014 ou moins, et customer_tenure_days ne perd rien (−0,002).

Comment le lire :

  • L’échelle. La précision moyenne du modèle en mai est de 0,391. Sans priority, elle baisse de 0,111, pour atteindre environ 0,28. Sans customer_tenure_days, rien ne change. Sa valeur de −0,002 signifie que la colonne mélangée a très légèrement amélioré le modèle, ce qui relève forcément du hasard. Le modèle ne dépend pas de l’ancienneté.
  • Une catégorie compte une fois. team est mélangée comme une seule colonne, donc ses cinq colonnes encodées à chaud changent ensemble. C’est pourquoi l’importance a 11 lignes et les coefficients en ont 28.
  • Les colonnes liées se partagent le mérite. prior_tickets_90d a une faible importance (0,015), en partie parce que prior_escalations_90d contient presque la même information. Si vous mélangez l’une de deux colonnes liées, le modèle dispose encore de l’autre. Une faible importance ne prouve donc pas qu’une colonne est inutile.

Omar pose une question sur un billet : T-117033, de priorité 3, traité par l’équipe des retours et escaladé en mai. Le modèle lui a donné un score de 0,029. Il fait donc partie des 55 escalades manquées avec un score bas. « Pourquoi si bas? »

Pour un modèle linéaire, le score d’un billet se décompose exactement. La contribution d’une colonne est son coefficient multiplié par la valeur préparée de ce billet. L’ordonnée à l’origine plus toutes les contributions donne la somme. La fonction logistique transforme cette somme en score :

score=11+e−(intercept+contribution1+contribution2+… )\text{score} = \frac{1}{1 + e^{-(\text{intercept} + \text{contribution}_1 + \text{contribution}_2 + \dots)}}

Priya écrit contributions() et affiche les colonnes dont la valeur du billet n’est pas 0. Les autres colonnes encodées à chaud valent 0 et ne contribuent donc pas :

def contributions(pipeline, ticket):
"""Coefficient times prepared value, for each column of one ticket."""
prepare, model = pipeline.named_steps["prepare"], pipeline.named_steps["model"]
values = np.asarray(prepare.transform(ticket[FEATURES]))[0]
table = pd.DataFrame({"value": values, "coefficient": model.coef_[0]},
index=prepare.get_feature_names_out())
table["contribution"] = table["value"] * table["coefficient"]
return table
ticket = valid[valid["ticket_id"] == "T-117033"]
table = contributions(pipeline, ticket)
print(table[table["value"] != 0].sort_values("contribution").round(2))

La sortie :

value coefficient contribution
numbers__prior_tickets_90d 2.04 -0.29 -0.59
numbers__priority 0.88 -0.64 -0.57
numbers__word_count -1.00 0.40 -0.40
categories__team_returns 1.00 -0.38 -0.38
categories__channel_email 1.00 -0.30 -0.30
categories__segment_trade 1.00 -0.26 -0.26
categories__region_east 1.00 -0.17 -0.17
numbers__customer_tenure_days 1.87 -0.02 -0.03
numbers__missingindicator_order_value -0.41 0.02 -0.01
numbers__created_hour -0.14 -0.04 0.01
numbers__missingindicator_customer_tenure_days -0.11 -0.07 0.01
numbers__order_value 0.67 0.15 0.10
numbers__prior_escalations_90d 0.84 0.43 0.36

Elle les additionne ensuite et vérifie la somme avec le modèle :

intercept = pipeline.named_steps["model"].intercept_[0]
total = intercept + table["contribution"].sum()
print("intercept", round(intercept, 2), "+ contributions", round(table["contribution"].sum(), 2), "=", round(total, 2))
print("score from the sum:", round(1 / (1 + np.exp(-total)), 3), "| predict_proba:", round(ticket["score"].iloc[0], 3))

La sortie :

intercept -1.28 + contributions -2.23 = -3.52
score from the sum: 0.029 | predict_proba: 0.029

La décomposition est exacte : −1,28 − 2,23 = −3,52, et 1 ÷ (1 + e3,52) = 0,029, le score du modèle. Lisez le billet :

  • Le client a écrit 25 billets dans les 90 derniers jours (2,04 écarts-types au-dessus de la moyenne). Cela diminue la somme de 0,59, la plus grande baisse. C’est le piège du signe de la dernière section, dans un seul billet.
  • Le client a aussi eu 3 escalades dans les 90 derniers jours. Cela augmente la somme de 0,36, mais pas assez.
  • La priorité 3 (−0,57), un court message de 35 mots (−0,40) et l’équipe des retours (−0,38) la diminuent davantage.

Priya dit à Omar : « Le modèle a donné un score bas à ce billet pour quatre raisons principales : priorité 3, beaucoup de billets récents du client, un court message et l’équipe des retours. Pour ce client, les nombreux billets n’étaient pas un signe de calme. » C’est une affirmation vraie sur le modèle. Ce n’est pas une affirmation sur la raison de l’escalade du billet.

Il est facile de lire une contribution comme une raison. Ce n’en est pas une, pour trois raisons :

  1. Elle compare avec un billet moyen, pas avec « rien ». Une valeur mise à l’échelle de 0 signifie « billet d’entraînement moyen ». La contribution de −0,59 de prior_tickets_90d signifie « 25 billets dépassent la moyenne de 7,71 ». Choisissez une autre référence, et chaque contribution change. Pour les colonnes encodées à chaud, la référence n’est même pas claire. Chaque catégorie a son coefficient, et seules les différences entre catégories ont un sens fixe.
  2. Les colonnes liées se partagent le mérite arbitrairement. prior_tickets_90d et prior_escalations_90d varient ensemble (corrélation de 0,73). Pour ce billet, le modèle attribue −0,59 à l’une et +0,36 à l’autre. Un autre ajustement sur des données légèrement différentes pourrait répartir la même information autrement.
  3. C’est le point de vue du modèle, pas l’histoire du billet. Le billet a été escaladé pour une raison qu’aucune des 11 colonnes ne consigne. Le problème du client était peut-être grave. Ou le client a tendance à faire des escalades, sans qu’aucune colonne le montre. Les contributions expliquent pourquoi le modèle l’a manqué. Elles ne peuvent pas expliquer pourquoi le client a fait une escalade.

Le nom honnête est donc « ce qui a fait varier le score du modèle ». Écrivez-le ainsi dans un rapport.

Faites la même chose pour T-117014, un billet de soutien de priorité 1 lié à un paiement, reçu par téléphone. Le modèle l’a signalé, et le billet a été transmis au niveau supérieur.

  1. Exécutez les cellules de l’exemple détaillé pour que pipeline, contributions() et intercept existent.
  2. Sélectionnez le billet : ticket2 = valid[valid["ticket_id"] == "T-117014"].
  3. Créez son tableau : table2 = contributions(pipeline, ticket2).
  4. Trouvez la colonne ayant la plus grande contribution : table2["contribution"].idxmax(). Vous devriez voir numbers__priority (priorité).
  5. Additionnez l’ordonnée à l’origine et toutes les contributions, puis appliquez la fonction logistique. Vous devriez obtenir un score de 0,38.
  6. Affichez les lignes dont la valeur n’est pas 0, triées par contribution. Trouvez la plus grande contribution négative.
Afficher la réponse
value coefficient contribution
categories__segment_home 1.00 -0.71 -0.71
categories__region_east 1.00 -0.17 -0.17
numbers__word_count -0.40 0.40 -0.16
numbers__prior_escalations_90d -0.09 0.43 -0.04
numbers__created_hour 0.90 -0.04 -0.04
numbers__order_value -0.12 0.15 -0.02
numbers__missingindicator_order_value -0.41 0.02 -0.01
numbers__customer_tenure_days 0.17 -0.02 -0.00
numbers__missingindicator_customer_tenure_days -0.11 -0.07 0.01
numbers__prior_tickets_90d -0.32 -0.29 0.09
categories__channel_phone 1.00 0.23 0.23
categories__team_payment 1.00 0.38 0.38
numbers__priority -1.91 -0.64 1.23

La somme est −1,28 + 0,80 = −0,49, et 1 ÷ (1 + e0,49) = 0,380. Ce résultat dépasse le seuil de 0,19. Le modèle signale donc le billet. La priorité 1 ajoute +1,23, de loin la plus forte hausse. L’équipe des paiements (+0,38) et le canal téléphonique (+0,23) ajoutent encore au score. Le segment résidentiel le réduit le plus (−0,71) : les clients résidentiels font moins souvent appel au niveau supérieur que les clients professionnels et les entreprises.

À vous de jouer : classer selon l’aire sous la courbe ROC

Section intitulée « À vous de jouer : classer selon l’aire sous la courbe ROC »

Changez une condition : la métrique. Exécutez de nouveau permutation_importance avec scoring="roc_auc", sans changer les autres paramètres. Avant de l’exécuter, faites une prédiction : les trois premières colonnes gardent-elles le même ordre?

Afficher la réponse
mean std
priority 0.089 0.016
team 0.048 0.011
prior_escalations_90d 0.040 0.005
word_count 0.030 0.010
prior_tickets_90d 0.011 0.005
channel 0.008 0.004
segment 0.007 0.003
order_value 0.005 0.003
region 0.002 0.001
created_hour 0.001 0.001
customer_tenure_days -0.000 0.001

La priorité reste première. team (équipe) et prior_escalations_90d (transmissions au niveau supérieur dans les 90 derniers jours) échangent leurs places. Avec l’aire sous la courbe ROC, team perd 0,048 et prior_escalations_90d, 0,040. Avec la précision moyenne, leurs valeurs étaient de 0,074 et 0,075. Elles étaient à égalité compte tenu de leurs écarts-types (0,009 et 0,014).

La raison : l’aire sous la courbe ROC récompense le classement de tous les billets. La précision moyenne récompense le classement en tête de liste, où se trouvent les billets signalés. Voici une raison probable du changement : prior_escalations_90d aide surtout en tête de liste. Les clients ayant de nombreuses transmissions antérieures au niveau supérieur y obtiennent les scores les plus élevés. L’importance est donc toujours une « importance pour une métrique ». Utilisez la métrique qui correspond à la décision, comme au module 3 : pour Larkfield, la précision moyenne.

Cas d’échec : l’importance n’est pas une cause

Section intitulée « Cas d’échec : l’importance n’est pas une cause »

Erreur courante

Changer la caractéristique, changer le résultat?

Symptôme : Tomás lit le tableau d’importance et prépare un plan : « La priorité est la caractéristique la plus importante. Si les agents attribuent la priorité 1 à moins de billets, le modèle en signale moins. Moins de billets seront alors transmis au niveau supérieur. » Omar propose un deuxième plan : « Les longs messages augmentent le score. Limitons le formulaire de contact à 50 mots. » Priya teste le premier plan sur T-117014 :

changed = ticket2.assign(priority=3)
print("score as written:", round(pipeline.predict_proba(ticket2[FEATURES])[0, 1], 3))
print("score with priority 3:", round(pipeline.predict_proba(changed[FEATURES])[0, 1], 3))
score as written: 0.38
score with priority 3: 0.092

Le score passe de 0,380 à 0,092, sous le seuil. Le plan « fonctionne » pour le score.

Cause : le modèle a appris quelles valeurs vont avec une transmission au niveau supérieur dans les données passées. Il n’a pas appris ce qui la cause. Un agent attribue la priorité 1 parce que le problème semble grave. Le problème grave mène à la transmission au niveau supérieur. Changez la priorité, et le problème de paiement du client reste le même. Le billet attend maintenant dans la file normale, et il nécessite toujours un agent expérimenté. C’est aussi vrai pour le nombre de mots. Dans les données d’entraînement, 20,4 % des billets de plus de 200 mots sont transmis au niveau supérieur. Ce taux est de 6,0 % pour les billets de 1 à 50 mots. Un long message est un signe de problème complexe. Un formulaire plus court cache ce signe; il ne supprime pas le problème. C’est l’idée fausse qui consiste à « interpréter l’importance des caractéristiques comme un lien de causalité ».

Solution : dites ce que les nombres représentent : « Le modèle dépend de la priorité; c’est le signal de risque le plus fort. » Pour savoir ce qui cause les transmissions au niveau supérieur, vous avez besoin d’un autre type d’étude. Par exemple, un changement contrôlé avec un groupe de comparaison, présenté dans Comparer des options du cours de mathématiques. Avant de modifier la façon de consigner une caractéristique, vérifiez l’effet de ce changement sur les entrées du modèle. Si les agents changent leur façon d’attribuer la priorité, l’entrée la plus importante du modèle change de sens. Vous devez alors vérifier le modèle de nouveau.

Conséquence dans le travail en IA : une équipe qui traite l’importance comme une cause modifie les entrées plutôt que le problème. Les nombres du modèle s’améliorent, et les clients attendent plus longtemps. Pire encore, le changement éloigne les données de ce que le modèle a appris. Ses scores n’ont donc plus le même sens.

Vérifiez votre compréhension

Le coefficient de prior_tickets_90d est −0,29. Omar conclut que les clients ayant beaucoup de billets font moins souvent appel au niveau supérieur. Que disent les données, et que signifie vraiment le coefficient?

Afficher la réponse

Les données disent le contraire pour l’ensemble des billets. Les clients ayant au moins 6 billets antérieurs font appel au niveau supérieur dans 13,7 % des cas. Ce taux est de 10,4 % pour ceux qui n’en ont aucun. Le coefficient signifie ceci : si prior_escalations_90d et les autres caractéristiques restent fixes, davantage de billets antérieurs réduisent légèrement la somme. Les deux colonnes sont liées (corrélation de 0,73). Le modèle capte le risque supplémentaire des clients qui contactent souvent le soutien grâce à prior_escalations_90d (+0,43). Seule, prior_tickets_90d (billets dans les 90 derniers jours) a un coefficient de +0,18. Un coefficient dépend des autres caractéristiques du modèle.

Vérifiez votre compréhension

Faites une prédiction : vous mélangez customer_tenure_days dans les données de validation, puis évaluez de nouveau le modèle. Quelle précision moyenne perdez-vous? Qu'est-ce que cela vous apprend, et qu'est-ce que cela ne vous apprend pas?

Afficher la réponse

Aucune : l’importance par permutation est de −0,002. Une colonne mélangée ne peut pas vraiment aider le modèle. Une petite valeur négative relève donc du hasard. Cela vous indique que ce modèle ne dépend pas de l’ancienneté. Cela ne signifie pas que l’ancienneté n’a aucun lien avec la transmission au niveau supérieur, ni qu’aucun modèle ne peut l’utiliser. Une colonne liée pourrait porter la même information, et un autre modèle pourrait l’utiliser.

Vérifiez votre compréhension

Les contributions de T-117033 montrent que prior_tickets_90d a réduit son score de 0,59. Omar écrit dans un rapport : le billet n'a pas été signalé parce que le client écrit souvent, ce qui rendait la transmission au niveau supérieur peu probable. Corrigez sa phrase.

Afficher la réponse

Une meilleure phrase : « Le modèle a donné un faible score à ce billet (0,029). La plus forte baisse vient des 25 billets du client dans les 90 derniers jours, contre une moyenne d’environ 8. La priorité 3 et un court message ont aussi réduit le score. » Trois corrections. Premièrement, la contribution compare le billet au billet moyen des données d’entraînement, et non à zéro. Deuxièmement, elle décrit ce qui a changé le score du modèle. Elle n’explique pas pourquoi le billet a été transmis au niveau supérieur ou non. Troisièmement, la répartition entre les colonnes liées (prior_tickets_90d, prior_escalations_90d) n’est pas un fait sur le client. Le billet a bien été transmis au niveau supérieur, pour une raison qu’aucune colonne ne consigne.

Récapitulatif

  • Un coefficient est le changement de la somme quand une valeur préparée augmente de 1 et que les autres restent fixes. Lisez-le avec la valeur : la priorité 1 a un coefficient négatif et une contribution positive.
  • Le piège du signe : prior_tickets_90d vaut +0,18 seule, +0,07 avec les 10 caractéristiques du cours de SQL, et −0,29 dans le modèle final. Le modèle final maintient prior_escalations_90d (corrélation de 0,73) fixe.
  • L’importance par permutation mélange une colonne et mesure la baisse de la métrique. En précision moyenne : priorité, 0,111; prior_escalations_90d, 0,075; équipe, 0,074. Elle fonctionne pour tout modèle, dépend de la métrique et répartit l’importance entre les colonnes liées.
  • Les contributions d’un billet s’additionnent exactement pour donner son score : T-117033 obtient −1,28 − 2,23 = −3,52, soit un score de 0,029. Elles décrivent le modèle, par comparaison avec un billet moyen. Elles ne sont ni une raison ni une cause.
  • Changer une caractéristique change le score, pas le problème du client.

Vous utiliserez ensuite ces explications pour répondre à une question plus vaste. Quand un modèle simple, que les gens peuvent lire et vérifier, est-il le meilleur choix? Les banques, les hôpitaux et les assureurs font face à cette question chaque jour. Certaines lois exigent des réponses que seul un modèle lisible fournit facilement.

Leçon suivante : Quand un modèle simple est meilleur