Границы параметров типа с объектами Spark получить сложно

Я новичок в Scala,

Я пытаюсь создать объект, который принимает ProbabilisticClassifier в качестве входных данных, и это дает CrossValidator модель в качестве выхода:

import org.apache.spark.ml.classification.{ProbabilisticClassifier, ProbabilisticClassificationModel}
import org.apache.spark.ml.evaluation.BinaryClassificationEvaluator
import org.apache.spark.ml.param.ParamMap
import org.apache.spark.ml.tuning.{CrossValidator, ParamGridBuilder}

import constants.Const

object MyModels {

  def loadOrCreateModel[A, M, T](
    model: ProbabilisticClassifier[Vector[T], A, M],
    paramGrid: Array[ParamMap]): CrossValidator = {

    // Binary evaluator.
    val binEvaluator = (
      new BinaryClassificationEvaluator()
        .setLabelCol("yCol")
      )

    // Cross validator.
    val cvModel = (
      new CrossValidator()
        .setEstimator(model)
        .setEvaluator(binEvaluator)
        .setEstimatorParamMaps(paramGrid)
        .setNumFolds(3)
      )
    cvModel
  }
}

Но это дает мне:

sbt package
[info] Loading project definition from somepath/project
[info] Loading settings from build.sbt ...
[info] Set current project to xxx (in build file:somepath/)
[info] Compiling 1 Scala source to somepath/target/scala-2.11/classes ...
[error] somepath/src/main/scala/models.scala:11:12: type arguments [Vector[T],A,M] do not conform to class ProbabilisticClassifier's type parameter bounds [FeaturesType,E <: org.apache.spark.ml.classification.ProbabilisticClassifier[FeaturesType,E,M],M <: org.apache.spark.ml.classification.ProbabilisticClassificationModel[FeaturesType,M]]
[error]     model: ProbabilisticClassifier[Vector[T], A, M],
[error]            ^
[error] one error found
[error] (Compile / compileIncremental) Compilation failed
[error] Total time: 3 s, completed Mar 31, 2018 4:22:31 PM
makefile:127: recipe for target 'target/scala-2.11/classes/models/XModels.class' failed
make: *** [target/scala-2.11/classes/models/XModels.class] Error 1

Я пробовал несколько комбинаций [A, M, T] параметры, а также различные типы внутри аргументов метода.

Идея состоит в том, чтобы быть в состоянии накормить LogisticRegression или RandomForestClassifier в эту функцию. Из документации:

class LogisticRegression extends ProbabilisticClassifier[Vector, LogisticRegression, LogisticRegressionModel] with LogisticRegressionParams with DefaultParamsWritable with Logging
class RandomForestClassifier extends ProbabilisticClassifier[Vector, RandomForestClassifier, RandomForestClassificationModel] with RandomForestClassifierParams with DefaultParamsWritable

Может кто-нибудь указать мне, где я могу узнать ресурсы, необходимые для того, чтобы реализовать этот метод?

я использую Spark 2.1.0.


Изменить 01

Спасибо @ Андрей Тюкин,

Я сожалею, что код не воспроизводился. Это была на самом деле строка. Ваш код работает, но, возможно, я ошибся:

<console>:35: error: type mismatch;
found   : org.apache.spark.ml.classification.LogisticRegression
required: org.apache.spark.ml.classification.ProbabilisticClassifier[Vector[?],?,?]
    val cvModel = models.TalkingDataModels.loadOrCreateModel(logistic_regressor, paramGrid)

Так что, возможно, моя идея была неправильной с самого начала. Можно ли создать метод, который принимает как LogisticRegression или RandomForestClassifier объекты?

  • Отредактированный код для MCVE:

    import org.apache.spark.ml.classification.{ProbabilisticClassifier, ProbabilisticClassificationModel}
    import org.apache.spark.ml.evaluation.BinaryClassificationEvaluator
    import org.apache.spark.ml.param.ParamMap
    import org.apache.spark.ml.tuning.{CrossValidator, ParamGridBuilder}
    import org.apache.spark.ml.classification.LogisticRegression
    
    object MyModels {
    
    def main(array: Array[String]): Unit = {
        val logisticRegressor = (
            new LogisticRegression()
                .setFeaturesCol("yCol")
                .setLabelCol("labels")
                .setMaxIter(10)
            )
        val paramGrid = (
            new ParamGridBuilder()
            .addGrid(logisticRegressor.regParam, Array(0.01, 0.1, 1))
            .build()
        )
        loadOrCreateModel(logisticRegressor, paramGrid)
        println()
    }
    
    def loadOrCreateModel[
        F,
        M <: ProbabilisticClassificationModel[Vector[F], M],
        P <: ProbabilisticClassifier[Vector[F], P, M]
        ](
        probClassif: ProbabilisticClassifier[Vector[F], P, M],
        paramGrid: Array[ParamMap]
        ): CrossValidator = {
    
        // Binary evaluator.
        val binEvaluator =
            new BinaryClassificationEvaluator()
            .setLabelCol("y")
    
        // Cross validator.
        val cvModel =
            new CrossValidator()
            .setEstimator(probClassif)
            .setEvaluator(binEvaluator)
            .setEstimatorParamMaps(paramGrid)
            .setNumFolds(3)
    
        cvModel
        }
    }
    

1 ответ

Решение

Это здесь компилируется, но я должен был выкинуть constants.Const.yColumnи заменил его на магическое значение "y":

import org.apache.spark.ml.classification.{ProbabilisticClassifier, ProbabilisticClassificationModel}
import org.apache.spark.ml.evaluation.BinaryClassificationEvaluator
import org.apache.spark.ml.param.ParamMap
import org.apache.spark.ml.tuning.{CrossValidator, ParamGridBuilder}

object CrossValidationExample {

  def loadOrCreateModel[
    F, 
    M <: ProbabilisticClassificationModel[Vector[F], M],
    P <: ProbabilisticClassifier[Vector[F], P, M]
  ](
    probClassif: ProbabilisticClassifier[Vector[F], P, M],
    paramGrid: Array[ParamMap]
  ): CrossValidator = {

    // Binary evaluator.
    val binEvaluator = 
      new BinaryClassificationEvaluator()
      .setLabelCol("y")

    // Cross validator.
    val cvModel = 
      new CrossValidator()
      .setEstimator(probClassif)
      .setEvaluator(binEvaluator)
      .setEstimatorParamMaps(paramGrid)
      .setNumFolds(3)

    cvModel
  }
}

Перед определением списка общих параметров может быть полезно выполнить топологическую сортировку, чтобы понять, какие параметры зависят от каких других параметров.

Здесь модель зависит от типа объектов, а вероятностный классификатор зависит как от типа объектов, так и от типа модели. Таким образом, вероятно, было бы более целесообразно объявить аргументы в порядке особенностей, модели, классификатора. Тогда вы должны были получить правильный F-ограниченный полиморфизм.


Да, и, кстати, отступ в египетских скобках - ИМХО - единственный разумный способ сделать отступ для нескольких списков аргументов с аргументами типа длиной в пятьдесят миль (к сожалению, вы не можете изменить длину параметров типа, они имеют тенденцию быть довольно длинным в каждой библиотеке машинного обучения, которую я видел).


РЕДАКТИРОВАТЬ (ответ для второй части MCVE)

Это довольно простое обобщение. Если захочет linalg.Vector вместо Vector[Feature]тогда просто абстрагируйся над этим тоже:

import org.apache.spark.ml.classification.{ProbabilisticClassifier, ProbabilisticClassificationModel}
import org.apache.spark.ml.evaluation.BinaryClassificationEvaluator
import org.apache.spark.ml.param.ParamMap
import org.apache.spark.ml.tuning.{CrossValidator, ParamGridBuilder}
import org.apache.spark.ml.classification.{LogisticRegression, LogisticRegressionModel}
import org.apache.spark.ml.classification.RandomForestClassifier
import org.apache.spark.ml.linalg.{Vector => LinalgVector}

object CrossValidationExample {

  def main(array: Array[String]): Unit = {
      val logisticRegressor = (
          new LogisticRegression()
              .setFeaturesCol("yCol")
              .setLabelCol("labels")
              .setMaxIter(10)
          )
      val paramGrid = (
          new ParamGridBuilder()
          .addGrid(logisticRegressor.regParam, Array(0.01, 0.1, 1))
          .build()
      )

      loadOrCreateModel(logisticRegressor, paramGrid)

      val rfc: RandomForestClassifier = ???
      loadOrCreateModel(rfc, paramGrid)
  }

  def loadOrCreateModel[
    FeatVec,
    M <: ProbabilisticClassificationModel[FeatVec, M],
    P <: ProbabilisticClassifier[FeatVec, P, M]
  ](
    probClassif: ProbabilisticClassifier[FeatVec, P, M],
    paramGrid: Array[ParamMap]
  ): CrossValidator = {
    // Binary evaluator.
    val binEvaluator =
        new BinaryClassificationEvaluator()
        .setLabelCol("y")

    // Cross validator.
    val cvModel =
        new CrossValidator()
        .setEstimator(probClassif)
        .setEvaluator(binEvaluator)
        .setEstimatorParamMaps(paramGrid)
        .setNumFolds(3)

    cvModel
  }
}
Другие вопросы по тегам