Anasayfa / Büyük Veri / Apache Spark K-Ortalamalar Tekniği ile Bilgisayar Ağlarında Anormallik Tespiti Bölüm 2/3

Apache Spark K-Ortalamalar Tekniği ile Bilgisayar Ağlarında Anormallik Tespiti Bölüm 2/3

Merhabalar Apache Spark ve K-Ortalamalar tekniğini kullanarak bilgisayar ağlarında anormallik tespiti yazısına devam ediyoruz. İlk yazımız buradadır. İlk yazıda K-Ortalamalar tekniğine genel bir giriş yaptık, veriyi yükledik, veri keşfi ve temizlik yaptık. Bu yazımızda veri ön işlemesi ile devam edeceğiz.

Kategorik Nitelikleri Dönüştürmek

Apache Spark K-Ortalamalar algoritması, girdi olarak nümerik değerler istediği için kategorik niteliklerimizi nümerik yapmamız gerekiyor. Bunun için Spark ML kütüphanesini kullanacağız ve işlem sıralamamız da şu şekilde olacak; StringIndexer ile string değerleri rakamlarla eşleştireceğiz. Sonra oneHotEncoder ile vector haline getireceğiz. Son olarak VectorAssembler ile hepsini birleştireceğiz. Ürettiğimiz en son sütun vector türünden olacak.

Kategorik niteliklerimiz üzerinde dönüşüm gerçekleştiren ve sonuç olarak Pipeline nesnesi ve oneHotEncoder sütun isminden oluşan bir tuple döndüren fonksiyon aşağıdadır:

import org.apache.spark.ml.feature.{OneHotEncoder, StringIndexer}
import org.apache.spark.ml.{Pipeline}
def oneHotPipeline(inputCol: String): (Pipeline, String) = {
    // Bir kategorik değişken için girdi ve çıktı sütun ismi belirle 
    val indexer = new StringIndexer().
      setInputCol(inputCol).
      setOutputCol(inputCol + "_indexed")

    // indexer nesnesiyle oluşacak yeni sütunu giridi olarak belirle çıktı sütunu da vector olacak
    val encoder = new OneHotEncoder().
      setInputCol(inputCol + "_indexed").
      setOutputCol(inputCol + "_one_hot")
    val pipeline = new Pipeline().setStages(Array(indexer, encoder))
    (pipeline, inputCol + "_one_hot")
  }

Şimdi yukarıdaki fonksiyonu içinde kullanacak, K-Ortalamalar modeli oluşturacak ve hepsini bir pipeline içinde kullanacak esas fonksiyonu yazalım.

import org.apache.spark.ml.clustering.{KMeans, KMeansModel}
import org.apache.spark.ml.feature.{VectorAssembler, StandardScaler}

def clusteringScore(df:DataFrame, k:Int):Double = {
    
    // Yukarıda yazdığımız oneHotPipeline() fonksiyonu ile her bir kategorik nitelik için bir pipeline nesnesini ve ilgili
    // sütun ismini alıp bir değişkende tutalım. Her kategorik nitelik için ayrı ayrı yapıyoruz.
    val (protocolTypePipeline, protocol_type_one_hot) = oneHotPipeline("protocol_type")
    val (servicePipeline, service_one_hot) = oneHotPipeline("service")
    val (flagPipeline, flag_one_hot) = oneHotPipeline("flag")
    
    // Model için gerekli nitelikleri seçmek:
    // Orijinal dataframe'den kategorik nitelikleri ve hedef değişken olan label sütununu çıkarıp 
    // yeni oluşturduğumuz vector türündeki isimleri ekliyoruz.
    // Buradaki hazırlığın amacı vector assembler için vereceğimiz sütun isimlerini bir arada toplamaktır.
    val gatherColumns = Set(df.columns: _*) --
      Seq("label", "protocol_type", "service", "flag") ++
      Seq(protocol_type_one_hot, service_one_hot, flag_one_hot)
      
    // Yukarıda vector cinsinde tek bir sütuna dönüşecek nitelik isim listesini girdi olarak veriyor, 
    // çıktı sütun ismi olarak featureVector istiyoruz.
    val vectorAssembler = new VectorAssembler().
      setInputCols(gatherColumns.toArray).
      setOutputCol("featureVector")
     
    // Oluşturduğumuz tek sütunlu featureVector içindeki her şeyi standartScaler ile normalleştiriyoruz. 
    val standartScaler = new StandardScaler()
      .setInputCol("featureVector")
      .setOutputCol("scaledFeatureVector")
      .setWithStd(true)
      .setWithMean(false)
      
    // Model için ilk adımı atıyor ve KMeans nesnesi oluşturup bazı özelliklerine değer ataması yapıyoruz.
    // girdi olarak kullanacağı scaledFeatureVector sütununa dikkat.
    val kmeansObject = new KMeans().
      setSeed(Random.nextLong()).
      setK(k).
      setPredictionCol("cluster").
      setFeaturesCol("scaledFeatureVector").
      setMaxIter(40).
      setTol(1.0e-5)

    // Yukarıda oluşturduğumuz tüm nesneleri burada Pipeline içine yerleştiriyoruz.
    // Bunun için tren vagonları analojisini kullanabiliriz. Yukarıda vagonları oluşturduk burada lokomotife sırasıyla takıyoruz.
    val pipeline = new Pipeline().setStages(
      Array(protocolTypePipeline, servicePipeline, flagPipeline, vectorAssembler, standartScaler, kmeansObject))
    
    // Pipeline oluşunca (tren katarı) onu eğitiyoruz. Hep ML modeli eğitmeye alışık olanlar bunu yadırgayabilirler.
    val pipelineModel = pipeline.fit(data)
    
    // Tren katarından son vagon olan KMeans modelini kullanarak maliyet hesaplayacağız.
    val kmeansModel = pipelineModel.stages.last.asInstanceOf[KMeansModel]
    
    // Fonksiyon bir Double döndürecekti. Burada retilecek rakam fonksiyon çağrılan yere Double değeri olarak dönecektir.
    kmeansModel.computeCost(pipelineModel.transform(data)) / data.count()
}

Şimdi yukarıda oluşturduğumuz esas fonksiyonu herhangi bir k değeri için çalıştıralım. Önce bakalım çalışıyor mu? Sonra da hızlı çalışıp çalışmadığını kontrol edelim.

println(clusteringScore(data, 80))
// Çıktı
21.582148449449406

Optimal küme sayısını belirlemek için kullanılan yöntemlerden birisi de dirsek (elbow method) metodudur. Bu metoda göre küme sayısı y ekseni, kümeler içi hataların kareler toplamı (WCSS) da x ekseninde olacak şekilde çizgi grafiği çizilir. Grafikte hata skoru hızla düşerken belirli bir küme sayısından sonra düşüş hızı yavaşlar ve eğimi azalır. İşte bu noktada bir dirsek oluşur. Dirseğin oluştuğu noktanın x eksenindeki karşılığı küme sayısını verecektir. Yukarıda ben bir k değeri için modeli çalıştırmıştım. Şimdi 10 ile 200 arasında 20’şer artacak şekilde k değerini belirleyecğim ve modeli 10 defa çalıştıracağım. Elde edeceğim küme sayısı ve skorları ile excelde grafik çizeceğim. Önce modeli farklı k değerleri ile 10 tekrar çalıştıralım. Bir hatırlatma: Bu hesaplama çok uzun sürebilir, bende yaklaşık beş saat sürdü.

(10 to 200 by 20).map(k => (k, clusteringScore(data, k))).foreach(println)
//Çıktı
(10,91.73509176364342)
(30,68.09714906525983)
(50,48.11428790459002)
(70,30.930791633655996)
(90,18.93826443365861)
(110,4.881485697122051)
(130,2.874150520909986)
(150,2.2586170815547404)
(170,1.8358218724246986)
(190,1.5962728574833083)

Çıktıyı excele aktarıp bazı düzenlemelerden sonra grafiğini çizdirelim.


Gördüğümüz gibi 110 civarında birden eğim azalıyor. Küme sayısı olarak k parametresini 110 vererek tekrar çalıştıralım.

println(clusteringScore(data, 110))
// Çıktı
4.541505773938476

Peki bu 110 ne anlama geliyor? Tüm nitelikleri kullanarak veriyi en optimum küme sayısı dediğimiz 110 kümeye ayırabiliyoruz. Peki bunlardan hangisi veya hangileri bilgisayar ağında anormallik gösteren grup? Üçüncü yazı ile devam edeceğiz.

Hakkında Erkan ŞİRİN

2014'ten beri hem akademik alanda hem de sektörde pratik anlamda büyük veri ve veri bilimi ile ilgili çalışmalar yürütmektedir. Halihazırda İmpektra Bilişim A.Ş.'de büyük veri yöneticisi olarak çalışmakta olup aynı zamanda Gazi Üniversitesi Yönetim Bilişim Sistemleri doktora öğrencisidir. Büyük veri ve veri bilimi ile ilgili birçok kurum ve şirkete eğitimler vermekte ve projeler icra etmektedir. Çalışma alanları: büyük veri platformlarının kurulum ve yönetimi, büyük veri üzerinde makine öğrenmesi, olağan dışılık tespiti, sahtecilik tespiti, veri hazırlama sürecidir.

GÖZ ATMAK İSTEYEBİLİRSİNİZ

Makine Öğrenmesine Çok Değişkenli İstatistiksel Yaklaşımlar: Temel Bileşenler Analizi

Temel Bileşenler Analizi nedir? Nasıl hesaplanır? Faydaları nelerdir? Nasıl uygulanır? Bu soruların cevabı için doğru yerdesiniz.

Bir Yorum

  1. Merhaba hocam. 3. Yazıyı ben göremedim acaba devamı gelecek mi?

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir