Преобразование модели Scala Spark возвращает все нули

Доброго времени суток всем. Для начала я выполняю простую задачу машинного обучения с apache-spark ml (не mllib) и scala. Мой build.sbt выглядит следующим образом:

name := "spark"
version := "1.0"
scalaVersion := "2.11.11"
libraryDependencies += "org.apache.spark" %% "spark-core"  % "2.1.1"
libraryDependencies += "org.apache.spark" %% "spark-mllib" % "2.1.1"
libraryDependencies += "com.crealytics" %% "spark-excel" % "0.8.2"
libraryDependencies += "com.databricks" %% "spark-csv" % "1.0.1"

На всех этапах все в порядке. Но есть проблема с набором данных, который должен содержать прогнозы. В моем случае я делаю классификацию по трем классам, метки 1.0, 2.0, 3.0, но столбец прогноза содержит только 0.0 меток, хотя такой метки нет вообще. Вот исходный фрейм данных:

+--------------------+--------+
|               tfIdf|estimate|
+--------------------+--------+
|(3000,[0,1,8,14,1...|     3.0|
|(3000,[0,1707,223...|     3.0|
|(3000,[1,24,33,64...|     3.0|
|(3000,[1,40,114,5...|     2.0|
|(3000,[1,363,743,...|     2.0|
|(3000,[2,20,65,88...|     3.0|
|(3000,[3,15,21,23...|     3.0|
|(3000,[3,45,53,14...|     3.0|
|(3000,[3,387,433,...|     1.0|
|(3000,[3,523,629,...|     3.0|
+--------------------+--------+

И после классификации мои прогнозы таковы:

+--------------------+--------+----------+
|               tfIdf|estimate|prediction|
+--------------------+--------+----------+
|(3000,[0,1,8,14,1...|     3.0|       0.0|
|(3000,[0,1707,223...|     3.0|       0.0|
|(3000,[1,24,33,64...|     3.0|       0.0|
|(3000,[1,40,114,5...|     2.0|       0.0|
|(3000,[1,363,743,...|     2.0|       0.0|
|(3000,[2,20,65,88...|     3.0|       0.0|
|(3000,[3,15,21,23...|     3.0|       0.0|
|(3000,[3,45,53,14...|     3.0|       0.0|
|(3000,[3,387,433,...|     1.0|       0.0|
|(3000,[3,523,629,...|     3.0|       0.0|
+--------------------+--------+----------+

И мой код выглядит следующим образом:

 val toDouble = udf[Double, String](_.toDouble)
  val kribrumData = krData.withColumn("estimate", toDouble(krData("estimate")))
    .select($"text",$"estimate")

  kribrumData.cache()

  val tokenizer = new Tokenizer()
    .setInputCol("text")
    .setOutputCol("tokens")
  val stopWordsRemover = new StopWordsRemover()
    .setInputCol("tokens")
    .setOutputCol("filtered")
    .setStopWords(STOP_WORDS)
  val hashingTF = new HashingTF()
    .setInputCol("filtered")
    .setNumFeatures(3000)
    .setOutputCol("tf")
  val idf = new IDF()
    .setInputCol("tf")
    .setOutputCol("tfIdf")
  val preprocessor = new Pipeline()
    .setStages(Array(tokenizer,stopWordsRemover,hashingTF,idf))
  val preprocessor_model = preprocessor.fit(kribrumData)

  val preprocessedKribrumData = preprocessor_model.transform(kribrumData)
    .select("tfIdf", "estimate")

  var Array(train, test) = preprocessedKribrumData.randomSplit(Array(0.8, 0.2), seed = 7)

  test.show(10)

  val logisticRegressor = new LogisticRegression()
    .setMaxIter(10)
    .setRegParam(0.3)
    .setElasticNetParam(0.8)
    .setLabelCol("estimate")
    .setFeaturesCol("tfIdf")
  val classifier = new OneVsRest()
    .setLabelCol("estimate")
    .setFeaturesCol("tfIdf")
    .setClassifier(logisticRegressor)


  val model = classifier.fit(train)

  val predictions = model.transform(test)

  predictions.show(10)

  val evaluator = new MulticlassClassificationEvaluator()
    .setMetricName("accuracy").setLabelCol("estimate")

  val accuracy = evaluator.evaluate(predictions)

  println("Classification accuracy" + accuracy.toString)

Этот код в конечном итоге обеспечивает точность предсказания, равную нулю (потому что в целевом столбце «оценка» нет метки «0,0»). Итак, что на самом деле я делаю не так? Мы будем благодарны за любые идеи.


person UGeorge    schedule 18.07.2017    source источник


Ответы (1)


Наконец я понял проблему. Spark не выдает ошибку или исключение, когда поле метки является двойным, но метки не находятся в допустимом диапазоне для классификатора, для преодоления этого использования StringIndexer требуется, поэтому нам просто нужно добавить в конвейер:

val labelIndexer = new StringIndexer()
  .setInputCol("estimate")
  .setOutputCol("indexedLabel")

Этот шаг решает проблему, но он неудобен.

person UGeorge    schedule 21.07.2017