4. 机器学习基础概念:分类与异常检测的区别、监督/无监督/半监督学习、过拟合与欠拟合
好,我们直接进入正题。这一章要聊的这几个概念,是后续所有模型工作的地基。你如果把它们理清了,后面看算法就不会犯迷糊。
本章核心:搞懂分类和异常检测到底有什么不同,三种学习范式该怎么选,以及模型为啥会"学过头"或"没学够"。
4.1 分类与异常检测——本质上的差异
很多人一开始会混淆这两个概念。我刚开始做审计日志时也犯过这个错。简单来说:分类是找共性,异常检测是找差异。
分类模型的任务是"这个东西属于哪一类"。它需要大量带标签的数据,每个样本都告诉模型"这是A类""那是B类"。模型学的是不同类别之间的边界。
异常检测就完全不一样了。它的目标是"这个东西是不是跟大多数不一样"。正常样本多如牛毛,异常样本凤毛麟角。模型只需要见过正常行为,就能判断出异常。
| 对比维度 | 分类 | 异常检测 |
|---|---|---|
| 数据标签 | 需要完整标签 | 通常不需要标签,或仅有正常标签 |
| 数据分布 | 各类别相对平衡 | 极度不平衡(异常常低于1%) |
| 学习目标 | 找到类别间的决策边界 | 描述正常数据的边界 |
| 评估指标 | 准确率、精确率、召回率 | F1、PR曲线、AUC |
| 典型算法 | 逻辑回归、随机森林、SVM | Isolation Forest、LOF、One-Class SVM |
我的经验:在审计日志场景中,异常样本占比往往不到 0.1%。如果硬用分类模型,你会被"类别不平衡"折磨得够呛。我个人建议——优先尝试异常检测方法,除非你确信能拿到足够多的标注异常样本。
你在实际项目里可以这样判断:如果业务方说"我们想识别出所有攻击行为",但你翻遍日志只找到几十条攻击记录,那就乖乖走异常检测路线。如果你有上万条标注好的攻击和正常数据,分类也可以试试。
看看代码上的区别,会更直观:
# 分类路线:需要标签 y
from sklearn.ensemble import RandomForestClassifier
clf = RandomForestClassifier()
clf.fit(X_train, y_train) # y 包含 0(正常)和 1(异常)
# 异常检测路线:不需要标签
from sklearn.ensemble import IsolationForest
detector = IsolationForest(contamination=0.01)
detector.fit(X_train) # 只用 X,不需要 y
predictions = detector.predict(X_test) # 返回 -1 为异常
看到了吧?异常检测不需要标签也能干活。这在审计日志这种标签稀缺的领域,简直是救命稻草。
4.2 三大学习范式——怎么选才对路
你肯定经常听到这三个词:监督、无监督、半监督。它们到底该怎么选?
监督学习:所有训练样本都有标签。模型直接从"输入-输出"对中学习。效果通常最好,但标签成本最高。在审计场景中,如果你有标注团队,或者能用规则自动生成一批标签,可以试试。
无监督学习:完全没有标签。模型自己找数据中的结构。异常检测里的 Isolation Forest、聚类算法都属于这一类。我当年第一次做日志异常检测时,手里只有几十条标注数据,根本没法训练分类器。后来直接用无监督方法,效果反而超出预期。
半监督学习:少量标签 + 大量无标签数据。这其实最接近真实场景——你费劲心力标注了几百条日志,剩下几百万条都是没标的。半监督学习能利用无标签数据帮助模型理解数据分布,再用少量标签指导方向。
| 学习范式 | 标签需求 | 典型场景 | 优点 | 缺点 |
|---|---|---|---|---|
| 监督学习 | 全部有标签 | 已知攻击模式,标签充足 | 精度高,可解释性强 | 标签成本高,对未知攻击无能为力 |
| 无监督学习 | 无标签 | 标签极少,或未知攻击较多 | 能发现未知异常,部署快 | 误报率可能较高 |
| 半监督学习 | 少量标签 | 有部分标注,大量未标注日志 | 兼顾精度和覆盖率 | 实现复杂度稍高 |
你想想看,大多数审计日志项目的标签状态其实介于"完全没有"和"足够多"之间。这时候,我建议你从无监督方法起步,建立基线,再通过半监督方式逐步迭代。
4.3 过拟合与欠拟合——模型的两大死穴
模型训练中最让人头疼的问题,无非就两个:学过头了,或者没学够。
过拟合:模型把训练数据里的噪声和偶然规律也记住了,导致在训练集上表现完美,在测试集上崩得一塌糊涂。说白了就是"考试只背了原题,换个题目就懵了"。
我踩过一个坑——有一次我在审计日志上训练了一个深度模型,训练集的 F1 到了 0.99,我高兴得不行。结果上线当天,误报刷了上千条。后来一看,模型把某个时间段的特定日志格式当成了规律,其实那只是网络抖动造成的。嗯,从此我再也不敢只看训练集指标了。
欠拟合:模型太简单,连训练数据的基本模式都没学到。表现就是训练集和测试集上的误差都很高。这通常是因为模型容量不够,或者特征工程没做好。
| 对比维度 | 过拟合 | 欠拟合 |
|---|---|---|
| 训练集表现 | 非常好(误差极低) | 差(误差高) |
| 测试集表现 | 差(泛化能力弱) | 差(没学到模式) |
| 模型复杂度 | 过高 | 过低 |
| 常见原因 | 数据太少、模型太深、训练太久 | 特征不足、模型容量不够 |
| 解决方法 | 正则化、早停、增加数据、降低模型容量 | 增加特征、提升模型复杂度、减少正则化 |
注意:不要为了提高训练集准确率而不停增加模型复杂度。我曾见过一个团队为了把训练集 AUC 从 0.98 提到 0.99,把模型参数量翻了三倍,结果测试集 AUC 从 0.95 掉到了 0.88。过拟合的代价,往往是上线后才知道。
怎么诊断过拟合和欠拟合?我的习惯是画学习曲线:看训练误差和验证误差随着训练数据量增加的变化。如果两条曲线之间的差距很大,就是过拟合;如果两条曲线都居高不下,就是欠拟合。
还记得有一次,我手头只有 2000 条标注日志,硬着头皮训练了一个随机森林。训练集准确率 0.97,验证集只有 0.82。典型的过拟合。后来加了一些正则化参数,又把树的数量从 500 降到 100,验证集准确率反而升到了 0.88。有时候,少即是多。
欠拟合呢?我遇到过一个场景——只用 "登录时间" 和 "登录IP" 两个特征去检测账号被盗。模型怎么调都只有 0.6 的 F1。后来增加了"登录频率""操作序列异常度""设备指纹变化"等特征,F1 直接跳到 0.85。特征才是模型的天花板。
一个小建议:在审计日志异常检测项目里,初期不要追求复杂模型。先用一个简单的 Isolation Forest 或 One-Class SVM 跑通全流程,画出学习曲线,确认没有过拟合/欠拟合问题后,再逐步迭代模型复杂度。这样能少走很多弯路。
好了,这一章的内容就是这些。概念虽然基础,但它们是后续所有章节的"操作系统"——你后面遇到的具体算法、评估方法、调优策略,全都建立在这些概念之上。