4. 基于机器学习的分类方法:特征选择与降维

好,咱们进入第四章节。说实话,这部分是我个人觉得最有意思的。为什么?因为流量分析做到后面,你会发现数据量太大了,特征太多了,光靠规则和人工根本扛不住。这时候,机器学习就该上场了。

但有个坑我得先说一下——不是所有特征都有用。有些特征就是噪音,甚至会把模型带偏。所以,特征选择和降维,是咱们必须迈过去的第一道坎。

4.1 特征选择:别让垃圾特征毁了模型

特征选择,说白了就是挑出那些真正能区分正常流量和异常流量的特征。我见过不少新手,一股脑把所有特征都扔进模型,结果准确率反而下降了。

常用的方法有三种:

  • 过滤法:计算每个特征与目标变量的相关性,比如卡方检验、互信息。速度快,但忽略了特征之间的交互。
  • 包裹法:比如递归特征消除(RFE),每次去掉一个最不重要的特征,反复训练模型。效果不错,但计算量大。
  • 嵌入法:像Lasso回归、决策树这类模型,训练完直接告诉你哪些特征重要。我个人比较喜欢这种方式,省事。
我的经验:在流量分析中,我习惯先用过滤法快速筛一遍,把那些方差接近0的特征直接扔掉。然后再用随机森林的特征重要性排序,挑出Top 20的特征。这样既快又准。

4.2 降维:PCA与t-SNE

特征选完了,但有时候维度还是太高。比如你提取了100多个流量特征,模型训练慢得要命。这时候就需要降维。

4.2.1 PCA(主成分分析)

PCA的原理我不展开讲,你只需要记住:它把原始特征投影到新的坐标轴上,新轴的方向是数据方差最大的方向。说白了,就是找到最能代表数据的那几个方向。

我在项目中遇到过一个问题:用PCA降维后,模型准确率反而下降了。后来发现,是因为我保留了太多主成分,把一些关键信息给稀释了。嗯,这里要注意:PCA不是保留越多越好,通常保留能解释90%方差的前几个主成分就够了。

from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler

# 标准化很重要,不然量纲大的特征会主导PCA
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

pca = PCA(n_components=0.95)  # 保留95%的方差
X_pca = pca.fit_transform(X_scaled)
print(f"降维后特征数: {X_pca.shape[1]}")

4.2.2 t-SNE(t分布随机邻域嵌入)

t-SNE和PCA不一样。PCA是线性降维,t-SNE是非线性的。它特别擅长把高维数据映射到2D或3D,方便可视化。

你想想看,如果你有100维的流量特征,想看看正常流量和攻击流量在空间里是怎么分布的,用t-SNE一画,一目了然。

避坑指南:我曾经用t-SNE做特征降维后直接拿去训练模型,结果效果很差。为什么?因为t-SNE只保留了数据的局部结构,全局距离关系被破坏了。所以,t-SNE适合做可视化探索,不适合作为模型输入的特征。

4.3 逻辑回归与决策树

好,特征准备好了,咱们开始建模。先从两个最基础的模型说起。

4.3.1 逻辑回归

别看它名字里带“回归”,它其实是分类算法。逻辑回归输出的是一个概率值,比如“这条流量是攻击流量的概率是0.85”。

它的优点是简单、可解释性强。每个特征的系数直接告诉你这个特征对结果的影响是正向还是负向。我在做DDoS流量检测时,经常用逻辑回归做基线模型,先看看数据是不是线性可分的。

from sklearn.linear_model import LogisticRegression

model = LogisticRegression()
model.fit(X_train, y_train)

# 查看特征权重
for feature, coef in zip(feature_names, model.coef_[0]):
    print(f"{feature}: {coef:.4f}")

4.3.2 决策树

决策树就更好理解了。它就像一连串的“如果...那么...”规则。比如:如果源IP的请求频率 > 100次/秒,并且请求的URL长度 > 2000,那么判定为攻击流量。

决策树的优势是直观,你可以把树画出来,给非技术人员看也能看懂。但缺点也很明显——容易过拟合,稍微变一点数据,树的结构就全变了。

我的建议:决策树单独用效果一般,但它作为集成学习的基础,威力巨大。接下来要说的随机森林和XGBoost,就是基于决策树的。

4.4 随机森林与XGBoost

这两个是流量分析中的王牌模型。我敢说,90%的异常流量检测比赛,冠军用的都是它们。

4.4.1 随机森林

随机森林就是很多棵决策树放在一起投票。每棵树用的数据是随机抽样的,特征也是随机选的。这样每棵树都不一样,综合起来就稳定多了。

我记得有一次做Web攻击流量检测,单棵决策树的准确率只有82%,换成随机森林后直接跳到94%。这就是集成的力量。

from sklearn.ensemble import RandomForestClassifier

rf = RandomForestClassifier(n_estimators=100, max_depth=10, random_state=42)
rf.fit(X_train, y_train)

# 特征重要性排序
importances = rf.feature_importances_
indices = np.argsort(importances)[::-1]
for i in range(10):
    print(f"{i+1}. {feature_names[indices[i]]}: {importances[indices[i]]:.4f}")

4.4.2 XGBoost

XGBoost是随机森林的升级版。它用的是梯度提升框架,每一棵新树都在纠正前一棵树的错误。说白了,就是“错题本”机制——越学越聪明。

XGBoost在流量分析中表现非常出色,尤其是处理不平衡数据时。正常流量可能占99%,攻击流量只有1%,XGBoost通过调整样本权重,能很好地处理这种场景。

import xgboost as xgb

model = xgb.XGBClassifier(
    n_estimators=200,
    max_depth=6,
    learning_rate=0.1,
    scale_pos_weight=99  # 处理不平衡数据,正负样本比例1:99
)
model.fit(X_train, y_train)
个人经验:XGBoost调参是个技术活。我一般先固定学习率0.1,调好树的数量和深度,最后再微调学习率。别一上来就调所有参数,容易陷入局部最优。

4.5 模型评估指标

模型训练完了,怎么知道它好不好?光看准确率可不行。在流量分析中,攻击流量往往很少,就算模型把所有流量都判为正常,准确率也有99%。但这模型有用吗?一点用都没有。

4.5.1 混淆矩阵

混淆矩阵把预测结果分成四类:

预测为正常 预测为攻击
实际为正常 TN(真阴性) FP(假阳性)
实际为攻击 FN(假阴性) TP(真阳性)

在流量分析中,我最关心的是召回率(TP / (TP + FN))。因为漏掉一个攻击流量,可能就导致整个系统被攻破。至于误报(FP),虽然烦人,但总比漏报好。

from sklearn.metrics import confusion_matrix, classification_report

y_pred = model.predict(X_test)
print(confusion_matrix(y_test, y_pred))
print(classification_report(y_test, y_pred))

4.5.2 ROC-AUC

ROC曲线画的是真正率(TPR)和假正率(FPR)的关系。AUC就是曲线下的面积,值越大说明模型越好。

AUC = 1 是完美模型,AUC = 0.5 就是瞎猜。在实际项目中,AUC能到0.95以上就算很好了。

我曾经踩过的坑:有一次模型AUC高达0.99,我高兴坏了。结果上线后发现误报率特别高。后来一查,原来是测试集和训练集有数据泄露——同一个攻击样本的变种同时出现在训练集和测试集里。所以,评估模型时一定要确保时间序列上的数据隔离。
from sklearn.metrics import roc_auc_score, roc_curve

y_prob = model.predict_proba(X_test)[:, 1]
auc = roc_auc_score(y_test, y_prob)
print(f"AUC: {auc:.4f}")

# 画ROC曲线
fpr, tpr, _ = roc_curve(y_test, y_prob)

小结

这一章内容不少,但核心就三件事:选好特征、降维、选对模型。我个人习惯是先用逻辑回归或决策树快速验证数据质量,再用随机森林或XGBoost提升效果。评估时别只看准确率,混淆矩阵和AUC才是关键。

下一章咱们聊聊深度学习在流量分析中的应用,那又是另一个世界了。