ARTICLE DETAIL

资讯详情

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

深度学习网络入侵检测毕设实战:从NSL-KDD预处理到CNN/LSTM模型落地

深度学习网络入侵检测毕设实战:从NSL-KDD预处理到CNN/LSTM模型落地 简介一套基于深度学习的网络入侵检测系统完整代码包面向毕业设计、课程设计与人工智能方向的初学者聚焦传统规则检测无法灵活应对新型未知攻击的难题。包内共174个文件压缩包约83.58MB以97个Python脚本、57个pyc编译文件为主体另含5个Markdown说明文档、4个IPython Notebook交互式笔记本、配置文件与pickle模型文件。Python脚本各司其职Picture.py实现数据特征可视化Cheak_matrix.py计算准确率、召回率等评估指标Generate_fedtask.py用于生成联邦学习任务Main.py作为主控程序串联完整检测流程ipynb文件则分别演示SDN与NSL数据集的分步处理方法适合边看边实践。目前已有67人学习下载。资源完整覆盖数据预处理、特征提取、深度学习模型训练与实时入侵检测四大模块并附依赖清单可直接运行复现也能为论文撰写或答辩演示提供有力支撑。1. 网络入侵检测毕设入门三十行代码能跑但答辩要先想清楚这三件事“基于深度学习的网络入侵检测”这几年在本科毕业设计和课程设计里出场频率很高但说实话它既不是把数据丢给模型就出结果的人工智能速成题也不像有些人以为的那样必须靠堆显卡。我拆过不少类似案例绝大多数人翻车点集中在三个地方NSL-KDD 数据集的标签处理、类别不平衡带来的指标虚高、以及答辩时被问到“这个检测模型实际能拦住什么攻击”却答不上来。这篇文章从数据预处理讲起一直写到模型搭建、训练评估、避坑和最后的推理落地。目标读者是打算拿这个题目做毕业设计或课程设计的学生也适合刚入门深度学习、想找一个安全领域实战项目练手的从业者。读完你可以直接照着代码复现一套完整的入侵检测流程。2. 数据准备NSL-KDD 的标签陷阱与归一化边界2.1 数据集选型为什么不用 KDD99 而用 NSL-KDD早期很多教程用 KDD99但 KDD99 的训练集里有大量重复记录模型会把“见过这条样本”误当成“学会识别这种攻击”测试的时候指标虚高得很厉害。NSL-KDD 是 KDD99 的修正版去掉了冗余训练集 125973 条、测试集 22544 条每条记录由一个 41 维特征向量加一个攻击类型标签和一个难度评分组成。选 NSL-KDD 的理由有三个数据量对课程设计、毕业设计刚好合适单卡 CPU 也能跑完一轮 epoch样本类别划分清晰适合讲多分类问题很多论文拿它做基准答辩时你引用指标容易找到对照。这里要提醒一句NSL-KDD 是 2009 年发布的老数据集它的价值不在“最新”而在“可复现性强”回答老师质疑时直接说“用它做基准是为了和同类文献对比”就够了。2.2 标签处理与特征归一化先把 41 维特征拆明白数据集里的第 0 到 40 列是特征第 41 列是攻击类型标签第 42 列是难度评分。攻击类型一共 39 种通常按行为归成四大类DoS、Probe、R2L、U2R加上正常流量 Normal所以模型输出一般是 5 分类。很多课程设计偷懒直接做二分类正常/异常也能交差但答辩时如果老师说“请分析你漏报的是哪一类攻击”你会比较被动。import pandas as pd import numpy as np from sklearn.preprocessing import MinMaxScaler, LabelEncoder # 列名按 NSL-KDD 官方顺序补全这里用索引读取 train_df pd.read_csv(KDDTrain.csv, headerNone) test_df pd.read_csv(KDDTest.csv, headerNone) # 第41列是攻击标签第42列是难度系数不参与训练 X_train train_df.iloc[:, 0:41].values y_train train_df.iloc[:, 41].values X_test test_df.iloc[:, 0:41].values y_test test_df.iloc[:, 41].values # 协议类型、服务、标志位是字符串需要编码 for i in [1, 2, 3]: # 第1列protocol_type第2列service第3列flag encoder LabelEncoder() # 训练测试合并编码防止测试集出现训练集没见过的类别时报错 all_values np.concatenate([train_df[i], test_df[i]]) encoder.fit(all_values) train_df[i] encoder.transform(train_df[i]) test_df[i] encoder.transform(test_df[i])这段代码有三个关键点。第一字符串列是 protocol_type、service、flag 这三列必须转成数值否则模型输入不了第二LabelEncoder 一定要把训练集和测试集的取值合并后一起 fit常见错误是只 fit 训练集测试集里新出现一个服务名直接报错第三归一化时同样只能 fit 训练集再用训练集的 min、max 去 transform 测试集这一点放到后面避坑章节展开它是数据泄漏的高发地。标签映射建议单独下一行attack_map { normal: 0, neptune: 1, back: 1, land: 1, pod: 1, smurf: 1, teardrop: 1, apache2: 1, udpstorm: 1, processtable: 1, mailbomb: 1, satan: 2, ipsweep: 2, nmap: 2, portsweep: 2, mscan: 2, saint: 2, warezclient: 3, guess_passwd: 3, warezmaster: 3, imap: 3, ftp_write: 3, multihop: 3, phf: 3, spy: 3, snmpgetattack: 3, snmpguess: 3, httptunnel: 3, named: 3, xlock: 3, sendmail: 3, worm: 3, buffer_overflow: 4, loadmodule: 4, perl: 4, rootkit: 4, ps: 4, sqlattack: 4, xterm: 4, saint: 2 }这种映射看起来简单但 R2L 和 U2R 的细分类特别容易抄错。常见翻车是把 ‘warezclient’ 和 ‘warezmaster’ 归到不同大类模型预测时标签错位F1 分数直接掉十几个点你还在傻乎乎调参。我一般会把映射表写成独立 py 文件训练之前先打印几行np.unique(y_train, return_countsTrue)和映射后的分布核对一遍确认四大类数量对得上再往下走。2.3 类别不平衡先看分布再谈模型NSL-KDD 训练集的类别分布大致是Normal 约 67343 条DoS 约 45927 条Probe 约 11656 条R2L 只有 995 条U2R 只有 52 条。U2R 连 100 条都不到拿它训练一个分类器基本等于没有训练。这就是整个项目里最容易踩的坑不做不平衡处理模型会学会“把所有流量都判成 Normal”来刷准确率。# 打印映射后分布 unique, counts np.unique(y_train_encoded, return_countsTrue) for label, count in zip(unique, counts): print(fclass {label}: {count})处理不平衡有三种常见做法。第一种是给少数类加权重class_weight参数在 Keras 和 sklearn 里都支持实现成本最低第二种是过采样比如用 SMOTE 给 R2L、U2R 生成合成样本但要注意 SMOTE 对数值型特征有效对经过 one-hot 编码的特征有时会生成不合理的组合第三种是调整评价指标不看准确率看每个类的召回率和精确率。我建议课程设计用第一种加第三种组合毕业设计想体现工作量可以再加 SMOTE 对比实验。这里还要提醒难度系数那一列第 42 列不要作为特征输入。有学生觉得它代表样本难度能帮模型学得更准实际上这是数据集作者给研究者做评估用的元信息测试集里这一列往往被留空模型训练用了它测试阶段就缺列直接报错。3. 模型搭建一维 CNN 与 LSTM 两种路线的取舍3.1 为什么换掉传统机器学习很多毕设第一版用的是随机森林或 SVM效果其实不差但题目既然叫“基于深度学习”网络结构必须占核心篇幅。深度模型在这里面的优势是自动特征提取41 维特征里有很多特征之间存在非线性关系传统方法需要手工构造特征组合而一维 CNN 能用卷积核自动扫描相邻特征LSTM 能捕捉特征在时间顺序上的依赖关系。虽然 NSL-KDD 每条样本相对独立时序性不强但把特征序列当成时间步输入 LSTM 后实验证明它依然能学到特征间的整体关联模式。3.2 一维 CNN卷积核扫过41维特征import tensorflow as tf from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv1D, MaxPooling1D, Flatten, Dense, Dropout model Sequential([ # 输入形状是 (样本数, 41, 1)把每个特征当成一个通道维度 Conv1D(filters64, kernel_size3, activationrelu, input_shape(41, 1)), Conv1D(filters64, kernel_size3, activationrelu), MaxPooling1D(pool_size2), Dropout(0.5), Flatten(), Dense(128, activationrelu), Dropout(0.5), Dense(5, activationsoftmax) # 5分类输出 ]) model.summary()注意输入要先 reshape 成(样本数, 41, 1)因为 Conv1D 期望三维输入样本维度、时间步/特征维度、通道维度。这里我把 41 维特征当作 41 个时间步每个特征维度为 1。kernel_size 设成 3 意思是每次卷积覆盖相邻 3 个特征相当于在特征之间做局部关联filters 64 表示有 64 组卷积核会提取出 64 种特征模式。Dropout 放在池化层后和全连接前防止过拟合NSL-KDD 特征维度不高Dropout 0.5 是个保守可靠的值。3.3 LSTM 与 CNNLSTM 混合结构from tensorflow.keras.layers import LSTM, Reshape, Bidirectional lstm_model Sequential([ LSTM(units64, return_sequencesFalse, input_shape(41, 1)), Dropout(0.5), Dense(128, activationrelu), Dropout(0.3), Dense(5, activationsoftmax) ]) # 混合模型CNN 先提特征LSTM 再处理时序 mixed_model Sequential([ Conv1D(filters32, kernel_size3, activationrelu, input_shape(41, 1)), MaxPooling1D(pool_size2), # 特征降到 20 维 LSTM(units32, return_sequencesFalse), Dense(64, activationrelu), Dense(5, activationsoftmax) ])LSTM 的输入形状同样是三维这里不做额外 reshape 是因为前面已经准备好了。units64 是 LSTM 隐层维度越大拟合能力越强但训练时间也越长。混合模型里 CNN 的池化把特征从 41 压到 20再交给 LSTM这样既利用了卷积的局部特征提取又保留了序列建模能力。从实验效果看混合模型通常比单用 CNN 高 1-2 个百分点的 F1但训练时间多一倍。做课程设计建议单用 CNN 足够毕业设计可以三个模型各跑一遍做对比答辩时图表更丰满。4. 训练与评估多分类指标、误报率与分层验证4.1 别只盯着 accuracy这个数据集上准确率会骗人NSL-KDD 测试集分布比训练集更极端R2L 样本数量大增如果只看准确率模型可能到 90% 以上但实际上 R2L 和 U2R 的召回率是 0。准确率此时就是一个“黑匣子指标”它只能告诉你绝大多数样本对了却无法告诉你漏掉的攻击是哪种。from sklearn.metrics import classification_report, confusion_matrix y_pred model.predict(X_test_reshaped) y_pred_classes np.argmax(y_pred, axis1) print(classification_report(y_test_encoded, y_pred_classes, target_names[Normal, DoS, Probe, R2L, U2R]))做完预测之后用classification_report输出每一类的 precision、recall、F1。注意看 R2L 和 U2R 这两行的 recall如果接近 0说明模型完全学不到这两类。此时不要着急改网络结构先回去检查类别不平衡的处理是否生效。误报率在安全场景里也很关键精确率低意味着正常流量被频繁告警负责运维的人会被报警淹没最终选择关掉系统——这是入侵检测落地的经典失败方式。4.2 训练超参数几个能直接用的值model.compile( optimizertf.keras.optimizers.Adam(learning_rate1e-3), losssparse_categorical_crossentropy, metrics[accuracy] ) early_stop tf.keras.callbacks.EarlyStopping( monitorval_loss, patience10, restore_best_weightsTrue ) reduce_lr tf.keras.callbacks.ReduceLROnPlateau( monitorval_loss, factor0.5, patience5, min_lr1e-5 ) history model.fit( X_train_reshaped, y_train_encoded, validation_split0.2, epochs50, batch_size64, callbacks[early_stop, reduce_lr], verbose1 )参数上有三处细节值得记录。第一losssparse_categorical_crossentropy对应的是整数标签如果你的标签做成了 one-hot就要改成categorical_crossentropy这两个经常搞混报错时先检查这里。第二validation_split0.2默认是从训练集尾部切 20%如果原数据没有打乱尾部几乎全是某几类攻击验证集分布失真正确做法是自己先train_test_split并传shuffleTrue或者在 fit 前手动洗牌。第三patience10表示验证损失连续 10 轮没下降就停restore_best_weightsTrue可以保证模型权重回滚到验证损失最低的那一轮这两个参数组合能省大量调参时间。4.3 分层验证按攻击类别切分训练集普通train_test_split默认按样本顺序切NSL-KDD 数据里每类攻击是分段聚集的顺序切分很容易让验证集里缺掉 U2R 甚至 R2L。更稳的做法是分层抽样from sklearn.model_selection import train_test_split X_train, X_val, y_train, y_val train_test_split( X_reshaped, y_encoded, test_size0.2, stratifyy_encoded, # 按类别比例切分 random_state42 )stratifyy_encoded保证切分后训练集和验证集里五类占比一致尤其保证 U2R 那 52 条样本在验证集里也能留下几条。random_state42是复现的命根子不设固定随机种子你每次跑出来的结果都不一样写进毕设论文里的实验数据就没法复现答辩时老师要你现场演示会很被动。建议在脚本最开头加上tf.random.set_seed(42)和np.random.seed(42)。5. 避坑指南特征泄漏、标签错位等五种翻车现场5.1 归一化把训练集和测试集合并了现象模型训练完在验证集上 F1 达到 0.93换到测试集直接掉到 0.81精确率和召回率全面下滑。原因很多人写代码图省事scaler.fit_transform(X_all)把训练集测试集放在一起归一化。测试集的 min、max 信息提前参与了训练集预处理等价于模型偷看到了测试数据的分布这就是数据泄漏。NSL-KDD 尤其容易中招因为训练集测试集特征分布存在系统性差异。解决改成scaler.fit(X_train)然后用scaler.transform(X_train)和scaler.transform(X_test)分开处理。确认方式很简单打印scaler.data_min_看看是不是只来自训练集。5.2 one-hot 编码使特征维度对不上现象训练时提示ValueError: shapes (n, 119) and (41, 64) incompatible。原因把 protocol_type、service、flag 做了 one-hot 编码后特征维度从 41 变成 100 多但模型第一个卷积层写死input_shape(41, 1)。解决训练前固定打印一次X_train.shape[1]模型输入的维度等号右边写这个值不要手写 41。另外编码 service 列时训练集和测试集要用同一个 encoder 的字典否则测试集出现训练集没有的 service 值会产生形状不一致。5.3 标签映射潜入笔误现象分类报告显示 Probe 类的 recall 异常低U2R 却异常高怎么调参都救不回来。原因映射表里某几个攻击名拼错或归错大类比如saint同时出现在 Probe 和 U2R 的映射里字典后面一个值覆盖前面一个导致一批样本被标错。解决写完映射表先跑一段统计打印每个攻击名映射后的目标类再和 NSL-KDD 官方文档核对一遍。这种错位不会报错只会默默污染训练数据属于最耗时的隐性问题。5.4 只跑 5 个 epoch 就提交现象毕设代码在别人电脑上复现正常自己电脑跑 50 个 epoch 要一小时于是改成 5 个 epoch 加无早停答辩前两天训练出的模型 loss 还在 1.2 附近徘徊。原因深度学习训练不是线性下降的前几个 epoch 看起来 loss 在降但网络远没有收敛Dropout 还没完成“纠错”过程。用缩减 epoch 来换时间模型质量会崩。解决不要动 epoch 数量改成减小模型规模或者换用更小的 batch size。如果训练硬件确实受限提前准备一份在 Google Colab 跑的版本答辩时把两次实验结果分开标注论文里写清楚硬件环境是 T4 GPU。5.5 忽略随机种子导致结果不可复现现象同一份脚本连续跑三次准确率分别是 0.87、0.90、0.88写进论文的只能是“约 0.88”。原因Keras 的权重初始化和数据洗牌都依赖随机数不设随机种子每次结果天然不一样。如果论文里只写最优一次的结果答辩被质疑时你无法解释为什么复现不出来。解决脚本开头固定tf.random.set_seed(42)、np.random.seed(42)、random.seed(42)并把 Python、TensorFlow 的版本号记录在 README 里标注“运行环境Python 3.10 TensorFlow 2.15.0结果可复现”。6. 从训练到落地保存模型、单条预测与答辩演示6.1 保存模型与加载推理训练完成不要只在内存里开心立刻把模型和预处理工具一起保存否则关掉 Jupyter 再回来一切要重跑。model.save(ids_model.keras) # 推理单条样本 from tensorflow.keras.models import load_model loaded_model load_model(ids_model.keras) def predict_one(sample): sample_scaled scaler.transform(sample.reshape(1, -1)) sample_scaled sample_scaled.reshape(1, 41, 1) prob loaded_model.predict(sample_scaled, verbose0) class_id np.argmax(prob, axis1)[0] return class_id, prob[0]推理脚本里最关键的是保证输入样本走相同的预处理链路先转数值标签再归一化再 reshape 成(1, 41, 1)。很多同学保存了模型却忘了保存 scaler推理阶段一跑就报错或者用训练时没见过的编码方式处理字符串列结果完全失真。建议把 scaler 和 LabelEncoder 用joblib.dump一并保存推理脚本里三件套一起加载。6.2 答辩演示一张图讲清网络结构答辩时不要贴一堆训练代码准备一张结构图画清楚 41 维输入到 Conv1D 输出维度变成(20, 64)再池化、展平、全连接、softmax 的完整链路。老师问到“为什么选这个结构”你就说卷积核在特征维度上做局部关联能捕捉特征间的依赖问到“模型能检测哪些攻击”直接拿测试集里一条真实样本现场预测把概率输出打印出来比背台词更能说明问题。这里我还想分享一个习惯后来我每次做毕设项目都强制自己在开始写模型之前先把数据分布、验证集划分和随机种子写进一个 README 文件这样就算隔两周再看也能快速还原。制作这类深度学习实战项目时最怕的不是模型做不出来而是做完之后留不下可复现的痕迹。希望这篇拆解能帮你把这个题目做得又快又稳。本文还有配套的精品资源点击获取
返回列表