使用 Forest Inference Library (FIL) 的异常行为分析示例
此示例说明了如何使用 Morpheus 通过利用 Forest Inference Library (FIL) 模型和 Triton 推理服务器自动检测 NVIDIA SMI 日志中的异常行为。我们将要搜索的特定行为是加密货币挖矿。
环境 |
是否支持 |
注释 |
---|---|---|
Conda | ✔ | |
Morpheus Docker 容器 | ✔ | 需要在主机上启动 Triton |
Morpheus 发布容器 | ✔ | 需要在主机上启动 Triton |
开发容器 | ✔ | 需要使用 dev-triton-start 脚本,并将 --server_url=localhost:8000 替换为 --server_url=triton:8000 |
此示例的目标是识别受监控的 NVIDIA GPU 是否正在积极挖矿加密货币,并在检测到时采取纠正措施。加密货币挖矿可能会大量消耗 GPU 集群的资源,并且检测挖矿可能很困难,因为挖矿工作负载看起来与其他有效工作负载相似。
在此示例中,我们将使用 Morpheus 提供的 ABP NVSMI 检测模型。此模型能够从 nvidia-smi
日志的输出中检测加密货币挖矿的特征。对于 nvidia-smi
日志数据可用的每个时间戳,模型将输出一个概率,指示是否检测到挖矿。
数据集
此工作流程旨在处理的数据集包含定期时间间隔的 NVIDIA GPU 指标,并由 NetQ 代理提取并序列化为 JSON。数据集中的每一行都包含与 nvidia-smi
实用程序返回的大部分相同的信息。我们不会直接检查完整的消息,因为每一行包含 176 个不同的列,但可以使用 nvidia-smi dmon
命令了解数据集是如何生成的。如果您自己运行此命令,输出将类似于以下内容
$ nvidia-smi dmon
# gpu pwr gtemp mtemp sm mem enc dec mclk pclk
# Idx W C C % % % % MHz MHz
0 70 48 - 5 1 0 0 7000 1350
0 68 48 - 11 1 0 0 7000 1350
0 69 48 - 3 1 0 0 7000 1350
0 270 53 - 10 1 0 0 7000 1875
0 274 55 - 75 46 0 0 7000 1740
0 278 55 - 86 56 0 0 7000 1755
0 279 56 - 99 63 0 0 7000 1755
0 277 57 - 86 55 0 0 7000 1755
0 281 57 - 85 54 0 0 7000 1740
输出中的每一行代表在单个时间点的 GPU 指标。随着工具的进行,GPU 开始被利用,并且 SM% 和 Mem% 值随着内存加载到 GPU 和执行计算而增加。我们将要使用的模型可以摄取此信息,并确定 GPU 是否正在挖矿加密货币,而无需来自主机机器的额外信息。
在此示例中,我们将使用 examples/data/nvsmi.jsonlines
数据集,该数据集已知包含挖矿行为配置文件。该数据集为 .jsonlines
格式,这意味着每一新行代表一个新的 JSON 对象。为了解析此数据,必须先摄取数据,按行拆分为单独的 JSON 对象,然后解析为 cuDF DataFrame。这些都将由 Morpheus 处理。
生成您自己的数据集
此示例可以轻松应用于从您自己的 NVIDIA GPU 设备生成的数据集。如果您的环境中未部署 NetQ,则提供了 nvsmi_data_extract.py
脚本,该脚本使用 pyNVML 和 pandas 来生成类似于 NetQ 的数据。pyNVML
包含 NVIDIA Management Library (NVML) 的 Python 绑定,NVML 与 nvidia-smi
使用的库相同。
默认情况下未安装 pyNVML,请使用以下命令安装它
conda env update --solver=libmamba -n morpheus --file conda/environments/examples_cuda-125_arch-x86_64.yaml
运行以下命令开始生成您的数据集
python nvsmi_data_extract.py
这将每秒向名为 nvsmi.jsonlines
的输出文件写入一个新条目,直到您按下 Ctrl+C 退出。
在此示例中,我们将使用的流水线是一个简单的前馈线性流水线,其中来自每个阶段的数据流向下一个阶段。像此示例一样,没有自定义阶段的简单线性流水线可以通过 Morpheus CLI 或使用 Python 库进行配置。在此示例中,我们将使用 Morpheus CLI。
以下是流水线的可视化,显示了所有阶段和数据类型,以及数据从一个阶段流向下一个阶段的过程。

此示例利用 Triton 推理服务器执行推理。
启动 Triton
拉取 Triton 的 Docker 镜像
docker pull nvcr.io/nvidia/morpheus/morpheus-tritonserver-models:24.10
运行以下命令以启动 Triton 并加载 abp-nvsmi-xgb
XGBoost 模型
docker run --rm -ti --gpus=all -p8000:8000 -p8001:8001 -p8002:8002 nvcr.io/nvidia/morpheus/morpheus-tritonserver-models:24.10 tritonserver --model-repository=/models/triton-model-repo --exit-on-error=false --model-control-mode=explicit --load-model abp-nvsmi-xgb
这将启动 Triton 并且仅加载 abp-nvsmi-xgb
模型。此模型已配置最大批处理大小为 32768,并使用动态批处理以提高性能。
一旦 Triton 加载了模型,将显示以下内容
+-------------------+---------+--------+
| Model | Version | Status |
+-------------------+---------+--------+
| abp-nvsmi-xgb | 1 | READY |
+-------------------+---------+--------+
注意:如果输出中没有此内容,请检查 Triton 日志中是否有与加载模型相关的任何错误消息。
使用 Morpheus CLI,可以配置和运行整个流水线,而无需编写任何代码。使用 morpheus run pipeline-fil
命令,我们可以通过在命令行上指定每个阶段的名称和配置来构建流水线。每个阶段的输出将成为下一个阶段的输入。
以下命令行是构建和启动流水线的完整命令。每一新行代表一个新的阶段。每个阶段上方的注释提供了有关为什么添加和配置该阶段的信息(您可以复制/粘贴带有注释的完整命令)。
从 Morpheus 仓库根目录运行
# Launch Morpheus printing debug messages
morpheus --log_level=DEBUG \
`# Run a pipeline with 8 threads and a model batch size of 1024 (Must be equal or less than Triton config)` \
run --num_threads=8 --pipeline_batch_size=1024 --model_max_batch_size=1024 \
`# Specify a NLP pipeline with 256 sequence length (Must match Triton config)` \
pipeline-fil --columns_file=data/columns_fil.txt \
`# 1st Stage: Read from file` \
from-file --filename=examples/data/nvsmi.jsonlines \
`# 2nd Stage: Deserialize batch DataFrame into ControlMessages` \
deserialize \
`# 3rd Stage: Preprocessing converts the input data into BERT tokens` \
preprocess \
`# 4th Stage: Send messages to Triton for inference. Specify the model loaded in Setup` \
inf-triton --model_name=abp-nvsmi-xgb --server_url=localhost:8000 \
`# 5th Stage: Monitor stage prints throughput information to the console` \
monitor --description "Inference Rate" --smoothing=0.001 --unit inf \
`# 6th Stage: Add results from inference to the messages` \
add-class \
`# 7th Stage: Convert from objects back into strings. Ignore verbose input data` \
serialize --include 'mining' \
`# 8th Stage: Write out the JSON lines to the detections.jsonlines file` \
to-file --filename=detections.jsonlines --overwrite
如果成功,应显示以下内容
Configuring Pipeline via CLI
Loaded columns. Current columns: [['nvidia_smi_log.gpu.fb_memory_usage.used', 'nvidia_smi_log.gpu.fb_memory_usage.free', 'nvidia_smi_log.gpu.utilization.gpu_util', 'nvidia_smi_log.gpu.utilization.memory_util', 'nvidia_smi_log.gpu.temperature.gpu_temp', 'nvidia_smi_log.gpu.temperature.gpu_temp_max_threshold', 'nvidia_smi_log.gpu.temperature.gpu_temp_slow_threshold', 'nvidia_smi_log.gpu.power_readings.power_draw', 'nvidia_smi_log.gpu.clocks.graphics_clock', 'nvidia_smi_log.gpu.clocks.sm_clock', 'nvidia_smi_log.gpu.clocks.mem_clock', 'nvidia_smi_log.gpu.applications_clocks.graphics_clock', 'nvidia_smi_log.gpu.applications_clocks.mem_clock', 'nvidia_smi_log.gpu.default_applications_clocks.graphics_clock', 'nvidia_smi_log.gpu.default_applications_clocks.mem_clock', 'nvidia_smi_log.gpu.max_clocks.graphics_clock', 'nvidia_smi_log.gpu.max_clocks.sm_clock', 'nvidia_smi_log.gpu.max_clocks.mem_clock']]
Starting pipeline via CLI... Ctrl+C to Quit
Config:
{
"ae": null,
"class_labels": [
"mining"
],
"debug": false,
"edge_buffer_size": 128,
"feature_length": 18,
"fil": {
"feature_columns": [
"nvidia_smi_log.gpu.pci.tx_util",
"nvidia_smi_log.gpu.pci.rx_util",
"nvidia_smi_log.gpu.fb_memory_usage.used",
"nvidia_smi_log.gpu.fb_memory_usage.free",
"nvidia_smi_log.gpu.bar1_memory_usage.total",
"nvidia_smi_log.gpu.bar1_memory_usage.used",
"nvidia_smi_log.gpu.bar1_memory_usage.free",
"nvidia_smi_log.gpu.utilization.gpu_util",
"nvidia_smi_log.gpu.utilization.memory_util",
"nvidia_smi_log.gpu.temperature.gpu_temp",
"nvidia_smi_log.gpu.temperature.gpu_temp_max_threshold",
"nvidia_smi_log.gpu.temperature.gpu_temp_slow_threshold",
"nvidia_smi_log.gpu.temperature.gpu_temp_max_gpu_threshold",
"nvidia_smi_log.gpu.temperature.memory_temp",
"nvidia_smi_log.gpu.temperature.gpu_temp_max_mem_threshold",
"nvidia_smi_log.gpu.power_readings.power_draw",
"nvidia_smi_log.gpu.clocks.graphics_clock",
"nvidia_smi_log.gpu.clocks.sm_clock",
"nvidia_smi_log.gpu.clocks.mem_clock",
"nvidia_smi_log.gpu.clocks.video_clock",
"nvidia_smi_log.gpu.applications_clocks.graphics_clock",
"nvidia_smi_log.gpu.applications_clocks.mem_clock",
"nvidia_smi_log.gpu.default_applications_clocks.graphics_clock",
"nvidia_smi_log.gpu.default_applications_clocks.mem_clock",
"nvidia_smi_log.gpu.max_clocks.graphics_clock",
"nvidia_smi_log.gpu.max_clocks.sm_clock",
"nvidia_smi_log.gpu.max_clocks.mem_clock",
"nvidia_smi_log.gpu.max_clocks.video_clock",
"nvidia_smi_log.gpu.max_customer_boost_clocks.graphics_clock"
]
},
"log_config_file": null,
"log_level": 10,
"mode": "FIL",
"model_max_batch_size": 1024,
"num_threads": 8,
"pipeline_batch_size": 1024
}
CPP Enabled: True
====Registering Pipeline====
====Registering Pipeline Complete!====
====Starting Pipeline====
====Pipeline Started====
====Building Pipeline====
Added source: <from-file-0; FileSourceStage(filename=examples/data/nvsmi.jsonlines, iterative=False, file_type=FileTypes.Auto, repeat=1, filter_null=True)>
└─> morpheus.MessageMeta
Added stage: <deserialize-1; DeserializeStage()>
└─ morpheus.MessageMeta -> morpheus.ControlMessage
Added stage: <preprocess-fil-2; PreprocessFILStage()>
└─ morpheus.ControlMessage -> morpheus.ControlMessage
Added stage: <inference-3; TritonInferenceStage(model_name=abp-nvsmi-xgb, server_url=localhost:8000, force_convert_inputs=False, use_shared_memory=False)>
└─ morpheus.ControlMessage -> morpheus.ControlMessage
Added stage: <monitor-4; MonitorStage(description=Inference Rate, smoothing=0.001, unit=inf, delayed_start=False, determine_count_fn=None)>
└─ morpheus.ControlMessage -> morpheus.ControlMessage
Added stage: <add-class-5; AddClassificationsStage(threshold=0.5, labels=[], prefix=)>
└─ morpheus.ControlMessage -> morpheus.ControlMessage
Added stage: <serialize-6; SerializeStage(include=['mining'], exclude=['^ID$', '^_ts_'], fixed_columns=True)>
└─ morpheus.ControlMessage -> morpheus.MessageMeta
Added stage: <to-file-7; WriteToFileStage(filename=detections.jsonlines, overwrite=True, file_type=FileTypes.Auto)>
└─ morpheus.MessageMeta -> morpheus.MessageMeta
====Building Pipeline Complete!====
Starting! Time: 1656353254.9919598
Inference Rate[Complete]: 1242inf [00:00, 1863.04inf/s]
====Pipeline Complete====
输出文件 detections.jsonlines
将为每个输入行包含一个布尔值。在某个时候,这些值将从 0
切换到 1
...
{"mining": 0}
{"mining": 0}
{"mining": 0}
{"mining": 0}
{"mining": 1}
{"mining": 1}
{"mining": 1}
{"mining": 1}
{"mining": 1}
{"mining": 1}
{"mining": 1}
{"mining": 1}
...
我们已剥离输入数据以使检测更容易识别。省略参数 --include 'mining'
将在检测文件中显示输入数据。