预测系统故障:异常检测与无监督学习实战指南
2026/9/15 16:56:15 网站建设 项目流程

系统故障往往不是瞬间爆发的。磁盘 IO 持续飙升、接口响应逐渐变慢、内存占用缓慢爬坡,这些“小信号”在故障真正发生前就已经出现,只是很少被及时捕捉。Sequoia 孵化的 Empirik 正是从这个角度切入,最近宣布独立运营并拿到 2100 万美元种子轮融资,核心方向就是“预测系统故障”。本文不打算只做新闻复述,而是结合这次事件,把预测系统故障这件事从概念到实现完整拆一遍:它到底在预测什么、主流技术路线有哪些、一个可落地的检测管道怎么写、以及真正放到生产环境里会遇到哪些坑。

1. 预测系统故障到底在解决什么问题

1.1 从“告警驱动”到“预测驱动”

传统运维基本是“告警驱动”的:系统出问题了,监控平台触发阈值,值班同学收到钉钉或电话,然后开始排查。这种模式最大的问题是:故障已经发生,影响已经造成,团队只是在做“事后响应”。

预测系统故障的思路完全不同。它希望基于历史数据、实时指标和日志信息,提前判断某个组件或服务是否正在走向异常状态,从而在故障真正发生之前完成干预。比如某个数据库实例的慢查询数量在过去 30 分钟持续上升,虽然当前 CPU 和内存都还正常,但模型可以给出“1 小时后存在高概率故障风险”的预警,运维就能提前扩容或重启。

Empirik 的定位也在这个方向上。根据公开信息,这家公司希望帮助企业在大规模系统环境中更早发现故障信号,减少业务中断时间。2100 万美元的种子轮融资说明资本市场对这个方向是认可的,但从技术角度看,真正难的从来不是“想清楚要做什么”,而是“怎么在复杂多变的系统中把预测做得足够准、足够稳定”。

1.2 核心概念:异常检测、根因定位与故障预测

要理解预测系统故障,需要先分清三个经常被混在一起的概念:

概念解决的问题典型输出
异常检测发现“当前状态是否偏离正常”某指标在 15:32 出现异常尖刺
根因定位找到“是什么导致了异常”异常由订单服务线程池耗尽导致
故障预测判断“未来是否会发生故障”30 分钟内有 85% 概率出现磁盘写满故障

三者的关系是递进的。异常检测是基础,它负责从海量指标中挑出可疑点;根因定位在异常点基础上做关联分析;而故障预测则需要结合时间序列的趋势、周期性和历史故障模式,对未来状态做推断。Empirik 这类产品通常不是只做其中某一环,而是把三件事打包成一个完整的“可观测性 + 预测”平台。

1.3 典型应用场景

预测系统故障的场景比很多人想象的要宽,至少包含以下几类:

  • 基础设施层:预测磁盘空间不足、CPU 持续过载、内存泄漏导致的 OOM、网络延迟劣化。
  • 应用层:预测接口 P99 延迟即将超时、某服务的错误率即将达到告警阈值。
  • 中间件层:预测消息队列积压量持续增长、连接池即将耗尽、慢 SQL 数量激增。
  • 业务连续性:在大促、发布、变更等场景下预测变更引入的稳定性风险。

这些场景的共同特点是:异常不是瞬间发生的,而是呈现一定的趋势或前兆。只要趋势可以被建模,就有被提前预测的可能。

2. 故障预测的主流技术路线

2.1 基于阈值的静态检测

最朴素的方法是为每个指标配置固定阈值,比如 CPU > 85% 就告警。这种方式实现成本最低,但问题也很明显:不同业务、不同时段的指标波动范围差异极大。同样的 CPU 使用率在业务低峰期可能是异常,在高峰期却完全正常。静态阈值很难适应这种动态变化,误报率和漏报率都高。

2.2 基于统计的时序异常检测

统计方法比固定阈值进了一步,它会根据历史数据的分布来判断当前值是否偏离“正常模式”。常见的包括:

  • 3-Sigma 原则:假设指标服从正态分布,超过均值三个标准差的点视为异常。
  • 移动平均 / 指数平滑:通过平滑历史值来估计当前期望值,实际值与期望值偏差过大则报警。
  • Holt-Winters 季节性分解:对具有明显周期性的指标(如每天流量高峰)做趋势、季节和残差分解,再检测残差中的异常。

统计方法适用于指标规律性较强、数据比较“干净”的场景,但对突发性变化和非周期模式响应较差。

2.3 基于机器学习的有监督异常检测

如果系统里已经积累了大量历史故障样本,可以通过有监督学习训练一个二分类模型:输入是时间窗口内的特征(如 CPU、内存、QPS、错误率、慢请求数),输出是“正常”或“即将故障”。

这类方法的准确率通常比阈值和统计方法高,但对样本质量和标注要求很高。故障本身就是低频事件,正常数据和故障数据之间的比例可能达到几万比一,这种情况下训练出来的模型很容易把故障样本当成噪声忽略掉。

2.4 基于无监督和半监督的异常检测

大部分预测系统故障的产品,实际落地时用的是无监督或半监督方法。核心逻辑是:我们不一定要见过所有故障模式才能发现异常,只要能够建立“正常状态”的模型,偏离正常模型的状态就可以标记为异常。

常用的算法包括:

  • Isolation Forest(孤立森林):适合高维数据的异常检测,通过随机切分特征空间,异常点更容易被孤立出来。
  • 自编码器(Autoencoder):用神经网络学习正常数据的压缩表示,重构误差大的样本视为异常。
  • One-Class SVM:在特征空间中学习一个边界,落在边界之外的样本为异常。
  • 时序预测残差法:用 ARIMA、Prophet 或 LSTM 预测下一时刻的值,如果实际值与预测值残差过大,就认为出现异常。

对于“预测系统故障”来说,无监督方法在工程上更实用,因为它不依赖大量已标注的故障样本,而且能够发现从未见过的异常模式。

2.5 技术路线如何选

选哪种路线取决于数据状况和业务目标:

  • 刚开始做、没有历史标注:从统计方法和 Isolation Forest 起步。
  • 有明确故障标签、样本量足够:尝试 XGBoost / LightGBM 有监督模型。
  • 指标具有强时间依赖性:使用 LSTM 或 Transformer 类时序模型,但要注意训练成本和稳定性。
  • 大规模多指标联动场景:先做主成分分析或相关分析降维,再套用异常检测算法。

Empirik 这类产品面对的通常是超大规模分布式系统,指标维度高、数据噪音大、故障模式复杂,所以背后必然是多种算法组合,而不是靠单模型打天下。

3. 环境准备与实验数据

3.1 环境说明

本文的实战部分使用 Python 完成,主要依赖以下库:

  • pandas:数据处理
  • numpy:数值计算
  • scikit-learn:机器学习模型
  • pyod:异常检测算法库
  • matplotlib:结果可视化

示例中的版本以实际安装为准,建议使用 Python 3.9 及以上版本。

pip install pandas numpy scikit-learn pyod matplotlib

如果是在公司内网环境,建议先创建独立的虚拟环境:

python -m venv fault_prediction_env source fault_prediction_env/bin/activate # Windows 下使用 fault_prediction_env\Scripts\activate pip install pandas numpy scikit-learn pyod matplotlib
3.2 实验数据说明

故障预测领域有一个经典公开数据集:NASA 轴承退化数据集。该数据集包含多个轴承从正常状态到最终失效的全程振动信号,非常适合用来演示“从正常到故障”的退化趋势。

我们可以使用数据集中的部分特征来模拟一个简单的故障预测场景。为了方便演示,不直接读原始信号文件,而是构造一个包含多个监测指标的时间序列数据集,其中最后一个阶段模拟故障前兆。

实际项目中,运维监控系统采集的指标通常是类似下面的结构:

时间戳cpu_usagememory_usagedisk_ioerror_rateresponse_timelabel
2025-01-01 00:00:0042.568.2105.30.01120.40
2025-01-01 00:01:0044.168.5111.80.02123.10

这里的label表示该时刻是否已处于“故障前兆期”或“即将故障”状态,用于评估预测效果。

4. 故障预测完整实战

下面我们从头实现一个故障预测的最小可用管道,包含数据模拟、特征构造、模型训练和结果评估四个步骤。

4.1 构造模拟数据

先模拟一组带趋势变化的指标数据。数据共 5000 分钟,前 60% 是正常状态,后 40% 开始出现性能退化信号,最终进入故障前兆期。

import numpy as np import pandas as pd np.random.seed(42) n = 5000 time_index = pd.date_range('2025-01-01', periods=n, freq='min') # 正常阶段的指标 cpu_usage = np.random.normal(40, 5, n) memory_usage = np.random.normal(60, 3, n) disk_io = np.random.normal(100, 20, n) error_rate = np.random.exponential(0.01, n) response_time = np.random.normal(120, 10, n) # 从第 3000 分钟开始加入退化趋势 degraded = np.zeros(n) degraded[3000:] = np.linspace(0, 1, n - 3000) cpu_usage = cpu_usage + degraded * 35 memory_usage = memory_usage + degraded * 25 disk_io = disk_io + degraded * 150 error_rate = error_rate + degraded * 0.5 response_time = response_time + degraded * 180 df = pd.DataFrame({ 'timestamp': time_index, 'cpu_usage': cpu_usage, 'memory_usage': memory_usage, 'disk_io': disk_io, 'error_rate': error_rate, 'response_time': response_time }) # 最后 800 分钟标为故障前兆 df['label'] = 0 df.loc[4200:, 'label'] = 1 print(df.head()) print(df['label'].value_counts())

运行结果可以看到前 5 行数据以及标签分布。这个模拟数据的含义是:前 3000 分钟各项指标稳定在正常区间,之后出现缓慢性退化,最后 800 分钟已经满足故障前兆特征。

4.2 特征构造

原始监控指标可以直接作为特征,但为了提升预测效果,通常会构造时序派生特征,包括滑窗均值、滑窗标准差、变化率等。

def build_features(data, window=10): df_feat = data.copy() # 滑动窗口均值 for col in ['cpu_usage', 'memory_usage', 'disk_io', 'error_rate', 'response_time']: df_feat[f'{col}_rolling_mean'] = df_feat[col].rolling(window=window).mean() df_feat[f'{col}_rolling_std'] = df_feat[col].rolling(window=window).std() # 前后差分表示变化趋势 for col in ['cpu_usage', 'memory_usage', 'disk_io', 'error_rate', 'response_time']: df_feat[f'{col}_diff'] = df_feat[col].diff() return df_feat df_feat = build_features(df) # 去掉包含空值的行 df_feat = df_feat.dropna().reset_index(drop=True) feature_cols = [col for col in df_feat.columns if col not in ['timestamp', 'label']] X = df_feat[feature_cols].values y = df_feat['label'].values print(feature_cols) print(f'特征矩阵形状: {X.shape}')

这里通过滚动窗口均值捕获短期趋势,通过滚动标准差捕获波动变化,通过差分值捕获实时变化率。需要注意的是,rolling会导致窗口期内的数据产生空值,所以使用dropna()去掉这些不完整的样本。

4.3 模型训练与异常检测

本文使用两种算法做对比:一种是适合高维异常检测的 Isolation Forest,一种是基于重构误差的 PCA 异常检测。

这里要说明一下:虽然我们生成了标签列,但实际训练时不使用标签,而是完全依靠无监督方法学习正常数据分布。

from sklearn.ensemble import IsolationForest from sklearn.decomposition import PCA from sklearn.preprocessing import StandardScaler # 数据标准化 scaler = StandardScaler() X_scaled = scaler.fit_transform(X) # 仅使用前 60% 数据训练,模拟“只知道正常状态” train_size = int(len(X_scaled) * 0.6) X_train = X_scaled[:train_size] X_test = X_scaled[train_size:] # 模型一:Isolation Forest iso_forest = IsolationForest( contamination=0.1, random_state=42, n_estimators=200 ) iso_forest.fit(X_train) # 注意:IsolationForest 中 -1 表示异常,1 表示正常 iso_pred = iso_forest.predict(X_test) iso_anomaly = (iso_pred == -1).astype(int) # 模型二:PCA 重构误差 pca = PCA(n_components=0.95, random_state=42) pca.fit(X_train) X_train_pca = pca.transform(X_train) X_test_pca = pca.transform(X_test) X_train_recon = pca.inverse_transform(X_train_pca) X_test_recon = pca.inverse_transform(X_test_pca) train_error = np.mean((X_train - X_train_recon) ** 2, axis=1) test_error = np.mean((X_test - X_test_recon) ** 2, axis=1) # 用训练集误差的 95 分位数作为阈值 threshold = np.percentile(train_error, 95) pca_anomaly = (test_error > threshold).astype(int)

这里采用“训练集只包含正常数据”的设定,更贴近真实场景。因为故障数据非常少,正常数据才是最容易获得的。

4.4 模型评估

由于测试集后段包含了真实的故障前兆标签,我们可以计算准确率、召回率等指标。

from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score def evaluate_model(name, y_true, y_pred): print(f'===== {name} =====') print(f'Accuracy : {accuracy_score(y_true, y_pred):.4f}') print(f'Precision: {precision_score(y_true, y_pred, zero_division=0):.4f}') print(f'Recall : {recall_score(y_true, y_pred, zero_division=0):.4f}') print(f'F1-Score : {f1_score(y_true, y_pred, zero_division=0):.4f}') print() # 注意:这里评估的是后 40% 部分的预测能力 y_true = y[train_size:] evaluate_model('Isolation Forest', y_true, iso_anomaly) evaluate_model('PCA Reconstruction Error', y_true, pca_anomaly)

运行后,Isolation Forest 通常能够在故障前兆段取得较高的召回率,但前段正常数据可能存在少量误报。这就是故障预测系统面临的核心矛盾:提前发现故障与减少误报之间需要做平衡。

4.5 可视化预测结果

可以通过 Matplotlib 将原始响应时间曲线和模型标记的异常点画在同一张图上。

import matplotlib.pyplot as plt fig, ax = plt.subplots(figsize=(12, 5)) ax.plot(df_feat['timestamp'][train_size:], df_feat['response_time'][train_size:], label='response_time', color='blue', alpha=0.6) anomaly_idx = np.where(iso_anomaly == 1)[0] ax.scatter(df_feat['timestamp'][train_size:].iloc[anomaly_idx], df_feat['response_time'][train_size:].iloc[anomaly_idx], color='red', s=20, label='ISF Anomaly') ax.axvline(df_feat['timestamp'][train_size:].iloc[0], color='gray', linestyle='--', label='train/test split') ax.legend() plt.title('Fault Prediction Result - Isolation Forest') plt.show()

可视化后可以看到,模型不是在整个退化过程中一直报异常,而是从某个时间点开始密集标记异常。这个时间点越接近真实的故障前兆段起点,说明模型对退化的感知越灵敏。

5. 从离线模型到在线故障预测系统

离线训练集和在线检测之间还有很大的工程距离。一个完整的预测系统故障平台,至少要包含以下几个模块。

5.1 数据采集层

监控数据的来源通常包括:

  • 主机 Agent:采集 CPU、内存、磁盘、网络等指标。
  • APM Agent:采集接口调用链、响应时间、错误率。
  • 中间件插件:采集 MQ 积压量、连接池状态、慢 SQL 数量。
  • 日志采集:从业务日志中提取异常关键字。

采集到的数据会进入时序数据库(如 Prometheus、InfluxDB、ClickHouse)或消息队列(如 Kafka),再被故障预测引擎消费。

5.2 特征计算层

在线环境无法像离线实验那样一次性计算所有特征,需要使用流式计算引擎,常见方案是 Flink + Kafka。每个时间窗口(如 1 分钟、5 分钟)计算一次指标均值、标准差、变化率等特征,并写入特征存储。

5.3 模型推理层

模型推理层需要具备以下能力:

  • 定时调度或事件触发式预测。
  • 多模型并行推理,例如同时运行多个异常检测算法。
  • 推理结果写入告警平台或工单系统。
  • 支持模型版本管理和回滚。
5.4 告警与干预闭环

预测成功的关键不在模型本身,而在于“预测出来之后怎么办”。需要确定以下流程:

  • 预测结果分几个等级:提示、预警、严重。
  • 不同等级对应什么干预动作:观察、日志采集增强、自动扩容、重启实例。
  • 干预后必须做效果评估:如果干预后系统恢复正常,说明预测是有效的;如果模型不断误报,需要调整阈值或重训模型。

下面是一个简化的在线故障预测引擎伪代码:

import time from datetime import datetime class FaultPredictor: def __init__(self, model, scaler, threshold=0.85): self.model = model self.scaler = scaler self.threshold = threshold def predict_once(self, metrics): """ metrics: dict, 当前窗口的指标数据 """ feature_vector = self._extract_features(metrics) feature_scaled = self.scaler.transform([feature_vector]) risk_score = self.model.predict_proba(feature_scaled)[0][1] return {'timestamp': datetime.now(), 'risk_score': risk_score} def _extract_features(self, metrics): # 实际项目中这里需要计算滚动窗口特征 return [ metrics['cpu_usage'], metrics['memory_usage'], metrics['disk_io'], metrics['error_rate'], metrics['response_time'] ] # 使用示例 if __name__ == '__main__': predictor = FaultPredictor(model=None, scaler=None) sample_metrics = { 'cpu_usage': 78.5, 'memory_usage': 85.2, 'disk_io': 320.1, 'error_rate': 0.12, 'response_time': 320.6 } result = predictor.predict_once(sample_metrics) if result['risk_score'] >= predictor.threshold: print(f"高风险告警: {result}")

6. 常用算法深入对比

6.1 有监督 vs 无监督
维度有监督无监督
数据要求需要标注故障标签只需正常数据
准确率上限中等
泛化能力依赖已见故障模式可发现未知异常
落地难度样本获取困难较容易
典型算法XGBoost、LightGBM、LSTMIsolation Forest、Autoencoder、PCA
6.2 统计方法 vs 机器学习方法

统计方法在可解释性上占优,什么样的点超出几个标准差都可以说清楚。机器学习方法通常准确率更高,但是黑盒属性更强,在故障预测场景中,解释性往往和准确率同样重要。推荐的做法是:用统计方法做基线,用机器学习方法做增强,最后通过人工规则对模型输出做校验。

6.3 单指标 vs 多指标联合检测

单指标检测实现简单,但大多数故障都涉及多个指标联动。例如磁盘 IO 升高往往会带动响应时间上升,如果只看响应时间,可能会漏掉真正的根因。多指标联合检测难度更大,但更能反映系统真实状态。

7. 常见问题与排查思路

7.1 模型误报率太高
问题现象常见原因解决思路
大量正常波动被判为故障前兆噪声过大、阈值过严先做数据平滑,调低异常比例或提高阈值
业务高峰被频繁告警模型未学习到周期性增加周期特征,例如小时数、星期数
指标存在明显离群点采集端异常或脏数据增加数据清洗逻辑,过滤采集故障数据
7.2 模型没有提前预测能力

如果模型输出异常的时间点几乎和真实故障时间点重合,说明特征不够“超前”。需要把预测目标从“当前是否异常”调整为“未来 N 个时间窗口内是否故障”。这种“超前预测”会用到时序标签平移的技巧:

# 将标签向前平移 10 个时间窗口,让模型学会预测 10 分钟后的故障状态 future_steps = 10 df_feat['label_shifted'] = df_feat['label'].shift(-future_steps) df_feat = df_feat.dropna()

通过标签平移,模型在 t 时刻看到的特征是当前状态,要预测的是 t + 10 分钟后的状态,这样真正拿到预测结果时,还留有干预时间。

7.3 训练数据和线上数据分布差异大

模型在训练时表现很好,上线后效果退化,通常是特征分布发生了漂移。需要考虑定时重训机制、增加特征监控,以及引入在线学习能力。

7.4 多模型输出不一致

不同算法对同一段数据的判断可能不同,出现矛盾时建议用投票机制或加权融合:多个模型都判定异常才告警,提高精度;任意模型判定异常就告警,提高召回。具体选哪种取决于业务容忍误报还是漏报。

8. 工程实践与落地建议

8.1 从单指标、单服务的预测开始

故障预测系统最容易犯的错误是“一开始就想做全平台”。实际项目建议从一个核心服务开始,选三到五个最能反映健康状态的指标,先离线跑通方案,再逐步扩大覆盖范围。

8.2 建立“预测效果”评估机制

不要让模型生成的风险分变成一个没人看的数字。需要建立“预测-干预-复盘”的闭环评估:每次预测告警是否对应了真实的运维事件?如果没有,是误报还是模型发现了我们没注意到的问题?这些反馈要持续回流到训练数据中。

8.3 安全与权限

故障预测引擎通常会访问大规模监控数据和关键基础设施信息,内部使用时要遵循最小权限原则,防止越权访问导致的数据泄露。任何自动化的干预操作(如自动重启、自动扩容)应在测试环境充分验证后再开放在线能力。

8.4 关注数据质量

再强的算法也救不了脏数据。采集端要重点检查:指标缺失、时间戳乱序、重复采集、单位不一致。建议在数据入口统一做质量校验和时间对齐。

8.5 不要把预测结果当作唯一依据

在工程落地上,故障预测更适合作为“辅助决策”的一部分,而不是替代现有监控。建议将预测结果与传统阈值告警、人工巡检结合使用,逐步积累经验,再决定是否提升自动化程度。

9. 总结与后续学习

这次 Empirik 拿到的 2100 万美元种子轮融资,再一次把“预测系统故障”这个 AIOps 方向带入了更多团队的视野。对于普通开发者和运维团队来说,不需要一开始就建设一个庞大的商业化平台,完全可以从一套开源技术栈、一个核心服务的监控数据开始,逐步搭建属于自己的故障预测能力。

本文从故障预测的概念、技术路线到完整代码示例,再到在线系统架构和常见问题,覆盖了从 0 到 1 的关键路径。你可以先跑通 Isolation Forest 和 PCA 两个基础模型,理解无监督异常检测的核心逻辑,再尝试引入 LSTM、Prophet 或者有监督模型。预测系统故障不是某一种算法能搞定的,它是数据工程、算法、运维流程和业务理解共同作用的结果。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询