What you will be able to do
- Explain why Hyperopt workloads should be migrated and what Databricks recommends instead
- Run parallel Optuna trials on Spark executors with MlflowStorage and MlflowSparkStudy
- Know the default sampler and pruner of MlflowSparkStudy
- Size a Ray-on-Spark cluster for Ray Tune and log the trials to MLflow as child runs
1.Why Hyperopt is no longer the answer
For years, distributed tuning on Databricks meant Hyperopt, which worked with both distributed algorithms such as Spark MLlib and single-machine libraries such as scikit-learn. That has changed for two reasons. The open-source Hyperopt project is no longer maintained, and Hyperopt is not included in Databricks Runtime for Machine Learning after 16.4 LTS ML. Databricks now recommends two replacements, and the split between them matters on the exam: Optuna for single-node optimization, and Ray Tune for an experience similar to Hyperopt's distributed tuning.
In practice, a migration starts by looking at the shape of the workload. If one machine is enough, Optuna is the documented choice. If trials must fan out across a cluster in the way Hyperopt did, Ray Tune is the closest match. Older Databricks examples, such as the notebook that compares three model types using Hyperopt with SparkTrials, remain useful for understanding existing code, but they are patterns to replace rather than patterns to build new work on. Keep this in mind when a question describes a legacy job and asks what to do next.
Checkpoint 1 of 6· Check yourself
A job tunes with Hyperopt on Databricks Runtime ML 16.4 LTS. What is the accurate reason to plan a migration?
Databricks documents removal after 16.4 LTS ML and an unmaintained upstream project. Hyperopt did support Spark MLlib, so that option is false.
“Hyperopt is not included in Databricks Runtime for Machine Learning after 16.4 LTS ML.”Source: docs.databricks.com
Checkpoint 2 of 6· Exam question
A deep learning model's parameters no longer fit in the memory of a single GPU, even though each training batch itself is modest in size. Which parallelization strategy directly addresses this specific constraint?
Correct answer: A — Model parallelism, which splits the model's layers or parameters across multiple devices so no single device must hold the full parameter set.
- A. Correct: model parallelism partitions the model itself across devices, which is exactly the fix when the parameter set is too large for one device's memory, independent of batch size.
- B. Data parallelism still requires a full model replica on every device, so it does nothing to relieve a per-device memory bottleneck caused by the model's own parameter footprint.
- C. `applyInPandas`-based partitioning distributes independent per-group models, not the layers of a single oversized model, so it does not solve a single model's device-memory limit.
- D. Hyperparameter search runs many independent full model instances to compare configurations; it does not shrink the memory footprint of any one instance's parameters.
Sources1
2.Optuna on Spark executors with MLflow 3
Optuna is a lightweight framework with dynamic search spaces. In plain Optuna you parallelize with Joblib and track results in MLflow. MLflow 3.0, which comes pre-installed in Databricks Runtime 17.0 ML and above, adds two classes that let Optuna scale horizontally. MlflowStorage makes the MLflow Tracking Server Optuna's storage backend. It batches writes so the trials don't hit REST API throttling. MlflowSparkStudy wraps an Optuna Study and runs its trials on PySpark executors, with all of them reading and writing through that shared storage.
The workflow runs in a fixed order. First, write an objective function that calls a trial's suggest methods to define the search space and returns the metric to minimize. Second, create the shared storage. Third, create the study and call its optimize function.
import mlflow
from mlflow.optuna.storage import MlflowStorage
experiment_id = mlflow.get_experiment_by_name(dbutils.notebook.entry_point.getDbutils().notebook().getContext().notebookPath().get()).experiment_id
mlflow_storage = MlflowStorage(experiment_id=experiment_id)Checkpoint 3 of 6· Fill the gap
Which method runs the trials of this Spark-backed study?
from mlflow.pyspark.optuna.study import MlflowSparkStudy
mlflow_study = MlflowSparkStudy(
study_name="spark-mlflow-tuning",
storage=mlflow_storage,
)
mlflow_study. ? (objective, n_trials=8, n_jobs=4)You run the tuning algorithm by calling the Study's optimize function. fmin belongs to Hyperopt.
Source: docs.databricks.com| Parameter | Purpose | Default |
|---|---|---|
| study_name | Name of the study | (none listed) |
| storage | mlflow.optuna.MlflowStorage backend | (none listed) |
| sampler | Algorithm that suggests values | optuna.samplers.TPESampler |
| pruner | Stops unpromising trials early | optuna.pruners.MedianPruner |
Because the sampler and pruner have defaults, a minimal study needs only a name and a storage object. In the documented example the study runs 8 trials with n_jobs=4, so several trials are in flight at once rather than one after another. The storage class has its own tuning knobs for the batching mentioned above: batch_flush_interval (default 1.0 seconds between automatic flushes) and batch_size_threshold (default 100 items before a flush is triggered). You rarely need to change them, but they explain how MLflow avoids throttling when many executors report results at once.
Checkpoint 4 of 6· Put it in order
Put the Optuna-on-Spark workflow in order
- 1.Create shared storage with MlflowStorage(experiment_id=...)
- 2.Define an objective function whose trial suggest calls set the search space
- 3.Create an MlflowSparkStudy that uses that storage
- 4.Call optimize(objective, n_trials=..., n_jobs=...) on the study
The objective and storage have to exist before the study can use them, and optimize is the step that actually runs the trials.
“Create an Optuna Study object, and run the tuning algorithm by calling the optimize function of the Study object.”Source: docs.databricks.com
Checkpoint 5 of 6· Exam question
A team wants to run hyperparameter search for a reinforcement learning agent where trial durations vary widely and unrelated trials should be scheduled independently and opportunistically as executor capacity frees up. Which distributed tuning approach best matches this workload's characteristics?
Correct answer: A — Ray Tune, since Ray's dynamic task scheduler handles independent, variable-duration tasks more responsively than Spark's static execution plan.
- A. Correct: Ray excels at task parallelism with dynamic scheduling, making it well suited to independent, variable-duration tasks like reinforcement learning trials, unlike Spark's more static execution planning.
- B. `CrossValidator` uses Spark's static execution plan, which is optimized for data-parallel, relatively uniform tasks and is not designed to dynamically reschedule wildly uneven independent trials.
- C. Grouped Pandas UDFs process fixed groups determined at planning time; they do not provide Ray-style dynamic, opportunistic rescheduling of independent variable-length tasks across free capacity.
- D. `local_mode=True` confines TorchDistributor to processes on a single machine for one coordinated training job, which is unrelated to dynamically scheduling many independent RL trials cluster-wide.
3.Ray Tune: distributed search on a Ray-on-Spark cluster
Ray Tune comes with Ray, which is included in Databricks Runtime ML, and uses Ray as its distributed backend. It is the recommended replacement for Hyperopt-style distributed tuning, and it fits trials that are compute-heavy and independent, such as deep learning runs. Ray Tune runs on a Ray cluster that you start inside your Spark cluster with setup_ray_cluster. That call starts a background Spark job. Each Spark task in the job becomes a Ray worker node, and the Ray head node runs on the driver.
from ray.util.spark import setup_ray_cluster, shutdown_ray_cluster
setup_ray_cluster(
min_worker_nodes=2,
max_worker_nodes=4,
num_cpus_per_node=4,
collect_log_to_path="/Volumes/<catalog>/<schema>/<volume>/path/to/ray_collected_logs"
)
# Pass any custom Ray configuration with ray.init
ray.init(ignore_reinit_error=True)How you size the cluster affects tuning throughput. Databricks recommends at least 4 CPU cores and 10GB of heap memory per Ray worker node. Setting the per-worker CPU (or GPU) count to match a Spark worker node gives one Ray worker per node that uses all of that node's resources. By default the head node gets 0 CPUs and 0 GPUs, because it only coordinates. In hybrid Spark-and-Ray clusters, reduce spark.executor.memory, because the Spark executor's lazy garbage collection and dataset cache can take memory Ray needs.
For tracking, Ray Tune uses a child-run approach with MLflow. You create a parent run in the driver process, and each Ray Tune task logs a child run under it, so all trials are grouped in one hierarchy.
Autoscaling is controlled by the range between min_worker_nodes and max_worker_nodes. If min_worker_nodes is left undefined, a fixed-size cluster starts with max_worker_nodes workers. A bounded range lets the Ray cluster grow for a search and shrink afterward, which frees resources for Spark tasks. Databricks also documents a Ray Tune example that uses the ASHA scheduler, which stops trials that fall behind instead of training every candidate to completion. Giving each trial a whole GPU through tune.with_resources keeps trials from sharing a device.
Checkpoint 6 of 6· Check yourself
With default Ray-on-Spark settings, where do Ray Tune trials execute?
Databricks limits the head node to 0 CPUs and 0 GPUs by default because it is used for global coordination. Trials run on the worker nodes created by Spark tasks.
“the Ray head node is usually used only for global coordination, not for running Ray tasks.”Source: docs.databricks.com
Exam traps
Each one states something that sounds right. Open it to see what is actually true.
1.Hyperopt is still the recommended tool for distributed tuning on current Databricks Runtime ML.Why is that wrong?
Hyperopt is not included after 16.4 LTS ML. Databricks recommends Optuna for single-node optimization and Ray Tune for Hyperopt-like distributed tuning.
Covered in Why Hyperopt is no longer the answer
2.MlflowSparkStudy defaults to random search with no pruning.Why is that wrong?
The default sampler is TPESampler and the default pruner is MedianPruner, which stops unpromising trials early.
Covered in Optuna on Spark executors with MLflow 3
Sources
Every claim above is drawn from one of these pages, quoted as it was written on the date shown.
- 1.
“The open-source version of Hyperopt is no longer being maintained.”
↩︎ Why Hyperopt is no longer the answer“Hyperopt is not included in Databricks Runtime for Machine Learning after 16.4 LTS ML.”
↩︎ Why Hyperopt is no longer the answer“Optuna can be easily parallelized with Joblib to scale workloads, and integrated with MLflow to track hyperparameters and metrics across trials.”
↩︎ Optuna on Spark executors with MLflow 3“Ray Tune is a hyperparameter tuning library that comes with Ray and uses Ray as a backend for distributed computing.”
↩︎ Ray Tune: distributed search on a Ray-on-Spark cluster“Databricks recommends using either Optuna for single-node optimization or RayTune for a similar experience to the deprecated Hyperopt distributed hyperparameter tuning functionality.”
↩︎ Exam trap 1 - 2.
“MlflowStorage class allows Optuna to use the MLflow Tracking Server as its storage backend.”
↩︎ Optuna on Spark executors with MLflow 3“MlflowStorage is a MLflow-based storage class for Optuna with batch processing to avoid REST API throttling.”
↩︎ Optuna on Spark executors with MLflow 3“MLflow 3.0 is pre-installed in Databricks Runtime 17.0 ML and above.”
↩︎ Optuna on Spark executors with MLflow 3“optuna.pruners.MedianPruner is used as the default.”
↩︎ Exam trap 2“MlflowSparkStudy class enables launching parallel Optuna studies using PySpark executors.”
↩︎ Prediction“Create an Optuna Study object, and run the tuning algorithm by calling the optimize function of the Study object.”
↩︎ Checkpoint - 3.
“Each Apache Spark task in the job creates a Ray worker node, and the Ray head node is created on the driver.”
↩︎ Ray Tune: distributed search on a Ray-on-Spark cluster“Databricks recommends setting num_cpus_per_node to a value greater than or equal to 4.”
↩︎ Ray Tune: distributed search on a Ray-on-Spark cluster“Databricks recommends you reduce the Spark executor memory to a small value.”
↩︎ Ray Tune: distributed search on a Ray-on-Spark cluster“the Ray head node is usually used only for global coordination, not for running Ray tasks.”
↩︎ Checkpoint - 4.
“Each Ray Tune task creates a child run under the parent run, maintaining a clear hierarchy of experiment results.”
↩︎ Ray Tune: distributed search on a Ray-on-Spark cluster