Skip to content

导论

预测在资源规划或者调度方面起到重要的作用。由于资源的规划和调度有提前期(Lead Time,物流供应链中的一个专业术语),比如,我们下一个订单后,我们订购的产品不是瞬间能够到达的,需要一段时间,这段时间就是提前期。预测服务能够告诉我们将来什么时候有多少数量的需求,这样,我们就能够提前规划和安排资源,满足未来的需求,这样能够极大地帮助客户缩短提前期。准确的预测还能够减少资源浪费、减少用户等待的时长和提高用户的满意度,这在分布式系统中的自动化资源调度方面,体现出来的作用尤为明显。

picture 1

图 1, 某个云用户的请求量的时间序列示意图

在一个大型的互联网应用如电子商务、云计算产品等应用里,通常有大量细粒度的对象,比如上百万或者千万级别的用户,百万或者千万级别的机器等等。在资源调度的时候,我们通常需要对每一个细粒度的对象所需要的资源进行预测,以方便提前进行资源调度管理。这些大型互联网应用基本上都会记录在每一个时间段内的每一个对象所消耗的资源或者记录其他的对系统有指导意义的指标。每一个对象的每一个指标在不同时刻的数值构成一个时间序列,这样,在大型的互联网应用系统里就有大规模如千万级别的时间序列。参考图 1,图中展示了一个云用户的请求量的时间序列。应用阿里云日志服务可以很轻松地帮助这些大型的应用实现大规模的预测模型和服务。无需特殊的分布式并行计算的处理甚至是分布式并行计算的知识,仅仅使用SQL等操作就可以让这些预测计算自动地分布在阿里云日志服务的大量的计算服务器中并行执行,让预测服务非常高效。后面我们会用一些例子来说明。

预测模型和超参数配置

为了方便更多没有统计或者机器学习背景的工程开发人员使用阿里云日志服务实现大规模预测模型和服务,我们先简单地介绍一下预测模型。有了这些预测模型的简单知识,对预测模型的参数就有较深刻的理解了。

预测有多种方法,基本上都是用统计的方法通过历史数据中反映出来的数据变化模式来预测将来的数量变化。其中比较通用和常用的预测模型是ARIMA(Auto-Regressive Integrated Moving Average,自回归及误差移动平均)。一个时间序列的ARIMA模型主要由三部分组成:(1)对时间序列进行多少次差分,可以是0次即不差分,(2)自回归部分,即差分变换后得到的新变量的前几个时刻的值的线性组合,(3)之前几步预测误差的线性组合。

举例说明,对于每一个云产品的用户,我们都会记录他们每分钟的访问量,我们不妨只看其中一个用户的分钟访问量。我们用 Y(t) 表示该用户在第 t 分钟的请求量, Y(t) 是一个时间序列。如果我们可以对 Y(t) 进行0次、1次或者两次的差分变换,会得到一个新的时间序列 y(t) 。

0次差分: y(t) = Y(t)

1次差分: y(t) = Y(t) – Y(t-1)

2次差分: y(t) = [Y(t) – Y(t-1)] – [Y(t-1) – Y(t-2)]

在经过变换后的时间序列的ARIMA的预测模型为:

picture 13

其中 为由时间序列在 t-1 时刻及之前的信息,通过预测计算得到的第 t 时刻的预测值。

a 为截距常数。

是前p个时刻的值线性回归组合(Auto-Regressive自回归部分)。

是前q个时刻预测误差的线性组合(Moving Average误差移动平均修正部分)。 表示第t时刻的预测误差。

其中的 a,b1, b2,...,bp, c1,c2,...cq 为ARIMA模型需要通过历史数据去识别的参数。我们把时间序列 Y(t-1),Y(t-2),Y(t-3),...,Y(1) 的各个历史时刻的数据喂给ARIMA模型,如果给 a,b1, b2,...,bp, c1,c2,...cp 选定一组特定的值,那么就可以根据上面的公式可以计算出每一个时刻预测值,然后和真实值比较,计算出每一个时刻的预测误差。ARIMA模型会非常聪明地根据所收到的历史数据,在历史数据上不断地进行尝试和优化计算,选择出一组最恰当的参数,使得所选中的一组参数值后,模型预测误差(平方和)比其他组参数值得到模型预测误差都要小,也就是这说,ARIMA模型会选一组参数让模型的预测最准确。

在ARIMA根据历史数据选组一组最合适的模型参数之前,我们需要确定三个超参数:(1)差分的阶数 d ,(2)自回归参数的个数 p ,(3)误差移动平均的参数的个数 q。要比较精细地确定这三个超参数的值需要非常大的篇幅来描述。我们从工程的角度出发,只要模型好到一定程度就可以了。我们可以根据一些经验来选择这些超参数。在实践上,所选择的超参数大部分情况下都满足:

我们不妨选择 d = 0 (不进行差分运算), p = 2 (两个自回归项), q = 1 (1个误差项),这样选择的超参数,能够让模型考虑到时间序列的递增或者递减模式、或者有递增加速度或者递减加速度的模式,误差项能够让模型考虑更早期的数据作为当前预测的贡献。

虽然这个选择不一定是最优的,但是通过在历史数据上训练后,得到的模型的预测精确程度和用最优的超参数得到的模型的预测精确程度相差一般来说都很小。因此,为了方便,我们可以对每一个时间序列,都使用同一组超参数 (d = 0, p = 2, q = 1) 。由于模型的参数(自回归项和误差移动平均项等)是通过喂给的历史数据学习出来,尽管不同时间序列的超参数都一样,但是每一个时间序列的ARIMA预测模型的具体参数是从不一样的历史数据上学习出来的,所以具体的系数也不一样,模型参数是根据历史数据自适应的。

阿里云的日志服务提供了ARIMA机器学习函数。在使用阿里云的日志服务提供了ARIMA机器学习函数时候,您并不需要知道如何对模型进行训练,您只需要选择您想要的超参数 (p, d, q) 和喂给模型数据就可以了。

如何在阿里云日志服务实现大规模预测模型和服务

前面提到,在大型的互联网应用系统里就有大规模如千万级别的时间序列。我们需要对每一个时间序列都做预测计算,所以也对应地会有上千万的大规模预测模型。应用阿里云日志服务可以很轻易地实现这些大规模预测模型。

单时间序列预测

在介绍怎么实现大规模预测模型和做大规模预测之前,我们先看一下在阿里云日志服务如何对单个时间序列建立时间序列预测模型并且进行预测的。阿里云日志服务提供的ARIMA预测模型的函数如下:

ts_predicate_arima(t, y, p, d, q, nPred)

其中的参数的含义见表 1。 表 1, 阿里云日志服务提供的ARIMA机器学习函数的参数描述

参数说明
t时间序列的时间列,格式为Unixtime时间戳,单位为秒,从小到大排列。
y数值列,对应某时刻的数据。
p自回归模型的阶数。
d差分模型的阶数。
q移动平均模型的阶数。
nPred预测未来的点的数量。

下面是ts_predicate_arima使用的范例SQL语句。在使用单时间序列预测之前,我们需要整理数据,把数据整理成为一个包含两列的数据表,一列是用Unix时间表示的时间列,另一列是对应的指标值。

sql
* | select ts_predicate_arima(unix_time_period, value, 2, 0, 1, 5) 
from (
	select __time__ - __time__ % 60 as unix_time_period, 
	avg(value) as value 
	from metric_dataset 
	GROUP BY unix_time_period 
	order by unix_time_period
)

对于单时间序列的预测,其返回结果是一个数据表。其中的每一行表示每一个时刻以及对应的原始数据值、预测值以及其他的统计量。这个数据表有6列,这6列分别对应Unix的时间戳(unixtime)、原始的指标值(src)、根据之前的数据预测的该时刻的指标值(predict)、预测值的85%置信区间的上界(upper)、预测值的85%置信区间的下界(lower)、该数据点为异常的概率(anomaly_prob)。 85%置信区间的意思是模型对每一个时刻的指标值给出一个预测区间,把所有时间点的预测区间和真实的指标值对比,会发现约有85%的指标真实值落在这个区间内。通常置信度越高,区间也就越宽。模型给出的预测值位于置信区间的中间。接下来会我们通过预测的应用案例里说明怎么应用这些预测值和置信区间。 我们把模型的结果的最前面一部分和最后一部分分别展示在图 2和图 3中。模型的结果包含了用于预测历史数据和未来时刻的预测值。我们看到图中最后一部分结果中的src为NaN,意思是未来时刻的真实值还不清楚,predict列里的数值是对应时刻的预测值。

picture 2

图 2,单时间序列预测结果的历史时刻数据部分

picture 3
图 3, 单时间序列预测结果的未来时刻数据部分

预测应用案例

我们使用一个云上资源动态租赁的例子来说明描述这些预测量的应用。企业上云的一个很大的好处是可以动态地租用云上的资源如ECS等。租用这些资源时,云系统需要配置和设置等,另外企业应用本身也需要安装配置,所以可能会有分钟级别的冷启动时间,也就是我们前面说到的提前期。企业租用云资源以后,很多是用来给企业的用户提供线上服务的。线上的用户数量多,企业的营收通常就越多,需要的资源也多。线上用户的服务请求在企业应用的后台通常使用队列的方式来控制和服务,参照图 4。即每个用户的请求都会先放在队列里,如果有足够的资源,就会从队列里拿出一个请求。如果所有的资源都繁忙,用户的请求会在队列中等到某些请求的服务请求完毕以后,资源释放出来以后才能够被响应。线上用户和请求的数量具有不确定性,忽高忽低。用户的请求量到资源的需求量通常有一个换算关系。这样,比较准确地预测用户请求量或者资源的需求量就很重要。如果不能比较准确预测资源的需求量,资源太少,用户的请求需要较长时间的等待,等待时间过长,系统还会返回超时错误等,用户的体验会很差,最终会流失用户,收入就会减少。如果租用的资源过多,会造成资源的浪费,运作成本高,会降低利润。较准确的预测能够帮忙用最合适的资源提供优质的服务。

picture 4

图 4, 用户请求资源绑定和队列方式的流量控制示意

在企业应用云资源动态的动态租赁的例子中,假设云资源有分钟级别的提前期,资源调度周期为5分钟,即系统应用会每隔5分钟决定现在需要创建或者释放多少云资源。如果是创建的云资源,这些云资源将会现在之后的第5分钟开始可用。因此,能够准确地预测接下来的第5到第10分钟的资源需求很重要。

在前面提到阿里云日志服务的预测函数会返回每个时段的预测值和85%的置信区间,请参考图 5的示意图。从图中我们可以看到每个时段的85%的置信区间构成一个条带区域。也就是说,将来的真实的资源需求大约有85%会落在这个条带以内,15%落在条带以外,条带以外的上方和下方分别大约占7.5%。这样,资源的需求有92.5%的概率在条带上边界一下,包括条带和条带外的下方。换一种说法,把资源池和请求队列结合起来看,如果按照条带的上边界来租赁资源,可以大致地认为,92.5%的用户请求一进来的时候,队列是空的,马上有资源可用,请求能够马上得到响应。剩下的7.5%的用户请求一进来的时候,队列不是空的,需要等待一些正在被响应的请求释放资源以后才能被处理。

picture 5

图 5, 置信区间的示意描述

读到这里,您可能有个疑问,我们在选择置信区间的时候有什么考虑吗?是85%还是100%?这主要考虑大部分的概率分布都有长尾,在做预测的时候,我们通常会假定预测值近似于正态分布,根据概率论的大数定律和中心极限定律,这个假设通常是合理的。参考下图各个不同概率的置信区间的宽度的比例大小。我们主要是选择一个比较紧凑,又能覆盖指标大部分情况的真实值的置信区间。让置信区间比较紧凑的目的是减少资源浪费。选择置信区间还和预测的准确程度有关。预测准确度低,同等概率的置信区间就会更宽。如果100%准确,就是100%的置信区间也可以收敛成为一个点,就是预测值。假设平均预测相对误差为E%,那么——

在预测的需求量之上增加 E% 的资源,能够让大约84%(68%的置信区间)的请求一进来的时候就无需排队可以直接响应。 在预测的需求量之上增加 1.44 × E% 的资源,能够让大约92.5%(85%的置信区间)的请求一进来的时候就无需排队可以直接响应。 在预测的需求量之上增加 1.96 × E% 的资源,能够让大约97.5%(95%的置信区间)的请求一进来的时候就无需排队可以直接响应。 在预测的需求量之上增加 2.58 × E% 的资源,能够让大约99.5%(99%的置信区间)的请求一进来的时候就无需排队可以直接响应。

用更具体的数字描述,如果预测的资源需求是100台机器,预测平均相对误差为20%,如果只租用100台机器,那么只有大约50%的请求一进来的时候无需排队就可以直接被响应。如果要让92.5%的请求一进来的时候无需排队就可以直接被响应,那么需要租用128台机器 (= 100 × (1 + 1.44 × 20%)) ,其中128是85%置信区间的上界。如果要让97.5%的请求一进来的时候无需排队就可以直接被响应,那么需要租用139台机器 (= 100 × (1 + 1.96 × 20%)) 。

如果预测100%准确(在现实中几乎不可能),那么预测相对误差为0%,套入前面的计算公式,租用100台机器能让100%的请求无需排队就可以直接被响应。

picture 6

图 6, 预测置信区间示意图

大规模时间序列预测

前面我们介绍了应用阿里云日志服务进行单时间序列预测的方法。在企业的实际应用中,我们通常需要对每一个细粒度的对象所需要的资源进行预测,进行方便提前进行资源调度管理。例如,对千万用户中的每一个用户的请求量或者资源需求量进行预测。这样,就需要识别大规模的时间序列模型。同样地,我们也可以应用阿里云的日志服务提供的机器学习函数去识别大规模的时间序列模型。

在使用单时间序列预测之前,我们需要整理数据,把数据整理成为一个包含两列的数据表,一个用Unix时间表示的时间列,另一列是对应的指标值。对于多时间序列的预测,除了这两列,我们还需要整理额外的数据列来区分不同的对象,如用户ID列等。这样,对于每一个系统中的对象,都有一个时间序列,全部组织在这个二维数据表中。假设整理好的数据有三列,列名分别为user_id, unix_time_period, metric_value,则应用阿里云日志服务为同时为这千万用户同时建立时间序列预测模型并且进行预测的方法为:

sql
* | select user_id,
	ts_predicate_arima(unix_time_period, metric_value, 2, 0, 1, 5) as model_results
from data_table
group by user_id

在上面的分析语句中。ts_predicate_arima返回的不再是一个数据表,而是把单时间序列的计算结果变成一个二维数组。可以把这个返回的二维数组看成一个数据表格。其中的每一行表示每一个时刻以及对应的原始数据值、预测值以及其他的统计量。这个二维数组的第二维只有6个元素对应表格的6列,这6个元素分别对应Unix的时间戳、原始的指标值、根据之前的数据预测的该时刻的指标值、预测值的85%置信区间的上界、预测值的85%置信区间的下界、该数据点为异常的概率。 在阿里云日志服务中采用的这种分析方法非常高效,因为阿里云日志服务在后台用了大量的数据处理服务器来进行分布式的并行计算,把每个对象的时间序列整理放在一起,然后把上千万的时间序列分布到不同的数据处理服务器上的不同的进程进行计算。分布式并行计算的可以参考图 7。这种大规模分布式计算的方法,相比较于其他常见的客户端应用如Python程序把所有的数据逐一读出来,然后调用时间序列模型的方式,再逐一计算的串行方式,能够把计算效率提升上千倍甚至更多。

picture 7

图 7, 阿里云日志服务大规模分布式并行时序预测示意

截取的大规模多时间序列预测计算的结果如图 8所示。主要有对象ID列和一个用二维数组的预测计算结果列。

picture 8

图 8, 多时间序列返回结果示意图

要读取某个时刻的预测值,可以用数组元素的下标的方式来处理。比如,要取预测计算结果的第10行的预测置信区间的上界,可以在SQL中用model_results[10][5]来获取。但是这种方法比较不方便,因为历史数据也混合在预测结果中,时间点和特定的行有一定的换算关系,但是这个换算过程比较繁琐。我们可以用unnest函数把这些二维数组的结果展开成表格的行的形式,这样用起来就方便多了,展开成行以后,也可以很轻易地按照时间来获取预测数据。参考下面的SQL语句如何使用unnest函数展开更方便地获取预测结果数据。

sql
* | 
with compact_prediction_results as
(
    select user_id,
        ts_predicate_arima(unix_time_period, metric_value, 2, 0, 1, 5) as model_results
    from data_table
    group by user_id
)

select main_table.user_id,
    from_unixtime(sub_table.data_row[1], 8, 0) as time_period,
    sub_table.data_row[2] as source_data_value,
    sub_table.data_row[3] as predicted_value,
    sub_table.data_row[4] as predicted_upper_bound,
    sub_table.data_row[5] as predicted_lower_bound,
    sub_table.data_row[6] as anomaly_probability
from compact_prediction_results as main_table, unnest(main_table.model_results) as sub_table(data_row)
order by user_id, time_period

使用上面的SQL语句把预测结果展开以后,变成一个二维数据表,二维表的结果参考图 9。在这个二维表的基础上,就可以很容易地延伸出各种有关预测的应用了。

picture 9

图 9, 重新整理后的多时序数据预测返回结果示例

生成持续计算的任务

由于生产、资源规划和调度是持续进行,所以我们的预测计算需要持续不断地执行。阿里云日志服务提供了ScheduledSQL的功能,帮助我们生成持续执行的预测计算任务。要在阿里云日志服务生成持续执行的预测计算任务,在查询分析的界面上提供了一个 “定时保存分析结果”的按钮,点击这个按钮,参考图 10,之后会弹出窗口,然后在这个窗口上配置这个定时执行的预测计算就可以了。

picture 10

图 10, 把预测计算的SQL语句变成持续计算的任务

ScheduledSQL任务的配置很直观,参考图 11和图 12。ScheduledSQL计算任务会定时把预测计算的SQL所产生的计算结果写入到另外一个日志库,如果这个目标日志库还不存在,需要先创建。另外要配置的是调度时间间隔和SQL读取的数据窗口。如果系统每5分钟需要进行资源调度一次,那么预测计算服务的调度时间间隔也应该是5分钟,整理的时间序列数据的单位也是5分钟,可以保留多步预测的结果到另外一个日志库,比如接下来的4个5分钟时间段的预测。这样,对于同一个时间段,会有4次预测,在资源调度计算的时候,使用最新的一次即可。

SQL时间窗口指的是做预测时候历史数据的时间窗口。在选择历史数据的时间窗口时候,为了预测比较可靠稳定,应该让数据样本数 n 足够多,比如满足下面的不等式

前面我们选择 d = 0 (不进行差分运算), p = 2 (两个自回归项), q = 1 (1个误差项),所以我们应该选择至少20个数据点,如果数据点的时间单位是5分钟,那么应该需要最近100分钟的历史数据。如果数据有延迟,比如最多1分钟的延迟,那么可以展开高级设置,让计算任务延迟60秒启动,这样不会造成数据丢失或者数据不完整。

picture 11

图 11, Scheduled SQL任务的配置窗口一

picture 12

图 12, Scheduled SQL任务的配置窗口二

一些预测最佳实践

不完整时段数据预处理

在预测中要注意数据的干净性。比如我们可能按天或者小时来组织数据,但是对于一个对象指标的最新的一条数据,可能是不完整天或者不完整小时的数据,因为当天或者当下小时还没有结束。在这种情况下,最后一个不完整时段的指标数据要比完整的小,甚至小很多。如果把这条不完整时段的数据也喂给时间序列模型的话,模型会误以为这个偏小的数据就是一个完整时段的数据,会造成后面的预测值偏小。在这种包含不完整时段的数据的情况下,需要把这条数据排除在外,再把数据交给时间序列模型去训练,这样预处理后的预测模型会准确很多。