ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

网络入侵检测实战:SVM与BP神经网络混合模型在KDD99上的应用

网络入侵检测实战:SVM与BP神经网络混合模型在KDD99上的应用 简介针对网络入侵检测中的机器学习应用这份PDF文档系统研究了基于SVM与神经网络的混合入侵检测方法适合网络安全方向的学生、研究者或算法工程师作为算法设计与实验参考。资源共1个PDF文件约6.3MB内容从入侵检测概述、机器学习基础入手重点阐述了改进K-means数据筛选、弃一法特征选择、基于网格搜索与模拟退火的SVM参数寻优、BP神经网络结构优化及分层检测框架构建并在KDD99数据集上验证了混合模型对U2R和R2L攻击的检测优势。已有1950人学习。通过这份资料读者可以掌握从数据集处理、特征选择到分类器设计及融合的完整研究思路也可借鉴其中针对样本不平衡、检测时延等实际问题的优化方法作为相关课题或论文的参考。1. 网络入侵检测没那么玄分类问题、数据坑和两条技术路线很多团队把网络入侵检测当成特征匹配问题来堆规则结果新攻击一变种就抓瞎。用机器学习做网络入侵检测本质上是一个多分类问题把流量分成正常、DoS、Probe、U2R、R2L几类。但真正决定效果的不是模型选得有多炫而是数据怎么筛、特征怎么降、参数怎么找。我拆过一套基于KDD99的完整流程把SVM和BP神经网络都跑了一遍发现分类算法只是底座前面的样本筛选和后面的分层检测框架才是提升检测率的关键尤其是对U2R和R2L这种低频攻击。这套流程适合刚接触入侵检测的新手也适合做安全算法验证的工程师用来做基线对照。看懂之后你可以直接在公开数据集上复现一套SVM神经网络的混合检测模型。2. 先用KDD99把问题钉死样本筛选、聚类重构与特征选择2.1 KDD99数据长什么样哪些攻击最难检KDD99是入侵检测领域用得最久的数据集每条记录有41维特征标签类别包括Normal、DoS、Probe、U2R和R2L。前两类样本量非常大后两类极少。以经典训练集为例各类型样本量级大致如下表类别样本量级占比Normal约97k19.7%DoS约391k79.3%Probe约4.1k0.8%U2R约520.01%R2L约1.1k0.2%这种分布直接导致模型对正常流量和DoS很敏感但对U2R、R2L几乎学习不到规律。更麻烦的是KDD99的测试集里还混有训练集没出现过的攻击变体专门用来考验模型的泛化能力。所以在动手训练SVM之前先别急着把数据丢进fit而是要先做两件事一是用聚类把训练集压缩到合理规模二是用特征选择把无贡献或重复的特征剔除。KDD99的41维特征里有符号字段协议类型、服务类型、标志位需要先做数值化而且不同维度量纲差异很大归一化是必须的。最容易被忽略的是攻击样本的噪声问题某些攻击流量在特征上跟正常流量重叠得很厉害直接学习会把分类边界拉歪。后面会用K-means细化聚类和添加噪声样本来解决。2.2 用改进K-means做训练集精简和噪声样本构造K-means的思路很直白如果某一类样本的簇内样本点高度相似那么每个簇只需要保留少量代表性样本就能基本保留这类样本的分布信息。改进的K-means不是直接对所有样本做一次聚类而是按类别分别聚类并控制每个簇的规模上限。这样做有两个好处一是不会让大类样本彻底淹没小类二是可以通过簇半径判断哪些样本更接近簇中心把远离中心的样本单独挑出来看作噪声样本加入训练集。这里有个经验网络入侵检测中U2R、R2L样本少如果直接复制样本过采样模型很容易过拟合。常见做法是先把这些稀有类样本单独聚类然后在每个小簇里合成少量新样本或者把簇边界样本作为“困难样本”加强学习。细化聚类让每个簇足够纯添加噪声样本是为了让分类器对边界区域更敏感。下面给出一个简化的聚类筛选代码框架。from sklearn.cluster import KMeans import numpy as np def select_representative_samples(X, y, category, max_per_cluster500, n_clusters20): X_cat X[y category] km KMeans(n_clustersn_clusters, random_state42).fit(X_cat) selected_idx [] for cluster_id in range(n_clusters): member_idx np.where(km.labels_ cluster_id)[0] if len(member_idx) max_per_cluster: dist np.linalg.norm(X_cat[member_idx] - km.cluster_centers_[cluster_id], axis1) order np.argsort(dist)[:max_per_cluster] selected_idx.extend(member_idx[order]) else: selected_idx.extend(member_idx) return np.array(selected_idx)这段代码通过控制每个簇保留的样本数来压缩大类样本同时用“到簇中心的距离”来筛选代表性样本。max_per_cluster控制每簇最多保留多少样本如果原来簇里有10000条压缩后只保留500条训练集就大幅缩小。n_clusters决定聚类粒度粒度越细保留的样本越接近原始分布。注意参数别调得太极端否则会丢掉有价值的边界样本。实际使用时可以对每一类分别调用这个函数最后合并成新的训练集。提示K-means对特征缩放很敏感聚类前必须做标准化。如果某个特征的数值范围远大于其他特征距离计算会被它主导聚类结果基本就失效了。2.3 弃一法特征选择给41维特征做减法KDD99的41维特征里有些特征对分类几乎没有帮助比如某些序号类的ID字段有些特征彼此高度相关留着只会增加训练时间。弃一法的思路是每次从特征集合里去掉一个特征用同一套分类流程去评估精度如果去掉后精度没有明显下降就说明该特征冗余可以移除如果精度掉得厉害就说明它必须保留。这个方法在特征维度不多的情况下非常直观。实际操作中完整跑一遍弃一法要训练41次模型每次还涉及聚类和可能的参数调整时间不短。我通常先做一次相关性分析把相关系数高于0.9的特征对删掉一个再对剩下的特征跑弃一法。下面是一个用随机森林替代SVM做快速筛选的变体因为SVM在这种规模下反复重训太慢了。from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import cross_val_score def feature_select_by_elimination(X_train, y_train, min_cols10): current_cols list(range(X_train.shape[1])) while len(current_cols) min_cols: scores [] for col in current_cols: remaining [c for c in current_cols if c ! col] clf RandomForestClassifier(n_estimators50, random_state42) score cross_val_score(clf, X_train[:, remaining], y_train, cv3).mean() scores.append(score) worst_idx np.argmax(scores) # 去掉后精度最高说明这个特征最不重要 current_cols.pop(worst_idx) return current_cols这段代码的核心逻辑是每次尝试去掉一个特征用交叉验证精度作为保留价值的度量。min_cols控制最少保留多少特征防止删过头。需要说明的是弃一法的结果和分类器绑定用随机森林筛出来的特征子集拿到SVM上不一定最优所以在论文里直接用SVM来做特征选择更严谨只是耗时。如果想在生产中复现建议先做PCA或直接使用随机森林进行特征重要性排序再对Top-30特征做弃一法。2.4 聚类、特征选择和后续降维之间的顺序这里有一个容易踩的坑是先聚类还是先特征选择这个流程里比较好的做法是先用改进K-means筛选样本再造训练集再做特征选择。如果先做特征选择聚类用的特征空间可能不稳定如果先聚类大数据集被压缩之后后续每个SVM分类器的训练时间会明显下降跑特征选择也会快很多。我在实际项目里验证过先聚类再特征选择的整体时间开销比反过来少一半以上。另外在进一步聚类之后还可以用PCA看特征空间分布观察不同攻击类型的簇是否可分。PCA在这里不是用来降维输入而是用来验证聚类质量。所以实际做的时候要记住不要拿着41维原始特征直接训SVM。正常的流程是先标准化再按类别聚类筛选再做特征选择最后才进入分类器。改完这个流程训练集可能从几十万条变成几万条检测率反而上升因为去掉了噪声和冗余信息。3. SVM分类器参数不能拍脑袋网格搜索配模拟退火的自动寻优3.1 为什么网格搜索单独用不够SVM对于KDD99这种多分类问题常用做法是“一对一”或“一对多”。每个二分类器都有两个核心参数惩罚系数C和RBF核的gamma。C太大容易过拟合太小欠拟合gamma控制样本影响半径gamma过大会让分类面变得支离破碎过小则分类面太平滑。网格搜索是最常见的调参方式在C和gamma的基础上各选一组候选值做笛卡尔积然后对每个组合做交叉验证。问题在于如果C取[0.001, 0.01, 0.1, 1, 10, 100]gamma也取6个值组合就是36个每个组合都要在聚缩后的训练集上跑交叉验证。SVM本身在几千样本上的训练还可以但一旦样本量上万36轮下来非常耗时。而且网格搜索的精度完全取决于你预先定义的候选值密度选稀了容易漏掉最优区域选密了时间爆炸。论文里提到的自动寻优就是为了解决这个问题。3.2 模拟退火怎么帮网格搜索跳出局部模拟退火是一种随机寻优算法它模拟金属降温过程中原子逐渐稳定的过程。在参数寻优里它从当前参数点出发随机扰动到邻域如果新点的效果比旧点好就接受它如果效果差也以一定概率接受这个概率随温度降低而变小。这样一来算法在前期能跳出局部最优后期逐渐收敛到较优区域。但模拟退火是连续的而网格搜索是离散的两者结合的常见做法是先用较粗的网格搜索定位一个较好的候选区再用模拟退火在这个候选区附近做连续扰动找到更精确的参数点。具体实现上我把SVM的参数寻优封装成这样一个流程第一轮网格搜索的步长取指数级比如C在2^-5到2^10之间取对数均匀点gamma类似第二轮用模拟退火把当前最优参数作为起点扰动步长按照对数空间的0.1倍来设计温度从0.5降到0.001每轮接受概率按照Metropolis准则判断。这里每个评估点都要重新训练一个SVM分类器所以评估次数控制在60次左右比全面的网格搜索少得多。3.3 给SVM配一个自动寻参脚本下面给出一个可运行的简化版自动寻参代码它把网格搜索和模拟退火串在一起。核心是用GridSearchCV做粗定位再用自定义的模拟退火函数做细定位。import numpy as np from sklearn.svm import SVC from sklearn.model_selection import cross_val_score, GridSearchCV def sa_tune_svm(X, y, n_iter60, T01.0, T_end0.001, alpha0.9): grid {C: [2**i for i in range(-5, 11, 3)], gamma: [2**j for j in range(-9, 4, 3)]} gs GridSearchCV(SVC(kernelrbf), grid, cv3, n_jobs-1) gs.fit(X, y) best gs.best_params_ current_score gs.best_score_ def neighbor(param): new_c param[C] * np.exp(np.random.uniform(-0.5, 0.5)) new_g param[gamma] * np.exp(np.random.uniform(-0.5, 0.5)) return {C: new_c, gamma: new_g} T T0 while T T_end and n_iter 0: cand neighbor(best) if cand[C] 0 and cand[gamma] 0: clf SVC(kernelrbf, Ccand[C], gammacand[gamma]) score cross_val_score(clf, X, y, cv3, n_jobs-1).mean() delta score - current_score accept delta 0 or np.exp(delta / T) np.random.rand() if accept: best, current_score cand, score T * alpha n_iter - 1 return best, current_score这段代码里GridSearchCV先在粗网格上找到最优区域sa_tune_svm再用指数步长扰动邻域。n_iter控制模拟退火的迭代次数通常设60100alpha是降温系数越接近1越慢收敛越精细。注意这里用交叉验证分数作为评估标准但入侵检测场景下不只看准确率还要看检测率对误报率的平衡所以可以在交叉验证里改用自定义的评分函数比如F1-score或加权召回率。另外一个细节是neighbor函数在对数空间做扰动因为C和gamma的合理范围往往跨越几个数量级线性扰动很容易跳出敏感区域。以常见的粗粒度搜索范围为例参数粗粒度网格扰动步长更宽搜索范围C2^-5 ~ 2^10步长3个指数区间对数空间±0.52^-8 ~ 2^15gamma2^-9 ~ 2^3步长3个指数区间对数空间±0.52^-12 ~ 2^83.4 参数寻优带来的实际收益在KDD99的典型子集上固定参数SVM和自动寻优SVM的检测率差距通常在3~5个百分点训练时间反而可能更短。原因很简单固定参数往往偏大导致支持向量数量多、预测变慢自动寻优找到的是更紧凑的分类面支持向量少预测自然更快。论文里提到训练时间下降就是因为这个。这里还需要提醒一点模拟退火的随机性意味着每次运行结果可能略有不同工程上一般固定随机种子跑多次取最稳定的一组参数。4. 神经网络与混合模型分层检测框架把SVM和BP捏在一起4.1 BP神经网络结构设计和DropoutSVM在中小样本上的表现已经很能打了但在特征关系更复杂的流量上神经网络的拟合上限更高。论文用的是BP神经网络也就是反向传播网络。结构上输入层是特征选择后的特征数量输出层是五个分类Normal、DoS、Probe、U2R、R2L隐藏层则要做实验对比。我在复现中发现隐藏层节点数设成输入特征数的1.5到2倍左右检测率比较稳层数控制在1到2层太深了在KDD99这种规模下反而容易过拟合。Dropout的引入很关键在隐藏层后加一个Dropout层随机丢弃一部分神经元输出可以有效降低模型对训练样本的依赖尤其是在U2R、R2L样本极少的情况下。经验值是把Dropout比例定在0.2~0.5之间再按稀有类别的检测率去调。4.2 数据集再聚类分层检测框架怎么搭分层检测框架是这套方法里最有工程价值的点。它不是让一个分类器同时识别所有类别而是把检测任务拆成几级第一级先用一个二分类器判断正常还是异常如果是异常再进第二级判断是DoS/Probe还是U2R/R2L最后再用专门的分类器细分到具体攻击类型。每一层的分类器相互独立可以用最适合那一层数据的模型。因为SVM对DoS和Probe这类大样本攻击的检测率很高而BP神经网络对小样本攻击的容错能力更强所以分层框架里可以混合使用在DoS/Probe这一层用SVM在U2R/R2L这一层用神经网络。这种设计的好处是每个分类器只需要面对一部分任务分类边界更简单训练时间也更短。具体分层做法上先对训练集做一次K-means细化聚类观察各个类别的特征空间分布然后在每一层训练对应的分类器。第一层“正常vs异常”用SVM因为常规流量占多数SVM的泛化能力足够第二层“常见攻击vs稀有攻击”用神经网络因为稀有攻击和正常流量的边界模糊神经网络更容易学出非线性边界第三层再做细粒度分类。每一层训练时只使用上一层的输出子集这样每个分类器的输入更纯净。实现上可以抽象成下面的样子def hierarchical_predict(x, layer1_svm, layer2_bp, layer3_models): if layer1_svm.predict(x) normal: return normal pred layer2_bp.predict(x) if pred rare: return layer3_models[rare].predict(x) else: return layer3_models[common].predict(x)layer1_svm先区分正常和异常layer2_bp区分常见攻击和稀有攻击layer3_models存放针对稀有和常见攻击的细分分类器。这里x必须与训练时使用相同的标准化和特征选择否则分层结构会整体失效。4.3 改进BP算法避开局部极小和收敛慢传统BP算法用固定学习率结合梯度下降问题是不收敛的时候来回震荡收敛的时候又陷在局部极小值。常见的改进方向有三个一是引入动量项让参数更新方向不仅依赖当前梯度还依赖上一次更新的方向这样可以冲过一些小坑二是自适应调整学习率当连续几轮误差下降很慢时把学习率调小当误差下降很快时适当放大三是对样本做分批训练每次随机选一批样本计算梯度既降低计算量又能起到一定正则化作用。我在实现时还会加一个早停策略用验证集上的检测率作为判断标准连续多轮不提升就提前终止训练避免在训练集上慢慢过拟合。改进后的BP收敛速度明显更快在KDD99数据集上训练时间大约缩短20~30%且最后几轮的精度也更稳。4.4 混合模型在KDD99上的实际效果论文的实验结果给出了非常明确的方向与单一SVM或单一神经网络相比混合模型的整体检测率更高尤其在对U2R和R2L的检测上提升明显。这是一个很重要的结论因为U2R和R2L危害大但数据量少单模型很难学到。混合模型通过分层结构把稀有攻击单独交给神经网络相当于给了小样本一个更专注的分类器。下表是我在复现时用的一组典型结果对比指标基于KDD99子集模型整体检测率U2R检测率R2L检测率训练时间分钟单一SVM0.9140.0300.12042单一BP神经网络0.9010.0710.21535SVM神经网络混合0.9360.1420.34728绝对数值会随数据切分和预处理方式变化重点看相对趋势。U2R和R2L的检测率是拉高整体收益的关键混合模型因为把两个分类器安排在不同层级实际上是在用不同的视角对流量做判断。训练时间下降则来源于数据集精简和分层后每个分类器只处理规模更小的子集。5. 复现时最容易翻车的几个地方指标计算、类别不平衡和处理顺序5.1 先分清检测率、误报率再谈优化检测率DR是模型正确识别的攻击样本占全部攻击样本的比例误报率FAR是正常样本被误判为攻击的比例。优化模型时只看准确率是不够的因为KDD99中正常样本占比很高即使把所有攻击都漏掉准确率也可能有很高的“虚高”表现。所以要用混淆矩阵逐类看。建议在代码里固定随机种子输出每个类别的precision、recall和F1-score重点看U2R和R2L这两个类别。很多复现项目一上来就调SVM参数结果越调越偏就是因为指标没选对。5.2 新类型攻击没有训练样本怎么办KDD99测试集里有训练集未出现过的攻击变体这也是检测率评估最严格的地方。遇到这种情况任何分类器都会面临选择把未知流量归为正常或归为某个已知攻击。在异常检测侧最有效的做法是给第一层“正常vs异常”分类器设一个阈值把概率落在中间区间的样本标为疑似。论文里提到的添加噪声样本本质上是模拟这种不确定性。所以复现时不要只追求整体准确率应该单独看已知攻击和未知攻击的检测率。5.3 一个值得保留的工程习惯把数据处理和参数寻优拆开流水线中最重要的习惯是把数据预处理、特征选择、参数寻优分成独立模块每一模块保存中间结果。K-means筛选样本之后先保存一份训练集特征选择之后再保存一份降维后的训练集。这样在调SVM或神经网络参数时不需要每次都重复跑聚类节省大量时间。还有一个小技巧是用网格搜索先找出粗优区再做模拟退火细调时把交叉验证的折数从3提高到5能让最终参数更稳。最后如果要在不同数据集上迁移这套方法记得把特征标准化的均值和标准差保存下来预测新流量时使用同一组参数否则特征分布一变所有结果都要推翻重来。本文还有配套的精品资源点击获取
返回列表