SPARK 1.6.1:在评估DataFrame上的分类器时,任务不可序列化

时间:2016-05-13 09:21:40

标签: scala apache-spark apache-zeppelin

我有一个DataFrame,我将它映射到()的RDD以测试SVMModel。

我正在使用Zeppelin和Spark 1.6.1

这是我的代码:

val loadedSVMModel = SVMModel.load(sc, pathToSvmModel)

// Clear the default threshold.
loadedSVMModel.clearThreshold()

// Compute raw scores on the test set.
val scoreAndLabels = df.select($"features", $"label")
                       .map { case Row(features:Vector, label: Double) =>
                                val score = loadedSVMModel.predict(features)
                                (score,label)
                            }

// Get evaluation metrics.
val metrics = new BinaryClassificationMetrics(scoreAndLabels)
val auROC = metrics.areaUnderROC()

println("Area under ROC = " + auROC)

执行代码时,我有一个org.apache.spark.SparkException: Task not serializable;,我很难理解为什么会发生这种情况,我该如何解决。

  • 是否是因为我使用的是Zeppelin?
  • 是因为原始的DataFrame吗?

我已经在Spark编程指南中执行了SVM example,它运行得很好。所以原因应该与上面的一点相关......我猜。

以下是Exception堆栈的一些相关元素:

Caused by: java.io.NotSerializableException: org.apache.spark.sql.Column
Serialization stack:
    - object not serializable (class: org.apache.spark.sql.Column, value: (sum(CASE WHEN (domainIndex = 0) THEN sumOfScores ELSE 0),mode=Complete,isDistinct=false) AS 0#100278)
    - element of array (index: 0)
    - array (class [Lorg.apache.spark.sql.Column;, size 372)

我没有发布完整的异常堆栈,因为Zeppelin倾向于显示非常长的不相关文本。如果您希望我通过完整的例外,请告诉我。

其他信息

使用VectorAssembler()生成特征向量,如下所示

// Prepare vector assemble
val vecAssembler =  new VectorAssembler()
                               .setInputCols(arrayOfIndices)
                               .setOutputCol("features")


// Aggregation expressions
val exprs = arrayOfIndices
                .map(c => sum(when($"domainIndex" === c, $"sumOfScores")
                .otherwise(lit(0))).alias(c))

val df = vecAssembler
           .transform(anotherDF.groupBy($"userID", $"val")
           .agg(exprs.head, exprs.tail: _*))
           .select($"userID", $"features", $"val")
           .withColumn("label", sqlCreateLabelValue($"val"))
           .drop($"val").drop($"userID")

1 个答案:

答案 0 :(得分:6)

问题的根源实际上与您使用的DataFrame无关,甚至与Zeppelin直接相关。更多的是代码组织与在同一范围内存在非可序列化对象的问题。

由于您使用交互式会话,因此所有对象都在同一范围内定义,并成为闭包的一部分。它包含exprs,其Seq[Column]看起来像Columnexprs不可序列化。

操作SQL表达式时不会出现问题,因为RDD仅在本地使用,但在下拉到exprs操作时会出现问题。 ColumnName作为闭包的一部分包含在内,并导致表达式。您可以重现此行为的最简单方法(ColumnWelcome to ____ __ / __/__ ___ _____/ /__ _\ \/ _ \/ _ `/ __/ '_/ /___/ .__/\_,_/_/ /_/\_\ version 2.0.0-SNAPSHOT /_/ Using Scala version 2.11.8 (OpenJDK 64-Bit Server VM, Java 1.8.0_91) Type in expressions to have them evaluated. Type :help for more information. scala> val df = Seq(1, 2, 3).toDF("x") df: org.apache.spark.sql.DataFrame = [x: int] scala> val x = $"x" x: org.apache.spark.sql.ColumnName = x scala> def f(x: Any) = 0 f: (x: Any)Int scala> df.select(x).rdd.map(f _) org.apache.spark.SparkException: Task not serializable ... Caused by: java.io.NotSerializableException: org.apache.spark.sql.ColumnName Serialization stack: - object not serializable (class: org.apache.spark.sql.ColumnName, value: x) ... )的子类是这样的:

exprs

您可以尝试解决此问题的一种方法是将@transient val exprs: Seq[Column] = ??? 标记为瞬态:

scala> @transient val x = $"x"
x: org.apache.spark.sql.ColumnName = x

scala> df.select(x).rdd.map(f _)
res1: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[8] at map at <console>:30

在我们的最小例子中也可以正常工作:

 foo.click() += some_other_function;   // supposedly to invoke another function
                                       // when the foo HTML element is clicked.