Pixie 旨在帮助开发者快速了解并调试产品系统。为了实现这一目标,我们将提供支持,帮助开发者轻松访问其生产系统中的一系列指标和日志数据。如,帮助收集系统中各个进程的 CPU 和内存用量的结构化数据,以及许多类型的非结构化数据(例如HTTP 请求的正文,或者程序发出的错误消息)。
以上只是其中两个示例,此外我们还收集许多其他类型的数据。在本篇文章中,我们将重点介绍在 Pixie 中收集如 HTTP 请求/相应正文等大量非结构化数据。在可以预见的未来中,我们认为查询此类非结构化计算机数据,可以像查询结构化数据一样轻松高效。为了实现这一目标,我们利用最先进的 NLP 技术来学习数据的结构。
我们将在本文中与您分享经验和进展,希望能为您在思考类似问题时提供一些帮助。
HTTP 聚类
假设使用 Pixie 的开发者想要了解哪些类型的 HTTP 请求特别慢。他们无需手动筛查大量单独的 HTTP 请求,因为我们可以将 HTTP 请求进行语义聚类,然后显示每种语义聚类请求的延迟时间序列。为了对此进行说明,我们先讲解最终结果,然后再回过头来讲解如何得到这些结果。我们将利用 Pixie 对一款名为 Online Boutique 的演示版应用进行探索。将 Pixie 部署至运行 Online Boutique 的 Kubernetes 集群之后,就可以开始了。举个例子,我们可以看看 Online Boutique 应用中的网络连接示意图:
Online Boutique
https://github.com/GoogleCloudPlatform/microservices-demo
从这个服务示意图中可以看出,前端服务处理传入请求,并将其发送至各自的微服务。接下来,我们深入了解一下发送至前端服务的 HTTP 请求及其相应延迟时间。
HTTP 请求正文延迟时间 (ms)
“product_id=L9ECAV7KIM&quantity=33.325302
“email=someone%40example.com&street_ad
dress=1600+Amphitheatr.。.102.625462
“product_id=OLJCESPC7Z&quantity=3”3.4530790000000002
“product_id=L9ECAV7KIM&quantity=5”4.828718
“product_id=0PUK6V6EV0&quantity=2”5.319163
“email=someone%40example.com&street_ad
dress=1600+Amphitheatr107.361424
”product_id=0PUK6V6EV0&quantity=4“3.81733
”currency_code=EUR“0.203676
”currency_code=USD“0.220932
”product_id=0PUK6V6EV0&quantity=4“4.538055
请求的样本数较少,无法立即明确判断出情况。包含 “email=。..?address=。..” 的请求似乎比其他请求慢很多,但我们无法确定这些示例并非离群值。数据就看到这里,接下来,我们使用非结构化文本聚类技术(稍后对此进行介绍),通过 HTTP 请求正文的内容对其进行语义聚类。
这是各个语义集群请求的第 99 百分位平均响应延迟时间图表。利用此图表,您可以快速确定发送至前端服务的三大类请求,以及这些请求的延迟概况。我们能够立即看到“电子邮件”请求集群的第 99 百分位平均延迟时间明显高于其他集群,而且“产品”集群偶尔会出现延迟尖峰。针对这两种可行的数据分析,我们可以进一步进行调试。现在,让我们来深入探讨如何达成这一目标。
模型开发详细信息
要求
我们的模型将部署至客户的产品集群中,因此这些模型必须为轻量级,且具有高性能;理想状态下,还应具备足够的速度,能够在最小 CPU 消耗的前提下以线速处理数据。为了保障数据隔离,必须在客户集群中训练客户数据。此外,由于数据平面完全包含在客户集群中,我们对数据有严格的存储空间限制,因此我们必须利用 ML 技术对我们收集的数据进行智能采样。
数据集
我们对数据隔离有严格要求,因此我们使用 Loghub 数据集来引导模型训练。此数据集是来自各种上下文的日志消息(Android 系统日志、Apache 服务器日志、超级计算机/HPC 日志等)的集合。为了测试未知日志格式的模型泛化,我们保留了 Android 日志数据用来进行测试,并基于日志数据的其余部分进行训练。
Loghub
https://github.com/logpai/loghub
我们使用 Google 的 SentencePiece 来对日志消息进行词条化,尤其是利用对词汇大小为 16k 的子词的词条化实现(基于 Unigram 语言模型)。下图是我们进行词条化后生成的所有 16k 词汇子词片段构成的词云图。字词的大小表示其在数据集中出现的频率。
SentencePiece
https://github.com/google/sentencepiece
词云图,展示对 Logpai Loghub 计算机日志数据集进行词条化后的词汇子词片段
此词云能够显示出我们数据集的偏向。举个例子,约 30% 的数据为 Windows 日志,从“windows”和“microsoft”词条的高频率就可以看出。此外,若您目光敏锐,您可能会发现我们的数据集中有很多“哭脸”,但实际上“):”几乎总是以左括号开头,如下例所示:
[Thu Jan 2612:23:072006] [error] config.update(): Can‘t create vm
[Fri Jan 27 1116 2006] [error] [client 202.143.128.18] client sent HTTP/1.1 request without hostname (see RFC2616 section 14.23): /
模型架构
我们可以使用此词条化的数据集,利用从左到右的下一字词预测(按照 OpenAI’s GPT 模型)来训练基于自注意力的模型。从左到右的下一字词预测的任务是,在给定一系列上文词条的情况下尝试预测下一个词条。这种从左到右的方式使我们的模型有别于使用双向上下文的模型,比如 BERT。我们将在未来尝试双向模型。此 TensorFlow教程演示了对类似架构的训练,唯一的区别是我们在教程中省略了架构的编码器端。
TensorFlow 教程
https://tensorflow.google.cn/tutorials/text/transformer
下图展示了我们所使用的架构。我们有 6 个解码器块,每个块都有一个自注意力层和一个前馈层。请注意,为简洁起见,该图省略了自注意力和前馈层上的跳跃连接,以及与这些跳跃连接相关的层规范化。
GPT 类语言模型架构
总而言之,此架构有 647 万个参数,与最先进的语言模型相比,这是一个非常小的架构。举个例子,DistillBERT 有 6,600 万个参数。另一方面,最大版本的 GPT-3 有 1,750 亿个参数。
我们对此架构进行了 10 个 epoch 的训练,每个纪元 epoch 大约有 1 亿条独特的日志消息。在每个 epoch 之后,我们在验证集上运行模型,并将验证准确率最高的纪元中的模型用作最终模型。在保留的 Android 日志数据上,我们下一字词预测的测试准确率达到 63.13%。鉴于我们尚未进行超参数调优或其他优化,此准确率水平在未来还有很大的提升空间。
我们现在有一种方法可以从上下文中预测计算机日志数据中的未来词条,并且具有不错的准确性。然而,这并不能立即帮助我们实现从数据中提取结构化特征的最初目标。为了进一步实现这一目标,我们将探索语言模型生成的特征空间,而不是语言模型的预测。
我们的目标是将复杂的数据空间转换为固定维度的特征空间,然后将其用于后续任务。为了实现这一目标,我们需要将语言模型的输出转换为一个固定维度的向量,我们称之为特征向量。要实现此目标,一种方法是使用 BERT 那样的模型。
使用 BERT 这类模型,将预训练语言模型扩展到监督任务的方法是在句子的 《CLS》(或 《s》)词条的输出上添加一个全连接网络,然后在全连接网络中,对某些分类任务的模型进行微调(如下图所示)。由此得出的自然特征向量可作为 Softmax 层之前的输出。
Alammar, J (2018),转换器图解
我们计划在未来对这种方法进行进一步研究,但是现在我们想看看在不添加任何额外监督的情况下能得到什么结果。这需要一种启发式方法将我们的输出序列转换为固定长度的向量。实现此目标的一种方法是,在输出的序列维度上使用最大池化运算符。假设我们的语言模型输出一个 256 维向量的序列,那么序列维度最大池化将输出一个单独的 256 维向量,其中每个维度都是序列的所有输出中该维度的最大值。这种方法背后的理念是,在最终表示中包含具有更强响应的神经元更为重要。
结果
我们可以测试这种方法对 Loghub 数据子集(我事先将其手动标记为不同语义集群)进行聚类的效果如何。以下是手动标记的测试数据集中的三个日志消息。前两个被标记为在同一个语义集群中,因为两者都与文件查找失败有关,最后一个来自不同的集群,因为它是关于刷新数据的无关消息。
Loghub
https://github.com/logpai/loghub
[Wed Nov 0922:30:052005] [error] [client 216.138.114.25] script not found or unable to stat: /var/www/cgi-bin/awstats.p
[Sat Jan 2819:29:292006] [error] [client 211.154.174.50] File does not exist: /var/www/html/modules
20171230-12:25:37:318|Step_StandStepCounter|30002312|flush sensor data
同时可以利用手动标记的测试集,衡量模型分离不同集群的效果。为了实现这个目标,我们使用 KMeans 算法根据模型的输出进行聚类,然后将这种聚类结果与手动标记的聚类结果进行比较。在此测试集上,模型调整后的随机性分数是 0.53,0.0 是随机标记,1.0 是完美标记。与下一字词预测的准确率一样,此性能不是很好,但却是一个很好的起点。
我们还可以查看模型特征空间的低维度表示,使用 PCA 将维数减少至二。下图显示了测试数据集中每个点的嵌入的前两个 PCA 维度。颜色代表点所属的语义集群。请注意,由于这些是嵌入空间的二维子空间中的分布图,点的绝对位置几乎没有意义,更多的意义来自每个集群的紧密度。在下图中,我们可以看到模型很好地分离了一些类,但在其他类上却失败了。
模型特征空间的二维表示
使用此方法,我们应该能够在 Pixie 中对非结构化数据进行聚类,并用其语义集群 ID 对其进行标记,从而从非结构化数据中提取结构化特征。到目前为止,此特殊特征的可解释性并不是很高,但未来我们会着手于这一点
推断
现在,让我们尝试在 Pixie 系统中实现此方法。为此,我们首先需要将模型转换为 TensorFlow Lite,然后将其加载到 Pixie 执行引擎中。决定使用 TensorFlow Lite 是因为我们需要尽可能减少消耗,并且我们希望未来能够灵活地将其部署至异构边缘设备,包括 Raspberry PI 和 ARM 微控制器。
转换至 TensorFlow Lite 十分简单。我们为自己的模型创建一个 TF 函数并调用内置转换器来生成一个 TensorFlow Lite 模型 Protobuf 文件:
model = tf.keras.models.load_model(model_path)
@tf.function(input_signature=[tf.TensorSpec([1, max_length], dtype=tf.int32)def pred_fn(encoded_text):# Create a mask that masks out 0 tokens, and future tokens for next word prediction.
mask = create_padded_lookahead_mask(max_length)
# Our saved model outputs both its next word predictions, and the activations of its final layer. We only use the activations of the final layer for clustering purposes.
model_preds, last_layer_output = model([encoded_text, mask], training=False)
# Max pool over the seq dimension.return tf.reduce_max(last_layer_output, axis=1)
converter = tf.lite.TFLiteConverter.from_concrete_functions([fn.get_concrete_function()])
tflite_model = converter.convert()
Pixie 的查询引擎可以查询和使用 Pixie 收集的数据。此引擎已经有了 KMeans 运算符,所以我们需要做的就是将我们的 TFLite 模型加载到引擎中,然后编写一个自定义的 PxL 脚本(一个基于 Python/Pandas 的 Pixie 脚本语言中的脚本)来对我们的数据进行聚类。我们正在开发一个公共 API,为的是将自定义 ML 模型加载到引擎中,但现在我们将使用一些内部特征来实现这一目标。模型加载完成后,我们就可以在 Pixie 平台中的任何非结构化数据上使用此模型。
责任编辑:haq
-
数据
+关注
关注
8文章
7002浏览量
88942 -
机器学习
+关注
关注
66文章
8406浏览量
132563
原文标题:利用机器学习在 Pixie 上进行非结构化数据处理
文章出处:【微信号:yingjiansanrenxing,微信公众号:硬件三人行】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论