esittely
K-tarkoittaa klusterointia on yksi yleisimmin käytetyistä valvomattomista koneoppimisalgoritmeista, jotka muodostavat dataklustereita datainstanssien samankaltaisuuden perusteella.
Tässä oppaassa tarkastellaan ensin yksinkertaista esimerkkiä ymmärtääksemme, kuinka K-Means-algoritmi toimii, ennen kuin otamme sen käyttöön Scikit-Learnin avulla. Sitten keskustelemme siitä, kuinka klusterien lukumäärä (Ks) määritetään K-Meansissa, ja käsitellään myös etäisyysmittauksia, varianssia ja K-Meansin etuja ja haittoja.
Motivoiminen
Kuvittele seuraava tilanne. Eräänä päivänä naapurustossa kävellessäsi huomasit, että siellä oli 10 lähikauppaa ja aloit miettimään, mitkä kaupat olivat samanlaisia – lähempänä toisiaan. Kun etsit tapoja vastata tähän kysymykseen, olet törmännyt mielenkiintoiseen lähestymistapaan, joka jakaa kaupat ryhmiin niiden koordinaattien perusteella kartalla.
Esimerkiksi, jos yksi kauppa sijaitsi 5 km länteen ja 3 km pohjoiseen – määrität (5, 3)
koordinoi sille ja edustaa sitä kaaviossa. Piirretään tämä ensimmäinen kohta visualisoidaksesi mitä tapahtuu:
import matplotlib.pyplot as plt
plt.title("Store With Coordinates (5, 3)")
plt.scatter(x=5, y=3)
Tämä on vasta ensimmäinen kohta, jotta voimme saada käsityksen siitä, kuinka voimme edustaa kauppaa. Oletetaan, että meillä on jo 10 koordinaattia 10 kerätylle myymälälle. Järjestettyään ne a numpy
matriisi, voimme myös piirtää niiden sijainnit:
import numpy as np
points = np.array([[5, 3], [10, 15], [15, 12], [24, 10], [30, 45], [85, 70], [71, 80], [60, 78], [55, 52],[80, 91]])
xs = points[:,0]
ys = points[:,1]
plt.title("10 Stores Coordinates")
plt.scatter(x=xs, y=ys)
K-Means-algoritmin manuaalinen käyttöönotto
Nyt voimme tarkastella 10 kauppaa kaaviossa, ja suurin ongelma on selvittää, voidaanko ne jakaa eri ryhmiin läheisyyden perusteella? Pelkästään vilkaisemalla nopeasti kaaviota voimme luultavasti huomata kaksi myymäläryhmää – yksi on alemmat pisteet alhaalla vasemmalla ja toinen on yläoikealla olevat pisteet. Ehkä voimme jopa erottaa nämä kaksi keskellä olevaa pistettä erillisenä ryhmänä - siten luoden kolmea eri ryhmää.
Tässä osiossa käydään läpi pisteiden manuaalinen klusterointi – jaetaan ne tiettyyn määrään ryhmiä. Tällä tavalla käymme pohjimmiltaan huolellisesti läpi kaikki vaiheet K-Means-klusterointialgoritmi. Tämän osion loppuun mennessä saat sekä intuitiivisen että käytännön käsityksen kaikista K-Means-klusteroinnin aikana suoritettavista vaiheista. Sen jälkeen siirrämme sen Scikit-Learnille.
Mikä olisi paras tapa määrittää, onko pisteryhmiä kaksi vai kolme? Yksi yksinkertainen tapa olisi yksinkertaisesti valita yksi määrä ryhmiä – esimerkiksi kaksi – ja yrittää sitten ryhmitellä pisteitä valinnan perusteella.
Oletetaan, että olemme päättäneet, että niitä on kaksi ryhmää myymälöistämme (pisteet). Nyt meidän on löydettävä tapa ymmärtää, mitkä pisteet kuuluvat mihinkin ryhmään. Tämä voidaan tehdä valitsemalla yksi piste edustamaan ryhmä 1 ja yksi edustamaan ryhmä 2. Näitä pisteitä käytetään viitteenä mitattaessa etäisyyttä kaikista muista pisteistä kuhunkin ryhmään.
Sillä tavalla sano pointti (5, 3)
päätyy kuulumaan ryhmään 1, ja piste (79, 60)
ryhmään 2. Kun yrität määrittää uuden pisteen (6, 3)
ryhmille, meidän on mitattava sen etäisyys näihin kahteen pisteeseen. Kohdan tapauksessa (6, 3)
is lähempänä että (5, 3)
, joten se kuuluu ryhmään, jota tämä kohta edustaa - ryhmä 1. Näin voimme helposti ryhmitellä kaikki pisteet vastaaviin ryhmiin.
Tässä esimerkissä ryhmien määrän määrittämisen lisäksi (klusterit) – valitsemme myös joitain kohtia a viite etäisyys kunkin ryhmän uusiin pisteisiin.
Tämä on yleinen ajatus ymmärtääksemme yhtäläisyyksiä myymälöidemme välillä. Toteutetaan se käytännössä – voimme ensin valita kaksi vertailupistettä satunnainen. Vertailupiste ryhmä 1 on (5, 3)
ja vertailupiste ryhmä 2 on (10, 15)
. Voimme valita molemmat pisteemme numpy
joukko [0]
ja [1]
indeksit ja tallenna ne sinne g1
(ryhmä 1) ja g2
(ryhmä 2) muuttujat:
g1 = points[0]
g2 = points[1]
Tämän jälkeen meidän on laskettava etäisyys kaikista muista pisteistä näihin vertailupisteisiin. Tämä herättää tärkeän kysymyksen – kuinka tuo etäisyys mitataan. Voimme käyttää pohjimmiltaan mitä tahansa etäisyysmittausta, mutta tässä oppaassa käytetään euklidista etäisyyttä_.
Voi olla hyödyllistä tietää, että Euklidinen etäisyysmitta perustuu Pythagoraan lauseeseen:
$$
c^2 = a^2 + b^2
$$
Kun se on mukautettu tason pisteisiin - (a1, b1)
ja (a2, b2)
, edellinen kaava tulee:
$$
c^2 = (a2-a1)^2 + (b2-b1)^2
$$
Etäisyys on neliöjuuri c
, joten voimme kirjoittaa kaavan myös seuraavasti:
$$
euklidinen_{jaka} = sqrt[2][(a2 – a1)^2 + (b2 – b1) ^2)]
$$
Huomautus: Voit myös yleistää euklidisen etäisyyskaavan moniulotteisille pisteille. Esimerkiksi kolmiulotteisessa avaruudessa pisteillä on kolme koordinaattia – kaavamme heijastaa sitä seuraavalla tavalla:
$$
euklidinen_{jaka} = sqrt[2][(a2 – a1)^2 + (b2 – b1) ^2 + (c2 – c1) ^2)]
$$
Samaa periaatetta noudatetaan riippumatta tilan, jossa toimimme, mittojen määrästä.
Tähän mennessä olemme valinneet pisteet edustamaan ryhmiä ja osaamme laskea etäisyydet. Laitetaan nyt etäisyydet ja ryhmät yhteen liittämällä jokainen keräämämme kauppapiste ryhmään.
Selvittääksemme sen paremmin, julistamme kolme luetteloa. Ensimmäinen, joka tallentaa ensimmäisen ryhmän pisteitä – points_in_g1
. Toinen, joka tallentaa pisteitä ryhmästä 2 – points_in_g2
ja viimeinen - group
, To etiketti pisteet joko 1
(kuuluu ryhmään 1) tai 2
(kuuluu ryhmään 2):
points_in_g1 = []
points_in_g2 = []
group = []
Voimme nyt iteroida pisteidemme läpi ja laskea euklidisen etäisyyden niiden ja kunkin ryhmäviitteemme välillä. Jokainen piste tulee olemaan lähempänä johonkin kahdesta ryhmästä – sen perusteella, kumpi ryhmä on lähimpänä, kohdistamme jokaisen pisteen vastaavaan luetteloon ja lisäämme samalla 1
or 2
että group
lista:
for p in points:
x1, y1 = p[0], p[1]
euclidean_distance_g1 = np.sqrt((g1[0] - x1)**2 + (g1[1] - y1)**2)
euclidean_distance_g2 = np.sqrt((g2[0] - x1)**2 + (g2[1] - y1)**2)
if euclidean_distance_g1 < euclidean_distance_g2:
points_in_g1.append(p)
group.append('1')
else:
points_in_g2.append(p)
group.append('2')
Katsotaanpa tämän iteroinnin tuloksia nähdäksesi mitä tapahtui:
print(f'points_in_g1:{points_in_g1}n
npoints_in_g2:{points_in_g2}n
ngroup:{group}')
Mikä johtaa:
points_in_g1:[array([5, 3])]
points_in_g2:[array([10, 15]), array([15, 12]),
array([24, 10]), array([30, 45]),
array([85, 70]), array([71, 80]),
array([60, 78]), array([55, 52]),
array([80, 91])]
group:[1, 2, 2, 2, 2, 2, 2, 2, 2, 2]
Voimme myös piirtää klusterointituloksen eri väreillä määritettyjen ryhmien perusteella Seabornin avulla scatterplot()
jossa group
kuten hue
Perustelu:
import seaborn as sns
sns.scatterplot(x=points[:, 0], y=points[:, 1], hue=group)
On selvästi nähtävissä, että vain ensimmäinen pisteemme on asetettu ryhmään 1 ja kaikki muut pisteet ryhmään 2. Tämä tulos poikkeaa siitä, mitä olimme kuvitelleet alussa. Kun otetaan huomioon ero tulostemme ja alkuperäisten odotustemme välillä – voimmeko muuttaa sitä mitenkään? Näyttää olevan olemassa!
Yksi lähestymistapa on toistaa prosessi ja valita eri pisteet ryhmien referensseiksi. Tämä muuttaa tuloksiamme, toivottavasti, vastaamaan paremmin sitä, mitä olemme kuvitelleet alussa. Tällä toisella kerralla emme voineet valita niitä sattumanvaraisesti kuten aiemmin, vaan saamalla a tarkoittaa kaikista jo ryhmitellyistä pisteistämme. Näin uudet pisteet voitaisiin sijoittaa vastaavien ryhmien keskelle.
Esimerkiksi jos toisella ryhmällä oli vain pisteitä (10, 15)
, (30, 45)
. Uuden keskeinen pointti olisi (10 + 30)/2
ja (15+45)/2
– joka on yhtä suuri kuin (20, 30)
.
Koska olemme laittaneet tulokset listoihin, voimme muuntaa ne ensin numpy
taulukot, valitse niiden xs, ys ja hanki sitten tarkoittaa:
g1_center = [np.array(points_in_g1)[:, 0].mean(), np.array(points_in_g1)[:, 1].mean()]
g2_center = [np.array(points_in_g2)[:, 0].mean(), np.array(points_in_g2)[:, 1].mean()]
g1_center, g2_center
Neuvo: Yritä käyttää numpy
ja NumPy-taulukoita niin paljon kuin mahdollista. Ne on optimoitu parantamaan suorituskykyä ja yksinkertaistavat monia lineaarialgebran operaatioita. Aina kun yrität ratkaista jotain lineaarista algebran ongelmaa, sinun tulee ehdottomasti katsoa numpy
asiakirjat tarkistaaksesi, onko niitä olemassa numpy
menetelmä, joka on suunniteltu ratkaisemaan ongelmasi. Mahdollisuus on, että on!
Auttaaksemme toistamaan prosessia uusilla keskipisteillämme muuttamalla edellinen koodimme funktioksi, suorittamalla se ja katsomalla, onko pisteiden ryhmittelyssä tapahtunut muutoksia:
def assigns_points_to_two_groups(g1_center, g2_center):
points_in_g1 = []
points_in_g2 = []
group = []
for p in points:
x1, y1 = p[0], p[1]
euclidean_distance_g1 = np.sqrt((g1_center[0] - x1)**2 + (g1_center[1] - y1)**2)
euclidean_distance_g2 = np.sqrt((g2_center[0] - x1)**2 + (g2_center[1] - y1)**2)
if euclidean_distance_g1 < euclidean_distance_g2:
points_in_g1.append(p)
group.append(1)
else:
points_in_g2.append(p)
group.append(2)
return points_in_g1, points_in_g2, group
Huomautus: Jos huomaat toistavasi samaa koodia yhä uudelleen ja uudelleen, sinun tulee kääriä koodi erilliseen funktioon. Parhaana käytäntönä pidetään koodin järjestämistä funktioiksi, erityisesti koska ne helpottavat testausta. On helpompi testata ja eristää koodinpala kuin täysi koodi ilman toimintoja.
Kutsutaan funktio ja tallennetaan sen tulokset sisään points_in_g1
, points_in_g2
ja group
muuttujat:
points_in_g1, points_in_g2, group = assigns_points_to_two_groups(g1_center, g2_center)
points_in_g1, points_in_g2, group
Ja piirrä myös sirontakaavio värillisillä pisteillä visualisoidaksesi ryhmien jaon:
sns.scatterplot(x=points[:, 0], y=points[:, 1], hue=group)
Näyttää siltä, että pisteidemme ryhmittely on paranee. Mutta silti, kaavion keskellä on kaksi pistettä, jotka voidaan osoittaa jommallekummalle ryhmälle, kun otetaan huomioon niiden läheisyys molempiin ryhmiin. Tähän mennessä kehittämämme algoritmi määrittää molemmat pisteet toiseen ryhmään.
Tämä tarkoittaa, että voimme luultavasti toistaa prosessin vielä kerran ottamalla X:n ja Y:n keskiarvot luoden kaksi uutta keskuspistettä (keskukset) ryhmiimme ja jakamalla ne uudelleen etäisyyden perusteella.
Luodaan myös funktio sentroidien päivittämiseksi. Koko prosessi voidaan nyt pelkistää useisiin kyseisen toiminnon kutsuihin:
def updates_centroids(points_in_g1, points_in_g2):
g1_center = np.array(points_in_g1)[:, 0].mean(), np.array(points_in_g1)[:, 1].mean()
g2_center = np.array(points_in_g2)[:, 0].mean(), np.array(points_in_g2)[:, 1].mean()
return g1_center, g2_center
g1_center, g2_center = updates_centroids(points_in_g1, points_in_g2)
points_in_g1, points_in_g2, group = assigns_points_to_two_groups(g1_center, g2_center)
sns.scatterplot(x=points[:, 0], y=points[:, 1], hue=group)
Huomaa, että tämän kolmannen iteraation jälkeen jokainen piste kuuluu nyt eri klustereihin. Näyttää siltä, että tulokset paranevat – tehdään se vielä kerran. Nyt mennään neljäs iteraatio menetelmästämme:
g1_center, g2_center = updates_centroids(points_in_g1, points_in_g2)
points_in_g1, points_in_g2, group = assigns_points_to_two_groups(g1_center, g2_center)
sns.scatterplot(x=points[:, 0], y=points[:, 1], hue=group)
Tällä neljännellä kerralla saimme sama tulos kuin edellinen. Joten näyttää siltä, että pisteemme eivät enää vaihda ryhmiä, tuloksemme on saavuttanut jonkinlaisen vakauden – se on päässyt muuttumattomaan tilaan, tai lähentyneet. Lisäksi meillä on täsmälleen sama tulos kuin olimme kuvitellut kahdelle ryhmälle. Voimme myös nähdä, onko tämä saavutettu jako järkevä.
Kerrataanpa nopeasti, mitä olemme tehneet tähän mennessä. Olemme jakaneet 10 myymäläämme maantieteellisesti kahteen osaan – yksi alemmille lounaisille alueille ja toiset koilliseen. Voi olla mielenkiintoista kerätä lisää tietoja jo olemassa olevien tietojen lisäksi – tulot, päivittäinen asiakasmäärä ja paljon muuta. Näin voimme tehdä monipuolisemman analyysin ja mahdollisesti tuottaa mielenkiintoisempia tuloksia.
Tällaisia klusterointitutkimuksia voidaan tehdä, kun jo vakiintunut brändi haluaa valita alueen uuden myymälän avaamiseksi. Tällöin huomioidaan paljon muitakin muuttujia kuin sijainti.
Mitä tekemistä tällä kaikella on K-Means-algoritmin kanssa?
Näitä vaiheita noudattaessasi olet ehkä miettinyt, mitä tekemistä niillä on K-Means-algoritmin kanssa. Toistaiseksi toteuttamamme prosessi on K-Means-algoritmi. Lyhyesti sanottuna olemme määrittäneet ryhmien/klusterien lukumäärän, valinneet satunnaisesti alkupisteitä ja päivittäneet sentroideja jokaisessa iteraatiossa, kunnes klusterit konvergoivat. Olemme pohjimmiltaan suorittaneet koko algoritmin käsin – jokainen vaihe on suoritettu huolellisesti.
- K K-Meansissa on peräisin klustereiden määrä jotka on asetettava ennen iterointiprosessin aloittamista. Meidän tapauksessamme K = 2. Tämä ominaisuus nähdään joskus mm negatiivinen Ottaen huomioon, että on olemassa muita klusterointimenetelmiä, kuten Hierarchical Clustering, joissa ei tarvitse olla kiinteää määrää klustereita etukäteen.
Keinokäytön ansiosta myös K-keino muuttuu herkkä poikkeaville arvoille ja ääriarvoille – ne lisäävät vaihtelua ja vaikeuttavat sentroididemme osuutta. Ole siis tietoinen suoritustarpeesta ääriarvot ja outlier-analyysit ennen klusteroinnin suorittamista K-Means-algoritmilla.
Huomaa myös, että pisteemme segmentoitiin suoriin osiin, klustereita luotaessa ei ole käyriä. Se voi myös olla K-Means-algoritmin haitta.
Huomautus: Kun haluat sen olevan joustavampi ja mukautuvampi ellipseihin ja muihin muotoihin, kokeile a yleistetty K-keskiarvo Gaussin sekoitusmalli. Tämä malli voi mukautua elliptisiin segmentointiklustereihin.
K-Meansilla on myös monia etuja! Toimii hyvin päällä suuret tietokannat joita voi olla vaikea käsitellä, jos käytät tietyntyyppisiä hierarkkisia klusterointialgoritmeja. Se myös takaa lähentymisenja voi helposti yleistää ja sopeuttaa. Lisäksi se on luultavasti eniten käytetty klusterointialgoritmi.
Nyt kun olemme käyneet läpi kaikki K-Means-algoritmissa suoritetut vaiheet ja ymmärtäneet sen kaikki edut ja haitat, voimme vihdoin ottaa K-Meansin käyttöön Scikit-Learn-kirjaston avulla.
K-Means-algoritmin käyttöönotto Scikit-Opi
Tarkistaaksemme tuloksemme uudelleen, tee tämä prosessi uudelleen, mutta nyt käyttämällä 3 riviä koodia sklearn
:
from sklearn.cluster import KMeans
kmeans = KMeans(n_clusters=2, random_state=42)
kmeans.fit(points)
kmeans.labels_
Tässä tarrat ovat samat kuin aiemmissa ryhmissämme. Piirretään tulos nopeasti:
sns.scatterplot(x = points[:,0], y = points[:,1], hue=kmeans.labels_)
Tuloksena oleva kaavio on sama kuin edellisen osan kuvaaja.
Tutustu käytännönläheiseen, käytännölliseen Gitin oppimisoppaaseemme, jossa on parhaat käytännöt, alan hyväksymät standardit ja mukana tuleva huijauslehti. Lopeta Git-komentojen googlailu ja oikeastaan oppia se!
Huomautus: Pelkästään sen tarkastelu, kuinka olemme suorittaneet K-Means-algoritmin Scikit-Learnin avulla, saattaa antaa vaikutelman, että se on turha ja että sinun ei tarvitse huolehtia siitä liikaa. Vain 3 koodiriviä suorittavat kaikki vaiheet, joista olemme keskustelleet edellisessä osiossa, kun olemme käyneet läpi K-Means-algoritmin vaihe vaiheelta. Mutta, paholainen on yksityiskohdissa tässä tapauksessa! Jos et ymmärrä kaikkia algoritmin vaiheita ja rajoituksia, kohtaat todennäköisesti tilanteen, jossa K-Means-algoritmi antaa tuloksia, joita et odottanut.
Scikit-Learnin avulla voit myös alustaa K-Meansin nopeuttaaksesi lähentymistä asettamalla init='k-means++'
Perustelu. Laajemmassa mielessä K-Means++ valitsee silti k alkuperäiset klusterikeskukset satunnaisesti tasaisen jakautumisen jälkeen. Sitten jokainen seuraava klusterin keskus valitaan jäljellä olevista datapisteistä, ei laskemalla vain etäisyysmitta - vaan käyttämällä todennäköisyyttä. Todennäköisyysarvon käyttäminen nopeuttaa algoritmia ja on hyödyllistä käsiteltäessä erittäin suuria tietojoukkoja.
Kyynärpäämenetelmä – Parhaan ryhmien lukumäärän valitseminen
Toistaiseksi niin hyvin! Olemme ryhmitelleet 10 kauppaa pisteiden ja sentroidien välisen euklidisen etäisyyden perusteella. Mutta entä ne kaksi kaavion keskellä olevaa pistettä, joita on hieman vaikeampi ryhmitellä? Eivätkö he voisi muodostaa myös erillisen ryhmän? Teimmekö todella virheen valitessaan K = 2 ryhmät? Ehkä meillä todella oli K = 3 ryhmät? Meillä voi jopa olla enemmän kuin kolme ryhmää, emmekä ole tietoisia siitä.
Tässä esitetty kysymys on kuinka määrittää ryhmien lukumäärä (K) K-Meansissa. Vastataksemme tähän kysymykseen, meidän on ymmärrettävä, olisiko olemassa "parempi" klusteri eri K:n arvolle.
Naiivi tapa selvittää se on ryhmitellä pisteitä eri arvoilla K, niin, varten K=2, K=3, K=4 ja niin edelleen:
for number_of_clusters in range(1, 11):
kmeans = KMeans(n_clusters = number_of_clusters, random_state = 42)
kmeans.fit(points)
Mutta klusterointipisteet eri Ks yksin ei riitä ymmärtääksemme, olemmeko valinneet ihanteellisen arvon K. Tarvitsemme tavan arvioida kunkin klusteroinnin laatua K olemme valinneet.
Lasketaan manuaalisesti Cluster Sum of Squares (WCSS) sisällä
Tässä on ihanteellinen paikka esitellä mitta siitä, kuinka paljon klusteripisteemme ovat lähellä toisiaan. Se kuvaa lähinnä kuinka paljon vaihtelu meillä on yhden klusterin sisällä. Tätä toimenpidettä kutsutaan Klusterin neliösumman sisällätai WCSS lyhyesti. Mitä pienempi WCSS on, sitä lähempänä pisteemme ovat, joten meillä on paremmin muodostettu klusteri. WCSS-kaavaa voidaan käyttää mille tahansa määrälle klustereita:
$$
WCSS = summa(Pi_1 – Keskipiste_1)^2 + cdots + summa(Pi_n – Keskipiste_n)^2
$$
Huomautus: Tässä oppaassa käytämme Euklidinen etäisyys sentroidien saamiseksi, mutta myös muita etäisyysmittoja, kuten Manhattania, voitaisiin käyttää.
Nyt voimme olettaa, että olemme valinneet kaksi klusteria ja yrittää toteuttaa WCSS:n ymmärtääksemme paremmin mitä WCSS on ja kuinka sitä käytetään. Kuten kaava kertoo, meidän on laskettava yhteen kaikkien klusteripisteiden ja sentroidien väliset erot. Joten jos ensimmäinen pisteemme ensimmäisestä ryhmästä on (5, 3)
ja ensimmäisen ryhmän viimeinen sentroidi (konvergenssin jälkeen) on (16.8, 17.0)
, WCSS on:
$$
WCSS = summa((5,3) – (16.8, 17.0))^2
$$
$$
WCSS = summa((5-16.8) + (3-17.0))^2
$$
$$
WCSS = summa((-11.8) + (-14.0))^2
$$
$$
WCSS = summa((-25.8))^2
$$
$$
WCSS = 335.24
$$
Tämä esimerkki havainnollistaa, kuinka laskemme WCSS:n yhdelle pisteelle klusterista. Mutta klusteri sisältää yleensä useamman kuin yhden pisteen, ja meidän on otettava ne kaikki huomioon WCSS:ää laskettaessa. Teemme sen määrittämällä funktion, joka vastaanottaa joukon pisteitä ja sentroideja ja palauttaa neliöiden summan:
def sum_of_squares(cluster, centroid):
squares = []
for p in cluster:
squares.append((p - centroid)**2)
ss = np.array(squares).sum()
return ss
Nyt voimme saada kunkin klusterin neliöiden summan:
g1 = sum_of_squares(points_in_g1, g1_center)
g2 = sum_of_squares(points_in_g2, g2_center)
Ja laske tulokset yhteen saadaksesi kokonaissumman WCSS:
g1 + g2
Tämä johtaa:
2964.3999999999996
Joten meidän tapauksessamme milloin K on yhtä suuri kuin 2, koko WCSS on 2964.39. Nyt voimme vaihtaa K:t ja laskea WCSS:n kaikille niille. Näin voimme saada käsityksen siitä, mitä K meidän tulisi valita, että klusterointimme toimii parhaiten.
laskettaessa WCSS Käyttäminen Scikit-Opi
Onneksi meidän ei tarvitse manuaalisesti laskea WCSS:ää jokaiselle K. Kun olet suorittanut K-Means-klusteroinnin tietylle klusterimäärälle, voimme saada sen WCSS:n käyttämällä inertia_
attribuutti. Nyt voimme palata K-Meansiin for
silmukka, käytä sitä klustereiden määrän määrittämiseen ja vastaavien WCSS-arvojen luetteloimiseen:
wcss = []
for number_of_clusters in range(1, 11):
kmeans = KMeans(n_clusters = number_of_clusters, random_state = 42)
kmeans.fit(points)
wcss.append(kmeans.inertia_)
wcss
Huomaa, että luettelon toinen arvo on täsmälleen sama, jolle olemme laskeneet aiemmin K = 2:
[18272.9, # For k=1
2964.3999999999996, # For k=2
1198.75, # For k=3
861.75,
570.5,
337.5,
175.83333333333334,
79.5,
17.0,
0.0]
Jos haluat visualisoida nämä tulokset, piirretään meidän Ks WCSS-arvojen kanssa:
ks = [1, 2, 3, 4, 5 , 6 , 7 , 8, 9, 10]
plt.plot(ks, wcss)
Juonissa on keskeytys, kun x = 2
, linjan alin kohta ja vielä alempi, kun x = 3
. Huomaa, että se muistuttaa meitä kyynärpään muoto. Piirtämällä K:t WCSS:n kanssa käytämme Kyynärpää menetelmä valitaksesi Ks:n määrän. Ja valittu K on täsmälleen alin kyynärpää, niin se olisi 3
sijasta 2
, meidän tapauksessamme:
ks = [1, 2, 3, 4, 5 , 6 , 7 , 8, 9, 10]
plt.plot(ks, wcss);
plt.axvline(3, linestyle='--', color='r')
Voimme ajaa K-Means-klusterialgoritmin uudelleen nähdäksemme, miltä tietomme näyttäisivät kolme klusteria:
kmeans = KMeans(n_clusters=3, random_state=42)
kmeans.fit(points)
sns.scatterplot(x = points[:,0], y = points[:,1], hue=kmeans.labels_)
Olimme jo tyytyväisiä kahteen klusteriin, mutta kyynärpäämenetelmän mukaan kolme klusteria sopisi paremmin tietoomme. Tässä tapauksessa meillä olisi kolmenlaisia myymälöitä kahden sijaan. Ennen kyynärpäämenetelmän käyttöä mietimme lounaisia ja koillisia myymäläklustereita, nyt meillä on myös myymälät keskustassa. Ehkä se voisi olla hyvä paikka avata toinen myymälä, koska sen lähellä olisi vähemmän kilpailua.
Vaihtoehtoiset klusterin laatutoimenpiteet
On myös muita mittareita, joita voidaan käyttää klusterin laadun arvioinnissa:
- Siluettipisteet – analysoi klusterin sisäisten pisteiden välisen etäisyyden lisäksi myös itse klusterien välistä etäisyyttä
- Klusterien välissä Neliöiden summa (BCSS) – WCSS:ää täydentävä mittari
- Neliöiden summa -virhe (SSE)
- Suurin säde – mittaa suurimman etäisyyden pisteestä sen painopisteeseen
- Keskimääräinen säde – suurimman etäisyyden summa pisteestä sen painopisteeseen jaettuna klusterien lukumäärällä.
On suositeltavaa kokeilla ja tutustua jokaiseen niistä, koska ongelmasta riippuen jotkut vaihtoehdot voivat olla soveltuvampia kuin yleisimmin käytetyt mittarit (WCSS ja Silhouette Score).
Loppujen lopuksi, kuten monien datatieteen algoritmien kohdalla, haluamme vähentää varianssia kunkin klusterin sisällä ja maksimoida varianssin eri klustereiden välillä. Joten meillä on enemmän määriteltyjä ja erotettavia klustereita.
K-Meansin käyttäminen toisessa tietojoukossa
Käytetään oppimaamme toisessa tietojoukossa. Tällä kertaa yritämme löytää samanlaisia viinejä.
Huomautus: Voit ladata tietojoukon tätä.
Aloitamme tuomalla pandas
lukea wine-clustering
CSV (Pilkuilla erotetut arvot) tiedosto a Dataframe
rakenne:
import pandas as pd
df = pd.read_csv('wine-clustering.csv')
Kun se on ladattu, katsotaanpa viittä ensimmäistä datatietuetta -sovelluksella head()
menetelmä:
df.head()
Tämä johtaa:
Alcohol Malic_Acid Ash Ash_Alcanity Magnesium Total_Phenols Flavanoids Nonflavanoid_Phenols Proanthocyanins Color_Intensity Hue OD280 Proline
0 14.23 1.71 2.43 15.6 127 2.80 3.06 0.28 2.29 5.64 1.04 3.92 1065
1 13.20 1.78 2.14 11.2 100 2.65 2.76 0.26 1.28 4.38 1.05 3.40 1050
2 13.16 2.36 2.67 18.6 101 2.80 3.24 0.30 2.81 5.68 1.03 3.17 1185
3 14.37 1.95 2.50 16.8 113 3.85 3.49 0.24 2.18 7.80 0.86 3.45 1480
4 13.24 2.59 2.87 21.0 118 2.80 2.69 0.39 1.82 4.32 1.04 2.93 735
Meillä on monia mittauksia viinien sisältämistä aineista. Tässä meidän ei myöskään tarvitse muuttaa kategoriallisia sarakkeita, koska ne kaikki ovat numeerisia. Katsotaanpa nyt kuvaavia tilastoja describe()
menetelmä:
df.describe().T
Kuvaustaulukko:
count mean std min 25% 50% 75% max
Alcohol 178.0 13.000618 0.811827 11.03 12.3625 13.050 13.6775 14.83
Malic_Acid 178.0 2.336348 1.117146 0.74 1.6025 1.865 3.0825 5.80
Ash 178.0 2.366517 0.274344 1.36 2.2100 2.360 2.5575 3.23
Ash_Alcanity 178.0 19.494944 3.339564 10.60 17.2000 19.500 21.5000 30.00
Magnesium 178.0 99.741573 14.282484 70.00 88.0000 98.000 107.0000 162.00
Total_Phenols 178.0 2.295112 0.625851 0.98 1.7425 2.355 2.8000 3.88
Flavanoids 178.0 2.029270 0.998859 0.34 1.2050 2.135 2.8750 5.08
Nonflavanoid_Phenols 178.0 0.361854 0.124453 0.13 0.2700 0.340 0.4375 0.66
Proanthocyanins 178.0 1.590899 0.572359 0.41 1.2500 1.555 1.9500 3.58
Color_Intensity 178.0 5.058090 2.318286 1.28 3.2200 4.690 6.2000 13.00
Hue 178.0 0.957449 0.228572 0.48 0.7825 0.965 1.1200 1.71
OD280 178.0 2.611685 0.709990 1.27 1.9375 2.780 3.1700 4.00
Proline 178.0 746.893258 314.907474 278.00 500.500 673.500 985.0000 1680.00
Taulukkoa katsomalla on selvää, että niitä on tietojen vaihtelua – joillekin sarakkeille, kuten Alchool
on enemmän, ja muille, kuten Malic_Acid
, vähemmän. Nyt voimme tarkistaa, onko niitä null
tai NaN
arvot tietojoukossamme:
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 178 entries, 0 to 177
Data columns (total 13 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Alcohol 178 non-null float64
1 Malic_Acid 178 non-null float64
2 Ash 178 non-null float64
3 Ash_Alcanity 178 non-null float64
4 Magnesium 178 non-null int64
5 Total_Phenols 178 non-null float64
6 Flavanoids 178 non-null float64
7 Nonflavanoid_Phenols 178 non-null float64
8 Proanthocyanins 178 non-null float64
9 Color_Intensity 178 non-null float64
10 Hue 178 non-null float64
11 OD280 178 non-null float64
12 Proline 178 non-null int64
dtypes: float64(11), int64(2)
memory usage: 18.2 KB
Tietoja ei tarvitse pudottaa tai syöttää, koska tietojoukossa ei ole tyhjiä arvoja. Voimme käyttää Seabornia pairplot()
nähdäksesi tietojen jakautumisen ja tarkistaaksesi, muodostaako tietojoukko sarakepareja, jotka voivat olla mielenkiintoisia klusteroinnin kannalta:
sns.pairplot(df)
Parikaaviota katsoen kaksi saraketta vaikuttaa lupaavilta klusterointitarkoituksiin - Alcohol
ja OD280
(joka on menetelmä viinien proteiinipitoisuuden määrittämiseksi). Näyttää siltä, että tontilla on 3 erillistä klusteria, jotka yhdistävät kaksi niistä.
On myös muita sarakkeita, jotka näyttävät olevan korrelaatiossa. Varsinkin Alcohol
ja Total_Phenols
ja Alcohol
ja Flavanoids
. Heillä on suuret lineaariset suhteet, jotka voidaan havaita parikaaviossa.
Koska painopisteemme on klusterointi K-Meansin kanssa, valitaan vaikkapa yksi sarakepari Alcohol
ja OD280
ja testaa tämän tietojoukon kyynärpäämenetelmää.
Huomautus: Kun käytetään useampia tietojoukon sarakkeita, on tarpeen joko piirtää 3-ulotteisesti tai pienentää tiedot pääkomponentit (PCA:n käyttö). Tämä on pätevä ja yleisempi lähestymistapa, muista vain valita pääkomponentit sen mukaan, kuinka paljon ne selittävät, ja muista, että datan ulottuvuuksia pienennettäessä tapahtuu jonkin verran informaatiota – joten käyrä on likiarvo todellisista tiedoista, ei siitä, miten se todellisuudessa on.
Piirretään sirontadiagrammi siten, että nämä kaksi saraketta on asetettu sen akseliksi, jotta voimme tarkastella lähemmin pisteitä, jotka haluamme jakaa ryhmiin:
sns.scatterplot(data=df, x='OD280', y='Alcohol')
Nyt voimme määrittää sarakkeet ja käyttää kyynärpäämenetelmää klustereiden määrän määrittämiseen. Aloitamme myös algoritmin kanssa kmeans++
vain varmistaaksesi, että se konvergoi nopeammin:
values = df[['OD280', 'Alcohol']]
wcss_wine = []
for i in range(1, 11):
kmeans = KMeans(n_clusters = i, init = 'k-means++', random_state = 42)
kmeans.fit(values)
wcss_wine.append(kmeans.inertia_)
Olemme laskeneet WCSS:n, jotta voimme piirtää tulokset:
clusters_wine = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
plt.plot(clusters_wine, wcss_wine)
plt.axvline(3, linestyle='--', color='r')
Kyynärpäämenetelmän mukaan tässä pitäisi olla 3 klusteria. Viimeistä vaihetta varten ryhmitellään pisteemme kolmeen klusteriin ja piirretään väreillä tunnistetut klusterit:
kmeans_wine = KMeans(n_clusters=3, random_state=42)
kmeans_wine.fit(values)
sns.scatterplot(x = values['OD280'], y = values['Alcohol'], hue=kmeans_wine.labels_)
Voimme nähdä klustereita 0
, 1
ja 2
kaaviossa. Analyysimme perusteella ryhmä 0 sisältää viinejä, joissa on korkeampi proteiinipitoisuus ja vähemmän alkoholia, ryhmä 1 on viinejä, joissa on korkeampi alkoholipitoisuus ja vähän proteiinia, ja ryhmä 2 sen viineissä on sekä runsaasti proteiinia että alkoholia.
Tämä on erittäin mielenkiintoinen tietojoukko ja rohkaisen sinua menemään analyysiin syvemmälle klusteroimalla tiedot normalisoinnin ja PCA:n jälkeen – myös tulkitsemalla tuloksia ja etsimällä uusia yhteyksiä.
Yhteenveto
K-tarkoittaa klusterointi on yksinkertainen mutta erittäin tehokas valvomaton koneoppimisalgoritmi tietojen klusterointiin. Se klusteroi tiedot datapisteiden välisen euklidisen etäisyyden perusteella. K-Means-klusterointialgoritmilla on monia käyttötarkoituksia tekstiasiakirjojen, kuvien, videoiden ja paljon muuta ryhmittelemään.