Study guidePro
Databricks Machine Learning Professional
Every domain of the Databricks Machine Learning Professional exam explained, with decision tables, runnable Python, diagrams and the traps the questions set, following the September 2025 exam guide.
Aligned to the September 2025 exam guide · checked against the Databricks docs on 31 October 2026 · certlake.com/exam/machine-learning-professional/study-guide
Exam at a glance
| Questions | 59 scored, multiple choice (plus up to 10 unscored) |
| Time | 120 minutes |
| Pass mark | Not published by Databricks; practise to 70% or better |
| Language | Python |
| Exam guide | September 2025 |
| Domain | Weight | ≈ Questions |
|---|---|---|
| Model Development | 44% | 26 |
| MLOps | 44% | 26 |
| Model Deployment | 12% | 7 |
1. Model Development44% of the exam
Spark ML or single node?
| Situation | Use |
|---|---|
| Data too big for one machine's memory; classic algorithms (linear, trees, GBT) | Spark ML on the cluster |
| Data fits on one node | scikit-learn, XGBoost, LightGBM on the driver |
| Many small models, one per group | single-node library inside apply |
| Deep learning | PyTorch / TensorFlow with Torch or Ray |
For inference: batch and streaming scoring of huge tables suit a Spark ML Pipeline (for a stream, apply the logged model as an MLflow UDF, mlflow.). Real-time, low-latency requests suit a single-node model behind Model Serving, since a Spark model needs a Spark session to score.
A Spark ML pipeline
Transformers have transform( (String model, Vector, One model); estimators have fit( and return a transformer (String, Logistic). A Pipeline is an estimator; fitting it gives a Pipeline.
from pyspark.ml import Pipeline
from pyspark.ml.feature import (
StringIndexer, OneHotEncoder, VectorAssembler)
from pyspark.ml.classification import (
LogisticRegression)
from pyspark.ml.evaluation import (
BinaryClassificationEvaluator)
from pyspark.ml.tuning import (
CrossValidator, ParamGridBuilder)
idx = StringIndexer(inputCol="plan",
outputCol="plan_idx",
handleInvalid="keep")
ohe = OneHotEncoder(inputCols=["plan_idx"],
outputCols=["plan_vec"])
vec = VectorAssembler(
inputCols=["plan_vec", "tenure", "spend"],
outputCol="features")
lr = LogisticRegression(labelCol="churned")
pipe = Pipeline(stages=[idx, ohe, vec, lr])
grid = (ParamGridBuilder()
.addGrid(lr.regParam, [0.01, 0.1])
.addGrid(lr.elasticNetParam, [0.0, 0.5])
.build())
ev = BinaryClassificationEvaluator(
labelCol="churned", metricName="areaUnderROC")
cv = CrossValidator(estimator=pipe,
estimatorParamMaps=grid,
evaluator=ev, numFolds=3,
parallelism=4)
model = cv.fit(train_df).bestModel
print(ev.evaluate(model.transform(test_df)))
model.write().overwrite().save(path)
Crossfits each grid pointValidator numtimes, then refits the best on all the data;Folds Trainfits each once, on one split (cheaper, noisier).Validation Split parallelismfits models concurrently.- Evaluators:
Binary(Classification Evaluator area,Under ROC area),Under PR Multiclass(Classification Evaluator f1,accuracy),Regression(Evaluator rmse,r2,mae). - Save and reload with
Pipeline, or log it withModel. load( path) mlflow..spark. log_ model
Scaling training out
| Choice | Pick it when |
|---|---|
| Vertical (bigger node, more GPU memory) | The library is single node, or communication would dominate; simplest |
| Horizontal (more nodes) | The data or the search is too big for one node and the work parallelises |
| Data parallelism | The model fits on one GPU: each worker trains a copy on a shard and gradients are averaged |
| Model parallelism | The model doesn't fit on one GPU: layers or tensors are split across devices |
| Spark | Ray | |
|---|---|---|
| Best at | Data-parallel ETL, Spark ML, scoring big tables | Task-parallel Python: tuning, RL, deep learning, many small tasks |
| On Databricks | The cluster | setup_ from ray., on the same cluster (Runtime 12.2 LTS ML+) |
Torch runs PyTorch distributed training on Spark workers; local_ runs all processes on the driver.
Group-wise models with the pandas function API
import pandas as pd
from sklearn.linear_model import LinearRegression
def fit_store(pdf: pd.DataFrame) -> pd.DataFrame:
m = LinearRegression().fit(
pdf[["price", "promo"]], pdf["units"])
return pd.DataFrame({
"store_id": [pdf["store_id"].iloc[0]],
"r2": [m.score(pdf[["price", "promo"]],
pdf["units"])]})
out = (df.groupBy("store_id")
.applyInPandas(fit_store,
schema="store_id long, r2 double"))
apply gets one pandas DataFrame per group (a whole group must fit in an executor's memory); map gets an iterator of batches with no grouping, which suits inference. A pandas UDF (@pandas_) is column in, column out, its type hints pick the UDF variant, and the return type goes in @pandas_.
Distributed tuning: Optuna and Ray Tune
import mlflow
from mlflow.optuna.storage import MlflowStorage
from mlflow.pyspark.optuna.study import (
MlflowSparkStudy)
storage = MlflowStorage(experiment_id=exp_id)
study = MlflowSparkStudy(
study_name="gbt-tuning", storage=storage)
study.optimize(objective, n_trials=32, n_jobs=4)
print(study.best_params)
Mlflowkeeps the study in the MLflow experiment;Storage Mlflowruns the trials on Spark executors (MLflow 3, preinstalled in Runtime 17.0 ML+).Spark Study - Optuna's pruners (default
Median) stop poor trials early; the default sampler is TPE.Pruner - Ray Tune:
Tuner(on a Ray-on-Spark cluster; log to MLflow with Ray's MLflow integration.. . . ). fit( )
Advanced MLflow
with mlflow.start_run(run_name="search"):
for p in grid:
with mlflow.start_run(nested=True):
mlflow.log_params(p)
mlflow.log_metric("cv_auc", cv_auc(p))
mlflow.log_dict(summary, "summary.json")
mlflow.log_figure(fig, "confusion.png")
- One parent run for the search, a child run per parameter set (logging the cross-validated metric), then the best set retrained and logged with its model.
log_,param( s) log_(withmetric( s) step=),log_,artifact( s) log_,dict log_,figure log_,table set_.tag( s)
Custom models with real-time feature engineering: subclass mlflow. and compute features from the request inside predict (see domain 3), so training and serving share one code path.
Feature Store: point in time
from datetime import timedelta
from databricks.feature_engineering import (
FeatureEngineeringClient, FeatureLookup,
FeatureFunction)
fe = FeatureEngineeringClient()
fe.create_table(
name="main.ml.txn_feats",
primary_keys=["card_id", "ts"],
timeseries_columns="ts", df=feats_df)
ts = fe.create_training_set(
df=labels_df,
feature_lookups=[
FeatureLookup(
table_name="main.ml.txn_feats",
lookup_key="card_id",
timestamp_lookup_key="event_ts",
lookback_window=timedelta(days=7)),
FeatureFunction(
udf_name="main.ml.amount_ratio",
input_bindings={"amt": "amount",
"avg": "avg_30d"},
output_name="amount_ratio")],
label="is_fraud")
- A time series feature table has one timestamp key, in its primary keys. A lookup with
timestamp_joins the latest feature value at or before each label's timestamp (an AS OF join), so no future data leaks in; no earlier value gives null.lookup_ key lookback_drops values older than the window, in training and batch inference; online lookups always use the latest value.window
Feature pipelines, online serving, on-demand features
- Automated pipelines: a scheduled job or Lakeflow pipeline computes features and calls
fe.(merge is the only mode). Pass a streaming DataFrame andwrite_ table( name=…, df=…, mode="merge") write_returns atable Streaming, keeping features fresh as events arrive.Query - Online Feature Store:
fe., thencreate_ online_ store( name=…, capacity="CU_ 1") fe..publish_ table( online_ store=…, source_ table_ name=…, online_ table_ name=…) publish_ismode TRIGGERED(default, incremental) orCONTINUOUS(streaming), both needing Change Data Feed on the source, orSNAPSHOT(one full copy). - The legacy Databricks SDK
online_API is gone with Online Tables; the SDK'stables feature_service still deletes online tables (store delete_).online_ table - A model logged with
fe.and served looks up published features by key; Feature Serving endpoints serve a feature spec to non-model clients.log_ model - On-demand features are Python UDFs in Unity Catalog (
CREATE FUNCTION … LANGUAGE PYTHON) wrapped inFeature. They're computed from request data at inference time and byFunction create_at training time: one definition, no skew. Runtime 13.3 LTS ML+.training_ set
The rest of this guide is part of Pro
2 more domains, explained with snippets, decision tables and diagrams:
- 2. MLOps · 44%
- 3. Model Deployment · 12%
Know it, or just recognise it?
The free diagnostic shows which domains you'd lose marks on, in about 10 minutes and without signing up.
Take the free diagnosticCheat sheet
The two-page cheat sheet: rules, defaults and traps for the last week before the exam.
Open the cheat sheetSpotted something out of date? Email support@certlake.com. CertLake is not affiliated with Databricks, Inc.