3. 异常检测基础:监督学习、无监督学习、半监督学习在频率异常中的应用
聊到异常检测,很多人第一反应就是「用机器学习啊」。但真正上手的时候,你会发现选哪种学习方式,直接决定了你能不能把模型跑起来。
我个人习惯把频率异常检测的问题分成三类:你有多少标签数据?
- 如果历史攻击记录全——用监督学习。
- 如果基本没标注——用无监督学习。
- 如果只有少量已知异常——用半监督学习。
下面我结合实际踩过的坑,一个一个拆开讲。
3.1 监督学习:有标签就是王道,但标签才是真奢侈
监督学习说白了,就是拿一堆「已确认正常」和「已确认异常」的样本去训练模型。在Secrets访问频率这个场景里,特征其实很直观:
- 时间粒度:每分钟请求次数、每小时访问量
- 统计特征:均值、方差、峰值、波谷
- 上下文:客户端IP、Secret ID、操作类型
常见算法:
| 算法 | 适用场景 | 我在项目中的感受 |
|---|---|---|
| 逻辑回归 | 二分类,特征线性可分 | 简单快速,但容易被突发流量搞崩 |
| 随机森林 | 特征多,有非线性关系 | 鲁棒性好,我常用它做基线模型 |
| XGBoost | 大规模数据,追求精度 | 调参麻烦,但效果确实好 |
| 神经网络 | 复杂时序模式 | 除非数据量特别大,否则不推荐 |
关键点:监督学习依赖高质量标注。我曾经在一个客户现场,他们几个月才积累了200条异常记录,其中一半还是误报。这种数据量训练出来的模型,基本就是摆设。
给你看一段简单的特征工程代码示例,基于每分钟访问次数构造特征:
import pandas as pd
import numpy as np
def build_features(access_logs):
# access_logs:DataFrame,包含timestamp和secret_id
df = access_logs.copy()
df['minute'] = df['timestamp'].dt.floor('T')
# 每分钟聚合
agg = df.groupby(['secret_id', 'minute']).size().reset_index(name='count')
# 滑动窗口统计
agg['mean_5min'] = agg.groupby('secret_id')['count'].transform(
lambda x: x.rolling(5, min_periods=1).mean()
)
agg['std_5min'] = agg.groupby('secret_id')['count'].transform(
lambda x: x.rolling(5, min_periods=1).std()
)
agg['max_10min'] = agg.groupby('secret_id')['count'].transform(
lambda x: x.rolling(10, min_periods=1).max()
)
# 填充空值
agg.fillna(0, inplace=True)
return agg
3.2 无监督学习:零标签,全靠「异常就是少数派」
现实中大部分团队连标注都没有。这时候无监督学习就派上用场了。基本假设就一个:正常行为占了绝大多数,异常行为在分布上会「离群」。
在Secrets访问频率里,常用的无监督方法:
- 基于统计的:3-sigma、Z-Score、MAD(中位差)。适合固定周期、无突变的场景。
- 基于聚类的:K-Means、DBSCAN。把正常访问聚成一团,剩下的小簇就是异常。
- 基于孤立森林:随机切割特征空间,异常路径短。我多次用它处理高维时序特征。
小提示:孤立森林对高频异常很敏感,但低频突刺容易漏掉。我在一个支付系统里遇到过:某Secret每分钟访问量从5次瞬间跳到500次,孤立森林确实抓住了。但如果是慢速爬升——比如从5次慢慢涨到50次持续一周,孤立森林就几乎无效。
无监督学习最大的坑是「误报率」——因为没有标签,你不知道模型的判断边界在哪。我曾经部署一套DBSCAN模型,头三天报警3000多次,运维同事差点要砍我……后来调了邻域半径和最小样本数才收敛。
3.3 半监督学习:标签少也能干,关键是用好「孤立点」
半监督学习介于两者之间。你手里有一点点正常样本(或者一点点异常样本),但大量数据没标注。常见思路:
- 自训练:先用少量标注训练一个弱分类器,对未标注数据打伪标签,反复迭代。
- 单类SVM:只用一个类别的正常样本训练,把不属于这个空间的都视为异常。
- 基于重构的方法:用AutoEncoder学习正常模式,重构误差大的就是异常。
说句实话,半监督是我个人最喜欢用的一类方法。因为在实际运维中,你总能手工确认几例正常访问模式和几例攻击模式,但没精力打上万条标签。只要能给出几十条高质量种子,半监督就能扩展到全量数据。
注意:半监督模型对种子质量极其敏感。我曾经犯过一个低级错误——种子样本里混入了两条真正的攻击流量,结果模型学歪了,认为正常行为就是带攻击特征的模式。后来排查了三天……所以,种子集务必人工复核三遍。
3.4 三种方法怎么选?一张流程图帮你理清楚
下面这张图是我自己总结的决策思路,你可以直接拿来当指南。
3.5 实践中的取舍:别迷信算法,先看数据长什么样
我见过不少团队,上来就上深度学习。结果数据量连几百条都没有,模型训完直接过拟合。你想想看,异常检测本质上是在和「未知模式」博弈——只有你自己最清楚业务场景。
几个实用建议:
- 先跑一个简单的均值+标准差阈值,比任何模型都稳定。
- 如果误报率超过5%,先排查数据噪声,别急着换算法。
- 半监督模型的「种子」最好每季度重新确认一次,业务变了,正常模式也会变。
避坑指南:我曾经为了炫技,在生产环境上了一个基于LSTM的异常检测模型。结果因为日志采集延迟,特征时间戳对不上,模型永远比真实异常慢10分钟。后来乖乖换回孤立森林+滚动统计……所以,模型再高级,也得先问一句:数据管道靠谱吗?
三种学习方式没有的好坏。监督学习贵在精确,无监督学习胜在省事,半监督学习则是在两者之间找平衡。我个人的经验是:大部分生产系统最后会演变成「半监督 + 无监督」的混合架构——用无监督做初筛,用半监督做精判,再辅以少量人工复核。这样既能抗住95%的日常流量,又不会漏掉那些真正要命的0.1%。