본문 바로가기
  • Let's go grab a data
Data/SPARK

7. 머신러닝 kmeans 알고리즘

by pub-lican-ai 2016. 2. 18.
반응형

7. 머신러닝 kmeans 알고리즘 #mllib #kmeans #k평균 알고리즘 #클러스터링 #clustering


[개념]

k-means 알고리즘

기본 개념은 본 블로그 Lecture_R 에서 언급한바 있다. 아래 링크에서 내용을 확인하기 바란다.

http://pubdata.tistory.com/26

이번 글에서는 동일한 데이터를 가지고 동일하게 kmeans 알고리즘을 돌려보고, 결과가 같은지 확인 후

시사점을 작성해보려고 한다


Within group sum of squares


R에서 스크립트로 withinss 값을 찾아 그래프를 그려보았고 

적정한 k값을 찾는 방법으로 각 k값에 따라 변하는 withinss값의 기울기가 완만해지는 지점을 Elbow point라고 했었다

좀 더 설명을 붙이자면 

kmeans에 의해 구분되어지는 각 클러스터 데이터의 평균값으로 부터 편차 또는 변동의 측정치를 말한다.

쉽게 말해 각 클러스터 데이터의 평균값으로 부터 멀어지는 정도, 분산의 크기라고 할 수 있다.

kmenas로 나눈 각 클러스터 데이터의 분산이 클 수록 즉, withinss값이 클수록  클러스터링의 정확도는 떨어질 수 있다.

하지만 반대로 withinss값이 작다고 정확도가 높다고 말할 수는 없다.

k가 클수록 withinss값이 작아지지만 그 만큼 클러스터 조각도 작아지는 이유도 포함하고 있어서

Elbow Method의 기울기가 완만해지는 점을 적정한 k로 선택한다.


k가 클경우 아래와 같이 이상치outlier 들에 의해 좋지 못한 결과를 보일 수 있다.


k=8


[데이터]

Lecture_R에서 진행했던 iris 데이터를 잠시 이용해보자

첨부파일 확인 kmeans_iris.txt


[코드]

scala> import org.apache.spark.mllib.clustering.KMeans

scala> import org.apache.spark.mllib.linalg.Vectors

 

데이터를 읽어옵니다.

scala> val data_iris = sc.textFile("spark/data/mllib/kmeans_iris.txt")

data_iris: org.apache.spark.rdd.RDD[String] = MapPartitionsRDD[414] at textFile at <console>:29

 

데이터를 각 라인별로 읽어서 공백으로 각 데이터를 나누고 1번째 데이터에서 부터 4번째 데이터까지 잘라 Double형태로 만들어 줍니다.

0번째 데이터는 LabeledVector로 종속변수에 해당하니 제외하고, 마지막 열 이후에 개행이 되어 있으면 Empty로 에러가 나니 주의하세요.

scala> val parsedData_iris = data_iris.map(s=>Vectors.dense(s.split(' ').slice(1,5).map(_.toDouble)))

parsedData_iris: org.apache.spark.rdd.RDD[org.apache.spark.mllib.linalg.Vector] = MapPartitionsRDD[415] at map at <console>:31

 

k는 iris데이터가 3종인 것을 우리는 알고 있으니 일단 3으로 합니다. R에서 진행한 데이터와의 비교를 위해

scala> val numClusters = 3

numClusters: Int = 3

 

반복은 20번

scala> val numIterations = 20

numIterations: Int = 20

 

전체 데이터를 KMeansModel로 만드는데 사용합니다

scala> val model = KMeans.train(parsedData_iris,numClusters,numIterations)

model: org.apache.spark.mllib.clustering.KMeansModel = org.apache.spark.mllib.clustering.KMeansModel@1091b3ba

 

모델의 각 클러스터 중심점을 찍어봅니다

scala> model.clusterCenters.foreach(println)

[5.901612903225807,2.748387096774194,4.393548387096774,1.4338709677419355]

[6.85,3.0736842105263147,5.742105263157893,2.071052631578947]

[5.005999999999999,3.428000000000001,1.4620000000000002,0.2459999999999999]

 

KmeansModel이 가지고 있는 메서드를 확인

scala> model.

asInstanceOf     clusterCenters   computeCost      isInstanceOf     k                predict          save             toPMML           toString         


scala> model.k

res37: Int = 3

 

Within group sum of squares를 계산해줍니다. R에서 계산한 각 클러스터의 within 합과 동일합니다.

R 결과 -> Within cluster sum of squares by cluster: [1] 39.82097 23.87947 15.15100

scala> model.computeCost(parsedData_iris)

res38: Double = 78.85144142614668

 

학습에 사용된 데이터를 다시 모델에 넣어서 얼마나 모델이 정확한지 보겠습니다.

읽어온 데이터의 0번째 행(iris의 종)과 예측결과를 index로 나타낸 결과입니다.

scala> val speciesByCluster = data_iris.map(row=>(row.split(' ').slice(0,1).head,model.predict(Vectors.dense(row.split(' ').slice(1,5).map(_.toDouble)))))

speciesByCluster: org.apache.spark.rdd.RDD[(String, Int)] = MapPartitionsRDD[448] at map at <console>:39


scala> speciesByCluster.foreach(println)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(setosa,2)

(versicolor,0)

(versicolor,0)

(versicolor,1)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,1)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(versicolor,0)

(virginica,1)

(virginica,0)

(virginica,1)

(virginica,1)

(virginica,1)

(virginica,1)

(virginica,0)

(virginica,1)

(virginica,1)

(virginica,1)

(virginica,1)

(virginica,1)

(virginica,1)

(virginica,0)

(virginica,0)

(virginica,1)

(virginica,1)

(virginica,1)

(virginica,1)

(virginica,0)

(virginica,1)

(virginica,0)

(virginica,1)

(virginica,0)

(virginica,1)

(virginica,1)

(virginica,0)

(virginica,0)

(virginica,1)

(virginica,1)

(virginica,1)

(virginica,1)

(virginica,1)

(virginica,0)

(virginica,1)

(virginica,1)

(virginica,1)

(virginica,1)

(virginica,0)

(virginica,1)

(virginica,1)

(virginica,1)

(virginica,0)

(virginica,1)

(virginica,1)

(virginica,1)

(virginica,0)

(virginica,1)

(virginica,1)

(virginica,0)



아래 결과는 Lecture_R 에서 R의 kmeans를 이용해 예측한 결과이다. 

위 결과와 비교해보면 동일하게 나오는 모습을 보이지만, 달라질 수도 있다.

왜냐하면 kmeans알고리즘의 Center point를 초기에 랜덤하게 찍고, 반복 횟수도 다를 수 있기 때문이다.


# iris데이터의 종과 구별해낸 cluster를 비교해 얼마나 맞았는지 파악

table(iris$Species,kc$cluster)

                  1  2  3

  setosa      0  0 50

  versicolor 48  2  0

  virginica   14 36  0

 

모델의 정확도 검증방법에 대해서는 아래링크 참고하세요

http://pubdata.tistory.com/19

[시사점]

Spark에서 kmeans 알고리즘을 이용해서 '실시간'으로 Clustering을 해야하는 일이 얼마나 있을까?

Batch형태로 R이나 다른 툴을 이용하여 어느 정도 시간을 두고 돌려서 나오는 클러스터 집합을 가지고도 충분히 업무에 적용 가능할 법하다. 다만 clustering의 Input data가 시간에 따라 추세가 있거나 패턴이 변화하는 형태라면 window sliding 형태로 Spark에서 개발이 가능하다. 


참고 : 





반응형