Spark
K-fold Cross Validation
Machine Learning
Data Science
Apache Spark

Spark K-fold Cross Validation

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

K-fold cross-validation estimates model quality by training the same pipeline multiple times on different train-validation splits. In Spark ML, this is handled by CrossValidator, which distributes the work across the cluster and evaluates each parameter combination over several folds.

This is useful when you want a more reliable estimate than a single holdout split, especially during hyperparameter tuning. The tradeoff is cost: more folds and more parameter combinations mean more model fits.

How Spark Cross Validation Works

Suppose k = 5. Spark divides the dataset into five folds. For each candidate parameter set:

  1. Train on four folds.
  2. Validate on the remaining fold.
  3. Repeat until each fold has served as the validation fold once.
  4. Average the evaluation metric.

Spark then compares the average scores across all parameter combinations and keeps the best model.

The core objects are:

  • An estimator such as logistic regression or random forest.
  • An evaluator such as BinaryClassificationEvaluator.
  • A parameter grid built with ParamGridBuilder.
  • A CrossValidator that coordinates the whole search.

PySpark Example

Here is a runnable example using a simple pipeline and logistic regression.

python
1from pyspark.sql import SparkSession
2from pyspark.ml import Pipeline
3from pyspark.ml.classification import LogisticRegression
4from pyspark.ml.evaluation import BinaryClassificationEvaluator
5from pyspark.ml.feature import VectorAssembler
6from pyspark.ml.tuning import CrossValidator, ParamGridBuilder
7
8spark = SparkSession.builder.appName("cv-demo").getOrCreate()
9
10data = spark.createDataFrame([
11    (0.0, 1.0, 0.0, 0),
12    (1.0, 0.0, 1.0, 1),
13    (0.5, 1.0, 1.0, 1),
14    (0.2, 0.1, 0.0, 0),
15    (1.2, 0.9, 1.0, 1),
16    (0.1, 0.2, 0.0, 0),
17], ["x1", "x2", "x3", "label"])
18
19assembler = VectorAssembler(inputCols=["x1", "x2", "x3"], outputCol="features")
20lr = LogisticRegression(featuresCol="features", labelCol="label")
21pipeline = Pipeline(stages=[assembler, lr])
22
23param_grid = (ParamGridBuilder()
24    .addGrid(lr.regParam, [0.0, 0.1, 0.5])
25    .addGrid(lr.maxIter, [10, 30])
26    .build())
27
28evaluator = BinaryClassificationEvaluator(labelCol="label")
29
30cv = CrossValidator(
31    estimator=pipeline,
32    estimatorParamMaps=param_grid,
33    evaluator=evaluator,
34    numFolds=3,
35    parallelism=2,
36)
37
38cv_model = cv.fit(data)
39print(cv_model.bestModel)

The important part is that the parameter grid is attached to the estimator, and Spark fits every combination across all folds.

When to Use It

Cross-validation is a good default when:

  • The dataset is large enough that repeated training is feasible.
  • Hyperparameters materially affect model quality.
  • A single train-test split feels too noisy.

However, it is not free. If the model is expensive to train, k is large, or the parameter grid is broad, total runtime grows quickly. A grid with 20 parameter combinations and 5 folds means 100 fits.

That is why Spark also offers TrainValidationSplit, which is cheaper but statistically less robust. If you need a quick coarse search before a final high-quality evaluation, a staged approach often works well.

Practical Performance Considerations

Cache the dataset when repeated scans are expensive and the data fits the available memory budget. Set parallelism on the CrossValidator to let multiple parameter combinations run concurrently when cluster resources allow it.

Also be realistic about the parameter grid. It is easy to explode runtime by sweeping too many values that are unlikely to matter. Narrow the search space using domain knowledge or a smaller pilot run.

Finally, remember that preprocessing steps in the pipeline are part of each fit. That is usually correct because it prevents data leakage, but it also means feature engineering costs are multiplied by the number of fits.

Common Pitfalls

A common mistake is fitting transformers outside the pipeline and then cross-validating only the estimator. That can leak information from validation folds into training and produce overly optimistic scores.

Another issue is forgetting how quickly the computation scales. Folds multiplied by parameter combinations gives you the approximate number of model fits, and each fit may trigger substantial Spark work.

Developers also sometimes use too many folds on small datasets inside Spark, where the orchestration overhead can dominate. For modest data sizes, a local tool may be simpler.

Finally, make sure the evaluator matches the task. Classification, regression, and ranking need different evaluation metrics.

Summary

  • Spark ML performs K-fold cross-validation with CrossValidator.
  • It evaluates every parameter combination across all folds and averages the metric.
  • Use a full pipeline to avoid leakage during preprocessing.
  • Runtime grows with numFolds times the size of the parameter grid.
  • For cheaper tuning, consider TrainValidationSplit before a final cross-validation run.

Course illustration
Course illustration

All Rights Reserved.