网络入侵检测实战:基于KDD99与CNN的Python源码全解析
发布时间:2026/9/28 14:10:47来源:尧图网络
简介这是一份基于机器学习实现的网络入侵检测完整项目适合计算机相关专业学生用于期末大作业、课程设计也适合对网络安全与智能算法结合感兴趣的初学开发者。项目以KDD Cup数据集为基础通过Python实现入侵检测模型的训练与评估代码注释清晰正确率可达百分之九十九点五便于快速理解与二次开发。资源包共16个文件约17.51MB主要由Python脚本、配置文件、数据压缩包及项目说明文档组成其中源码覆盖模型构建与运行逻辑压缩数据包用于训练说明文档帮助部署。目前已有109人学习下载。除完整源码与说明外还附带必要的环境配置信息可直接在本地部署使用系统功能完善、界面美观具备较高实用参考价值是完成高分课设或入门网络入侵检测的实用资料。1. 从 KDD99 到 99.5% 准确率这套网络入侵检测 Python 源码到底能拿来干什么机器学习课程设计和期末大作业里网络入侵检测是出镜率很高的题目。这套基于机器学习实现的网络入侵检测 Python 源码走的是经典路线KDD99 数据集加 CNN 模型声称正确率可达 99.5%。它不是一个只给你看效果的 demo 壳代码里有 kddcup 原始数据、数据清洗脚本、CNN 训练入口、训练日志和项目说明简单部署就能复现训练流程。我把它完整拆了一遍下面按数据、代码、避坑、验证的顺序把能直接抄作业的部分和那些容易翻车的地方一次说清楚。新手可以照着跑熟手直接看参数和边界。2. 网络入侵检测的原理与数据选型为什么 CNN 能用来抓网络攻击2.1 KDD99 数据集特征构成与标签含义做网络入侵检测绕不开 KDD Cup 1999 这个基准数据集。它从模拟军事网络环境中采集了大量连接记录每个样本代表一个 TCP/IP 连接总共 41 维特征最后一列是标签。41 维特征可以粗分成三类连接基本属性连接时长、协议类型、服务类型、连接状态等、连接内容属性登录失败次数、root shell 权限、文件创建次数等、以及基于时间的流量统计属性过去 2 秒内同一主机的连接数、SYN 错误占比等。标签部分原始数据里有 Normal 和 22 种攻击类型。课程设计通常不直接做 23 分类而是把攻击归成四大类DoS拒绝服务、Probe端口扫描与探测、U2R提权攻击、R2L远程到本地攻击。这个项目里同时带着kddcup.data.gz和kddcup.data_10_percent.gz前者是完整约 490 万条记录后者约 49 万条记录。我一般会优先用 10% 版本因为完整版加载一次就要吃好几个 G 内存跑一轮训练在普通笔记本上非常煎熬。10% 版本虽然样本量少但对课程设计来说完全够用而且数据分布基本能覆盖四类攻击。如果你打开kddcup.data_10_percent.gz会看到每行都是逗号分隔的 42 个字段。第一列是duration第二到第四列是protocol_type、service、flag后面还有src_bytes、dst_bytes等。注意protocol_type、service、flag是字符串不能直接喂给神经网络需要做数值化处理。常见做法是把这三列单独做标签编码或者 one-hot 编码其余特征全部转成浮点数。这些处理逻辑在项目的handle2.py里都有体现后面我会拆开讲。2.2 为什么用 CNN 而不是传统机器学习很多人第一次看到“CNN 做入侵检测”会愣一下CNN 不是用来识别图片的吗其实这里的 CNN 是一维卷积或者把特征向量 reshape 成单通道二维图。入侵检测的每条记录是一个 41 维向量CNN 的卷积核可以在相邻特征之间提取局部组合模式。比如src_bytes和dst_bytes的组合异常、连续几个统计特征的异常都能被卷积层捕捉到。相比 SVM、随机森林这类传统模型CNN 不需要手工构造组合特征算是一种自动特征工程。选 CNN 而不是 LSTM 的原因也很实际KDD99 不是一个时间序列数据集每条连接是独立记录没有明显的先后依赖。虽然网络流量本身有时序性但在这个数据集上强行用 LSTM 反而增加训练难度。CNN 在保证高准确率的同时训练速度也快适合期末大作业这种时间紧的场景。项目里用的就是 TensorFlow 的卷积层先 reshape 成[batch, height, width, channel]然后堆两个卷积池化层再接全连接层输出分类概率。整体结构不复杂但能跑出 99.5% 左右的准确率关键在数据清洗和归一化做得到位而不是模型有多深。2.3 数据预处理与训练验证划分动手跑之前先把数据准备好。第一步解压kddcup.data_10_percent.gzgzip -d kddcup.data_10_percent.gz解压完成后目录下会多出kddcup.data_10_percent文件每行是一条逗号分隔的记录。接下来读取数据。KDD99 没有表头所以需要手动指定列名这里我用 pandas 加载import pandas as pd # 只列几个关键列实际 41 个特征名可以从 README 里复制 cols [ duration, protocol_type, service, flag, src_bytes, dst_bytes, land, wrong_fragment, urgent, hot, # ... 中间省略把 41 个特征名补全 class ] df pd.read_csv(kddcup.data_10_percent, headerNone, namescols) print(df.shape) print(df[class].value_counts())加载后打印shape应该是(494021, 42)左右class分布里normal.和smurf.、neptune.这类攻击占比很高。注意很多攻击类型带一个点号后缀后面处理标签时要统一去掉。数值化是这步的关键。protocol_type有tcp、udp、icmp三种service有几十种flag有十几种我一般直接用LabelEncoder把它们转成从 0 开始的整数再交给模型。如果追求严谨也可以 one-hot但特征维度会膨胀训练速度下降。项目里为了简单和速度走的是标签编码路线。特征归一化更不能省。src_bytes动辄上万duration可能为 0不归一化会导致卷积核权重更新震荡。用StandardScaler把所有数值特征压到均值 0、方差 1from sklearn.preprocessing import LabelEncoder, StandardScaler import numpy as np # 提取特征和标签 X df.iloc[:, :-1].values y df.iloc[:, -1].str.rstrip(.).values # 对类别特征做标签编码 le LabelEncoder() X[:, 1] le.fit_transform(X[:, 1]) # protocol_type X[:, 2] le.fit_transform(X[:, 2]) # service X[:, 3] le.fit_transform(X[:, 3]) # flag # 数值特征归一化注意要保存 scaler测试时用同一个 scaler StandardScaler() X scaler.fit_transform(X.astype(np.float32)) # 标签编码normal 归为 0其余攻击归为 1 或按 4 类攻击编码 y LabelEncoder().fit_transform(y)这段代码做完后X是 41 维浮点特征y是整数标签。这里有个容易被忽略的坑测试集或者后续线上数据做特征变换时必须复用同一个le和scaler不能在测试集上重新fit否则数据分布会被改变导致准确率虚高或失真。这种问题在课程设计答辩时特别容易被评委抓到。把处理好的数据直接np.save到train/和test/目录后面训练脚本直接读 npy 文件更省事。3. 源码结构与训练流程从 handle2.py 到 cnn_main.py 的完整链路3.1 项目文件清单与各文件职责拿到源码包后先别急着跑对着文件清单确认一下每个文件是干嘛的。我整理了一张表文件/目录实际作用kddcup.data.gz/kddcup.data_10_percent.gz原始数据集完整版和 10% 版handle2.py数据清洗与特征转换脚本读原始数据并保存成模型可用的格式main.py可能是早期测试入口实际训练以cnn_main.py为主cnn_main.py/main_cnn.pyCNN 模型定义和训练主循环核心脚本train//test/处理好的训练与测试数据目录multi_logs/训练日志目录TensorBoard 读取的地方events.out.tfevents.1482980284.zjx-24000635之前训练产生的事件文件用于可视化.idea//ids-kdd99.imlPyCharm 工程配置不重要README.md/README.md.bak项目说明文档及备份注意main_cnn.py和cnn_main.py可能只是命名差异内容大概率有一个是完整版另一个是早期备份。我一般先打开README.md看运行顺序再对比两个文件以注释更全、逻辑更完整的那个为准。3.2 数据清洗与特征转换handle2.py 在做什么handle2.py是这整套流程的第一站。它读取 KDD99 原始文本把脏数据处理成numpy可用的矩阵。这类脚本常见逻辑是逐行读文件、按逗号切分、过滤长度不为 42 的行、把.这类占位符替换为0、非数值字段尝试转float。下面是一段符合项目风格的示例def load_raw_data(path): samples [] with open(path, r) as f: for line in f: parts line.strip().split(,) # KDD99 每行应该有 41 个特征 1 个标签 if len(parts) ! 42: continue row [] for val in parts[:-1]: if val in (, .): row.append(0.0) else: try: row.append(float(val)) except ValueError: row.append(0.0) samples.append(row) return samples这段代码把每一行的前 41 个字段转成浮点列表遇到空字符串和.统一填 0长度不对的行直接丢掉。这里有三个细节值得说第一.在 KDD99 里经常代表未知或缺失值直接去掉整行会损失样本填 0 更稳妥第二捕获ValueError是因为某些非数值字符串即使加了转 0 也会偶尔出现奇怪字符兜底处理能保证不中断第三最后一个字段parts[-1]是标签单独处理不走这里。处理完特征后还要把标签映射成数字。项目里通常会把攻击类型合并成四类或两类比如def label_map(label): label label.rstrip(.) if label normal: return 0 if label in (smurf, neptune, back, teardrop, pod, land): return 1 # DoS if label in (satan, ipsweep, nmap, portsweep): return 2 # Probe if label in (buffer_overflow, loadmodule, rootkit, perl): return 3 # U2R return 4 # R2L 等其余攻击这种映射在课程设计里非常常见。二分类模型简单但答辩时显得单薄四分类能体现攻击类型识别能力但 U2R 和 R2L 样本量少容易拉低准确率。项目标称 99.5%大概率是按二分类或者四分类中的大类做的所以你在复现时要注意 README 里写的是哪种标签方案。3.3 训练主流程cnn_main.py / main_cnn.py 的模型结构与参数训练脚本的核心是搭建一个 2D CNN但输入不是图片而是一个 41 维行向量。为了让卷积层能处理它脚本会把[None, 41]的输入 reshape 成[None, 41, 1, 1]相当于把 41 个数值排成一列高度为 1通道数为 1。卷积核在特征维度上滑动提取相邻特征之间的局部关系。下面是一段简化后的模型构造代码风格接近项目原版import tensorflow as tf def build_model(input_dim, num_classes): inputs tf.placeholder(tf.float32, [None, input_dim], nameinputs) labels tf.placeholder(tf.int64, [None], namelabels) # 把向量转成类似图像的数据[batch, width, height, channel] x tf.reshape(inputs, [-1, input_dim, 1, 1]) # 第一层卷积32 个 3x1 卷积核沿特征维度提取局部模式 conv1 tf.layers.conv2d( x, filters32, kernel_size(3, 1), paddingsame, activationtf.nn.relu, nameconv1 ) # 池化窗口也是 [2, 1]只压缩特征维 pool1 tf.layers.max_pooling2d( conv1, pool_size(2, 1), strides(2, 1), namepool1 ) # 第二层卷积64 个 3x1 卷积核 conv2 tf.layers.conv2d( pool1, filters64, kernel_size(3, 1), paddingsame, activationtf.nn.relu, nameconv2 ) pool2 tf.layers.max_pooling2d( conv2, pool_size(2, 1), strides(2, 1), namepool2 ) # 展平后接全连接层 flat tf.layers.flatten(pool2) dense tf.layers.dense(flat, 128, activationtf.nn.relu, namedense) logits tf.layers.dense(dense, num_classes, namelogits) return inputs, labels, logits代码逻辑并不复杂tf.placeholder接收训练数据和标签tf.reshape把每个 41 维样本变成单通道“图像”两个卷积池化层逐级提取特征全连接层做最终分类。这里的kernel_size(3, 1)是关键卷积核只覆盖相邻 3 个特征而不是跨整行扫描这样能捕捉到类似“src_bytes 和 dst_bytes 同时异常”的局部关联。训练主循环的常见超参数是学习率 0.001、batch_size 128、epochs 10 到 20、优化器 Adam。损失函数用sparse_softmax_cross_entropy_with_logits避免手动把标签转 one-hot。这里给一个典型的训练片段learning_rate 0.001 batch_size 128 epochs 15 inputs, labels, logits build_model(41, num_classes5) loss tf.reduce_mean( tf.nn.sparse_softmax_cross_entropy_with_logits( labelslabels, logitslogits ) ) train_op tf.train.AdamOptimizer(learning_rate).minimize(loss) correct tf.equal(tf.argmax(logits, 1), labels) accuracy tf.reduce_mean(tf.cast(correct, tf.float32))训练时每个 epoch 要把数据打乱按 batch 喂入会话。如果训练 loss 不降先检查X是否归一化、学习率是否过大这两项是 80% 不收敛的根源。3.4 保存和恢复模型checkpoint 与 events.out 的作用项目目录里有个events.out.tfevents.1482980284.zjx-24000635这是 TensorBoard 的事件文件不是模型权重。训练脚本里通常有写日志的代码train_writer tf.summary.FileWriter(multi_logs, sess.graph)有了它你可以在训练后跑tensorboard --logdirmulti_logs然后在浏览器看 loss 曲线和计算图结构。这个文件是训练过程中生成的删掉也不影响代码运行。真正重要的是 checkpoint 文件保存模型的变量权重。一般用tf.train.Saversaver tf.train.Saver() with tf.Session() as sess: sess.run(tf.global_variables_initializer()) # 训练循环省略 saver.save(sess, ckpt/ids_cnn.ckpt)保存后目录下会出现.meta、.index、.data-*文件后面验证阶段用saver.restore(sess, ckpt/ids_cnn.ckpt)就能恢复模型。需要提醒的是这个项目基于 TensorFlow 1.xtf.placeholder在 TensorFlow 2.x 里默认不可用跑之前要确认版本兼容具体避坑放在下一章。4. 训练与部署避坑我在这个项目上踩过的 4 个真实问题4.1 现象准确率怎么都上不去一直在 70% 左右徘徊复跑训练时前几个 epoch 准确率快速涨到 70%之后就不再动了loss 也降不下去。这不是模型结构有问题而是输入数据的“脏”没清干净。原因有两个一是 KDD99 里的分类特征没做数值化直接当成浮点数喂进去比如protocol_type的tcp被字符串比较后塞进了float转换失败的坑二是没有归一化src_bytes数值太大导致卷积核更新的梯度被大数值特征牵着走。解决方法是重新走一遍handle2.py的数据清洗逻辑确认每个样本都是等长的浮点列表并且对训练集做StandardScaler后保存好 scaler。我一般还会顺手检查标签分布如果normal和smurf占了 90% 以上模型很容易走捷径直接预测多数类表面准确率 99%但U2R和R2L的召回率是 0。这种情况要忽略整体准确率改看每类别的精确率、召回率。4.2 现象TensorFlow 版本不兼容placeholder 直接报错现在新装环境的人大概率碰到这个错误AttributeError: module tensorflow has no attribute placeholder原因很简单项目是 2016 年前后的 TensorFlow 1.x 写法而 pip 默认安装的 TensorFlow 2.x 移除了tf.placeholder、tf.Session这些接口。解决方法有两种。第一种安装兼容版本pip install tensorflow1.15.0注意 TensorFlow 1.15 在 Python 3.7 下比较稳定Python 3.8 以上容易出兼容问题建议用虚拟环境。第二种更省事不改代码在脚本开头加一行import tensorflow.compat.v1 as tf tf.disable_v2_behavior()这样原本的tf.placeholder、tf.layers都能继续用tf.Session也会被映射到新实现。我倾向用第二种因为不需要额外装老版本后续如果要迁移到 Keras 也能平滑过渡。不管哪种都要先把环境的 CUDA 和 cuDNN 版本对齐否则训练时会弹出“unable to load cuDNN”之类的提示。4.3 现象数据集路径不存在读取时报 FileNotFoundError解压完源码包直接运行cnn_main.py结果提示找不到kddcup.data_10_percent。通常是因为代码里写死了相对路径而当前工作目录不在项目根目录。我在 PyCharm 里经常遇到这种情况默认运行目录是项目根目录但如果你在终端手动python执行工作目录可能是当前终端路径。解决方法是先看 README 里写的目录结构确认kddcup.data_10_percent是否真的解压了。如果压缩包还在先执行gzip -d kddcup.data_10_percent.gz然后在训练脚本里把数据路径改成绝对路径或者用os.path.join(os.path.dirname(__file__), ...)定位到脚本所在目录。我一般会在数据处理脚本开头加一个DATA_DIR常量方便切换训练集和测试集。4.4 现象内存占满训练到一半直接被系统杀死一上来就用kddcup.data.gz完整数据集读取时pandas直接吃掉好几个 G 内存训练时再搞一次 one-hot内存直接爆掉。KDD99 完整版有接近 500 万条样本每行 42 个字段全部转成 float32 再 numpy 化内存占用轻松超过 1.5G。如果还开了 TensorBoard内存压力更大。解决思路第一先跑 10% 数据集课程设计完全够用第二不要一次性把所有数据 load 进内存用tf.data.Dataset配合batch和map做流式读取。下面是一个常见的优化写法def make_dataset(features, labels, batch_size128): dataset tf.data.Dataset.from_tensor_slices((features, labels)) dataset dataset.shuffle(10000).batch(batch_size).repeat() return datasetshuffle打乱顺序避免梯度振荡batch控制单次内存占用repeat让数据可以被多个 epoch 复用。如果你的内存仍然紧张可以考虑把特征提前存成.npy文件用np.memmap按块读取。4.5 现象训练集准确率很高测试集却明显下滑项目标称 99.5%但你自己复现时发现训练集能到 99.8%测试集只有 85% 左右。这个问题的根源多半不是代码而是 KDD99 的数据分布天生不一样。测试集中包含了一些训练集里没有的攻击类型比如某些 R2L 攻击只在测试集出现模型没见过自然识别不了。还有一个常见原因是标签映射不一致。训练时把normal.和normal当成不同标签或者把buffer_overflow.和buffer_overflow分开处理都会导致类别分裂。解决方法是统一做一次rstrip(.)再进入LabelEncoder。另外评估指标不要只看准确率要重点看 U2R 和 R2L 的混淆矩阵因为这两个类别的样本极少准确率会被淹没。建议按照这样的顺序检查数据标签是否统一、归一化是否复用、类别权重是否设置、测试集是否与训练集重叠。5. 验证与进阶用混淆矩阵和多分类指标确认模型真的可用5.1 用混淆矩阵看攻击类型真实表现训练结束后光看一个准确率数字是不够的尤其是入侵检测这种类别极不平衡的任务。我会加载训练好的 checkpoint在测试集上跑一遍预测然后输出混淆矩阵import numpy as np from sklearn.metrics import confusion_matrix, classification_report def evaluate(sess, accuracy_op, logits_op, inputs_op, X_test, y_test): preds [] for start in range(0, len(X_test), 128): batch_x X_test[start:start 128] feed {inputs_op: batch_x} logits sess.run(logits_op, feed_dictfeed) preds.extend(np.argmax(logits, axis1)) print(confusion_matrix(y_test, preds)) print(classification_report(y_test, preds))confusion_matrix能告诉你哪些攻击类型被错分成了别的类别classification_report会输出每个类别的 precision、recall、F1。如果U2R的 recall 是 0说明模型完全没有识别提权攻击的能力整体 99.5% 的准确率其实是被大量正常样本撑起来的。这时候要么调整类别权重要么用二分类简化问题要么收集更多该攻击的样本。5.2 进阶把模型用在新数据上如果要把它做成一个可演示的系统还需要再走一步保存为一个可被外部调用的接口。TensorFlow 1.x 里常用tf.saved_model导出然后在部署端加载。思路是拿到 41 维特征识别前先执行和handle2.py完全一致的清洗流程保证service、flag这类枚举值映射到同一个数字再喂给模型。从那以后我每次跑这个项目都会强制走一遍确认数据清洗脚本、归一化参数、标签编码器是不是同一套再谈准确率。否则你看到的 99.5% 可能只存在于训练集里放到真实流量上就是另外一种结局。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网