资讯详情

资讯详情

CNN+随机森林做网络入侵检测:UNSW_NB15课设完整实战

简介这份资源是面向计算机、通信、人工智能、自动化等专业学生与从业者的网络入侵检测实战项目包可作为毕业设计、课程设计或期末大作业的完整参考方案。项目基于UNSW_NB15官方数据集围绕42列特征与二分类标签完整实现了特征处理、Pearson相关性过滤、随机森林特征选择以及Keras搭建CNN网络训练的全流程并配套项目说明与答辩PPT便于理解整体设计思路与实验结论。压缩包为zip格式大小约10.02MB内含源码、说明文档与演示材料代码按StrTonum、Min-max、Balanced、Pearson、Find_feature、Pro_Testdata、CNN等模块划分运行流程清晰并附有requirements.txt便于快速配置环境。目前已有339人学习关注适合希望借鉴特征工程与深度学习融合思路、快速上手复现或进行二次开发的读者参考使用。1. 从一份课设说起CNN 加随机森林做入侵检测到底能跑出什么结果如果你正在为机器学习课程大作业或者毕业设计找方向网络入侵检测是个很稳的选题——数据集公开、评价指标清晰、答辩时也好讲。但真正动手时你会发现从原始流量特征到能跑出一个像样的准确率中间隔着特征清洗、样本不平衡、维度灾难好几道坎。这份资源给了一条完整的流水线用 UNSW_NB15 官方数据集先做 Pearson 相关性过滤再用随机森林做特征选择最后把筛出来的特征送进 Keras 搭的 CNN 做二分类。42 列特征一列标签二分类问题代码按StrTonum.py到CNN.py的顺序串起来就能跑。它适合计算机、通信、人工智能方向的学生直接拿来做期末课设或毕设也适合想快速了解「传统特征工程 深度学习」这套组合拳的从业者。下面我把这份源码拆开按实际运行顺序讲清楚每一步在干什么、参数怎么调、哪里容易翻车。2. 数据预处理流水线从字符串到归一化每一步都在解决什么2.1 为什么第一步是 StrTonum.py 而不是直接读 CSVUNSW_NB15 数据集里混着数值型和分类型特征。像dur、sbytes、dbytes这些是数值但proto、service、state是字符串类别。CNN 的输入层只认数字所以第一步必须把字符串映射成整数编码。StrTonum.py干的就是这件事——遍历所有 object 类型的列用LabelEncoder或者自定义映射表转成数值。常见做法是先把训练集和测试集拼在一起做编码避免测试集出现训练集没见过的类别导致编码越界。import pandas as pd from sklearn.preprocessing import LabelEncoder # 读取原始训练集和测试集 train pd.read_csv(UNSW_NB15_training-set.csv) test pd.read_csv(UNSW_NB15_testing-set.csv) # 合并后统一编码保证类别映射一致 full pd.concat([train, test], axis0, ignore_indexTrue) cat_cols full.select_dtypes(include[object]).columns.tolist() # 排除标签列标签单独处理 if attack_cat in cat_cols: cat_cols.remove(attack_cat) le LabelEncoder() for col in cat_cols: full[col] le.fit_transform(full[col].astype(str)) # 标签列二值化0 正常1 攻击 full[label] full[label].astype(int) # 重新切分 train full.iloc[:len(train), :].copy() test full.iloc[len(train):, :].copy()这段代码的关键点在于合并编码。如果分别对训练集和测试集做LabelEncoder同一个proto值可能在两边被编成不同数字模型直接学废。参数上注意attack_cat是多分类标签二分类任务里用不到要提前剔除否则它会干扰特征选择。另外LabelEncoder对无序类别是安全的但如果你后续想换 One-Hot特征维度会从 42 膨胀到上百CNN 训练时间成倍增加课设阶段不建议。2.2 Min-max 归一化和 Balanced.py 的样本平衡数值特征量纲差异极大sbytes可能上万dur只有零点几秒。不做归一化CNN 的梯度会被大量纲特征主导收敛慢甚至不收敛。Min-max 把每个特征线性映射到 [0,1]公式是(x - min) / (max - min)。注意 min 和 max 必须从训练集算然后应用到测试集否则就是数据泄露。from sklearn.preprocessing import MinMaxScaler # 分离特征和标签 feature_cols [c for c in train.columns if c not in [label, attack_cat]] X_train train[feature_cols].values y_train train[label].values X_test test[feature_cols].values y_test test[label].values # 只在训练集上 fit scaler MinMaxScaler() X_train scaler.fit_transform(X_train) X_test scaler.transform(X_test) # 用训练集的 min/maxBalanced.py处理的是另一类问题UNSW_NB15 里正常流量和攻击流量比例失衡直接训练会让模型偏向多数类准确率看着高但召回率惨淡。代码里参考 ADASYN 算法做过采样核心思想是根据少数类样本的分布密度自适应生成新样本——密度低的区域多生成密度高的区域少生成。实际运行时如果 ADASYN 太慢可以退化成 SMOTE效果差距在课设场景下可以接受。平衡之后正负样本接近 1:1后续 CNN 的 loss 才不会一边倒。提示归一化和平衡的顺序不能反。先平衡再归一化新生成的样本会参与 min/max 计算导致测试集归一化基准偏移。正确顺序是先在训练集上归一化再对归一化后的数据做过采样。3. 特征选择双保险Pearson 过滤加随机森林把 42 维压到多少维合适3.1 Pearson.py 在筛什么线性相关性的边界Pearson 相关系数衡量的是两个连续变量之间的线性关系取值 -1 到 1绝对值越接近 1 越相关。Pearson.py计算的是特征与特征之间、特征与标签之间的相关性。特征之间相关性过高说明信息冗余留着只会增加计算量还可能过拟合特征与标签相关性太低说明这个特征对分类没贡献应该丢掉。import numpy as np import seaborn as sns import matplotlib.pyplot as plt # 计算特征间相关系数矩阵 corr_matrix pd.DataFrame(X_train, columnsfeature_cols).corr(methodpearson) # 找出相关性高于阈值的特征对 threshold 0.9 upper corr_matrix.where(np.triu(np.ones(corr_matrix.shape), k1).astype(bool)) to_drop [column for column in upper.columns if any(upper[column].abs() threshold)] print(f因高相关拟剔除的特征: {to_drop})阈值 0.9 是常见起点但别死守。UNSW_NB15 里sbytes和dbytes这类流量统计特征相关性天然偏高如果一刀切到 0.8 以下可能把有用信息也砍了。我的习惯是先跑一遍看分布如果大部分特征对都在 0.95 以上才考虑降阈值。另外 Pearson 只抓线性关系对非线性关联无能为力所以它只是第一道粗筛不能替代后面的随机森林。3.2 Find_feature.py 用随机森林做特征重要性排序随机森林做特征选择的逻辑很直接训练多棵决策树每棵树在分裂节点时选最优特征最后统计每个特征在所有树中被选为分裂点的次数或者带来的不纯度下降总量归一化后就是特征重要性。Find_feature.py通常会把重要性从高到低排序然后让你选一个累计贡献率阈值比如前 15 个特征已经覆盖 95% 的重要性后面的就可以扔了。from sklearn.ensemble import RandomForestClassifier # 用训练集训练随机森林 rf RandomForestClassifier( n_estimators100, # 树的数量100 是精度和速度的平衡点 max_depthNone, # 不限制深度让树充分生长 random_state42, # 固定随机种子保证结果可复现 n_jobs-1 # 用满所有 CPU 核心 ) rf.fit(X_train, y_train) # 获取特征重要性 importances rf.feature_importances_ indices np.argsort(importances)[::-1] # 按累计贡献率选特征 sorted_imp importances[indices] cumsum np.cumsum(sorted_imp) n_selected np.searchsorted(cumsum, 0.95) 1 # 累计到 95% selected_features [feature_cols[i] for i in indices[:n_selected]] print(f选中 {n_selected} 个特征: {selected_features})n_estimators设 100 是课设级别的稳妥选择再往上加精度提升有限但训练时间线性增长。random_state必须固定不然每次跑出来的特征排序都不一样答辩时没法复现。累计贡献率 95% 是我一般会用的值但如果你发现选出来的特征数还是个位数可以放宽到 90% 看看是不是漏掉了弱相关但有用的特征。选完之后记得把selected_features存下来Pro_Testdata.py要用同一套特征列来对齐测试集。3.3 Pro_Testdata.py 的对齐逻辑训练集筛了什么测试集就得跟什么这一步最容易被忽略但错了整个评估就废了。训练集经过 Pearson 和随机森林筛完之后只剩一部分特征测试集必须用完全相同的特征列和顺序。Pro_Testdata.py做的就是按selected_features列表重新索引测试集确保送入 CNN 的输入维度一致。# 按训练集选出的特征对齐测试集 X_test_selected test[selected_features].values # 重新做归一化注意 scaler 要用训练集 fit 好的那个 X_test_selected scaler.transform(X_test_selected) # 确认维度一致 assert X_train_selected.shape[1] X_test_selected.shape[1], 特征维度不匹配如果跳过这一步直接拿原始 42 维测试集去评估模型输入维度对不上会直接报错更隐蔽的情况是维度碰巧一样但特征顺序不同模型照样能跑但准确率会莫名其妙掉一大截。这种坑我在早期项目里踩过排查了半天才发现是列顺序问题。4. CNN.py 搭网络Keras 结构、参数和训练过程里的几个关键决策4.1 一维卷积处理表格特征输入形状怎么定网络入侵检测的特征是一维向量不是图像所以用Conv1D而不是Conv2D。输入形状是(n_features, 1)把每个特征当成一个时间步或者一个通道。Keras 里Conv1D的filters参数决定卷积核数量kernel_size决定一次看几个相邻特征。对于 15 到 20 维的特征向量kernel_size设 3 比较合适太大感受野覆盖过多反而模糊了局部模式。from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv1D, MaxPooling1D, Flatten, Dense, Dropout from tensorflow.keras.optimizers import Adam n_features X_train_selected.shape[1] model Sequential([ # 输入 reshape 成 (n_features, 1) Conv1D(filters32, kernel_size3, activationrelu, input_shape(n_features, 1), paddingsame), MaxPooling1D(pool_size2), Conv1D(filters64, kernel_size3, activationrelu, paddingsame), MaxPooling1D(pool_size2), Flatten(), Dense(64, activationrelu), Dropout(0.5), # 丢弃一半神经元防过拟合 Dense(1, activationsigmoid) # 二分类输出 ]) model.compile( optimizerAdam(learning_rate0.001), lossbinary_crossentropy, metrics[accuracy] ) model.summary()两层卷积加两层池化是课设级别的标准配置。filters从 32 到 64 递增让网络先抓局部细节再组合高层模式。Dropout(0.5)在全连接层之前加是因为全连接层参数量最大、最容易过拟合。paddingsame保证卷积后长度不变池化层再降维避免特征维度掉太快。输出层用sigmoid是因为二分类如果改成多分类攻击类型识别要换成softmax并且 loss 改成categorical_crossentropy。4.2 训练参数和 TensorFlow 与 Keras 的版本对应训练时batch_size设 64 或 128epochs先跑 30 看收敛曲线。如果验证集 loss 在 10 轮之后还在降可以加到 50如果 5 轮就平了说明学习率或者网络容量有问题。validation_split0.2从训练集里切 20% 做验证不要动测试集。# reshape 输入 X_train_cnn X_train_selected.reshape(-1, n_features, 1) X_test_cnn X_test_selected.reshape(-1, n_features, 1) history model.fit( X_train_cnn, y_train, batch_size128, epochs30, validation_split0.2, verbose1 ) # 评估 loss, acc model.evaluate(X_test_cnn, y_test, verbose0) print(f测试集准确率: {acc:.4f})TensorFlow 和 Keras 的版本对应是个血泪坑。TensorFlow 2.x 已经把 Keras 集成进去了from tensorflow.keras是正确写法。如果你环境里同时装了独立的keras包版本不匹配会报ImportError或者某些层参数不识别。requirements.txt里一般会锁版本但如果你自己配环境记住 TensorFlow 2.6 以上对应 Keras 2.6 以上TensorFlow 2.10 之后独立 Keras 包不再需要单独安装。跑之前用pip list | grep tensorflow和pip list | grep keras确认一下。注意如果训练时 loss 一直是nan先检查输入数据里有没有 NaN 或者无穷大。归一化之前用np.isnan(X_train).sum()扫一遍有缺失值就填 0 或者均值。5. 避坑与排查这份源码跑不起来时先看这五条5.1 报错ValueError: Input 0 is incompatible with layer conv1d现象是模型fit的时候直接抛维度不匹配。原因通常是Pro_Testdata.py没跑或者跑的顺序不对测试集还是 42 维而模型输入是按筛选后的维度建的。解决办法是确认selected_features列表在训练和测试阶段是同一个并且X_test_selected的列顺序和X_train_selected完全一致。可以在 reshape 之前打印两个数组的 shape 对比。5.2 准确率虚高到 99% 但召回率极低现象是测试集 accuracy 很好看但混淆矩阵里少数类几乎全被预测成多数类。原因是Balanced.py没执行或者过采样只对训练集做了但评估时用了原始分布。解决方法是确认过采样只作用于训练集测试集保持原始分布并且评估时看classification_report里的 recall 和 f1-score不要只盯 accuracy。5.3pip install -r requirements.txt卡在 TensorFlow 下载现象是安装依赖时 TensorFlow 包下载极慢或者超时。原因是 TensorFlow 包体积大默认源在国外。解决办法是换国内镜像源比如pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple。如果还是慢可以单独先装 TensorFlow再装其他依赖。5.4 随机森林特征重要性每次跑出来不一样现象是Find_feature.py两次运行选出的特征数量不同。原因是RandomForestClassifier的random_state没固定。解决办法是在实例化时显式传入random_state42并且确保n_estimators不要太小100 以上结果基本稳定。5.5 答辩 PPT 里的准确率和代码跑出来的对不上现象是 PPT 上写的 98.5%自己跑出来只有 95% 左右。原因是 PPT 里的结果可能是在特定随机种子或者特定预处理参数下得到的而你的环境有细微差异。解决办法是不要改random_state不要改归一化范围不要改特征选择阈值按源码默认参数跑一遍。如果还是对不上检查数据集版本是否一致UNSW_NB15 官方数据集有过更新。6. 进阶玩法把二分类改成多分类以及怎么验证模型不是靠猜6.1 从二分类扩展到攻击类型识别原始代码做的是二分类正常 vs 攻击。但 UNSW_NB15 的attack_cat列有 9 种攻击类型改成多分类只需要动三个地方。第一标签处理从label列换成attack_cat列用LabelEncoder编成 0 到 8。第二输出层从Dense(1, activationsigmoid)改成Dense(9, activationsoftmax)。第三loss 从binary_crossentropy改成sparse_categorical_crossentropy因为标签是整数不是 one-hot。from tensorflow.keras.utils import to_categorical # 多分类标签 le_attack LabelEncoder() y_train_multi le_attack.fit_transform(train[attack_cat]) y_test_multi le_attack.transform(test[attack_cat]) n_classes len(le_attack.classes_) # 输出层改 softmax model_multi Sequential([ Conv1D(32, 3, activationrelu, input_shape(n_features, 1), paddingsame), MaxPooling1D(2), Conv1D(64, 3, activationrelu, paddingsame), MaxPooling1D(2), Flatten(), Dense(64, activationrelu), Dropout(0.5), Dense(n_classes, activationsoftmax) ]) model_multi.compile( optimizerAdam(learning_rate0.001), losssparse_categorical_crossentropy, metrics[accuracy] )多分类的难点在于类别极度不平衡某些攻击类型样本只有几十条。这时候Balanced.py里的 ADASYN 要针对每个少数类分别做不能只对二分类的正类做。另外评估指标要看 macro f1-score它给每个类别同等权重不会被多数类拉高。6.2 验证模型不是靠猜混淆矩阵和特征重要性交叉检查跑出高准确率之后怎么确认模型学到的是真实模式而不是数据泄露或者随机猜测我一般会做两件事。第一画混淆矩阵看每一类的误判方向。如果正常流量被大量误判成某一种攻击说明这个攻击的特征和正常流量重叠严重需要回去看特征选择是不是漏了关键维度。第二把随机森林选出的 top 特征和 CNN 第一层卷积核的激活模式对照如果 CNN 关注的特征和随机森林重要性高的特征高度重合说明两条路径互相验证了结果可信度更高。from sklearn.metrics import confusion_matrix, classification_report import seaborn as sns y_pred (model.predict(X_test_cnn) 0.5).astype(int) cm confusion_matrix(y_test, y_pred) sns.heatmap(cm, annotTrue, fmtd, cmapBlues) plt.xlabel(Predicted) plt.ylabel(Actual) plt.title(Confusion Matrix) plt.show() print(classification_report(y_test, y_pred, target_names[Normal, Attack]))从那以后我每次跑完入侵检测模型都强制走一遍混淆矩阵加分类报告不看 accuracy 就下结论。这份源码把数据预处理、特征选择、CNN 训练串成了一条完整的线课设答辩够用想往深了做也有扩展空间。希望帮到你。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →