Spark 时间序列分析(三)

原文:annas-archive.org/md5/92b806e0bfe2149821d7ca72450c0f00

译者:飞龙

协议:CC BY-NC-SA 4.0

第九章:进入生产环境

在上一章中,我们已构建并测试了我们的时间序列分析模型,并展示了其可扩展性,接下来我们将探讨将时间序列模型部署到生产环境中的实际考量和步骤,使用 Spark 框架。此信息对于指导你从开发过渡到实际应用至关重要,确保时间序列模型在操作环境中的可靠性和有效性。许多机器学习项目停滞在开发和概念验证阶段,掌握部署到生产环境的细节将增强你将时间序列分析无缝集成到决策过程中。

第四章中,我们介绍了时间序列分析项目的整体端到端视角,而在本章中,我们将专注于以下几个主要主题,帮助项目进入生产环境:

  • 工作流

  • 监控与报告

  • 额外的考虑事项

技术要求

在本章中,我们将通过代码示例探讨如何在基于容器的环境中部署可扩展的时间序列分析端到端工作流。构建一个生产就绪的环境需要大量工作,远超我们在本章中可以合理涵盖的范围。我们将专注于提供一个示例作为起点。我们将看到迄今为止关于时间序列分析的知识如何结合起来,形成一个完整的端到端工作流。

本章的代码可以在以下网址找到:github.com/PacktPublishing/Time-Series-Analysis-with-Spark/tree/main/ch9

我们先从为示例设置环境开始。

环境设置

我们将使用 Docker 容器,如第三章第四章中所述,用于平台基础设施。请按照第三章使用容器进行部署部分和第四章环境设置部分的说明设置容器环境。

一旦环境设置好,从本章的 Git 仓库下载部署脚本,链接地址为:

github.com/PacktPublishing/Time-Series-Analysis-with-Spark/tree/main/ch9

然后你可以按照第四章环境启动部分的说明,启动容器环境。根据同一章中的访问 UI部分,快速进行组件的可视化验证。

在深入代码细节之前,让我们回顾一下工作流的概述,看看我们将在本节中构建的整体框架。

工作流

本章中的代码示例包含两个工作流。它们作为 有向无环图DAGs)在 Airflow 中实现,类似于 第四章。可视化工作流的最佳方式是通过 Airflow 中的 DAG 视图,如 图 9.19.2 所示。

这两个工作流如下:

  • ts-spark_ch9_data-ml-ops:这是端到端流程的示例,见 图 9.1,包括以下任务:

    • get_config

    • ingest_train_data

    • transform_train_data

    • train_and_log_model

    • forecast

    • ingest_eval_data

    • transform_eval_data

    • eval_forecast

https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_09_01.jpg

图 9.1:端到端工作流的 Airflow DAG

  • ts-spark_ch9_data-ml-ops_runall:这是第二个工作流,如 图 9.2 所示,它多次调用前一个工作流,并使用不同的日期范围。它模拟了现实中的情况,其中前一个端到端工作流会在定期间隔(如每日或每周)内启动,并使用新数据。

https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_09_02.jpg

图 9.2:Airflow DAG,多个调用端到端工作流

这些 Airflow DAG 的代码位于 dags 文件夹中。它们是用 Python 编写的(.py 文件),可以通过文本编辑器或代码编辑器进行可视化查看。

模块化

值得注意的是,在这里的示例中,我们本可以将这些独立任务的所有代码合并成一个大任务。然而,我们将工作流拆分为多个任务,以说明模块化的最佳实践。在实际情况下,这样做有利于独立的代码修改、扩展以及任务的重新执行。不同的团队可能拥有不同任务的所有权。

工作流分离

我们在此示例中演示的工作流,在您自己的实现中可以进一步拆分。例如,通常会将模型训练相关任务、预测和模型评估拆分为各自独立的工作流,并在不同的时间间隔内启动。

我们将在接下来的章节中详细解释每个 DAG 和相关任务,从 ts-spark_ch9_data-ml-ops_runall 开始。

模拟与运行

如我们在 图 9.2 中看到的,ts-spark_ch9_data-ml-ops_runall 有五个任务,我们将在此进一步解释这些任务。

_runall 工作流的目的是模拟在定期间隔内,训练、预测和评估周期的真实执行过程。在我们的示例中,_runall 工作流的每个任务对应一次训练、预测和评估的循环。我们将每个任务称为一次运行,所有任务总共有五次运行,对应 _runall 的五个任务。这些任务将在定期的间隔内调度,如每日、每周、每月等。在这里的示例中,我们只是顺序执行它们,一个接一个。

每个任务都使用不同的参数调用 ts-spark_ch9_data-ml-ops 工作流。它们如下:

  • runid:一个整数,用于标识运行

  • START_DATE:用于训练的时间序列数据集的起始日期

  • TRAIN_END_DATE:训练数据集中的时间序列结束日期

  • EVAL_END_DATE:评估数据集中的时间序列结束日期

不同运行的配置方式是使用一个滑动窗口,其中训练数据为 5 年,评估数据为 1 年。在实际场景中,评估日期范围可能更短,对应于更短的预测期。

运行配置如下:

conf_run1 = {
    'runid':          1,
    'START_DATE':     '1981-01-01',
    'TRAIN_END_DATE': '1985-12-31',
    'EVAL_END_DATE':  '1986-12-31',
}
conf_run2 = {
    'runid':          2,
    'START_DATE':     '1982-01-01',
    'TRAIN_END_DATE': '1986-12-31',
    'EVAL_END_DATE':  '1987-12-31',
}

任务按以下方式定义,以触发ts-spark_ch9_data-ml-ops工作流并将运行配置作为参数传递:

# Define tasks
t1 = TriggerDagRunOperator(
    task_id="ts-spark_ch9_data-ml-ops_1",
    trigger_dag_id="ts-spark_ch9_data-ml-ops",
    conf=conf_run1,
    wait_for_completion=True,
    dag=dag,
)
t2 = TriggerDagRunOperator(
    task_id="ts-spark_ch9_data-ml-ops_2",
    trigger_dag_id="ts-spark_ch9_data-ml-ops",
    conf=conf_run2,
    wait_for_completion=True,
    dag=dag,
)

任务随后按以下顺序依次启动:

t1 >> t2 >> t3 >> t4 >> t5

您可以从 Airflow DAG 视图中启动此ts-spark_ch9_data-ml-ops_runall Airflow DAG,如图 9.3所示,通过点击绿色突出显示的运行按钮(>)。

https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_09_03.jpg

图 9.3:运行 Airflow DAG

本 DAG 的结果可以在图 9.2中看到,显示了各个任务的状态。

现在我们将讨论这些任务的细节,正如我们所见,它们使用不同的参数调用ts-spark_ch9_data-ml-ops工作流。我们从第一个步骤get_config开始,它负责处理这些参数。

配置

ts-spark_ch9_data-ml-ops工作流中的第一个任务是t0,它调用get_config函数来获取运行工作流所需的配置。这些配置作为参数传递给工作流。正如前面提到的,它们是运行标识符和时间序列数据的日期范围,工作流将基于这些范围运行。我们将看到它们在随后的任务中是如何使用的。

定义任务t0的代码如下:

t0 = PythonOperator(
    task_id='get_config',
    python_callable=get_config,
    op_kwargs={'_vars': {
        'runid': "{{ dag_run.conf['runid'] }}",
        'START_DATE': "{{ dag_run.conf['START_DATE'] }}",
        'TRAIN_END_DATE': "{{ dag_run.conf['TRAIN_END_DATE'] }}",
        'EVAL_END_DATE': "{{ dag_run.conf['EVAL_END_DATE'] }}",
        },
    },
    provide_context=True,
    dag=dag,
)

get_config函数由任务t0调用,其代码如下:

def get_config(_vars, **kwargs):
    print(f"dag_config: {_vars}")
    return _vars variable for use by subsequent tasks.
We can see the status of the task in the DAG view in Airflow as per *Figure 9**.1*.
Data ingestion and storage
At this step, after completion of the `t0` task, the `t1` task is launched by Airflow. It calls the `ingest_train_data` function to ingest the training data from the input CSV file as specified by the `DATASOURCE` variable. In this example, as it is a relatively small file, we ingest the full file every time. You will likely ingest only new data points incrementally at this stage.
The code for this step is as follows:

def ingest_train_data(_vars, **kwargs):

sdf = spark.read.csv(

DATASOURCE, header=True, inferSchema=True

)

sdf = sdf.filter(

(F.col(‘date’) >= F.lit(_vars[‘START_DATE’])) &

(F.col(‘date’) <= F.lit(_vars[‘TRAIN_END_DATE’]))

)

data_ingest_count = sdf.count()

sdf.write.format(“delta”).mode(“overwrite”).save(

f"/data/delta/ts-spark_ch9_bronze_train_{_vars[‘runid’]}"

)

_vars[‘train_ingest_count’] = data_ingest_count

return _vars


 The data is ingested using Spark, with the `spark.read.csv` function, into a Spark DataFrame. We then filter the data for the range of dates that fall within the training dataset as per the `START_DATE` and `TRAIN_END_DATE` parameters.
We want to be able to later report on how much data we ingest every time. To enable this, we count the number of rows in the DataFrame.
Finally, in this task, we persist the ingested data with the `write` function to disk storage in `delta` format for use by the next steps of the workflow. As we will parallelize the workflow tasks in the future, and to avoid multiple parallel writes to the same disk location, we store the data for this specific run in its own table appended with `runid`. Note as well how we used the term `bronze` in the name. This corresponds to the **medallion** approach, which we discussed in the *Data processing and storage* section of *Chapter 4*. Persisting the data to storage at this stage can come in handy when we are ingesting a lot of data. This makes it possible in the future to change and rerun the rest of the pipeline without having to re-ingest the data.
The status of the task is visible in the DAG view in Airflow as per *Figure 9**.1*.
With the data ingested from the source and persisted, we can move on to the data transformation stage.
Data transformations
This stage corresponds to Airflow task `t2`, which calls the `transform_train_data` function. As its name suggests, this function transforms the training data into the right format for the upcoming training stage.
The code for this step is as follows:

def transform_train_data(_vars, **kwargs):

sdf = spark.read.format(“delta”).load(

f"/data/delta/ts-spark_ch9_bronze_train_{_vars[‘runid’]}"

)

sdf = sdf.selectExpr(

“date as ds”,

“cast(daily_min_temperature as double) as y”

)

sdf = sdf.dropna()

data_transform_count = sdf.count()

sdf.write.format(“delta”).mode(“overwrite”).save(

f"/data/delta/ts-spark_ch9_silver_train_{_vars[‘runid’]}"

)

_vars[‘train_transform_count’] = data_transform_count

return _vars


 We first read the data from `bronze`, where it was stored by the previous task, `t1`. This stored data can then be used as input to run the current task.
In this example, we do the following simple transformations:

*   Column level: Rename the `date` column as `ds`
*   Column level: Change `daily_min_temperature` to the double data type (the `cast` function) and rename it as `y`
*   DataFrame level: Remove all rows with missing values using the `dropna` function

As in the previous stage, we want to collect metrics specific to this stage so that we can later report on the transformations. To do this, we count the number of rows in the DataFrame after the transformations.
Note
This stage is likely to include several data checks and transformations, as discussed in the *Data quality checks, cleaning, and transformations* section of *Chapter 5*.
Finally, in this task, we persist the ingested data with the `write` function to disk storage in `delta` format for use by the next steps of the workflow. We call this data stage `silver`, as per the medallion approach explained previously.
Similarly to the previous tasks, we can see the task’s status in the DAG view in Airflow, as per *Figure 9**.1*.
With the data curated and persisted, we can move on to the model training stage.
Model training and validation
This stage is the longest in our example and corresponds to Airflow task `t3`, which calls the `train_and_log_model` function. This function trains and validates a Prophet forecasting model using the training data from the previous stage. As we saw in *Chapter 7*, choosing the right model involves a whole process, which we have simplified here to a minimum.
The code extract for this step is as follows:

def train_and_log_model(_vars, **kwargs):

sdf = spark.read.format(“delta”).load(

f"/data/delta/ts-spark_ch9_silver_train_{_vars[‘runid’]}"

)

pdf = sdf.toPandas()

mlflow.set_experiment(

‘ts-spark_ch9_data-ml-ops_time_series_prophet_train’

)

mlflow.start_run()

mlflow.log_param(“DAG_NAME”, DAG_NAME)

mlflow.log_param(“TRAIN_START_DATE”, _vars[‘START_DATE’])

mlflow.log_metric(

‘train_ingest_count’, _vars[‘train_ingest_count’])

model = Prophet().fit(pdf)

cv_metrics_name = [

“mse”, “rmse”, “mae”, “mdape”, “smape”, “coverage”]

cv_params = cross_validation(

)

_cv_metrics = performance_metrics(cv_params)

cv_metrics = {

n: _cv_metrics[n].mean() for n in cv_metrics_name}

signature = infer_signature(train, predictions)

mlflow.prophet.log_model(

model, artifact_path=ARTIFACT_DIR,

signature=signature, registered_model_name=model_name,)

mlflow.log_params(param)

mlflow.log_metrics(cv_metrics)

mlflow.end_run()

return _vars


 In this code example, we do the following:

1.  We first read the data from `silver`, where it was stored by the previous task, `t2`. Then, we can run the current task using the stored data as input.
2.  MLflow Tracking Server is used to save all the parameters and metrics for each run. We group them under an experiment called `ts-spark_ch9_data-ml-ops_time_series_prophet_train` and use `log_param` and `log_metric` functions to capture the parameters and metrics gathered so far in the run.
3.  We then train the Prophet model with the training data using the `fit` function.
4.  As a model validation step, we use the `cross_validation` function and retrieve the corresponding metrics with the `performance_metrics` function.
5.  The final step is to log the model to the MLflow Model Registry, using the `log_model` function, and all the related training and validation metrics with MLflow. Note that we log the model signature as a best practice to document the model in the MLflow Model Registry.

We can see the task’s status in the DAG view in Airflow, as per *Figure 9**.1*. The logged parameters and metrics are visible in MLflow Tracking server, as shown in *Figure 9**.4*.
<https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_09_04.jpg>

Figure 9.4: MLflow experiment tracking (training)
The model saved in the MLflow Model Registry is shown in *Figure 9**.5*.
<https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_09_05.jpg>

Figure 9.5: MLflow Model Registry
After the conclusion of the model training stage, we can progress to the next stage, where we will use the trained model to do forecasting.
Forecasting
This stage corresponds to Airflow task `t4`, which calls the `forecast` function. As its name suggests, this function infers future values of the time series. While we have this task in the same workflow as the prior training tasks, it is common for the forecasting task to be in a separate inferencing pipeline. This separation allows for scheduling the training and inferencing at different times.
The code for this step is as follows:

def forecast(_vars, **kwargs):

Load the model from the Model Registry

model_uri = f"models:/{model_name}/{model_version}"

_model = mlflow.prophet.load_model(model_uri)

forecast = _model.predict(

_model.make_future_dataframe(

periods=365, include_history = False))

sdf = spark.createDataFrame(forecast[

[‘ds’, ‘yhat’, ‘yhat_lower’, ‘yhat_upper’]])

sdf.write.format(“delta”).mode(“overwrite”).save(

f"/data/delta/ts-spark_ch9_gold_forecast_{_vars[‘runid’]}")

print(f"forecast:\n${forecast.tail(30)}")

mlflow.end_run()

return _vars


 We first load the model from the model registry, where it was stored by the previous task, `t3`. This model can then be used for forecasting in the current task.
In this example, we want to generate a forecast for 365 days in advance by calling the following:

*   The `make_future_dataframe` function to generate the future period
*   The `predict` function to forecast for these future times

Another approach to generating the future period is to get this as input from the user or another application calling the model. As for the 365-day forecasting horizon, this is a relatively long time to forecast. We discussed in the *Forecasting* section of *Chapter 2* how a shorter forecasting horizon is likely to yield better forecasting accuracy. We have used a long period in this example for practical reasons to showcase forecasting over a 5-year period, with 5 runs of 365 days each. These runs were explained in the earlier *Simulation and runs* section. Moving beyond the requirement of the example here, keep the forecasting horizon shorter relative to the span of the training dataset and the level of granularity.
Finally, in this task, we persist the forecasted data with the `write` function to disk storage in `delta` format for use by the next steps of the workflow. We call this data stage `gold` as per the medallion approach explained previously. This delta table, where the forecasting outcome is stored, is also known as the inference table.
With the forecasts persisted, we can move on to the model evaluation stage.
Model evaluation
This stage corresponds to Airflow tasks `t5`, `t6`, and `t7`, which call the `ingest_eval_data`, `transform_eval_data`, and `eval_forecast` functions respectively.
Note
In a production environment, we want to monitor the accuracy of our model’s forecast against real data so that we can detect when the model is not accurate enough and needs retraining. In the example here, we have these tasks in the same workflow as the prior forecasting task to keep the example simple enough to fit within this chapter. These tasks will be a separately scheduled workflow, which will be executed a posteriori of the event being forecasted. In the example, we are simulating the post-event evaluation by using the data points following the training data.
The `ingest_eval_data` and `transform_eval_data` functions are very similar to the `ingest_train_data` and `transform_train_data` functions, which we have seen in the previous sections. The main difference, as the name suggests, is that they operate on the evaluation and training data respectively.
We will focus on the `eval_forecast` function in this section, with the code extract as follows:

def eval_forecast(_vars, **kwargs):

sdf = spark.read.format(“delta”).load(

f"/data/delta/ts-spark_ch9_silver_eval_{_vars[‘runid’]}")

sdf_forecast = spark.read.format(“delta”).load(

f"/data/delta/ts-spark_ch9_gold_forecast_{_vars[‘runid’]}")

sdf_eval = sdf.join(sdf_forecast, ‘ds’, “inner”)

evaluator = RegressionEvaluator(

labelCol=‘y’, predictionCol=‘yhat’, metricName=‘rmse’)

eval_rmse = evaluator.evaluate(sdf_eval)

mlflow.set_experiment(‘ts-spark_ch9_data-ml-ops_time_series_prophet_eval’)

mlflow.start_run()

mlflow.log_param(“DAG_NAME”, DAG_NAME)

mlflow.log_param(“EVAL_START_DATE”, _vars[‘START_DATE’])

mlflow.log_metric(‘eval_rmse’, _vars[‘eval_rmse’])

mlflow.end_run()

return _vars


 In this code example, we do the following:

1.  We first read the evaluation data from `silver`, where it was stored by the previous task, `t6`. We also read the forecasted data from `gold`, where it was stored earlier by the forecasting task, `t4`. We join both datasets with the `join` function so that we can compare the forecasts to the actuals.
2.  In this example, we use `RegressionEvaluator` from the `pyspark.ml.evaluation` library is used to do the calculation.
3.  As a final step, MLflow Tracking Server is used to save all the parameters and metrics for each run. We group them under an experiment called `ts-spark_ch9_data-ml-ops_time_series_prophet_eval` and use the `log_param` and `log_metric` functions to capture the parameters and metrics gathered so far in the run.

We can see the task’s status in the DAG view in Airflow, as per *Figure 9**.1*. The logged parameters and metrics are visible in MLflow Tracking Server, as shown in *Figure 9**.6*.
<https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_09_06.jpg>

Figure 9.6: MLflow experiment tracking (evaluation)
As with the training experiment tracking shown in *Figure 9**.4*, we can see in the evaluation experiment in *Figure 9**.6* the ingest, transform, and forecast data counts, as well as the evaluation RMSE.
With this concluding the model evaluation stage, we have seen the end-to-end workflow example. In the next section, we will cover the monitoring and reporting part of the example.
Monitoring and reporting
The workflows we covered in the previous section are the backend processes in our end-to-end time series analysis example. In this section, we will cover the operational monitoring of the runs and the end user reporting of the forecasting outcome.
Monitoring
The work to collect the metrics has been done as part of the code executed by the workflows we have seen in this chapter. Our focus in this section is on the visualizations to monitor the workflows and the metrics.
Workflow
Starting with the workflow, as we have seen in *Figures 9.1* and *9.2*, the Airflow DAG shows the status of the runs. In case a task fails, as shown in *Figure 9**.7*, we can select the failed task in Airflow and inspect the event log and logs to troubleshoot.
<https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_09_07.jpg>

Figure 9.7: Airflow DAG with failed task
Training
We can visualize the training metrics for a specific run in MLflow Tracking Server, as shown in *Figure 9**.4*. We can also monitor the metrics across multiple runs and compare them in a table, as per *Figure 9**.8*.
<https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_09_08.jpg>

Figure 9.8: MLflow experiments (training) – select and compare
By selecting the five runs of our `_runall` workflow and clicking on the **Compare** button as in *Figure 9**.8*, we can create a scatter plot as per *Figure 9**.9*. This allows us to see the details for a specific run as well by hovering the mouse pointer over a data point in the graph.
<https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_09_09.jpg>

Figure 9.9: Training – plot by runs with details
An interesting metric to monitor is the count of training data transformed and ready for training, as per *Figure 9**.10*. We can see here that the first four runs had fewer data points for training than the last run.
<https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_09_10.jpg>

Figure 9.10: Training – transform count by runs
We can similarly monitor the RMSE for each training run, as per *Figure 9**.11*. We can see here that the model accuracy has improved (lower RMSE) in the last two runs. If the accuracy had dropped instead, then the question from an operational point of view would have been whether this drop is acceptable or there is a need to develop another model. In this situation, this decision is dependent on your specific requirement and what was agreed as an acceptable drop in accuracy.
<https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_09_11.jpg>

Figure 9.11: Training – RMSE by runs
After the model has been trained and is used for forecasting, we can evaluate the model’s forecasted values against actuals. We will cover the monitoring of the evaluation metrics next.
Evaluation
We can visualize the evaluation metrics for a specific run in MLflow Tracking Server, as shown in *Figure 9**.6*. We can also monitor the metrics across multiple runs and compare them in a table, as per *Figure 9**.12*.
<https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_09_12.jpg>

Figure 9.12: MLflow experiments (evaluation) – select and compare
By selecting the five runs of our `_runall` workflow and clicking on the **Compare** button as in *Figure 9**.12*, we can create a scatter plot as per *Figure 9**.13*. This allows us to also see the details for a specific run by hovering the mouse pointer over a data point in the graph.
<https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_09_13.jpg>

Figure 9.13: Evaluation – RMSE by runs with details
An interesting metric to monitor is the count of forecasted data points, as per *Figure 9**.14*. We can see here that all the runs had the expected number of data points, except the fourth run, having one less. This can be explained by the fact that the evaluation dataset missed one data point during this time period.
<https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_09_14.jpg>

Figure 9.14: Evaluation – forecast count by runs
We can similarly monitor the RMSE for each evaluation run, as per *Figure 9**.13*. We can see here that the model accuracy dropped gradually (higher RMSE) until the fourth run and then improved in the last run. If the drop had persisted instead, the question from an operational point of view would have been whether this drop is acceptable or there is a need to develop another model. This decision is dependent on your specific requirement and what has been agreed as an acceptable drop in accuracy.
This concludes the section on monitoring. While using MLflow was sufficient for the example here, most organizations have dedicated monitoring solutions into which MLflow metrics can be integrated. These solutions also include alerting capabilities, which we have not covered here.
We have explored the process to reach an outcome so far, but have not seen the outcome yet. In the next section, we will report on the forecasting outcome.
Reporting
We will use a Jupyter notebook in this example to create a set of graphs to represent the forecasting outcome. The `ts-spark_ch9_data-ml-ops_results.ipynb` notebook can be accessed from the local web location, as shown in *Figure 9**.15*. This Jupyter environment was deployed as part of the *Environment* *setup* section.
<https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_09_15.jpg>

Figure 9.15: Reporting – notebook to create a graph
After running the notebook, we can see at the end of the notebook the graph, as per *Figure 9**.16*, of the forecasts (gray lines) and actuals (scatter plot) for the different runs. The forecast captures the seasonality well, and most of the actuals fall within the uncertainty intervals, which are set at 80% by default on Prophet.
<https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_09_16.jpg>

Figure 9.16: Reporting – actuals (scatter plot) compared to forecasts (gray lines)
We can zoom into specific runs as per *Figures 9.17* and *9.18*. These match with the RMSE values we saw in the earlier *Monitoring* section, as we will detail next.
As we can see in *Figure 9**.13*, the first run had the lowest RMSE. This is reflected in *Figure 9**.17*, with most actuals falling within the forecasting interval.
<https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_09_17.jpg>

Figure 9.17: Reporting – actuals compared to forecasts (run with lowest RMSE)
In *Figure 9**.13*, the fourth run had the highest RMSE. This is reflected in *Figure 9**.18*, with many more actuals than in the first run falling outside the forecasting interval.
<https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_09_18.jpg>

Figure 9.18: Reporting – actuals compared to forecasts (run with highest RMSE)
At this point, the output of the Jupyter notebook can be exported as a report in several formats, such as HTML or PDF. While using Jupyter was sufficient for the example here, most organizations have reporting solutions into which the forecasting outcome can be integrated.
Additional considerations
We will discuss here some of the additional considerations that apply when going to production, in addition to what we already covered in the example in this chapter.
Scaling
We covered scaling extensively in *Chapter 8*. The environment and workflows in this chapter can be scaled as well. At a high level, this can be achieved in the following ways:

*   Airflow server: scale up by adding more CPU and memory resources
*   Airflow DAG: run the tasks in parallel
*   Spark cluster: scale up by adding more CPU and memory resources
*   Spark cluster: scale out by adding more workers
*   Model: use Spark-enabled models or parallelize the use of pandas, as discussed in the previous chapter

You can find more information about Airflow DAGs, including parallel tasks, here: [`airflow.apache.org/docs/apache-airflow/stable/core-concepts/dags.html`](https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dags.html)
To relate this to our Airflow DAG example in this chapter, we defined the tasks as sequential in the following way in the code for `ts-spark_ch9_data-ml-ops_runall`:

t1 >> t2 >> t3 >> t4 >> t5


 The code to run tasks `t3` and `t4` in parallel is as follows:

t1 >> t2 >> [t3, t4] >> t5


 With regard to the considerations for scaling the Spark cluster, refer to *Chapter 3* and, more specifically, the *Driver and worker nodes* section, for a detailed discussion.
Model retraining
We already included retraining in our example workflow at every run using a sliding window of the most recent data. In practice, and to optimize resource utilization, the retraining can be scheduled at a less frequent interval in its own separate workflow. We discussed tracking the model’s accuracy metrics across runs of the workflow in the *Monitoring* section. The trigger of this retraining workflow can be based on the accuracy dropping below a predefined threshold. The appropriate value for the threshold depends on your specific requirements.
Governance and security
In *Chapter 4*, in the *From DataOps to ModelOps to DevOps* section, we discussed the considerations for governance and security at various points. Securing your environment and production rollout is beyond the scope of this book. As these are key requirements, and we will not be going into further details here, we highly recommend referring to the following resources to secure the components used in our example:

  **Apache Spark**
 |
  [`spark.apache.org/docs/latest/security.html`](https://spark.apache.org/docs/latest/security.html)
 |

  **MLflow**
 |
  [`mlflow.org/docs/latest/auth/index.html`](https://mlflow.org/docs/latest/auth/index.html)
[`github.com/mlflow/mlflow/security`](https://github.com/mlflow/mlflow/security)
 |

  **Airflow**
 |
  [`airflow.apache.org/docs/apache-airflow/stable/security/index.html`](https://airflow.apache.org/docs/apache-airflow/stable/security/index.html)
 |

  **Jupyter**
 |
  [`jupyter.org/security`](https://jupyter.org/security)
 |

  **Docker**
 |
  [`www.docker.com/blog/container-security-and-why-it-matters/`](https://www.docker.com/blog/container-security-and-why-it-matters/)
 |

  **Unity Catalog**
 |
  [`www.unitycatalog.io/`](https://www.unitycatalog.io/)
 |

Table 9.1: Resources on security and governance for components in use
This concludes the section on the additional considerations before going to production.
Summary
In this chapter, we focused on the crucial phase of moving projects into production, especially given the challenges many projects face in achieving this transition and delivering measurable business results. We saw an example of an end-to-end workflow, covering the stages of data ingestion, storage, data transformations, model training and validation, forecasting, model evaluation, and monitoring. With this example, we brought together what we have learned in this book in view of planning for a production rollout.
In the next chapter, we will explore how to go further with Apache Spark for time series analysis by leveraging the advanced capabilities of a managed cloud platform for data and AI.
Join our community on Discord
Join our community’s Discord space for discussions with the authors and other readers:
[`packt.link/ds`](https://packt.link/ds)
<https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/ds_(1>.jpg)

第十章:进一步了解 Apache Spark

在上一章中,我们利用开源组件将时间序列分析投入生产。这需要大量的努力来设置和管理平台。在本章中,我们将通过使用 Databricks 作为基于云的托管平台即服务PaaS)解决方案,进一步利用 Apache Spark。我们将使用一个基于 Databricks 构建的端到端时间序列分析示例,采用先进的功能,如 Delta Live Tables 结合流处理管道、AutoML、Unity Catalog 和 AI/BI 仪表盘。

本章将介绍以下主要内容:

  • Databricks 组件和设置

  • 工作流

  • 监控、安全性和治理

  • 用户界面

技术要求

在本章中,我们将通过代码示例,探索如何在 Databricks 上部署一个可扩展的端到端时间序列分析解决方案,从下一节的环境设置开始。

本章的代码位于以下网址:

github.com/PacktPublishing/Time-Series-Analysis-with-Spark/tree/main/ch10

Databricks 组件和设置

我们将使用 Databricks 环境,如在第八章中所述,作为平台基础设施。请按照第八章中的技术要求部分的说明来设置 Databricks 环境。

工作区、文件夹和笔记本

环境设置完成后,请按照此处提供的链接中的说明导入笔记本:

  1. 浏览 Databricks 工作区:docs.databricks.com/en/workspace/index.html

  2. 创建一个名为ts_spark的文件夹,以及一个名为ch10的子文件夹:docs.databricks.com/en/workspace/workspace-objects.html#folders

  3. 将本示例的笔记本导入到ch10文件夹中:docs.databricks.com/en/notebooks/notebook-export-import.html#import-a-notebook

    总共有八个笔记本,它们可以从以下网址导入:

导入笔记本后,我们可以接下来设置集群。

集群

我们可以为集群使用 Databricks 机器学习运行时MLR)或无服务器计算。

MLR 集群预加载了用于机器学习ML)的常见库。它在你的云提供商账户中实例化虚拟机。云提供商将向你收取虚拟机的费用。在创建集群时,选择一个小型实例,配备最小的 CPU 和内存,以最小化此费用。对于本章的示例,这样就足够了。请参阅第八章中关于设置 Databricks 集群的技术要求部分。

MLR 集群具有 AutoML 示例所需的库,我们将在后续部分中讲解。如果你不想为相关虚拟机产生 MLR 相关的云提供商费用,可以跳过该示例的代码执行。我们将提供一个不使用 AutoML 的替代工作流。

注意

在撰写本文时,这些虚拟机的云提供商费用已超出免费云提供商试用账户提供的免费额度。这意味着你需要升级到付费云提供商账户,费用将通过你在创建账户时指定的信用卡进行扣费。虚拟机和云基础设施的费用并不包含在免费的 Databricks 试用账户内。

无服务器集群已包含在您的 Databricks 费用中,因为底层虚拟机由 Databricks 完全管理。这意味着它不会产生额外的云服务商费用。然而,无服务器集群在写作时需要安装 ML 库,正如您在代码示例中所看到的。未来,Databricks 可能会提供预加载 ML 库的无服务器集群。

注意

Databricks 已开始在写作时将无服务器功能包含在免费试用账户中。这意味着如果在 Databricks 免费试用账户的时间和费用限制内,您使用无服务器集群执行本章代码将是免费的。未来此政策可能会有所变化。

您可以在以下资源中找到更多关于 MLR 和无服务器集群的信息:

在涵盖集群之后,我们将接下来使用 Delta Live Tables 配置数据管道。

使用 Delta Live Tables 进行流处理

Databricks Delta Live TablesDLT)是一个低代码声明式的数据管道构建解决方案。在我们的示例中,我们将使用 DLT 构建特征工程管道,从源文件获取数据,检查数据质量,并将其转换为可以用于训练时间序列模型的特征。您可以在以下链接中找到更多关于 DLT 的信息:

www.databricks.com/discover/pages/getting-started-with-delta-live-tables

我们将在实施 工作流部分深入探讨 DLT 配置的细节。

工作流

Databricks 工作流相当于我们在第四章第九章中使用的 Airflow DAGs。您可以在以下链接中找到更多关于工作流,也称为任务的信息:

docs.databricks.com/en/jobs/index.html

我们接下来将深入探讨任务配置的细节。

实施工作流

本章的代码示例包括四个工作流。这些工作流在 Databricks 中作为任务实现。查看任务的最佳方式是通过 Databricks 中的工作流 > 任务 > 任务视图,参考图 10.110.210.310.4

任务如下:

  • ts-spark_ch10_1a_ingest_and_train – 该任务用于数据摄取、特征工程和模型训练,显示在图 10.1中。它包括以下任务:

    • reset

    • dlt_features

    • model_training

https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_10_01.jpg

图 10.1:数据摄取、特征工程和模型训练任务

  • ts-spark_ch10_1b_ingest_and_train_automl – 第二个作业,如图 10**.2所示,是第一个作业的另一个版本,区别在于使用了 AutoML,具体内容将在使用 AutoML 训练部分进行解释。

https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_10_02.jpg

图 10.2:数据摄取、特征工程和模型训练(AutoML)作业

  • ts-spark_ch10_2b_ingest_and_forecast – 该作业用于摄取新数据、重新训练模型,并生成和评估预测,如图 10**.3所示。它包括以下任务。

    • dlt_features

    • update_model

    • generate_forecast

    • update_data

    • evaluate_forecast

https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_10_03.jpg

图 10.3:摄取新数据、重新训练模型并生成预测作业

  • ts-spark_ch10_2a_update_iteration – 如图 10**.4所示,该作业多次调用前一个作业以摄取新数据。它模拟了现实世界中的情境,即以固定时间间隔(例如每日或每周)启动前一个端到端工作流,并处理新数据。

https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_10_04.jpg

图 10.4:多次调用摄取和处理新数据作业

模块化和任务分离

第九章一样,我们将作业拆分为多个任务,以展示模块化的最佳实践。这有助于独立修改代码、扩展和任务重跑。任务的所有权可以由不同的团队负责。根据您的需求,您可以进一步拆分这些作业,以便分别启动任务。

我们将在接下来的章节中详细解释每个作业和相关任务,从摄取和训练作业开始。

要设置本章所需的作业,请按照以下链接中的说明创建作业并配置任务:

请参考接下来的章节中的表格,查看创建作业和相关任务时的配置,并将<USER_LOGIN>替换为您自己的 Databricks 用户登录。

摄取和训练

ts-spark_ch10_1a_ingest_and_train作业,如图 10**.1所示,将在本节中详细介绍。

表 10.1 显示了ts_spark_ch10_1a_ingest_and_train作业的配置,您可以在之前提供的 URL 中按照说明使用。请注意,为简便起见,我们给每个任务起了与其运行的代码笔记本或管道相同的名称。

作业ts_spark_ch10_1a_ingest_and_train
任务 1任务名称
类型
来源
路径(笔记本)
计算
任务 2任务名称
类型
管道
触发管道的完整刷新
依赖
任务 3任务名称
类型
来源
路径(笔记本)
计算
依赖

表 10.1:作业配置 – ts_spark_ch10_1a_ingest_and_train

reset

reset 任务执行以下操作:

  • 重置 Databricks 目录 ts_spark,该目录用于本示例

  • 从 GitHub 下载本章节的数据文件到 ts_spark 目录下创建的卷

此任务的代码位于 ts_spark_ch10_reset 笔记本中。

目录和卷

Databricks 的 Unity Catalog 提供数据治理和管理功能。它将数据组织成三级层次结构:目录、模式(相当于数据库)以及表、视图或卷。表格数据存储在表和视图中,而文件存储在卷中。在我们的代码示例中,我们使用一个单独的目录 ts_spark 和卷来存储数据文件。

dlt_features

此任务用于数据摄取和特征工程。它实现为 ts_spark_ch10_dlt_features DLT 管道,如图 10.5所示。

https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_10_05.jpg

图 10.5:特征工程管道

您可以在这里查看并放大图 10.5的数字版本:packt.link/D9OXb

要设置本章节所需的 DLT 管道,请按照以下链接中的指示创建管道:

docs.databricks.com/en/delta-live-tables/configure-pipeline.html#configure-a-new-delta-live-tables-pipeline

请注意,您需要先创建 ts_spark 目录,才能设置 DLT 管道。请参阅以下说明,通过目录浏览器创建 ts_spark 目录:docs.databricks.com/aws/en/catalogs/create-catalog?language=Catalog%C2%A0Explorer

创建管道时,请参考表 10.2中的配置,并将 <USER_LOGIN> 替换为您自己的 Databricks 用户登录。

管道ts_spark_ch10_dlt_features
通用管道名称
无服务器
管道模式
源代码路径(笔记本)
目标存储选项
默认目录 / 默认模式

表 10.2:DLT 配置 – ts_spark_ch10_dlt_features

此管道任务的代码位于ts_spark_ch10_dlt_features笔记本中,包含以下步骤:

  1. 使用 Auto Loader 从vol01_hist卷中的文件读取历史数据,检查数据,并将数据存储在raw_hist_power_consumption流表中。

Auto Loader

Databricks Auto Loader,也称为代码中的cloudfiles,可以高效地增量导入到达云存储位置的新数据文件。你可以通过以下链接了解更多关于 Auto Loader 的信息:docs.databricks.com/en/ingestion/cloud-object-storage/auto-loader/index.html

数据质量检查

Databricks DLT 可以包括数据质量检查,以确保数据管道中的数据完整性,基于质量约束进行校验。你可以通过以下链接了解更多关于 DLT 中数据质量检查的信息:docs.databricks.com/en/delta-live-tables/expectations.html

  1. 使用 Auto Loader 从vol01_upd卷中的文件读取更新数据,检查数据,并将数据存储在raw_upd_power_consumption流表中。

  2. raw_hist_power_consumption流表中读取原始历史数据,转换数据,并将结果存储在curated_hist_power_consumption流表中。

  3. raw_upd_power_consumption流表中读取原始更新数据,转换数据,并将结果存储在curated_upd_power_consumption流表中。

  4. curated_hist_power_consumptioncurated_upd_power_consumption流表中追加数据,并将合并结果存储在curated_all_power_consumption流表中。

  5. curated_all_power_consumption流表中读取整理后的数据,使用 Tempo 计算features_aggr_power_consumption物化视图。

Tempo

Databricks Tempo 是一个开源项目,简化了在 Apache Spark 中处理时间序列数据。你可以通过以下链接了解更多关于 Tempo 的信息:databrickslabs.github.io/tempo/

  1. features_aggr_power_consumption物化视图中读取汇总数据,使用 Tempo 与curated_all_power_consumption流表进行AsOf连接。然后,将结果存储在features_gnlr_power_consumption物化视图中。

这些步骤对应于“金银铜”方法中的数据转换阶段,这部分在第四章数据处理和存储章节中进行了讨论。

model_training

此任务用于训练一个 Prophet 模型,使用在之前dlt_features任务中计算的特征。model_training的代码位于ts_spark_ch10_model_training笔记本中。步骤如下:

  1. features_aggr_power_consumption读取特征。

  2. Date列重命名为ds,并将hourly_Global_active_power重命名为y。这些列名是 Prophet 所要求的。

  3. 启动一个 MLflow 运行以追踪训练过程。

  4. 将 Prophet 模型拟合到数据集。

  5. 将模型注册到 Unity Catalog,并将别名设置为Champion

请注意,这个笔记本展示的是简化的模型训练,足以说明本章示例中的训练步骤。它没有包括完整的模型实验过程和超参数调整,这部分内容在第七章中有所讨论。

使用 AutoML 进行训练

另一种模型训练方法是使用 Databricks AutoML 来为给定的数据集找到最佳模型。

AutoML 是 Databricks 中的一项功能,自动化了机器学习模型开发的过程。它包含数据分析、特征工程、模型选择和超参数调整等任务。此功能使用户能够快速生成回归、分类和预测问题的基线模型。通过其“玻璃盒”方法,AutoML 提供每个模型的底层代码,这与不显示代码细节的“黑盒”方法不同。AutoML 可以通过 UI 使用,如图 10.6所示,也可以通过编程方式使用,如本章提供的示例所示。

https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_10_06.jpg

图 10.6:Databricks AutoML

你可以在此处找到有关 AutoML 的更多信息:

www.databricks.com/product/automl

ts-spark_ch10_1b_ingest_and_train_automl作业是如何在训练任务中以编程方式包括 AutoML 的示例。此任务的代码位于ts_spark_ch10_model_training_automl笔记本中。步骤如下:

  1. features_aggr_power_consumption读取特征。

  2. 调用databricks.automl.forecast函数,它负责重命名列、启动一个 MLflow 运行以追踪训练过程,并根据指定的primary_metric(示例中使用的是mdape)找到最佳的预测模型。

  3. 将模型注册到 Unity Catalog,并将别名设置为Champion

ts_spark_ch10_1b_ingest_and_train_automl作业的配置见表 10.3

任务(可选)ts_spark_ch10_1b_ingest_and_train_automl
任务 1任务名称
类型
来源
路径(笔记本)
计算
任务 2任务名称
类型
管道
触发管道的完整刷新
依赖于
任务 3任务名称
类型
来源
路径(笔记本)
计算
依赖于

表 10.3:作业配置 - ts_spark_ch10_1b_ingest_and_train_automl

请注意,除了简化与之前不使用 AutoML 的训练方法步骤之外,我们还可以找到最佳模型。

导入和预测

ts-spark_ch10_2b_ingest_and_forecast 作业,如 图 10.3 所示,将在本节中详细介绍。

ts_spark_ch10_2b_ingest_and_forecast 作业的配置如 表 10.4 所示。

作业ts_spark_ch10_2b_ingest_and_forecast
作业参数
任务 1任务名称
类型
管道
触发管道的完全刷新
任务 2任务名称
类型
来源
路径(笔记本)
计算
依赖于
任务 3任务名称
类型
来源
路径(笔记本)
计算
依赖于
任务 4任务名称
类型
来源
路径(笔记本)
计算
依赖于
任务 5任务名称
类型
来源
路径(笔记本)
计算
依赖于

表 10.4:作业配置 - ts_spark_ch10_2b_ingest_and_forecast

dlt_features

该任务与前面 导入和训练 部分中使用的 ts_spark_ch10_dlt_features DLT 管道相同,如 图 10.5 所示,只不过这次我们将调用该管道来处理来自 vol01_upd 卷的新数据文件。

update_model

该任务用于使用先前 dlt_features 任务中计算的特征来训练 Prophet 模型。update_model 的代码位于 ts_spark_ch10_update_model 笔记本中。该任务与 模型训练 部分中讨论的任务类似,唯一的区别是我们现在有了新数据来包含在训练中。步骤如下:

  1. features_aggr_power_consumption 中读取特征。

  2. Date 列重命名为 ds,并将 hourly_Global_active_power 列重命名为 y。这些列名是 Prophet 所要求的。

  3. 将 Prophet 模型拟合到数据集。

  4. 将模型注册到 Unity Catalog,并设置别名为Champion

更新完最新模型后,我们可以使用它进行下一步的预测。

generate_forecast

该任务使用之前训练好的模型来生成和存储预测结果。generate_forecast的代码位于ts_spark_ch10_generate_forecast笔记本中。步骤如下:

  1. 从 Unity Catalog 加载Champion模型。

  2. 为接下来的 24 小时生成预测。

  3. 将预测结果与模型的名称和版本一起存储在forecast表中。

在生成预测后,我们可以将预测的时间段与实际数据进行比较,实际数据将在接下来获取。

update_data

该任务只是将新的时间段的数据文件从vol01_upd_src卷复制到vol01_updupdate_data的代码位于ts_spark_ch10_update_data笔记本中。

evaluate_forecast

该任务计算并存储预测准确度指标。evaluate_forecast的代码位于ts_spark_ch10_evaluate_forecast笔记本中。步骤如下:

  1. features_aggr_power_consumption实际数据表与之前创建的forecast表连接。

  2. 计算mdape指标。

  3. 将计算得到的指标与模型的名称和版本一起存储在forecast_metrics表中。

  4. 将数据质量检查结果存储在dq_results表中。

在评估完预测后,我们可以报告结果和指标。我们将在用户界面部分介绍这一部分内容。在进入这部分之前,先详细说明如何协调多个新数据到达并进行相应处理的迭代过程。

更新迭代

ts-spark_ch10_2a_update_iteration作业,如图 10.4所示,模拟了现实中我们在定期时间间隔(如每天或每周)处理新数据的情况。它调用ts-spark_ch10_2b_ingest_and_forecast作业七次,对应一周的每日新数据。每次调用都会触发一个新的数据文件的端到端处理,如前面的获取和预测部分所描述。

ts_spark_ch10_2a_update_iterations作业的配置见表 10.5

作业ts_spark_ch10_2a_update_iterations
任务 1任务名称
类型
输入
任务 2(添加一个任务以 循环遍历)任务名称
类型
作业
作业参数

表 10.5:作业配置 – ts_spark_ch10_2a_update_iterations

启动作业

配置并解释完作业后,我们将启动这些作业,这些作业将执行本章的代码。有关运行作业的更多信息,请参见:

docs.databricks.com/en/jobs/run-now.html

按照以下顺序进行操作:

  1. 点击ts-spark_ch10_1a_ingest_and_train。等待任务完成。

  2. 点击ts-spark_ch10_2a_update_iteration

在启动并执行作业后,我们可以查看它们的状态,下一节将对此进行详细解释。

监控、安全和治理

正如我们在第四章从 DataOps 到 ModelOps 再到 DevOps部分和第九章治理与安全部分中讨论的那样,生产环境和涉及敏感数据的工作负载的关键要求是必须具备适当的监控、安全和治理。这通过利用像 Databricks 与 Unity Catalog 这样的托管平台的内置功能得到极大的促进。如果我们开发和测试自己定制的平台,替代方法将需要相当多的时间和精力,才能稳健地满足这些要求。

监控

可以通过ts-spark_ch10_2b_ingest_and_forecast任务来进行作业监控。我们可以查看不同的运行、它们的参数、持续时间、状态等信息,这些对于监控非常有用。

https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_10_07.jpg

图 10.7:Databricks Workflows – 作业 – 运行

ts_spark_ch10_dlt_features DLT 管道的监控可以通过Workflows > Pipelines页面完成,如图 10.8所示。我们可以看到不同的阶段、数据检查、持续时间和状态等信息,这些对于监控非常有用。

https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_10_08.jpg

图 10.8:Databricks DLT 管道

你可以在此处找到更多关于可观察性、监控和警报的信息:

安全

图 10.9所示,使用 Unity Catalog 设置表格及其他对象的访问权限只需要几次点击。

https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_10_09.jpg

图 10.9:Databricks Unity Catalog – 设置权限

还可以根据以下资源在表格内定义更加细粒度的访问控制,细化到行或列级别:

www.databricks.com/resources/demos/videos/governance/access-controls-with-unity-catalog

你可以在此处找到更多关于安全的信息:

docs.databricks.com/en/security/index.html

治理

治理的重要考虑因素之一是能够追踪数据资产的血缘关系,如图 10.10所示。我们可以看到数据的来源、多个中间阶段,以及数据存储的最终表格。Unity Catalog 会在 Databricks 中自动跟踪这一过程,让我们能够实时监控数据流。

https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_10_10.jpg

图 10.10:Databricks Unity Catalog – 血缘视图

你可以在这里找到并放大图 10.10的数字版本:

https://packt.link/D6DyC

我们仅简要提及了使用 Databricks Unity Catalog 进行治理和安全性。你可以在这里找到更多信息:

www.databricks.com/product/unity-catalog

了解如何利用 Databricks 这样的平台进行监控、安全性和治理后,我们将继续揭示如何展示时间序列分析的结果。

Databricks 用户界面 — AI/BI 仪表板

在展示我们迄今为止进行的时间序列分析结果时,Databricks 提供了多种用户界面的选项,包括 AI/BI 仪表板、Genie 空间、基于 AI 的聊天机器人和 Lakehouse 应用。我们将在本节中介绍 AI/BI 仪表板,其它选项将在下一章中讨论。

在本书中,我们广泛使用了各种图表来表示数据和分析结果。这要求我们在笔记本中执行代码来创建图表。当我们能够编写代码并拥有执行环境时,这种方式非常有效。然而,在无法编写代码的情况下,常见的展示数据和分析结果的方式是使用报告仪表板。Databricks AI/BI 仪表板便提供了这种功能,如图 10.11所示。

Databricks AI/BI 仪表板是一个集成到 Databricks 平台中的解决方案,用于创建报告和仪表板。它具备 AI 驱动的功能,帮助生成查询和数据可视化。仪表板可以发布并共享,供他人使用。

https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_10_11.jpg

图 10.11:Databricks AI/BI 仪表板

要在自己的环境中安装此仪表板,首先,下载它并从以下位置获取:

github.com/PacktPublishing/Time-Series-Analysis-with-Spark/blob/main/ch10/ts_spark_ch10.lvdash.json

然后,您可以按照这里的说明将仪表板文件导入到自己的环境中:

docs.databricks.com/en/dashboards/index.html#import-a-dashboard-file

注意

你需要一个 SQL 仓库来运行仪表板。请参考以下说明来创建 SQL 仓库:

docs.databricks.com/aws/en/compute/sql-warehouse/create

在这个仪表板中,我们将以下内容整合在一个视图中:

  • 实际值与预测值的图表

  • 通过数据质量检查的记录数(失败和通过)

  • 不同模型版本的指标

您可以通过以下链接找到更多关于 AI/BI 仪表板的信息:

总结

通过在托管的 Spark 平台上进行时间序列分析的端到端示例,本章展示了如何利用 Databricks 的开箱即用功能进一步推动 Apache Spark 的应用。我们从通过流处理管道进行数据摄取开始,到特征工程和模型训练,再到推理和报告,同时确保监控、安全性和治理得到了落实。通过将 Databricks 上预构建的功能与我们自己的自定义代码相结合,我们实现了一个可以扩展到更多使用场景的解决方案。

这将引导我们进入最后一章,在本章中,我们将扩展一些近期在时间序列分析中的发展。

加入我们的 Discord 社区

加入我们社区的 Discord 空间,与作者和其他读者进行讨论:

packt.link/ds

https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/ds_(1.jpg)

第十一章:时间序列分析的最新发展

当我们走到本书的最后一章时,让我们简要回顾一下我们走过的历程。从在第一章中介绍时间序列及其组成开始,我们在第二章中查看了时间序列分析的不同应用场景。接着,我们在第三章中介绍了 Apache Spark 及其架构,以及它是如何工作的。在深入探讨 Apache Spark 如何用于时间序列分析之前,我们在第四章中回顾了一个端到端的时间序列项目的整体框架。随后,我们将焦点转向项目的主要阶段,从第五章第九章,涵盖了数据准备、探索性数据分析、模型开发、测试、扩展和生产部署。在第十章中,我们讨论了通过使用如 Databricks 这样的托管数据和 AI 平台,如何进一步利用 Apache Spark。

在本章的结尾,我们将探讨时间序列分析领域的最新发展,涵盖新兴的方法论、工具和趋势。我们将介绍一种来自生成式 AI 领域的时间序列预测方法。拥有一个预测机制固然很棒,但还不够。另一个有趣的发展方向是如何通过 API 向数据分析师和应用程序提供并按需提供预测结果。最终用户也可以通过新的方法受益,使时间序列分析的结果以非技术性的方式对他们可访问。

在本章中,我们将讨论以下主要内容:

  • 时间序列的生成式 AI

  • 通过 API 提供预测

  • 民主化时间序列分析的访问

技术要求

我们将使用 Databricks 环境作为平台基础设施。要设置环境,请按照第十章环境设置部分的说明进行操作。

本章的代码可以在此 URL 找到:

github.com/PacktPublishing/Time-Series-Analysis-with-Spark/tree/main/ch11

时间序列分析中的生成式 AI

尽管传统的时间序列模型有效,但在大规模数据或复杂模式下,它们在性能和准确性上存在局限性。

生成式人工智能,特别是时间序列变换器TSTs),为这些挑战提供了解决方案。类似于自然语言处理NLP)中的 transformer 模型,TSTs 擅长捕捉长序列上的复杂、非线性依赖关系。这种能力使它们适用于包含缺失值、季节性和不规则模式的真实世界数据。TSTs 使用自注意机制分析时间序列数据并识别季节性模式。这些模型在庞大数据集上进行预训练以创建基础模型,然后可以针对特定时间序列应用进行微调。

最近,已发布了几个预构建的 TST,使我们能够利用它们的功能,而无需努力工程化这些解决方案。示例包括 Chronos、Moira、TimesFM 和 TimeGPT 等。

在下一节中,我们将研究如何使用其中之一与 TimesFM。

TimesFM 简介

TimesFM,简称时间序列基础模型,是由谷歌研究开发的开源预测模型,专门设计用于时间序列数据。TimesFM 建立在基于 transformer 的架构上,具有多功能性,可以处理从短期到长期预测的各种任务。与 Chronos 等将时间序列类似于自然语言处理的模型不同,TimesFM 包括针对时间序列数据的专门机制,如季节性处理、支持缺失值和捕捉多变量依赖关系。

在超过 1000 亿真实世界时间序列点上进行预训练,TimesFM 有效地推广到新数据集,通常在没有额外训练的情况下提供准确的零-shot 预测。这种广泛的预训练使 TimesFM 能够识别时间序列数据中的短期和长期依赖关系,使其非常适用于需要了解季节模式和趋势的应用程序。

要了解 TimesFM 架构的概述和详细解释,我们建议查阅原始研究论文,一种仅解码器的时间序列基础模型 预测,请点击这里:

research.google/blog/a-decoder-only-foundation-model-for-time-series-forecasting/

我们将在下一节中通过一个预测示例看到 TimesFM 的实际应用。

预测

在本节的时间序列预测示例中,我们将使用在技术要求部分设置的 Databricks 环境。本节的代码可以从以下 URL 上传到 Databricks 工作区:

github.com/PacktPublishing/Time-Series-Analysis-with-Spark/raw/main/ch11/ts_spark_ch11_timesFM.dbc

你可以使用 Databricks 无服务器计算来执行代码,正如我们在第十章中所做的那样。或者,你可以使用 Databricks Runtime for ML。由于 TimesFM 在撰写时支持的 Python 版本要求,必须使用 14.3 版本。

我们将在这里通过代码示例逐步讲解如何安装和使用 TimesFM。完整代码请参见笔记本:

  1. 安装以下必要的库:timesfm[torch]torchsktime

  2. 指定超参数(hparams),并从 Hugging Face 的检查点加载 TimesFM 模型(huggingface_repos_id)。请注意,500m指的是模型支持的 5 亿个参数,由于与 Databricks 的兼容性,我们将使用pytorch版本:

    # Initialize the TimesFm model with specified hyperparameters 
    # and load from checkpoint
    model = timesfm.TimesFm(
        hparams = timesfm.TimesFmHparams(
            backend="gpu",
            per_core_batch_size=32,
            horizon_len=128,
            num_layers=50,
            use_positional_embedding=False,
            context_len=2048,
        ),
        checkpoint = timesfm.TimesFmCheckpoint(
            huggingface_repo_id="google/timesfm-2.0-500m-pytorch"
        )
    )
    

    虽然我们使用了超参数的默认值,但你需要进行实验,根据你的预测需求找到最佳的超参数。

    加载 TimesFM 模型后,我们可以引入用于预测的数据集。我们将重新使用来自第十章的能源消耗数据集。在进行下一步之前,你必须执行代码示例,包括上一章中的特征工程管道。

  3. 我们将从features_aggr_power_consumption表中读取数据,并将 Spark DataFrame 转换为 pandas DataFrame,这是 TimesFM 所需要的。Date列重命名为date,并转换为模型所期望的datetime格式:

    # Define catalog, schema, and table names
    CATALOG_NAME = "ts_spark"
    SCHEMA_NAME = "ch10"
    ACTUALS_TABLE_NAME = f"{CATALOG_NAME}.{SCHEMA_NAME}.features_aggr_power_consumption"
    FORECAST_TABLE_NAME = f"{CATALOG_NAME}.{SCHEMA_NAME}.forecast"
    # Load data from the actuals table into a Spark DataFrame
    sdf = spark.sql(f"""
        SELECT * FROM {ACTUALS_TABLE_NAME}
    """)
    # Convert Spark DataFrame to Pandas DataFrame
    df = sdf.toPandas()
    # Convert 'Date' column to datetime format
    df['get_batched_data_fn function. The important part, shown in the following code extract, is the mapping of inputs and outputs. The code is based on an adaptation of an example from TimesFM([`github.com/google-research/timesfm/blob/master/notebooks/covariates.ipynb`](https://github.com/google-research/timesfm/blob/master/notebooks/covariates.ipynb)):
    
    

    创建批量时间序列数据管道的函数

    数据

    def get_batched_data_fn(

    examples[“inputs”].append(

    sub_df[“hourly_Global_active_power”][

    start:(context_end := start + context_len)

    ].tolist())

    examples[“outputs”].append(

    sub_df[“hourly_Global_active_power”][

    context_end:(context_end + horizon_len)

    ].tolist())

    
    
  4. 然后,我们可以遍历输入数据的批次,使用forecast函数生成预测,代码示例如下:

    # Iterate over the batches of data
    for i, example in enumerate(input_data()):
        # Generate raw forecast using the model
        raw_forecast, _ = model.forecast(
            inputs=example["inputs"],
            freq=[0] * len(example["inputs"])
        )
    
  5. 我们使用mdape指标来评估预测效果,正如我们在前几章所做的那样。这与第十章类似:

    # Calculate and store the evaluation metric for the forecast
    metrics["eval_mdape_timesfm"].extend([
        mdape(
            pd.DavtaFrame(raw_forecast[:, :horizon_len]),
            pd.DataFrame(example["outputs"])
        )
    ])
    

    这将得到以下结果:

    eval_mdape_timesfm: 0.36983413500008916
    

正如我们在本节中所看到的,使用基于预训练的 Transformer 模型(如 TimesFM),并且使用默认的超参数,能够提供与我们在前几章中使用的不同方法相当的准确性。通过超参数调优和协变量的使用(下文将讨论),我们可以进一步提高准确性。

协变量支持

TimesFM 的一个重要特性是它支持外部协变量,因为时间序列很少是孤立出现的。经济指标或天气条件等多种因素可能与时间序列相关联,将这些因素纳入分析可以提高预测的准确性。简单来说,协变量是一个独立的变量,可以帮助我们预测时间序列。

TimesFM 支持单变量和多变量预测,包含协变量,使其能够捕捉目标序列与这些外部变量之间的相关性。通过将协变量作为并行序列输入,模型可以学习它们与未来值之间的关系,从而增强其在外部因素对结果产生显著影响的实际场景中的适应性。例如,我们可以通过估算道路交通来预测污染水平。这一支持协变量的能力使得 TimesFM 在预测上相较于传统时间序列模型和其他不包含这些变量的基础模型具有优势。

您可以在这里找到有关协变量支持的更多信息和示例:

community.databricks.com/t5/technical-blog/genai-for-time-series-analysis-with-timesfm/ba-p/95507

其他生成式 AI 模型和多模型预测

您可以测试其他生成式模型,以找到最适合您用例的模型。一种方法是使用 Databricks 的 多模型预测MMF)解决方案加速器。该加速器为需要在多个时间序列上创建预测的组织提供了解决方案,例如销售、需求或库存预测。该仓库提供了一个可扩展的方法,使用 Databricks 同时部署和管理多个预测模型。它包括笔记本、模型模板和数据管道等资源,简化了在大规模上训练、评估和部署时间序列模型的过程。

您可以在此找到更多信息:

github.com/databricks-industry-solutions/many-model-forecasting

随着生成式 AI 和 MMF 成为我们时间序列分析工具包的一部分,让我们探索如何增强预测结果对应用和数据分析师的可用性。

通过 API 提供预测

本书的主要部分集中在准备和分析时间序列数据集上。我们还涵盖了如何在笔记本和报告仪表板中以表格和图形的形式呈现分析结果。然而,在许多情况下,预测必须按需提供给数据分析师和应用程序。我们现在将探讨如何实现这一目标。

通过 ai_forecast 简化预测

在这种情况下,数据分析师可以访问时间序列数据,并希望将其作为输入来获取预测,而无需首先开发一个模型。通过将预测功能抽象为 Databricks 平台上的 ai_forecast 函数,可以大大简化没有预测模型和算法知识的用户进行预测的过程。

您可以在以下 URL 查看一个简单示例:

github.com/PacktPublishing/Time-Series-Analysis-with-Spark/raw/main/ch11/ts_spark_ch11_aiforecast.dbc

这段代码基于文档中的示例,链接在本节末尾提供:

SELECT *
FROM AI_FORECAST(
    TABLE(aggregated),
    horizon => '2016-03-31',
    time_col => 'ds',
    value_col => 'revenue'
)

运行此示例的输出如图 11.1所示。

https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_11_1.jpg

图 11.1:ai_forecast 示例输出

您可以在此处找到并放大图 11.1的数字版本:

https://packt.link/vg87q

请注意,在撰写本文时,此功能仍处于公开预览阶段,因此您可能需要向 Databricks 请求访问权限才能试用它。

您可以在此处找到更多信息:

docs.databricks.com/en/sql/language-manual/functions/ai_forecast.html

虽然像ai_function这样简化且预定义的函数是快速生成预测的好方法,但我们可能希望使我们自己定制开发的预测模型能够轻松供其他应用访问,接下来我们将介绍如何做到这一点。

模型服务

在某些情况下,我们需要从另一个应用程序中以编程方式获取模型的预测。对于这种应用间集成,使用 REST API 是一种常见做法。提供 REST API 接口的一种方式是使用 Databricks 的模型服务

Databricks 的模型服务提供了部署、管理和查询 ML 和 AI 模型的功能,支持实时推理和批量推理。已部署的模型可以通过 REST API 访问,从而集成到 Web 或客户端应用程序中。支持多种模型类型,包括以 MLflow 格式打包的自定义 Python 模型和提供的开放基础模型。该服务旨在高可用性和低延迟,并能自动扩展以应对需求变化。

这是提供模型服务的步骤概览。请注意,这不是一个实际的示例。此处展示的截图仅用于说明步骤:

  1. 按照图 11.2中的步骤访问 Unity Catalog 中的模型,并点击右上角的服务此模型按钮。

https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_11_2.jpg

图 11.2:Unity Catalog 中的模型

  1. 按照图 11.3中的步骤创建服务端点。此时将显示访问 REST API 以调用模型的 URL。

https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_11_3.jpg

图 11.3:创建服务端点

  1. 创建服务端点时,我们可以启用推理表,如图 11.4所示,以存储与模型 REST API 交互的所有输入和输出。

https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_11_4.jpg

图 11.4:推理表

  1. 创建后,服务端点将显示为准备就绪状态,如图 11.5所示,并可以使用。

https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_11_5.jpg

图 11.5:服务端点已准备就绪

  1. 当使用服务端点时,可以根据图 11.6来监控其指标。

https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_11_6.jpg

图 11.6:服务端点指标

你可以在此找到有关模型服务的更多信息:

docs.databricks.com/en/machine-learning/serve-models.html

正如我们在本节中看到的,通过 REST API 暴露我们的时间序列分析模型,使得将分析与其他应用程序集成变得更加容易。继续讨论时间序列分析的可访问性,接下来我们将探讨如何为最终用户简化这一过程。

普及时间序列分析的访问

在本节中,我们将探索访问时间序列结果的创新方法如何使非技术用户受益。这使得我们能够将时间序列分析普及化,惠及更广泛的受众。

Genie 空间

在第一种方法中,我们将使用 Databricks 上类似自然语言聊天机器人的界面,称为 Genie 空间

Databricks Genie 空间是一个对话式 UI,使业务用户能够用自然语言提问并获得分析见解,而无需技术专长。这通过配置 Genie 空间与相关数据集、示例查询和说明来实现。然后,用户可以用自然语言与系统互动,提出关于数据的问题和可视化需求。Genie 使用带注释的表格和列元数据将用户查询转化为 SQL 语句。这些语句用于查询数据,以便 Genie 可以向用户提供响应。

为了实践这一点,我们将使用在 第十章 中创建的仪表板,如 图 11.7 所示。这是访问 Genie 的一种方式——在仪表板上,我们可以点击左上角的 询问 Genie 按钮。这将打开右下角的聊天机器人界面,我们可以开始用自然语言输入问题。或者,我们可以选择将 Genie 空间打开为全屏模式。

https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_11_7.jpg

图 11.7:从仪表板访问 Genie 空间

图 11.8 中,我们可以看到完整的 Genie 空间,包括查询、结果和用于获取结果的生成 SQL。

https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_11_8.jpg

图 11.8:Genie 空间查询和结果

该示例中的查询是显示 预测与实际,这也可以作为可视化请求,如 图 11.9 所示。

https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/B18568_11_9.jpg

图 11.9:Genie 空间可视化

你可以在此找到有关 Databricks Genie 空间的更多信息:docs.databricks.com/en/genie/index.html

应用

在需要更多应用式交互性的情况下,仪表盘或聊天机器人界面不足以满足用户需求。Databricks 应用提供了一个平台,可以在 Databricks 环境中直接构建和部署应用。目前处于公开预览阶段,Databricks 应用支持如 Dash、Shiny、Gradio、Streamlit 和 Flask 等开发框架,用于创建数据可视化、AI 应用、自助分析和其他数据应用。

你可以在这里找到更多关于 Databricks 应用的信息:www.databricks.com/blog/introducing-databricks-apps

总结

在本章的最后,我们深入探讨了时间序列分析的最新进展,重点关注新兴的方法论、工具和趋势。我们尝试了将生成式 AI 这一创新方法应用于时间序列预测的前沿领域。为了响应对通过 API 进行预测的需求增长,我们探索了如何为数据分析师和应用提供按需预测服务。最后,我们使用了 AI 聊天机器人和 Databricks 应用,旨在使非技术用户也能便捷地进行时间序列分析。

当我们到达本书的尾声,回顾我们的旅程和所获得的技能时,我们已经在使用 Apache Spark 和其他组件进行时间序列分析项目的多个阶段上打下了坚实的基础。凭借第二章中讨论的多个应用场景、本书中获得的实践技能,以及本章中的最新进展,我们已经具备了成功实施可生产、可扩展并具备未来适应性的时间序列分析项目所需的所有要素。

我们以 Pericles 关于时间重要性的智慧建议开始了这本书——现在,在本书的结尾,我们具备了揭示时间序列中隐藏的宝贵洞察并将其运用到实际中的能力。愿这些知识使你能够以新的思路和信心应对挑战。祝你在时间序列分析和 Apache Spark 的学习旅程中取得成功!

加入我们在 Discord 的社区

加入我们社区的 Discord 空间,与作者和其他读者讨论:

packt.link/ds

https://github.com/OpenDocCN/freelearn-ds-pt3-zh/raw/master/docs/ts-anal-spk/img/ds_(1.jpg)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值