Guide définitif de la régression logistique dans Python PlatoBlockchain Data Intelligence. Recherche verticale. Aï.

Guide définitif de la régression logistique en Python

Introduction

Parfois confondu avec régression linéaire par des novices - en raison du partage du terme régression - régression logistique est très différent de régression linéaire. Alors que la régression linéaire prédit des valeurs telles que 2, 2.45, 6.77 ou valeurs continues, ce qui en fait un régression algorithme, régression logistique prédit des valeurs telles que 0 ou 1, 1 ou 2 ou 3, qui sont valeurs discrètes, ce qui en fait un classification algorithme. Oui, ça s'appelle régression mais est un classification algorithme. Plus sur cela dans un instant.

Par conséquent, si votre problème de science des données implique des valeurs continues, vous pouvez appliquer un régression algorithme (la régression linéaire en fait partie). Sinon, s'il s'agit de classer des entrées, des valeurs discrètes ou des classes, vous pouvez appliquer un classification algorithme (la régression logistique en fait partie).

Dans ce guide, nous allons effectuer une régression logistique en Python avec la bibliothèque Scikit-Learn. Nous expliquerons également pourquoi le mot "régression" est présent dans le nom et comment fonctionne la régression logistique.

Pour ce faire, nous allons d'abord charger des données qui seront classées, visualisées et prétraitées. Ensuite, nous construirons un modèle de régression logistique qui comprendra ces données. Ce modèle sera ensuite évalué et utilisé pour prédire les valeurs en fonction des nouvelles entrées.

motivation

L'entreprise pour laquelle vous travaillez a conclu un partenariat avec une ferme agricole turque. Ce partenariat consiste à vendre des graines de citrouille. Les graines de citrouille sont très importantes pour la nutrition humaine. Ils contiennent une bonne proportion de glucides, de lipides, de protéines, de calcium, de potassium, de phosphore, de magnésium, de fer et de zinc.

Dans l'équipe de science des données, votre tâche consiste à faire la différence entre les types de graines de citrouille simplement en utilisant des données - ou classement les données selon le type de semence.

La ferme turque travaille avec deux types de graines de citrouille, l'un s'appelle Cerçevelik et l'autre Ürgüp Sivrisi.

Pour classer les graines de citrouille, votre équipe a suivi le papier 2021 "L'utilisation de méthodes d'apprentissage automatique dans la classification des graines de citrouille (Cucurbita pepo L.). Ressources génétiques et évolution des cultures » de Koklu, Sarigil et Ozbek - dans cet article, il existe une méthodologie pour photographier et extraire les mesures des graines à partir des images.

Après avoir terminé le processus décrit dans l'article, les mesures suivantes ont été extraites :

  • Région – le nombre de pixels à l'intérieur des bordures d'une graine de citrouille
  • Périmètre – la circonférence en pixels d'une graine de courge
  • Longueur de l'axe majeur – aussi la circonférence en pixels d'une graine de courge
  • Longueur de l'axe mineur – la petite distance axiale d'une graine de courge
  • Excentricité – l'excentricité d'une graine de courge
  • Zone convexe – le nombre de pixels de la plus petite coque convexe au niveau de la région formée par la graine de courge
  • Ampleur – le rapport entre une zone de graine de citrouille et les pixels de la boîte englobante
  • Diamètre équivalent – la racine carrée de la multiplication de l'aire de la graine de courge par quatre divisée par pi
  • La compacité – la proportion de la surface de la graine de courge par rapport à la surface du cercle de même circonférence
  • Solidité – l'état convexe et convexe des graines de citrouille
  • Circularité – l'ovalité des pépins de courge sans tenir compte des déformations de ses bords
  • Aspect Ratio – le rapport d'aspect des graines de citrouille

Ce sont les mesures avec lesquelles vous devez travailler. Outre les mesures, il y a aussi le Classe étiquette pour les deux types de graines de citrouille.

Pour commencer à classer les graines, importons les données et commençons à les examiner.

Comprendre l'ensemble de données

Remarque: Vous pouvez télécharger le jeu de données citrouille ici.

Après avoir téléchargé le jeu de données, nous pouvons le charger dans une structure de dataframe en utilisant le pandas bibliothèque. Comme il s'agit d'un fichier Excel, nous utiliserons le read_excel() méthode:

import pandas as pd

fpath = 'dataset/pumpkin_seeds_dataset.xlsx' 
df = pd.read_excel(fpath)

Une fois les données chargées, nous pouvons jeter un coup d'œil rapide aux 5 premières lignes en utilisant le head() méthode:

df.head() 

Cela se traduit par:

	Area 	Perimeter 	Major_Axis_Length 	Minor_Axis_Length 	Convex_Area 	Equiv_Diameter 	Eccentricity 	Solidity 	Extent 	Roundness 	Aspect_Ration 	Compactness 	Class
0 	56276 	888.242 	326.1485 			220.2388 			56831 			267.6805 		0.7376 			0.9902 		0.7453 	0.8963 		1.4809 			0.8207 			Çerçevelik
1 	76631 	1068.146 	417.1932 			234.2289 			77280 			312.3614 		0.8275 			0.9916 		0.7151 	0.8440 		1.7811 			0.7487 			Çerçevelik
2 	71623 	1082.987 	435.8328 			211.0457 			72663 			301.9822 		0.8749 			0.9857 		0.7400 	0.7674 		2.0651 			0.6929 			Çerçevelik
3 	66458 	992.051 	381.5638 			222.5322 			67118 			290.8899 		0.8123 			0.9902 		0.7396 	0.8486 		1.7146 			0.7624 			Çerçevelik
4 	66107 	998.146 	383.8883 			220.4545 			67117 			290.1207 		0.8187 			0.9850 		0.6752 	0.8338 		1.7413 			0.7557 			Çerçevelik

Ici, nous avons toutes les mesures dans leurs colonnes respectives, notre Caractéristiqueset aussi le Classe colonne, notre l'objectif, qui est le dernier dans le dataframe. Nous pouvons voir combien de mesures nous avons en utilisant le shape attribut:

df.shape 

La sortie est:

(2500, 13)

Le résultat de la forme nous indique qu'il y a 2500 entrées (ou lignes) dans l'ensemble de données et 13 colonnes. Puisque nous savons qu'il y a une colonne cible, cela signifie que nous avons 12 colonnes de fonctionnalités.

Nous pouvons maintenant explorer la variable cible, la graine de citrouille Class. Puisque nous allons prédire cette variable, il est intéressant de voir combien d'échantillons de chaque graine de citrouille nous avons. Habituellement, plus la différence entre le nombre d'instances dans nos classes est petite, plus notre échantillon est équilibré et meilleures sont nos prédictions.

Cette inspection peut être effectuée en comptant chaque échantillon de semences avec le value_counts() méthode:

df['Class'].value_counts() 

Le code ci-dessus affiche :

Çerçevelik       1300
Ürgüp Sivrisi    1200
Name: Class, dtype: int64

On peut voir qu'il y a 1300 échantillons du Cerçevelik graines et 1200 échantillons de Ürgüp Sivrisi planter. Notez que la différence entre eux est de 100 échantillons, une très petite différence, ce qui est bon pour nous et indique qu'il n'est pas nécessaire de rééquilibrer le nombre d'échantillons.

Regardons aussi les statistiques descriptives de nos fonctionnalités avec le describe() méthode pour voir à quel point les données sont bien distribuées. Nous allons également transposer le tableau résultant avec T pour faciliter la comparaison entre les statistiques :

df.describe().T

Le tableau résultant est :

					count 	mean 			std 			min 		25% 			50% 			75% 			max
Area 				2500.0 	80658.220800 	13664.510228 	47939.0000 	70765.000000 	79076.00000 	89757.500000 	136574.0000
Perimeter 			2500.0 	1130.279015 	109.256418 		868.4850 	1048.829750 	1123.67200 		1203.340500 	1559.4500
Major_Axis_Length 	2500.0 	456.601840 		56.235704 		320.8446 	414.957850 		449.49660 		492.737650 		661.9113
Minor_Axis_Length 	2500.0 	225.794921 		23.297245 		152.1718 	211.245925 		224.70310 		240.672875 		305.8180
Convex_Area 		2500.0 	81508.084400 	13764.092788 	48366.0000 	71512.000000 	79872.00000 	90797.750000 	138384.0000
Equiv_Diameter 		2500.0 	319.334230 		26.891920 		247.0584 	300.167975 		317.30535 		338.057375 		417.0029
Eccentricity 		2500.0 	0.860879 		0.045167 		0.4921 		0.831700 		0.86370 		0.897025 		0.9481
Solidity 			2500.0 	0.989492 		0.003494 		0.9186 		0.988300 		0.99030 		0.991500 		0.9944
Extent 				2500.0 	0.693205 		0.060914 		0.4680 		0.658900 		0.71305 		0.740225 		0.8296
Roundness 			2500.0 	0.791533 		0.055924 		0.5546 		0.751900 		0.79775 		0.834325 		0.9396
Aspect_Ration 		2500.0 	2.041702 		0.315997 		1.1487 		1.801050 		1.98420 		2.262075 		3.1444
Compactness 		2500.0 	0.704121 		0.053067 		0.5608 		0.663475 		0.70770 		0.743500 		0.9049

En regardant le tableau, en comparant les signifier ainsi que écart-type (std), on peut voir que la plupart des caractéristiques ont une moyenne éloignée de l'écart type. Cela indique que les valeurs des données ne sont pas concentrées autour de la valeur moyenne, mais plus dispersées autour d'elle - en d'autres termes, elles ont grande variabilité.

Aussi, lorsqu'on regarde le minimum (min) et maximales (max) colonnes, certaines fonctionnalités, telles que Areaet une Convex_Area, ont de grandes différences entre les valeurs minimales et maximales. Cela signifie que ces colonnes contiennent de très petites données et également de très grandes valeurs de données, ou amplitude plus élevée entre les valeurs de données.

Avec une variabilité élevée, une amplitude élevée et des caractéristiques avec différentes unités de mesure, la plupart de nos données gagneraient à avoir la même échelle pour toutes les caractéristiques ou à être escaladé. La mise à l'échelle des données centrera les données autour de la moyenne et réduira sa variance.

Ce scénario indique probablement aussi qu'il existe des valeurs aberrantes et des valeurs extrêmes dans les données. Il est donc préférable d'avoir quelques traitement des valeurs aberrantes en plus de mettre à l'échelle les données.

Il existe des algorithmes d'apprentissage automatique, par exemple, des algorithmes basés sur des arbres tels que Classification aléatoire des forêts, qui ne sont pas affectés par la variance élevée des données, les valeurs aberrantes et les valeurs extrêmes. Régression logistique est différent, il est basé sur une fonction qui catégorise nos valeurs, et les paramètres de cette fonction peuvent être affectés par des valeurs qui sont hors de la tendance générale des données et ont une variance élevée.

Nous en comprendrons plus sur la régression logistique dans un instant lorsque nous arriverons à l'implémenter. Pour l'instant, nous pouvons continuer à explorer nos données.

Remarque: Il y a un dicton populaire en informatique : « Garbage in, garbage out » (GIGO), qui est bien adapté à l'apprentissage automatique. Cela signifie que lorsque nous avons des données erronées - des mesures qui ne décrivent pas les phénomènes en eux-mêmes, des données qui n'ont pas été comprises et bien préparées selon le type d'algorithme ou de modèle, généreront probablement une sortie incorrecte qui ne fonctionnera pas sur au jour le jour.
C'est l'une des raisons pour lesquelles l'exploration, la compréhension des données et le fonctionnement du modèle choisi sont si importants. En faisant cela, nous pouvons éviter de mettre des ordures dans notre modèle - y mettre de la valeur à la place et en tirer de la valeur.

Visualiser les données

Jusqu'à présent, avec les statistiques descriptives, nous avons un instantané quelque peu abstrait de certaines qualités des données. Une autre étape importante consiste à le visualiser et à confirmer notre hypothèse de variance, d'amplitude et de valeurs aberrantes élevées. Pour voir si ce que nous avons observé jusqu'à présent apparaît dans les données, nous pouvons tracer des graphiques.

Il est également intéressant de voir comment les caractéristiques sont liées aux deux classes qui seront prédites. Pour ce faire, importons le seaborn emballer et utiliser le pairplot graphique pour examiner chaque distribution d'entités et chaque séparation de classe par entité :

import seaborn as sns


sns.pairplot(data=df, hue='Class')

Remarque: L'exécution du code ci-dessus peut prendre un certain temps, car le pairplot combine les nuages ​​de points de toutes les entités (il le peut) et affiche également les distributions des entités.

En regardant le pairplot, nous pouvons voir que dans la plupart des cas, les points du Çerçevelik classe sont clairement séparés des points de la Ürgüp Sivrisi classer. Soit les points d'une classe sont à droite quand les autres sont à gauche, soit certains sont en haut tandis que les autres sont en bas. Si nous devions utiliser une sorte de courbe ou de ligne pour séparer les classes, cela montre qu'il est plus facile de les séparer, si elles étaient mélangées, la classification serait une tâche plus difficile.

Dans le Eccentricity, Compactness ainsi que Aspect_Ration Dans les colonnes, certains points « isolés » ou s'écartant de la tendance générale des données - les valeurs aberrantes - sont également facilement repérables.

Lorsque vous regardez la diagonale du coin supérieur gauche au coin inférieur droit du graphique, notez que les distributions de données sont également codées par couleur en fonction de nos classes. Les formes de distribution et la distance entre les deux courbes sont d'autres indicateurs de leur séparabilité - plus elles sont éloignées l'une de l'autre, mieux c'est. Dans la plupart des cas, ils ne se superposent pas, ce qui implique qu'ils sont plus faciles à séparer, ce qui contribue également à notre tâche.

En séquence, nous pouvons également tracer les boîtes à moustaches de toutes les variables avec le sns.boxplot() méthode. La plupart du temps, il est utile d'orienter les boxplots horizontalement, de sorte que les formes des boxplots soient les mêmes que les formes de distribution, nous pouvons le faire avec le orient argument:


sns.boxplot(data=df, orient='h') 

Guide définitif de la régression logistique dans Python PlatoBlockchain Data Intelligence. Recherche verticale. Aï.

Dans le graphique ci-dessus, notez que Area ainsi que Convex_Area ont une magnitude si élevée par rapport aux magnitudes des autres colonnes, qu'elles écrasent les autres boxplots. Pour pouvoir regarder toutes les boîtes à moustaches, nous pouvons redimensionner les entités et les tracer à nouveau.

Avant de faire cela, comprenons simplement que s'il y a des valeurs de caractéristiques qui sont intimement liées à d'autres valeurs, par exemple - s'il y a des valeurs qui augmentent également lorsque d'autres valeurs de caractéristiques deviennent plus grandes, avoir un corrélation positive; ou s'il y a des valeurs qui font le contraire, deviennent plus petites tandis que d'autres valeurs deviennent plus petites, ayant un corrélation négative.

Il est important d'examiner cela, car le fait d'avoir des relations solides dans les données peut signifier que certaines colonnes sont dérivées d'autres colonnes ou ont une signification similaire à notre modèle. Lorsque cela se produit, les résultats du modèle peuvent être surestimés et nous voulons des résultats plus proches de la réalité. S'il existe de fortes corrélations, cela signifie également que nous pouvons réduire le nombre de fonctionnalités et utiliser moins de colonnes, ce qui rend le modèle plus efficace. parcimonieux.

Remarque: La corrélation par défaut calculée avec le corr() la méthode est la Coefficient de corrélation de Pearson. Ce coefficient est indiqué lorsque les données sont quantitatives, distribuées normalement, n'ont pas de valeurs aberrantes et ont une relation linéaire.

Un autre choix serait de calculer Coefficient de corrélation de Spearman. Le coefficient de Spearman est utilisé lorsque les données sont ordinales, non linéaires, ont une distribution quelconque et présentent des valeurs aberrantes. Notez que nos données ne correspondent pas totalement aux hypothèses de Pearson ou de Spearman (il existe également d'autres méthodes de corrélation, telles que celle de Kendall). Comme nos données sont quantitatives et qu'il est important pour nous de mesurer leur relation linéaire, nous utiliserons le coefficient de Pearson.

Examinons les corrélations entre les variables, puis nous pourrons passer au prétraitement des données. Nous allons calculer les corrélations avec corr() méthode et visualisez-les avec Seaborn's heatmap(). La taille standard de la carte thermique a tendance à être petite, nous allons donc importer matplotlib (moteur de visualisation général/bibliothèque sur laquelle Seaborn est construit) et modifiez la taille avec figsize:

import matplotlib.pyplot as plt
plt.figure(figsize=(15, 10))

correlations = df.corr()
sns.heatmap(correlations, annot=True) 

Guide définitif de la régression logistique dans Python PlatoBlockchain Data Intelligence. Recherche verticale. Aï.

Dans cette carte thermique, les valeurs les plus proches de 1 ou -1 sont les valeurs auxquelles nous devons prêter attention. Le premier cas, dénote une forte corrélation positive et le second, une forte corrélation négative. Les deux valeurs, si elles ne sont pas supérieures à 0.8 ou -0.8, seront bénéfiques pour notre modèle de régression logistique.

Lorsqu'il existe des corrélations élevées telles que celle de 0.99 jusqu'à XNUMX fois Aspec_Ration ainsi que Compactness, cela signifie que nous pouvons choisir d'utiliser uniquement Aspec_Ration ou seulement Compactness, au lieu des deux (puisqu'ils seraient presque égaux prédicteurs les uns des autres). La même chose vaut pour Eccentricity ainsi que Compactness avec une -0.98 corrélation, pour Area ainsi que Perimeter avec une 0.94 corrélation, et quelques autres colonnes.

Pré-traitement des données

Comme nous avons déjà exploré les données pendant un certain temps, nous pouvons commencer à les pré-traiter. Pour l'instant, utilisons toutes les fonctionnalités pour la prédiction de classe. Après avoir obtenu un premier modèle, une ligne de base, nous pouvons ensuite supprimer certaines des colonnes fortement corrélées et les comparer à la ligne de base.

Les colonnes de fonctionnalités seront nos X data et la colonne class, notre y données cibles :

y = df['Class']
X = df.drop(columns=['Class'], axis=1)

Transformer des caractéristiques catégorielles en caractéristiques numériques

Concernant notre Class colonne - ses valeurs ne sont pas des nombres, cela signifie que nous devons également les transformer. Il existe de nombreuses façons de faire cette transformation; ici, nous utiliserons le replace() méthode et remplacer Çerçevelik à 0 ainsi que Ürgüp Sivrisi à 1.

y = y.replace('Çerçevelik', 0).replace('Ürgüp Sivrisi', 1)

Gardez la cartographie à l'esprit! Lors de la lecture des résultats de votre modèle, vous souhaiterez les reconvertir au moins dans votre esprit, ou les reconvertir en nom de classe pour les autres utilisateurs.

Diviser les données en ensembles d'apprentissage et de test

Dans notre exploration, nous avons noté que les fonctionnalités nécessitaient une mise à l'échelle. Si nous faisions la mise à l'échelle maintenant, ou de manière automatique, nous mettrions à l'échelle les valeurs avec l'ensemble de X ainsi que y. Dans ce cas, nous présenterions fuite de données, car les valeurs de l'ensemble de test à venir auraient eu un impact sur la mise à l'échelle. La fuite de données est une cause fréquente de résultats non reproductibles et de hautes performances illusoires des modèles ML.

Penser à la mise à l'échelle montre que nous devons d'abord diviser X ainsi que y données plus loin dans les ensembles de train et de test, puis à s'adapter un scaler sur l'ensemble de formation, et à transform à la fois le train et les ensembles de test (sans jamais que l'ensemble de test ait un impact sur le scaler qui le fait). Pour cela, nous utiliserons les outils de Scikit-Learn train_test_split() méthode:

from sklearn.model_selection import train_test_split
SEED = 42 

X_train, X_test, y_train, y_test = train_test_split(X, y, 
                                                    test_size=.25, 
                                                    random_state=SEED)

Paramètres test_size=.25 s'assure que nous utilisons 25 % des données pour les tests et 75 % pour la formation. Cela pourrait être omis, une fois que c'est la division par défaut, mais le Pythonique façon d'écrire du code indique qu'être « explicite vaut mieux qu'implicite ».

Remarque: La phrase "explicite vaut mieux qu'implicite" fait référence à Le zen de Python, ou PEP20. Il présente quelques suggestions pour écrire du code Python. Si ces suggestions sont suivies, le code est considéré Pythonique. Vous pouvez en savoir plus ici.

Après avoir divisé les données en ensembles d'apprentissage et de test, il est recommandé d'examiner le nombre d'enregistrements dans chaque ensemble. Cela peut se faire avec le shape attribut:

X_train.shape, X_test.shape, y_train.shape, y_test.shape

Cela affiche :

((1875, 12), (625, 12), (1875,), (625,))

Nous pouvons voir qu'après la scission, nous avons 1875 enregistrements pour la formation et 625 pour les tests.

Mise à l'échelle des données

Une fois que nos ensembles d'entraînement et de test sont prêts, nous pouvons procéder à la mise à l'échelle des données avec Scikit-Learn StandardScaler objet (ou d'autres échelles fournies par la bibliothèque). Pour éviter les fuites, le détartreur est monté sur le X_train les données et les valeurs d'entraînement sont ensuite utilisées pour mettre à l'échelle - ou transformer - à la fois les données d'entraînement et de test :

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)

Étant donné que vous appelez généralement :

scaler.fit(X_train)
X_train = scaler.transform(X_train)
X_test = scaler.transform(X_test)

Les deux premières lignes peuvent être réduites avec un singulier fit_transform() call, qui adapte le scaler sur le plateau, et le transforme en une seule fois. Nous pouvons maintenant reproduire les graphiques de la boîte à moustaches pour voir la différence après la mise à l'échelle des données.

Étant donné que la mise à l'échelle supprime les noms de colonnes, avant le traçage, nous pouvons organiser à nouveau les données d'entraînement dans une trame de données avec des noms de colonnes pour faciliter la visualisation :

column_names = df.columns[:12] 
X_train = pd.DataFrame(X_train, columns=column_names)

sns.boxplot(data=X_train, orient='h')

Guide définitif de la régression logistique dans Python PlatoBlockchain Data Intelligence. Recherche verticale. Aï.

Nous pouvons enfin voir tous nos boxplots ! Notez que tous ont des valeurs aberrantes et les caractéristiques qui présentent une distribution plus éloignée de la normale (qui ont des courbes biaisées vers la gauche ou vers la droite), telles que Solidity, Extent, Aspect_Rationet une Compactedness, sont les mêmes qui avaient des corrélations plus élevées.

Suppression des valeurs aberrantes avec la méthode IQR

Nous savons déjà que la régression logistique peut être affectée par des valeurs aberrantes. L'une des façons de les traiter est d'utiliser une méthode appelée Gamme interquartile or IQR. La première étape de la méthode IQR consiste à diviser nos données de train en quatre parties, appelées quartiles. Le premier quartile, Q1, représente 25 % des données, la seconde, Q2, à 50%, le troisième, Q3, à 75%, et le dernier, Q4, à 100 %. Les cases du boxplot sont définies par la méthode IQR et en sont une représentation visuelle.

Considérant une boîte à moustaches horizontale, la ligne verticale à gauche marque 25 % des données, la ligne verticale au milieu, 50 % des données (ou la médiane) et la dernière ligne verticale à droite, 75 % des données . Plus les deux carrés définis par les lignes verticales sont de taille uniforme - ou plus la ligne verticale médiane est au milieu - signifie que nos données sont plus proches de la distribution normale ou moins asymétriques, ce qui est utile pour notre analyse.

Outre la boîte IQR, il y a aussi des lignes horizontales des deux côtés de celle-ci. Ces lignes marquent les valeurs de distribution minimale et maximale définies par

$$
Minimum = Q1 – 1.5*IQR
$$

ainsi que

$$
Maximum = Q3 + 1.5*IQR
$$

L'IQR est exactement la différence entre Q3 et Q1 (ou Q3 - Q1) et c'est le point de données le plus central. C'est pourquoi, lors de la recherche de l'IQR, nous finissons par filtrer les valeurs aberrantes dans les extrémités des données, ou dans les points minimum et maximum. Les boîtes à moustaches nous donnent un aperçu de ce que sera le résultat de la méthode IQR.

Guide définitif de la régression logistique dans Python PlatoBlockchain Data Intelligence. Recherche verticale. Aï.

Nous pouvons utiliser des pandas quantile() méthode pour trouver nos quantiles, et iqr du scipy.stats package pour obtenir la plage de données interquartile pour chaque colonne :

from scipy.stats import iqr

Q1 = X_train.quantile(q=.25)
Q3 = X_train.quantile(q=.75)

IQR = X_train.apply(iqr)

Maintenant que nous avons Q1, Q3 et IQR, nous pouvons filtrer les valeurs les plus proches de la médiane :


minimum = X_train < (Q1-1.5*IQR)
maximum = X_train > (Q3+1.5*IQR)


filter = ~(minimum | maximum).any(axis=1)


X_train = X_train[filter]

Après avoir filtré nos lignes d'entraînement, nous pouvons voir combien d'entre elles sont encore dans les données avec shape:

X_train.shape

Cela se traduit par:

(1714, 12)

On voit que le nombre de lignes est passé de 1875 à 1714 après filtrage. Cela signifie que 161 lignes contenaient des valeurs aberrantes ou 8.5 % des données.

Remarque: Il est conseillé que le filtrage des valeurs aberrantes, la suppression des valeurs NaN et les autres actions impliquant le filtrage et le nettoyage des données restent en dessous ou jusqu'à 10 % des données. Essayez de penser à d'autres solutions si votre filtrage ou suppression dépasse 10 % de vos données.

Après avoir supprimé les valeurs aberrantes, nous sommes presque prêts à inclure des données dans le modèle. Pour l'ajustement du modèle, nous utiliserons les données du train. X_train est filtré, mais qu'en est-il y_train?

y_train.shape

Cela produit:

(1875,)

Remarquerez que y_train a encore 1875 lignes. Nous devons faire correspondre le nombre de y_train lignes au nombre de X_train lignes et pas seulement arbitrairement. Nous devons supprimer les valeurs y des instances de graines de citrouille que nous avons supprimées, qui sont probablement dispersées dans le y_train Positionner. Le filtré X_train a toujours ses indices d'origine et l'indice a des lacunes où nous avons supprimé les valeurs aberrantes ! On peut alors utiliser l'indice du X_train DataFrame pour rechercher les valeurs correspondantes dans y_train:

y_train = y_train.iloc[X_train.index]

Après avoir fait cela, nous pouvons regarder le y_train forme à nouveau :

y_train.shape

Quelles sorties :

(1714,)

Consultez notre guide pratique et pratique pour apprendre Git, avec les meilleures pratiques, les normes acceptées par l'industrie et la feuille de triche incluse. Arrêtez de googler les commandes Git et en fait apprendre il!

Maintenant, y_train a également 1714 lignes et ils sont les mêmes que le X_train Lignes. Nous sommes enfin prêts à créer notre modèle de régression logistique !

Implémentation du modèle de régression logistique

Le plus dur est fait ! Le prétraitement est généralement plus difficile que le développement de modèles, lorsqu'il s'agit d'utiliser des bibliothèques comme Scikit-Learn, qui ont simplifié l'application des modèles ML à seulement quelques lignes.

Tout d'abord, nous importons le LogisticRegression classe et instanciez-la, en créant une LogisticRegression objet:

from sklearn.linear_model import LogisticRegression
logreg = LogisticRegression(random_state=SEED)

Deuxièmement, nous adaptons nos données sur les trains aux logreg modèle avec le fit() méthode, et prédire nos données de test avec la predict() méthode, en stockant les résultats sous forme y_pred:



logreg.fit(X_train.values, y_train)
y_pred = logreg.predict(X_test)

Nous avons déjà fait des prédictions avec notre modèle ! Examinons les 3 premières lignes de X_train pour voir quelles données nous avons utilisées :

X_train[:3]

Le code ci-dessus affiche :

       Area          Perimeter     Major_Axis_Length    Minor_Axis_Length    Convex_Area   Equiv_Diameter       Eccentricity  Solidity      Extent        Roundness     Aspect_Ration        Compactness
0      -1.098308     -0.936518     -0.607941            -1.132551            -1.082768     -1.122359            0.458911      -1.078259     0.562847      -0.176041     0.236617             -0.360134
1      -0.501526     -0.468936     -0.387303            -0.376176            -0.507652     -0.475015            0.125764      0.258195      0.211703      0.094213      -0.122270            0.019480
2      0.012372      -0.209168     -0.354107            0.465095              0.003871      0.054384            -0.453911     0.432515      0.794735      0.647084      -0.617427            0.571137

Et aux 3 premières prédictions de y_pred pour voir les résultats :

y_pred[:3] 

Cela se traduit par:

array([0, 0, 0])

Pour ces trois rangées, nos prédictions étaient qu'il s'agissait de graines de première classe, Çerçevelik.

Avec régression logistique, au lieu de prédire la classe finale, comme 0, nous pouvons également prédire la probabilité qu'a la ligne d'appartenir au 0 classer. C'est ce qui se passe réellement lorsque la régression logistique classe les données, et que predict() La méthode passe ensuite cette prédiction à travers un seuil pour renvoyer une classe "dure". Pour prédire la probabilité d'appartenir à une classe, predict_proba() est utilisé:

y_pred_proba = logreg.predict_proba(X_test)

Examinons également les 3 premières valeurs des prédictions des probabilités y :

y_pred_proba[:3] 

Quelles sorties :

        # class 0   class 1   
array([[0.54726628, 0.45273372],
       [0.56324527, 0.43675473],
       [0.86233349, 0.13766651]])

Maintenant, au lieu de trois zéros, nous avons une colonne pour chaque classe. Dans la colonne de gauche, en commençant par 0.54726628, sont les probabilités des données appartenant à la classe 0; et dans la colonne de droite, en commençant par 0.45273372, sont la probabilité qu'il appartienne à la classe 1.

Remarque: Cette différence de classification est également connue sous le nom de dur ainsi que qualité, que vous pourrez utilisé prédiction. La prédiction dure place la prédiction dans une classe, tandis que les prédictions douces génèrent le probabilité de l'instance appartenant à une classe.

Il y a plus d'informations sur la façon dont la sortie prévue a été faite. Ce n'était pas en fait 0, mais 55 % de chances d'être classé 0, et 45 % de chances d'être classé 1. Cela montre comment les trois premiers X_test points de données, relatifs à la classe 0, ne sont vraiment clairs qu'en ce qui concerne le troisième point de données, avec une probabilité de 86 % - et pas tellement pour les deux premiers points de données.

Lors de la communication de résultats à l'aide de méthodes ML, il est généralement préférable de renvoyer une classe souple et la probabilité associée en tant que "confiance" de ce classement.

Nous parlerons davantage de la façon dont cela est calculé lorsque nous approfondirons le modèle. À ce stade, nous pouvons passer à l'étape suivante.

Évaluation du modèle avec des rapports de classification

La troisième étape consiste à voir comment le modèle fonctionne sur les données de test. Nous pouvons importer Scikit-Learn classification_report() et passer notre y_test ainsi que y_pred comme arguments. Après cela, nous pouvons imprimer sa réponse.

Le rapport de classification contient les mesures de classification les plus utilisées, telles que avec précision, rappeler, score f1et une précision.

  1. La précision: pour comprendre quelles valeurs de prédiction correctes ont été considérées comme correctes par notre classifieur. La précision divisera ces vraies valeurs positives par tout ce qui a été prédit comme positif :

$$
précision = frac{texte{vrai positif}}{texte{vrai positif} + texte{faux positif}}
$$

  1. Rappeler: pour comprendre combien de vrais positifs ont été identifiés par notre classificateur. Le rappel est calculé en divisant les vrais positifs par tout ce qui aurait dû être prédit comme positif :

$$
rappel = frac{texte{vrai positif}}{texte{vrai positif} + texte{faux négatif}}
$$

  1. Score F1: est l'équilibré ou moyenne harmonique de précision et de rappel. La valeur la plus basse est 0 et la plus élevée est 1. Lorsque f1-score est égal à 1, cela signifie que toutes les classes ont été correctement prédites – c'est un score très difficile à obtenir avec des données réelles :

$$
texte{f1-score} = 2* frac{texte{précision} * texte{rappel}}{texte{précision} + texte{rappel}}
$$

  1. Précision: décrit le nombre de prédictions que notre classificateur a eues. La valeur de précision la plus basse est 0 et la plus élevée est 1. Cette valeur est généralement multipliée par 100 pour obtenir un pourcentage :

$$
précision = frac{text{nombre de prédictions correctes}}{text{nombre total de prédictions}}
$$

Remarque: Il est extrêmement difficile d'obtenir une précision de 100 % sur des données réelles, si cela se produit, sachez qu'une fuite ou quelque chose de mal peut se produire - il n'y a pas de consensus sur une valeur de précision idéale et cela dépend également du contexte. Une valeur de 70 %, ce qui signifie que le classificateur fera des erreurs sur 30 % des données, ou au-dessus de 70 %, est généralement suffisante pour la plupart des modèles.

from sklearn.metrics import classification_report
cr = classification_report(y_test, y_pred)
print(cr)

Nous pouvons ensuite examiner la sortie du rapport de classification :

				precision    recall  f1-score   support

           0       0.83      0.91      0.87       316
           1       0.90      0.81      0.85       309

    accuracy                           0.86       625
   macro avg       0.86      0.86      0.86       625
weighted avg       0.86      0.86      0.86       625

C'est notre résultat. Remarquerez que precision, recall, f1-scoreet une accuracy les mesures sont toutes très élevées, supérieures à 80 %, ce qui est idéal – mais ces résultats ont probablement été influencés par des corrélations élevées et ne se maintiendront pas à long terme.

La précision du modèle est de 86 %, ce qui signifie qu'il se trompe de classification 14 % du temps. Nous avons cette information globale, mais il serait intéressant de savoir si les 14 % d'erreurs se produisent concernant la classification de la classe 0 ou classe 1. Pour identifier quelles classes sont identifiées à tort comme lesquelles et à quelle fréquence - nous pouvons calculer et tracer un matrice de confusion des prédictions de notre modèle.

Évaluation du modèle avec une matrice de confusion

Calculons puis traçons la matrice de confusion. Après cela, nous pouvons en comprendre chaque partie. Pour tracer la matrice de confusion, nous utiliserons Scikit-Learn confusion_matrix(), que nous importerons depuis le metrics module.

La matrice de confusion est plus facile à visualiser à l'aide d'un Seaborn heatmap(). Ainsi, après l'avoir généré, nous passerons notre matrice de confusion comme argument pour la heatmap :

from sklearn.metrics import confusion_matrix

cm = confusion_matrix(y_test, y_pred)
sns.heatmap(cm, annot=True, fmt='d')

Guide définitif de la régression logistique dans Python PlatoBlockchain Data Intelligence. Recherche verticale. Aï.

  1. Matrice de confusion: la matrice montre combien d'échantillons le modèle a eu raison ou tort pour chaque classe. Les valeurs qui étaient correctes et correctement prédites sont appelées vrais positifs, et ceux qui ont été prédits comme positifs mais qui ne l'étaient pas sont appelés faux positifs. La même nomenclature de vrais négatifs ainsi que faux négatifs est utilisé pour les valeurs négatives ;

En regardant le graphique de la matrice de confusion, nous pouvons voir que nous avons 287 des valeurs qui étaient 0 et prédit comme 0 - ou vrais positifs pour la classe 0 (les graines de Çerçevelik). Nous avons également 250 vrais positifs pour la classe 1 (Graines d'Ürgüp Sivrisi). Les vrais positifs sont toujours situés dans la diagonale de la matrice qui va du coin supérieur gauche au coin inférieur droit.

Nous avons également 29 des valeurs censées être 0, mais prédit comme 1 (faux positifs) et 59 des valeurs qui étaient 1 et prédit comme 0 (faux négatifs). Avec ces chiffres, on peut comprendre que l'erreur que fait le plus le modèle est qu'il prédit des faux négatifs. Ainsi, cela peut surtout finir par classer une graine d'Ürgüp Sivrisi comme une graine de Çerçevelik.

Ce genre d'erreur s'explique aussi par le rappel de classe de 81% 1. Notez que les métriques sont connectées. Et la différence dans le rappel vient du fait d'avoir 100 échantillons de moins de la classe Ürgüp Sivrisi. C'est l'une des implications d'avoir seulement quelques échantillons de moins que l'autre classe. Pour améliorer encore le rappel, vous pouvez soit expérimenter avec des pondérations de classe, soit utiliser plus d'échantillons d'Ürgüp Sivrisi.

Jusqu'à présent, nous avons exécuté la plupart des étapes traditionnelles de la science des données et utilisé le modèle de régression logistique comme une boîte noire.

Remarque: Si vous voulez aller plus loin, utilisez Validation croisée (CV) et grille de recherche rechercher, respectivement, le modèle qui généralise le plus les données, et les meilleurs paramètres du modèle qui sont choisis avant l'apprentissage, ou hyperparamètres.

Idéalement, avec CV et Grid Search, vous pouvez également implémenter une manière concaténée d'effectuer les étapes de prétraitement des données, la division des données, la modélisation et l'évaluation - ce qui est facilité avec Scikit-Learn pipelines.

Il est maintenant temps d'ouvrir la boîte noire et de regarder à l'intérieur, pour approfondir la compréhension du fonctionnement de la régression logistique.

Approfondir le fonctionnement réel de la régression logistique

La régression mot n'est pas là par accident, pour comprendre ce que fait la régression logistique, nous pouvons nous souvenir de ce que son frère, la régression linéaire, fait aux données. La formule de régression linéaire était la suivante :

$$
y = b_0 + b_1 * x_1 + b_2 * x_2 + b_3 * x_3 + ldots + b_n * x_n
$$

Dans lequel b0 était l'ordonnée à l'origine de la régression, b1 le coefficient et x1 les données.

Cette équation a abouti à une ligne droite qui a été utilisée pour prédire de nouvelles valeurs. Rappelant l'introduction, la différence maintenant est que nous ne prédirons pas de nouvelles valeurs, mais une classe. Cette ligne droite doit donc changer. Avec la régression logistique, nous introduisons une non-linéarité et la prédiction est maintenant faite en utilisant une courbe au lieu d'une droite :

Guide définitif de la régression logistique dans Python PlatoBlockchain Data Intelligence. Recherche verticale. Aï.

Observez que tandis que la ligne de régression linéaire continue et est composée de valeurs infinies continues, la courbe de régression logistique peut être divisée au milieu et a des valeurs extrêmes de 0 et 1. Cette forme en "S" est la raison pour laquelle il classe les données - les points les plus proches ou tombant sur l'extrémité la plus élevée appartiennent à la classe 1, tandis que les points situés dans le quadrant inférieur ou plus proches de 0 appartiennent à la classe 0. Le milieu de le "S" est le milieu entre 0 et 1, 0.5 - c'est le seuil pour les points de régression logistique.

Guide définitif de la régression logistique dans Python PlatoBlockchain Data Intelligence. Recherche verticale. Aï.

Nous comprenons déjà la différence visuelle entre régression logistique et linéaire, mais qu'en est-il de la formule ? La formule de régression logistique est la suivante :

$$
y = b_0 + b_1 * x_1 + b_2 * x_2 + b_3 * x_3 + ldots + b_n * x_n
$$

Il peut aussi s'écrire :

$$
y_{prob} = frac{1}{1 + e^{(b_0 + b_1 * x_1 + b_2 * x_2 + b_3 * x_3 + ldots + b_n * x_n)}}
$$

Ou même s'écrire :

$$
y_{prob} = frac{e^{(b_0 + b_1 * x_1 + b_2 * x_2 + b_3 * x_3 + ldots + b_n * x_n)}}{1 + e^{(b_0 + b_1 * x_1 + b_2 * x_2 + b_3 * x_3 + ldots + b_n * x_n)}}
$$

Dans l'équation ci-dessus, nous avons la probabilité d'entrée, au lieu de sa valeur. Il a 1 comme numérateur, il peut donc donner une valeur comprise entre 0 et 1, et 1 plus une valeur dans son dénominateur, de sorte que sa valeur est 1 et quelque chose - cela signifie que le résultat de la fraction entière ne peut pas être supérieur à 1 .

Et quelle est la valeur qui est dans le dénominateur ? Il est e, la base du logarithme népérien (environ 2.718282), élevée à la puissance de régression linéaire :

$$
e^{(b_0 + b_1 * x_1 + b_2 * x_2 + b_3 * x_3 + ldots + b_n * x_n)}
$$

Une autre façon de l'écrire serait :

$$
ln gauche( frac{p}{1-p} droite) = {(b_0 + b_1 * x_1 + b_2 * x_2 + b_3 * x_3 + ldots + b_n * x_n)}
$$

Dans cette dernière équation, ln est le logarithme naturel (base e) et p est la probabilité, donc le logarithme de la probabilité du résultat est le même que le résultat de la régression linéaire.

En d'autres termes, avec le résultat de la régression linéaire et le logarithme naturel, nous pouvons arriver à la probabilité qu'une entrée appartienne ou non à une classe conçue.

L'ensemble du processus de dérivation de la régression logistique est le suivant :

$$
p{X} = frac{e^{(b_0 + b_1 * x_1 + b_2 * x_2 + b_3 * x_3 + ldots + b_n * x_n)}}{1 + e^{(b_0 + b_1 * x_1 + b_2 * x_2 + b_3 * x_3 + ldots + b_n * x_n)}}
$$

$$
p(1 + e^{(b_0 + b_1 * x_1 + b_2 * x_2 + b_3 * x_3 + ldots + b_n * x_n)}) = e^{(b_0 + b_1 * x_1 + b_2 *x_2 + b_3 * x_3 + ldots + b_n * x_n)}
$$

$$
p + p*e^{(b_0 + b_1 * x_1 + b_2 * x_2 + b_3 * x_3 + ldots + b_n * x_n)} = e^{(b_0 + b_1 * x_1 + b_2 *x_2 + b_3 * x_3 + ldots + b_n * x_n)}
$$

p
=

e

(

b
0

+

b
1

*

x
1

+

b
2

*

x
2

+

b
3

*

x
3

+
...
+

b
n

*

x
n

)

-
p
*

e

(

b
0

+

b
1

*

x
1

+

b
2

*

x
2

+

b
3

*

x
3

+
...
+

b
n

*

x
n

)

$$
frac{p}{1-p} = e^{(b_0 + b_1 * x_1 + b_2 *x_2 + b_3 * x_3 + ldots + b_n * x_n)}
$$

$$
ln gauche( frac{p}{1-p} droite) = (b_0 + b_1 * x_1 + b_2 *x_2 + b_3 * x_3 + ldots + b_n * x_n)
$$

Cela signifie que le modèle de régression logistique a également des coefficients et une valeur d'interception. Parce qu'il utilise une régression linéaire et y ajoute une composante non linéaire avec le logarithme népérien (e).

Nous pouvons voir les valeurs des coefficients et l'ordonnée à l'origine de notre modèle, de la même manière que nous l'avons fait pour la régression linéaire, en utilisant coef_ ainsi que intercept_ Propriétés:

logreg.coef_

Qui affiche les coefficients de chacune des 12 caractéristiques :

array([[ 1.43726172, -1.03136968,  0.24099522, -0.61180768,  1.36538261,
        -1.45321951, -1.22826034,  0.98766966,  0.0438686 , -0.78687889,
         1.9601197 , -1.77226097]])
logreg.intercept_

Cela se traduit par :

array([0.08735782])

Avec les coefficients et les valeurs d'interception, nous pouvons calculer les probabilités prédites de nos données. Prenons le premier X_test valeurs à nouveau, à titre d'exemple :

X_test[:1]

Cela renvoie la première ligne de X_test en tant que tableau NumPy :

array([[-1.09830823, -0.93651823, -0.60794138, -1.13255059, -1.0827684 ,
        -1.12235877,  0.45891056, -1.07825898,  0.56284738, -0.17604099,
         0.23661678, -0.36013424]])

Suite à l'équation initiale :

$$
p{X} = frac{e^{(b_0 + b_1 * x_1 + b_2 * x_2 + b_3 * x_3 + ldots + b_n * x_n)}}{1 + e^{(b_0 + b_1 * x_1 + b_2 * x_2 + b_3 * x_3 + ldots + b_n * x_n)}}
$$

En python, nous avons :

import math

lin_reg = logreg.intercept_[0] + 
((logreg.coef_[0][0]* X_test[:1][0][0])+ 
(logreg.coef_[0][1]* X_test[:1][0][1])+ 
(logreg.coef_[0][2]* X_test[:1][0][2])+ 
(logreg.coef_[0][3]* X_test[:1][0][3])+ 
(logreg.coef_[0][4]* X_test[:1][0][4])+ 
(logreg.coef_[0][5]* X_test[:1][0][5])+ 
(logreg.coef_[0][6]* X_test[:1][0][6])+ 
(logreg.coef_[0][7]* X_test[:1][0][7])+ 
(logreg.coef_[0][8]* X_test[:1][0][8])+ 
(logreg.coef_[0][9]* X_test[:1][0][9])+ 
(logreg.coef_[0][10]* X_test[:1][0][10])+ 
(logreg.coef_[0][11]* X_test[:1][0][11]))

px = math.exp(lin_reg)/(1 +(math.exp(lin_reg)))
px

Cela se traduit par:

0.45273372469369133

Si nous regardons à nouveau le predict_proba résultat du premier X_test ligne, nous avons :

logreg.predict_proba(X_test[:1])


Cela signifie que l'équation de régression logistique d'origine nous donne la probabilité de l'entrée concernant la classe 1, pour savoir quelle probabilité est pour la classe 0, on peut simplement :

1 - px


Notez que les deux px ainsi que 1-px sont identiques à predict_proba résultats. Voici comment la régression logistique est calculée et pourquoi régression fait partie de son nom. Mais qu'en est-il du terme logistique?

Le terme logistique vient de Logit, qui est une fonction que nous avons déjà vue :

$$
ln gauche( frac{p}{1-p} droite)
$$

Nous venons de le calculer avec px ainsi que 1-px. C'est le logit, aussi appelé log-cotes puisqu'il est égal au logarithme des cotes où p est une probabilité.

Conclusion

Dans ce guide, nous avons étudié l'un des algorithmes de classification d'apprentissage automatique les plus fondamentaux, à savoir régression logistique.

Initialement, nous avons implémenté la régression logistique comme une boîte noire avec la bibliothèque d'apprentissage automatique de Scikit-Learn, et plus tard nous l'avons compris étape par étape pour avoir une idée claire du pourquoi et de l'origine des termes régression et logistique.

Nous avons également exploré et étudié les données, comprenant qu'il s'agit de l'une des parties les plus cruciales d'une analyse de science des données.

À partir de là, je vous conseillerais de jouer avec régression logistique multiclasse, régression logistique pour plus de deux classes - vous pouvez appliquer le même algorithme de régression logistique pour d'autres ensembles de données qui ont plusieurs classes et interpréter les résultats.

Remarque: Une bonne collection d'ensembles de données est disponible ici pour que tu joues avec.

Je vous conseillerais également d'étudier la L1 et la L2 régularisations, ils sont un moyen de "pénaliser" les données les plus élevées afin qu'elles se rapprochent de la normale, en maintenant la complexité du modèle, afin que l'algorithme puisse obtenir un meilleur résultat. L'implémentation Scikit-Learn que nous avons utilisée a déjà la régularisation L2 par défaut. Une autre chose à regarder est la différence solveurs tels que lbgs, qui optimisent les performances de l'algorithme de régression logistique.

Il est également important de jeter un coup d'œil sur statistique Approche de la régression logistique. Il a hypothèses sur le comportement des données, et sur d'autres statistiques qui doivent tenir pour garantir des résultats satisfaisants, telles que :

  • les observations sont indépendantes ;
  • il n'y a pas de multicolinéarité entre les variables explicatives ;
  • il n'y a pas de valeurs aberrantes extrêmes ;
  • il existe une relation linéaire entre les variables explicatives et le logit de la variable réponse ;
  • la taille de l'échantillon est suffisamment grande.

Remarquez combien de ces hypothèses étaient déjà couvertes par notre analyse et notre traitement des données.

J'espère que vous continuerez à explorer ce que la régression logistique a à offrir dans toutes ses différentes approches !

Horodatage:

Plus de Stackabuse