2015-06-24 4 views
32

Ich versuche zufällige Gesamtstrukturklassifizierung mit Spark ML api ausführen, aber ich habe Probleme mit der Erstellung von rechten Datenrahmen Eingabe in Pipeline. HierSo erstellen Sie den richtigen Datenrahmen für die Klassifizierung in Spark ML

ist Beispieldaten:

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" 

Alter und hours_per_week ganze Zahlen sind, während andere Funktionen, einschließlich Label salaryRange kategorische sind (String)

Laden des CSV-Datei (nennen wir es sample.csv) kann von Spark csv library wie folgt ausgeführt werden:

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

Standardmäßig werden alle Spalten als String importiert werden, so müssen wir "Alter" und "hours_per_week" ändern Int:

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

Nur um zu überprüfen, wie Schema sieht jetzt:

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) 

Dann Hiermit kann der Quer Validator und Pipeline gesetzt:

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

Fehler zeigt sich, wenn diese Zeile ausgeführt wird:

val cmModel = cv.fit(dataFixed) 

java.lang.IllegalArgumentException: Feld "Features" existiert nicht.

Es ist möglich, Label-Spalte und Feature-Spalte in RandomForestClassifier zu setzen, jedoch habe ich 4 Spalten als Prädiktoren (Features) nicht nur eins.

Wie sollte ich meinen Datenrahmen organisieren, damit die Spalten für Etiketten und Funktionen korrekt organisiert sind?

Für Ihre Bequemlichkeit hier ist voll Code:

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) 
    } 

} 

Dank für die Hilfe!

+0

Die Scala-Sprache ist nicht bekannt, aber wo werden Beschriftungen und Features aus dem Dataset wie LabeledPoint (Beschriftungen, Liste (Features)) festgelegt, überprüfen Sie das Beispiel in https://spark.apache.org/docs/latest/mllib -linear-methods.html –

+0

@ABC, Bitte überprüfen Sie meinen Kommentar in der folgenden Frage. –

+0

Überprüfen Sie dieses Beispiel https://github.com/apache/spark/blob/master/examples/src/main/scala/org/apache/spark/examples/ml/SimpleTextClassificationPipeline.scala, wobei val model = pipeline.fit (training .toDF()) benutzt den Dataframe in der Pipeline –

Antwort

29

Sie müssen nur sicherstellen, dass Sie eine "features" Spalte in dem Datenrahmen, die vom Typ unter VectorUDF als Show:

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

Im Wesentlichen gibt es braucht zwei Felder in Ihrem Datenrahmen "Features" für Feature-Vektor und "Label" für Instanz-Labels. Die Instanz muss vom Typ Double sein.

Um ein "Features" Felder mit Vector Typ zu erstellen zunächst ein udf als Show erstellen unter:

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) 
} 

nun auch das "Label" Feld zu codieren, eine anderen udf erstellen, wie unten dargestellt:

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

Jetzt verwandeln wir original Datenrahmen diese beiden udf mit:

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

Hinweis, dass es in dem Datenrahmen vorhanden zusätzliche Spalten/Felder sein, aber in diesem Fall habe ich nur features und label ausgewählt:

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| 
+-------------------+-----+ 

Jetzt sein bis zu Ihnen richtige Parameter für Ihren Lernalgorithmus einstellen zu machen es funktioniert.

+0

Gibt es eine Chance, dass Sie zeigen können, wie ich eine Spalte namens "Features" vom Typ VectorUDF aus meinen Daten erstellen kann? –

+1

@DusanGrubjesic: Ich habe Codebeispiele hinzugefügt. Bitte überprüfen ** EDIT1 ** – tuxdna

+0

das ist wirklich toll! Ich bin nur nicht sicher, wie wir Informationen von ML an den Klassifikator weitergeben können, dass diese e3 und e4 kategorischen Merkmale nun nicht numerisch sind? Ursache in "low level" mllib api war es möglich, ** categoricalFeaturesInfo ** mit Indizes und Anzahl der Kategorien von kategorischen Merkmalen zu übergeben. In "High Level" ml API sollte dies direkt aus dem Schema extrahiert werden. –

0

Laut Dokumentation von Sparks auf mllib - zufällige Bäume, scheint mir, dass Sie die Feature-Karte, die Sie verwenden, definieren sollten, und die Punkte sollten einen markierten Punkt sein.

Dies teilt dem Algorithmus mit, welche Spalte als Vorhersage verwendet werden soll und welche die Features sind.

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

+1

Es gibt eine alte API im Paket ** mllib ** und die Punkte sollten tatsächlich LabeledPoint sein. Ich versuche jedoch, neue API im * ml * -Paket zu verwenden, da sie Pipelines, Kreuzvalidierung usw. unterstützt. Diese neue API verwendet DataFrame als Eingabe. z.B. vergleiche diese zwei: [RandomForestClassifier] (https://spark.apache.org/docs/1.4.0/api/scala/index.html#org.apache.spark.ml.classification.RandomForestClassifier) ​​von ** ml ** welche verwendet DataFrame und RandomForestModel (https://spark.apache.org/docs/1.4.0/api/scala/index.html#org.apache.spark.mllib.tree.model.RandomForestModel) von ** mllib ** –

45

Ab Spark 1.4 können Sie Transformer org.apache.spark.ml.feature.VectorAssembler verwenden. Geben Sie nur die Spaltennamen an, die Sie als Features verwenden möchten.

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

und fügen Sie es zu Ihrer Pipeline hinzu.

+1

[tuxdnas Antwort] (http://stackoverflow.com/a/31102246/1281433) erklärte die Details des Problems, und wie die Lösung aussehen muss. ** Diese Antwort ** zeigt den schönen Weg, dies zu erreichen. –

+1

Dies würde nicht funktionieren, da einige der Features vom Typ String sind. Große Lösung für streng numerische Daten. – gstvolvr

+2

@gstvolvr Sie müssen 'StringIndexer' zuerst verwenden, um Zeichenfolgen in numerische Werte zu konvertieren. Könnte es wert sein, diesen Schritt zur Klarheit der Antwort hinzuzufügen. – max

Verwandte Themen