[ML-3749] Log metric name and isLargerBetter in BenchmarkResult (#144)

* Add new case class "MLMetric" to save all different metrics
* Change "mlResult" in BenchmarkResult to Array[MLMetric]
* score function will return MLMetric
This commit is contained in:
ludatabricks 2018-06-01 15:49:16 -07:00 committed by Xiangrui Meng
parent 789a0f5b8b
commit 1768d376f9
4 changed files with 37 additions and 27 deletions

View File

@ -7,6 +7,8 @@ import org.apache.spark.ml.evaluation.Evaluator
import org.apache.spark.sql._ import org.apache.spark.sql._
import org.apache.spark.sql.functions._ import org.apache.spark.sql.functions._
import com.databricks.spark.sql.perf._
/** /**
* The description of a benchmark for an ML algorithm. It follows a simple, standard proceduce: * The description of a benchmark for an ML algorithm. It follows a simple, standard proceduce:
* - generate some test and training data * - generate some test and training data
@ -38,7 +40,7 @@ trait BenchmarkAlgorithm extends Logging {
def score( def score(
ctx: MLBenchContext, ctx: MLBenchContext,
testSet: DataFrame, testSet: DataFrame,
model: Transformer): Double = -1.0 // Not putting NaN because it is not valid JSON. model: Transformer): MLMetric = MLMetric.Invalid
def name: String = { def name: String = {
this.getClass.getCanonicalName.replace("$", "") this.getClass.getCanonicalName.replace("$", "")
@ -67,9 +69,17 @@ trait ScoringWithEvaluator {
final override def score( final override def score(
ctx: MLBenchContext, ctx: MLBenchContext,
testSet: DataFrame, testSet: DataFrame,
model: Transformer): Double = { model: Transformer): MLMetric = {
val eval = model.transform(testSet) val results = model.transform(testSet)
evaluator(ctx).evaluate(eval) val eval = evaluator(ctx)
val metricName = if (eval.hasParam("metricName")) {
val param = eval.getParam("metricName")
eval.getOrDefault(param).toString
} else {
eval.getClass.getSimpleName
}
val metricValue = eval.evaluate(results)
MLMetric(metricName, metricValue, eval.isLargerBetter)
} }
} }

View File

@ -72,9 +72,17 @@ class MLPipelineStageBenchmarkable(
val (scoreTrainTime, scoreTraining) = measureTime { val (scoreTrainTime, scoreTraining) = measureTime {
test.score(param, trainingData, model) test.score(param, trainingData, model)
} }
val metricTrainingTime = MLMetric("training.time", scoreTrainTime.toMillis, false)
val metricTraining = MLMetric("training."+scoreTraining.metricName,
scoreTraining.metricValue,
scoreTraining.isLargerBetter)
val (scoreTestTime, scoreTest) = measureTime { val (scoreTestTime, scoreTest) = measureTime {
test.score(param, testData, model) test.score(param, testData, model)
} }
val metricTestTime = MLMetric("test.time", scoreTestTime.toMillis, false)
val metricTest = MLMetric("test."+scoreTraining.metricName,
scoreTraining.metricValue,
scoreTraining.isLargerBetter)
logger.info(s"$this doBenchmark: Trained model in ${trainingTime.toMillis / 1000.0}" + logger.info(s"$this doBenchmark: Trained model in ${trainingTime.toMillis / 1000.0}" +
s" s, Scored training dataset in ${scoreTrainTime.toMillis / 1000.0} s," + s" s, Scored training dataset in ${scoreTrainTime.toMillis / 1000.0} s," +
@ -86,19 +94,14 @@ class MLPipelineStageBenchmarkable(
tuple._1 -> additionalMethodTime.toMillis.toDouble tuple._1 -> additionalMethodTime.toMillis.toDouble
} }
val ml = MLResult( val mlMetrics = Array(metricTrainingTime, metricTraining, metricTestTime, metricTest)
trainingTime = Some(trainingTime.toMillis),
trainingMetric = Some(scoreTraining),
testTime = Some(scoreTestTime.toMillis),
testMetric = Some(scoreTest / testDataCount.get),
additionalTests = additionalTests)
BenchmarkResult( BenchmarkResult(
name = name, name = name,
mode = executionMode.toString, mode = executionMode.toString,
parameters = params.toMap, parameters = params.toMap,
executionTime = Some(trainingTime.toMillis), executionTime = Some(trainingTime.toMillis),
mlResult = Some(ml)) mlResult = Some(mlMetrics))
} catch { } catch {
case e: Exception => case e: Exception =>
BenchmarkResult( BenchmarkResult(

View File

@ -9,7 +9,6 @@ import org.apache.spark.ml.linalg.Vectors
import org.apache.spark.sql.DataFrame import org.apache.spark.sql.DataFrame
import org.apache.spark.sql.functions.{col, split} import org.apache.spark.sql.functions.{col, split}
import com.databricks.spark.sql.perf.MLResult
import com.databricks.spark.sql.perf.mllib.{BenchmarkAlgorithm, MLBenchContext, TestFromTraining} import com.databricks.spark.sql.perf.mllib.{BenchmarkAlgorithm, MLBenchContext, TestFromTraining}
import com.databricks.spark.sql.perf.mllib.OptionImplicits._ import com.databricks.spark.sql.perf.mllib.OptionImplicits._
import com.databricks.spark.sql.perf.mllib.data.DataGenerator import com.databricks.spark.sql.perf.mllib.data.DataGenerator

View File

@ -83,7 +83,7 @@ case class BenchmarkResult(
breakDown: Seq[BreakdownResult] = Nil, breakDown: Seq[BreakdownResult] = Nil,
queryExecution: Option[String] = None, queryExecution: Option[String] = None,
failure: Option[Failure] = None, failure: Option[Failure] = None,
mlResult: Option[MLResult] = None) mlResult: Option[Array[MLMetric]] = None)
/** /**
* The execution time of a subtree of the query plan tree of a specific query. * The execution time of a subtree of the query plan tree of a specific query.
@ -223,19 +223,17 @@ object MLParams {
} }
/** /**
* Result information specific to MLlib. * Metrics specific to MLlib benchmark.
* *
* @param trainingTime (MLlib) Training time. * @param metricName the name of the metric
* executionTime is set to the same value to match Spark Core tests. * @param metricValue the value of the metric
* @param trainingMetric (MLlib) Training metric, such as accuracy * @param isLargerBetter the indicator showing whether larger metric value is better
* @param testTime (MLlib) Test time (for prediction on test set, or on training set if there
* is no test set).
* @param testMetric (MLlib) Test metric, such as accuracy
* @param additionalTests (MLlib) Additional methods test results.
*/ */
case class MLResult( case class MLMetric(
trainingTime: Option[Double] = None, metricName: String,
trainingMetric: Option[Double] = None, metricValue: Double,
testTime: Option[Double] = None, isLargerBetter: Boolean)
testMetric: Option[Double] = None,
additionalTests: Map[String, Double]) object MLMetric {
val Invalid = MLMetric("Invalid", 0.0, false)
}