Spark ML Pipelines
A machine learning project repeats the same steps: clean the data, convert text to numbers, combine features, and train a model. A Spark ML Pipeline chains those steps into one object. You train the pipeline once and reuse it on new data with the same preparation every time.
The Layman View
A factory assembly line moves a product through fixed stations. Each station performs one job, and the product leaves as a finished item. A pipeline moves a DataFrame through stations and returns predictions.
Two Building Blocks
- Transformer converts one DataFrame into another. It applies a rule and needs no learning. Example: VectorAssembler.
- Estimator learns from data and produces a Transformer. Example: LogisticRegression produces a trained model.
Transformer : DataFrame --transform()--> DataFrame Estimator : DataFrame --fit()-------> Model (a Transformer)
Sample Data
data = [
("Delhi", 25, 40000.0, 0), ("Pune", 45, 90000.0, 1),
("Delhi", 35, 60000.0, 1), ("Mumbai", 22, 30000.0, 0),
("Pune", 50, 85000.0, 1), ("Mumbai", 28, 35000.0, 0),
("Delhi", 41, 78000.0, 1), ("Pune", 23, 32000.0, 0),
]
df = spark.createDataFrame(data, ["city", "age", "income", "label"])
train, test = df.randomSplit([0.75, 0.25], seed=42)
The label column holds the answer that the model learns to predict, such as whether a customer buys a product.
The Stations
from pyspark.ml import Pipeline from pyspark.ml.feature import StringIndexer, OneHotEncoder, VectorAssembler, StandardScaler from pyspark.ml.classification import LogisticRegression indexer = StringIndexer(inputCol="city", outputCol="city_idx", handleInvalid="keep") encoder = OneHotEncoder(inputCols=["city_idx"], outputCols=["city_vec"]) assembler = VectorAssembler(inputCols=["city_vec", "age", "income"], outputCol="raw_features") scaler = StandardScaler(inputCol="raw_features", outputCol="features") lr = LogisticRegression(featuresCol="features", labelCol="label")
- StringIndexer turns city names into numbers.
- OneHotEncoder turns those numbers into separate yes-or-no columns.
- VectorAssembler packs all inputs into one feature vector.
- StandardScaler puts features on a similar scale.
- LogisticRegression learns the prediction rule.
Building and Training
pipeline = Pipeline(stages=[indexer, encoder, assembler, scaler, lr]) model = pipeline.fit(train)
train data
|
StringIndexer --> OneHotEncoder --> VectorAssembler --> StandardScaler --> LogisticRegression
|
PipelineModel
The fit call runs every Estimator in order and returns a PipelineModel that holds the learned stations.
Making Predictions
predictions = model.transform(test)
predictions.select("city", "age", "income", "label", "prediction").show()
The same fitted model prepares and scores brand-new data. No step gets repeated by hand, so training and prediction treat the data identically.
Measuring Quality
from pyspark.ml.evaluation import BinaryClassificationEvaluator
evaluator = BinaryClassificationEvaluator(labelCol="label", metricName="areaUnderROC")
print("AUC:", evaluator.evaluate(predictions))
A score near 1.0 shows strong separation between the two classes. A score near 0.5 shows guessing.
Tuning with Cross-Validation
A grid of settings lets Spark test several model configurations and keep the best.
from pyspark.ml.tuning import ParamGridBuilder, CrossValidator
grid = (ParamGridBuilder()
.addGrid(lr.regParam, [0.01, 0.1, 1.0])
.addGrid(lr.maxIter, [10, 50])
.build())
cv = CrossValidator(estimator=pipeline, estimatorParamMaps=grid,
evaluator=evaluator, numFolds=3)
best_model = cv.fit(train).bestModel
Saving and Loading
from pyspark.ml import PipelineModel
model.write().overwrite().save("/models/purchase_model")
loaded = PipelineModel.load("/models/purchase_model")
loaded.transform(test).show()
Good Practices
- Fit the pipeline on training data only. Test data stays unseen until evaluation.
- Use handleInvalid="keep" so unknown categories at prediction time do not crash the job.
- Save the full PipelineModel, never only the final model, so preparation steps travel with it.
- Version saved models with dates or numbers.
Inspecting the Learned Model
Every stage of a PipelineModel stays accessible. Pull out the last stage to read the learned weights.
lr_model = model.stages[-1]
print("Coefficients:", lr_model.coefficients)
print("Intercept:", lr_model.intercept)
Each coefficient shows how strongly one feature pushes the prediction toward the positive class. A positive value raises the chance of a positive result, and a negative value lowers it.
Train and Test Split Reminder
All data (100%) | |-- Train (75%) : fit the pipeline |-- Test (25%) : measure quality on unseen rows
