☰
k均值聚类辅助的有监督对比学习网络入侵检测方案详解
2026/10/24 2:49:55 网站建设 项目流程

毕设选了“基于k均值聚类的有监督对比学习网络入侵检测算法研究”这个方向,说句实话,一开始我心里是有点打鼓的。做网络入侵检测(NIDS)的都知道,传统方案要么靠特征工程加树模型,要么直接端到端丢给深度网络,近几年Transformer也有不少人在试。但“对比学习”这个词,在CV和NLP领域已经被讲烂了,放到入侵检测这种表格型、多类别、且带明显噪声的数据上,能不能行,根本没人敢打包票。再加上一个k均值聚类,乍一听像是硬凑算法数量。但整套做下来,我反而觉得这个组合并不是噱头,它确实解决了几个比较挠头的问题,比如正常流量和未知攻击难分、小样本攻击类学不好、以及表征空间混乱导致误报降不下去。

这篇文章就把我踩过的坑、对比实验的过程、还有最后跑通的那套完整方案记录下来。不管你是准备拿这个方向做毕设,还是工作中想改进一下现有检测系统,应该都能从里面捞到点能直接用的东西。

1. 整体方案与设计思路拆解

1.1 为什么要用k均值聚类,而不是直接上深度分类器

先聊聊方案选型这件事。很多人看到“聚类”在检测任务里出现,第一反应是这不就是个无监督降维加聚类,然后打个标签吗?实际上不是。这边k均值聚类的应用是放在表征学习内部的,并不是拿来做最终的分类决策。

传统监督学习在入侵检测上有两个点一直处理不好。第一个是数据不平衡性,像U2R、R2L这类攻击在公开数据集里占比经常不到1%,网络初始化随机,模型学到最后对这些小类完全“躺平”。第二个是正常流量的“内紧外松”问题,正常行为模式的范围很宽,一个简单分类边界很容易把少量正常样本分错,导致误报率高。

对比学习天然适合处理这两个问题。它不直接学习“这个样本是哪个类”,而是学习“哪些样本是相似的,哪些样本是不相似的”,这让模型在特征空间里拉近了同类别样本的距离,推远了不同类别样本。特别是有监督对比学习(SupCon),它利用标签信息去构建正负样本对,比无监督那套完全靠增广的玩法稳定得多——NIDS里你很难做图像那种随机裁剪、颜色抖动类的数据增强,所以必须靠标签来辅助。

那k均值聚类加进来的意义在哪?这要从项目实际调试中发现的一个现象说起:直接用SupCon训练出来的表征,拿去做KNN或者直接接全连接分类层,正常流量和几种常见的攻击流量分得还挺开,但一到那些样本量极低、特征形态跟正常流量很像的攻击类型,表征空间就乱了。这些攻击类在特征空间里“骑墙”,拉近同类样本的约束对它们来说不够强。

k均值聚类在这里起到一个“结构初始化”或者说“显式原型约束”的作用。我先用k均值在原始特征空间做一次粗糙的类别划分,把簇中心作为每个类别在高维空间的“原型”估计值。这个原型向量在训练早期用来做对比学习的锚点权重,帮助那些小样本攻击类别锚定在正确的区域里。相当于给模型画了几个“岛”,让样本表征在学习初期不会漂得太远,后期再通过对比损失逐步细化这些岛的边界。这个思路借鉴了聚类辅助对比学习的一些前沿做法,不过加的监督信号把原型估计做得更准确了。

实测下来,在NSL-KDD和UNSW-NB15这类数据集上,加了k均值原型以后,几个小数类的F1值提升不是一星半点。比如R2L类F1从不到0.3直接拉到0.65以上。这就足以说明设计方向的正确性。

1.2 有监督对比学习与入侵检测场景的匹配度分析

很多人会问,入侵检测本质上是一个分类问题,直接交叉熵不就行了,费劲搞对比学习干嘛?这里就涉及对分类任务本质的理解了。

入侵检测这个场景,有标签的数据其实很贵。公开数据集里标签是现成的,但实际企业环境里,你自己采集的流量要打标签往往得靠专家做大量的人工研判。加上攻击层出不穷,今天打标完的样本池子,明天可能就有新的未知攻击混进来。这种情况下,模型最好具备这样的能力:它的数据表征本身是有区分度的,而不是只记住一个表面上的分类边界。

交叉熵训练出来的模型有个毛病——它只在最后一个线性层上做区分。如果数据分布稍微偏移,比如某个服务端口上突然多了一种非常规流量,特征空间里的表征几乎不变,最后那层分类器直接乱套,表现为大量误报。但如果你把整个特征空间都训练得具备“同类聚拢、异类排斥”的结构,那么面对偏移的数据,它可能不会精确认出这是哪种攻击,但起码会在表征空间里偏离正常流量的区域,这样至少能被当作可疑流量捞出来。

有监督对比学习正是干这个的。它拿k均值聚出来的一组原型向量,结合SupCon损失,让样本表征在空间里形成多个语义分簇。有了这种簇结构,后面就算你只接一个非常朴素的分类器,效果也比直接交叉熵好很多。我在实验里做了对比,同样一个全连接分类头,接在SupCon+kmeans表征后面,模型在NSL-KDD关键类别的F1平均比直接端到端训练高出了大约6个百分点。

再加上k均值聚类提供的簇中心本身就是可解释的,你可以事后分析每一簇里的代表特征,看看聚出来的簇到底对应哪一类行为模式。安全运营的人看了也更容易理解这个模型到底在“想什么”,不是个纯黑盒。这一点在真实落地时很重要,毕竟告警出来的结果是要给人去研判的。

2. 核心细节解析与实操要点

2.1 数据预处理与特征工程,90%的坑都在这里

做NIDS的实验,很多人上来就调模型,最后效果上不去,九成是数据预处理埋了雷。我先梳理一下我们用的整体流程,然后逐个说明里面的坑。

数据集方面,我们主要用了NSL-KDD和UNSW-NB15,另外也用一个内部的模拟业务流量数据集做了泛化性验证。NSL-KDD虽然是老数据集,但它的样本量适中、类别齐全、方便跟历史论文对比;UNSW-NB15则包含更多现代攻击类型,比如fuzzers、exploits、worms。

预处理分为几个关键步骤:

  • 缺失值处理:直接删除还是填充?这是个讲究。NSL-KDD基本没有缺失,但UNSW-NB15里有一些字段缺失率超过5%,比如sbytes和dbytes在某些攻击类里会大量出现0值,这其实是协议解析拿不到长度,应该保留为特征,不能瞎填中位数,否则模型会误以为这些样本“特征一致”,反而学歪。我的处理办法是把缺失或无效字段统一映射到一个独立特征值-1,同时再保留一个二值mask特征标记该样本此字段是否有效,让模型自己学要不要参考这个维度。
  • 类别特征编码:协议类型、服务类型这类类别特征直接one-hot会爆炸,UNSW里有超过40种服务这种长尾分布。我用的是CatBoost的序数编码思路,把每个类别按其在目标变量里的平均编码值排序后映射为数值。手写代码也不复杂,就是groupby均值排序,比直接one-hot效果好很多,实际操作中提点明显。
  • 数值特征标准化:流量统计特征的分布极度偏态,像包长度均值在几十到几万之间波动,直接用标准化的效果很糟糕。我用了RobustScaler,它对异常值不那么敏感,而NIDS里攻击样本经常在特征分布两端拉出极端值,标准标准化会被这些极值带着走,小类样本的特征就被压缩得几乎没法区分了。

预处理之后,每个样本有大约90到120维特征(不同数据集不一样)。特征速览表大概长这个样子:

特征类别代表性特征预处理方式
基础流量特征协议、服务、状态序数编码/目标编码
统计特征包长均值/方差、流持续时间RobustScaler
连接特征同目标主机次数、SYN错误占比缺失值mask
内容特征登录次数、root权限调用幂变换+标准化

这套预处理跑完后,NSL-KDD在原始特征上做一次k均值(k=类别数),轮廓系数能到0.25左右,虽然不算高,但作为原型初始化已经够用了。

2.2 原型引导的对比学习架构设计

模型架构是这个方案的核心。很多人对比学习入坑之后最大的迷惑是不知道在NIDS这种非图像数据上,到底要怎么设计正负样本对,以及网络结构该怎么搭。

我最终采用的模型结构是三层堆叠:

  • 输入层:预处理后的特征向量,大约100维。
  • 特征编码器:用了一个三层全连接网络,维度是256-128-64,激活函数全部用GELU,每一层后面加BatchNorm。选择GELU是因为它在激活函数输出上不像ReLU那样会硬截断,对流量特征中的连续分布更友好一些,实测比ReLU版本涨了大约1.5%的分类准确率。
  • 投影头:把编码器输出的64维投影到32维空间做对比损失。投影头就一层全连接加L2归一化。注意对比学习里投影头输出是用来算距离的,不是用来做分类的,所以最后一定要归一化到单位超球面上。
  • 原型注册模块:这不是网络层,而是一个维护k个原型向量的模块。每个原型向量由k均值聚类初始化,并在每个训练步用滑动平均的方式动态更新。这个模块在整个流程中扮演的角色就是给对比学习提供了“软标签中心”。

看一段核心模型实现代码就明白了:

class PrototypeSupCon(nn.Module): def __init__(self, input_dim, hidden_dim=256, proj_dim=32, num_classes=5, momentum=0.99): super().__init__() self.encoder = nn.Sequential( nn.Linear(input_dim, hidden_dim), nn.BatchNorm1d(hidden_dim), nn.GELU(), nn.Linear(hidden_dim, hidden_dim // 2), nn.BatchNorm1d(hidden_dim // 2), nn.GELU(), nn.Linear(hidden_dim // 2, proj_dim) ) self.projection = nn.Sequential( nn.Linear(proj_dim, proj_dim), nn.ReLU(), nn.Linear(proj_dim, proj_dim) ) self.num_classes = num_classes self.momentum = momentum # 原型向量,注册为buffer,不参与梯度更新 self.register_buffer("prototypes", torch.zeros(num_classes, proj_dim)) def update_prototypes(self, feats, labels): # feats是编码器输出的特征,labels是硬标签 with torch.no_grad(): for c in range(self.num_classes): mask = labels == c if mask.sum() > 0: cls_feats = feats[mask].mean(dim=0) self.prototypes[c] = ( self.momentum * self.prototypes[c] + (1 - self.momentum) * cls_feats )

代码里的update_prototypes函数会在每一个batch喂到网络后执行一次。用滑动平均而不是直接赋值,是为了防止个别batch的噪声把原型中心拉飞。我试过直接赋值的方式,训练几轮之后loss直接变成NaN,后来加了体温系数调度才稳住。

序列上来讲,每个batch的数据经过编码器,得到投影特征之后,同时做两件事:一是计算有监督对比损失;二是把当前batch里每个类别的特征均值拿来滑动更新原型向量。到验证阶段,原型向量和投影特征一起算相似度,作为辅助分类信号输出。

2.3 有监督对比损失的设计与温度系数调节

有监督对比损失(SupCon Loss)的公式是这个方案的关键引擎。它跟自监督对比损失的最大区别,是可以用标签来定义正样本对,这意味着哪怕没有数据增广,也能找到语义一致的“同类样本对”。

公式长这样:

[ \mathcal{L}{sup} = \sum{i} \frac{1}{|P(i)|} \sum_{p \in P(i)} -\log \frac{\exp(z_i \cdot z_p / \tau)}{\sum_{j \ne i} \exp(z_i \cdot z_j / \tau)} ]

其中(P(i))是样本i所有与它标签相同的样本集合,(z)是归一化后的投影特征,(\tau)是温度系数。这个loss的本质就是让同类样本在超球面上尽量靠近,不同类的尽量疏远。

那温度系数(\tau)怎么选呢?这个数值对结果影响极大。我做过一组对照实验,温度从0.05到0.5分布做了一遍,发现这个数据集上0.07到0.1之间是一个非常合适的区间,表现为画出来的表征空间里簇间界限清晰,迁移到分类器后准确率也最高。温度太小会让难负样本的惩罚过重,训练不稳定;温度太大则会让所有样本都变成“差不多远”,区分度拉胯。

我在实现中还加了一个小技巧:训练前5个epoch用一个较大的温度(0.2),随后进行指数衰减至0.075。把温度参数做成warmup的形式,是为了在训练早期让模型不要过拟合到某些难学的小类样本上,先学习整体的粗略聚类结构,后期再慢慢收紧。这个操作让R2L这个最难学的类别额外提升了4%的F1值。经验上这个小技巧的收益远大于专门调模型结构。

2.4 有标签数据比例对模型效果的影响

对比学习和聚类的组合还有一个比较吸引人的点——它在标签数据不足时的表现。我专门做了一组有标签数据比例的消融,把NSL-KDD训练集分别拿10%、25%、50%的样本做有监督训练,记录每个配置下测试集上的F1。毕竟任何实际部署的检测系统都不可能拿到100%的完整标注流量。

结果很有意思:

  • 只有10%的标注数据时,k均值聚类原型的作用非常大。模型即使没有足够的正负样本对,也能靠聚类提供的原型信息维持基本的检测能力,整体F1比纯SupCon高出约5%。
  • 标注数据达到50%以上时,聚类的辅助作用逐渐减小,但也没有负作用。
  • 当标注数据特别少、又没有k均值原型时,几个小类攻击直接完全学不出来,模型输出几乎全部是normal和dos两类。典型的“大路货分类器”现象。

这说明空间结构约束对弱监督场景是有天然增益的。就算你将来不只是做公开数据集,想在自家环境里用少量的打标流量训练出能用的检测器,这个方案也有落地价值。

2.5 训练流程与超参配置

整套训练流程我整理下来是这样:

  1. 数据预处理完,先在特征空间上跑一次k均值聚类,初始化原型向量。
  2. 然后进入训练循环,每个batch内代码逻辑有四个步骤:
    • 编码器前向,得到投影特征;
    • 用当前batch特征更新原型向量(滑动平均);
    • 用原型向量辅助参与对比损失的加权(对标签占比小的类给予更高权重);
    • 反向传播,更新编码器和投影头参数。
  3. 每个epoch结束后,在验证集上计算表征聚类效果(轮廓系数、KNN准确率),保存最优模型。

其他重要超参配置我列一下:

超参数取值说明
优化器AdamW权重衰减1e-4,比标准Adam稳
学习率1e-3(warmup 10% steps后余弦衰减)直接用固定学习率容易震荡
Batch Size256太小则对比负样本不够丰富
编码器维度256-128-64实测比128-64-32涨点明显
投影维度32过大会让对比loss过于稀疏
训练轮数150(早停patience=20)数据量不大,过拟合概率高

训练早停这一块,我直接对比了验证集上KNN的top-1准确率变化图,通常在70个epoch左右达到平台期,之后继续训练也不会再涨了,再见势不妙直接收工,不必硬跑满150轮。这里特别说一下,如果你发现轮廓系数持续上涨但KNN准确率原地踏步,往往是投影头学到的信息跟下游分类任务不同步,需要检查是不是投影头过深,加正则或者把投影头换成单层线性层就好。

3. 分类器接口设计与集成评估

3.1 表征输出后的小型分类器如何拼接

当我拿到一个训练好的对比学习编码器,下游分类这一步的灵活性很大。我的方案里把表征输出之后接了两种分类头做对比:

  • 一种是最朴素的softmax分类层,直接在编码器的64维输出上接全连接。
  • 另一种是KNN分类器,因为对比学习的表征天然适合距离度量,KNN几乎不用训练,效果还稳。

实际用下来,KNN在小类攻击的识别上更稳。softmax头在特征空间重叠比较严重的攻击类别上,会出现概率分布“两头摇摆”的情况;KNN因为依赖多个邻居投票,反而抑制了个别异常点的噪声。最终我采用的方法是把两种预测概率做一个加权融合,其中KNN概率权重设为0.4,softmax为0.6,这样在整体准确率和少类F1上都能取到平衡。这个融合权重是在验证集上网格搜索出来的,成本不高但收益实在。

3.2 与基线模型的横向对比

为了说清楚这套方案到底值不值得用,我做了四组对照实验,分别使用:

  • 传统机器学习方案:随机森林+XGBoost集成(当时毕业设计的标配)。
  • 纯深度监督方案:三层FC+交叉熵直接训。
  • 无监督对比学习方案:SimCLR风格,无标签辅助,纯靠数据增广(这里我们用了特征加噪和特征遮蔽作为增广手段)。
  • 本项目方案:k均值原型+有监督对比学习。

用NSL-KDD测试集(包含20%未知攻击变种)的数据表现,大概如下:

方法AccuracyMacro F1攻击检测率误报率
RF+XGBoost0.78210.6810.7420.178
FC+CrossEntropy0.81230.7030.7740.152
SimCLR + FC0.80330.6750.7620.161
SupCon + FC0.83520.7390.8080.112
SupCon + Proto + KNN融合0.85430.7690.8330.094

几个值得注意的观察点:

  • SimCLR作为无监督方案,在没有标签时上限很明显被压制了,但它在攻击检测率上不比交叉熵低太多,原因就是表征空间本身有结构,原始分类头对未知攻击变种更友好。
  • 加了有监督信号后,整体一下高出3到4个点。证明NIDS领域只要标签好搞,有监督对比学习定比纯无监督强。
  • 再加上k均值原型和融合分类器,攻击检测率突破83%,误报率压在10%以内,后面的隐患就是对比学习的训练时间和复杂度——每个epoch大约比交叉熵慢了一倍,但在深度学习普遍烧GPU的今天,这点代价完全值得。

3.3 表征可视化与原型簇的可解释性

做完实验之后,我特意用t-SNE把所有样本的表征投射到二维平面做了可视化。可以明显看到5个大类(Normal、Probe、DoS、R2L、U2R)分别形成了界定清晰的簇团,尤其是之前最头疼的R2L和U2R不再糊成一团,也是这个方案最容易向导师或者答辩评委展示的“成果图”。

同时,在k均值聚出来的每个原型向量上,我做了最近邻样本的对比。每个簇选出来的最靠近中心的5个样本,并把这些样本对应回原始流量特征,发现它们的行为模式高度一致,比如某个簇的样本都表现为“短时间内对同一目标发起大量连接请求”,这就是Probe攻击的典型特征。用这个方式,我在最终项目报告里给每个簇都写了一段特征描述,让整个检测流程从“模型说出一个数字”变成了“模型告诉你,它发现了什么模式的可疑行为”。这对项目展示和答辩都会是一个明显的加分项。

3.4 在UNSW-NB15数据集上的泛化评估

NSL-KDD作为老牌数据集,已经被刷了很多年,存在一定的“研究饱和”现象。为了验证方案不是在新数据上碰巧奏效,我在UNSW-NB15上也完整跑了一遍。这个数据集包含9类攻击,其中Worms这种类型样本量极少,一般模型根本扫不出来。整体结论和NSL-KDD上一致,但有几个特别值得说:

  • 对比学习对Fuzzers、Analysis这类特征分布比较杂的攻击,仍然有效,因为它们虽然类内差异大,但在对比学习里有一个监督信号能强迫它们往同一个簇靠拢。
  • Worms类别的F1虽然只有0.42,但已经是当时对照组里所有算法中最高的,传统XGBoost根本检测不出这一类攻击,F1几乎为0,纯监督FC也只有0.18。这就是监督对比学习拉近同类的优势:只要那一小撮样本的语义簇建起来了,分类器就能抓到它们。
  • k均值聚类在40多个原始特征维度上跑出来的簇跟真实标签重叠度不够高,但它的目的本来就不是最终分类,只是提供一个合理的初始原型。这一点也必须想明白,不然你会误以为聚类效果不好,就把整个方案放弃了。实际上,哪怕初始原型只有60%的准确率,经过对比学习训练后,原型的指引作用依然能把表征调优到位。

4. 常见问题与排查技巧实录

4.1 温度系数与损失尺度的交互坑

刚跑这个方案的时候,最常碰到的第一个就是loss值跑飞。明明学习率没调高,数据预处理也没毛病,但loss自己在30个step之后就冲到了几十甚至上百。排查了半天,最终发现根源在温度系数上。z_i * z_p在32维向量上的余弦相似度,如果温度设置成了0.5,这个分数就显得特别大,softmax分母直接爆掉。解决方法是把温度系数限定在0.1以下的同时,把投影层出来的特征严格做L2归一化,确保每个向量的模长是1。归一化这一步是必须的,千万不要觉得“大家都做归一化,我就不做了”或者“加在验证时做就行了”。训练时就必须同步进行,否则对比学习学到的表征就废了。

4.2 滑动平均更新原型时的类别缺失问题

滑动平均更新原型时,难点是batch比较小,有些小类在batch里根本没出现。如果这时候还去做更新,原型向量就会在旧状态上继续衰减,最后直接退化成一个极其偏置的中心。我的做法是,当某个batch里某一个类别样本数为0时,这一类的原型向量不参与更新。如果连续很多batch都缺失某一类,则每个epoch结束后在完整训练集上强制跑一次全量原型更新,相当于做一次“兜底校准”。这个逻辑不要省略,不然后面验证集上小类的检测率会突然崩塌,而且很难查出来是哪个环节出的问题。

4.3 对比学习对Batch Size的敏感度

对比学习的负样本数量跟batch大小直接挂钩。如果你的batch太小,每个样本能看到的负样本对数不足,模型根本无法学到有区分度的表征。我这里固定用了256的batch size,如果你显存不够,建议优先缩减编码器宽度而不是batch,比如256维改成128维,而不是把batch从256降到64。如果降到64,你会发现表征空间整个“糊”掉了。

4.4 如何判断模型学好了,还是只是在硬啃标签

训练这类模型,一定要同时盯着两个指标看:特征空间本身的聚类效果,和下游分类的准确率。如果分类准确率高但聚类很乱,说明分类器只是记住了决策边界,泛化会差。如果聚类效果好但分类准确率提不上来,那问题一般出在分类头或融合策略上。这不是玄学,是表征分工问题。我在验证阶段每次都会同时打印这两个指标,看到它们“步调一致”地在提升,才能说明模型是真的学到东西了。

4.5 流量数据的类别标签噪声如何应对

真实的流量打标并不都是干净的,总会有一些样本的标签是错的或者漏标的。我对对比学习做了一个小改造:当batch里某个类别的样本数特别少,且它跟其他类别的原型向量余弦相似度极高时,就把它视作潜在的噪声样本,在计算对比损失的时候把这个样本的权重降低到0.3。这样模型不会因为一个错误标签的“强势拉拢”而把整个簇带偏。这个小策略在我们内部的模拟业务流量上非常有用,值得作为改进点继续深挖。

5. 一些后续可以继续深挖的方向

其实做到这里,我最大的感触是整个方案的扩展性。很多传统检测模型,加了新攻击就得全部重训,而对比学习这个架构天然地支持增量学习。新攻击类别出现时,系统只需要在原编码器后面加一个新的原型向量,然后在少量新样本上fine-tune几轮,旧类别的检测能力不会有明显衰退。这一点对小样本环境特别实用。

代码层面,整个实验最核心的骨架也就200多行,写在PyTorch里非常清晰,训练单卡V100都完全够用。如果各位是在做毕设或者创新项目,完全可以把原型的更新策略、温度系数的自适应调整、融合分类阈值的动态设定这些点作为创新点去扩展,每一个方向都够写出一篇论文级别的改进。

以我个人做这套实验的体会来说,最后模型能在NSL-KDD上跑到85%的准确率和77%的Macro F1,靠的并不是某个花哨的模块,而是把对比学习的核心思想跟入侵检测数据本身的特性反复磨合,找到最合适的那条适配路径。希望这些直接把实验推着走的经验,能让你少走几个我已经替你踩过的弯道。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询