.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "gyexamples/ml_basic/plot_binary_classification.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code .. rst-class:: sphx-glr-example-title .. _sphx_glr_gyexamples_ml_basic_plot_binary_classification.py: Classification Binaire ====================== Un problème de classification binaire consiste à trouver un moyen de séparer deux nuages de points (voir `classification `_). .. contents:: :local: .. GENERATED FROM PYTHON SOURCE LINES 16-20 Principe -------- On commence par générer un nuage de points artificiel. .. GENERATED FROM PYTHON SOURCE LINES 21-34 .. code-block:: default from sklearn.metrics import log_loss from sklearn.metrics import auc from sklearn.metrics import roc_curve from sklearn.metrics import confusion_matrix from sklearn.model_selection import train_test_split import numpy from sklearn.linear_model import LogisticRegression import matplotlib.pyplot as plt from sklearn.datasets import make_classification X, Y = make_classification(n_samples=500, n_features=2, n_classes=2, n_repeated=0, n_redundant=0) .. GENERATED FROM PYTHON SOURCE LINES 35-36 On représente ces données. .. GENERATED FROM PYTHON SOURCE LINES 36-42 .. code-block:: default fig = plt.figure(figsize=(5, 5)) ax = plt.subplot() ax.plot(X[Y == 0, 0], X[Y == 0, 1], "ob") ax.plot(X[Y == 1, 0], X[Y == 1, 1], "or") .. image-sg:: /gyexamples/ml_basic/images/sphx_glr_plot_binary_classification_001.png :alt: plot binary classification :srcset: /gyexamples/ml_basic/images/sphx_glr_plot_binary_classification_001.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none [] .. GENERATED FROM PYTHON SOURCE LINES 43-50 D'un point de vue géométrique, un problème de classification consiste à trouver la meilleure frontière entre deux nuages de points. Le plus simple est de supposer que c'est une droite. Dans ce cas, on choisira un modèle de régression logistique : `LogisticRegression `_. .. GENERATED FROM PYTHON SOURCE LINES 50-54 .. code-block:: default logreg = LogisticRegression() logreg.fit(X, Y) .. raw:: html
LogisticRegression()
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.


.. GENERATED FROM PYTHON SOURCE LINES 55-57 L'optimisation du modèle produit une droite dont les coefficients sont : .. GENERATED FROM PYTHON SOURCE LINES 57-59 .. code-block:: default print(logreg.coef_, logreg.intercept_) .. rst-class:: sphx-glr-script-out .. code-block:: none [[ 2.44207867 -0.05064282]] [-0.07998426] .. GENERATED FROM PYTHON SOURCE LINES 60-64 Nous pourrions tracer cette droite mais ce graphe ne serait valable que pour un modèle linéaire. Il est aussi facile de colorier le fond du graphe avec la couleur de la classe prédite par le modèle. .. GENERATED FROM PYTHON SOURCE LINES 64-91 .. code-block:: default def colorie(X, model, ax, fig): xmin, xmax = numpy.min(X[:, 0]), numpy.max(X[:, 0]) ymin, ymax = numpy.min(X[:, 1]), numpy.max(X[:, 1]) hx = (xmax - xmin) / 100 hy = (ymax - ymin) / 100 xx, yy = numpy.mgrid[xmin:xmax:hx, ymin:ymax:hy] grid = numpy.c_[xx.ravel(), yy.ravel()] probs = model.predict_proba(grid)[:, 1].reshape(xx.shape) contour = ax.contourf(xx, yy, probs, 25, cmap="RdBu", vmin=0, vmax=1) ax_c = fig.colorbar(contour) ax_c.set_label("$P(y = 1)$") ax_c.set_ticks([0, .25, .5, .75, 1]) ax.set_xlim([xmin, xmax]) ax.set_ylim([ymin, ymax]) fig = plt.figure(figsize=(7, 5)) ax = plt.subplot() colorie(X, logreg, ax, fig) ax.scatter(X[:, 0], X[:, 1], c=Y, s=50, cmap="RdBu", vmin=-.2, vmax=1.2, edgecolor="white", linewidth=1) ax.set(aspect="equal", xlabel="$X_1$", ylabel="$X_2$") .. image-sg:: /gyexamples/ml_basic/images/sphx_glr_plot_binary_classification_002.png :alt: plot binary classification :srcset: /gyexamples/ml_basic/images/sphx_glr_plot_binary_classification_002.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none [None, Text(0.5, 25.722222222222214, '$X_1$'), Text(83.10591224957602, 0.5, '$X_2$')] .. GENERATED FROM PYTHON SOURCE LINES 92-108 Evaluation ---------- Il y a deux tests qu'on effectue de façon quasi-systématique pour ce type de problème : une :epkg:`matrice de confusion` et une courbe :epkg:`ROC`. La première étape consiste à diviser les données en base d'apprentissage (train) et base de test (test). C'est nécessaire car certains modèles peuvent simplement apprendre par coeur et l'erreur est sur les données ayant servi à apprendre. C'est le cas des `plus proches voisins `_. La division doit être aléatoire. On peut d'ailleurs recommencer plusieurs pour s'assurer de la robustesse des résultats. .. GENERATED FROM PYTHON SOURCE LINES 109-112 .. code-block:: default X_train, X_test, y_train, y_test = train_test_split(X, Y) .. GENERATED FROM PYTHON SOURCE LINES 113-114 On apprend sur la base d'apprentissage. .. GENERATED FROM PYTHON SOURCE LINES 114-118 .. code-block:: default logreg = LogisticRegression() logreg.fit(X_train, y_train) .. raw:: html
LogisticRegression()
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.


.. GENERATED FROM PYTHON SOURCE LINES 119-120 Et on prédit sur la base de test. .. GENERATED FROM PYTHON SOURCE LINES 120-122 .. code-block:: default y_pred = logreg.predict(X_test) .. GENERATED FROM PYTHON SOURCE LINES 123-124 Puis on calcule la :epkg:`matrice de confusion`. .. GENERATED FROM PYTHON SOURCE LINES 124-127 .. code-block:: default conf = confusion_matrix(y_test, y_pred) print(conf) .. rst-class:: sphx-glr-script-out .. code-block:: none [[55 6] [11 53]] .. GENERATED FROM PYTHON SOURCE LINES 128-132 Les nombres sur la diagonale indiquent les éléments classés dans la bonne classe. Ailleurs, il s'agit des éléments mal classés par le modèle. Ce sont des points bleus sur un fond rouge par exemple. .. GENERATED FROM PYTHON SOURCE LINES 132-142 .. code-block:: default fig = plt.figure(figsize=(7, 5)) ax = plt.subplot() colorie(X_test, logreg, ax, fig) ax.scatter(X_test[:, 0], X_test[:, 1], c=y_test, s=50, cmap="RdBu", vmin=-.2, vmax=1.2, edgecolor="white", linewidth=1) ax.set(aspect="equal", xlabel="$X_1$", ylabel="$X_2$") ax.set_title("Résultats sur la base de test") .. image-sg:: /gyexamples/ml_basic/images/sphx_glr_plot_binary_classification_003.png :alt: Résultats sur la base de test :srcset: /gyexamples/ml_basic/images/sphx_glr_plot_binary_classification_003.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none Text(0.5, 1.0, 'Résultats sur la base de test') .. GENERATED FROM PYTHON SOURCE LINES 143-159 Certains points sont plus ou moins éloignés de la frontière que le modèle a trouvé entre les deux classes. Plus le point est loin de la frontière, plus le modèle est sûr de lui. C'est vrai pour la régression logistique mais aussi pour la plupart des modèles utilisés pour faire une classification binaire. C'est comme cela que le modèle est capable de donner un indicateur de confiance : la distance du point à la frontière que le modèle a trouvé. Cette distance est convertie en score ou en probabilité. La :epkg:`matrice de confusion` n'utilise pas cette information, la courbe :epkg:`ROC` si. Voyons comment. On s'inspire de l'exemple `Receiver Operating Characteristic (ROC) `_. La courbe :epkg:`ROC` ne change pas qu'on choisisse un score ou une probabilité. On calcule les probabilités. Le modèle retourne la probabilité que chaque exemple appartienne dans chacune des classes. .. GENERATED FROM PYTHON SOURCE LINES 159-162 .. code-block:: default y_proba = logreg.predict_proba(X_test) print(y_proba[:3]) .. rst-class:: sphx-glr-script-out .. code-block:: none [[0.14774986 0.85225014] [0.10717777 0.89282223] [0.9821646 0.0178354 ]] .. GENERATED FROM PYTHON SOURCE LINES 163-169 On construit la courbe :epkg:`ROC`. Tout d'abord les coordonnées des points. Le modèle prédit bien si le modèle trouve la bonne classe ce qu'on traduit par ``y_pred == y_test``. Le score est celui de la classe prédite : ``y_score[y_pred]``. .. GENERATED FROM PYTHON SOURCE LINES 169-173 .. code-block:: default prob_pred = [y_proba[i, c] for i, c in enumerate(y_pred)] fpr, tpr, th = roc_curve((y_pred == y_test).ravel(), prob_pred) .. GENERATED FROM PYTHON SOURCE LINES 174-179 Pour un indice i, ``th[i]`` est un seuil, ``tpr[i]`` est la proportion d'exemples bien classés pour lesquels le score du modèle est supérieur ``th[i]``. ``fpr[i]`` est la proportion d'exemples mal classés pour lesquels le score du modèle est supérieur ``th[i]``. .. GENERATED FROM PYTHON SOURCE LINES 179-183 .. code-block:: default print("i=2", th[2], tpr[2], fpr[2]) print(f"i={len(th) - 2}", th[-2], tpr[-2], fpr[-2]) .. rst-class:: sphx-glr-script-out .. code-block:: none i=2 0.984833882844044 0.25 0.0 i=24 0.5360625604318461 1.0 0.8823529411764706 .. GENERATED FROM PYTHON SOURCE LINES 184-189 Plus le score est faible, plus ces proportions sont grandes. Quand le seuil est très faible, le modèle retourne un score toujours supérieur au seuil. Les deux proportions sont égales à 1. A l'inverse, si le seuil est élevé, les deux proportions sont nulles. On trace la courbe en faisant varier le seuil. .. GENERATED FROM PYTHON SOURCE LINES 189-202 .. code-block:: default plt.figure() lw = 2 plt.plot(fpr, tpr, color='darkorange', lw=lw, label='Courbe ROC') plt.plot([0, 1], [0, 1], color='navy', lw=lw, linestyle='--') plt.xlim([0.0, 1.0]) plt.ylim([0.0, 1.05]) plt.xlabel("Proportion mal classée") plt.ylabel("Proportion bien classée") plt.title('ROC') plt.legend(loc="lower right") .. image-sg:: /gyexamples/ml_basic/images/sphx_glr_plot_binary_classification_004.png :alt: ROC :srcset: /gyexamples/ml_basic/images/sphx_glr_plot_binary_classification_004.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none .. GENERATED FROM PYTHON SOURCE LINES 203-211 L'aire sous la courbe ou :epkg:`AUC` (Area Under the Curve) est liée à la pertinence du modèle. Plus le score est elevé, plus on s'attend à ce que la proportion d'exemples bien classés soit grande par rapport à la proportion d'exemple mal classés. A l'inverse, quand ces proportions sont toujours égales quelque soit le sueil, la courbe correspond à la première diagonale et le modèle n'a rien appris. Plus la courbe est haute, meilleur le modèle est. C'est pourquoi on calcule l'aire sous la courbe. .. GENERATED FROM PYTHON SOURCE LINES 211-214 .. code-block:: default print(auc(fpr, tpr)) .. rst-class:: sphx-glr-script-out .. code-block:: none 0.8120915032679739 .. GENERATED FROM PYTHON SOURCE LINES 215-233 La courbe :epkg:`ROC` s'applique toujours à un problème de classification binaire qu'on peut scinder en trois questions : * Le modèle a bien classé un exemple dans la classe 0. * Le modèle a bien classé un exemple dans la classe 1. * Le modèle a bien classé un exemple, que ce soit dans la classe 0 ou la classe 1. Ce problème suppose implicitement que le même seuil est utilisé sur chacun des classes. C'est-à-dire qu'on prédit la classe 1 si le score pour la classe 1 est supérieur à à celui obtenu pour la classe 0 mais aussi qu'on valide la réponse si le score de la classe 1 ou celui de la classe 0 est supérieur au même seuil *s*, ce qui n'est pas nécessairement le meilleur choix. Si les réponses sont liées, le modèle peut répondre de manière plus ou moins efficace à ces trois questions. On calcule les courbes :epkg:`ROC` à ces trois questions. .. GENERATED FROM PYTHON SOURCE LINES 233-241 .. code-block:: default fpr_cl = dict() tpr_cl = dict() fpr_cl["classe 0"], tpr_cl["classe 0"], _ = roc_curve( y_test == 0, y_proba[:, 0].ravel()) fpr_cl["classe 1"], tpr_cl["classe 1"], _ = roc_curve( y_test, y_proba[:, 1].ravel()) # y_test == 1 fpr_cl["tout"], tpr_cl["tout"] = fpr, tpr # On reprend ceux déjà calculés. .. GENERATED FROM PYTHON SOURCE LINES 242-243 Et on les représente. .. GENERATED FROM PYTHON SOURCE LINES 243-255 .. code-block:: default plt.figure() for key in fpr_cl: plt.plot(fpr_cl[key], tpr_cl[key], label=key) plt.plot([0, 1], [0, 1], color='navy', lw=lw, linestyle='--') plt.xlim([0.0, 1.0]) plt.ylim([0.0, 1.05]) plt.xlabel("Proportion mal classée") plt.ylabel("Proportion bien classée") plt.title('ROC(s)') plt.legend(loc="lower right") .. image-sg:: /gyexamples/ml_basic/images/sphx_glr_plot_binary_classification_005.png :alt: ROC(s) :srcset: /gyexamples/ml_basic/images/sphx_glr_plot_binary_classification_005.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none .. GENERATED FROM PYTHON SOURCE LINES 256-275 Fonctions de coût ----------------- La performance d'un modèle est parfois évaluée avec une fonction de coût qui n'est pas celle utilisée pour optimiser le modèle. C'est le cas souvent de l'aire sous la courbe d'une courbe ROC (AUC). Dans la plupart des cas, le modèle n'est pas optimisée pour cette métrique. La métrique AUC est coûteuse à calculer, c'est pourquoi on lui préfère souvent d'autres métriques comme : la `logloss `_. Si on note :math:`y_i` la classe attendue et :math:`p_i` la probabilité que l'observation *i* appartiennent à cette classe selon le modèle, alors : .. math:: logloss = \sum_i y_i \log p_i + (1-y_i)\log(1-p_i) On utilise la fonction :epkg:`scikit-learn:metrics:log_loss`. .. GENERATED FROM PYTHON SOURCE LINES 276-279 .. code-block:: default err = log_loss(y_test, y_proba) print(err) .. rst-class:: sphx-glr-script-out .. code-block:: none 0.32113050602407 .. rst-class:: sphx-glr-timing **Total running time of the script:** ( 0 minutes 2.897 seconds) .. _sphx_glr_download_gyexamples_ml_basic_plot_binary_classification.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_binary_classification.py ` .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_binary_classification.ipynb ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_