5

Sto cercando di trovare Precisione utilizzando la convalida incrociata 5 volte utilizzando il modello di classificatore di foresta casuale in SCALA. Ma sto ottenendo il seguente errore durante l'esecuzione:RandomForestClassifier ha ricevuto input con errore di colonna dell'etichetta non valido in Apache Spark

java.lang.IllegalArgumentException: RandomForestClassifier è stato dato in ingresso con l'etichetta di colonna etichetta non valida, senza il numero di classi specificate. Vedi StringIndexer.

Ottenere l'errore sopra alla linea ---> val cvModel = cv.fit (trainingData)

Il codice che ho usato per la convalida incrociata dei dati impostati utilizzando foresta casuale è la seguente:

import org.apache.spark.ml.Pipeline 
import org.apache.spark.ml.tuning.{ParamGridBuilder, CrossValidator} 
import org.apache.spark.ml.classification.RandomForestClassifier 
import org.apache.spark.ml.evaluation.MulticlassClassificationEvaluator 
import org.apache.spark.mllib.linalg.Vectors 
import org.apache.spark.mllib.regression.LabeledPoint 

val data = sc.textFile("exprogram/dataset.txt") 
val parsedData = data.map { line => 
val parts = line.split(',') 
LabeledPoint(parts(41).toDouble, 
Vectors.dense(parts(0).split(',').map(_.toDouble))) 
} 


val splits = parsedData.randomSplit(Array(0.6, 0.4), seed = 11L) 
val training = splits(0) 
val test = splits(1) 

val trainingData = training.toDF() 

val testData = test.toDF() 

val nFolds: Int = 5 
val NumTrees: Int = 5 

val rf = new  
RandomForestClassifier() 
     .setLabelCol("label") 
     .setFeaturesCol("features") 
     .setNumTrees(NumTrees) 

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

val paramGrid = new ParamGridBuilder() 
      .build() 

val evaluator = new MulticlassClassificationEvaluator() 
    .setLabelCol("label") 
    .setPredictionCol("prediction") 
    .setMetricName("precision") 

val cv = new CrossValidator() 
    .setEstimator(pipeline) 
    .setEvaluator(evaluator) 
    .setEstimatorParamMaps(paramGrid) 
    .setNumFolds(nFolds) 

val cvModel = cv.fit(trainingData) 

val results = cvModel.transform(testData) 
.select("label","prediction").collect 

val numCorrectPredictions = results.map(row => 
if (row.getDouble(0) == row.getDouble(1)) 1 else 0).foldLeft(0)(_ + _) 
val accuracy = 1.0D * numCorrectPredictions/results.size 

println("Test set accuracy: %.3f".format(accuracy)) 

Qualcuno può spiegare qual è l'errore nel codice sopra.

risposta

8

RandomForestClassifier, come molti altri algoritmi ML, richiedono che sulla colonna dell'etichetta siano impostati metadati specifici e che i valori delle etichette siano valori interi da [0, 1, 2 ..., # classiche] rappresentati come doppi. In genere questo viene gestito da un upstream Transformers come StringIndexer. Poiché le etichette vengono convertite manualmente, i campi dei metadati non sono impostati e il classificatore non può confermare che tali requisiti siano soddisfatti.

val df = Seq(
    (0.0, Vectors.dense(1, 0, 0, 0)), 
    (1.0, Vectors.dense(0, 1, 0, 0)), 
    (2.0, Vectors.dense(0, 0, 1, 0)), 
    (2.0, Vectors.dense(0, 0, 0, 1)) 
).toDF("label", "features") 

val rf = new RandomForestClassifier() 
    .setFeaturesCol("features") 
    .setNumTrees(5) 

rf.setLabelCol("label").fit(df) 
// java.lang.IllegalArgumentException: RandomForestClassifier was given input ... 

Puoi colonna o ri-codificare etichetta utilizzando StringIndexer:

import org.apache.spark.ml.feature.StringIndexer 

val indexer = new StringIndexer() 
    .setInputCol("label") 
    .setOutputCol("label_idx") 
    .fit(df) 

rf.setLabelCol("label_idx").fit(indexer.transform(df)) 

o set required metadata manually:

val meta = NominalAttribute 
    .defaultAttr 
    .withName("label") 
    .withValues("0.0", "1.0", "2.0") 
    .toMetadata 

rf.setLabelCol("label_meta").fit(
    df.withColumn("label_meta", $"label".as("", meta)) 
) 

Nota:

etichette create utilizzando StringIndexer dipendono dalla frequenza non valore:

indexer.labels 
// Array[String] = Array(2.0, 0.0, 1.0) 

PySpark:

In campi di metadati Python può essere impostato direttamente sullo schema:

from pyspark.sql.types import StructField, DoubleType 

StructField(
    "label", DoubleType(), False, 
    {"ml_attr": { 
     "name": "label", 
     "type": "nominal", 
     "vals": ["0.0", "1.0", "2.0"] 
    }} 
)