2021 年 6 月 9 日 - Pixie 的 James Bartlett 和 Zain Asgar 的客座文章。 在 Pixie,我们的目标是让开发人员能够快速理解和调试生产系统。我们通过为开发人员提供轻松访问其生产系统中各种指标和日志数据来实现这一目标。例如,我们收集有关系统中每个进程的 CPU 和内存使用情况的结构化信息,以及许多类型的非结构化数据(例如,HTTP 请求的主体,或程序的错误消息)。
Pixie 的 James Bartlett 和 Zain Asgar 的客座文章。
在 Pixie,我们的目标是让开发人员能够快速理解和调试生产系统。我们通过为开发人员提供轻松访问其生产系统中各种指标和日志数据来实现这一目标。例如,我们收集有关系统中每个进程的 CPU 和内存使用情况的结构化信息,以及许多类型的非结构化数据(例如,HTTP 请求的主体,或程序的错误消息)。
这些只是两个例子,我们还收集了许多其他类型的数据。在这篇博文中,我们将重点关注我们在 Pixie 中收集的大量非结构化数据,例如 HTTP 请求/响应主体。我们预见未来,这些非结构化机器数据可以像结构化数据一样轻松高效地查询。为了实现这一点,我们利用最先进的 NLP 技术来学习数据的结构。
在本文中,我们希望分享我们的经验和努力,希望它们能对您思考类似问题有所帮助。
假设使用 Pixie 的开发人员想了解哪些类型的 HTTP 请求特别慢。与其强迫开发人员手动筛选许多单独的 HTTP 请求,我们可以对 HTTP 请求进行语义聚类,然后向他们显示每个语义聚类请求类型的延迟时间序列。为了演示这一点,让我们看一下最终结果,然后我们将回到我们如何到达这一点。我们将使用 Pixie 来探索一个名为 在线精品店 的演示应用程序。一旦我们将 Pixie 部署到运行在线精品店的 kubernetes 集群中,我们就可以开始探索。例如,我们可以查看在线精品店应用程序中网络连接的图表
正如您在服务图中看到的,有一个前端服务处理传入的请求并将它们发送到各自的微服务。因此,让我们深入研究发送到前端服务的 HTTP 请求及其相应的延迟。
HTTP 请求主体 |
延迟(毫秒) |
“product_id=L9ECAV7KIM&quantity=3 |
3.325302 |
“email=someone%40example.com&street_address=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_address=1600+Amphitheatr |
107.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 个百分位响应延迟的图表。使用此视图,您可以快速确定进入前端服务的三个主要请求类别,以及这些请求的延迟配置文件。我们立即看到,“电子邮件”集群的请求的平均 p99 延迟明显高于其他集群,并且我们看到“产品”集群偶尔会出现延迟峰值。这两者都是我们可以进一步调试的可操作见解。现在让我们深入探讨并讨论如何到达这一点。
由于我们的模型将在客户的生产集群中部署,因此它们必须轻量级且高效;理想情况下,速度足够快,能够以线速处理数据,并最大限度地减少 CPU 开销。对客户数据的任何训练都必须在客户集群中进行,以维护数据隔离保证。此外,由于数据平面完全包含在客户集群中,因此我们对数据的存储限制很严格,因此我们必须利用 ML 技术来智能地对我们收集的数据进行采样。
由于我们严格的数据隔离要求,我们使用 loghub 数据集来引导我们的模型训练。该数据集是来自各种上下文的日志消息的集合(Android 系统日志、Apache 服务器日志、超级计算机/HPC 日志等)。为了测试模型对未见日志格式的泛化能力,我们将 Android 日志数据保留用于测试,并在其余日志数据上进行训练。
我们使用 Google 的 SentencePiece 对日志消息进行分词。特别是,我们使用他们基于 unigram 语言模型的子词分词的实现,词汇量为 16k。下图显示了我们分词生成的 16k 个词汇子词片段的词云。单词的大小表示其在数据集中出现的频率。
显示来自 Logpai Loghub 机器日志数据集分词的词汇子词片段的词云。 |
这个词云提供了我们数据集偏差的洞察。例如,大约 30% 的数据是 Windows 日志,正如您在令牌“windows”和“microsoft”中看到的频率很高。此外,如果您眼光敏锐,您可能会认为我们在数据集中有很多悲伤的表情,但实际上“):”几乎总是出现在一个左括号之前,例如以下示例
[Thu Jan 26 12:23:07 2006] [error] config.update(): Can't create vm
[Fri Jan 27 11:55:16 2006] [error] [client 202.143.128.18] client sent HTTP/1.1 request without hostname (see RFC2616 section 14.23): /
使用这个分词数据集,我们训练了一个基于自注意力的模型,使用从左到右的下一个词预测(类似于 OpenAI 的 GPT 模型)。从左到右的下一个词预测是尝试根据一系列先前的上下文令牌预测下一个令牌的任务。从左到右的部分将其与使用双向上下文的 BERT 样式模型区分开来(我们将在将来尝试双向模型)。这个 TensorFlow 教程 演示了类似架构的训练,唯一的区别是我们删除了教程中架构的编码器部分。
我们使用的架构显示在下图中。我们有 6 个解码器块,每个块都有一个自注意力层和一个前馈层。请注意,为了简单起见,该图省略了自注意力和前馈层上的跳过连接,以及与这些跳过连接相关的层规范化。
GPT 样式的语言模型架构 |
总而言之,这种架构有 6.47M 个参数,与最先进的语言模型相比非常小。例如,DistillBERT 有 66M 个参数。另一方面,GPT-3 的最大版本有 175B 个参数。
我们对这种架构进行了 10 个 epochs 的训练,每个 epoch 大约有 1 亿个独特的日志消息。在每个 epoch 之后,我们在验证集上运行模型,来自验证精度最高的 epoch 的模型被用作最终模型。我们在保留的 Android 日志数据上,对下一个词预测的测试精度达到了 63.13%。鉴于我们尚未探索超参数调整或其他优化,这种水平的精度是一个很有希望的起点。
我们现在有一种方法可以从上下文中预测机器日志数据中的未来令牌,并且准确率相当不错。但是,这并不能立即帮助我们实现从数据中提取结构化特征的最初目标。为了进一步实现这个目标,我们将探索语言模型生成的特征空间,而不是语言模型的预测。
目标是将我们复杂的数据空间转换为固定维度的特征空间,然后我们可以将其用于后续任务。为了做到这一点,我们需要将语言模型的输出转换为固定维度的向量,我们将称之为特征向量。一种方法来自 BERT 样式的模型。
使用 BERT 样式的模型,将预训练的语言模型扩展到监督任务的方法是在句子的 <CLS>(或 <s>)令牌的输出上添加一个全连接网络,然后使用全连接网络对模型进行微调,用于一些分类任务(如下图所示)。这导致了一个自然的特征向量,作为 softmax 层之前的输出。
Alammar, J (2018). Illustrated Transformer [博客文章]. 检索自 https://jalammar.github.io/illustrated-transformer/ |
我们计划在将来探索这种方法,但是现在,我们想看看在不添加任何额外监督的情况下,我们可以得到什么结果。这需要一个启发式方法来将我们的输出序列转换为固定长度的向量。一种方法是对输出的序列维度使用最大池化运算符。假设我们的语言模型输出一系列 256 维向量,那么在序列维度上的最大池化将输出一个 256 维向量,其中每个维度是序列中所有输出在该维度上的最大值。这种方法背后的想法是,响应更强的 neurons 对于包含在最终表示中更重要。
我们可以测试这种方法对 loghub 数据子集的聚类效果,我已经将该子集手动标记为语义集群。以下是手动标记的测试数据集中三条日志消息。前两条被标记为属于同一个语义集群,因为它们都与找不到文件有关,最后一条来自不同的集群,因为它与刷新数据相关的无关消息。
[Wed Nov 09 22:30:05 2005] [error] [client 216.138.114.25] script not found or unable to stat: /var/www/cgi-bin/awstats.p
[Sat Jan 28 19:29:29 2006] [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 算法根据模型的输出生成一个聚类,然后将该聚类与手动标记的聚类进行比较。在这个测试集中,模型的调整后的 rand 分数(指标,其中 0.0 是随机标记,1.0 是完美标记)为 0.53。与下一个词预测精度一样,性能并不理想,但这是一个良好的起点。
我们还可以使用 PCA 将维度降至二维,从而查看模型特征空间的低维表示。下图显示了测试数据集中每个点的嵌入的前两个 PCA 维度。颜色表示点所属的语义集群。请注意,由于这些是在嵌入空间的二维子空间中的绘图,因此点的绝对位置没有太大意义,更多意义来自每个集群的紧密度。在下图中,我们可以看到模型很好地分离了一些类别,但在其他类别上却失败了。
模型特征空间的二维表示。 |
使用这种方法,我们应该能够在 Pixie 中对非结构化数据进行聚类,并用其语义聚类 ID 对其进行标记,从而从非结构化数据中提取结构化特征。这个特定的特征目前还不太容易被人理解,但我们会在后面讨论。
所以让我们尝试在 Pixie 系统中实现这种方法。为此,我们首先需要将我们的模型转换为 TensorFlow Lite,然后将其加载到 Pixie 执行引擎中。我们决定使用 TensorFlow Lite,因为我们需要尽可能地减少开销,并且在将来我们希望能够灵活地部署到异构边缘设备,包括树莓派和 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 平台中的任何非结构化数据。
我们目前正在探索的一些领域包括我们对模型的联邦差分隐私训练的愿景、BERT 风格的双向语言模型、基于数据的学习结构表示的非结构化数据压缩方案,以及非结构化数据的异常检测。
我们 Pixie ML 团队的目标是利用 ML 简化开发人员访问监控数据的过程,同时在异构边缘环境中运行。如果您对这些内容感兴趣,或者您有任何其他问题,请随时加入我们的 Slack 群组.
Pixie 是一个开源项目,它可以让你即时了解你的应用程序。它提供了对指标、事件、跟踪和日志的访问权限,而无需更改代码。Pixie 正在被贡献到 CNCF (云原生计算基金会)。Pixie 最初是在 Pixie Labs, Inc. 创建的,但由 New Relic, Inc. 贡献给了开源社区。
James 是 New Relic Pixie 团队的一名软件工程师。他是 Pixie Labs 的创始工程师。
Zain 是 New Relic 的 Pixie 和开源产品总监/高级副总裁。他还是斯坦福大学的计算机科学兼职教授。他是 Pixie Labs 的联合创始人/首席执行官。
2021 年 6 月 9 日 — James Bartlett 和 Zain Asgar 的客座文章。在 Pixie,我们的目标是使开发人员能够快速理解和调试生产系统。我们通过为开发人员提供轻松访问其生产系统中的各种指标和日志数据来实现这一目标。例如,我们收集有关系统中每个进程的 CPU 和内存使用情况的结构化信息,以及许多 ty…