4. 时间序列基础:构成、平稳性与自相关
做异常检测,说白了就是跟时间序列打交道。你拿到的遥测数据,比如 CPU 使用率、磁盘 IO、网络延迟,本质上都是一串按时间顺序排列的数值。这串数值背后藏着什么规律?怎么把它拆开看明白?这一章我们就来聊聊时间序列的底子。
我个人习惯,拿到任何遥测数据,第一件事不是直接上模型,而是先做一次“拆解”。就像修表一样,你得先知道齿轮怎么转的,才能判断哪个零件坏了。
4.1 时间序列的构成:趋势、季节、残差
任何一个时间序列,都可以拆成三个部分:趋势、季节性、残差。用公式表达就是:
Y(t) = Trend(t) + Seasonal(t) + Residual(t)
或者在某些乘法模型里:
Y(t) = Trend(t) × Seasonal(t) × Residual(t)
我一般在遥测场景下用加法模型居多,因为指标波动幅度相对稳定。下面我们逐个拆开看。
趋势(Trend)
趋势是时间序列长期的变化方向。比如服务器 CPU 使用率随着业务增长,半年内从 30% 慢慢爬到 60%,这就是趋势。它反映的是系统整体的“老化”或“增长”。
季节性(Seasonal)
季节性是指固定周期内的重复模式。注意,这里的“季节”不一定是春夏秋冬。在运维场景里,最常见的周期是 24 小时(日周期)和 7 天(周周期)。
举个例子,我做过一个电商平台的监控。每天晚上 8 点到 10 点是流量高峰,凌晨 4 点到 6 点是低谷。这个规律每天重复,就是日季节性。周末的流量模式和工作日不一样,那就是周季节性。
残差(Residual)
残差是去掉趋势和季节性之后剩下的部分。它应该是随机波动的,没有明显规律。如果残差里还有结构性的模式,说明你的模型没拆干净,或者数据里有异常。
下面这张图展示了时间序列拆解的整体流程:
4.2 平稳性概念
平稳性,是时间序列分析里绕不开的一个概念。很多模型(比如 ARIMA)都要求数据是平稳的。那什么是平稳?
简单说,平稳时间序列的统计性质不随时间变化。具体来说,均值、方差、自协方差都是常数。
你想想看,如果一段 CPU 数据,前半段均值 30%,后半段均值 80%,那它肯定不平稳。因为均值在变。
平稳性分两种:
| 类型 | 定义 | 要求 | 实际场景 |
|---|---|---|---|
| 严平稳 | 所有统计性质完全不变 | 极其严格,几乎不存在 | 理论概念,实际很少用 |
| 弱平稳 | 均值、方差为常数,自相关只与滞后有关 | 较宽松,实用 | 大多数运维指标经处理后可达 |
我们平时说的平稳,默认指弱平稳。怎么判断数据是否平稳?我常用的方法有两个:
- 肉眼观察法:画个时序图,看均值是否上下漂移,方差是否忽大忽小。
- 单位根检验:比如 ADF 检验(Augmented Dickey-Fuller Test)。p 值小于 0.05 就认为平稳。
4.3 自相关与偏自相关
这两个概念,是理解时间序列“记忆性”的关键。说白了,就是看当前时刻的值,跟过去某个时刻的值有没有关系。
自相关函数(ACF)
ACF 衡量的是 Y(t) 和 Y(t-k) 之间的相关性,不考虑中间变量的影响。k 叫滞后阶数。
举个例子,今天的 CPU 使用率和昨天的 CPU 使用率相关吗?如果相关,ACF 在 lag=1 处就会显著非零。
但 ACF 有个问题:它会把间接相关也算进去。比如今天和前天相关,可能是因为今天和昨天相关,昨天和前天相关。这种“传递”效应,ACF 分不清。
偏自相关函数(PACF)
PACF 解决了这个问题。它衡量的是 Y(t) 和 Y(t-k) 之间的直接相关性,剔除了中间滞后项的影响。
我打个比方:ACF 就像问“你和你的祖父长得像吗?”——可能像,但中间隔了你父亲。PACF 则问“剔除你父亲的影响后,你和祖父还像吗?”
在模型选择时,ACF 和 PACF 的形态能帮你判断用 AR 还是 MA 模型:
| 模型类型 | ACF 形态 | PACF 形态 |
|---|---|---|
| AR(p) 自回归 | 拖尾(逐渐衰减) | p 阶后截尾(突然变零) |
| MA(q) 滑动平均 | q 阶后截尾 | 拖尾 |
| ARMA(p,q) | 拖尾 | 拖尾 |
下面这张图展示了 ACF 和 PACF 的典型形态对比:
4.4 在异常检测中的实际应用
讲完理论,我们回到异常检测这个主题。这些概念到底怎么用?
我总结了一个三步走的流程:
- 拆解:对原始序列做 STL 或 X13-ARIMA 分解,得到趋势、季节、残差。
- 平稳化:对残差做平稳性检验。如果不平稳,说明趋势或季节没拆干净,需要调整分解参数。
- 建模:用 ACF/PACF 判断残差的自相关结构,选择合适的模型(如 ARIMA)拟合正常行为,然后检测偏离。
最后,给你一个简单的 Python 代码片段,演示如何做时间序列拆解和 ACF 计算:
import numpy as np
import pandas as pd
from statsmodels.tsa.seasonal import STL
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
from statsmodels.tsa.stattools import adfuller
# 模拟遥测数据:趋势 + 日周期 + 噪声
np.random.seed(42)
t = np.arange(0, 720) # 30天,每小时一个点
trend = 0.01 * t
seasonal = 10 * np.sin(2 * np.pi * t / 24)
noise = np.random.normal(0, 2, len(t))
data = trend + seasonal + noise
# 1. 时间序列拆解
stl = STL(data, period=24)
result = stl.fit()
residual = result.resid
# 2. 平稳性检验(ADF)
adf_stat, p_value, _, _, critical_values, _ = adfuller(residual)
print(f"ADF p-value: {p_value:.4f}")
if p_value < 0.05:
print("残差平稳,可以继续建模")
else:
print("残差不平稳,需要调整分解参数")
# 3. 查看残差的 ACF/PACF
# plot_acf(residual, lags=48)
# plot_pacf(residual, lags=48)
嗯,这一章的内容就到这里。时间序列的构成、平稳性、自相关,这三块是后续所有模型的基础。你把这些概念吃透了,后面讲 ARIMA、季节性分解、异常检测阈值设定,都会顺很多。