Introduzione
A volte confuso con regressione lineare dai novizi – per la condivisione del termine regressione - regressione logistica è molto diverso da regressione lineare. Mentre la regressione lineare prevede valori come 2, 2.45, 6.77 o valori continui, rendendolo un regressione algoritmo, regressione logistica prevede valori come 0 o 1, 1 o 2 o 3, che sono valori discreti, rendendolo un classificazione algoritmo. Sì, si chiama regressione ma è un classificazione algoritmo. Ne parleremo tra un momento.
Pertanto, se il tuo problema di data science coinvolge valori continui, puoi applicare a regressione algoritmo (la regressione lineare è uno di questi). In caso contrario, se si tratta di classificare input, valori discreti o classi, è possibile applicare a classificazione algoritmo (la regressione logistica è uno di questi).
In questa guida, eseguiremo la regressione logistica in Python con la libreria Scikit-Learn. Spiegheremo anche perché la parola "regressione" è presente nel nome e come funziona la regressione logistica.
Per fare ciò, caricheremo prima i dati che verranno classificati, visualizzati e pre-elaborati. Quindi, costruiremo un modello di regressione logistica che comprenderà quei dati. Questo modello verrà quindi valutato e utilizzato per prevedere i valori sulla base di nuovi input.
Motivazione
L'azienda per cui lavori ha stretto una partnership con un'azienda agricola turca. Questa partnership prevede la vendita di semi di zucca. I semi di zucca sono molto importanti per l'alimentazione umana. Contengono una buona proporzione di carboidrati, grassi, proteine, calcio, potassio, fosforo, magnesio, ferro e zinco.
Nel team di scienza dei dati, il tuo compito è distinguere tra i tipi di semi di zucca semplicemente utilizzando i dati - o classificazione i dati in base al tipo di seme.
La fattoria turca lavora con due tipi di semi di zucca, uno si chiama Cercevelik e l'altro Ürgup Sivrisi.
Per classificare i semi di zucca, il tuo team ha seguito il documento del 2021 “L'uso dei metodi di machine learning nella classificazione dei semi di zucca (Cucurbita pepo L.). Risorse genetiche ed evoluzione delle colture” di Koklu, Sarigil e Ozbek: in questo articolo c'è una metodologia per fotografare ed estrarre le misurazioni dei semi dalle immagini.
Dopo aver completato il processo descritto nel documento, sono state estratte le seguenti misurazioni:
- Zona – il numero di pixel all'interno dei bordi di un seme di zucca
- Perimetro – la circonferenza in pixel di un seme di zucca
- Lunghezza dell'asse maggiore – anche la circonferenza in pixel di un seme di zucca
- Lunghezza dell'asse minore – la piccola distanza dell'asse di un seme di zucca
- Eccentricità – l'eccentricità di un seme di zucca
- Area convessa – il numero di pixel del guscio convesso più piccolo nella regione formata dal seme di zucca
- Estensione – il rapporto tra un'area di semi di zucca e i pixel del riquadro di delimitazione
- Diametro equivalente – la radice quadrata della moltiplicazione dell'area del seme di zucca per quattro divisa per pi
- Compattezza – la proporzione dell'area del seme di zucca rispetto all'area del cerchio con la stessa circonferenza
- Solidity – la condizione convessa e convessa dei semi di zucca
- Rotondità – l'ovalità dei semi di zucca senza considerare le distorsioni dei bordi
- Aspect Ratio – le proporzioni dei semi di zucca
Queste sono le misure con cui devi lavorare. Oltre alle misure, c'è anche il Classe etichetta per i due tipi di semi di zucca.
Per iniziare a classificare i semi, importiamo i dati e iniziamo a guardarli.
Comprendere il set di dati
Nota: Puoi scaricare il set di dati della zucca qui.
Dopo aver scaricato il set di dati, possiamo caricarlo in una struttura dataframe utilizzando il file pandas
biblioteca. Poiché si tratta di un file excel, utilizzeremo il read_excel()
Metodo:
import pandas as pd
fpath = 'dataset/pumpkin_seeds_dataset.xlsx'
df = pd.read_excel(fpath)
Una volta caricati i dati, possiamo dare una rapida occhiata alle prime 5 righe usando il file head()
Metodo:
df.head()
Questo risulta in:
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
Qui abbiamo tutte le misure nelle rispettive colonne, la nostra Caratteristiche, E anche il Classe colonna, ns bersaglio, che è l'ultimo nel dataframe. Possiamo vedere quante misurazioni abbiamo usando il shape
attributo:
df.shape
L'output è:
(2500, 13)
Il risultato della forma ci dice che ci sono 2500 voci (o righe) nel set di dati e 13 colonne. Poiché sappiamo che esiste una colonna di destinazione, ciò significa che abbiamo 12 colonne di funzionalità.
Ora possiamo esplorare la variabile target, il seme di zucca Class
. Dal momento che prevediamo quella variabile, è interessante vedere quanti campioni di ciascun seme di zucca abbiamo. Di solito, minore è la differenza tra il numero di istanze nelle nostre classi, più equilibrato è il nostro campione e migliori sono le nostre previsioni.
Questa ispezione può essere eseguita contando ogni campione di semi con il value_counts()
Metodo:
df['Class'].value_counts()
Il codice sopra mostra:
Çerçevelik 1300
Ürgüp Sivrisi 1200
Name: Class, dtype: int64
Possiamo vedere che ci sono 1300 campioni di Cercevelik seme e 1200 campioni di Ürgup Sivrisi seme. Si noti che la differenza tra loro è di 100 campioni, una differenza molto piccola, il che è positivo per noi e indica che non è necessario riequilibrare il numero di campioni.
Diamo un'occhiata anche alle statistiche descrittive delle nostre funzionalità con il describe()
metodo per vedere quanto sono ben distribuiti i dati. Trasporremo anche la tabella risultante con T
per semplificare il confronto tra le statistiche:
df.describe().T
La tabella risultante è:
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
Guardando la tabella, quando si confrontano i significare ed deviazione standard (std
) colonne, si può notare che la maggior parte delle caratteristiche ha una media che è lontana dalla deviazione standard. Ciò indica che i valori dei dati non sono concentrati attorno al valore medio, ma più sparsi intorno ad esso, in altre parole, lo sono elevata variabilità.
Inoltre, guardando il ordine (min
) e massimo (max
) colonne, alcune funzionalità, ad esempio Area
e Convex_Area
, presentano grandi differenze tra i valori minimo e massimo. Ciò significa che quelle colonne contengono dati molto piccoli e anche valori di dati molto grandi, o ampiezza maggiore tra valori di dati.
Con elevata variabilità, ampiezza elevata e caratteristiche con unità di misura diverse, la maggior parte dei nostri dati trarrebbero vantaggio dall'avere la stessa scala per tutte le caratteristiche o dall'essere scalato. Il ridimensionamento dei dati centra i dati attorno alla media e ne riduce la varianza.
Questo scenario probabilmente indica anche la presenza di valori anomali e valori estremi nei dati. Quindi, è meglio averne alcuni trattamento anomalo oltre a ridimensionare i dati.
Esistono alcuni algoritmi di apprendimento automatico, ad esempio algoritmi basati su alberi come Classificazione casuale delle foreste, che non sono influenzati da varianza dei dati elevata, valori anomali e valori estremi. Regressione logistica è diverso, si basa su una funzione che classifica i nostri valori e i parametri di tale funzione possono essere influenzati da valori che sono fuori dall'andamento generale dei dati e hanno una varianza elevata.
Capiremo di più sulla regressione logistica tra un po' quando la implementeremo. Per ora, possiamo continuare a esplorare i nostri dati.
Nota: C'è un detto popolare in Informatica: "Spazzatura dentro, spazzatura fuori" (GIGO), che è adatto per l'apprendimento automatico. Ciò significa che quando abbiamo dati spazzatura - misurazioni che non descrivono i fenomeni in sé, dati che non sono stati compresi e ben preparati in base al tipo di algoritmo o modello, genereranno probabilmente un output errato che non funzionerà su una base quotidiana.
Questo è uno dei motivi per cui l'esplorazione, la comprensione dei dati e il funzionamento del modello scelto sono così importanti. In questo modo, possiamo evitare di mettere spazzatura nel nostro modello, mettendo invece valore in esso e ottenendo valore.
Visualizzazione dei dati
Finora, con le statistiche descrittive, abbiamo un'istantanea in qualche modo astratta di alcune qualità dei dati. Un altro passo importante è visualizzarlo e confermare la nostra ipotesi di varianza, ampiezza e valori anomali elevati. Per vedere se ciò che abbiamo osservato finora appare nei dati, possiamo tracciare alcuni grafici.
È anche interessante vedere come le caratteristiche siano relative alle due classi che saranno previste. Per fare ciò, importiamo il file seaborn
pacchetto e utilizzare il pairplot
grafico per esaminare ogni distribuzione delle caratteristiche e ogni separazione di classi per caratteristica:
import seaborn as sns
sns.pairplot(data=df, hue='Class')
Nota: L'esecuzione del codice precedente potrebbe richiedere del tempo, poiché il pairplot combina i grafici a dispersione di tutte le funzionalità (può farlo) e visualizza anche le distribuzioni delle funzionalità.
Osservando il pairplot, possiamo vedere che nella maggior parte dei casi i punti del Çerçevelik
classe sono nettamente separati dai punti della Ürgüp Sivrisi
classe. O i punti di una classe sono a destra quando gli altri sono a sinistra, oppure alcuni sono in alto mentre gli altri sono in basso. Se dovessimo usare una sorta di curva o linea per separare le classi, questo mostra che è più facile separarle, se fossero miste, la classificazione sarebbe un compito più difficile.
Nel Eccentricity
, Compactness
ed Aspect_Ration
colonne, alcuni punti che sono "isolati" o devianti dall'andamento generale dei dati - valori anomali - sono facilmente individuabili.
Osservando la diagonale dalla parte superiore sinistra alla parte inferiore destra del grafico, si noti che anche le distribuzioni dei dati sono codificate a colori in base alle nostre classi. Le forme di distribuzione e la distanza tra le due curve sono altri indicatori di quanto siano separabili: più sono distanti l'una dall'altra, meglio è. Nella maggior parte dei casi, non sono sovrapposti, il che implica che sono più facili da separare, contribuendo anche al nostro compito.
In sequenza, possiamo anche tracciare i boxplot di tutte le variabili con il sns.boxplot()
metodo. La maggior parte delle volte, è utile orientare i boxplot orizzontalmente, quindi le forme dei boxplot sono le stesse delle forme di distribuzione, possiamo farlo con il orient
argomento:
sns.boxplot(data=df, orient='h')
Nella trama sopra, notalo Area
ed Convex_Area
hanno una magnitudine così elevata rispetto alle magnitudini delle altre colonne, che schiacciano gli altri boxplot. Per essere in grado di guardare tutti i boxplot, possiamo ridimensionare le funzionalità e tracciarle di nuovo.
Prima di farlo, capiamo solo che se ci sono valori di caratteristiche che sono intimamente correlati ad altri valori, per esempio, se ci sono valori che aumentano anche quando altri valori di caratteristica diventano più grandi, avere un correlazione positiva; o se ci sono valori che fanno il contrario, diventano più piccoli mentre altri valori diventano più piccoli, avendo a correlazione negativa.
Questo è importante da considerare perché avere relazioni forti nei dati potrebbe significare che alcune colonne sono state derivate da altre colonne o hanno un significato simile al nostro modello. Quando ciò accade, i risultati del modello potrebbero essere sovrastimati e vogliamo risultati più vicini alla realtà. Se ci sono forti correlazioni, significa anche che possiamo ridurre il numero di funzionalità e utilizzare meno colonne per aumentare il modello parsimonioso.
Nota: La correlazione predefinita calcolata con il corr()
il metodo è il Coefficiente di correlazione di Pearson. Questo coefficiente è indicato quando i dati sono quantitativi, normalmente distribuiti, non hanno valori anomali e hanno una relazione lineare.
Un'altra scelta sarebbe calcolare Coefficiente di correlazione di Spearman. Il coefficiente di Spearman viene utilizzato quando i dati sono ordinali, non lineari, hanno qualsiasi distribuzione e hanno valori anomali. Si noti che i nostri dati non si adattano completamente alle ipotesi di Pearson o Spearman (ci sono anche più metodi di correlazione, come quello di Kendall). Poiché i nostri dati sono quantitativi ed è importante per noi misurarne la relazione lineare, utilizzeremo il coefficiente di Pearson.
Diamo un'occhiata alle correlazioni tra le variabili e poi possiamo passare alla pre-elaborazione dei dati. Calcoleremo le correlazioni con il corr()
metodo e visualizzarli con Seaborn's heatmap()
. La dimensione standard della mappa di calore tende ad essere piccola, quindi importeremo matplotlib
(motore di visualizzazione generale/libreria su cui è costruito Seaborn) e modificare le dimensioni con figsize
:
import matplotlib.pyplot as plt
plt.figure(figsize=(15, 10))
correlations = df.corr()
sns.heatmap(correlations, annot=True)
In questa mappa di calore, i valori più vicini a 1 o -1 sono i valori a cui dobbiamo prestare attenzione. Il primo caso, denota un'elevata correlazione positiva e il secondo, un'elevata correlazione negativa. Entrambi i valori, se non superiori a 0.8 o -0.8, saranno utili per il nostro modello di regressione logistica.
Quando ci sono alte correlazioni come quella di 0.99
fra Aspec_Ration
ed Compactness
, questo significa che possiamo scegliere di utilizzare solo Aspec_Ration
o solo Compactness
, invece di entrambi (dal momento che sarebbero quasi uguali predittori di ciascun altro). Lo stesso vale per Eccentricity
ed Compactness
con una -0.98
correlazione, per Area
ed Perimeter
con una 0.94
correlazione e alcune altre colonne.
Pre-elaborazione dei dati
Poiché abbiamo già esplorato i dati per un po', possiamo iniziare a pre-elaborarli. Per ora, utilizziamo tutte le funzionalità per la previsione della classe. Dopo aver ottenuto un primo modello, una linea di base, possiamo quindi rimuovere alcune delle colonne altamente correlate e confrontarlo con la linea di base.
Le colonne delle caratteristiche saranno le nostre X
data e la colonna classe, ns y
dati di destinazione:
y = df['Class']
X = df.drop(columns=['Class'], axis=1)
Trasformare le feature categoriali in feature numeriche
Per quanto riguarda il nostro Class
colonna: i suoi valori non sono numeri, questo significa che dobbiamo anche trasformarli. Ci sono molti modi per fare questa trasformazione; qui, useremo il replace()
metodo e sostituzione Çerçevelik
a 0
ed Ürgüp Sivrisi
a 1
.
y = y.replace('Çerçevelik', 0).replace('Ürgüp Sivrisi', 1)
Tieni a mente la mappatura! Quando leggi i risultati dal tuo modello, ti consigliamo di riconvertirli almeno nella tua mente o di nuovo nel nome della classe per altri utenti.
Suddivisione dei dati in Train e Test Set
Nella nostra esplorazione, abbiamo notato che le funzionalità richiedevano il ridimensionamento. Se eseguissimo il ridimensionamento ora, o in modo automatico, ridimensioneremmo i valori con l'intero X
ed y
. In tal caso, vorremmo introdurre perdita di dati, poiché i valori del prossimo set di test avrebbero influito sul ridimensionamento. La fuga di dati è una causa comune di risultati irriproducibili e prestazioni elevate illusorie dei modelli ML.
Pensare al ridimensionamento mostra che dobbiamo prima dividere X
ed y
dati ulteriormente in treno e set di test e poi a in forma uno scaler sul set di allenamento e a trasformare sia il treno che i set di prova (senza mai che il set di prova influisca sull'ablatore che lo fa). Per questo, useremo Scikit-Learn's train_test_split()
Metodo:
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)
Configurazione test_size=.25
sta assicurando che stiamo utilizzando il 25% dei dati per i test e il 75% per la formazione. Questo potrebbe essere omesso, una volta che è la divisione predefinita, ma il Divinatorio il modo di scrivere il codice suggerisce che "essere espliciti è meglio che impliciti".
Nota: La frase "esplicito è meglio di implicito" è un riferimento a Lo Zen di Pythono PEP20. Fornisce alcuni suggerimenti per scrivere codice Python. Se si seguono questi suggerimenti, il codice viene considerato Divinatorio. Puoi saperne di più qui.
Dopo aver suddiviso i dati in set di treni e test, è buona norma esaminare quanti record sono presenti in ciascun set. Questo può essere fatto con il shape
attributo:
X_train.shape, X_test.shape, y_train.shape, y_test.shape
Questo mostra:
((1875, 12), (625, 12), (1875,), (625,))
Possiamo vedere che dopo la divisione, abbiamo 1875 record per l'addestramento e 625 per i test.
Ridimensionamento dei dati
Una volta che abbiamo il nostro treno e set di test pronti, possiamo procedere a ridimensionare i dati con Scikit-Learn StandardScaler
oggetto (o altri scaler forniti dalla libreria). Per evitare perdite, l'ablatore è montato sul X_train
i dati e i valori del treno vengono quindi utilizzati per ridimensionare o trasformare i dati del treno e del test:
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
Dal momento che in genere chiamerai:
scaler.fit(X_train)
X_train = scaler.transform(X_train)
X_test = scaler.transform(X_test)
Le prime due righe possono essere compresse al singolare fit_transform()
call, che si adatta allo scaler sul set e lo trasforma in una volta sola. Ora possiamo riprodurre i grafici boxplot per vedere la differenza dopo aver ridimensionato i dati.
Considerando che il ridimensionamento rimuove i nomi delle colonne, prima del tracciamento, possiamo organizzare nuovamente i dati del treno in un dataframe con i nomi delle colonne per facilitare la visualizzazione:
column_names = df.columns[:12]
X_train = pd.DataFrame(X_train, columns=column_names)
sns.boxplot(data=X_train, orient='h')
Possiamo finalmente vedere tutti i nostri boxplot! Si noti che tutti hanno valori anomali e le caratteristiche che presentano una distribuzione più lontana dalla normale (che hanno curve oblique a sinistra o a destra), come Solidity
, Extent
, Aspect_Ration
e Compactedness
, sono gli stessi che avevano correlazioni maggiori.
Rimozione dei valori anomali con il metodo IQR
Sappiamo già che la regressione logistica può essere influenzata da valori anomali. Uno dei modi per trattarli è usare un metodo chiamato Intervallo interquartile or IQR. Il passaggio iniziale del metodo IQR consiste nel dividere i dati del nostro treno in quattro parti, chiamate quartili. Il primo quartile, Q1, ammonta al 25% dei dati, il secondo, Q2, al 50%, il terzo, Q3, al 75%, e l'ultimo, Q4, al 100%. I riquadri nel boxplot sono definiti dal metodo IQR e ne sono una rappresentazione visiva.
Considerando un boxplot orizzontale, la linea verticale a sinistra segna il 25% dei dati, la linea verticale al centro, il 50% dei dati (o la mediana), e l'ultima linea verticale a destra, il 75% dei dati . Più sono uniformi in termini di dimensioni entrambi i quadrati definiti dalle linee verticali – o più la linea verticale mediana è al centro – significa che i nostri dati sono più vicini alla distribuzione normale o meno distorti, il che è utile per la nostra analisi.
Oltre alla casella IQR, ci sono anche linee orizzontali su entrambi i lati. Tali linee segnano i valori di distribuzione minimo e massimo definiti da
$$
Minimo = Q1 – 1.5*IQR
$$
ed
$$
Massimo = Q3 + 1.5*IQR
$$
IQR è esattamente la differenza tra Q3 e Q1 (o Q3 – Q1) ed è il punto più centrale dei dati. Ecco perché quando troviamo l'IQR, finiamo per filtrare i valori anomali nelle estremità dei dati o nei punti minimo e massimo. I box plot ci danno un'anteprima di quale sarà il risultato del metodo IQR.
Possiamo usare i Panda quantile()
metodo per trovare i nostri quantili, e iqr
dal scipy.stats
pacchetto per ottenere l'intervallo di dati interquartile per ciascuna colonna:
from scipy.stats import iqr
Q1 = X_train.quantile(q=.25)
Q3 = X_train.quantile(q=.75)
IQR = X_train.apply(iqr)
Ora abbiamo Q1, Q3 e IQR, possiamo filtrare i valori più vicini alla mediana:
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]
Dopo aver filtrato le nostre righe di addestramento, possiamo vedere con quante di esse sono ancora presenti nei dati shape
:
X_train.shape
Questo risulta in:
(1714, 12)
Possiamo vedere che il numero di righe è passato da 1875 a 1714 dopo il filtraggio. Ciò significa che 161 righe contenevano valori anomali o l'8.5% dei dati.
Nota: Si consiglia che il filtraggio dei valori anomali, la rimozione dei valori NaN e altre azioni che implicano il filtraggio e la pulizia dei dati rimangano al di sotto o fino al 10% dei dati. Prova a pensare ad altre soluzioni se il filtraggio o la rimozione supera il 10% dei tuoi dati.
Dopo aver rimosso i valori anomali, siamo quasi pronti per includere i dati nel modello. Per l'adattamento del modello, utilizzeremo i dati del treno. X_train
è filtrato, ma che dire y_train
?
y_train.shape
Questo produce:
(1875,)
Notare che y_train
ha ancora 1875 righe. Dobbiamo abbinare il numero di y_train
righe al numero di X_train
righe e non solo arbitrariamente. Dobbiamo rimuovere i valori y delle istanze di semi di zucca che abbiamo rimosso, che sono probabilmente sparsi per il y_train
impostare. Il filtrato X_train
ancora ha i suoi indici originali e l'indice ha delle lacune in cui abbiamo rimosso i valori anomali! Possiamo quindi utilizzare l'indice di X_train
DataFrame in cui cercare i valori corrispondenti y_train
:
y_train = y_train.iloc[X_train.index]
Dopo averlo fatto, possiamo guardare il y_train
forma di nuovo:
y_train.shape
Quali uscite:
(1714,)
Dai un'occhiata alla nostra guida pratica e pratica per l'apprendimento di Git, con le migliori pratiche, gli standard accettati dal settore e il cheat sheet incluso. Smetti di cercare su Google i comandi Git e in realtà imparare esso!
Adesso, y_train
ha anche 1714 righe e sono le stesse del X_train
righe. Siamo finalmente pronti per creare il nostro modello di regressione logistica!
Implementazione del modello di regressione logistica
La parte difficile è fatta! La preelaborazione è solitamente più difficile dello sviluppo di modelli, quando si tratta di utilizzare librerie come Scikit-Learn, che hanno semplificato l'applicazione dei modelli ML a solo un paio di righe.
Per prima cosa importiamo il LogisticRegression
class e istanziarla, creando a LogisticRegression
oggetto:
from sklearn.linear_model import LogisticRegression
logreg = LogisticRegression(random_state=SEED)
In secondo luogo, adattiamo i dati del nostro treno a logreg
modello con il fit()
metodo e prevedere i nostri dati di test con il predict()
metodo, memorizzando i risultati come y_pred
:
logreg.fit(X_train.values, y_train)
y_pred = logreg.predict(X_test)
Abbiamo già fatto previsioni con il nostro modello! Diamo un'occhiata alle prime 3 righe X_train
per vedere quali dati abbiamo utilizzato:
X_train[:3]
Il codice sopra restituisce:
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
E ai primi 3 pronostici in y_pred
per vedere i risultati:
y_pred[:3]
Questo risulta in:
array([0, 0, 0])
Per quelle tre file, le nostre previsioni erano che fossero semi di prima classe, Çerçevelik
.
Con regressione logistica, invece di prevedere la classe finale, ad esempio 0
, possiamo anche prevedere la probabilità che la riga abbia di appartenere a 0
classe. Questo è ciò che accade effettivamente quando la regressione logistica classifica i dati e il predict()
il metodo passa quindi questa previsione attraverso una soglia per restituire una classe "difficile". Per prevedere la probabilità di appartenere a una classe, predict_proba()
si usa:
y_pred_proba = logreg.predict_proba(X_test)
Diamo anche un'occhiata ai primi 3 valori delle previsioni di probabilità y:
y_pred_proba[:3]
Quali uscite:
# class 0 class 1
array([[0.54726628, 0.45273372],
[0.56324527, 0.43675473],
[0.86233349, 0.13766651]])
Ora, invece di tre zeri, abbiamo una colonna per ogni classe. Nella colonna a sinistra, a partire da 0.54726628
, sono le probabilità dei dati relativi alla classe 0
; e nella colonna di destra, iniziando con 0.45273372
, sono le probabilità che appartengano alla classe 1
.
Nota: Questa differenza di classificazione è anche nota come difficile ed morbido predizione. La previsione hard racchiude la previsione in una classe, mentre le previsioni soft generano il probabilità dell'istanza appartenente a una classe.
Sono disponibili ulteriori informazioni su come è stato realizzato l'output previsto. In realtà non lo era 0
, ma una probabilità del 55% di classe 0
, e una probabilità del 45% di lezione 1
. Questo emerge come i primi tre X_test
punti dati, relativi alla classe 0
, sono veramente chiari solo per quanto riguarda il terzo punto dati, con una probabilità dell'86% – e non tanto per i primi due punti dati.
Quando si comunicano i risultati utilizzando i metodi ML, in genere è meglio restituire una classe soft e la probabilità associata come "fiducia" di tale classificazione.
Parleremo di più su come viene calcolato quando andremo più a fondo nel modello. A questo punto, possiamo procedere al passaggio successivo.
Valutazione del Modello con Report di Classificazione
Il terzo passaggio consiste nel vedere come si comporta il modello sui dati di test. Possiamo importare Scikit-Learn classification_report()
e passa il nostro y_test
ed y_pred
come argomenti. Successivamente, possiamo stampare la sua risposta.
Il rapporto di classificazione contiene le metriche di classificazione più utilizzate, ad esempio precisione, ricordare, punteggio f1e precisione.
- Precisione: per capire quali valori di previsione corretti sono stati considerati corretti dal nostro classificatore. La precisione dividerà quei valori di veri positivi per tutto ciò che è stato previsto come positivo:
$$
precision = frac{testo{vero positivo}}{testo{vero positivo} + testo{falso positivo}}
$$
- Richiamo: per capire quanti dei veri positivi sono stati individuati dal nostro classificatore. Il richiamo viene calcolato dividendo i veri positivi per tutto ciò che avrebbe dovuto essere previsto come positivo:
$$
richiamo = frac{testo{vero positivo}}{testo{vero positivo} + testo{falso negativo}}
$$
- punteggio F1: è il bilanciato o media armonica di precisione e di richiamo. Il valore più basso è 0 e il più alto è 1. Quando
f1-score
è uguale a 1, significa che tutte le classi sono state previste correttamente – questo è un punteggio molto difficile da ottenere con dati reali:
$$
testo{f1-score} = 2* frac{testo{precision} * testo{recall}}{text{precision} + testo{recall}}
$$
- Precisione: descrive quante previsioni ha ottenuto il nostro classificatore. Il valore di precisione più basso è 0 e il più alto è 1. Tale valore viene solitamente moltiplicato per 100 per ottenere una percentuale:
$$
accuratezza = frac{testo{numero di previsioni corrette}}{testo{numero totale di previsioni}}
$$
Nota: È estremamente difficile ottenere un'accuratezza del 100% su qualsiasi dato reale, se ciò accade, tieni presente che potrebbero verificarsi perdite o qualcosa di sbagliato: non c'è consenso su un valore di precisione ideale ed è anche dipendente dal contesto. Un valore del 70%, il che significa che il classificatore commetterà errori sul 30% dei dati, o superiore al 70% tende ad essere sufficiente per la maggior parte dei modelli.
from sklearn.metrics import classification_report
cr = classification_report(y_test, y_pred)
print(cr)
Possiamo quindi guardare l'output del rapporto di classificazione:
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
Questo è il nostro risultato. Notare che precision
, recall
, f1-score
e accuracy
le metriche sono tutte molto alte, superiori all'80%, il che è l'ideale, ma quei risultati sono stati probabilmente influenzati da correlazioni elevate e non si manterranno a lungo termine.
La precisione del modello è dell'86%, il che significa che sbaglia la classificazione il 14% delle volte. Abbiamo queste informazioni generali, ma sarebbe interessante sapere se gli errori del 14% si verificano nella classificazione della classe 0
o classe 1
. Per identificare quali classi sono erroneamente identificate come quali e con quale frequenza, possiamo calcolare e tracciare a matrice di confusione delle previsioni del nostro modello.
Valutazione del modello con una matrice di confusione
Calcoliamo e quindi tracciamo la matrice di confusione. Dopo averlo fatto, possiamo capirne ogni parte. Per tracciare la matrice di confusione, useremo Scikit-Learn confusion_matrix()
, che importeremo da metrics
modulo.
La matrice di confusione è più facile da visualizzare usando un Seaborn heatmap()
. Quindi, dopo averlo generato, passeremo la nostra matrice di confusione come argomento per la mappa di calore:
from sklearn.metrics import confusion_matrix
cm = confusion_matrix(y_test, y_pred)
sns.heatmap(cm, annot=True, fmt='d')
- Matrice di confusione: la matrice mostra quanti campioni il modello ha corretto o sbagliato per ogni classe. Vengono chiamati i valori che erano corretti e correttamente previsti veri positivi, e quelli che erano stati previsti come positivi ma non erano positivi vengono chiamati falsi positivi. La stessa nomenclatura di veri negativi ed falsi negativi è usato per valori negativi;
Osservando la trama della matrice di confusione, possiamo vedere che abbiamo 287
valori che erano 0
e previsto come 0
- o veri positivi per la classe 0
(i semi di Çerçevelik). Abbiamo anche 250
veri aspetti positivi per la classe 1
(Semi di Ürgüp Sivrisi). I veri positivi si trovano sempre nella diagonale della matrice che va da sinistra in alto a destra in basso.
Abbiamo anche 29
valori che avrebbero dovuto essere 0
, ma previsto come 1
(falsi positivi) e 59
valori che erano 1
e previsto come 0
(falsi negativi). Con quei numeri, possiamo capire che l'errore che il modello fa di più è che prevede falsi negativi. Quindi, può finire principalmente per classificare un seme Ürgüp Sivrisi come seme Çerçevelik.
Questo tipo di errore si spiega anche con il richiamo dell'81% della classe 1
. Si noti che le metriche sono collegate. E la differenza nel richiamo deriva dall'avere 100 campioni in meno della classe Ürgüp Sivrisi. Questa è una delle implicazioni dell'avere solo pochi campioni in meno rispetto all'altra classe. Per migliorare ulteriormente il ricordo, puoi sperimentare con i pesi delle classi o utilizzare più campioni di Ürgüp Sivrisi.
Finora, abbiamo eseguito la maggior parte dei passaggi tradizionali della scienza dei dati e utilizzato il modello di regressione logistica come una scatola nera.
Nota: Se vuoi andare oltre, usa Convalida incrociata (CV) e ricerca griglia cercare, rispettivamente, il modello che generalizza maggiormente i dati e i migliori parametri del modello che vengono scelti prima dell'addestramento, oppure iperparametri.
Idealmente, con CV e Grid Search, potresti anche implementare un modo concatenato per eseguire fasi di pre-elaborazione dei dati, suddivisione dei dati, modellazione e valutazione, il che è semplificato con Scikit-Learn oleodotti.
Ora è il momento di aprire la scatola nera e guardarci dentro, per approfondire la comprensione di come funziona la regressione logistica.
Approfondire come funziona davvero la regressione logistica
Il regressione la parola non è lì per caso, per capire cosa fa la regressione logistica, possiamo ricordare cosa fa la sua regressione lineare ai dati. La formula di regressione lineare era la seguente:
$$
y = b_0 + b_1 * x_1 + b_2 * x_2 + b_3 * x_3 + ldot + b_n * x_n
$$
in cui b0 era l'intercetta di regressione, b1 il coefficiente e x1 i dati.
Tale equazione ha prodotto una linea retta che è stata utilizzata per prevedere nuovi valori. Ricordando l'introduzione, la differenza ora è che non prevediamo nuovi valori, ma una classe. Quindi quella linea retta deve cambiare. Con la regressione logistica, introduciamo una non linearità e la previsione viene ora effettuata utilizzando una curva anziché una linea:
Osservare che mentre la retta di regressione lineare continua ed è composta da valori infiniti continui, la curva di regressione logistica può essere divisa a metà e ha estremi in 0 e 1 valori. Quella forma a "S" è il motivo per cui classifica i dati: i punti più vicini o che cadono sull'estremità più alta appartengono alla classe 1, mentre i punti che si trovano nel quadrante inferiore o più vicini a 0 appartengono alla classe 0. La parte centrale di la “S” è la metà tra 0 e 1, 0.5 – è la soglia per i punti di regressione logistica.
Comprendiamo già la differenza visiva tra regressione logistica e lineare, ma per quanto riguarda la formula? La formula per la regressione logistica è la seguente:
$$
y = b_0 + b_1 * x_1 + b_2 * x_2 + b_3 * x_3 + ldot + b_n * x_n
$$
Può anche essere scritto come:
$$
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)}}
$$
O anche essere scritto come:
$$
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 + ldot + b_n * x_n)}}
$$
Nell'equazione sopra, abbiamo la probabilità di input, invece del suo valore. Ha 1 come numeratore, quindi può risultare in un valore compreso tra 0 e 1 e 1 più un valore al denominatore, in modo che il suo valore sia 1 e qualcosa del genere: ciò significa che il risultato della frazione intera non può essere maggiore di 1 .
E qual è il valore che c'è al denominatore? è e, la base del logaritmo naturale (circa 2.718282), elevata alla potenza della regressione lineare:
$$
e^{(b_0 + b_1 * x_1 + b_2 * x_2 + b_3 * x_3 + ldots + b_n * x_n)}
$$
Un altro modo di scriverlo sarebbe:
$$
ln sinistra( frac{p}{1-p} destra) = {(b_0 + b_1 * x_1 + b_2 * x_2 + b_3 * x_3 + ldots + b_n * x_n)}
$$
In quell'ultima equazione, ln è il logaritmo naturale (base e) e p è la probabilità, quindi il logaritmo della probabilità del risultato è lo stesso del risultato della regressione lineare.
In altre parole, con il risultato della regressione lineare e il logaritmo naturale, possiamo arrivare alla probabilità che un input appartenga o meno a una classe progettata.
L'intero processo di derivazione della regressione logistica è il seguente:
$$
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 + ldot + 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 sinistra( frac{p}{1-p} destra) = (b_0 + b_1 * x_1 + b_2 *x_2 + b_3 * x_3 + ldots + b_n * x_n)
$$
Ciò significa che il modello di regressione logistica ha anche coefficienti e un valore di intercetta. Perché usa una regressione lineare e vi aggiunge una componente non lineare con il logaritmo naturale (e
).
Possiamo vedere i valori dei coefficienti e l'intercetta del nostro modello, allo stesso modo in cui abbiamo fatto per la regressione lineare, usando coef_
ed intercept_
proprietà:
logreg.coef_
Che mostra i coefficienti di ciascuna delle 12 caratteristiche:
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_
Ciò si traduce in:
array([0.08735782])
Con i coefficienti e i valori di intercettazione, possiamo calcolare le probabilità previste dei nostri dati. Prendiamo il primo X_test
di nuovo i valori, ad esempio:
X_test[:1]
Questo restituisce la prima riga di X_test
come matrice 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]])
Seguendo l'equazione iniziale:
$$
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 + ldot + b_n * x_n)}}
$$
In Python abbiamo:
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
Questo risulta in:
0.45273372469369133
Se guardiamo di nuovo al predict_proba
risultato del primo X_test
riga, abbiamo:
logreg.predict_proba(X_test[:1])
Ciò significa che l'equazione di regressione logistica originale ci fornisce la probabilità dell'input relativo alla classe 1
, per scoprire quale probabilità è per la classe 0
, possiamo semplicemente:
1 - px
Nota che entrambi px
ed 1-px
sono identici a predict_proba
risultati. Ecco come viene calcolata la regressione logistica e perché regressione fa parte del suo nome. Ma che dire del termine logistica?
Il termine logistica viene da logit, che è una funzione che abbiamo già visto:
$$
ln sinistra( frac{p}{1-p} destra)
$$
L'abbiamo appena calcolato con px
ed 1-px
. Questo è il logit, chiamato anche log-quote poiché è uguale al logaritmo delle quote dove p
è una probabilità.
Conclusione
In questa guida abbiamo studiato uno degli algoritmi di classificazione del machine learning più fondamentali, es regressione logistica.
Inizialmente, abbiamo implementato la regressione logistica come una scatola nera con la libreria di apprendimento automatico di Scikit-Learn, e in seguito l'abbiamo capito passo dopo passo per avere un chiaro perché e da dove provengono i termini regressione e logistica.
Abbiamo anche esplorato e studiato i dati, comprendendo che è una delle parti più cruciali di un'analisi della scienza dei dati.
Da qui, ti consiglierei di giocarci regressione logistica multiclasse, regressione logistica per più di due classi: puoi applicare lo stesso algoritmo di regressione logistica per altri set di dati che hanno più classi e interpretare i risultati.
Nota: È disponibile una buona raccolta di set di dati qui con cui giocare.
Ti consiglierei anche di studiare L1 e L2 regolarizzazioni, sono un modo per "penalizzare" i dati più elevati in modo che si avvicinino alla normalità, mantenendo la complessità del modello, in modo che l'algoritmo possa ottenere un risultato migliore. L'implementazione Scikit-Learn che abbiamo utilizzato ha già la regolarizzazione L2 per impostazione predefinita. Un'altra cosa da guardare è il diverso risolutori, come lbgs
, che ottimizzano le prestazioni dell'algoritmo di regressione logistica.
È anche importante dare un'occhiata al statistiche approccio alla regressione logistica. Esso ha ipotesi sul comportamento dei dati, e su altre statistiche che devono valere per garantire risultati soddisfacenti, quali:
- le osservazioni sono indipendenti;
- non c'è multicollinearità tra variabili esplicative;
- non ci sono valori anomali estremi;
- esiste una relazione lineare tra variabili esplicative e logit della variabile di risposta;
- la dimensione del campione è sufficientemente grande.
Notare quante di queste ipotesi erano già coperte nella nostra analisi e trattamento dei dati.
Spero che continuerai a esplorare ciò che la regressione logistica ha da offrire in tutti i suoi diversi approcci!
- blockchain
- C++
- codice
- geniale
- scienza dei dati
- visualizzazione dati
- Java
- matplotlib
- gettone non fungibile
- numpy
- OpenSea
- panda
- PHP
- Platone
- platone ai
- Platone Data Intelligence
- Gioco di Platone
- Platoneblockchain
- PlatoneDati
- gioco di plato
- Poligono
- Python
- Reagire
- scikit-impara
- Seaborn
- smart contract
- solario
- Impilamento
- Vyper
- Web3
- zefiro