Apache Spark ile Artık İdeal Küme Sayısını Bulmak Daha Kolay

Bildiğimiz gibi makine öğrenmesinde öğrenme yöntemleri genel olarak denetimli (supervised) ve denetimsiz (unsupervised) şeklinde ikiye ayrılıyor. Denetimli yöntemlerde sınıflandırma ağırlık kazanırken denetimsiz yöntemlerde ise kümeleme öne çıkmaktadır. Sınıflandırmada veri içinde etiketlenmiş bir hedef değişken bulunurken kümelemede ise herhangi bir hedef  değişken bulunmaz. Bu nedenle kümeleme daha çok veri içindeki nesnelerin doğal gruplanmalarını ortaya çıkarmaya yöneliktir.

Sınıflandırmaya göre biraz daha zorlu, sonuçları yorumlamaya muhtaç, daha fazla alan bilgisi ve uzmanlık gerektiren kümeleme yöntemleri içinde K-ortalamalar (KMeans) tekniği yaygın olarak kullanılmaktadır. Bu teknik girdi olarak veri seti ile birlikte kullanıcıdan küme sayısı olan k değerini istemektedir. İdeal k sayısını belirlemek çok kolay olmasa da bunun için bazı yöntemler geliştirilmiştir. Spark ML’ye göre daha zengin kütüphanelerden olan Scikit-learn’de k sayısı belirlemek için bazı metrikler (sklearn.metrics.silhouette_score) bulunuyor. Bunlardan birisi de silhouette analizidir. K-ortalamalar tekniğinde ideal k sayısı bu analizle belirlenebilmektedir. Apache Spark ise 2.3.0 sürümüne kadar kümeleme değerlendirme metriği sunmuyordu. 2.3.0 ile beraber ML kütüphanesine ClusteringEvaluator sınıfı eklendi. Bu sınıftan yarattığımız nesne ile artık ideal küme sayısının ne olması gerektiğine dair bir fikir edinebiliyoruz. Bunu bu sınıftan oluşturduğumuz nesnenin evaluate metoduyla yapıyoruz. Bu metod bize bir ondalıklı sayı döndürüyor. Bu sayı [-1,1] arasında değer alıyor. Değerin yüksek olması kümelerin birbirinden daha iyi ayrıştığına işaret ediyor. Bu yazımızda iris veri seti üzerinde basit bir uygulama yapacağız. Bakalım iris verisinde optimal küme sayısı ne çıkacak?

Aşağıdaki kodları: Windows 10 üzerine kurulu Spark 2.3.1, IntelliJ Idea Community Edition ve Scala kullanarak yazdım ve çalıştırdım.

Kodları Veri Bilimi Okulu GitHub deposunda da bulabilirsiniz.

Gerekli kütüphaneleri indirme:

import org.apache.spark.ml.{PipelineModel, Pipeline}
import org.apache.spark.ml.clustering.{KMeans, KMeansModel}
import org.apache.spark.ml.feature.{VectorAssembler, StandardScaler}
import org.apache.spark.ml.evaluation.ClusteringEvaluator
import org.apache.spark.sql.{DataFrame, SparkSession}

Spark session yaratma:

val spark = SparkSession.builder
      .master("local[*]")
      .appName("FindClusterNumber")
      .getOrCreate()

Veri setini yükleme:

val data = spark.read.format("csv").option("inferSchema","true").option("header","true")
      .load("C:\\Users\\toshiba\\SkyDrive\\veribilimi.co\\Datasets\\iris.csv")

Veriye bir göz atalım:

 data.show()
+-------------+------------+-------------+------------+-----------+
|SepalLengthCm|SepalWidthCm|PetalLengthCm|PetalWidthCm|    Species|
+-------------+------------+-------------+------------+-----------+
|          5.1|         3.5|          1.4|         0.2|Iris-setosa|
|          4.9|         3.0|          1.4|         0.2|Iris-setosa|
|          4.7|         3.2|          1.3|         0.2|Iris-setosa|
|          4.6|         3.1|          1.5|         0.2|Iris-setosa|
|          5.0|         3.6|          1.4|         0.2|Iris-setosa|
|          5.4|         3.9|          1.7|         0.4|Iris-setosa|
|          4.6|         3.4|          1.4|         0.3|Iris-setosa|
|          5.0|         3.4|          1.5|         0.2|Iris-setosa|
|          4.4|         2.9|          1.4|         0.2|Iris-setosa|
|          4.9|         3.1|          1.5|         0.1|Iris-setosa|
|          5.4|         3.7|          1.5|         0.2|Iris-setosa|
|          4.8|         3.4|          1.6|         0.2|Iris-setosa|
|          4.8|         3.0|          1.4|         0.1|Iris-setosa|
|          4.3|         3.0|          1.1|         0.1|Iris-setosa|
|          5.8|         4.0|          1.2|         0.2|Iris-setosa|
|          5.7|         4.4|          1.5|         0.4|Iris-setosa|
|          5.4|         3.9|          1.3|         0.4|Iris-setosa|
|          5.1|         3.5|          1.4|         0.3|Iris-setosa|
|          5.7|         3.8|          1.7|         0.3|Iris-setosa|
|          5.1|         3.8|          1.5|         0.3|Iris-setosa|
+-------------+------------+-------------+------------+-----------+

KMeans modeli oluşturan bir fonksiyon yazalım. Bu fonksiyon bize bir PipelineModel döndürsün.

 def ComputeKMeansModel(df:DataFrame, k:Int): PipelineModel = {

     // Pick up input columns
      val inputColumns = df.columns.filter(_!="Species")

      val vectorAssembler = new VectorAssembler().
        setInputCols(inputColumns).
        setOutputCol("featureVector")

      val standartScaler = new StandardScaler()
        .setInputCol("featureVector")
        .setOutputCol("scaledFeatureVector")
        .setWithStd(true)
        .setWithMean(false)

      val kmeansObject = new KMeans().
        setSeed(142).
        setK(k).
        setPredictionCol("cluster").
        setFeaturesCol("scaledFeatureVector").
        setMaxIter(40).
        setTol(1.0e-5)

      val pipeline = new Pipeline().setStages(
        Array(vectorAssembler, standartScaler, kmeansObject))
      pipeline.fit(df)

    }

Optimal k sayısı için evaluator nesnesi yaratalım ve bir for döngüsü ile evaluator score hesaplayalım. Bu arada kmeans computecost değerini de hesaplamış olalım.

 val evaluator = new ClusteringEvaluator()
      .setFeaturesCol("scaledFeatureVector")
      .setPredictionCol("cluster")
      .setMetricName("silhouette")

    for(k <- 2 to 7 by 1){
      val pipelineModel =  ComputeKMeansModel(data,k)

      val transformedDF = pipelineModel.transform(data)

      val score = evaluator.evaluate(transformedDF)
      val kmeansModel = pipelineModel.stages.last.asInstanceOf[KMeansModel]
      println("k value:"+k, "Score: "+score, "KMeans ComputeCost: "+ kmeansModel.computeCost(transformedDF))
    }

Sonuç:

(k value:2,Score: 0.7714149126311811,KMeans ComputeCost: 222.24045903184924)
(k value:3,Score: 0.6797395814522242,KMeans ComputeCost: 196.38339394892998)
(k value:4,Score: 0.6156585151435247,KMeans ComputeCost: 128.02116415016857)
(k value:5,Score: 0.5326790461597242,KMeans ComputeCost: 91.08870518145912)
(k value:6,Score: 0.5740571668931305,KMeans ComputeCost: 95.73796235425911)
(k value:7,Score: 0.5678099400784106,KMeans ComputeCost: 87.25990479785739)

Yorum: Bildiğimiz gibi iris çiçeği üç farklı cinsten oluşuyordu. Yukarıda 2’yi saymaz isek en yüksek score değeri k=3 için alınmış. Bu değerlere göre optimal küme sayısı için k değerini 3 seçmek gerekiyor. 2’yi es geçmemizin sebebi bu analizin 2 ve 4 sayıları için kararsızlık eğiliminde olmasıdır[1]. Ayrıca ComputeCost değeri de en büyük sıçramayı 3’üncü kümeden sonra yapmaktadır. Yani dirsek metodunu da kullansak 3’ü seçmemiz isabet olacaktı.

[1] http://scikit-learn.org/stable/auto_examples/cluster/plot_kmeans_silhouette_analysis.html

Yazar Hakkında
Toplam 180 yazı
Erkan ŞİRİN
Erkan ŞİRİN
10 yılı aşkın süredir yurtiçi ve yurtdışında sektörde büyük veri mühendisliği, platform yönetimi ve makine öğrenmesi ile ilgili çalışmalar yürütmekte ve aynı zamanda birçok kurum ve şirkete danışmanlık ve eğitimler vermektedir. Çalışma alanları: Data ve MLOps platformları, gerçek zamanlı veri işleme, değişen veriyi yakalama (CDC) ve Lakehouse.
Yorumlar (Yorum yapılmamış)

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

×

Bir Şeyler Ara