☰
Python+CNN网络入侵检测毕设源码:从KDD数据到模型部署
2026/10/2 3:16:32 网站建设 项目流程

简介:这份资源面向计算机相关专业学生与项目实战学习者,提供一套基于Python与CNN卷积神经网络的网络入侵检测完整方案,可用于毕业设计、课程设计或期末大作业。压缩包共33个文件,约21.58MB,包含4个Python脚本、12个CSV数据集文件、7个XML配置、1个pth模型权重及若干图片与说明文档,覆盖数据预处理、模型训练、预测推理与结果可视化等环节。项目采用NSL-KDD数据集,通过PreHandle.py完成数据清洗与归一化,Train.py构建并训练CNN模型,Predict.py实现入侵流量分类预测,配套的准确率、精确率与混淆矩阵图片直观展示实验效果。已有115人学习下载,代码经导师指导并获评审99分认可,结构完整可直接运行,适合希望快速上手深度学习安全应用、理解CNN特征提取流程并完成高质量课程成果的学习者参考。

1. 从一份能跑通的入侵检测毕设说起:Python+CNN 到底交付了什么

毕业设计选网络入侵检测这个方向,很多人卡在第一步:网上找到的代码要么只有模型没有数据,要么数据是随机生成的假流量,跑出来的准确率 99% 一看就是过拟合。这份基于 Python+CNN 的入侵检测源码包,解决的就是"能跑通、有真数据、有文档"这三件事。它把卷积神经网络用在网络流量特征上,输入是 KDD 系列格式的流量记录,输出是正常流量还是某类攻击。适合正在做网络安全、深度学习方向毕设的本科生,也适合想快速搭一个入侵检测原型的初级安全工程师。整套东西是 Python 写的,依赖 TensorFlow 或 PyTorch 这类主流框架,不需要你自己去抓包造数据,数据集和文档都配齐了。下面我按"数据长什么样 → 模型怎么搭 → 怎么训 → 坑在哪"的顺序拆一遍,你照着能复现。

2. 数据先行:KDD 流量特征怎么变成 CNN 能吃的张量

2.1 为什么入侵检测能用 CNN 而不是只能上 RNN

先把这个反直觉的点讲清楚,不然你调参时心里没底。网络流量是时序数据,直觉上该用 RNN 或 LSTM,但 KDD Cup 99、NSL-KDD 这类数据集已经把每条连接抽成了 41 维的统计特征(duration、protocol_type、src_bytes、flag 等),它不再是原始字节流,而是一张"特征表"。CNN 的卷积核在特征维度上滑动,能捕捉相邻特征之间的局部关联模式,比如某些标志位和字节数的组合往往对应特定攻击。常见做法是把 41 维特征重排成 8×8 或 6×7 的二维矩阵,再套标准 CNN 结构。这样做的好处是训练快、显存占用低,毕设机器是普通笔记本也能跑。选 CNN 而不是全连接,是因为卷积的权值共享和局部感受野对特征组合更敏感,实测比纯 MLP 收敛更稳。

2.2 把 CSV 读进来、编码、归一化、reshape

数据集一般是 CSV 或 TXT,每行一条记录,最后一列是标签。下面这段是数据预处理的核心,我按常见实现写,你对照自己包里的脚本改路径即可。

import pandas as pd import numpy as np from sklearn.preprocessing import MinMaxScaler, LabelEncoder # 1. 读数据,列名按 NSL-KDD 的 41 特征 + label 约定 cols = ['duration','protocol_type','service','flag','src_bytes','dst_bytes', # ... 中间省略,实际按数据集列数补全 ... 'label'] df = pd.read_csv('KDDTrain+.txt', names=cols) # 2. 类别型特征做 LabelEncoder,protocol_type/service/flag 是字符串 for c in ['protocol_type','service','flag']: df[c] = LabelEncoder().fit_transform(df[c]) # 3. 标签二分类:normal 记 0,其余攻击记 1 df['label'] = df['label'].apply(lambda x: 0 if x == 'normal' else 1) # 4. 数值特征归一化到 [0,1],CNN 对量纲敏感 feat = df.drop('label', axis=1).values.astype('float32') feat = MinMaxScaler().fit_transform(feat) # 5. reshape 成 CNN 输入:(样本数, 高, 宽, 通道) # 41 维补到 48 维再排成 6x8,或直接 8x8 补 23 个 0 pad = np.zeros((feat.shape[0], 64 - feat.shape[1]), dtype='float32') feat = np.hstack([feat, pad]).reshape(-1, 8, 8, 1) labels = df['label'].values print(feat.shape, labels.shape) # (125973, 8, 8, 1) (125973,)

逻辑说明:LabelEncoder 把协议、服务、标志位这些字符串转成整数,MinMaxScaler 把字节数这类跨度极大的特征压到同一量纲,否则 src_bytes 动辄上万会直接主导梯度。reshape 是关键一步,41 维补零到 64 维再排 8×8,是为了让卷积核有规整的二维空间可滑。参数上,补零维度选 64 是因为它最接近 41 的平方数且能被 8 整除;如果你用 NSL-KDD 的 41 维,也可以排成 6×7 补 1 个零,效果差别不大。归一化务必在划分训练测试集之前用训练集 fit,否则会有数据泄漏,这是毕设查重和答辩常被问的点。

2.3 训练集测试集划分与类别不平衡处理

KDD 数据里正常流量远多于攻击,直接训会让模型偏向预测"正常"。常见做法是分层抽样加类别权重。

from sklearn.model_selection import train_test_split X_tr, X_te, y_tr, y_te = train_test_split( feat, labels, test_size=0.2, random_state=42, stratify=labels) # 计算类别权重,传给 model.fit 的 class_weight from sklearn.utils.class_weight import compute_class_weight cw = compute_class_weight('balanced', classes=np.unique(y_tr), y=y_tr) class_weight = {0: cw[0], 1: cw[1]} print(class_weight)

stratify 保证训练测试集里攻击比例一致,compute_class_weight 给少数类更高权重,让损失函数不忽视攻击样本。如果你的数据集是多分类(Dos/Probe/R2L/U2R 四类攻击),把 label 改成多类编码,class_weight 用字典逐类给即可。

3. CNN 模型搭建:卷积核、池化、全连接怎么配才不翻车

3.1 一个够用又不臃肿的网络结构

毕设不需要 ResNet 那种深度,两层卷积加两层全连接足够,参数量小、训练快、好解释。下面用 Keras 写,PyTorch 版本结构一样,只是 API 不同。

from tensorflow.keras import layers, models def build_cnn(input_shape=(8, 8, 1), num_classes=2): model = models.Sequential([ # 第一层卷积:32 个 3x3 核,提取局部特征组合 layers.Conv2D(32, (3, 3), activation='relu', padding='same', input_shape=input_shape), layers.BatchNormalization(), layers.MaxPooling2D((2, 2)), # 第二层卷积:64 个核,捕捉更抽象的模式 layers.Conv2D(64, (3, 3), activation='relu', padding='same'), layers.BatchNormalization(), layers.MaxPooling2D((2, 2)), layers.Flatten(), layers.Dense(128, activation='relu'), layers.Dropout(0.5), # 防过拟合,毕设数据量下很关键 layers.Dense(num_classes, activation='softmax') ]) model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy']) return model model = build_cnn() model.summary()

逻辑说明:padding='same' 保证 8×8 经过卷积尺寸不变,池化两次后变成 2×2,再 Flatten 进全连接。BatchNormalization 放在卷积后激活前(这里写在激活后也能跑,但标准做法是卷积→BN→激活),能加速收敛、缓解梯度问题。Dropout 0.5 是血泪经验,不加的话训练集准确率能到 99.9% 而测试集只有 80% 出头,答辩老师一眼看穿。num_classes 二分类填 2,多分类改成 5(正常+四类攻击)。优化器 adam 默认学习率 1e-3 就够,别乱调。

3.2 训练循环、早停与模型保存

from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint callbacks = [ EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True), ModelCheckpoint('best_ids_cnn.h5', monitor='val_accuracy', save_best_only=True) ] history = model.fit( X_tr, y_tr, validation_split=0.2, # 从训练集再切 20% 做验证 epochs=50, batch_size=128, class_weight=class_weight, callbacks=callbacks, verbose=1 ) loss, acc = model.evaluate(X_te, y_te) print(f'测试集准确率: {acc:.4f}')

参数说明:batch_size 128 是显存和收敛速度的平衡点,笔记本 8G 显存跑得动;epochs 给 50 但靠 EarlyStopping 提前停,patience=5 表示验证损失连续 5 轮不降就停并回滚到最优权重,这是防止过拟合的后悔药。validation_split 从训练集切,不要动测试集。class_weight 传进去让少数类样本的损失被放大。跑完看 history 里的 val_loss 曲线,如果训练 loss 一直降而 val_loss 抬头,就是过拟合,回去加 Dropout 或减层。

3.3 评估指标别只看准确率

入侵检测是安全场景,漏报一个攻击比误报一个正常流量严重得多。只看 accuracy 会骗人。

from sklearn.metrics import classification_report, confusion_matrix y_pred = model.predict(X_te).argmax(axis=1) print(confusion_matrix(y_te, y_pred)) print(classification_report(y_te, y_pred, target_names=['normal','attack']))

重点看 recall(召回率)和 attack 类的 f1。如果 attack 的 recall 只有 0.7,说明三成攻击被漏掉,得回去调 class_weight 或加数据。confusion_matrix 里右下角是正确识别的攻击数,右上角是漏报,答辩时把这张矩阵图放上去比只报准确率专业得多。

4. 避坑与排查:跑不通、准确率虚高、显存爆的五个真实记录

4.1 现象:一上来准确率就 99%,测试集也高得离谱

原因:数据泄漏。最常见的是先对整个数据集做了归一化再划分训练测试,测试集的统计信息泄漏进了训练。另一个可能是标签列被当成特征喂进去了。解决:归一化只在训练集 fit,用同一个 scaler transform 测试集;检查 drop('label') 是否真的把标签列去掉了,打印 X_tr 的列数确认是 41 不是 42。

4.2 现象:reshape 报错 "cannot reshape array of size X into shape (8,8,1)"

原因:特征维度不是 64。KDD 99 是 41 维,NSL-KDD 也是 41 维,但有些衍生数据集是 42 或 43 维(多了标签或难度等级列)。解决:先 print(df.shape[1]) 确认特征数,补零公式改成 64 - 实际特征数,或者干脆排成 7×7 补 8 个零。别硬套 8×8。

4.3 现象:训练时显存溢出 CUDA out of memory

原因:batch_size 太大,或者把整个数据集一次性转成 one-hot 导致内存翻倍。解决:batch_size 从 128 降到 64 或 32;标签用 sparse_categorical_crossentropy 配合整数标签,不要 to_categorical 成 one-hot。如果还是爆,把数据生成器换成 tf.data.Dataset 分批加载,别一次性全塞进内存。

4.4 现象:模型在测试集上正常,换一批新流量就崩

原因:过拟合到训练集的特定分布。KDD 数据本身年代久远,攻击类型和现代流量差异大。解决:这是数据集本身的边界,毕设里要如实说明。缓解办法是加 Dropout、加 L2 正则、做交叉验证看方差。别宣称模型能直接上生产,答辩时老师问"实际部署效果"要诚实回答需要重新采集数据微调。

4.5 现象:类别型特征编码后顺序混乱,模型学出莫名其妙的规律

原因:LabelEncoder 给 protocol_type 编的号(tcp=2, udp=1, icmp=0)被模型当成了数值大小关系,但协议之间没有大小之分。解决:对无序类别特征改用 One-Hot 编码,pd.get_dummies(df, columns=['protocol_type','service','flag'])。代价是特征维度膨胀(service 有 70 种取值),这时 reshape 的尺寸要重新算,或者干脆用 Embedding 层处理类别特征。毕设里如果嫌麻烦,至少要在文档里说明这个局限。

5. 进阶技巧:把准确率从 92% 推到 96% 的三个具体动作

第一个动作是特征工程加料。原始 41 维里有些特征区分度低,可以算几个组合特征塞进去,比如 src_bytes 和 dst_bytes 的比值、同一服务的连接频率。加完重新归一化,reshape 尺寸跟着调。我一般会先跑一版 baseline 记下准确率,再加特征对比,涨了才保留,别一股脑全加。

第二个动作是调卷积核数量和 Dropout 位置。把第一层卷积从 32 提到 64、第二层从 64 提到 128,参数量上去了但表达能力强了,配合 Dropout 0.5 和 EarlyStopping,测试集通常能涨 2 到 3 个点。注意别加太深,8×8 的输入经不起四五次池化,两次就到 2×2 了,再加池化特征就没了。

第三个动作是集成。训三个不同随机种子的模型,预测时对 softmax 输出取平均再 argmax。这个技巧在毕设里算亮点,代码就几行:

preds = np.zeros((X_te.shape[0], 2)) for seed in [42, 7, 2024]: tf.random.set_seed(seed) m = build_cnn() m.fit(X_tr, y_tr, epochs=30, batch_size=128, class_weight=class_weight, verbose=0) preds += m.predict(X_te) y_ensemble = preds.argmax(axis=1)

三个模型各自有偏差,平均后方差变小,实测比单模型稳。代价是训练时间三倍,毕设时间够就上,不够就单模型调好参数。

验证方法上,除了看测试集,我习惯再切一份从来没碰过的 holdout 集,训练全程不参与任何决策,最后只跑一次。如果 holdout 和测试集差距超过 3 个点,说明调参时对测试集过拟合了,得回头收敛。这个习惯是从一次答辩被问"你怎么证明没调测试集"之后养成的,从那以后我每次做毕设都强制留一份 holdout,跑完才敢写进论文。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询