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 三种方法怎么选?一张流程图帮你理清楚

下面这张图是我自己总结的决策思路,你可以直接拿来当指南。

异常检测学习方法选择流程 异常检测学习方法选型流程 开始:你有多少标签? 大量高质量标注 监督学习 逻辑回归 / 随机森林 XGBoost / 神经网络 基本无标注 无监督学习 Z-Score / 孤立森林 K-Means / DBSCAN 少量标注 + 大量无标注 半监督学习 单类SVM 自训练 / AutoEncoder 选型要点 • 标签量 > 5000 且质量可靠 → 监督学习,精度最高 • 无标签但有周期规律 → 无监督 + 人工验证阈值 • 只有几十个种子 → 半监督,但种子必须纯 • 实际运维中最常见的是「半监督 + 无监督混合」 • 我个人的建议:先用无监督跑一周收集候选, 再手工标注 Top 异常并切换成半监督。

3.5 实践中的取舍:别迷信算法,先看数据长什么样

我见过不少团队,上来就上深度学习。结果数据量连几百条都没有,模型训完直接过拟合。你想想看,异常检测本质上是在和「未知模式」博弈——只有你自己最清楚业务场景。

几个实用建议:

  • 先跑一个简单的均值+标准差阈值,比任何模型都稳定。
  • 如果误报率超过5%,先排查数据噪声,别急着换算法。
  • 半监督模型的「种子」最好每季度重新确认一次,业务变了,正常模式也会变。

避坑指南:我曾经为了炫技,在生产环境上了一个基于LSTM的异常检测模型。结果因为日志采集延迟,特征时间戳对不上,模型永远比真实异常慢10分钟。后来乖乖换回孤立森林+滚动统计……所以,模型再高级,也得先问一句:数据管道靠谱吗?

三种学习方式没有的好坏。监督学习贵在精确,无监督学习胜在省事,半监督学习则是在两者之间找平衡。我个人的经验是:大部分生产系统最后会演变成「半监督 + 无监督」的混合架构——用无监督做初筛,用半监督做精判,再辅以少量人工复核。这样既能抗住95%的日常流量,又不会漏掉那些真正要命的0.1%。