DBSCAN med Scikit-Learn i Python

DBSCAN med Scikit-Learn i Python

Introduksjon

Du jobber i et konsulentselskap som data scientis. Prosjektet du nå ble tildelt har data fra studenter som nylig har fullført kurs om økonomi. Finansselskapet som gjennomfører kursene ønsker å forstå om det er felles faktorer som påvirker studentene til å kjøpe de samme kursene eller til å kjøpe ulike kurs. Ved å forstå disse faktorene kan selskapet opprette en studentprofil, klassifisere hver student etter profil og anbefale en liste over kurs.

Når du inspiserer data fra forskjellige elevgrupper, har du kommet over tre disposisjoner av poeng, som i 1, 2 og 3 nedenfor:

DBSCAN med Scikit-Learn i Python PlatoBlockchain Data Intelligence. Vertikalt søk. Ai.

Legg merke til at i plott 1 er det lilla punkter organisert i en halv sirkel, med en masse rosa punkter innenfor den sirkelen, en liten konsentrasjon av oransje punkter utenfor den halvsirkelen, og fem grå punkter som er langt fra alle andre.

I plott 2 er det en rund masse med lilla punkter, en annen med oransje punkter, og også fire grå punkter som er langt fra alle de andre.

Og i plott 3 kan vi se fire konsentrasjoner av punkter, lilla, blå, oransje, rosa og ytterligere tre grå punkter.

Nå, hvis du skulle velge en modell som kunne forstå nye studentdata og bestemme lignende grupper, er det en klyngealgoritme som kan gi interessante resultater til den typen data?

Ved beskrivelse av tomtene nevnte vi begreper som masse av poeng og konsentrasjon av poeng, som indikerer at det er områder i alle grafer med større tetthet. Vi henviste også til sirkulær og halvsirkelformet former, som er vanskelige å identifisere ved å tegne en rett linje eller bare undersøke de nærmeste punktene. I tillegg er det noen fjerne punkter som sannsynligvis avviker fra hoveddatadistribusjonen, og introduserer flere utfordringer eller støy når gruppene skal bestemmes.

En tetthetsbasert algoritme som kan filtrere ut støy, som f.eks DBSCAN (Densitet-Bbeleirer Spatial Cglans av Aapplikasjoner med Noise), er et sterkt valg for situasjoner med tettere områder, avrundede former og støy.

Om DBSCAN

DBSCAN er en av de mest siterte algoritmene innen forskning, dens første publikasjon dukker opp i 1996, dette er originalt DBSCAN papir. I artikkelen demonstrerer forskere hvordan algoritmen kan identifisere ikke-lineære romlige klynger og håndtere data med høyere dimensjoner effektivt.

Hovedideen bak DBSCAN er at det er et minimum antall punkter som vil være innenfor en bestemt avstand eller radius fra det mest "sentrale" klyngepunktet, kalt kjernepunkt. Punktene innenfor den radiusen er nabolagspunktene, og punktene på kanten av det nabolaget er grensepunkter or grensepunkter. Radius eller nabolagsavstand kalles epsilon-området, ε-nabolaget eller bare ε (symbolet for gresk bokstav epsilon).

I tillegg, når det er punkter som ikke er kjernepunkter eller grensepunkter fordi de overskrider radiusen for å tilhøre en bestemt klynge og heller ikke har minimum antall punkter for å være et kjernepunkt, anses de støypunkter.

Dette betyr at vi har tre forskjellige typer poeng, nemlig kjerne, grensen og støy. Videre er det viktig å merke seg at hovedideen er grunnleggende basert på en radius eller avstand, noe som gjør DBSCAN – som de fleste klyngemodeller – avhengig av den avstandsmetrikken. Denne beregningen kan være euklidisk, Manhattan, Mahalanobis og mange flere. Derfor er det avgjørende å velge en passende avstandsberegning som tar hensyn til konteksten til dataene. Hvis du for eksempel bruker kjøreavstandsdata fra en GPS, kan det være interessant å bruke en beregning som tar hensyn til gateoppsett, for eksempel Manhattan-avstand.

DBSCAN med Scikit-Learn i Python PlatoBlockchain Data Intelligence. Vertikalt søk. Ai.

OBS: Siden DBSCAN kartlegger punktene som utgjør støy, kan den også brukes som en uteliggerdeteksjonsalgoritme. For eksempel, hvis du prøver å finne ut hvilke banktransaksjoner som kan være uredelige og frekvensen av uredelige transaksjoner er liten, kan DBSCAN være en løsning for å identifisere disse punktene.

For å finne kjernepunktet, vil DBSCAN først velge et punkt tilfeldig, kartlegge alle punktene i ε-nabolaget, og sammenligne antall naboer til det valgte punktet med minimum antall punkter. Hvis det valgte punktet har like mange eller flere naboer enn minimum antall punkter, vil det bli merket som et kjernepunkt. Dette kjernepunktet og dets nabolagspunkter vil utgjøre den første klyngen.

Algoritmen vil deretter undersøke hvert punkt i den første klyngen og se om den har like mange eller flere nabopunkter enn minimumsantallet av punkter innenfor ε. Hvis den gjør det, vil disse nabopunktene også bli lagt til den første klyngen. Denne prosessen vil fortsette til punktene til den første klyngen har færre naboer enn minimumsantallet av punkter innenfor ε. Når det skjer, slutter algoritmen å legge til punkter til den klyngen, identifiserer et annet kjernepunkt utenfor klyngen, og oppretter en ny klynge for det nye kjernepunktet.

DBSCAN vil deretter gjenta den første klyngeprosessen med å finne alle punkter koblet til et nytt kjernepunkt i den andre klyngen til det ikke er flere punkter som skal legges til klyngen. Den vil da møte et annet kjernepunkt og lage en tredje klynge, eller den vil iterere gjennom alle punktene den ikke tidligere har sett på. Hvis disse punktene er i ε avstand fra en klynge, blir de lagt til den klyngen og blir grensepunkter. Hvis de ikke er det, regnes de som støypunkter.

Råd: Det er mange regler og matematiske demonstrasjoner involvert i ideen bak DBSCAN, hvis du vil grave dypere, kan det være lurt å ta en titt på den originale artikkelen, som er lenket ovenfor.

Det er interessant å vite hvordan DBSCAN-algoritmen fungerer, selv om det heldigvis ikke er behov for å kode algoritmen når Pythons Scikit-Learn-bibliotek allerede har en implementering.

La oss se hvordan det fungerer i praksis!

Importere data for gruppering

For å se hvordan DBSCAN fungerer i praksis, vil vi endre litt på prosjekter og bruke et lite kundedatasett som har sjanger, alder, årsinntekt og forbruksscore på 200 kunder.

Brukspoengsummen varierer fra 0 til 100 og representerer hvor ofte en person bruker penger i et kjøpesenter på en skala fra 1 til 100. Med andre ord, hvis en kunde har en score på 0, bruker de aldri penger, og hvis poengsummen er 100, de er den høyeste brukeren.

OBS: Du kan laste ned datasettet her..

Etter å ha lastet ned datasettet, vil du se at det er en CSV-fil (kommaseparerte verdier) kalt shopping-data.csv, laster vi den inn i en DataFrame ved hjelp av Pandas og lagrer den i customer_data variabel:

import pandas as pd path_to_file = '../../datasets/dbscan/dbscan-with-python-and-scikit-learn-shopping-data.csv'
customer_data = pd.read_csv(path_to_file)

For å ta en titt på de første fem radene med dataene våre, kan du utføre customer_data.head():

Dette resulterer i:

 CustomerID Genre Age Annual Income (k$) Spending Score (1-100)
0 1 Male 19 15 39
1 2 Male 21 15 81
2 3 Female 20 16 6
3 4 Female 23 16 77
4 5 Female 31 17 40

Ved å undersøke dataene kan vi se kunde-ID-nummer, sjanger, alder, inntekter i k$ og forbrukspoeng. Husk at noen eller alle disse variablene vil bli brukt i modellen. For eksempel hvis vi skulle bruke Age og Spending Score (1-100) som variabler for DBSCAN, som bruker en avstandsmåling, er det viktig å bringe dem til en felles skala for å unngå å introdusere forvrengninger siden Age måles i år og Spending Score (1-100) har et begrenset område fra 0 til 100. Dette betyr at vi skal utføre en slags dataskalering.

Vi kan også sjekke om dataene trenger mer forhåndsbehandling bortsett fra skalering ved å se om datatypen er konsistent og verifisere om det mangler verdier som må behandles ved å utføre Pandas info() metode:

customer_data.info()

Dette viser:

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 200 entries, 0 to 199
Data columns (total 5 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 CustomerID 200 non-null int64 1 Genre 200 non-null object 2 Age 200 non-null int64 3 Annual Income (k$) 200 non-null int64 4 Spending Score (1-100) 200 non-null int64 dtypes: int64(4), object(1)
memory usage: 7.9+ KB

Vi kan observere at det ikke mangler verdier fordi det er 200 oppføringer som ikke er null for hver kundefunksjon. Vi kan også se at kun sjangerkolonnen har tekstinnhold, da det er en kategorisk variabel, som vises som object, og alle andre funksjoner er numeriske, av typen int64. Når det gjelder datatypekonsistens og fravær av nullverdier, er dataene våre klare for videre analyse.

Vi kan fortsette å visualisere dataene og finne ut hvilke funksjoner som vil være interessante å bruke i DBSCAN. Etter å ha valgt disse funksjonene, kan vi skalere dem.

Dette kundedatasettet er det samme som brukes i vår definitive veiledning for hierarkisk klynging. For å lære mer om disse dataene, hvordan du utforsker dem og om avstandsberegninger, kan du ta en titt på Definitiv guide til hierarkisk gruppering med Python og Scikit-Learn!

Visualisering av data

Ved å bruke Seaborn's pairplot(), kan vi plotte en spredningsgraf for hver kombinasjon av funksjoner. Siden CustomerID er bare en identifikasjon og ikke en funksjon, vi vil fjerne den med drop() før plotting:

import seaborn as sns customer_data = customer_data.drop('CustomerID', axis=1) sns.pairplot(customer_data);

Dette gir utganger:

DBSCAN med Scikit-Learn i Python PlatoBlockchain Data Intelligence. Vertikalt søk. Ai.

Når man ser på kombinasjonen av funksjoner produsert av pairplot, grafen til Annual Income (k$) med Spending Score (1-100) ser ut til å vise rundt 5 grupper med poeng. Dette ser ut til å være den mest lovende kombinasjonen av funksjoner. Vi kan lage en liste med navnene deres, velg dem fra customer_data DataFrame, og lagre utvalget i customer_data variabel igjen for bruk i vår fremtidige modell.

selected_cols = ['Annual Income (k$)', 'Spending Score (1-100)']
customer_data = customer_data[selected_cols]

Etter å ha valgt kolonnene, kan vi utføre skaleringen diskutert i forrige avsnitt. For å bringe funksjonene til samme skala eller standardisere dem, kan vi importere Scikit-Learn's StandardScaler, lag det, tilpass dataene våre for å beregne gjennomsnittet og standardavviket, og transformer dataene ved å subtrahere gjennomsnittet og dele det med standardavviket. Dette kan gjøres i ett trinn med fit_transform() metode:

from sklearn.preprocessing import StandardScaler ss = StandardScaler() scaled_data = ss.fit_transform(customer_data)

Variablene er nå skalert, og vi kan undersøke dem ved ganske enkelt å skrive ut innholdet i scaled_data variabel. Alternativt kan vi også legge dem til en ny scaled_customer_data DataFrame sammen med kolonnenavn og bruk head() metode igjen:

scaled_customer_data = pd.DataFrame(columns=selected_cols, data=scaled_data)
scaled_customer_data.head()

Dette gir utganger:

 Annual Income (k$) Spending Score (1-100)
0 -1.738999 -0.434801
1 -1.738999 1.195704
2 -1.700830 -1.715913
3 -1.700830 1.040418
4 -1.662660 -0.395980 

Disse dataene er klare for gruppering! Da vi introduserte DBSCAN, nevnte vi minimum antall poeng og epsilon. Disse to verdiene må velges før du oppretter modellen. La oss se hvordan det gjøres.

Velge Min. Prøver og Epsilon

For å velge minimum antall poeng for DBSCAN-klynger, er det en tommelfingerregel som sier at den må være lik eller høyere enn antall dimensjoner i dataene pluss én, som i:

$$
tekst{min. points} >= tekst{datadimensjoner} + 1
$$

Dimensjonene er antall kolonner i datarammen, vi bruker 2 kolonner, så min. poeng skal enten være 2+1, som er 3, eller høyere. For dette eksemplet, la oss bruke 5 min. poeng.

$$
tekst{5 (min. poeng)} >= tekst{2 (datadimensjoner)} + ​​1
$$

Sjekk ut vår praktiske, praktiske guide for å lære Git, med beste praksis, bransjeaksepterte standarder og inkludert jukseark. Slutt å google Git-kommandoer og faktisk lære den!

For å velge verdien for ε er det en metode der a Nærmeste naboer Algoritmen brukes til å finne avstandene til et forhåndsdefinert antall nærmeste punkter for hvert punkt. Dette forhåndsdefinerte antallet naboer er min. poeng vi nettopp har valgt minus 1. Så i vårt tilfelle vil algoritmen finne de 5-1, eller 4 nærmeste punktene for hvert punkt i dataene våre. det er de k-naboer og vår k tilsvarer 4.

$$
tekst{k-naboer} = tekst{min. poeng} – 1
$$

Etter å ha funnet naboene vil vi sortere avstandene deres fra størst til minste og plotte avstandene til y-aksen og punktene på x-aksen. Ser vi på plottet, vil vi finne hvor det ligner bøyningen av en albue og y-aksepunktet som beskriver at albuen bøyd er den foreslåtte ε-verdien.

Merknader: det er mulig at grafen for å finne ε-verdien har enten en eller flere "albuebøyninger", enten store eller mini, når det skjer, kan du finne verdiene, teste dem og velge de med resultater som best beskriver klyngene, enten ved å se på beregninger av plott.

For å utføre disse trinnene kan vi importere algoritmen, tilpasse den til dataene, og så kan vi trekke ut avstandene og indeksene til hvert punkt med kneighbors() metode:

from sklearn.neighbors import NearestNeighbors
import numpy as np nn = NearestNeighbors(n_neighbors=4) nbrs = nn.fit(scaled_customer_data)
distances, indices = nbrs.kneighbors(scaled_customer_data)

Etter å ha funnet avstandene kan vi sortere dem fra størst til minste. Siden avstandsgruppens første kolonne er av punktet til seg selv (som betyr at alle er 0), og den andre kolonnen inneholder de minste avstandene, etterfulgt av den tredje kolonnen som har større avstander enn den andre, og så videre, kan vi bare velge verdiene i den andre kolonnen og lagre dem i distances variabel:

distances = np.sort(distances, axis=0)
distances = distances[:,1] 

Nå som vi har våre sorterte minste avstander, kan vi importere matplotlib, plott avstandene og tegn en rød linje på hvor "albuebøyningen" er:

import matplotlib.pyplot as plt plt.figure(figsize=(6,3))
plt.plot(distances)
plt.axhline(y=0.24, color='r', linestyle='--', alpha=0.4) plt.title('Kneighbors distance graph')
plt.xlabel('Data points')
plt.ylabel('Epsilon value')
plt.show();

Dette er resultatet:

DBSCAN med Scikit-Learn i Python PlatoBlockchain Data Intelligence. Vertikalt søk. Ai.

Legg merke til at når vi tegner linjen, vil vi finne ut ε-verdien, i dette tilfellet er den det 0.24.

Vi har endelig minimumspoeng og ε. Med begge variablene kan vi lage og kjøre DBSCAN-modellen.

Opprette en DBSCAN-modell

For å lage modellen kan vi importere den fra Scikit-Learn, lage den med ε som er det samme som eps argumentet, og minimumspoengene som er mean_samples argument. Vi kan da lagre det i en variabel, la oss kalle det dbs og tilpasse den til de skalerte dataene:

from sklearn.cluster import DBSCAN dbs = DBSCAN(eps=0.24, min_samples=5)
dbs.fit(scaled_customer_data)

Akkurat slik er DBSCAN-modellen vår laget og trent på dataene! For å trekke ut resultatene, får vi tilgang til labels_ eiendom. Vi kan også lage en ny labels kolonne i scaled_customer_data dataramme og fyll den med de forutsagte etikettene:

labels = dbs.labels_ scaled_customer_data['labels'] = labels
scaled_customer_data.head()

Dette er sluttresultatet:

 Annual Income (k$) Spending Score (1-100) labels
0 -1.738999 -0.434801 -1
1 -1.738999 1.195704 0
2 -1.700830 -1.715913 -1
3 -1.700830 1.040418 0
4 -1.662660 -0.395980 -1

Legg merke til at vi har etiketter med -1 verdier; disse er støypunkter, de som ikke tilhører noen klynge. For å vite hvor mange støypunkter algoritmen fant, kan vi telle hvor mange ganger verdien -1 vises i etikettlisten vår:

labels_list = list(scaled_customer_data['labels'])
n_noise = labels_list.count(-1)
print("Number of noise points:", n_noise)

Dette gir utganger:

Number of noise points: 62

Vi vet allerede at 62 punkter av våre opprinnelige data på 200 punkter ble ansett som støy. Dette er mye støy, noe som indikerer at DBSCAN-klyngen kanskje ikke vurderte mange punkter som en del av en klynge. Vi vil snart forstå hva som skjedde når vi plotter dataene.

Til å begynne med, da vi observerte dataene, så det ut til å ha 5 klynger med punkter. For å vite hvor mange klynger DBSCAN har dannet, kan vi telle antall etiketter som ikke er -1. Det er mange måter å skrive den koden på; her har vi skrevet en for loop, som også vil fungere for data der DBSCAN har funnet mange klynger:

total_labels = np.unique(labels) n_labels = 0
for n in total_labels: if n != -1: n_labels += 1
print("Number of clusters:", n_labels)

Dette gir utganger:

Number of clusters: 6

Vi kan se at algoritmen spådde dataene til å ha 6 klynger, med mange støypunkter. La oss visualisere det ved å plotte det med seaborns scatterplot:

sns.scatterplot(data=scaled_customer_data, x='Annual Income (k$)', y='Spending Score (1-100)', hue='labels', palette='muted').set_title('DBSCAN found clusters');

Dette resulterer i:

DBSCAN med Scikit-Learn i Python PlatoBlockchain Data Intelligence. Vertikalt søk. Ai.

Ser vi på plottet, kan vi se at DBSCAN har fanget opp punktene som var tettere forbundet, og punkter som kunne betraktes som en del av samme klynge var enten støy eller ansett for å danne en annen mindre klynge.

DBSCAN med Scikit-Learn i Python PlatoBlockchain Data Intelligence. Vertikalt søk. Ai.

Hvis vi fremhever klyngene, legg merke til hvordan DBSCAN får klynge 1 fullstendig, som er klyngen med mindre plass mellom punktene. Deretter får den delene av klyngene 0 og 3 der punktene ligger tett sammen, vurderer mer adskilte punkter som støy. Den betrakter også punktene i nedre venstre halvdel som støy og deler punktene nede til høyre i 3 klynger, og fanger igjen klynger 4, 2 og 5 der punktene er nærmere hverandre.

Vi kan begynne å komme til en konklusjon om at DBSCAN var utmerket for å fange de tette områdene av klyngene, men ikke så mye for å identifisere det større skjemaet av dataene, de 5 klyngenes avgrensninger. Det ville vært interessant å teste flere klyngealgoritmer med dataene våre. La oss se om en beregning vil bekrefte denne hypotesen.

Evaluering av algoritmen

For å evaluere DBSCAN vil vi bruke silhuettscore som vil ta hensyn til avstanden mellom punktene i en samme klynge og avstandene mellom klyngene.

OBS: Foreløpig er de fleste klyngeberegninger egentlig ikke tilpasset til å brukes til å evaluere DBSCAN fordi de ikke er basert på tetthet. Her bruker vi silhuettskåren fordi den allerede er implementert i Scikit-learn og fordi den prøver å se på klyngeformen.

For å få en mer tilpasset evaluering kan du bruke eller kombinere den med Tetthetsbasert klyngevalidering (DBCV) metrikk, som ble designet spesielt for tetthetsbasert clustering. Det er en implementering for DBCV tilgjengelig på dette GitHub.

Først kan vi importere silhouette_score fra Scikit-Learn, så send det våre kolonner og etiketter:

from sklearn.metrics import silhouette_score s_score = silhouette_score(scaled_customer_data, labels)
print(f"Silhouette coefficient: {s_score:.3f}")

Dette gir utganger:

Silhouette coefficient: 0.506

I følge denne poengsummen ser det ut til at DBSCAN kan fange opp omtrent 50 % av dataene.

konklusjonen

DBSCAN fordeler og ulemper

DBSCAN er en veldig unik klyngealgoritme eller modell.

Ser vi på dens fordeler, er den veldig god til å plukke opp tette områder i data og punkter som er langt fra andre. Dette betyr at dataene ikke trenger å ha en bestemt form og kan være omgitt av andre punkter, så lenge de også er tett forbundet.

Det krever at vi spesifiserer minimumspoeng og ε, men det er ikke nødvendig å spesifisere antall klynger på forhånd, som for eksempel i K-Means. Den kan også brukes med svært store databaser siden den ble designet for høydimensjonale data.

Når det gjelder ulempene, har vi sett at den ikke kunne fange opp forskjellige tettheter i samme klynge, så den har det vanskelig med store forskjeller i tettheter. Det er også avhengig av avstandsmetrikken og skaleringen av punktene. Dette betyr at hvis dataene ikke er godt forstått, med forskjeller i skala og med en avstandsberegning som ikke gir mening, vil den sannsynligvis ikke forstå det.

DBSCAN-utvidelser

Det finnes andre algoritmer, som f.eks Hierarkisk DBSCAN (HDBSCAN) og Bestilling av punkter for å identifisere klyngestrukturen (OPTICS), som regnes som utvidelser av DBSCAN.

Både HDBSCAN og OPTICS kan vanligvis prestere bedre når det er klynger med varierende tetthet i dataene og er også mindre følsomme for valget eller innledende min. poeng og ε-parametere.

Tidstempel:

Mer fra Stackabuse