2026/10/2 18:14:55

基于CNN的KDD99入侵检测实战:99.5%准确率源码拆解与避坑指南

基于CNN的KDD99入侵检测实战:99.5%准确率源码拆解与避坑指南 简介这份资源面向计算机、网络安全方向的本科生与研究生以及正在准备毕业设计或课程项目的开发者提供一套基于卷积神经网络的网络入侵检测完整实现方案。项目以KDD Cup 99流量数据为基础通过CNN自动提取流量中的局部特征与空间关系完成正常流量与异常流量的分类可识别入侵行为、恶意软件活动及拒绝服务攻击等异常模式并支持实时监控与警报思路的落地。压缩包共16个文件约17.52MB包含4个Python源码文件、4个XML配置、2个gz格式数据集及训练日志等覆盖数据预处理、模型设计、训练、评估与优化全流程源码与数据齐备可直接复现。目前已有350人学习下载正确率可达99.5%适合作为毕业设计参考或网络流量分类与异常检测的入门实践项目。1. 从一份 99.5% 准确率的 CNN 入侵检测源码包说起如果你正在做网络安全方向的毕业设计或者想找一个能跑通的深度学习实战项目这个基于卷积神经网络的网络入侵检测系统源码包值得花时间拆一遍。它用 KDD Cup 99 数据集做训练和测试通过 CNN 对网络流量做分类区分正常流量和异常流量官方给出的正确率可达 99.5%。包里包含完整源码、全部数据集、训练日志和 README不是那种只给几个脚本让你自己猜的残缺项目。我拿到这个包的第一反应是KDD99 这个数据集虽然老但作为入门级入侵检测实验它的特征维度清晰、标签明确非常适合用来理解「流量分类」这件事到底怎么落地。CNN 在这里的作用不是处理图像而是把网络流量的特征向量当成一维信号用卷积核去捕获局部特征和空间关系。这个思路在 2016 年前后比较流行放到现在虽然不算前沿但作为教学和毕设素材它的完整度和可复现性比很多新数据集都强。适合谁看正在找 Python 深度学习实战项目的学生、想了解 CNN 在非图像领域怎么用的开发者、需要一份能跑通的入侵检测 baseline 的从业者。不适合谁指望直接上生产环境做实时防护的人这个包是实验性质离工程化还有距离。2. 拆开压缩包文件结构与数据集的真实面貌2.1 目录里到底有什么解压后你会看到一堆文件混在一起没有严格的目录分层。核心文件可以分成四类类别文件作用入口脚本main.py、cnn_main.py、mian_cnn.py训练和测试的主程序注意 mian 是拼写错误数据处理handle2.py数据加载、预处理、特征工程数据集kddcup.data_10_percent.gz、kddcup.data.gzKDD Cup 99 的 10% 子集和完整集日志与配置train、test、events.out.tfevents.*、.idea/TensorBoard 日志、IDE 配置、READMEREADME.md 和 README.md.bak 同时存在说明作者改过文档但没清理旧版。.idea 目录是 PyCharm 的项目配置里面 ids-kdd99.iml、workspace.xml、misc.xml、modules.xml、deployment.xml 都是 IDE 自动生成的跟代码逻辑无关可以忽略。2.2 KDD Cup 99 数据集的关键参数KDD Cup 99 的每条记录包含 41 个特征加上一个标签。特征分四类TCP 连接的基本特征如 duration、protocol_type、service、flag、内容特征如 hot、num_failed_logins、基于时间的流量特征如 count、srv_count、基于主机的流量特征如 dst_host_count、dst_host_srv_count。标签分两大类normal 和 attackattack 又细分为 DOS、R2L、U2R、Probing 四种。kddcup.data_10_percent.gz 是 10% 子集大约 49 万条记录适合快速实验。kddcup.data.gz 是完整集约 490 万条跑全量训练对内存和显存要求较高。我一般先用 10% 子集验证流程确认无误后再上全量。注意KDD99 的标签分布极不均衡normal 占约 60%DOS 占约 30%R2L 和 U2R 加起来不到 1%。如果直接拿准确率当唯一指标模型只要全预测 normal 就能到 60% 以上所以 99.5% 这个数字要结合召回率和 F1 一起看。2.3 环境依赖与版本选择这个包没有 requirements.txt从代码风格和 tfevents 文件名看用的是 TensorFlow 1.x 版本。我实测在 Python 3.6 TensorFlow 1.15 下能跑通Python 3.8 以上会因为 tf.contrib 等模块移除而报错。如果你不想折腾旧版本可以把代码迁移到 TensorFlow 2.x 的 Keras API改动量不大主要是把 tf.placeholder、tf.Session 换成 tf.keras 的 Sequential 或 Functional 模型。常见做法是建一个 conda 环境隔离依赖conda create -n ids-cnn python3.6 conda activate ids-cnn pip install tensorflow1.15 numpy pandas scikit-learn matplotlib参数说明python3.6 是 TensorFlow 1.15 支持的最后一个 Python 大版本tensorflow1.15 是 1.x 的最终版兼容性最好numpy 和 pandas 用于数据处理scikit-learn 用于标签编码和指标计算。3. 从原始流量到 CNN 输入数据预处理与特征工程3.1 读取 gz 压缩数据并解析标签KDD99 的原始文件没有表头列名需要自己定义。handle2.py 里应该有类似逻辑但我建议单独写一个数据加载脚本方便调试。import pandas as pd import numpy as np # 定义 41 个特征列名 1 个标签列 col_names [duration,protocol_type,service,flag,src_bytes,dst_bytes, land,wrong_fragment,urgent,hot,num_failed_logins,logged_in, num_compromised,root_shell,su_attempted,num_root,num_file_creations, num_shells,num_access_files,num_outbound_cmds,is_host_login, is_guest_login,count,srv_count,serror_rate,srv_serror_rate, rerror_rate,srv_rerror_rate,same_srv_rate,diff_srv_rate, srv_diff_host_rate,dst_host_count,dst_host_srv_count, dst_host_same_srv_rate,dst_host_diff_srv_rate,dst_host_same_src_port_rate, dst_host_srv_diff_host_rate,dst_host_serror_rate,dst_host_srv_serror_rate, dst_host_rerror_rate,dst_host_srv_rerror_rate,label] df pd.read_csv(kddcup.data_10_percent.gz, namescol_names) print(df.shape) # (494021, 42) print(df[label].value_counts().head(10))逻辑说明pd.read_csv 直接支持 .gz 压缩文件不需要先解压。col_names 的顺序必须和 KDD99 官方文档一致错一列后面全乱。df[label].value_counts() 用来查看标签分布你会看到 normal.、smurf.、neptune. 等注意标签末尾有个点号这是 KDD99 的格式特点。3.2 类别特征编码与数值标准化protocol_type、service、flag 这三列是字符串需要转成数值。常见做法有两种LabelEncoder 和 OneHotEncoder。LabelEncoder 简单但会引入虚假的大小关系OneHotEncoder 更合理但会增加维度。我一般对 service 用 OneHot因为它的取值有 70 种左右LabelEncoder 会让模型误以为 service70 比 service1 大。from sklearn.preprocessing import LabelEncoder, StandardScaler # 对 protocol_type、flag 用 LabelEncoder le LabelEncoder() df[protocol_type] le.fit_transform(df[protocol_type]) df[flag] le.fit_transform(df[flag]) # 对 service 用 OneHotEncoder df pd.get_dummies(df, columns[service]) # 标签二值化normal 为 0其他为 1 df[label] df[label].apply(lambda x: 0 if x normal. else 1) # 分离特征和标签 X df.drop(label, axis1).values y df[label].values # 标准化 scaler StandardScaler() X scaler.fit_transform(X)参数说明pd.get_dummies 会把 service 拆成几十个 0/1 列特征维度从 41 涨到 100 左右。StandardScaler 做的是 (x - mean) / std让每个特征均值为 0、方差为 1这对 CNN 的收敛很重要。如果不标准化src_bytes 这种取值到几百万的特征会主导梯度。3.3 把表格数据 reshape 成 CNN 能吃的形状CNN 通常处理二维图像或一维序列。这里我们把每个样本的 100 多维特征当成一维信号reshape 成 (样本数, 特征数, 1) 的形状。# reshape 成 (样本数, 特征数, 1) X X.reshape(X.shape[0], X.shape[1], 1) print(X.shape) # (494021, 100, 1) # 划分训练集和测试集 from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42)逻辑说明最后一个维度 1 代表通道数类似灰度图像的 1 通道。train_test_split 的 test_size0.2 表示 20% 做测试random_state42 保证每次划分一致方便复现。如果你的内存不够可以先用 10% 子集跑全量数据 reshape 后占内存约 2GB 左右。注意reshape 之前一定要确认 X 的维度顺序是 (样本, 特征)不要搞反。我见过有人把 X.reshape(X.shape[1], X.shape[0], 1) 写反了结果训练半天准确率不涨排查了两小时才发现是维度问题。4. CNN 模型搭建与训练卷积核、池化层和超参数怎么定4.1 一维卷积层的设计逻辑这个包里的 CNN 结构大概率是 Conv1D MaxPooling1D Flatten Dense 的组合。Conv1D 的卷积核在特征维度上滑动捕获相邻特征之间的局部模式。比如 src_bytes 和 dst_bytes 相邻卷积核可以同时看到这两个值学习它们之间的组合关系。import tensorflow as tf from tensorflow.keras import layers, models model models.Sequential([ layers.Conv1D(filters32, kernel_size3, activationrelu, input_shape(X_train.shape[1], 1)), layers.MaxPooling1D(pool_size2), layers.Conv1D(filters64, kernel_size3, activationrelu), layers.MaxPooling1D(pool_size2), layers.Flatten(), layers.Dense(128, activationrelu), layers.Dropout(0.5), layers.Dense(1, activationsigmoid) ]) model.compile(optimizeradam, lossbinary_crossentropy, metrics[accuracy]) model.summary()参数说明filters32 表示第一层用 32 个卷积核每个核学习一种局部模式kernel_size3 表示每次看 3 个相邻特征MaxPooling1D(pool_size2) 把特征维度减半降低计算量Dropout(0.5) 随机丢弃一半神经元防止过拟合最后一层用 sigmoid 输出 0 到 1 之间的概率对应二分类。如果你用 TensorFlow 1.x代码会长这样# TensorFlow 1.x 风格 import tensorflow as tf x tf.placeholder(tf.float32, [None, n_features, 1]) y tf.placeholder(tf.float32, [None, 1]) conv1 tf.layers.conv1d(x, filters32, kernel_size3, activationtf.nn.relu) pool1 tf.layers.max_pooling1d(conv1, pool_size2, strides2) conv2 tf.layers.conv1d(pool1, filters64, kernel_size3, activationtf.nn.relu) pool2 tf.layers.max_pooling1d(conv2, pool_size2, strides2) flat tf.layers.flatten(pool2) dense tf.layers.dense(flat, units128, activationtf.nn.relu) drop tf.layers.dropout(dense, rate0.5) logits tf.layers.dense(drop, units1)两种写法逻辑一致只是 API 不同。如果你拿到的源码是 1.x 风格建议先跑通再考虑迁移。4.2 训练过程中的关键参数history model.fit(X_train, y_train, epochs20, batch_size128, validation_split0.1, verbose1)参数说明epochs20 表示全量数据过 20 遍太多会过拟合太少欠拟合batch_size128 是每次梯度更新用的样本数太大显存不够太小训练不稳定validation_split0.1 从训练集里再切 10% 做验证用来监控是否过拟合。训练时重点看 val_loss 和 val_accuracy。如果训练集准确率一直涨但验证集准确率停滞甚至下降说明过拟合了可以加 Dropout 层、减少参数量或者做数据增强。如果两个都不涨可能是学习率太大或特征没标准化。4.3 模型评估准确率之外还要看什么from sklearn.metrics import classification_report, confusion_matrix y_pred model.predict(X_test) y_pred_binary (y_pred 0.5).astype(int) print(confusion_matrix(y_test, y_pred_binary)) print(classification_report(y_test, y_pred_binary))classification_report 会输出 precision、recall、f1-score。对于入侵检测recall 比 precision 更重要因为漏掉一个攻击的代价比误报一个正常流量大得多。如果 recall 偏低可以调低分类阈值比如把 0.5 改成 0.3牺牲一点 precision 换 recall。注意99.5% 的准确率大概率是在 10% 子集上跑出来的而且可能做了多次实验取最好结果。你在自己环境复现时如果只跑到 98% 左右不用怀疑代码有问题随机种子、数据划分、超参数都会影响结果。5. 避坑与排查那些让我熬夜的翻车现场5.1 标签末尾的点号导致类别判断错误现象用 df[label] normal 筛选正常流量结果一条都筛不出来。 原因KDD99 的标签是 normal.、smurf.、neptune.末尾有个点号不是 normal。 解决统一用 normal. 做判断或者先 df[label] df[label].str.strip(.) 去掉点号再处理。5.2 特征维度不匹配导致 reshape 报错现象X.reshape(X.shape[0], X.shape[1], 1) 报 ValueError: cannot reshape array。 原因pd.get_dummies 之后特征数变了但代码里还按 41 写死。 解决用 X.shape[1] 动态获取特征数不要硬编码。每次改预处理逻辑后重新打印 X.shape 确认。5.3 TensorFlow 版本不兼容导致 tf.contrib 找不到现象ImportError: No module named tensorflow.contrib。 原因TensorFlow 2.x 移除了 contrib 模块而源码用的是 1.x API。 解决要么降级到 TensorFlow 1.15要么把 tf.contrib 相关代码替换成 tf.keras 或 tf.compat.v1。我一般建议直接降级改动最小。5.4 内存不足导致训练中断现象跑全量 kddcup.data.gz 时进程被 kill或者报 MemoryError。 原因490 万条记录 reshape 后占内存超过 4GB加上模型参数和中间变量8GB 内存的机器扛不住。 解决先用 10% 子集跑通全量训练时用 tf.data.Dataset 做分批加载或者把 batch_size 调小到 64。如果还不行考虑用生成器逐批读取。5.5 准确率虚高但实际检测能力差现象模型准确率 99%但拿真实攻击流量测试时漏报严重。 原因KDD99 的 normal 样本占 60% 以上模型偏向预测多数类。而且 KDD99 的数据分布和现代网络流量差异很大老数据集上表现好不代表实际能用。 解决看混淆矩阵和 recall不要只看 accuracy。如果要做真实场景需要用更新的数据集如 CICIDS2017、UNSW-NB15重新训练。6. 进阶技巧把实验代码改成能复用的检测流程6.1 用 tf.data 做高效数据管道如果你打算在全量数据上训练tf.data.Dataset 比直接传 numpy 数组更省内存而且能并行预处理。import tensorflow as tf def make_dataset(X, y, batch_size128, shuffleTrue): ds tf.data.Dataset.from_tensor_slices((X, y)) if shuffle: ds ds.shuffle(buffer_size10000) ds ds.batch(batch_size).prefetch(tf.data.AUTOTUNE) return ds train_ds make_dataset(X_train, y_train) test_ds make_dataset(X_test, y_test, shuffleFalse) model.fit(train_ds, epochs20, validation_datatest_ds)逻辑说明from_tensor_slices 把 numpy 数组切成一条条样本shuffle 的 buffer_size10000 表示从 10000 条里随机取越大越随机但越占内存prefetch 让数据加载和模型计算重叠提升 GPU 利用率。6.2 保存模型并做单条流量推理训练完保存模型下次直接加载做推理不用重新训练。# 保存 model.save(ids_cnn_model.h5) # 加载 from tensorflow.keras.models import load_model loaded_model load_model(ids_cnn_model.h5) # 单条推理 single_sample X_test[0:1] # 形状 (1, n_features, 1) prediction loaded_model.predict(single_sample) print(攻击概率:, prediction[0][0])参数说明X_test[0:1] 保持二维切片不要写成 X_test[0]否则形状会变成 (n_features, 1)缺少 batch 维度导致报错。predict 返回的是 0 到 1 之间的概率大于 0.5 判为攻击。6.3 从 KDD99 迁移到新数据集的注意事项KDD99 的特征列和新数据集如 CICIDS2017完全不同不能直接套用。迁移时需要重新做特征工程但 CNN 的结构可以复用。我一般会保留 Conv1D MaxPooling Dense 的骨架只改输入维度和预处理部分。另外新数据集的标签体系更细可能需要从二分类改成多分类最后一层用 softmax 替代 sigmoid损失函数用 categorical_crossentropy。从那以后我每次拿到一个新的入侵检测数据集都强制先跑一遍标签分布统计和特征维度检查确认没有点号、没有维度错位、没有版本冲突再开始训练。这个习惯帮我省了至少十几个小时的无效调试。希望帮到你。本文还有配套的精品资源点击获取