Praca w dziale machine learning w Polsce

Machine learning to pojęcie, o którym słyszała już każda osoba związana z tematyką obróbki danych. Mówi się, że uczenie maszynowe jest przyszłością gospodarki, a jego zastosowania są nieograniczone. Tak dobra reklama tej tematyki wpływa na rynek – zapotrzebowanie na specjalistów jest ogromne.

Liczba ofert pracy związanych z machine learningiem w Polsce na portalu LinkedIn (7.11.2018r.)

Jednak uczenie maszynowe – ze swojej definicji – przebiega przecież w sposób automatyczny. Wygenerowanie wyniku nie wymaga zaawansowanych obliczeń ze strony człowieka. Dlaczego więc nadal poszukuje się tylu nowych pracowników w tym obszarze?

Jest to spowodowane dużą rolą czynnika eksperckiego – maszyna wykonuje oczywiście obliczenia, ale kluczowe okazuje się tutaj dopasowanie używanych algorytmów do danych oraz późniejsza interpretacja wyników – a te wymagają już dużej wiedzy i zmysłu analitycznego badacza.

Aby lepiej naświetlić ten problem, przedstawię działanie dwóch popularnych algorytmów przydatnych w analizie skupień. Różni je sposób przyporządkowywania punktów do klastrów. Algorytmy zostaną przetestowane na czterech syntetycznych zbiorach danych o różnych właściwościach.

Algorytmy klastrujące

W dynamicznej rzeczywistości machine learningu znajduje się miejsce dla wielu algorytmów klastrujących. Metody są różnorodne: twarde (przyporządkowujące punkt do dokładnie jednego skupienia), miękkie (dające jedynie prawdopodobieństwo przynależności do klastra np. fuzzy clustering); te wymagające wcześniejszego zdefiniowania liczby grup lub takie, które same zatrzymują się na ich najbardziej optymalnej liczbie. Możliwości wyboru metody są więc właściwie nieograniczone.

W tej pracy skoncentruję się na dwóch metodach klastrowania - odmiennych od siebie, ale bardzo popularnych.

Pierwszą z nich jest metoda k-średnich (k-means). Jest to podstawowy algorytm służący do maszynowego podziału analizowanych danych. W tej metodzie nacisk położony jest na odległości punktów od siebie. Celem klastrowania jest jednoznaczne przyporządkowanie każdego punktu ze zbioru danych do jednego z klastrów. Liczba skupień jest określona a priori i ustalona na poziomie n.

Zasada jest następująca: punkty wewnątrz jednej grupy powinny znajdować się jak najbliżej siebie, jednocześnie będąc możliwie najdalej od punktów należących do innych podgrup. Model przelicza się, aż do odnalezienia optymalnych centrów skupień. W tym podejściu środki grup nie są punktami z oryginalnego zbioru, lecz wynikają z obliczeń optymalizacyjnych. Bliższe spojrzenie na matematyczne aspekty metody znajduje się w artykule J. B. MacQueena z 1967r. 1

Inną metodą jest oparty na gęstości DBScan (Density-Based Spatial Clustering of Applications with Noise). Algorytm dzieli zbiór danych na obszary różniące się zagęszczeniem występowania punktów. Za dane wejściowe w tej metodzie służą: parametr epsilon, który stanowi promień okręgu wyznaczającego „sąsiedztwo punktu” oraz liczba obserwacji, które w tym promieniu muszą się znajdować, by uznać ten obszar za „gęsty”.

Punkty są przypisywane do jednego zbioru wraz ze swoimi sąsiadami, również spełniającymi założenie o gęstości obszaru wokół nich. W ten sposób łańcuchowo tworzy się grupy połączone gęstością. Granice skupień wyznaczają punkty niespełniające warunku zagęszczenia, które znajdują się w promieniu sąsiedztwa obserwacji go spełniających. Analizując kolejne punkty, algorytm sam określa liczbę klastrów. Obserwacje, które nie mają wokół siebie odpowiedniej liczby sąsiadów, są uznawane za szum i nie przypisują się do żadnego zbioru (lub nadawana im jest przynależność do tak zwanego klastra 0). Jest to szczególnie przydatna metoda podczas pracy z danymi zanieczyszczonymi lub przestrzennymi. Więcej o tym algorytmie i jego zastosowaniach można znaleźć w artykule Martina Estera et al. z 1996r.2

Dane

W celu przetestowania algorytmów użyłam czterech zbiorów danych o różnych charakterystykach: - zbiór 1 - dane losowe, wygenerowane przy użyciu rozkładu normalnego o parametrach (1, 0.05^2), - zbiór 2 - dane utworzone z pięciu podgrup, odseparowanych od siebie, - zbiór 3 - dane utworzone z czterech podgrup z dodanymi obserwacjami odstającymi, - zbiór 4 - dane utworzone z czterech podgrup formujących wzór przestrzenny (pasy).

Zbiór pierwszy powstał przy wykorzystaniu wbudowanego w R generatora liczb losowych z rozkładu normalnego. Kolejne trzy zbiory powstały przy użyciu pakietu MixSim. Jest to użyteczne narzędzie, pozwalające na generowanie danych w oparciu o mieszaniny rozkładów Gaussa.3 Dzięki temu można utworzyć kolekcję punktów podzielonych na podgrupy. Porównanie oryginalnego podziału mieszaniny do wyniku klastrowania pozwala na dość dobrą ocenę działania algorytmu.

Kod generujący zbiory danych.

#zbior danych losowych
zb1<- matrix(rnorm(300, mean=1, sd=0.05),150, 2) 
zb1<-as.data.frame(zb1)
summary(zb1)
##        V1               V2        
##  Min.   :0.8358   Min.   :0.8894  
##  1st Qu.:0.9754   1st Qu.:0.9692  
##  Median :1.0019   Median :0.9962  
##  Mean   :1.0010   Mean   :1.0011  
##  3rd Qu.:1.0344   3rd Qu.:1.0339  
##  Max.   :1.0961   Max.   :1.1310
#wykres
par(mar = c(0.2, 0.2, 1.5, 0.2))
par(mfrow = c(1,1))
kolor <- c(rainbow(5, alpha = 1))
plot(zb1, col=kolor[4], pch = 19, cex = 0.8,xlab = "", ylab = "", axes = FALSE, main="Zbiór 1")
box()

#zbior z jasnymi klastrami
library("MixSim")
## Loading required package: MASS
set.seed(1232)
A3 <- MixSim(MaxOmega = 0.020, K = 5, p = 2)
B3 <- simdataset(n = 300, Pi = A3$Pi, Mu = A3$Mu, S = A3$S)
zb2<-as.data.frame(B3$X)
summary(zb2)
##        V1               V2        
##  Min.   :0.1487   Min.   :0.4095  
##  1st Qu.:0.2869   1st Qu.:0.5701  
##  Median :0.8245   Median :0.8399  
##  Mean   :0.6477   Mean   :0.7384  
##  3rd Qu.:0.9123   3rd Qu.:0.8879  
##  Max.   :1.0209   Max.   :0.9946
#wykres
par(mar = c(0.2, 0.2, 1.5, 0.2))
plot(B3$X, col = kolor[B3$id], pch = 19, cex = 0.8, xlab = "", ylab = "", axes = FALSE, main="Zbiór 2 z podziałem na skupienia")
box()

#zbior jasne klastry + outliers
set.seed(1236)
A4<- MixSim(BarOmega = 0.01, K = 4, p = 2)
B4 <- simdataset(n = 290, Pi = A4$Pi, Mu = A4$Mu, S = A4$S, n.out=10)
zb3<-as.data.frame(B4$X)
summary(zb3)
##        V1                  V2         
##  Min.   :0.0007544   Min.   :-0.3174  
##  1st Qu.:0.2370496   1st Qu.: 0.4529  
##  Median :0.3548580   Median : 0.7793  
##  Mean   :0.4355272   Mean   : 0.6198  
##  3rd Qu.:0.6205312   3rd Qu.: 0.8744  
##  Max.   :1.0331413   Max.   : 1.1283
#wykres
par(mar = c(0.2, 0.2, 1.5, 0.2))
plot(B4$X, col = kolor[B4$id+1], pch = 19, cex = 0.8, xlab = "", ylab = "", axes = FALSE, main="Zbiór 3 z podziałem na skupienia")
box()

#pionowe pasy
set.seed(1235)
A5 <- MixSim(MaxOmega = 0.0001, K = 4, p = 1)
B5 <- simdataset(n = 300, Pi = A5$Pi, Mu = A5$Mu, S = A5$S, n.noise = 1)
zb4<-as.data.frame(B5$X)
summary(zb4)
##        V1                V2        
##  Min.   :0.07077   Min.   :0.1083  
##  1st Qu.:0.21383   1st Qu.:0.3330  
##  Median :0.47231   Median :0.5401  
##  Mean   :0.45673   Mean   :0.5238  
##  3rd Qu.:0.84962   3rd Qu.:0.7085  
##  Max.   :0.96350   Max.   :0.9004
#wykres
par(mar = c(0.2, 0.2, 1.5, 0.2))
plot(B5$X, col = kolor[B5$id], pch = 19, cex = 0.8,xlab = "", ylab = "", axes = FALSE, main="Zbiór 4 z podziałem na skupienia")
box()

Klastrowanie danych - przykład

Na początek przeprowadzę prostą analizę skupień, opartą wyłączniena intuicji. Do próby posłuży mi zbiór 2. Jako liczbę docelowych klastrów ustalam 5, gdyż był on wygenerowany z pięciu podzbiorów danych (K=5 w kodzie: A3<-MixSim(MaxOmega = 0.020, K = 5, p = 2).

#ładowanie pakietów przydatnych później
library(factoextra)
## Loading required package: ggplot2
## Welcome! Related Books: `Practical Guide To Cluster Analysis in R` at https://goo.gl/13EFCZ
library(dbscan)

kprzyklad2<-eclust(zb2, "kmeans", hc_metric="euclidean",k=5, graph=FALSE)
fviz_cluster(kprzyklad2, geom="point", main="Zbiór 2, kmeans, 5 klastrów", xlab=FALSE, ylab=FALSE, ggtheme=theme_classic()) 

Jak widać algorytm poradził sobie z tym zadaniem całkiem dobrze. Pojawiła się tylko jedna nieścisłość – w oryginalnym zbiorze dane z skupienia nr 3 pochodziły z dwóch podgrup. Tutaj algorytm przypisał je do jednego. Podzielił za to niesłusznie zbiór danych w lewym górnym rogu na klastry 4 i 5. Nie jest to pożądany efekt, ponieważ staramy się odwzorować naturalne zależności występujące w danych.

Nieco naiwne podejście dało nam wymiernie dobry rezultat. Teraz jednak przyjrzę się głębiej analizowanym zbiorom danych i spróbuję dobrać do nich odpowiednią metodę i parametry klastrowania.

Założenia dotyczące analizy

Dokonując analizy skupień będę porównywać działanie metody k-średnich i DBScan. Dla uproszczenia rozważań, będę zmieniać tylko jeden parametr danego algorytmu. Dla k-means będzie to oczywiście liczba klastrów. Przed podziałem sprawdzę ich optymalną liczbę i podstawię ją do funkcji. Dla DBScan będę szukać optymalnej wielkości parametru epsilon, przyjmując parametr minPts jako stały, na poziomie równym 3. Ustalam tę wartość arbitralnie, aby pokazać wartość roli dobrego analityka, bez zbytniego koncentrowania się na szczegółach. Uchylę to założenie w ostatniej części.

Badanie zbioru 1

Najpierw warto sprawdzić, czy zbiór nadaje się do klastrowania. Posłuży mi do tego statystyka Hopkinsa, według interpretacji: h=0 to zbiór z wyraźnie odznaczającymi się klastrami, a h=1 to dane z rozkładu jednostajnego. Towarzyszący jej wykres stanowi dodatkowe wsparcie analizy - czerwone elementy oznaczają wysokie podobieństwo punktów między sobą (potencjalne klastry), niebieskie obszary to obszar zróżnicowany (nie można zbudować z niego skupień). Jeśli wykres pokazuje wyraźne czerwone pola - zbiór da się łatwo podzielić na podgrupy.

get_clust_tendency(zb1, 2, graph=TRUE, gradient=list(low="red", mid="white", high="blue"), seed = 123)
## $hopkins_stat
## [1] 0.2421759
## 
## $plot

Statystyka Hopkinsa jest bliska 0,54. Jest to zgodne z oczekiwaniami – ta wartość oznacza dane losowe. Jednak na wykresie widać niewyraźne klastry (czerwone pola oznaczają grupy punktów o wysokim podobieństwie, a więc należące potencjalnie do jednego skupienia).

Następna do sprawdzenia jest statystyka silhouette, dzięki której będę w stanie określić liczbę klastrów metody k-średnich dającą najlepsze rezultaty.

fviz_nbclust(zb1, kmeans, method="silhouette")

Najlepszym wyborem jest punkt maksymalny na wykresie, a więc k=10.

Dla metody DBScan posłużę się wykresem odległości kNNDistplot. Na przegięciu wykresu znajduje się odpowiednia wartość parametru epsilon, który powinnam przyjąć do klastrowania.

#Poszukiwanie optymalnego epsilon, dla minPts=3
kNNdistplot(zb1, k=3) 
#linia pomocnicza na przegięciu wykresu 
abline(h=0.017, col="red", lty=3)

Dobrą wartością parametru epsilon przy minPts=3 jest 0.017.

Po takim przygotowaniu mogę w rozsądny sposób użyć algorytmów klastrujących na tym zbiorze.

#zbiór 1 kmeans
k1a<-eclust(zb1, "kmeans", hc_metric="euclidean",k=10, graph=FALSE)
fviz_cluster(k1a, geom="point", main="Zbiór 1, kmeans, 10 klastrów", xlab=FALSE, ylab=FALSE, ggtheme=theme_classic()) 

#zbiór 1 dbscan
k1b<-dbscan(zb1, eps=0.017, minPts=3) 
b1 = list(data = zb1, cluster = k1b$cluster)
fviz_cluster(b1, geom="point", main="Zbiór 1, DBScan, epsilon=0,017, minPts=3", xlab=FALSE, ylab=FALSE, ggtheme=theme_classic())

Zbiór danych, na których pracowałam, był całkowicie losowy. Oznacza to, że klastrowanie może nie przynieść zbyt dobrych rezultatów. Obie metody dały tu różne wyniki. Nie da się ich jednak między sobą porównać, czy też ocenić ich skuteczność. Trudno klasyfikować losowe punkty w podzbiory, skoro nie ma wśród nich żadnych cech różnicujących.

Badanie zbioru 2

Przechodzę do analizy zbioru nr 2. Został on wygenerowany z pięciu mniejszych podzbiorów. Zgodnie z logiką, idealny wynik analizy skupień powinien odwzorować pierwotny podział danych.

get_clust_tendency(zb2, 2, graph=TRUE, gradient=list(low="red", mid="white", high="blue"), seed = 123)
## $hopkins_stat
## [1] 0.1539524
## 
## $plot

Na wykresie widać wysoki potencjał podziału tego zbioru na klastry (czerwone obszary są wyjątkowo wyraźne). Niska wartość statystki Hopkinsa dodatkowo popiera tę obserwację.

fviz_nbclust(zb2, kmeans, method="silhouette")

Najlepszym wyborem parametru liczby klastrów dla algorytmu k-means jest 5. Jest to zgodne z pierwotnym podziałem danych.

kNNdistplot(zb2, k=3) 
abline(h=0.02, col="red", lty=3)

W algorytmie DBScan powinnam wykorzystać epsilon=0,02.

#zbiór 2 kmeans
k2a<-eclust(zb2, "kmeans", hc_metric="euclidean",k=5, graph=FALSE)
fviz_cluster(k2a, geom="point", main="Zbiór 2, kmeans, 5 klastrów", xlab=FALSE, ylab=FALSE, ggtheme=theme_classic()) 

#zbiór 2 dbscan
k2b<-dbscan(zb2, eps=0.02, minPts=3)
b2 = list(data = zb2, cluster = k2b$cluster)
fviz_cluster(b2, geom="point", main="Zbiór 2, DBScan, epsilon=0.02, minPts=3", xlab=FALSE, ylab=FALSE, ggtheme=theme_classic())

Ponownie rezultaty obu metod różnią się. Wynik algorytmu k-means jest dość dobry, choć pozostał problem ze złym rozpoznaniem piątej podgrupy. Optymalną wartość silhouette uzyskujemy dla 5 klastrów - nie są to jednak skupienia takie, jak w zbiorze oryginalnym. Algorytm DBScan dla zadanych parametrów dobrze poradził sobie z podziałem danych w prawym górnym rogu wykresu. Błędnie jednak doprowadził do podziału danych w prawym dolnym rogu. Kilka punktów nie zostało przydzielone do żadnego klastra (cluster 0 na wykresie to punkty uznane za obserwacje odstające). Jest to nieprawidłowy wynik – powinny one należeć do pobliskich skupień.

Badanie zbioru 3

Kolejny przypadek to zbiór nr 3. Zawiera on trzy podgrupy oraz dziesięć obserwacji odstających, wygenerowanych bez związku z innymi punktami. W idealnym świecie analiza powinna pokazać właśnie taki rezultat.

get_clust_tendency(zb3, 2, graph=TRUE, gradient=list(low="red", mid="white", high="blue"), seed = 123)
## $hopkins_stat
## [1] 0.293522
## 
## $plot

Na wykresie widać podział na klastry, choć jest to mniej dobitne niż dla zbioru nr 2. Niemniej, zbiór dobrze nadaje się do podziału na podgrupy.

fviz_nbclust(zb3, kmeans, method="silhouette")

Optymalną liczbą klastrów dla k-means jest 3. Wykres przyjmuje tam swoje wyraźne maksimum.

kNNdistplot(zb3, k=3) 
abline(h=0.042, col="red", lty=3)

Za epsilon przyjmę wartość 0,042.

#zbiór 3 kmeans
k3a<-eclust(zb3, "kmeans", hc_metric="euclidean",k=3, graph=FALSE)
fviz_cluster(k3a, geom="point", main="Zbiór 3, kmeans, 3 klastry", xlab=FALSE, ylab=FALSE, ggtheme=theme_classic()) 

#zbiór 3 dbscan
k3b<-dbscan(zb3, eps=0.042, minPts=3) 
b3 = list(data = zb3, cluster = k3b$cluster)
fviz_cluster(b3, geom="point", main="Zbiór 3, DBScan, epsilon=0.044, minPts=3", xlab=FALSE, ylab=FALSE, ggtheme=theme_classic())

Metoda k-średnich bardzo dobrze rozdzieliła punkty na trzy części. Jednak w wyniku pominięta jest zupełnie kwestia obserwacji odstających. Wszystkie punkty zostały zaklasyfikowane do skupień. Z drugiej strony, algorytm DBScan dla zadanych parametrów dobrze poradził sobie ze zidentyfikowaniem tylko jednego klastra. Cała reszta została za bardzo rozdrobniona i błędnie sklasyfikowana. Wiele punktów zostało uznane za odstające i nie zostały one przyporządkowane do żadnej z podgrup.

Badanie zbioru 4

Zbiór nr 4 jest w pewien sposób podobny do zbioru nr 2. Są na nim wyraźnie widoczne cztery klastry. Jednak ich specyficzny układ w formie pionowych pasów może stanowić problem dla algorytmów bazujących na odleglości.

get_clust_tendency(zb4, 2, graph=TRUE, gradient=list(low="red", mid="white", high="blue"), seed = 123)
## $hopkins_stat
## [1] 0.1990177
## 
## $plot

Statystyka Hopkinsa jest niska - zbiór 4 nadaje się więc do podziału na podgrupy. Wykres zawiera bardziej czerwone obszary, jednak, ponownie, nie są one tak wyraźne jak dla zbioru nr 2.

fviz_nbclust(zb4, kmeans, method="silhouette")

Dla algorytmu k-średnich najlepsze wyniki mamy uzyskać dla sześciu podgrup.

kNNdistplot(zb4, k=3) 
abline(h=0.047, col="red", lty=3)

Odpowiednia wartość parametru epsilon dla algorytmu DBScan to 0,047.

#zbiór 4 kmeans
k4a<-eclust(zb4, "kmeans", hc_metric="euclidean", k=6, graph=FALSE)
fviz_cluster(k4a, geom="point", main="Zbiór 4, kmeans, 6 klastrów", xlab=FALSE, ylab=FALSE, ggtheme=theme_classic()) 

#zbiór 4 dbscan
k4b<-dbscan(zb4, eps=0.047, minPts=3)
b4 = list(data = zb4, cluster = k4b$cluster)
fviz_cluster(b4, geom="point", main="Zbiór 4, DBScan, epsilon=0.047, minPts=3", xlab=FALSE, ylab=FALSE, ggtheme=theme_classic())

Metoda k-średnich podzieliła wszystkie podgrupy na pół, wzdłóż lini y=0. Oprócz tego dwa skrajnie lewe skupienia zostały połączone w jeden - algorytm nie odróżnił ich od siebie. Wzorzec czterech pasów nie został pokazany. Metody oparte na minimalizacji odległości wewnątrz klastra nie radzą sobie z odnajdywaniem wzorców w danych. Nie są więc dobrym narzędziem do analizy takich zbiorów.

Funkcja DBScan nie wymieszała między sobą pasów. Jest to wyraźna przewaga, wynikająca z “gęstościowego” charakteru metody. Jednak to tylko połowiczny sukces - klastry są znów za bardzo rozdrobnione, a nie które obserwacje nie zostały przypisane do żadnego z nich.

Poprawa wyników i wnioski

Dość trudno poprawić wyniki analizy w metodzie k-średnich. Po dobraniu optymalnej liczby klastrów nie ma tu już zbyt wielkiego pola do zmian. Jednak ze względu na to, że w metodzie DBScan występują dwa parametry, a ich dobór nie jest aż tak kategoryczny jak w przypadku całkowitych liczb klastrów - analizę można poprawić.

Poniżej propozycja podziału zbioru 4. Algorytm dobrze zidentyfikował trzy pasy. Niestety dwa zbiory po lewej stronie zostały ze sobą scalone. Jednak już w tej wersji jest to lepszy podział danych niż początkowy.

k4c<-dbscan(zb4, eps=0.14, minPts=15) 
b4b = list(data = zb4, cluster = k4c$cluster)
fviz_cluster(b4b, geom="point", main="Zbiór 4, DBScan, epsilon=0.14, minPts=15", xlab=FALSE, ylab=FALSE, ggtheme=theme_classic())

Oto lepsze dopasowanie wyniku klastrowania zbioru nr 3. Znów dwa zbiory zostały scalone, ale pozostałe podgrupy dostały poprawnie zidentyfikowane. Dodatkowo ukazało się kilka obserwacji odstających. Algorytm pomylił się zaledwie dla kilku punktów.

k3c<-dbscan(zb3, eps=0.092, minPts=5) 
b3b = list(data = zb3, cluster = k3c$cluster)
fviz_cluster(b3b, geom="point", main="Zbiór 3, DBScan, epsilon=0.092, minPts=5", xlab=FALSE, ylab=FALSE, ggtheme=theme_classic())

Uzyskałam takie podziały, zmieniajac wartość parametru minPts oraz dopasowując nowe wartości epsilon. Do odpowiednich wyników doprowadziły mnie kilkukrotne iteracje, podczas których modyfikowałam wartości zmiennych. Chciałam uzyskać podział danych podobny do oryginalnego. Pierwszy wynik analizy nie spełnił moich oczekiwań, więc szukałam lepszych parametrów funkcji.

Wnioski

Badanie skupień zbioru pierwszego było z góry skazane na porażkę. Nie zawierał on jednorodnych podgrup danych. Za to zbiór drugi idealnie nadawał się do wykorzystania algorytmu optymalizującego odległości punktów od siebie. Niewielką poprawę wyniku mogło przynieść wykorzystanie bardziej zaawansowanego modelu. Zbiór trzeci został dobrze podzielony za pomocą k-średnich, ale zupełnie pominięte zostały obserwacje odstające. Niemal idealne dopasowanie zapewniła metoda DBScan ze sprytnie dobranymi parametrami. W zetknięciu ze zbiorem czwartym metoda k-średnich zupełnie pominęła kwestie wzorca przestrzenengo. Ponownie, odpowiednio dobrane parametry w algorytmie DBScan doprowadziły do niemal zgodnego z rzeczywistością podziału.

Jak widać, kluczową rolę w pracy specjalisty od uczenia maszynowego pełni nie sama znajomość algorytmów, ale posiadanie odpowiedniej intuicji i oczekiwań co do wyniku analizy. Dzięki temu, dobry analityk może w odpowiedni sposób dobrać metodę i modyfikować jej parametry, by lepiej dopasować się do danych. Właśnie ten czynnik ekspercki zapewnia sukces.

To prawda, że komputer wykonuje obecnie dużą część analityki. Jednak to nie sam wynik jest ważny - bardziej istotna jest późniejsza ocena rezultatu i wyciągnięte z niej wnioski. Tych zaś nie możemy oczekiwać od maszyny. Dobry specjalista od machine learningu to ktoś, kto zna metody i potrafi zinterpretować ich wyniki. To właśnie takich osób poszukuje się obecnie na polskim rynku pracy.


  1. J. B. MacQueen “Some Methods for classification and Analysis of Multivariate Observations”, Proceedings of 5-th Berkeley Symposium on Mathematical Statistics and Probability, Berkeley, University of California Press, 1:281-297, 1967r.

  2. M. Ester, H. Kriegel, J. Sander, X. Xu „A Density-Based Algorithm for Discovering Clusters in Large Spatial Databases with Noise” KDD-96 Proceedings. (www.aaai.org). 1996r.

  3. Przykładowe kody i wyjaśnienia dotyczące pakietu: V. Melnykov, W. Chen, R. Maitra „MixSim: An R Package for Simulating Data to Study Performance of Clustering Algorithms” Journal of Statistical Software, Volume 51, Issue 12. http://www.jstatsoft.org/, 2012r.

  4. Statystyka Hopkinsa w tym przypadku w pierwszych kilku iteracjach wynosiła rzeczywiście około 0,5. Jednak podczas kolejnych przeliczeń uzyskiwałam wartości z przedziału (0,1; 0,57). Niewątpliwą słabością tej miary jest problem z powtarzalnością wyników. Niemniej, analiza wykresu pozostaje w mocy.