Classification, entropie croisée et l’art de ne pas se raconter d’histoires
Construisez un classifieur logistique, puis découvrez pourquoi 98 % d’accuracy peut masquer un modèle qui ne trouve rien.
Dans cet article
Un modèle qui répond cette pièce est bonne pour chaque pièce sortant du tapis a raison 98,15 % du temps. Il est aussi inutile : sur les 74 pièces défectueuses de l’ensemble de test, il n’en détecte aucune.
Les deux phrases décrivent le même modèle. La distance entre elles, c’est ce chapitre.
La première moitié construit le classifieur. Il ne lui faut presque rien de nouveau : le chapitre 2 a donné la recette pour transformer une hypothèse sur la façon dont les données sont produites en fonction de perte, et le chapitre 3 a donné la mécanique pour descendre la pente de n’importe quelle perte fournie par cette recette. Appliquez les deux à une question oui/non, et la régression logistique apparaît, avec une idée nouvelle — un logit — qui resservira au chapitre 17.
La seconde moitié est la plus difficile. À partir d’ici, tout le reste du cours est jugé par un nombre que quelqu’un a mesuré, et si vous ne savez pas distinguer une vraie amélioration d’un artefact de mesure, tous les chapitres suivants ne sont que décoration. Donc : la matrice de confusion, la précision et le rappel, les trois découpages, les fuites, et la question à laquelle presque personne ne répond honnêtement — combien d’exemples de test me faut-il vraiment ?
Les calculs ici portent sur 20 000 lignes, ils sont donc vectorisés partout — NumPy fait le travail depuis le chapitre 2, et à partir de maintenant cela ne vaut plus la peine de le rappeler.
Le tapis, avec une question plus rare
Lien vers la section : Le tapis, avec une question plus rareMême usine qu’au chapitre 1, question plus difficile. Au lieu de accepter ou rejeter, la question est cette pièce est-elle défectueuse — et les défauts sont rares, ce qui rend la moitié consacrée à la mesure difficile et la moitié consacrée à la modélisation faussement simple.
import numpy as np
rng = np.random.default_rng(4)
N = 20_000
width = rng.normal(22.0, 0.9, N) # millimetres
weight = rng.normal(57.0, 3.0, N) # grams
z_true = -5.90 + 1.90 * (width - 22.0) + 0.42 * (weight - 57.0)
y = (rng.random(N) < 1 / (1 + np.exp(-z_true))).astype(float)
perm = rng.permutation(N)
train, val, test = perm[:12_000], perm[12_000:16_000], perm[16_000:]N = 20000 defects = 337 base rate = 0.0169
defects per split = 203 60 74Trois découpages, pas deux. La raison mérite sa propre section et l’obtient plus bas ; pour l’instant, entraînez sur le premier, ajustez sur le deuxième, et ne regardez pas le troisième.
Les features sont standardisées — moyenne soustraite, division par l’écart type — en utilisant uniquement les statistiques de l’entraînement, pour la raison que le chapitre 1 a démontrée avec la borne de convergence du perceptron : des données non centrées rendent la géométrie hostile. La question des lignes à partir desquelles vous avez le droit de calculer cette moyenne deviendra concrète plus loin dans ce chapitre.
D’un verdict à une probabilité
Lien vers la section : D’un verdict à une probabilitéLe perceptron renvoyait un signe. Un signe ne peut pas distinguer rejeter de rejeter, mais de justesse, et cette différence est exactement ce dont une usine a besoin pour décider quelles pièces un humain doit réinspecter en premier.
Suivez donc littéralement la recette du chapitre 2. Écrivez ce que vous affirmez sur la façon dont une étiquette est produite, prenez la vraisemblance, prenez le logarithme, changez le signe, et vous avez une perte. Pour un résultat oui/non, l’affirmation est une distribution de Bernoulli : il existe une probabilité que la pièce soit défectueuse, et
ce qui est simplement une façon compacte d’écrire « si , et si ». Prenez le logarithme de cela et changez le signe, et la perte pour un exemple est
C’est l’entropie croisée binaire. Elle n’a pas été choisie parce qu’elle est pratique ; c’est la log-vraisemblance négative de la seule distribution qu’un tirage pile ou face puisse avoir. Rien d’autre n’était disponible.
Il manque encore l’origine de . Le modèle calcule une somme pondérée , qui est un nombre réel pouvant parcourir toute la droite, tandis qu’une probabilité doit vivre dans . La fonction qui fait le passage entre les deux est la sigmoïde logistique :
logit -4.0 -> p = 0.0180 loss when y=1 and p=0.9 : 0.1054
logit -1.0 -> p = 0.2689 loss when y=1 and p=0.5 : 0.6931
logit 0.0 -> p = 0.5000 loss when y=1 and p=0.01 : 4.6052
logit 4.0 -> p = 0.9820Lisez la colonne de droite comme une liste de prix. Avoir raison avec 90 % de confiance coûte 0,105. Refuser de s’engager coûte 0,693 — soit , le prix d’un haussement d’épaules. Avoir tort avec confiance coûte 4,6, quarante-quatre fois plus, et le prix augmente sans limite à mesure que le modèle devient plus certain de son erreur. L’entropie croisée ne compte pas seulement les erreurs : elle facture l’arrogance.
Le gradient est la prédiction moins la vérité
Lien vers la section : Le gradient est la prédiction moins la véritéLe chapitre 3 disait : pour entraîner quoi que ce soit, obtenez la dérivée de la perte par rapport à chaque paramètre. Faites-le pour un exemple. Avec et :
Afficher les détails
Les deux lignes qui font disparaître le désordre. La sigmoïde a une dérivée inhabituellement agréable, . Et la perte se dérive en
Multipliez les deux avec la règle de la chaîne et le apparaît une fois au numérateur et une fois au dénominateur. Il s’annule exactement, et est ce qui reste. Cette annulation n’est pas une coïncidence — c’est ce qui se produit chaque fois que la perte est la log-vraisemblance négative d’une distribution et que la fonction de sortie est celle que cette distribution utilise naturellement. Ce couplage porte un nom — un modèle linéaire généralisé — et le gradient propre en est l’empreinte.1
La mise à jour est donc prédiction moins vérité, multiplié par l’entrée. Rien d’autre. Voici l’entraîneur complet, qui est la descente du chapitre 3 avec une seule ligne modifiée :
def sigmoid(z):
return np.where(z >= 0, 1.0 / (1.0 + np.exp(-z)),
np.exp(np.minimum(z, 0)) / (1.0 + np.exp(np.minimum(z, 0))))
def fit_logistic(X, y, lr=0.5, epochs=4000):
w, b = np.zeros(X.shape[1]), 0.0
for _ in range(epochs):
p = sigmoid(X @ w + b)
g = p - y
w -= lr * (X.T @ g) / len(y)
b -= lr * g.sum() / len(y)
return w, bLe np.where dans sigmoid n’est pas cosmétique. Calculer directement déborde pour de grandes valeurs négatives de ; la branche choisit la forme algébriquement identique qui garde l’exposant négatif. C’est la boîte des nombres à virgule flottante du chapitre 2 qui réclame sa première dette, et elle en réclamera une plus grosse dans deux sections.
Pourquoi pas l’erreur quadratique, et pourquoi la réponse parle du gradient
Lien vers la section : Pourquoi pas l’erreur quadratique, et pourquoi la réponse parle du gradientL’explication standard pour préférer l’entropie croisée à l’erreur quadratique est l’argument de vraisemblance ci-dessus : l’erreur quadratique est ce que vous obtenez en supposant un bruit gaussien, les étiquettes ne sont pas gaussiennes, donc ne le faites pas. C’est correct et cela ne convainc personne, parce que vous pouvez écrire au-dessus d’une sigmoïde et l’entraînement fonctionnera.
L’argument qui porte concerne le gradient. Placez l’erreur quadratique au-dessus d’une sigmoïde et la règle de la chaîne donne
Ce supplémentaire est celui qui s’annulait tout à l’heure. Maintenant il ne s’annule plus, et il tend vers zéro chaque fois que le modèle est confiant — y compris lorsque le modèle a tort avec confiance. Évaluez les deux pour quelques scores, pour un exemple dont la vraie étiquette est 1 :
| score | entropie croisée | erreur quadratique | ratio | |
|---|---|---|---|---|
| 0,000335 | 1 491 | |||
| 0,017986 | 28,3 | |||
| 0,119203 | 4,8 | |||
| 0,500000 | 2,0 | |||
| 0,880797 | 4,8 |
À , le modèle a autant tort qu’il est possible d’avoir tort, et l’erreur quadratique répond avec un gradient 1 491 fois plus petit que celui de l’entropie croisée. Plus l’erreur est grave, moins le modèle en apprend. Le gradient de l’entropie croisée, lui, sature à : une erreur maximale produit un signal maximal, et pas plus grand.
Lancez la course. Deux mille points équilibrés, des poids de départ identiques choisis pour être faux avec confiance (), un learning rate identique, seule la perte diffère. Les deux exécutions sont évaluées avec l’entropie croisée afin que les colonnes soient comparables.
| epoch | perte par entropie croisée | accuracy | perte par erreur quadratique | accuracy |
|---|---|---|---|---|
| 1 | 5,4865 | 0,2300 | 5,9499 | 0,2290 |
| 10 | 1,5525 | 0,2460 | 5,9042 | 0,2290 |
| 50 | 0,4642 | 0,7780 | 5,6913 | 0,2320 |
| 100 | 0,4639 | 0,7770 | 5,3955 | 0,2410 |
| 200 | 0,4639 | 0,7770 | 4,6311 | 0,2745 |
| 500 | 0,4639 | 0,7770 | 0,5291 | 0,7660 |
| 1 000 | 0,4639 | 0,7770 | 0,4640 | 0,7765 |
L’entropie croisée a terminé à l’epoch 50. L’erreur quadratique est encore à 24 % d’accuracy à l’epoch 100 — et n’avait pas bougé depuis 23 % à l’epoch 10 — pire que le hasard, parce qu’elle a commencé avec une confiance erronée et que le gradient qui devait la sauver a été multiplié par 0,0007. Elle s’échappe vers l’epoch 500 et arrive au même endroit. Le résumé honnête est donc que l’erreur quadratique au-dessus d’une sigmoïde n’est pas incorrecte ; elle est lente exactement là où la vitesse compte le plus. Sur un modèle à deux paramètres, vous perdez 450 epochs. Sur un réseau à cent couches, où une unité quelque part se trompe toujours avec confiance, vous perdez l’entraînement entier.
Entropie, entropie croisée et KL, en une page
Lien vers la section : Entropie, entropie croisée et KL, en une pageTrois quantités, nécessaires correctement au chapitre 8 pour la perplexité et au chapitre 11 pour la pénalité qui garde une politique fine-tuned proche de sa référence. Elles sont plus simples que leur réputation.2
L’entropie est le nombre moyen de bits que vous devez dépenser pour communiquer un tirage issu d’une distribution, si vous utilisez le meilleur code possible pour elle :
L’entropie croisée est ce que vous dépensez lorsque vous utilisez un code construit pour sur des données qui viennent en réalité de :
La divergence KL est l’excédent — le gaspillage, en bits, causé par le fait de croire lorsque la vérité est :
Vérifiez les trois sur le tapis :
test defect rate = 0.0185
entropy of that coin = 0.1329 bits
cross-entropy of the constant predictor on test = 0.1330 bits
KL(test coin || fair coin) = 0.8671 bits
H + KL = 1.0000 bits
cross-entropy of the p=0.5 predictor on test = 1.0000 bitsDeux choses y sont visibles. D’abord, un modèle qui se contente d’annoncer le taux de base d’entraînement, 1,69 %, atteint une entropie croisée de 0,1330 bit, presque exactement l’entropie des étiquettes de test — comme il se doit, puisqu’il a la bonne distribution et aucune autre information. L’entropie est le plancher que vous achète l’ignorance de l’individu. Ensuite, un modèle qui hausse les épaules et dit 0,5 paie exactement 1 bit, et l’écart entre les deux, 0,8671 bit, est précisément la divergence KL. n’est pas une identité à mémoriser ; c’est une facture que vous pouvez regarder s’additionner.
Et le lien avec l’entraînement : lorsque l’étiquette est une seule classe connue, la « vraie » distribution est one-hot, son entropie est nulle, et l’entropie croisée égale la divergence KL. Minimiser l’entropie croisée et tirer la distribution du modèle vers la vérité sont le même acte.
Plus de deux réponses : softmax, et le décalage qui ne coûte rien
Lien vers la section : Plus de deux réponses : softmax, et le décalage qui ne coûte rienDéfectueux n’est pas une seule chose. En moulage, une pièce peut sortir en short shot (pas assez de matière), en flash (trop de matière, expulsée du moule), ou en burn. Quatre résultats, donc quatre logits, qui doivent devenir quatre probabilités dont la somme vaut un. C’est le softmax :
Il a une propriété qui ressemble à un accident et qui est en fait toute l’implémentation :
pour n’importe quelle constante , parce que et le s’annulent en haut et en bas. Seules les différences entre logits signifient quelque chose. Le niveau absolu n’est pas de l’information.
Heureusement, car le niveau absolu est ce qui casse l’ordinateur :
logits = [800. 801. 799.]
naive softmax = [nan nan nan]
shifted by -max = [0.2447 0.6652 0.09 ]
same softmax after adding 1000 to every logit: True déborde un flottant 64 bits, la somme devient infinie, et l’infini divisé par l’infini donne nan — pas une erreur, pas un plantage, juste un trou silencieux là où trois probabilités se trouvaient auparavant. Soustraire le logit maximal ne change rien mathématiquement et change tout numériquement, parce que le plus grand exposant devient exactement . C’est l’astuce logsumexp du chapitre 2 en tenue de travail, et toute implémentation sérieuse le fait :
def softmax(Z):
Z = Z - Z.max(axis=1, keepdims=True)
E = np.exp(Z)
return E / E.sum(axis=1, keepdims=True)
def fit_softmax(X, Y, lr=1.0, epochs=6000):
W, b = np.zeros((X.shape[1], Y.shape[1])), np.zeros(Y.shape[1])
for _ in range(epochs):
G = (softmax(X @ W + b) - Y) / len(X)
W -= lr * (X.T @ G)
b -= lr * G.sum(0)
return W, bLe gradient est à nouveau prédiction moins vérité, avec maintenant one-hot. Le cas binaire était depuis le début un cas particulier.
Entraîné sur 3 000 pièces et testé sur 1 000, avec trois mesures chacune (largeur, poids, température de fusion), il atteint 94,00 % d’accuracy. Voici ce que ce nombre cache :
| vérité ↓ / prédiction → | ok | short shot | flash | burn | rappel |
|---|---|---|---|---|---|
| ok | 850 | 5 | 9 | 0 | 0,984 |
| short shot | 22 | 21 | 0 | 0 | 0,488 |
| flash | 20 | 0 | 30 | 1 | 0,588 |
| burn | 3 | 0 | 0 | 39 | 0,929 |
| précision | 0,950 | 0,808 | 0,769 | 0,975 |
Le modèle trouve moins de la moitié des short shots. L’accuracy ne peut pas le voir, parce que 86 % des pièces sont bonnes et qu’il suffit de les classer correctement pour porter la moyenne. Le macro F1 — la moyenne des scores F1 par classe, qui pondère une classe rare comme une classe fréquente — est de 0,7983, contre un micro F1 de 0,9400 qui, par définition, est identique à l’accuracy. Quand quelqu’un annonce un seul nombre F1, demandez lequel.
C’est la fin de la modélisation. Le reste du chapitre porte sur les chiffres.
Trois modèles, une accuracy
Lien vers la section : Trois modèles, une accuracyPrenez le modèle binaire entraîné et créez deux variantes en multipliant chaque logit par une constante : 0,35 pour une version hésitante, 4 pour une version trop confiante. Multiplier par un nombre positif ne peut changer aucun signe, donc les trois modèles prédisent exactement la même étiquette pour les 4 000 pièces de test. L’accuracy ne peut pas les distinguer. L’entropie croisée n’a aucun problème :
| modèle | accuracy | entropie croisée | perte moyenne quand il a raison | perte moyenne quand il a tort | pire perte unique |
|---|---|---|---|---|---|
| hésitant (logits × 0,35) | 0,9830 | 0,1549 | 0,1369 | 1,1990 | 2,80 |
| tel qu’entraîné | 0,9830 | 0,0564 | 0,0147 | 2,4689 | 7,82 |
| trop confiant (logits × 4) | 0,9830 | 0,1563 | 0,0009 | 9,1427 | 27,63 |
Le modèle hésitant paie une petite taxe sur chaque pièce, y compris les milliers qu’il classe correctement. Le modèle trop confiant est presque gratuit quand il a raison et catastrophique quand il a tort — une seule pièce de cet ensemble de test lui coûte 27,63 nats à elle seule. Les deux arrivent presque au même total par des chemins opposés, et le modèle entraîné, dont les probabilités sont calibrées sur les données, se situe trois fois plus bas que les deux.
C’est la façon la plus nette d’énoncer la différence entre une perte et une métrique. La perte est ce que vous optimisez : elle doit être différentiable, et elle voit tout ce que le modèle a dit, y compris son degré de certitude. La métrique est ce sur quoi vous êtes jugé : elle peut être une fonction en escalier, une règle métier, un décompte des défauts manqués. Ce ne sont pas le même objet et elles ne sont pas toujours d’accord — c’est pourquoi vous définissez les deux avant de commencer, et ne laissez jamais la perte remplacer la métrique simplement parce qu’elle est affichée à l’écran.
La baseline stupide passe en premier
Lien vers la section : La baseline stupide passe en premierAvant tout modèle, l’exigence : quel score obtient la réponse la plus paresseuse possible ? Sur ce tapis, toujours dire bon :
always-say-fine baseline: accuracy = 0.9815
confusion (tn, fp, fn, tp) = (3926, 0, 74, 0)98,15 %. Maintenant le modèle logistique entraîné, au seuil par défaut de 0,5 :
logistic @0.5: accuracy=0.9830 precision=0.8000 recall=0.1081 F1=0.1905
confusion (tn, fp, fn, tp) = (3924, 2, 66, 8)98,30 %. Il a battu la baseline de 0,15 point de pourcentage, et tout rapport qui s’arrête à l’accuracy appellera cela une victoire. La matrice de confusion dit ce qui s’est réellement passé :
| prédit bon | prédit défectueux | |
|---|---|---|
| réellement bon | 3 924 | 2 |
| réellement défectueux | 66 | 8 |
Trois nombres nomment les trois façons de lire ce tableau :
- Précision . Parmi les pièces signalées, combien étaient réellement défectueuses. C’est le coût des inspections gaspillées.
- Rappel . Parmi les pièces défectueuses, combien il en a attrapé. C’est le coût d’expédier une mauvaise pièce à un client.
- F1 , leur moyenne harmonique, qui reste proche du plus petit des deux et refuse donc d’être flattée par l’un seul.
Ce qui compte dépend de l’usine, pas des mathématiques : une inspection coûte quelques secondes et un défaut expédié coûte un avis de rappel, donc ici le rappel domine et 0,108 est un échec.
Mais le modèle n’est pas le problème. Le seuil l’est, et le seuil ne fait pas partie du modèle — c’est une décision métier appliquée ensuite à une probabilité. Balayez-le :
| seuil | TP | FP | FN | accuracy | précision | rappel | F1 |
|---|---|---|---|---|---|---|---|
| 0,500 | 8 | 2 | 66 | 0,9830 | 0,800 | 0,108 | 0,190 |
| 0,200 | 27 | 28 | 47 | 0,9812 | 0,491 | 0,365 | 0,419 |
| 0,100 | 42 | 118 | 32 | 0,9625 | 0,263 | 0,568 | 0,359 |
| 0,050 | 54 | 236 | 20 | 0,9360 | 0,186 | 0,730 | 0,297 |
| 0,020 | 67 | 570 | 7 | 0,8558 | 0,105 | 0,905 | 0,188 |
| 0,005 | 71 | 1 360 | 3 | 0,6593 | 0,050 | 0,959 | 0,094 |
Lisez la colonne accuracy vers le bas. Elle baisse tout du long — de 98,30 % à 65,93 % — pendant que le modèle passe de 8 défauts détectés à 71 sur 74. Tout ce que ce modèle peut faire d’utile rend son accuracy pire. Une équipe qui optimiserait le chiffre en gros titre expédierait la version qui ne trouve rien.
Afficher les détails
La pondération des classes ne crée pas de signal, elle déplace le point de fonctionnement. Le premier réflexe habituel avec des classes déséquilibrées est de pondérer la classe rare dans la perte. En le faisant, avec des poids de 1, 10 et 60 sur les positifs :
| poids sur les positifs | accuracy | précision | rappel | F1 | AUC |
|---|---|---|---|---|---|
| 1 | 0,9830 | 0,800 | 0,108 | 0,190 | 0,9363 |
| 10 | 0,9605 | 0,253 | 0,581 | 0,352 | 0,9361 |
| 60 | 0,8290 | 0,091 | 0,919 | 0,166 | 0,9361 |
La précision et le rappel bougent beaucoup. L’AUC — la probabilité que le modèle classe une pièce défectueuse aléatoire au-dessus d’une bonne pièce aléatoire, en ignorant entièrement le seuil — bouge de 0,0002, c’est-à-dire de rien. La repondération a fait glisser le même modèle le long de la même courbe de compromis. C’est souvent ce que vous voulez, et ce n’est jamais une information nouvelle : si le classement est mauvais, aucun schéma de pondération ne le sauvera.
Trois découpages, et la fuite que vous allez trouver
Lien vers la section : Trois découpages, et la fuite que vous allez trouverPourquoi trois découpages et pas deux ? Parce qu’au moment où vous utilisez un ensemble d’exemples pour choisir quoi que ce soit — un seuil, un learning rate, lequel de six modèles expédier — cet ensemble a été utilisé pour l’ajustement, et son score cesse d’être non biaisé.3 Mesuré sur ce tapis : balayer le seuil sur l’ensemble de validation choisit 0,196, puis le modèle obtient F1 = 0,4122 sur l’ensemble de test resté intact. Si le balayage avait été fait directement sur l’ensemble de test, le meilleur résultat atteignable là était 0,4186 — un nombre que personne n’a le droit de publier.
L’écart est faible ici, 0,006, parce qu’il s’agit d’un hyperparamètre balayé une fois face à 4 000 exemples de validation. Il augmente avec chaque décision supplémentaire et chaque réduction de l’ensemble de validation. Notez aussi que la direction n’est pas garantie sur une exécution unique : le seuil choisi a obtenu 0,3902 en validation et 0,4122 en test, donc la validation l’a sous-estimé cette fois. Le biais est systématique sur de nombreuses décisions, pas visible sur une seule.4
Maintenant l’exercice. Le journal du tapis arrive avec une troisième colonne, station_seconds : combien de temps chaque pièce a passé au poste d’inspection. L’ajouter est une modification d’une ligne au prétraitement. Voici ce que cela produit :
| modèle | accuracy | précision | rappel | F1 | entropie croisée | AUC |
|---|---|---|---|---|---|---|
| largeur + poids | 0,9830 | 0,800 | 0,108 | 0,190 | 0,0564 | 0,9363 |
| + station_seconds | 0,9920 | 0,792 | 0,770 | 0,781 | 0,0236 | 0,9970 |
Le rappel passe de 10,8 % à 77,0 %. F1 fait plus que quadrupler. Et regardez ce qu’a fait l’accuracy : 98,30 % → 99,20 %, un gain de neuf dixièmes de point, le genre de nombre qu’une diapositive de synthèse arrondit en « environ 99 % dans les deux cas ». L’accuracy n’a pas vu l’échec tout à l’heure et ne voit pas la fraude maintenant.
Avant de continuer : le modèle triche. Trouvez comment.
Comment traquer une fuite, dans l’ordre qui la trouve le plus vite.
-
Comparez train et test. Le surapprentissage apparaît sous forme d’un grand écart. Ici : modèle honnête 0,9838 train / 0,9830 test ; modèle avec fuite 0,9936 train / 0,9920 test. Les deux écarts sont inférieurs à 0,2 point. Une fuite ne ressemble pas à du surapprentissage — la feature avec fuite est tout aussi disponible au moment du test, donc le modèle généralise merveilleusement vers un monde qui n’existe pas.
-
Entraînez un modèle par feature, seule. Tout ce qui porte la réponse se dénoncera :
feature seule accuracy rappel F1 AUC largeur 0,9815 0,014 0,026 0,8691 poids 0,9815 0,000 0,000 0,7914 station_seconds0,9850 0,405 0,500 0,9960 Une colonne, à elle seule, classe les défauts avec une AUC de 0,9960. Deux mesures prises au pied à coulisse et à la balance arrivent à 0,87 et 0,79. Cette asymétrie est l’alarme.
-
Demandez quand chaque nombre a été écrit. Temps de séjour moyen : 2,23 secondes pour les pièces qui passent, 15,56 secondes pour les pièces qui échouent. Bien sûr. Une pièce reste au poste parce qu’un inspecteur l’a retirée du tapis — ce qui arrive après, et seulement parce que quelqu’un a décidé qu’elle était défectueuse. La colonne n’est pas une mesure de la pièce. C’est une mesure du verdict.
station = 1.8 + rng.exponential(0.35, N) # a part just passing through
audited = rng.random(N) < 0.006 # random spot checks
station[audited] += rng.uniform(6.0, 26.0, audited.sum())
station[y == 1] = 9.0 + rng.exponential(7.0, (y == 1).sum()) La ligne mise en évidence est la fuite : le temps de séjour d’une pièce défectueuse est tiré d’une distribution différente, parce qu’un humain l’a retirée du tapis. C’est le bug sérieux le plus courant en machine learning appliqué, et il porte un nom : fuite de cible — de l’information dans les features d’entraînement qui ne serait pas disponible au moment où la prédiction doit être faite.5 Elle ne lève aucune exception. Elle produit un meilleur nombre. Toutes les incitations d’un projet poussent à la conserver.
La défense tient en une question, posée à chaque colonne : à l’instant où j’ai besoin de cette prédiction, cette valeur existe-t-elle déjà ? Sur un tapis en direct, station_seconds est inconnu jusqu’après l’inspection de la pièce — précisément ce que le modèle était censé remplacer.
Combien d’exemples de test me faut-il ?
Lien vers la section : Combien d’exemples de test me faut-il ?Supposons que vous évaluiez un modèle sur 20 exemples et qu’il en classe 17 correctement. Vous annoncez 85 %.
17 correct out of 20 -> accuracy 0.8500
Wilson 95% CI : [0.6396, 0.9476]
bootstrap 95% CI : [0.7000, 1.0000]
P(a 65% model scores 17 or more out of 20) = 0.0444
P(an 85% model scores 17 or more out of 20) = 0.6477La lecture honnête de 17/20 est quelque part entre 64 % et 95 %. Un vrai modèle à 65 % produit ce résultat 4,4 % du temps — une exécution sur vingt-trois — et si vous avez essayé une poignée de prompts puis rapporté le meilleur, vous avez fabriqué cette exécution vous-même. Dix-sept sur vingt ne permet pas de distinguer un modèle à 85 % d’un modèle à 65 %.
Deux façons de mettre un intervalle sur un taux, et toutes deux ont leur place dans votre boîte à outils :
def wilson(k, n, z=1.959963985):
"""95% interval for k successes in n trials. Correct at small n; no simulation."""
ph, d = k / n, 1 + z * z / n
centre = (ph + z * z / (2 * n)) / d
half = z * (ph * (1 - ph) / n + z * z / (4 * n * n)) ** 0.5 / d
return centre - half, centre + half
def bootstrap_ci(correct, n_resamples=10_000, alpha=0.05, seed=0):
"""95% interval for the mean of any per-example score array. Works on F1 too."""
rng = np.random.default_rng(seed)
correct = np.asarray(correct, dtype=float)
draws = correct[rng.integers(0, len(correct), size=(n_resamples, len(correct)))]
lo, hi = np.quantile(draws.mean(axis=1), [alpha / 2, 1 - alpha / 2])
return float(correct.mean()), float(lo), float(hi)Utilisez Wilson6 pour un simple taux de réussite ; il reste bien comporté pour n’importe quel et ne nécessite aucun aléa. Notez ci-dessus qu’à , l’extrémité supérieure du bootstrap est 1,0000 — en rééchantillonnant 20 points, on peut facilement tirer 20 réussites, donc il ne peut pas représenter un intervalle plus fin que sa propre granularité. Utilisez le bootstrap7 lorsqu’il n’existe pas de formule, ce qui couvre la plupart des cas intéressants : F1, macro-moyennes, BLEU, pass@1, le score d’un juge fondé sur une grille. Sur ce tapis, le F1 du modèle ajusté, 0,4122, porte un intervalle bootstrap de [0,3009, 0,5156] — c’est le nombre qui devrait apparaître dans le rapport, parce que l’estimation ponctuelle seule invite une comparaison qu’elle ne peut pas soutenir.
Une dernière mesure, parce qu’elle change la manière dont vous devriez comparer deux modèles. Deux modèles évalués sur les mêmes 500 exemples :
model A: 0.8580 95% CI [0.8260, 0.8880]
model B: 0.8120 95% CI [0.7780, 0.8460]
the two intervals overlap: True
paired difference A-B: 0.0460 95% CI [0.0260, 0.0680]
they disagree on 31 of 500 examples (A right 27, B right 4)Leurs intervalles se recouvrent, et la règle populaire — des barres d’erreur qui se recouvrent signifient pas de différence significative — jugerait la comparaison non concluante. Elle ne l’est pas. Les deux modèles ont tourné sur les mêmes exemples, donc la bonne quantité est la différence par exemple, dont l’intervalle est [0.0260, 0.0680], confortablement au-dessus de zéro. Ils ne sont en désaccord que sur 31 éléments sur 500, et A gagne 27 de ces désaccords ; les exemples partagés, faciles comme difficiles, s’annulent au lieu d’ajouter du bruit. Comparez les modèles de façon appariée, et vous atteignez la même conclusion avec une fraction des données.
Où cela mène ensuite
Lien vers la section : Où cela mène ensuiteVous avez maintenant un modèle qui produit des probabilités calibrées, une perte dérivée d’une affirmation sur les données plutôt que choisie par commodité, un gradient qui est littéralement prédiction moins vérité, et — plus important — la mécanique pour découvrir si tout cela fonctionne. L’intervalle de Wilson en dix lignes ci-dessus est réutilisé tel quel : il accompagne les variantes de prompt au chapitre 15, les tableaux de retrieval au chapitre 19, et le golden set au chapitre 29. Le bootstrap est ce vers quoi vous vous tournez lorsqu’il n’existe aucune formule.
Mais le modèle n’a encore qu’une couche. Il trace une ligne, et le chapitre 1 a prouvé avec les quatre lignes du XOR qu’une ligne ne suffit pas. La correction consiste à empiler : une première couche qui courbe l’espace, une seconde qui trace la ligne dans l’espace courbé.
C’est là que le gradient bien rangé de ce chapitre s’arrête. Tout ce qui précède fonctionnait parce que pouvait être écrit à la main, une seule fois, pour un modèle avec une couche entre l’entrée et la perte. Placez une seconde couche au milieu et la question change de forme : quelle est la dérivée de la perte par rapport à un poids qui ne touche pas du tout la sortie — un poids dont l’influence n’arrive qu’à travers une autre couche, peut-être par plusieurs chemins à la fois ?
Cette dérivée existe. La calculer à la main est désespéré pour tout ce qui dépasse un jouet, et la calculer un paramètre à la fois est désespéré à une autre échelle. Il faut une procédure qui obtienne toutes les dérivées du réseau à partir d’un seul passage arrière sur le même graphe que le passage avant vient de parcourir.
C’est le chapitre 5, et c’est le moteur sur lequel tourne le reste de ce cours.
Sources et méthode
Lien vers la section : Sources et méthodeÀ lire aussi en parallèle de ce chapitre : Bishop, Pattern Recognition and Machine Learning §1.2, §1.5, §1.6 et §4.3, qui couvre probabilité, théorie de la décision, théorie de l’information et classification linéaire dans l’ordre suivi par ce chapitre ; Murphy, Probabilistic Machine Learning: An Introduction, chapitres 6 et 10 ; Prince, Understanding Deep Learning §5.4–5.7 ; et Saito et Rehmsmeier, The Precision-Recall Plot Is More Informative than the ROC Plot When Evaluating Binary Classifiers on Imbalanced Datasets (PLOS ONE, 2015) — pourquoi l’AUC citée ci-dessus ne devrait pas être le seul nombre indépendant du seuil que vous regardez lorsque 1,7 % des pièces sont défectueuses.
Références
Lien vers la section : Références-
Ma, T. et Ng, A. CS229 Lecture Notes, Stanford University, chapitres 2 et 3. Là où l’annulation qui produit cesse de ressembler à de la chance : choisissez la distribution de la famille exponentielle qui correspond à votre sortie, utilisez son lien canonique, et le gradient est toujours prédiction moins vérité. ↩
-
Olah, C. Visual Information Theory (2015),
colah.github.io/posts/2015-09-Visual-Information. L’explication la plus claire disponible de l’entropie, de l’entropie croisée et de la divergence KL comme coûts en bits plutôt que comme formules. ↩ -
Abu-Mostafa, Y. S., Magdon-Ismail, M. et Lin, H.-T. Learning From Data (AMLBook, 2012), cours 13 et 17 du cours de Caltech. Le cours 13 traite de la validation ; le cours 17, sur les trois principes de l’apprentissage, nomme le data snooping. Ensemble, ils sont la source de la discipline de ce chapitre : chaque regard porté sur un jeu de données est une décision d’ajustement, que vous ayez lancé un optimiseur ou non. ↩
-
James, G., Witten, D., Hastie, T. et Tibshirani, R. An Introduction to Statistical Learning, 2e édition (Springer, 2021), chapitres 2 et 5, pour la décomposition biais-variance et pour le rééchantillonnage. Le volume compagnon est celui où le piège de sélection est énoncé directement : Hastie, Tibshirani et Friedman, The Elements of Statistical Learning, 2e édition, §7.10.2, The Wrong and Right Way to Do Cross-validation. ↩
-
Kaufman, S., Rosset, S., Perlich, C. et Stitelman, O. Leakage in Data Mining: Formulation, Detection, and Avoidance. ACM Transactions on Knowledge Discovery from Data 6(4), 2012. Un traitement formel de l’échec démontré ci-dessus, avec des études de cas de compétitions remportées par un modèle qui avait appris un artefact de la manière dont les données avaient été assemblées. ↩
-
Wilson, E. B. Probable Inference, the Law of Succession, and Statistical Inference. Journal of the American Statistical Association 22(158), pp. 209–212 (1927). L’intervalle de score utilisé dans
wilson()ci-dessus, qui reste le bon choix par défaut pour une proportion. L’intervalle de manuel est celui à éviter : il donne des absurdités près de 0 et 1, et sous-couvre fortement aux petits . ↩ -
Efron, B. Bootstrap Methods: Another Look at the Jackknife. The Annals of Statistics 7(1), pp. 1–26 (1979). L’idée qui vous permet de mettre un intervalle sur n’importe quelle statistique que vous pouvez calculer, y compris celles qui n’ont pas de théorie d’échantillonnage. ↩