Definitiv guide til logistisk regression i Python PlatoBlockchain Data Intelligence. Lodret søgning. Ai.

Definitiv guide til logistisk regression i Python

Introduktion

Nogle gange forvekslet med lineær regression af nybegyndere - på grund af at dele udtrykket regression - Logistisk regression er vidt forskellig fra lineær regression. Mens lineær regression forudsiger værdier som 2, 2.45, 6.77 eller kontinuerlige værdier, hvilket gør det til en regression algoritme, Logistisk regression forudsiger værdier som 0 eller 1, 1 eller 2 eller 3, som er diskrete værdier, hvilket gør det til en klassificering algoritme. Ja, den hedder regression men er en klassificering algoritme. Mere om det om et øjeblik.

Derfor, hvis dit datavidenskabelige problem involverer kontinuerlige værdier, kan du anvende en regression algoritme (lineær regression er en af ​​dem). Ellers, hvis det involverer klassificering af input, diskrete værdier eller klasser, kan du anvende en klassificering algoritme (logistisk regression er en af ​​dem).

I denne guide vil vi udføre logistisk regression i Python med Scikit-Learn-biblioteket. Vi vil også forklare hvorfor ordet "regression" er til stede i navnet og hvordan logistisk regression fungerer.

For at gøre det vil vi først indlæse data, der vil blive klassificeret, visualiseret og forbehandlet. Derefter vil vi bygge en logistisk regressionsmodel, der vil forstå disse data. Denne model vil derefter blive evalueret og brugt til at forudsige værdier baseret på nye input.

Motivation

Den virksomhed, du arbejder for, har et partnerskab med en tyrkisk landbrugsbedrift. Dette partnerskab involverer salg af græskarkerner. Græskarkerner er meget vigtige for menneskers ernæring. De indeholder en god del kulhydrater, fedt, protein, calcium, kalium, fosfor, magnesium, jern og zink.

I data science-teamet er din opgave at kende forskel på typerne af græskarkerner blot ved at bruge data – eller klassificering dataene efter frøtype.

Den tyrkiske gård arbejder med to græskarkernetyper, den ene hedder Çerçevelik og den anden Ürgüp Sivrisi.

For at klassificere græskarkernerne har dit team fulgt 2021-avisen "Brugen af ​​maskinlæringsmetoder til klassificering af græskarfrø (Cucurbita pepo L.). Genetiske ressourcer og afgrødeudvikling" fra Koklu, Sarigil og Ozbek – i dette papir er der en metode til at fotografere og udtrække frømålingerne fra billederne.

Efter at have fuldført processen beskrevet i papiret, blev følgende målinger udtrukket:

  • Miljø – antallet af pixels inden for grænserne af et græskarfrø
  • Omkreds – omkredsen i pixels af et græskarfrø
  • Hovedaksens længde – også omkredsen i pixels af et græskarfrø
  • Lille akse længde – den lille akseafstand for et græskarfrø
  • excentricitet – et græskarfrøs excentricitet
  • Konveks område – antallet af pixels af den mindste konvekse skal i området dannet af græskarkernen
  • Grad – forholdet mellem et græskarkerneområde og afgrænsningsrammens pixels
  • Ækvivalent diameter – kvadratroden af ​​multiplikationen af ​​arealet af græskarkernen med fire divideret med pi
  • kompakt – andelen af ​​arealet af græskarkernen i forhold til arealet af cirklen med samme omkreds
  • soliditet – græskarkernernes konvekse og konvekse tilstand
  • rundhed – ovalen af ​​græskarfrø uden at tage højde for forvrængninger i kanterne
  • Aspect Ratio – størrelsesforholdet af græskarkernerne

Det er de mål, du skal arbejde med. Udover målene er der også Klasse etiket for de to typer græskarkerner.

For at begynde at klassificere frøene, lad os importere dataene og begynde at se på dem.

Forståelse af datasættet

Bemærk: Du kan downloade græskardatasættet link..

Efter at have downloadet datasættet, kan vi indlæse det i en datarammestruktur ved hjælp af pandas bibliotek. Da det er en excel-fil, vil vi bruge read_excel() metode:

import pandas as pd

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

Når dataene er indlæst, kan vi tage et hurtigt kig på de første 5 rækker ved hjælp af head() metode:

df.head() 

Dette resulterer i:

	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

Her har vi alle målene i deres respektive kolonner, vores funktionerog også Klasse kolonne, vores mål, som er den sidste i datarammen. Vi kan se, hvor mange målinger vi har ved hjælp af shape attribut:

df.shape 

Udgangen er:

(2500, 13)

Formresultatet fortæller os, at der er 2500 poster (eller rækker) i datasættet og 13 kolonner. Da vi ved, at der er én målkolonne - betyder det, at vi har 12 funktionskolonner.

Vi kan nu udforske målvariablen, græskarfrøet Class. Da vi vil forudsige den variabel, er det interessant at se, hvor mange prøver af hvert græskarfrø vi har. Normalt, jo mindre forskellen er mellem antallet af tilfælde i vores klasser, jo mere afbalanceret er vores stikprøve og jo bedre er vores forudsigelser.

Denne inspektion kan udføres ved at tælle hver frøprøve med value_counts() metode:

df['Class'].value_counts() 

Ovenstående kode viser:

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

Vi kan se, at der er 1300 prøver af Çerçevelik frø og 1200 prøver af Ürgüp Sivrisi frø. Bemærk, at forskellen mellem dem er 100 prøver, en meget lille forskel, hvilket er godt for os og indikerer, at der ikke er behov for at rebalancere antallet af prøver.

Lad os også se på den beskrivende statistik over vores funktioner med describe() metode til at se, hvor godt fordelt dataene er. Vi vil også transponere den resulterende tabel med T for at gøre det nemmere at sammenligne på tværs af statistikker:

df.describe().T

Den resulterende tabel er:

					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

Ved at se på tabellen, når man sammenligner betyde , standardafvigelse (std) kolonner, kan det ses, at de fleste funktioner har en middelværdi, der er langt fra standardafvigelsen. Det indikerer, at dataværdierne ikke er koncentreret omkring middelværdien, men mere spredt omkring den – med andre ord, de har høj variabilitet.

Også når man ser på minimum (min) og maksimal (max) kolonner, nogle funktioner, som f.eks Areaog Convex_Area, har store forskelle mellem minimum og maksimum værdier. Det betyder, at disse kolonner har meget små data og også meget store dataværdier, eller højere amplitude mellem dataværdier.

Med høj variabilitet, høj amplitude og funktioner med forskellige måleenheder ville de fleste af vores data drage fordel af at have den samme skala for alle funktioner eller være skaleret. Dataskalering vil centrere data omkring middelværdien og reducere dens varians.

Dette scenarie indikerer formentlig også, at der er outliers og ekstreme værdier i data. Så det er bedst at have nogle afvigende behandling udover at skalere dataene.

Der er nogle maskinlæringsalgoritmer, for eksempel træbaserede algoritmer som f.eks Tilfældig skovklassifikation, som ikke er påvirket af høj datavarians, afvigelser og ekstreme værdier. Logistisk regression er anderledes, er den baseret på en funktion, der kategoriserer vores værdier, og parametrene for den funktion kan blive påvirket af værdier, der er uden for den generelle datatendens og har høj varians.

Vi vil forstå mere om logistisk regression om lidt, når vi får implementeret det. Indtil videre kan vi fortsætte med at udforske vores data.

Bemærk: Der er et populært ordsprog inden for datalogi: "Affald ind, skrald ud" (GIGO), der er velegnet til maskinlæring. Det betyder, at når vi har affaldsdata – målinger, der ikke beskriver fænomenerne i sig selv, vil data, der ikke blev forstået og velforberedt i henhold til den slags algoritme eller model, sandsynligvis generere et forkert output, som ikke vil fungere på en dag til dag basis.
Dette er en af ​​grundene til, at det er så vigtigt at udforske, forstå data, og hvordan den valgte model fungerer. Ved at gøre det kan vi undgå at lægge skrald i vores model – sætte værdi i det i stedet og få værdi ud.

Visualisering af data

Indtil nu har vi med den beskrivende statistik et noget abstrakt øjebliksbillede af nogle kvaliteter ved dataene. Et andet vigtigt skridt er at visualisere det og bekræfte vores hypotese om høj varians, amplitude og outliers. For at se, om det, vi har observeret indtil videre, viser sig i dataene, kan vi plotte nogle grafer.

Det er også interessant at se, hvordan funktionerne er relateret til de to klasser, der vil blive forudsagt. For at gøre det, lad os importere seaborn pakke og bruge pairplot graf for at se på hver funktionsfordeling og hver klasseadskillelse pr. funktion:

import seaborn as sns


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

Bemærk: Ovenstående kode kan tage et stykke tid at køre, da parplotten kombinerer scatterplots af alle funktionerne (det kan den), og også viser featuredistributionerne.

Ser vi på parplot, kan vi se, at punkterne i de fleste tilfælde Çerçevelik klasse er klart adskilt fra punkterne i Ürgüp Sivrisi klasse. Enten er pointene i en klasse til højre, når de andre er til venstre, eller nogle er oppe, mens de andre er nede. Hvis vi skulle bruge en form for kurve eller linje til at adskille klasser, viser dette, at det er lettere at adskille dem, hvis de var blandet, ville klassificering være en sværere opgave.

I Eccentricity, Compactness , Aspect_Ration kolonner, er nogle punkter, der er "isolerede" eller afviger fra den generelle datatendens - outliers - også let at få øje på.

Når du ser på diagonalen fra øverst til venstre til nederst til højre i diagrammet, skal du bemærke, at datafordelingerne også er farvekodede i henhold til vores klasser. Fordelingsformerne og afstanden mellem begge kurver er andre indikatorer for, hvor adskillelige de er - jo længere fra hinanden, jo bedre. I de fleste tilfælde er de ikke overlejrede, hvilket betyder, at de er nemmere at adskille, hvilket også bidrager til vores opgave.

I rækkefølge kan vi også plotte boxplottene for alle variabler med sns.boxplot() metode. De fleste gange er det nyttigt at orientere boxplottene vandret, så formerne på boxplottene er de samme som distributionsformerne, det kan vi gøre med orient argument:


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

Definitiv guide til logistisk regression i Python PlatoBlockchain Data Intelligence. Lodret søgning. Ai.

Læg mærke til det i plottet ovenfor Area , Convex_Area har så høj en størrelse sammenlignet med størrelsen af ​​de andre søjler, at de klemmer de andre boxplotter. For at kunne se på alle boxplots kan vi skalere funktionerne og plotte dem igen.

Før vi gør det, lad os bare forstå, at hvis der er værdier af funktioner, der er tæt forbundet med andre værdier, for eksempel – hvis der er værdier, der også bliver større, når andre egenskabsværdier bliver større, har en positiv korrelation; eller hvis der er værdier, der gør det modsatte, bliver mindre, mens andre værdier bliver mindre, med en negativ korrelation.

Dette er vigtigt at se på, fordi at have stærke relationer i data kan betyde, at nogle kolonner er afledt fra andre kolonner eller har en lignende betydning som vores model. Når det sker, kan modelresultaterne blive overvurderet, og vi ønsker resultater, der er tættere på virkeligheden. Hvis der er stærke sammenhænge, ​​betyder det også, at vi kan reducere antallet af funktioner og bruge færre kolonner, hvilket gør modellen mere påholdende.

Bemærk: Standardkorrelationen beregnet med corr() metoden er Pearson korrelationskoefficient. Denne koefficient er angivet, når data er kvantitative, normalfordelte, ikke har outliers og har en lineær sammenhæng.

Et andet valg ville være at beregne Spearmans korrelationskoefficient. Spearmans koefficient bruges, når data er ordinale, ikke-lineære, har en hvilken som helst fordeling og har outliers. Bemærk, at vores data ikke helt passer ind i Pearsons eller Spearmans antagelser (der er også flere korrelationsmetoder, såsom Kendalls). Da vores data er kvantitative, og det er vigtigt for os at måle deres lineære sammenhæng, vil vi bruge Pearsons koefficient.

Lad os tage et kig på korrelationerne mellem variabler, og så kan vi gå videre til at forbehandle dataene. Vi vil beregne korrelationerne med corr() metode og visualiser dem med Seaborn's heatmap(). Standardstørrelsen på varmekortet plejer at være lille, så vi importerer matplotlib (generel visualiseringsmotor/bibliotek som Seaborn er bygget ovenpå) og ændre størrelsen med figsize:

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

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

Definitiv guide til logistisk regression i Python PlatoBlockchain Data Intelligence. Lodret søgning. Ai.

I dette heatmap er værdierne tættere på 1 eller -1 de værdier, vi skal være opmærksomme på. Det første tilfælde angiver en høj positiv korrelation og det andet en høj negativ korrelation. Begge værdier, hvis de ikke er over 0.8 eller -0.8, vil være gavnlige for vores logistiske regressionsmodel.

Når der er høje korrelationer som f.eks 0.99 mellem Aspec_Ration , Compactness, betyder det, at vi kan vælge kun at bruge Aspec_Ration eller kun Compactness, i stedet for dem begge (da de næsten ville være ens forudsigere af hinanden). Det samme gælder for Eccentricity , Compactness med en -0.98 sammenhæng, for Area , Perimeter med en 0.94 korrelation og nogle andre kolonner.

Forbehandling af data

Da vi allerede har udforsket dataene i et stykke tid, kan vi begynde at forbehandle dem. Lad os indtil videre bruge alle funktionerne til klasseforudsigelsen. Efter at have opnået en første model, en basislinje, kan vi derefter fjerne nogle af de højt korrelerede kolonner og sammenligne den med basislinjen.

Funktionskolonnerne vil være vores X data og klassekolonnen, vores y måldata:

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

At omdanne kategoriske funktioner til numeriske funktioner

Med hensyn til vores Class kolonne – dens værdier er ikke tal, det betyder, at vi også skal transformere dem. Der er mange måder at gøre denne transformation på; her vil vi bruge replace() metode og erstatte Çerçevelik til 0 , Ürgüp Sivrisi til 1.

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

Husk kortlægningen! Når du læser resultater fra din model, vil du i det mindste konvertere disse tilbage i dit sind eller tilbage til klassenavnet for andre brugere.

Opdeling af data i tog- og testsæt

I vores udforskning har vi bemærket, at funktionerne skulle skaleres. Hvis vi foretog skaleringen nu eller på en automatisk måde, ville vi skalere værdier med hele X , y. I så fald ville vi introducere datalækage, da værdierne af det snart kommende testsæt ville have påvirket skaleringen. Datalækage er en almindelig årsag til irreproducerbare resultater og illusorisk høj ydeevne af ML-modeller.

At tænke på skaleringen viser, at vi først skal splittes X , y data videre ind i tog- og testsæt og derefter til passer en scaler på træningssættet, og til omdanne både toget og testsættene (uden nogensinde at prøvesættet påvirker den scaler, der gør dette). Til dette vil vi bruge Scikit-Learn's train_test_split() metode:

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)

Lokal område test_size=.25 sikrer, at vi bruger 25 % af dataene til test og 75 % til træning. Dette kunne udelades, når det først er standardopdelingen, men Pytonisk måde at skrive kode på råder til, at det at være "eksplicit er bedre end implicit".

Bemærk: Sætningen "eksplicit er bedre end implicit" er en henvisning til Zen af ​​Pythoneller PEP20. Den indeholder nogle forslag til at skrive Python-kode. Hvis disse forslag følges, tages koden i betragtning Pytonisk. Du kan vide mere om det link..

Efter at have opdelt dataene i tog- og testsæt, er det en god praksis at se på, hvor mange poster der er i hvert sæt. Det kan gøres med shape attribut:

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

Dette viser:

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

Vi kan se, at vi efter opdelingen har 1875 rekorder for træning og 625 for test.

Skalering af data

Når vi har vores tog- og testsæt klar, kan vi fortsætte med at skalere dataene med Scikit-Learn StandardScaler objekt (eller andre skalere leveret af biblioteket). For at undgå lækage er scaler monteret på X_train data og togværdier bruges derefter til at skalere – eller transformere – både tog- og testdata:

from sklearn.preprocessing import StandardScaler

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

Da du typisk ringer:

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

De første to linjer kan sammenklappes med en ental fit_transform() call, som passer til scaleren på sættet, og transformerer den på én gang. Vi kan nu reproducere boxplot-graferne for at se forskellen efter skalering af data.

I betragtning af at skaleringen fjerner kolonnenavne, kan vi inden plotning organisere togdata i en dataramme med kolonnenavne igen for at lette visualiseringen:

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

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

Definitiv guide til logistisk regression i Python PlatoBlockchain Data Intelligence. Lodret søgning. Ai.

Vi kan endelig se alle vores boxplots! Bemærk, at de alle har udligninger, og de funktioner, der præsenterer en fordeling længere fra normalen (der har kurver enten skæve til venstre eller højre), som f.eks. Solidity, Extent, Aspect_Rationog Compactedness, er de samme, der havde højere korrelationer.

Fjernelse af outliers med IQR-metoden

Vi ved allerede, at logistisk regression kan blive påvirket af outliers. En af måderne at behandle dem på er at bruge en metode kaldet Interkvartil rækkevidde or I.Q.R.. Det første trin i IQR-metoden er at opdele vores togdata i fire dele, kaldet kvartiler. Den første kvartil, Q1, udgør 25 % af data, den anden, Q2, til 50 %, den tredje, Q3, til 75 %, og den sidste, Q4, til 100 %. Boksene i boxplotten er defineret af IQR-metoden og er en visuel repræsentation af det.

I betragtning af et vandret boxplot markerer den lodrette linje til venstre 25 % af dataene, den lodrette linje i midten, 50 % af dataene (eller medianen), og den sidste lodrette linje til højre, 75 % af dataene . Jo mere jævne i størrelse begge kvadrater defineret af de lodrette linjer er – eller jo mere den lodrette medianlinje er i midten – betyder, at vores data er tættere på normalfordelingen eller mindre skæve, hvilket er nyttigt for vores analyse.

Udover IQR-boksen er der også vandrette linjer på begge sider af den. Disse linjer markerer minimums- og maksimumsfordelingsværdierne defineret af

$$
Minimum = Q1 – 1.5*IQR
$$

,

$$
Maksimum = Q3 + 1.5*IQR
$$

IQR er præcis forskellen mellem Q3 og Q1 (eller Q3 – Q1), og det er det mest centrale punkt i data. Det er derfor, når vi finder IQR, ender vi med at filtrere outlierne i dataekstremiteterne eller i minimum og maksimum point. Boksplot giver os et smugkig på, hvad resultatet af IQR-metoden bliver.

Definitiv guide til logistisk regression i Python PlatoBlockchain Data Intelligence. Lodret søgning. Ai.

Vi kan bruge pandaer quantile() metode til at finde vores kvantiler, og iqr fra scipy.stats pakke for at få det interkvartile dataområde for hver kolonne:

from scipy.stats import iqr

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

IQR = X_train.apply(iqr)

Nu har vi Q1, Q3 og IQR, vi kan filtrere værdierne tættere på medianen:


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]

Efter at have filtreret vores træningsrækker, kan vi se, hvor mange af dem der stadig er i dataene med shape:

X_train.shape

Dette resulterer i:

(1714, 12)

Vi kan se, at antallet af rækker gik fra 1875 til 1714 efter filtrering. Det betyder, at 161 rækker indeholdt outliers eller 8.5 % af dataene.

Bemærk: Det tilrådes, at filtrering af outliers, fjernelse af NaN-værdier og andre handlinger, der involverer filtrering og rensning af data, forbliver under eller op til 10 % af data. Prøv at tænke på andre løsninger, hvis din filtrering eller fjernelse overstiger 10 % af dine data.

Efter at have fjernet outliers er vi næsten klar til at inkludere data i modellen. Til modeltilpasningen vil vi bruge togdata. X_train er filtreret, men hvad med y_train?

y_train.shape

Dette udsender:

(1875,)

Læg mærke til det y_train har stadig 1875 rækker. Vi skal matche antallet af y_train rækker til antallet af X_train rækker og ikke kun vilkårligt. Vi er nødt til at fjerne y-værdierne for de forekomster af græskarkerner, som vi fjernede, og som sandsynligvis er spredt gennem y_train sæt. Den filtrerede X_train stil har sine originale indekser, og indekset har huller, hvor vi fjernede outliers! Vi kan derefter bruge indekset for X_train DataFrame til at søge efter de tilsvarende værdier i y_train:

y_train = y_train.iloc[X_train.index]

Efter at have gjort det, kan vi se på y_train form igen:

y_train.shape

Hvilken udgang:

(1714,)

Tjek vores praktiske, praktiske guide til at lære Git, med bedste praksis, brancheaccepterede standarder og inkluderet snydeark. Stop med at google Git-kommandoer og faktisk lærer det!

Nu y_train har også 1714 rækker, og de er de samme som X_train rækker. Vi er endelig klar til at skabe vores logistiske regressionsmodel!

Implementering af den logistiske regressionsmodel

Det svære er gjort! Forbehandling er normalt vanskeligere end modeludvikling, når det kommer til at bruge biblioteker som Scikit-Learn, som har strømlinet anvendelsen af ​​ML-modeller til blot et par linjer.

Først importerer vi LogisticRegression klasse og instansiere det, hvilket skaber en LogisticRegression objekt:

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

For det andet tilpasser vi vores togdata til logreg model med fit() metode, og forudsige vores testdata med predict() metode, lagring af resultaterne som y_pred:



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

Vi har allerede lavet forudsigelser med vores model! Lad os se på de første 3 rækker i X_train for at se, hvilke data vi har brugt:

X_train[:3]

Koden ovenfor udsender:

       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

Og ved de første 3 forudsigelser i y_pred for at se resultaterne:

y_pred[:3] 

Dette resulterer i:

array([0, 0, 0])

For disse tre rækker var vores forudsigelser, at de var frø af første klasse, Çerçevelik.

Med Logistisk regression, i stedet for at forudsige den afsluttende klasse, som f.eks 0, kan vi også forudsige, hvilken sandsynlighed rækken har for at vedrøre 0 klasse. Dette er, hvad der faktisk sker, når logistisk regression klassificerer data, og det predict() metoden passerer derefter denne forudsigelse gennem en tærskel for at returnere en "hård" klasse. For at forudsige sandsynligheden for at tilhøre en klasse, predict_proba() anvendes:

y_pred_proba = logreg.predict_proba(X_test)

Lad os også tage et kig på de første 3 værdier af y-sandsynlighedsforudsigelserne:

y_pred_proba[:3] 

Hvilken udgang:

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

Nu, i stedet for tre nuller, har vi en kolonne for hver klasse. I kolonnen til venstre begynder med 0.54726628, er sandsynligheden for de data, der vedrører klassen 0; og i højre kolonne, begyndende med 0.45273372, er sandsynligheden for, at det vedrører klassen 1.

Bemærk: Denne forskel i klassificering er også kendt som hårdt , blød forudsigelse. Hård forudsigelse bokser forudsigelsen ind i en klasse, mens bløde forudsigelser udsender sandsynlighed af den instans, der tilhører en klasse.

Der er flere oplysninger om, hvordan det forudsagte output blev lavet. Det var det faktisk ikke 0, men en 55% chance for klasse 0, og en 45% chance for klasse 1. Dette viser, hvordan de første tre X_test datapunkter, der vedrører klasse 0, er egentlig kun klare med hensyn til det tredje datapunkt, med en sandsynlighed på 86 % – og ikke så meget for de to første datapunkter.

Når man kommunikerer resultater ved hjælp af ML-metoder – er det typisk bedst at returnere en blød klasse, og den tilhørende sandsynlighed som "tillid" af den klassifikation.

Vi vil tale mere om, hvordan det beregnes, når vi går dybere ind i modellen. På dette tidspunkt kan vi fortsætte til næste trin.

Evaluering af modellen med klassifikationsrapporter

Det tredje trin er at se, hvordan modellen klarer sig på testdata. Vi kan importere Scikit-Learn classification_report() og bestå vores y_test , y_pred som argumenter. Derefter kan vi udskrive svaret.

Klassifikationsrapporten indeholder de mest anvendte klassifikationsmetrikker, som f.eks præcision, tilbagekaldelse, f1-scoreog nøjagtighed.

  1. Precision: for at forstå, hvilke korrekte forudsigelsesværdier, der blev anset for at være korrekte af vores klassifikator. Præcision vil dividere disse sande positive værdier med alt, der blev forudsagt som positivt:

$$
præcision = frac{tekst{sand positiv}}{tekst{sand positiv} + tekst{falsk positiv}}
$$

  1. Recall: for at forstå, hvor mange af de sande positive, der blev identificeret af vores klassifikator. Tilbagekaldelsen beregnes ved at dividere de sande positive med alt, der skulle have været forudsagt som positivt:

$$
recall = frac{tekst{sand positiv}}{tekst{sand positiv} + tekst{falsk negativ}}
$$

  1. f1 score: er den balancerede eller harmonisk middel af præcision og genkaldelse. Den laveste værdi er 0 og den højeste er 1. Hvornår f1-score er lig med 1, betyder det, at alle klasser var korrekt forudsagt – dette er en meget svær score at opnå med rigtige data:

$$
tekst{f1-score} = 2* frac{tekst{præcision} * tekst{recall}}{tekst{præcision} + tekst{recall}}
$$

  1. Nøjagtighed: beskriver, hvor mange forudsigelser vores klassificerer fik rigtige. Den laveste nøjagtighedsværdi er 0, og den højeste er 1. Denne værdi ganges normalt med 100 for at få en procentdel:

$$
nøjagtighed = frac{tekst{antal korrekte forudsigelser}}{tekst{samlet antal forudsigelser}}
$$

Bemærk: Det er ekstremt svært at opnå 100 % nøjagtighed på nogen reelle data, hvis det sker, skal du være opmærksom på, at der kan ske en lækage eller noget forkert – der er ingen konsensus om en ideel nøjagtighedsværdi, og den er også kontekstafhængig. En værdi på 70 %, hvilket betyder, at klassificereren laver fejl på 30 % af dataene, eller over 70 % plejer at være tilstrækkeligt for de fleste modeller.

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

Vi kan derefter se på klassificeringsrapportens output:

				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

Dette er vores resultat. Læg mærke til det precision, recall, f1-scoreog accuracy målinger er alle meget høje, over 80 %, hvilket er ideelt – men disse resultater var sandsynligvis påvirket af høje korrelationer og vil ikke opretholde i det lange løb.

Modellens nøjagtighed er 86%, hvilket betyder, at den får klassificeringen forkert 14% af tiden. Vi har den overordnede information, men det ville være interessant at vide, om de 14 % fejl sker med hensyn til klassificeringen af ​​klasse 0 eller klasse 1. For at identificere hvilke klasser der er fejlidentificeret som hvilke, og i hvilken frekvens – kan vi beregne og plotte en forvirringsmatrix af vores models forudsigelser.

Evaluering af modellen med en forvirringsmatrix

Lad os beregne og plotte forvirringsmatricen. Efter at have gjort det, kan vi forstå hver del af det. For at plotte forvirringsmatricen bruger vi Scikit-Learn confusion_matrix(), som vi importerer fra metrics modul.

Forvirringsmatricen er lettere at visualisere med en Seaborn heatmap(). Så efter at have genereret det, vil vi videregive vores forvirringsmatrix som et argument for varmekortet:

from sklearn.metrics import confusion_matrix

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

Definitiv guide til logistisk regression i Python PlatoBlockchain Data Intelligence. Lodret søgning. Ai.

  1. Forvirringsmatrix: Matrixen viser, hvor mange prøver modellen fik rigtige eller forkerte for hver klasse. De værdier, der var korrekte og korrekt forudsagte, kaldes sande positive, og dem, der blev forudsagt som positive, men ikke var positive, kaldes falske positive. Den samme nomenklatur af sande negativer , falske negativer bruges til negative værdier;

Ved at se på forvirringsmatrix-plottet kan vi se, at vi har 287 værdier, der var 0 og forudsagt som 0 - eller sande positive for klasse 0 (Cerçevelik-frøene). Vi har også 250 sande positive ting for klassen 1 (Ürgüp Sivrisi frø). De sande positive er altid placeret i matrixdiagonalen, der går fra øverste venstre til nederste højre.

Vi har også 29 værdier, der skulle være 0, men forudsagt som 1 (falske positive) og 59 værdier, der var 1 og forudsagt som 0 (falske negativer). Med de tal kan vi forstå, at den fejl, som modellen laver mest, er, at den forudsiger falske negativer. Så det kan for det meste ende med at klassificere et Ürgüp Sivrisi-frø som et Çerçevelik-frø.

Denne form for fejl forklares også af 81% tilbagekaldelse af klassen 1. Bemærk, at metrikken er forbundet. Og forskellen i tilbagekaldelsen kommer fra at have 100 færre prøver af Ürgüp Sivrisi-klassen. Dette er en af ​​konsekvenserne af at have nogle få prøver mindre end den anden klasse. For yderligere at forbedre tilbagekaldelsen kan du enten eksperimentere med klassevægte eller bruge flere Ürgüp Sivrisi-prøver.

Indtil videre har vi udført de fleste af de traditionelle datavidenskabelige trin og brugt den logistiske regressionsmodel som en sort boks.

Bemærk: Hvis du vil gå længere, så brug Krydsvalidering (CV) og Grid Search at lede efter henholdsvis den model, der generaliserer mest angående data, og de bedste modelparametre, der vælges inden træning, eller hyperparametre.

Ideelt set kan du med CV og Grid Search også implementere en sammenkædet måde at udføre dataforbehandlingstrin, dataopdeling, modellering og evaluering – hvilket er gjort nemt med Scikit-Learn rørledninger.

Nu er det tid til at åbne den sorte boks og se ind i den, for at gå dybere ind i at forstå, hvordan logistisk regression fungerer.

Går dybere ind i, hvordan logistisk regression virkelig fungerer

regression ord er der ikke ved et uheld, for at forstå, hvad logistisk regression gør, kan vi huske, hvad dets søskende, lineær regression gør ved dataene. Den lineære regressionsformel var følgende:

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

I hvilket b0 var regressionsskæringen, b1 koefficienten og x1 dataene.

Den ligning resulterede i en lige linje, der blev brugt til at forudsige nye værdier. Når vi husker på introduktionen, er forskellen nu, at vi ikke forudsiger nye værdier, men en klasse. Så den lige linje skal ændres. Med logistisk regression introducerer vi en ikke-linearitet, og forudsigelsen er nu lavet ved hjælp af en kurve i stedet for en linje:

Definitiv guide til logistisk regression i Python PlatoBlockchain Data Intelligence. Lodret søgning. Ai.

Bemærk, at mens den lineære regressionslinje fortsætter og er lavet af kontinuerlige uendelige værdier, kan den logistiske regressionskurve opdeles på midten og har ekstremer i 0 og 1 værdier. Denne "S"-form er grunden til, at den klassificerer data - de punkter, der er tættere på eller falder på den højeste ekstremitet, tilhører klasse 1, mens de punkter, der er i den nederste kvadrant eller tættere på 0, tilhører klasse 0. Midten af "S" er midten mellem 0 og 1, 0.5 - det er tærsklen for de logistiske regressionspunkter.

Definitiv guide til logistisk regression i Python PlatoBlockchain Data Intelligence. Lodret søgning. Ai.

Vi forstår allerede den visuelle forskel mellem logistisk og lineær regression, men hvad med formlen? Formlen for logistisk regression er følgende:

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

Det kan også skrives som:

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

Eller endda skrives som:

$$
y_{sandsynlighed} = 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)}}
$$

I ligningen ovenfor har vi sandsynligheden for input i stedet for dens værdi. Den har 1 som tæller, så den kan resultere i en værdi mellem 0 og 1, og 1 plus en værdi i dens nævner, så dens værdi er 1 og noget – det betyder, at hele brøkresultatet ikke kan være større end 1 .

Og hvad er værdien, der er i nævneren? det er e, bunden af ​​den naturlige logaritme (ca. 2.718282), hævet til styrken af ​​lineær regression:

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

En anden måde at skrive det på ville være:

$$
ln venstre(frac{p}{1-p} højre) = {(b_0 + b_1 * x_1 + b_2 * x_2 + b_3 * x_3 + ldots + b_n * x_n)}
$$

I den sidste ligning, ln er den naturlige logaritme (grundlag e) og p er sandsynligheden, så logaritmen af ​​sandsynligheden for resultatet er den samme som det lineære regressionsresultat.

Med andre ord, med det lineære regressionsresultat og den naturlige logaritme kan vi nå frem til sandsynligheden for, at et input hører til eller ej til en designet klasse.

Hele den logistiske regressionsafledningsproces er følgende:

$$
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 venstre (frac{p}{1-p} højre) = (b_0 + b_1 * x_1 + b_2 *x_2 + b_3 * x_3 + ldots + b_n * x_n)
$$

Det betyder, at den logistiske regressionsmodel også har koefficienter og en interceptværdi. Fordi den bruger en lineær regression og tilføjer en ikke-lineær komponent til den med den naturlige logaritme (e).

Vi kan se værdierne af koefficienterne og skæringen af ​​vores model, på samme måde som vi gjorde for lineær regression, vha. coef_ , intercept_ ejendomme:

logreg.coef_

Som viser koefficienterne for hver af de 12 funktioner:

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_

Det resulterer i:

array([0.08735782])

Med koefficienterne og skæringsværdierne kan vi beregne de forudsagte sandsynligheder for vores data. Lad os få den første X_test værdier igen, som et eksempel:

X_test[:1]

Dette returnerer den første række af X_test som et NumPy-array:

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]])

Efter den indledende ligning:

$$
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)}}
$$

I python har vi:

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

Dette resulterer i:

0.45273372469369133

Hvis vi ser igen på predict_proba resultatet af den første X_test linje, vi har:

logreg.predict_proba(X_test[:1])


Det betyder, at den oprindelige logistiske regressionsligning giver os sandsynligheden for input vedrørende klasse 1, for at finde ud af, hvilken sandsynlighed der er for klasse 0, vi kan ganske enkelt:

1 - px


Bemærk at begge dele px , 1-px er identiske med predict_proba resultater. Sådan beregnes logistisk regression og hvorfor regression er en del af dens navn. Men hvad med udtrykket logistisk?

Udtrykket logistisk kommer fra logit, hvilket er en funktion, vi allerede har set:

$$
l venstre (frac{p}{1-p} højre)
$$

Vi har lige regnet det ud med px , 1-px. Dette er logit, også kaldet log-odds da det er lig med logaritmen af ​​odds hvor p er en sandsynlighed.

Konklusion

I denne guide har vi studeret en af ​​de mest fundamentale maskinlæringsklassifikationsalgoritmer, dvs Logistisk regression.

I første omgang implementerede vi logistisk regression som en sort boks med Scikit-Learns maskinlæringsbibliotek, og senere forstod vi det trin for trin for at have et klart, hvorfor og hvor begreberne regression og logistik kommer fra.

Vi har også udforsket og studeret dataene, idet vi forstår, at det er en af ​​de mest afgørende dele af en datavidenskabsanalyse.

Herfra vil jeg råde dig til at lege med multiklasse logistisk regression, logistisk regression for mere end to klasser – du kan anvende den samme logistiske regressionsalgoritme for andre datasæt, der har flere klasser, og fortolke resultaterne.

Bemærk: En god samling af datasæt er tilgængelig link. for dig at lege med.

Jeg vil også råde dig til at studere L1 og L2 reguleringer, de er en måde at "straffe" de højere data på, for at det bliver tættere på det normale, hvilket holder modellens kompleksitet ude, så algoritmen kan få et bedre resultat. Den Scikit-Learn-implementering, vi brugte, har allerede L2-regularisering som standard. En anden ting at se på er det anderledes løsere, Såsom lbgs, som optimerer den logistiske regressionsalgoritmeydelse.

Det er også vigtigt at tage et kig på statistiske tilgang til logistisk regression. Det har antagelser om datas adfærd og om andre statistikker, der skal holde for at garantere tilfredsstillende resultater, såsom:

  • observationerne er uafhængige;
  • der er ingen multikolinearitet blandt forklarende variable;
  • der er ingen ekstreme outliers;
  • der er en lineær sammenhæng mellem forklarende variable og logit af responsvariablen;
  • prøvestørrelsen er tilstrækkelig stor.

Læg mærke til, hvor mange af disse antagelser, der allerede var dækket i vores analyse og behandling af data.

Jeg håber, du bliver ved med at udforske, hvad logistisk regression har at tilbyde i alle dens forskellige tilgange!

Tidsstempel:

Mere fra Stablemisbrug