4、异常检测基础:统计方法(3Sigma、箱线图)、时间序列分解、移动平均法
聊到异常检测,很多新手一上来就上深度学习模型。我个人习惯是,先拿统计方法试试水。为什么?因为简单、可解释、还快。你想想看,一个线上系统每天几亿条指标,你不可能每条都跑神经网络。统计方法就是那个“先开枪后瞄准”的利器。
今天咱们就掰扯清楚四个最常用的统计异常检测手段:3Sigma、箱线图、时间序列分解、移动平均法。这些方法我几乎在每个项目里都用过,踩过坑,也填过坑。
4.1 3Sigma法则:最朴素的“离群点”判断
3Sigma,说白了就是假设你的数据服从正态分布。然后呢,均值加减三个标准差,落在这个范围之外的,我就认为它是异常。
公式很简单:
上限 = μ + 3σ
下限 = μ - 3σ
我在项目中遇到过一个问题:某电商平台的QPS监控,平时很平稳,但一到双十一大促,流量暴涨,3Sigma直接报警报疯了。其实那不是异常,那是业务高峰。所以用3Sigma之前,你得先确认数据是不是真的“正常波动”。
代码实现也很直接:
import numpy as np
def detect_3sigma(data):
mean = np.mean(data)
std = np.std(data)
upper = mean + 3 * std
lower = mean - 3 * std
anomalies = [x for x in data if x > upper or x < lower]
return anomalies, upper, lower
4.2 箱线图:不依赖分布的“稳健派”
3Sigma要求正态分布,但现实世界的数据哪有那么乖?这时候箱线图就派上用场了。它基于四分位数,不假设任何分布。
核心概念:
- Q1:下四分位数(25%分位)
- Q3:上四分位数(75%分位)
- IQR:四分位距 = Q3 - Q1
- 异常判定:小于 Q1 - 1.5×IQR 或大于 Q3 + 1.5×IQR
嗯,这里要注意。1.5这个系数是经验值,不是铁律。我在做服务器CPU使用率监控时,发现业务特性导致数据偏态严重,1.5倍IQR会漏掉很多异常。后来我调成了2.0倍,效果反而更好。说白了,参数得根据你的业务场景调。
import numpy as np
def detect_boxplot(data, multiplier=1.5):
q1 = np.percentile(data, 25)
q3 = np.percentile(data, 75)
iqr = q3 - q1
lower = q1 - multiplier * iqr
upper = q3 + multiplier * iqr
anomalies = [x for x in data if x < lower or x > upper]
return anomalies, lower, upper
4.3 时间序列分解:把趋势和周期拆开看
前面两种方法都是“静态”的,但运维指标是随时间变化的。比如每天的访问量有周期性,周末低、工作日高。如果你直接用3Sigma去检测,周末的数据全被判成异常了。
时间序列分解就是干这个的。它把一条原始序列拆成三部分:
- 趋势分量(Trend):长期上升或下降
- 季节分量(Seasonal):周期性波动
- 残差分量(Residual):剩下的随机波动
异常检测的核心,就是盯着残差分量看。因为趋势和周期都是“正常”的部分,只有残差里蹦出来的大尖刺,才是真正的异常。
我记得有一次帮一个金融客户做交易量监控。他们的数据有明显的日周期和周周期。直接用原始数据做阈值,误报率高达40%。分解之后只看残差,误报率降到了5%以下。效果立竿见影。
import statsmodels.api as sm
def decompose_and_detect(series, period=24):
# period=24 表示以24小时为周期
result = sm.tsa.seasonal_decompose(series, model='additive', period=period)
residual = result.resid.dropna()
# 对残差用3Sigma检测
anomalies, _, _ = detect_3sigma(residual.values)
return anomalies, result
4.4 移动平均法:平滑掉“毛刺”再看异常
移动平均法,说白了就是拿一个滑动窗口,算窗口内的平均值。这样可以把短期的随机抖动平滑掉,露出真正的趋势。
常用的有两种:
- 简单移动平均(SMA):窗口内所有点权重一样
- 指数加权移动平均(EWMA):越近的点权重越大
我个人更偏爱EWMA。为什么?因为它对最近的变化更敏感。比如服务器突然CPU飙升,EWMA能更快地跟上这个变化,而SMA反应会慢半拍。
异常检测的做法是:用移动平均预测下一个点的值,然后看实际值和预测值的偏差。如果偏差超过某个阈值,就报警。
import pandas as pd
def ewma_detect(series, span=10, threshold=3):
# span是窗口大小,threshold是标准差倍数
ewma = series.ewm(span=span).mean()
residuals = series - ewma
std = residuals.std()
anomalies = series[abs(residuals) > threshold * std]
return anomalies, ewma
4.5 四种方法怎么选?一张表说清楚
| 方法 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 3Sigma | 数据近似正态分布 | 简单、计算快 | 对极端值敏感、依赖正态假设 |
| 箱线图 | 分布未知、有偏态 | 稳健、不依赖分布 | 对周期性数据不敏感 |
| 时间序列分解 | 有明显周期和趋势 | 能分离出真正的异常 | 需要等间隔数据、计算稍复杂 |
| 移动平均法 | 实时流式数据 | 适合在线检测、反应快 | 窗口大小难调、滞后性 |
4.6 知识体系总览
下面这张图,我把这四种方法的核心逻辑和适用场景串起来了。你可以把它当作一个“选型指南”。
好了,这四种方法讲完了。你可能会问,到底用哪个?我的回答是:别纠结,先跑一遍箱线图看看数据长什么样。如果数据有明显的周期,就上时间序列分解。如果是实时流,就用EWMA。记住,没有银弹,只有最合适的工具。