
简介一套基于Python与CNN卷积神经网络的网络入侵检测毕业设计项目面向计算机相关专业学生及需要项目实战的初学者可直接用于毕业设计、课程设计或期末大作业。资源完整覆盖数据预处理、CNN模型构建、模型训练与评估预测全流程以NSL-KDD数据集为实验基础能够帮助读者快速搭建入侵检测系统并理解深度学习在网络安全分类任务中的实际应用。压缩包共含33个文件大小约21.58MB主要文件类型包括csv数据集、py源码脚本、xml工程配置另有pth模型权重、png/jpg可视化图表、md说明文档等目录结构清晰便于按模块查阅。已有115人学习下载。项目包含数据预处理脚本、CNN模型定义、训练与预测入口、准确率曲线图、归一化对比图及模型权重文件并配有README说明文档代码完整可运行适合作为毕业设计或课程设计的参考方案方便在此基础上扩展与二次开发。1. 网络入侵检测毕设CNN 为何成了首选方案做网络入侵检测的毕业设计最容易踩的坑不是模型跑不起来而是把工夫全花在模型上最后发现数据根本喂不进去。我拆过不少类似项目结论大同小异CNN 做入侵检测真正的难点在数据预处理和特征编码不在网络结构本身。这套基于 Python CNN 的网络入侵检测源码包包含完整的数据集、训练代码和文档说明属于那种「拿到手就能跑通全流程」的毕设型资源——从数据清洗、特征编码到模型训练和评估每一步都有对应代码不是只有孤零零一个模型文件。我推荐它的理由很简单入侵检测本质上是一个分类问题CNN 对局部特征的提取能力刚好能捕捉网络流量中相邻特征之间的关联加上卷积网络在图像领域积累的大量可视化工具和调参经验毕设答辩时无论是讲原理还是展示结果都有现成素材可用。适合正在做毕业设计、需要完整 baseline 的本科生也适合想快速跑通一版入侵检测流程、后面再换数据集的从业者。2. 从原始流量到可训练样本为什么选 CNN 而不是传统机器学习2.1 误用检测与异常检测的边界CNN 的优势在哪网络入侵检测的本质是在网络流量数据上判断当前连接是正常行为还是攻击行为。传统做法有两个流派基于规则的误用检测本质上是一堆 if-else 特征匹配遇到未知攻击直接失效基于统计的异常检测对阈值极其敏感生产环境里误报率高到没法用。深度学习的思路是把特征工程交给网络自己完成这也是 CNN 在这个场景里站得住脚的原因。KDD Cup 1999 这类经典数据集每个样本是一条网络连接记录包含 41 维特征——协议类型、服务类型、连接状态、传输字节数、连接时长、flag 字段等等。这 41 维特征之间不是孤立的比如连接时长和传输字节数往往强相关同一服务类型下的错误率分布也有规律。CNN 的卷积核天然擅长捕捉这种局部关联在特征维度不算高、但交叉特征复杂的数据上效果往往比直接 flatten 后接全连接层好很多。这种方案还有一个隐形优势可视化。卷积核的激活图、池化后的特征分布都能直接画出来毕设答辩时解释「模型学到了什么」比对着树模型的特征重要性讲要直观得多。后面的 t-SNE 可视化也可以直接用在论文里。2.2 数据集结构与标签分布先搞清楚你要处理什么这个资源默认用的是 NSL-KDD 数据集它是 KDD Cup 1999 的改进版解决了原版大量冗余样本导致的训练偏差问题。数据集包含训练集和测试集两个部分文件是 CSV 格式每行一条网络连接记录最后一列是标签。41 维特征里需要特别注意的是前 3 列protocol_type协议类型如 tcp、udp、icmp、service目标主机服务如 http、ftp、telnet、flag连接状态标志如 SF、REJ、S0这三列是符号型特征不能直接喂给神经网络后面预处理要单独处理。剩下的 38 列是数值型连续特征比如连接时长、发送字节数、接收字节数、失败登录次数等。标签分两类视角粗粒度分是二分类normal和attack细粒度分是五分类normal、DoS、Probe、R2L、U2R其中attack被拆成了后四类。毕设一般建议做五分类信息量更大答辩时也更好展开。数据集里的类别分布很不均匀DoS 样本占比极高R2L 和 U2R 样本极少这条后面在避坑章节单独说。2.3 预处理流程总览先建立全局认知拿到原始 CSV 到最终能输入 CNN中间隔着四步每步的输出是下一步的输入步骤输入操作输出1. 符号编码原始 CSV对protocol_type、service、flag做映射编码全是数值的二维表2. 标准化数值化后的数据StandardScaler 或 MinMaxScaler均值方差归一同一量纲的特征矩阵3. 标签映射原始标签列字符串标签转成 one-hot 向量分类标签矩阵4. 二维化一维特征向量补零后 reshape 成矩阵类似图像的二维输入第 4 步是 CNN 方案的关键。41 维特征向量要输入 Conv2D 层必须转成二维矩阵。常见做法是补零到 49 维reshape 成 7×7或者补到 64 维reshape 成 8×8。这个资源里用的是 7×7 方案是因为 49 最接近 41 且是 7 的平方补零最少原始特征信息保留最完整。3. 把原始流量喂给 CNN数据预处理全流程与参数细节3.1 读取 CSV 并处理符号特征这一步的错误会在后面放大打开数据文件先看列名和前几行数据确认分隔符和编码。NSL-KDD 的标准格式是逗号分隔但有些版本的文件头带了额外字段处理前先核对列数——41 列特征加 1 列标签共 42 列。import pandas as pd from sklearn.preprocessing import LabelEncoder # 读取原始数据NSL-KDD 没有表头需要手动指定列名 column_names [ duration, protocol_type, service, flag, src_bytes, dst_bytes, land, wrong_fragment, urgent, # ... 其余特征列按数据集顺序补齐共 41 列 class ] df pd.read_csv(KDDTrain.txt, headerNone, namescolumn_names) # 符号型特征做标签编码 symbolic_cols [protocol_type, service, flag] encoder_map {} for col in symbolic_cols: le LabelEncoder() df[col] le.fit_transform(df[col]) encoder_map[col] le # 保存编码器测试集要用同一个不能重新 fit print(df.shape) # 训练集样本数、特征维度 print(df[class].value_counts()) # 查看类别分布确认数据加载无误这段代码里最关键的不是读取而是encoder_map的保存。测试集做符号编码时必须用训练集拟合好的编码器去 transform如果对测试集单独 fit同一个字符串可能被编码成不同的数字类别语义就错乱了。LabelEncoder 会把tcp编成 0、udp编成 1但顺序是按字母排的不是按语义排的所以跨数据集复用编码器是唯一正确做法。另外注意value_counts()的输出——如果 DoS 类别的样本数比正常类别多出好几倍说明类别不平衡问题确实存在后面训练时要考虑 class_weight。3.2 特征标准化与训练测试切分先切分再标准化标准化这一步的坑在于顺序。正确顺序是先切分训练集和测试集再分别做标准化在训练集上 fit 统计量均值和方差然后同时 transform 训练集和测试集。如果把全部数据一起标准化再切分测试集的统计信息已经混进了训练集的均值和方差里这叫数据泄露会让测试集评估结果虚高。from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler # 分离特征和标签 X df.drop(columns[class]).values y df[class].values # 先切分再标准化 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) # 训练集上 fit测试集上只 transform scaler StandardScaler() X_train scaler.fit_transform(X_train) X_test scaler.transform(X_test) print(f训练集形状: {X_train.shape}, 测试集形状: {X_test.shape})stratifyy这个参数很关键。它保证切分后训练集和测试集的类别比例与原始数据一致避免某类样本在测试集中一个都没分到。标准化的逻辑是StandardScaler 会计算每列特征的均值和标准差然后把数据转成均值为 0、方差为 1 的分布。特征之间的量级差异很大——src_bytes可能到上万count可能只有个位数——不标准化的话数值大的特征会在梯度更新中占据主导地位CNN 的训练会非常不稳定。3.3 标签二值化与二维化把一维流量变成「伪图像」CNN 的 Conv2D 层期望输入是四维张量(batch, height, width, channels)。当前数据是 41 维的一维向量需要补零到 49 维再 reshape 成 7×7最后增加一个 channel 维度。import numpy as np from tensorflow.keras.utils import to_categorical # 标签映射为 one-hot 向量 class_mapping {normal: 0, DoS: 1, Probe: 2, R2L: 3, U2R: 4} y_train_num np.array([class_mapping[label] for label in y_train]) y_test_num np.array([class_mapping[label] for label in y_test]) y_train_cat to_categorical(y_train_num, num_classes5) y_test_cat to_categorical(y_test_num, num_classes5) # 一维特征补零到 49 维reshape 成 7x7 的单通道矩阵 FEATURE_DIM 41 TARGET_DIM 49 IMG_SIZE 7 def pad_and_reshape(features): batch_size features.shape[0] padded np.zeros((batch_size, TARGET_DIM)) padded[:, :FEATURE_DIM] features # 原特征放前面后面补零 return padded.reshape(batch_size, IMG_SIZE, IMG_SIZE, 1) X_train_cnn pad_and_reshape(X_train) X_test_cnn pad_and_reshape(X_test) print(fCNN 输入形状: {X_train_cnn.shape})补零不是玄学是有明确工程理由的。41 不能被开方整除直接 reshape 会报错补零到 49 是最小的平方数补的 8 个零落在矩阵边缘。卷积核在滑动时边缘补零的位置几乎不会产生激活值等于告诉网络这些位置没有实际特征。相比之下如果补到 64 维 reshape 成 8×8补零比例从 16% 涨到了 36%无效信息占比太大训练效果会明显变差。这就是为什么 7×7 是这个数据集上的常用选择。这里注意一点FEATURE_DIM和TARGET_DIM不要写死。如果后面换了数据集特征维度变了这两个常量必须跟着改否则 reshape 直接报错。建议把这两个值放在配置区域标注清楚含义后面换数据时只改一处。4. CNN 模型设计与训练结构参数、优化器选择与评估指标4.1 模型结构两层卷积加池化足够毕设但不多余入侵检测的数据维度是 7×7比图像小得多。模型结构不需要很深两层 Conv2D 加一层全连接足够太深反而容易过拟合。这个资源里的模型结构是输入层 → Conv2D(32 个 3×3 卷积核) → BatchNormalization → MaxPooling2D → Conv2D(64 个 3×3 卷积核) → BatchNormalization → MaxPooling2D → Flatten → Dense(128) → Dropout → Dense(5) → Softmax。from tensorflow.keras.models import Sequential from tensorflow.keras.layers import ( Conv2D, MaxPooling2D, Flatten, Dense, Dropout, BatchNormalization ) model Sequential([ Conv2D(32, (3, 3), activationrelu, paddingsame, input_shape(7, 7, 1)), BatchNormalization(), MaxPooling2D(pool_size(2, 2)), Conv2D(64, (3, 3), activationrelu, paddingsame), BatchNormalization(), MaxPooling2D(pool_size(2, 2)), Flatten(), Dense(128, activationrelu), Dropout(0.5), Dense(5, activationsoftmax) ]) model.compile( optimizeradam, losscategorical_crossentropy, metrics[accuracy] ) model.summary()几个参数值得展开。paddingsame保证卷积后特征图尺寸不变7×7 的输入经过 stride1 的 3×3 卷积核输出还是 7×7信息不因尺寸缩小而丢失。BatchNormalization放在卷积层和激活函数之间作用是把每批数据的激活值拉回到标准正态附近防止梯度消失同时允许用更大的学习率。Dropout(0.5)在全连接层前随机丢弃一半神经元是防过拟合最直接的手段——数据量不大时这个比例很关键。第一层卷积 32 个卷积核第二层翻倍到 64这是 CNN 的经典设计模式浅层提取简单特征深层组合复杂特征通道数逐层翻倍。为什么从 32 开始而不是 16因为 7×7 的输入很小输出特征图也小32 个卷积核能覆盖更多样的局部模式又不会让参数量爆炸。第一层参数量是 32×(3×3×11)320加上后面几层总参数量在 5 万左右CPU 上跑也很快。4.2 训练配置学习率、批次大小与早停机制训练配置直接影响收敛速度和最终效果。学习率用自适应优化器 Adam 的默认值 0.001配合批次大小 64 和 30 个 epoch是经过多次实验后最稳的组合。from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau # 类别不平衡时使用 class_weight让模型更关注少数类 class_weights { 0: 1.0, # normal 1: 1.0, # DoS 2: 1.0, # Probe 3: 5.0, # R2L样本少加大权重 4: 5.0, # U2R样本少加大权重 } callbacks [ EarlyStopping(monitorval_loss, patience5, restore_best_weightsTrue), ReduceLROnPlateau(monitorval_loss, factor0.5, patience3, min_lr1e-6) ] history model.fit( X_train_cnn, y_train_cat, validation_data(X_test_cnn, y_test_cat), epochs30, batch_size64, class_weightclass_weights, callbackscallbacks, verbose1 )class_weight这个参数是针对类别不平衡的关键设置。数据集中 DoS 攻击样本占比高R2L 和 U2R 样本很少如果不加权重模型会倾向于把少数类全预测成多数类accuracy 看着很高但少数类的召回率趋近于零。给 R2L 和 U2R 各加 5 倍权重相当于每个少数类样本在计算损失时被当成 5 个样本梯度更新更偏向它。EarlyStopping的patience5表示验证集损失连续 5 轮不降就停止训练restore_best_weightsTrue会把模型权重回滚到验证集表现最好的那一轮避免保存过拟合状态的权重。ReduceLROnPlateau在验证集 loss 停滞时把学习率减半让优化器以更小步长继续搜索比固定学习率多跑几轮更有效。4.3 评估指标准确率不是唯一答案重点看各类别召回率训练结束后用测试集评估模型。打印准确率只是第一步必须看混淆矩阵和各类别的精确率、召回率、F1才能判断模型是真正学到了各攻击类别的特征还是单纯靠类别分布蒙对了结果。from sklearn.metrics import classification_report, confusion_matrix import numpy as np y_pred model.predict(X_test_cnn) y_pred_class np.argmax(y_pred, axis1) y_true_class np.argmax(y_test_cat, axis1) # 打印每个类别的精确率、召回率、F1 target_names [normal, DoS, Probe, R2L, U2R] print(classification_report(y_true_class, y_pred_class, target_namestarget_names)) # 混淆矩阵后续可以画热力图 cm confusion_matrix(y_true_class, y_pred_class) print(cm)classification_report的输出要重点看 R2L 和 U2R 这两行。如果精确率高但召回率极低说明模型把大多数少数类误判成了其他类改进方向是继续加大 class_weight 或做上采样。如果 U2R 的 F1 仍低于 0.5是正常的这个类别的样本量太少CNN 很难学到足够判别性的特征答辩时可以提一句这是数据集的固有限制反而显得你对问题理解深入。毕设评估一般以 macro-F1 为核心指标因为它对少数类更敏感比 accuracy 更能反映模型的真实能力。5. 避坑清单网络入侵检测毕业设计最常见的五个翻车点5.1 数据泄露先标准化后切分测试集评估虚高现象训练时准确率正常测试集准确率却异常高甚至接近 100%但换一批数据就崩。原因对全部数据统一做了标准化测试集的均值和方差混入了训练集。模型在训练阶段偷看了测试集的分布信息评估结果自然虚高。这在毕业设计答辩时被老师问到「测试集怎么评估的」会非常被动。解决严格遵循先切分、再 fit、再 transform 的顺序。训练集上 fit StandardScaler然后用同一个 scaler 去 transform 测试集。代码写成scaler.fit(X_train)和scaler.transform(X_test)永不调用scaler.fit(X_test)。5.2 符号编码不一致训练集和测试集用了不同的编码器现象模型在训练集上表现好测试集上一塌糊涂。检查时发现同一个protocol_type值在两份数据里编码成了不同数字。原因测试集的符号特征单独做了 LabelEncoder 的 fit。比如tcp在训练集编码成 0测试集按字母序可能编码成 2特征语义完全错位。解决训练集 fit 好的编码器用一个字典存起来测试集直接复用只做 transform 不做 fit。代码里encoder_map[col]保存每个符号列的编码器处理测试集时从encoder_map取出来用。5.3 类别不平衡导致模型全是「懒惰预测」现象训练完成后 accuracy 高达 95% 以上但看 R2L 的召回率只有 0.1U2R 的 F1 是 0。模型几乎把所有样本都预测成了多数类。原因数据集中 DoS 样本占比过半loss 函数按样本平均多数类贡献了绝大部分梯度模型发现全部预测成 DoS 就能获得低 loss。解决训练时传class_weight给少数类更高权重或者用RandomOverSampler对 R2L 和 U2R 样本做上采样。评估时不要只看 accuracy打印classification_report把 macro-F1 当核心指标。5.4 日志乱码或训练中断Windows 下编码问题现象训练时输出日志全是UnicodeDecodeError或者在读取 CSV 时报gbk codec cant decode。原因NSL-KDD 数据集文件是 UTF-8 编码Windows 下 pandas 默认用 GBK 读取。解决读取文件时显式指定编码pd.read_csv(KDDTrain.txt, encodingutf-8)。如果还报错先看文件的实际编码用chardet检测后统一转码。5.5 特征维度写死导致换数据即崩现象把代码换成其他入侵检测数据集比如 CICIDS2017运行到reshape时报维度错误。原因预处理代码里FEATURE_DIM 41、TARGET_DIM 49、IMG_SIZE 7都是硬编码。CICIDS2017 特征维度不同reshape 直接失败。解决把这三个值提取到脚本顶部的配置区换数据集时先打印特征的 shape再改配置。同时写一个断言assert X.shape[1] TARGET_DIM如果特征数超过目标维度直接报错提示而不是静默截断。6. 进阶用混淆矩阵和 t-SNE 验证模型真的学到了特征分布训练完模型只是第一步答辩时展示「模型为什么有效」比展示「模型准确率有多高」更有说服力。我一般会随手做两张图混淆矩阵热力图和特征向量的 t-SNE 可视化。import matplotlib.pyplot as plt import seaborn as sns from sklearn.manifold import TSNE # 图一混淆矩阵热力图 plt.figure(figsize(8, 6)) sns.heatmap(cm, annotTrue, fmtd, cmapBlues, xticklabelstarget_names, yticklabelstarget_names) plt.xlabel(Predicted) plt.ylabel(True) plt.title(Confusion Matrix on Test Set) plt.show() # 图二t-SNE 特征分布 # 取 CNN 倒数第二层Dense 128的输出作为特征向量 feature_extractor tf.keras.Model( inputsmodel.input, outputsmodel.layers[-3].output # Flatten 后的 Dense 层输出 ) features feature_extractor.predict(X_test_cnn) tsne TSNE(n_components2, random_state42, perplexity30) features_2d tsne.fit_transform(features) plt.figure(figsize(8, 8)) colors [#1f77b4, #ff7f0e, #2ca02c, #d62728, #9467bd] for i, label in enumerate(target_names): mask (y_true_class i) plt.scatter(features_2d[mask, 0], features_2d[mask, 1], ccolors[i], labellabel, s10, alpha0.7) plt.legend() plt.title(t-SNE Visualization of CNN Features) plt.show()这两张图的读法有讲究。混淆矩阵要看对角线是否明显优于其他位置——如果 DoS 那一列都亮说明模型把所有攻击都判成了 DoS是懒惰预测的典型特征。t-SNE 图要看同一类别的点是否聚成团、不同类别之间是否有明显间隔——如果五个类彻底混在一起说明模型提取的特征没有区分度调参方向错了。我一直习惯把这两步当成模型训练的强制收尾动作。之前做过一个实验训练集 accuracy 99%t-SNE 图一画出来发现 U2R 的点和 R2L 完全重叠模型根本没学会区分它们只是靠 class_weight 的梯度强行拉升了 loss。从那以后我每次做完入侵检测实验都强制自己跑一遍混淆矩阵和 t-SNE 可视化确认模型真的学到了分布再谈结论。这套流程里同样保留了这两个脚本你换了其他数据集也可以直接把这两段代码拿过去用。希望帮到你。本文还有配套的精品资源点击获取