背景
告警系统是运维人员的“眼”,是维护生产安全的第一层保障。传统基于规则的告警系统是通过同比、环比、差分、设置阀值等手段来判断当前指标是否存在异常,但往往不尽人意,存在维护成本高、准确率低等问题。随着人工智能的兴起,大数据运维也迎来了新的契机,同时也对自身告警系统的实时性提出了更高的需求。
于是我们结合Tensorflow深度学习算法,基于大数据平台天然原生的分布式流处理框架Spark,打造了一套高覆盖性与强实时性的告警系统,有效的减少了人工规则的参与,并降低了维护成本。
本文主要从“基于深度学习的时序预测算法”和“算法结合Spark流处理的实时告警”两个方面对本套告警系统进行介绍。框架图如下图所示:
一、基于深度学习的时序预测算法
使用算法监控时序数据的常规思路,是通过本时刻实时值与用前几个时间点的时序数据对本时刻的预测值进行比较。因此,告警的第一步是做好时序预测工作,即时序分析。
引用一段百度文库对时序分析原理的介绍:“承认事物发展的延续性, 事物的现实是历史发展的结果,而事物的未来又是现实的延伸,事物的过去和未来是有联系的。时间序列分析预测法的哲学依据,是唯物辩证法中的基本观点,即认为一切事物都是发展变化的,事物的发展变化在时间上具有连续性。”
时序分析的过程分为以下三个操作步骤:
- 第一步:数据预处理;
- 第二步:核心指标分类;
- 第三步:选择算法训练模型。
下面对以上步骤展开叙述。
1、数据预处理
利用好数据的前提是数据是好用的,因此所有的数据分析工作第一步一定要先经过数据预处理,主要包括数据清洗和特征提取。
时序预测基于历史数据预测未来,历史数据中的“空值”、“异常值”将会极大的影响到预测的质量,因此在训练预测模型前需要对训练集时序数据进行清洗。可以先对数据绘图直观的看一下是否有周期性,然后对于空值采取补齐的方式,具体补齐方式可以根据原序列有明显周期性则可以用历史多个周期在这一个时间点的平均值,无明显周期性则可以用前后两点的均值代替,对于异常值同理。
时序分析的特征提取,业界多采用滑动窗口时序特性分析的方法,也即多个时间窗口之间数据做统计上的均值、方差、协方差等对比,分解出时间序列的成分。时间序列的成分则分为3种:
- 长期趋势(T):一段时间后序列总体呈现的上升或下降的趋势;
- 季节性(S):周期性固定的变化;
- 余项(I):时间序列除去趋势、季节性后的偶然性波动,称为随机性(random),也称不规则波动,是常态。
长期趋势(T)
季节波动(S)
余项(I)
不同于分类场景中将提取的特征用于模型训练,这里特征提取的季节波动和余项都将服务于下一步的指标分类。
2、核心指标分类
对核心指标的时序预测,则需要事先分解时序数据的成分并进行预分类。参照业界时序预测指标分类的方式,用是否具有周期性和是否稳定两个标准可分为以下四类,以集群环境真实指标图解说明:
(1)周期性稳定
(2)周期性非稳定
(3)非周期性稳定
(4)非周期性非稳定
从算法角度上,我们提出了一种基于时序分解,对周期分量进行观察并对余项进行方差分析的指标类型判定方式:
Step 1:时序分解算法将时序数据分解出“趋势分量”、“周期分量”和“余项”
通过观察周期分量,上述第三张图(Seasonality)具有明显规律性波动才被认为具有周期性。
Step 2:对时序分解法分解出的“余项”做方差分析
首先对“余项”进行预处理,采用“归一化”的处理方式将“余项”归一化到[0,1]区间。再计算出“归一化余项”的方差,只有方差在一定阈值以内才被认定为稳定。具体阈值的大小没有一个统一标准,在对多达近百个指标进行如上分析,最终阈值取了0.2。
至此,完成黄金指标的类型分类。对于非周期性指标的监控,业界内暂时也没有一个好的方案,主要还是采用同比环比、差分、设置阀值等经典规则方法。同一种方法,不可能解决所有类型指标的监控,使用新算法并不意味着完全摒弃一些旧方式,这里不做进一步探讨。对于周期性非平稳序列,则可以通过粗粒度时窗,比如原本1分钟一个的点,现统计每5分钟的值进行求和或者求均,都可以处理成周期平稳序列。因此,本文主要针对通过以上方式分类和处理得到的周期性平稳数据。
3、基于LSTM算法的时序预测
时序预测的算法整体上有两大类:基于统计学的传统机器学习算法(注:简称机器学习算法,下同);基于神经网络的深度学习算法。常用的机器学习算法,如指数平滑法(holt-winters)、移动平均自回归模型(ARIMA)以及添加了周期性成分的季节性移动平均自回归模型(SARIMAX);常用的深度学习算法,如深度神经网络(DNN)、循环神经网络(RNN)以及解决了RNN存在长期依赖问题的长短时记忆网络(LSTM)。
考虑到平台部署、稳定性等实用性因素,并对统计学模型两种算法ARIMA、SARIMAX跟深度学习模型LSTM算法的实时性进行对比,在真实环境下,进行多次对比取均值,得出以下结论:
可以看到,LSTM模型不仅在训练跟预测上所花费的时间均远低于其他两个模型,其他两个模型的实时性无法满足需求;同时在资源消耗以及丢失值敏感问题上也要优于其他。因此,最终选择的是深度学习算法——LSTM。
1)LSTM简介
近几年,随着计算能力的迅猛提升,上个世纪末研究到一半的深度学习(Deep Learning,简称DL)在今天重新“翻热”,并在各个领域大放异彩。深度学习模仿大脑的神经元之间传递,处理信息的模式,对其算法思想的理解需要有一定机器学习的基础,下面只做简要介绍。
深度学习隶属于机器学习,与基于人工规则定义特征的传统机器学习相比,DL明确了特征学习的重要性。简单的深度学习算法有深度神经网络DNN,它通过逐层特征变换,将样本在原空间的特征表示变换到一个新特征空间,通过组合低层特征形成更加抽象的高层表示属性类别或特征。
随着深度学习的进一步研究,除了DNN以外还有较多应用于图像识别的卷积神经网络CNN以及自然语言处理NLP常用到的循环神经网络RNN等等。RNN又因为其输入是同一事物的多个时间点的数据,通过不停的将信息循环操作,保证信息持续存在,从而可以利用前面的事件信息的特性被应用于时序分析。本文中的LSTM是一种特殊的循环神经网络RNN,它既有RNN保存历史信息的功能,还解决了普通RNN存在的梯度消失跟梯度爆炸问题。LSTM也有很多变种,这里只给出朴素LSTM的网络结构图。
2)LSTM模型训练操作过程
LSTM 算法的时序预测方式是先基于指标的历史数据训练出模型,再根据当前数据预测指标未来。以下具体的操作使用的是Tensorflow框架,Tensorflow是Tensor(张量:可类比多维数组)以数据流图的形式进行数值计算。
Step 1:已经预处理过的数据转换为Tensorflow可接受的张量形式
- A:数据归一化(方便模型训练快速收敛);
- B:训练数据转换格式(X:x(1),……,x(n);Y:x(n+1));
- C:Reshape成指定形状的张量。
以上过程举例说明,假设有时序数据(1,2,3,4,5,6,7,8,9),设n为3。那么对这个序列进行处理得到(1,2,3;4),(2,3,4;5),……,(7,8,9;10)共7个训练样本。然后再转换为张量格式类似下图所示:
两个6维三阶张量X
两个1维二阶张量Y
Step2:定义网络结构
- A:选择网络:这里使用的是LSTM网络;
- B:设置参数:输入层(层数,隐层神经元个数)、输出层、timestep;
- C:初始化各个神经元之间的连接(权重)跟偏置。
Step3:网络训练
- A:设置优化器、损失函数、学习率、激活函数…;
- B:设置训练方式(epoch,batch_size)。
Step4:生产预测
- A:保存模型;
- B:复用模型进行预测,注意需要反归一化。
经过以上过程,即完成了LSTM模型的训练,此时得到的网络结构格式如下(这里以一个简单的单隐藏层的LSTM网络进行图解,非集群使用真实模型):
二、算法结合Spark流处理的实时告警
上一章节中,通过深度学习算法有效的对指标进行预测,本章节则将其通过与Spark流处理框架的结合应用到告警系统的生产部署中。限于Spark已有的机器学习库MLlib里没有深度学习算法,以及大数据组件跟深度学习算法之间的语言兼容问题。因此尝试在Spark中调用训练好的Tensorflow模型,即“Tensorflow离线训练,Spark实时预测“的AI生产部署模式。
具体分为以下几步:模型上传至HDFS;Yarn调度Spark Streaming加载PB模型;数据的实时消费与告警。涉及到以下引擎层和Hadoop底层:
1、模型上传至HDFS
把一章节中的深度学习算法模型输出为可上传至HDFS文件系统格式的文件并上传至集群HDFS。首先对深度学习模型的保存方式做了简单对比分析:
通过以上对比分析,根据场景适用性、接口丰富性、开发语言兼容性等优势,最终选用PB格式的保存方式。PB模型实质是一个PB文件,是深度学习算法最终生成的一个二进制文件,需要Tensorflow框架进行读取、编译,最后在程序中形成一个预测模型。对其上传则通过crontab定期执行上传即可,上传周期可调。
2、Spark加载PB模型
首先将PB文件存储在HDFS上,通过Spark Streaming程序可以直接读取到HDFS上的PB文件,然后利用Tensorflow框架将所有读取到的PB文件,还原成之前训练好的神经网络模型,用于深度学习的预测。因为在生产环境中,程序是On Yarn模式,以此来保证程序的高可用和高性能,所以所有的PB模型都是通过Spark程序的Driver组件统一生成,然后在向下分发至各个Executor,预测过程将在各个Job中执行。
此外,因为选用的是Spark Streaming框架,所以支持动态PB模型加载,即如果想更新PB模型,可以直接替换HDFS上的PB文件,程序即可识别新的PB文件,生成新的PB模型为后面的数据进行预测。动态更新PB模型遍于调整预测规则,根据指标数据的实时变动,更加有效的更改调节算法策略,使得预测结果更加精准。
整套程序上基于Spark Streaming框架开发的,所以在实时性能够得以保证,在数据平台搭建完善的基础上,所有的实时数据指标信息都会存放在Kafka消息队列中。程序在实时消费Kafka获取当前指标数据,每一次预测都会根据当前的指标数据计算,得到的预测结果会更加贴合真实值。
上图为Spark Streaming基本内部原理,SparkStreaming从实时数据流接入数据,再将其划分为一个个小批量供后续Spark engine处理,所以实际上,Spark是按一个个小批量来处理数据流的。这正是符合我们的实际应用场景,数据实时接收,一批批处理,再结合深度学习算法,实现了智能AI实时预测的功能。
3、数据的实时消费与告警
为防止瞬时异常导致的模型失真问题,因此需要有个替换逻辑来保证模型的稳定。我们利用了外部应用Redis,因Redis是内存数据库,其速度快、多类型存储以及其他丰富特性,能够完全支持我们的存储需求。
对每一个需要预测的指标数据设计了三个不同的数据集,分别为forecast队列、real队列和pb队列:
- forecast队列即为预测值队列,此队列中存放的是根据深度学习算法生成的预测值;
- real队列即为真实值队列,此队列中存放的是从Kafka消息队列中消费得来的真实数据;
- pb队列即为模型输入队列,此队列是用来灌输到PB模型中的数据队列。
三个队列互相协作,保证模型的稳定。首先在程序第一次启动时,需要先积累足够多的原始数据,填入到real队列和pb队列中,当积累了t分钟后,有了足够多的数据可以预测时,将pb队列中的数据填入PB模型中,预测t+1分钟的数据,将t+1分钟的预测值填入到forecast队列中。
在t+1分钟时,拿到了t+1分钟的真实值,用t+1分钟的真实值与t分钟预测出来的t+1分钟的预测值根据特定的比对规则比对,如果真实值与预测值符合比对要求,那么将t+1分钟的真实值填入到pb队列中,反之则用t+1分钟的预测值填入到pb队列中,并且保持pb模型要求的长度,去掉pb队列中时间最早的数据,用替换更改过后的pb队列填入到PB模型中,预测t+2分钟的预测值。
当以上步骤进行了足够多的时候,那么real队列与forecast队列都已经积累的足够多的数据,并且时间都会互相匹配,即real队列的数据为t分钟至t+n分钟的真实值,forecast队列的数据为t+1分钟至t+n+1分钟的预测值,那么在t+n+1分钟时,拿到了t+n+1分钟的真实数据,即可按照每个时间的真实值和预测值互相对比,判断此指标数据是否出现异常,如果比对结果中的异常时间点超过既定个数,那么此指标则被判定有异常发生,立刻会发出告警,提醒运维人员检查修复。
对于每个队列的长度和应用都做成可随时更改的配置项,里面有详细的要求和规则来规定和约束每个不同的预测模型的数据队列便于模型定制化调整,真正的实现了特定预测,特殊预测和准确预测。
三、总结
整个告警系统平台,采用深度学习算法提高了预测的准确性,再结合Spark Streaming流处理框架保障了告警的实时性,减少了人为规则的参与,降低运维人员维护的时间投入。
从2019年年底正式上线,现已稳定支撑某运营商包括Kafka、HBase等多个大数据技术栈的核心指标业务的实时监控。从平台正式运行至今,总的查全率能达到98%以上;由于核心业务更注重查全率,因此查准率稍低,查准率大约维持在90%的水平;同时集群环境提供的容错性,以及Spark Streaming流处理提供的实时消费能力极大的降低了告警的时间延迟,理论上可以达到与指标采集频率相差毫秒级的延迟。