Elki GDBSCAN Java/Scala — как изменить CorePredicate

Как реализован обобщенный dbscan (gdbscan) в elki на Java/Scala? В настоящее время я пытаюсь найти эффективный способ реализации взвешенного dbscan на elki, чтобы компенсировать неэффективность, связанную с sklearn-реализацией взвешенного dbscan.

Причина, по которой я делаю это в данный момент, заключается в том, что sklearn просто отстой для реализации dbscan на кластерах в наборах данных в масштабе терабайта (в облаке, которым в данном случае я являюсь).

Например, я сделал следующий код с функцией создания базы данных и функцией dbscan, которая считывает массив массивов и выдает индексы кластерных индексов.

/* Libraries imported from the ELKI library - https://elki-project.github.io/releases/current/doc/overview-summary.html */
import de.lmu.ifi.dbs.elki.algorithm.clustering.kmeans.KMeansElkan 
import de.lmu.ifi.dbs.elki.data.model.{ClusterModel, DimensionModel, KMeansModel, Model} 
import de.lmu.ifi.dbs.elki.data.model
import de.lmu.ifi.dbs.elki.data.{Clustering, DoubleVector, NumberVector}
import de.lmu.ifi.dbs.elki.database.{Database, StaticArrayDatabase}
import de.lmu.ifi.dbs.elki.datasource.ArrayAdapterDatabaseConnection
import de.lmu.ifi.dbs.elki.distance.distancefunction.minkowski.SquaredEuclideanDistanceFunction
import de.lmu.ifi.dbs.elki.distance.distancefunction.minkowski.EuclideanDistanceFunction
import de.lmu.ifi.dbs.elki.distance.distancefunction.NumberVectorDistanceFunction
import de.lmu.ifi.dbs.elki.algorithm.clustering.DBSCAN

// Imports for generalized DBSCAN
import de.lmu.ifi.dbs.elki.algorithm.clustering.gdbscan // Generalized dbscan function here required for weighted dbscan
import de.lmu.ifi.dbs.elki.algorithm.clustering.gdbscan.CorePredicate // THIS IS IMPORTANT TO GET GENERALIZED DBSCAN
import de.lmu.ifi.dbs.elki.algorithm.clustering.gdbscan.GeneralizedDBSCAN
import de.lmu.ifi.dbs.elki.utilities.ELKIBuilder

import de.lmu.ifi.dbs.elki.database.relation.Relation
import de.lmu.ifi.dbs.elki.datasource.DatabaseConnection
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter
import de.lmu.ifi.dbs.elki.index.tree.metrical.covertree.SimplifiedCoverTree
import de.lmu.ifi.dbs.elki.data.{`type`=>TYPE} // Need to import in this way as 'type' is a class method in Scala
import de.lmu.ifi.dbs.elki.index.tree.spatial.rstarvariants.rstar.RStarTreeFactory // Important

def createDatabaseWeighted(data: Array[Array[Double]], distanceFunction: NumberVectorDistanceFunction[NumberVector]): Database = {
  val indexFactory = new SimplifiedCoverTree.Factory[NumberVector](distanceFunction, 0, 30)
  // Create a database
  val db = new StaticArrayDatabase(new ArrayAdapterDatabaseConnection(data), java.util.Arrays.asList(indexFactory))
  // Load the data into the database
  val CustomPredicate = CorePredicate
  db
}

def dbscanClusteringOriginalTest(data: Array[Array[Double]], distanceFunction: NumberVectorDistanceFunction[NumberVector] = SquaredEuclideanDistanceFunction.STATIC, epsilon: Double = 10, minpts: Int = 10) = {
  // Use the same `distanceFunction` for the database and DBSCAN <- is it required??
  val db = createDatabaseWeighted(data, distanceFunction)
  val rel = db.getRelation(TYPE.TypeUtil.NUMBER_VECTOR_FIELD) // Create the required relational database
  val dbscan = new DBSCAN[DoubleVector](distanceFunction, epsilon, minpts) // Epsilon and minpoints needed - either you define in the function input, or will use default values
  val result: Clustering[Model] = dbscan.run(db)
  var ClusterCounter = 0 // Indexing the number of datapoints allocated from DBSCAN

  result.getAllClusters.asScala.zipWithIndex.foreach { case (cluster, idx) =>
    println("The type is " + cluster.getNameAutomatic)
    /* Isolate only the clusters and store the median from the DBSCAN results */
    if (cluster.getNameAutomatic == "Cluster" || cluster.getNameAutomatic == "Noise") {
      ClusterCounter += 1
      val ArrayMedian =  Array[Double]()
      println(s"# $idx: ${cluster.getNameAutomatic}")
      println(s"Size: ${cluster.size()}")
      println(s"Model: ${cluster.getModel}")
      println(s"ids: ${cluster.getIDs.iter().toString}")
    }
  }
}

Я могу заставить это работать довольно эффективно, но в настоящее время я изо всех сил пытаюсь получить аналогичный эффект с помощью функции gdbscan. Например, был ответ, в котором предполагалось, что это можно сделать, изменив CorePredicate на ELKI (sample_weight в реализации ELKI DBSCAN), но я не уверен, как это можно реализовать.

Любые указатели будут высоко оценены!


person sang young noh    schedule 30.11.2020    source источник
comment
вот исходный код elkis   -  person Nikolai Dmitriev    schedule 30.11.2020


Ответы (1)


Реализуйте свой собственный основной предикат GDBSCAN.

Вместо подсчета соседей, как в стандартной реализации, добавьте их веса.

Затем вы взвешиваете DBSCAN.

person Erich Schubert    schedule 02.12.2020
comment
Привет. Спасибо за ваш ответ. Я просто смотрю на функцию «GeneralizedDBSCAN.java» в пакете и думаю, что реализация, которую необходимо изменить, находится в методе processCorePoint, в частности, в части clustersize++. Здесь мне нужно перейти от простого подсчета индексов к подсчету весов, правильно ли я предполагаю? Заранее спасибо. - person sang young noh; 04.12.2020
comment
Нет, вам нужно реализовать свой собственный основной предикат. Вот почему это интерфейс: так что вы можете легко предоставить свою собственную реализацию. Упомянутый вами класс не знает, что такое параметр minpts. Размер кластера предназначен только для оптимизации использования памяти в выходных данных. - person Erich Schubert; 08.12.2020
comment
Я понимаю. Я добавил еще один вопрос с изменениями в коде для WeightedCorePredicate - stackoverflow.com/questions/65204069/ - буду очень признателен за отзыв, если у вас есть время. Спасибо. - person sang young noh; 09.12.2020