2015-06-24 1 views
32

Я пытаюсь запустить произвольную классификацию леса с помощью Spark ML api, но у меня возникают проблемы с созданием ввода правильного ввода данных в конвейер.Как создать правильный фрейм данных для классификации в Spark ML

Вот выборка данных:

age,hours_per_week,education,sex,salaryRange 
38,40,"hs-grad","male","A" 
28,40,"bachelors","female","A" 
52,45,"hs-grad","male","B" 
31,50,"masters","female","B" 
42,40,"bachelors","male","B" 

возраст и hours_per_week являются целыми числами, а другие функции, включая этикетки salaryRange категоричны (String)

Загрузка этого CSV-файл (назовем его sample.csv) может быть сделано Spark csv library следующим образом:

val data = sqlContext.csvFile("/home/dusan/sample.csv") 

По умолчанию все столбцы импортируются в виде строки, поэтому мы должны изменить «возраст» и «hours_per_week» в Int:

val toInt = udf[Int, String](_.toInt) 
val dataFixed = data.withColumn("age", toInt(data("age"))).withColumn("hours_per_week",toInt(data("hours_per_week"))) 

Просто чтобы проверить, как схема выглядит сейчас:

scala> dataFixed.printSchema 
root 
|-- age: integer (nullable = true) 
|-- hours_per_week: integer (nullable = true) 
|-- education: string (nullable = true) 
|-- sex: string (nullable = true) 
|-- salaryRange: string (nullable = true) 

Тогда позволяет настроить кросс валидатора и трубопровод:

val rf = new RandomForestClassifier() 
val pipeline = new Pipeline().setStages(Array(rf)) 
val cv = new CrossValidator().setNumFolds(10).setEstimator(pipeline).setEvaluator(new BinaryClassificationEvaluator) 

ошибки проявляющейся при выполнении этой строки:

val cmModel = cv.fit(dataFixed) 

java.lang.IllegalArgumentException: Поле "функции" не существует.

Можно установить столбец меток и колонку функций в RandomForestClassifier, однако у меня есть 4 столбца в качестве предикторов (функций) не только один.

Как я должен организовать свой фрейм данных, чтобы он правильно маркировал столбцы и столбцы?

Для вашего удобства здесь полный код:

import org.apache.spark.SparkConf 
import org.apache.spark.SparkContext 
import org.apache.spark.ml.classification.RandomForestClassifier 
import org.apache.spark.ml.evaluation.BinaryClassificationEvaluator 
import org.apache.spark.ml.tuning.CrossValidator 
import org.apache.spark.ml.Pipeline 
import org.apache.spark.sql.DataFrame 

import org.apache.spark.sql.functions._ 
import org.apache.spark.mllib.linalg.{Vector, Vectors} 


object SampleClassification { 

    def main(args: Array[String]): Unit = { 

    //set spark context 
    val conf = new SparkConf().setAppName("Simple Application").setMaster("local"); 
    val sc = new SparkContext(conf) 
    val sqlContext = new org.apache.spark.sql.SQLContext(sc) 

    import sqlContext.implicits._ 
    import com.databricks.spark.csv._ 

    //load data by using databricks "Spark CSV Library" 
    val data = sqlContext.csvFile("/home/dusan/sample.csv") 

    //by default all columns are imported as string so we need to change "age" and "hours_per_week" to Int 
    val toInt = udf[Int, String](_.toInt) 
    val dataFixed = data.withColumn("age", toInt(data("age"))).withColumn("hours_per_week",toInt(data("hours_per_week"))) 


    val rf = new RandomForestClassifier() 

    val pipeline = new Pipeline().setStages(Array(rf)) 

    val cv = new CrossValidator().setNumFolds(10).setEstimator(pipeline).setEvaluator(new BinaryClassificationEvaluator) 

    // this fails with error 
    //java.lang.IllegalArgumentException: Field "features" does not exist. 
    val cmModel = cv.fit(dataFixed) 
    } 

} 

Спасибо за помощь!

+0

Не известно о языке scala, но где вы устанавливаете метки и функции из набора данных, например LabeledPoint (метки, список (функции)), посмотрите пример в https://spark.apache.org/docs/latest/mllib -linear-methods.html –

+0

@ABC, пожалуйста, проверьте мой комментарий в вопросе ниже. –

+0

проверьте этот пример https://github.com/apache/spark/blob/master/examples/src/main/scala/org/apache/spark/examples/ml/SimpleTextClassificationPipeline.scala где val model = pipeline.fit (обучение .toDF()) использует dataframe в конвейере –

ответ

29

Вам просто нужно, чтобы убедиться, что у вас есть "features" столбец в вашем dataframe, что имеет тип VectorUDF, как показано на рисунке ниже:

scala> val df2 = dataFixed.withColumnRenamed("age", "features") 
df2: org.apache.spark.sql.DataFrame = [features: int, hours_per_week: int, education: string, sex: string, salaryRange: string] 

scala> val cmModel = cv.fit(df2) 
java.lang.IllegalArgumentException: requirement failed: Column features must be of type [email protected] but was actually IntegerType. 
    at scala.Predef$.require(Predef.scala:233) 
    at org.apache.spark.ml.util.SchemaUtils$.checkColumnType(SchemaUtils.scala:37) 
    at org.apache.spark.ml.PredictorParams$class.validateAndTransformSchema(Predictor.scala:50) 
    at org.apache.spark.ml.Predictor.validateAndTransformSchema(Predictor.scala:71) 
    at org.apache.spark.ml.Predictor.transformSchema(Predictor.scala:118) 
    at org.apache.spark.ml.Pipeline$$anonfun$transformSchema$4.apply(Pipeline.scala:164) 
    at org.apache.spark.ml.Pipeline$$anonfun$transformSchema$4.apply(Pipeline.scala:164) 
    at scala.collection.IndexedSeqOptimized$class.foldl(IndexedSeqOptimized.scala:51) 
    at scala.collection.IndexedSeqOptimized$class.foldLeft(IndexedSeqOptimized.scala:60) 
    at scala.collection.mutable.ArrayOps$ofRef.foldLeft(ArrayOps.scala:108) 
    at org.apache.spark.ml.Pipeline.transformSchema(Pipeline.scala:164) 
    at org.apache.spark.ml.tuning.CrossValidator.transformSchema(CrossValidator.scala:142) 
    at org.apache.spark.ml.PipelineStage.transformSchema(Pipeline.scala:59) 
    at org.apache.spark.ml.tuning.CrossValidator.fit(CrossValidator.scala:107) 
    at $iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC.<init>(<console>:67) 
    at $iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC.<init>(<console>:72) 
    at $iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC.<init>(<console>:74) 
    at $iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC$$iwC.<init>(<console>:76) 

EDIT1

По существу нужно быть двумя полями в вашем кадре данных «функции» для вектор-функций и «ярлык», например, меток. Экземпляр должен быть типа Double.

Чтобы создать «особенности» полей с Vector типа сначала создайте udf, как показано на рисунке ниже:

val toVec4 = udf[Vector, Int, Int, String, String] { (a,b,c,d) => 
    val e3 = c match { 
    case "hs-grad" => 0 
    case "bachelors" => 1 
    case "masters" => 2 
    } 
    val e4 = d match {case "male" => 0 case "female" => 1} 
    Vectors.dense(a, b, e3, e4) 
} 

Теперь также кодировать поле «метки», создайте еще один udf, как показано ниже:

val encodeLabel = udf[Double, String](_ match { case "A" => 0.0 case "B" => 1.0}) 

Теперь мы преобразуем исходную dataframe с помощью этих двух udf:

val df = dataFixed.withColumn(
    "features", 
    toVec4(
    dataFixed("age"), 
    dataFixed("hours_per_week"), 
    dataFixed("education"), 
    dataFixed("sex") 
) 
).withColumn("label", encodeLabel(dataFixed("salaryRange"))).select("features", "label") 

Обратите внимание, что там могут быть дополнительные столбцы/поля, присутствующие в dataframe, но в данном случае я выбрал только features и label:

scala> df.show() 
+-------------------+-----+ 
|   features|label| 
+-------------------+-----+ 
|[38.0,40.0,0.0,0.0]| 0.0| 
|[28.0,40.0,1.0,1.0]| 0.0| 
|[52.0,45.0,0.0,0.0]| 1.0| 
|[31.0,50.0,2.0,1.0]| 1.0| 
|[42.0,40.0,1.0,0.0]| 1.0| 
+-------------------+-----+ 

Теперь его ДО установить правильные параметры для вашего алгоритма обучения, чтобы сделать он работает.

+0

Любые шансы вы можете показать, как я могу создать столбцы с именем «features» типа VectorUDF из моих данных? –

+1

@DusanGrubjesic: Я добавил примеры кода. Пожалуйста, проверьте ** EDIT1 ** – tuxdna

+0

это действительно здорово! Я просто не уверен, как мы можем передавать информацию в классификатор от ML, что теперь эти e3 и e4 являются категориальными функциями, не численными? Причина в «низком уровне» mllib api удалось передать ** категориальнымFeaturesInfo ** с индексами и количеством категорий категориальных признаков. В «высоком уровне» ml api это нужно извлечь непосредственно из схемы. –

0

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

Это скажет алгоритм, который должен использоваться в качестве предсказания, и какие из них являются функциями.

https://spark.apache.org/docs/latest/mllib-decision-tree.html

+1

Существует старый api, расположенный в пакете ** mllib **, и точки должны быть LabeledPoint действительно. Тем не менее, я пытаюсь использовать новый api, расположенный в пакете * ml *, потому что он поддерживает конвейеры, перекрестные проверки и т. Д. Этот новый api использует DataFrame в качестве входных данных. например сравните эти два: [RandomForestClassifier] (https://spark.apache.org/docs/1.4.0/api/scala/index.html#org.apache.spark.ml.classification.RandomForestClassifier) ​​из ** мл ** который использует DataFrame и RandomForestModel (https://spark.apache.org/docs/1.4.0/api/scala/index.html#org.apache.spark.mllib.tree.model.RandomForestModel) из ** mllib ** –

45

С версии Spark 1.4 вы можете использовать трансформатор org.apache.spark.ml.feature.VectorAssembler. Просто укажите имена столбцов, которые вы хотите использовать.

val assembler = new VectorAssembler() 
    .setInputCols(Array("col1", "col2", "col3")) 
    .setOutputCol("features") 

и добавить его в свой трубопровод.

+1

[ответ tuxdna] (http://stackoverflow.com/a/31102246/1281433) объяснил детали проблемы и какое решение должно выглядеть. ** Этот ответ ** показывает хороший способ его достижения. –

+1

Это не сработает, так как некоторые функции имеют тип String. Отличное решение для строго числовых данных. – gstvolvr

+2

@gstvolvr Сначала вам нужно использовать 'StringIndexer' для преобразования строк в числовые. Возможно, стоит добавить этот шаг к ответу для ясности. – max

 Смежные вопросы

  • Нет связанных вопросов^_^