Границы параметров типа с объектами 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
}
}