
简介一份基于数据挖掘的网络入侵检测MATLAB实现资源包围绕入侵检测中的数据聚类与分类问题展开面向网络安全研究人员、数据挖掘学习者以及需要复现KMeans及其改进算法的开发者。资源包共12个文件以.m脚本和.mat数据文件为主整体仅24KB包含了KMeans、GAKMeans、KNN等多种算法的核心实现及mydata.mat实验数据便于直接运行和二次开发。已有250人学习下载适合作为课程设计、算法对比或入侵检测方向入门研究的参考资料。通过源码可直观理解改进KMeans在初始化策略、距离度量及异常值处理上的优化思路并借助配套数据快速验证聚类与分类效果为网络异常行为识别提供可操作的实验基础。1. 为什么入侵检测要转向数据挖掘网络入侵检测做了这么多年一个很现实的瓶颈是基于签名的规则库永远在追赶新攻击。攻击者只要稍微改变载荷特征或者组合方式规则库里的旧签名就失效了而安全团队维护规则的代价又很高。所以业界一直在尝试换一条路把流量数据本身当作分析对象用机器学习方法从数据中找规律而不是靠人写死规则。这条路就是基于数据挖掘的网络入侵检测它解决的已经不是“这个包像不像已知攻击”而是“这段流量行为是否符合正常基线”。KMeans 聚类在这类系统里是使用频率很高的算法之一原因很直接无监督、不需要标注数据、实现简单、适合做异常发现。但 KMeans 也一直被人诟病比如对初始中心敏感、对非球形分布的数据力不从心、对离群点几乎没什么抵抗力。如果用原始 KMeans 直接处理网络流量特征聚类结果往往不稳定检测率也上不去。所以这篇博文要拆的就是一份结合了 KMeans、KMeans、GA-KMeans 以及 KNN 的 MATLAB 实现工程重点看改进后的算法怎么落地到入侵检测的流程里。这份资源适合两类人一类是正在做数据挖掘课设、需要一套能跑的聚类和分类代码来支撑实验结果的在校学生另一类是做安全分析但想了解无监督方法在入侵检测里到底怎么用、参数如何设的工程师。前者可以直接基于 MATLAB 脚本改数据集跑出对比图后者可以从中抽取特征选择、归一化和聚类评估的思路迁移到 Python 或者 Spark 环境下实现。2. KMeans 的缺陷与改进思路在检测场景中的取舍2.1 为什么原始 KMeans 不适合直接做入侵检测KMeans 的核心逻辑其实很简单先随机选 K 个中心然后反复迭代把每个样本分到最近的中心再更新中心位置直到收敛。这套逻辑在实验室的二维数据集上表现很好但放到网络流量上问题就暴露了。第一个问题是初始中心随机选聚类结果波动很大。同样的数据第一次跑可能聚成 5 个簇第二次可能合并成 4 个簇检测结果自然不稳定。第二个问题是 KMeans 用欧氏距离衡量相似度但网络流量特征之间往往存在量纲差异连接时长是毫秒级别而数据包字节数可能上万如果不归一化某些特征会完全主导距离计算。第三个问题更隐蔽入侵流量往往只占整体流量的极小比例在聚类过程中容易被当成离群点或者并到正常簇里。所以在工程上直接拿原始 KMeans 做入侵检测是不太现实的。需要做两件事一是在算法层面改进初始中心选取二是在数据层面做归一化和特征缩放。这份资源里的 GAKMeans.m 和 KMeans 思路就是针对前一个问题的。2.2 用遗传算法优化初始中心的具体策略对 KMeans 来说初始中心的质量直接决定迭代结果是否收敛到好的局部最优。GA遗传算法在这里不是用来替代聚类的而是用来搜索更好的初始中心点。常见的做法是把一组 K 个中心点编码成种群中的一个个体然后通过选择、交叉、变异迭代若干代用聚类结果的类内距离和作为适应度函数最后得到一组质量较高的初始中心再交给 KMeans 做精细迭代。GA-KMeans 在数据量适中的情况下很有效比如 KS 数据集或者 UNSW-NB15 的抽样集。但如果数据量达到百万级GA 的种群评估开销会变得很大每秒收敛速度远不如 KMeans。所以这里就有一个选型权衡工程上如果追求稳定性和效果GA-KMeans 值得用如果追求速度和可扩展性KMeans 是更稳妥的选择。在 MATLAB 里实现 GA-KMeans 时适应度函数一般写成function score fitness(centers, data) % centers: K x D 矩阵表示一组聚类中心 % data: N x D 矩阵表示全部样本 [~, distsum] kmeans(data, size(centers, 1), Start, centers, MaxIter, 50); score 1 / (distsum eps); % 距离和越小适应度越高 end这段代码里我把 KMeans 的最大迭代次数限制在 50 代因为 GA 只需要一个粗略的适应度估计不需要完整收敛。distsum是从 kmeans 函数返回的类内距离和可以作为聚类紧凑度的度量距离和越小说明中心选得好。eps用来防止除零。这个适应度函数在 GA 迭代中会被反复调用所以数据量大的时候要注意矩阵维度避免内存溢出。2.3 KNN 算法的补充作用聚类方法擅长发现未知模式但无法输出“这个样本属于哪一类攻击”的明确标签。KNNK 近邻在这里的价值是基于已有标注数据做分类把聚类结果和已知攻击类型关联起来。KNN 的原理简单到几乎没有训练过程给定一个待检测样本在特征空间里找 K 个最近邻居按照邻居的多数投票决定类别。这个思路适合与 KMeans 结合使用先用 KMeans 对海量无标注流量做聚合选出簇中心的代表性样本再用 KNN 对实时流量做分类。这样既避免了 KNN 全量计算的开销又获得了分类能力。在这个工程里KNNnew.m和Main_KNN*.m脚本基本就是用来跑这种实验的。KNN 有一个比较关键的参数 K 值通常在 3 到 7 之间选择。K 值太小容易被噪声干扰太大则会把边界样本误分到多数类。我在实际实验中一般先用交叉验证扫一遍 K 值比如设置K 3:2:11然后选择准确率最高的值而不是拍脑袋定一个数。3. 从数据预处理到核心检测流程的完整实现3.1 数据收集与特征选择网络入侵检测系统的数据收集阶段需要捕获原始网络流量常见的数据源是 tcpdump 抓包文件、NetFlow 记录或者公开数据集如 NSL-KDD。这份工程里用的是mydata.mat已经是处理好的 MATLAB 矩阵格式可以直接加载进工作区。它一般包含若干特征列和最后一列的标签标签部分用数字表示攻击类型。特征选择在这里是直接影响检测效果的关键步骤。网络流量中常见的特征包括协议类型、源/目标端口、连接时长、源字节数、目标字节数、同主机连接频率等。但并不是特征越多越好无关特征会干扰聚类距离的计算。我一般先做相关系数分析去掉与标签相关性极低的特征再用主成分分析压缩维度。在 MATLAB 中加载和观察数据可以通过以下方式load mydata.mat; % 加载数据 disp(size(data)); % 查看矩阵维度 disp(unique(data(:, end))); % 查看标签种类这段代码里size返回的是样本数和特征数unique可以列出所有标签类别。在做入侵检测实验时先确认类别数量很重要因为 KMeans 的簇数 K 通常要和类别数量对齐或者根据聚类评估指标来确定。默认情况下如果数据集有 4 类攻击加 1 类正常流量K 试 5、6、7 三档比较合理。3.2 归一化与标准化的必要性在聚类算法中距离计算的结果完全取决于特征尺度。如果不归一化端口号取值范围可能是 0 到 65535而连接时长可能是 0 到 1 秒那么端口号会主导整个距离计算。这里有个常见误区很多人以为只要聚类跑了就完事但实际上归一化操作对检测率的提升幅度有时比换算法还大。标准化处理的常见方式有两种Min-Max 归一化和 Z-score 标准化。Min-Max 会把数据压缩到 [0, 1] 区间适合均匀分布的特征Z-score 会把数据转换为均值为 0、方差为 1 的分布适合有离群点的场景。因为网络流量特征中经常出现极端大值比如超大流量包我通常倾向用 Z-score% 取特征列最后一列是标签 X data(:, 1:end-1); labels data(:, end); % Z-score 标准化每列减去均值再除以标准差 mu mean(X); sigma std(X); sigma(sigma 0) 1; % 防止除零 X_norm (X - mu) ./ sigma;这段代码的逻辑很明确先计算每个特征的均值和标准差然后逐列做标准化。sigma(sigma 0) 1处理的是可能存在的常数列比如某个特征在所有样本中取值完全相同的情况。标准化之后所有特征都在同一量级后面的距离计算才真正反映样本之间的实际差异。3.3 基于改进 KMeans 的聚类检测流程聚类检测的核心流程可以分为三步先用改进的 KMeans 把流量聚成 K 簇然后标记每个簇的正常或异常属性最后对实时流量做预测。在这个工程里Main_GA_kmeans20170604.m就是整个流程的主控脚本。标记簇属性的方式有两种一种是基于簇内正常样本的比例。一个簇如果绝大多数是正常流量就把它标记为正常簇如果包含大量异常样本就标记为异常簇。另一种是基于簇中心与已知正常样本中心的距离距离越远越可能异常。第二种方法更适用于实时检测场景因为不需要对整个数据集重新聚类。聚类主流程的 MATLAB 代码可以这样写K 5; maxIter 100; % 用 GA 改进的初始中心 [bestCenters, ~] ga_initialize(X_norm, K, 30, 20); % 执行 KMeans 迭代 [clusterIdx, centers] kmeans_with_centers(X_norm, K, bestCenters, maxIter);这里ga_initialize是 GA 搜索初始中心的入口参数 30 表示种群规模20 表示遗传代数。kmeans_with_centers是在 MATLAB 自带 kmeans 基础上封装一层把 GA 搜索到的中心作为起始点传入。之所以不直接用 MATLAB 内置的kmeans的Start参数是因为内置接口不方便和 GA 代码联动调试。在实际运行中K 值的选取需要反复实验。K 设小了异常样本可能被并入正常簇漏报率上升K 设大了正常流量被切得太碎误报率会飙升。一个可行的经验值是先统计数据集标签类别数再在类别数的基础上加 1 到 3 试 K最后用轮廓系数或检测率来选定。4. 实战跑通 KMeans、GA-KMeans 与 KNN 对比实验4.1 数据集划分与实验设计要做对比实验第一步是要把数据分成训练集和测试集。但这里有个细节KMeans 和 KNN 对数据划分的策略完全不同。KMeans 是聚类算法不需要标签做训练测试集和训练集在整个实验过程中只是在评估阶段分开。KNN 是分类算法训练集必须有标签。因为这份工程里同时包含了聚类和分类脚本我建议用以下策略来组织实验步骤脚本/操作说明1load mydata.mat加载数据2X_norm normalize(X)标准化3运行Main_GA_kmeans20170604.m执行 GA-KMeans 聚类4运行Main_IMP_KNN20170604.m执行改进 KNN 分类5对比检测率和误报率分析结果在实际操作中要保证 KMeans 和 KNN 使用相同的特征集和归一化参数。如果先在 KMeans 里做了 PCA 降维那 KNN 也要在降维后的特征上训练否则对比不公平。4.2 主控脚本的参数调整与运行打开Main_GA_kmeans20170604.m后需要确认几个关键参数设置。首先是聚类个数 K一般从 5 开始调其次是 GA 的种群规模和迭代代数这两个决定初始中心搜索的质量最后是 KMeans 的最大迭代次数通常设为 100 到 300 之间过大没有意义因为中心早就收敛了。典型的参数修改位置如下K 6; % 聚类个数根据样本数调整 popSize 50; % 种群规模越大搜索越充分但越慢 maxGen 30; % 遗传代数一般 20-50 之间 maxIter 200; % KMeans 内部迭代上限这些参数的设定逻辑是种群规模越大GA 搜索覆盖面越广但每次适应度计算都要跑一次 KMeans代价很高。在普通 PC 上跑 1 万条样本种群规模 50、遗传代数 30 的时间大概在几十秒到几分钟之间所以不建议再加大。如果你用的是 10 万条级别的大数据集建议把 GA 部分替换成 KMeans 实现否则等待时间会让人失去耐心。4.3 结果评估与检测率计算聚类和分类做完之后需要量化检测效果。入侵检测领域常用的指标有三个检测率DR、误报率FPR和准确率ACC。检测率是异常样本中被正确识别的比例误报率是正常样本被误判为异常的比例准确率则是全体样本中预测正确的比例。在 MATLAB 中可以用混淆矩阵的来算% pred 是预测标签trueLabel 是真实标签 % 假设异常类标为 1正常类标为 0 tp sum(pred 1 trueLabel 1); fp sum(pred 1 trueLabel 0); tn sum(pred 0 trueLabel 0); fn sum(pred 0 trueLabel 1); DR tp / (tp fn); % 检测率 FPR fp / (fp tn); % 误报率 ACC (tp tn) / length(trueLabel); % 准确率 fprintf(DR%.2f%% FPR%.2f%% ACC%.2f%%\n, DR*100, FPR*100, ACC*100);tp和fp的计算方式在聚类场景下要注意一个前提聚类输出的簇编号是 0 到 K-1不一定对应正常或异常。需要先根据簇内正常样本占比给每个簇打标签。比如某个簇里 90% 都是异常样本这个簇整体就标记为异常簇这样在对比时才不会因为簇编号和类别编号不对齐而算错指标。4.4 KNN 分类脚本的对比思路Main_KNN*系列脚本用于分类对比。KNN 在入侵检测里的表现受两个因素影响K 值和距离度量。工程里的KNNnew.m默认用欧氏距离如果特征维度比较高也可以换成余弦距离。余弦距离对向量的模长不敏感在处理不同流量的字节数差异时有时比欧氏距离更稳定。运行 KNN 分类前要设定 K 值和训练测试划分比例K 5; splitRatio 0.7; % 70% 作为训练集 cv cvpartition(size(X_norm, 1), HoldOut, splitRatio); trainIdx training(cv); testIdx test(cv); XTrain X_norm(trainIdx, :); yTrain labels(trainIdx); XTest X_norm(testIdx, :); yTest labels(testIdx);这里cvpartition是 MATLAB 提供的数据划分函数HoldOut表示按固定比例随机划分不会打乱原始数据顺序。要注意网络流量数据往往存在时序相关性随机划分可能高估模型效果严谨的做法是按时间顺序划分。如果这个数据集本身没有时间字段就只能接受随机划分了。5. 参数调优、边界条件与离群点处理技巧5.1 轮廓系数选择 K 值K 值的确定是 KMeans 类方法绕不开的难题。实际工程中我不会只依赖检测率这一个指标因为检测率在类别不平衡时会虚高。更好的辅助指标是轮廓系数Silhouette Coefficient它衡量每个样本到自己簇内样本的平均距离与最近其他簇样本的平均距离的差异取值范围在 -1 到 1 之间越接近 1 表示聚类效果越好。在 MATLAB 中计算轮廓系数的代码sil silhouette(X_norm, clusterIdx); meanSil mean(sil); fprintf(Mean silhouette: %.4f\n, meanSil);这段代码会输出每个样本的轮廓系数值meanSil是整体平均。遍历 K 值从 4 到 10选择平均轮廓系数最高且计算开销可接受的 K 值。但要提醒一点轮廓系数高并不等于检测率高因为聚类关注的是数据内部结构而入侵检测关心的是攻击类是否与正常类分离。所以更稳的做法是K 值先用轮廓系数圈定范围再用检测率精调。5.2 离群点对聚类过程的影响及应对网络流量数据里的离群点非常常见比如端口扫描流量、DDoS 的高频小包等。KMeans 的最小平方误差目标函数对离群点很敏感少数极端值就能把簇中心拉偏。改进的方式有以下几种。第一种是在聚类前用 Isolation Forest 或者基于密度的 LBSC 方法先过滤掉明显离群点再对剩余数据做 KMeans。这样聚类中心的估计会更稳健但也面临误杀正常样本的风险。第二种是调整距离函数比如使用 Manhattan 距离替代欧氏距离后者对异常值的敏感度更低。第三种是给样本加权重对距离中心较远的样本降低贡献类似带权 KMeans。在 MATLAB 中实现简单的离群点过滤可以这样做% 计算每个样本到数据集中心的马氏距离 d mahal(X_norm, X_norm); % 去除距离超过阈值 99% 分位数 的样本 thresh prctile(d, 99); keepIdx d thresh; X_clean X_norm(keepIdx, :);这里用的是马氏距离它考虑了特征之间的相关性比欧氏距离更适合多特征场景。prctile函数取 99% 分位数作为阈值意味着只剔除最极端的 1% 样本。这个操作要谨慎做因为极端的 1% 样本里有可能是真正的攻击流量过滤太多会直接降低检测率。我的建议是先看数据分布如果离群点明显是噪声比如特征值为 0 或负数的异常记录再过滤否则保留。5.3 实时检测与模型更新策略入侵检测系统最终要面对实时流量这和离线聚类的场景有很大不同。离线聚类可以对全量历史数据反复迭代实时检测则要求单条样本快速判定。常见的工程做法是定期比如每小时用滑动窗口内的流量数据重新训练聚类模型然后把当前时刻的流量映射到已有的簇中判断是否落在正常簇的边界内。在使用这份 MATLAB 工程做演示时实时性的模拟方式比较简单先训练好 KMeans 模型然后用min函数找到新样本到各簇中心的最小距离如果这个距离超过某一阈值就认为异常newSample X_norm(1, :); % 假设来了一条新数据 distToCenters sqrt(sum((centers - newSample).^2, 2)); [minDist, minIdx] min(distToCenters); if minDist threshold fprintf(Anomaly detected, closest cluster: %d, distance: %.2f\n, minIdx, minDist); else fprintf(Normal, cluster: %d, distance: %.2f\n, minIdx, minDist); end阈值threshold的设定一般用训练集里所有样本到各自簇中心的距离分布的 95% 分位数。这样做的好处是无需实时重新聚类单条样本的判定复杂度是 O(K * D)K 通常在 5 到 10 之间D 是特征维度即使在嵌入式设备上也能跑得动。5.4 误报率与漏报率的平衡最后一点要强调的是入侵检测系统里误报率和漏报率不是独立的两个指标它们之间是此消彼长的关系。在实际配置中如果系统用于生产网络的旁路监控我可以接受较高的误报率因为分析师可以人工复核如果系统直接联动防火墙做自动阻断那么误报率必须压到极低否则正常业务会被拦停。手段之一是调整判定阈值。比如在上述距离阈值方法中阈值设低一点会捕获更多异常但误报也会增加。手段之二是对簇内的密度信息做进一步分析如果一个簇内样本非常集中簇边缘的样本更有可能是异常可以用离簇中心的相对距离做归一化评分而不是用绝对距离。手段之三是把 KMeans 和 KNN 结果做投票融合两者都判定为异常才触发告警这样能有效降低单算法偏差带来的误报代价是可能漏掉部分双方都没识别出的攻击。在实验阶段我建议输出一条 ROC 曲线把不同阈值下的检测率和误报率画出来从中选择业务可接受的工作点。这样得到的参数具有可解释性而不是拍脑袋定的。本文还有配套的精品资源点击获取