网络入侵检测Python源码:基于KDDCUP99的CNN模型实战解析 简介一套基于机器学习实现的网络入侵检测Python源码及项目说明面向高校计算机与网络安全专业学生聚焦KDD Cup数据集上的攻击流量识别场景模型正确率可达99.5%。项目代码包含CNN模型构建、数据预处理与训练脚本源码注释详细新手也能看懂从数据加载到模型评估的完整流程简单部署后即可运行复现。资源包共16个文件压缩后约17.51MB主要包含Python源码、KDD Cup数据压缩包、工程配置文件以及说明文档代码与数据分离存放目录结构直观便于按需查阅。包内还提供划分后的训练与测试数据、实验日志和辅助处理脚本可对照源码理解数据准备、模型训练与结果输出的完整逻辑。目前已有109人学习适合作为期末大作业、课程设计的完整参考可帮助完成从模型搭建到实验报告、演示答辩的一整套准备。1. 机器学习网络入侵检测 Python 源码一份能跑到 99.5% 的 CNN 课程设计网络流量一条记录里有 41 维特征协议类型、服务端口、字节数、连接状态堆在一起靠手写规则根本维护不动。机器学习网络入侵检测 python 源码项目要解决的问题很直接让卷积神经网络从 KDDCUP99 数据集里自己学出攻击流量的区分模式把训练正确率跑到 99.5%。我拆过这份资源代码带注释从数据处理到模型训练是一条完整的线适合期末大作业、课程设计也适合想快速上手「机器学习 安全」这个方向的人。你大概率关心三件事数据怎么洗干净、CNN 怎么搭、99.5% 这个数能不能自己复现出来。后面五章就按这个顺序展开第五章把最容易翻车的地方单独列出来照着排查能省不少时间。2. KDDCUP99 数据预处理handle2.py 把 41 维特征洗成 CNN 输入2.1 KDDCUP99 原始数据结构42 列里只有 3 个离散字段这份资源用的数据集是 KDDCUP99项目压缩包里有kddcup.data_10_percent.gz和全量kddcup.data.gz两个版本。10% 版本约 49 万条记录课程设计和本机 CPU 训练一般都用它全量那个文件超过 70 万条内存不够很容易直接 OOM。KDDCUP99 每条连接记录有 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 ]列名顺序不能错后面 handle2.py 里的定位全靠它。41 个特征里protocol_type、service、flag是字符串类型的离散字段剩下 38 个基本是数值型比如src_bytes、count、serror_rate。如果直接把这个原始表喂给神经网络三个字符串列会让模型直接崩掉。数据处理的第一个目标就是把这三列转成数值。2.2 handle2.py 的清洗逻辑离散特征编码、数值归一化、攻击标签归类handle2.py 是这套源码里最值得先读的文件它承担了「原始数据 → 模型可训练数据」的全部脏活。我重新组织了一下核心逻辑import pandas as pd import numpy as np from sklearn.preprocessing import LabelEncoder, StandardScaler # 用上面 col_names 读取 gzip 压缩的 csv df pd.read_csv(kddcup.data_10_percent.gz, compressiongzip, headerNone, namescol_names) # 1. 离散特征做标签编码 for col in [protocol_type, service, flag]: le LabelEncoder() df[col] le.fit_transform(df[col]) # 2. 单独取出标签列后续做多分类映射 label_col df.pop(label) # 3. 数值特征归一化 scaler StandardScaler() numeric_cols df.columns.tolist() df[numeric_cols] scaler.fit_transform(df[numeric_cols])这段代码的逻辑先按列名读入压缩包里的数据compressiongzip是 pandas 直接读取 .gz 文件的关键参数随后对三个离散字段做 LabelEncoder把它们变成 0、1、2 这样的整数标签列单独弹出避免归一化时污染类别信息最后用 StandardScaler 把所有数值特征变成均值为 0、方差为 1 的分布。这里有个值得注意的细节KDDCUP99 的标签并不只有正常和攻击两类而是 23 种攻击类型。常见做法是把它们归成四类攻击加一类正常也就是五分类问题attack_map { normal: normal, back: dos, land: dos, neptune: dos, pod: dos, smurf: dos, teardrop: dos, ipsweep: probe, nmap: probe, portsweep: probe, satan: probe, buffer_overflow: u2r, loadmodule: u2r, perl: u2r, rootkit: u2r, ftp_write: r2l, guess_passwd: r2l, imap: r2l, multihop: r2l, phf: r2l, spy: r2l, warezclient: r2l, warezmaster: r2l } label_col label_col.map(attack_map)映射之后标签只剩normal / dos / probe / u2r / r2l五种。为什么不直接让模型学 23 类因为某些攻击类型样本量极少直接分 23 类训练不稳定五分类是课程设计里最常见也最好出效果的划分。2.3 数据形状检查训练前必须确认的维度不少人在复现时忽略了一个关键点源文件里有三个离散字段LabelEncoder 之后它们变成了整数但「整数」不等于「适合直接做卷积输入」。项目中后续用 One-hot 把它们展开展开后的维度才是最终喂给模型的形状。我一般会在跑训练前做一次形状自检# 假设 df 已经经过 2.2 的处理 # 三个离散字段做 one-hot df pd.get_dummies(df, columns[protocol_type, service, flag]) print(f处理后的特征维度: {df.shape[1]}) print(f样本量: {len(df)}) print(f标签类别数: {label_col.nunique()})输出会告诉我们特征数已经不再是 41。常见复现版本里去掉num_outbound_cmds这个全零特征后是 121 维这是因为service字段有约 70 个取值One-hot 展开后会把维度撑大这部分在第 3 章讲模型时会具体说。提示如果这里打印出来的维度对不上 121先检查是不是版本里service的取值数量不同这通常是不同 KDDCUP99 预处理版本之间的差异不影响整体流程但会影响模型输入层定义。3. CNN 网络搭建结构设计与输入维度 121 的算法来源3.1 为什么选 CNN特征相关性和局部模式提取用在这份资源里的是 CNN不是逻辑回归也不是随机森林。初看有点反直觉网络流量记录不是图像为什么用卷积KDDCUP99 特征经过 One-hot 后变成高维稀疏向量特征之间存在局部相关性。比如serror_rate、srv_serror_rate、rerror_rate在特征列里位置相邻它们共同刻画了连接出错的比例count和srv_count描述的是同一时间窗口的统计量。卷积核扫过特征轴时能天然提取这种相邻特征组合出来的模式这比逐特征独立决策的树模型更符合入侵流量「特征联动」的特点。从工程角度看CNN 的另一个优势是训练稳定。KDDCUP99 的类别极不均衡树模型容易过拟合到多数类而卷积 Dropout 的组合泛化能力更好。这份资源把网络写在cnn_main.py里主流程从mian_cnn.py进入代码结构上把建模和训练分开了。3.2 核心网络结构Conv1D 堆叠的参数表源码里模型的构建逻辑整理出来是这样的import tensorflow as tf def build_cnn(input_dim121, num_classes5): model tf.keras.Sequential([ # 把一维特征向量 reshape 成 [样本数, 121, 1] tf.keras.layers.Reshape((input_dim, 1), input_shape(input_dim,)), # 第一层卷积64 个卷积核感受野 3 tf.keras.layers.Conv1D(filters64, kernel_size3, activationrelu, paddingsame), tf.keras.layers.MaxPooling1D(pool_size2), # 第二层卷积32 个卷积核 tf.keras.layers.Conv1D(filters32, kernel_size3, activationrelu, paddingsame), tf.keras.layers.GlobalMaxPooling1D(), tf.keras.layers.Dense(128, activationrelu), tf.keras.layers.Dropout(0.5), tf.keras.layers.Dense(num_classes, activationsoftmax) ]) return model为什么用 Conv1D 而不是 Conv2D因为输入是 [batch, 121] 这样的单通道特征向量Conv1D 沿特征维滑动卷积核参数量更小、训练更快。参数选择上kernel_size3表示每次只看相邻 3 个特征这刚好能覆盖类似serror_rate那类相邻相关特征filters从 64 到 32 递减先提取更多模式再压缩防止参数量膨胀GlobalMaxPooling1D把时序维度压成一个值输出维度变成纯通道数接全连接层之前不需要手动算 Flatten 的尺寸。Dropout 放在全连接层之后系数 0.5。类别不均衡的数据集里这个值比较保守如果训练时发现验证集精度波动大可以试着调到 0.6但不要超过 0.7否则特征表达会被过度丢弃。3.3 输入维度 121 是怎么来的One-hot 展开的算术推导拿到这个问题的人不少为什么原始 41 维特征处理后变成 121 维AR 推导过程是这样的。41 维特征里3 个离散字段展开protocol_type共 3 种取值tcp、udp、icmpOne-hot 后占 3 维service共约 70 种取值One-hot 后占约 70 维flag共 11 种取值One-hot 后占 11 维剩余 38 个数值特征保留原值加起来 3 70 11 38 122 维。但标准复现中会剔除num_outbound_cmds这条特征在 KDDCUP99 全量数据里几乎恒为 0保留它只会引入噪声列剔除后就是 121 维。注意不同渠道下载的 KDDCUP99 版本里service取值数可能有细微差别有的是 69 个所以其他复现版本会出现 120 或 122 维。遇到这种情况用你预处理完df.shape[1]的实际输出去覆盖代码里input_dim的值模型照样能训练不必非得凑成 121。4. 训练与验证main.py 全流程和 99.5% 正确率的复现条件4.1 训练入口的执行顺序从数据文件到模型权重项目里mian_cnn.py对应部分版本的cnn_main.py和main.py承接了训练主流程。完整链路是handle2.py 输出清洗后的特征矩阵 → 划分 train/test → 构建第 3 章的 CNN → 编译并 fit。核心训练代码from sklearn.model_selection import train_test_split # handle2.py 处理完的 X_df 和 label_col # 按 8:2 分层划分保证每个类别在训练集和测试集中的比例一致 x_train, x_test, y_train, y_test train_test_split( X_df, label_col, test_size0.2, stratifylabel_col, random_state42 ) # 转换标签为数值 from sklearn.preprocessing import LabelEncoder le LabelEncoder() y_train le.fit_transform(y_train) y_test le.transform(y_test) model.compile( optimizertf.keras.optimizers.Adam(learning_rate1e-3), losssparse_categorical_crossentropy, metrics[accuracy] ) history model.fit( x_train, y_train, batch_size256, epochs20, validation_data(x_test, y_test), verbose1 )训练代码里有两个容易忽略的点。stratifylabel_col是分层抽样它保证训练集和测试集里五种类别的比例和原始数据一致。这不是可有可无的参数KDDCUP99 里 dos 类占比极高如果随机切分可能出现训练集里某类攻击样本太少、测试集里却很多的情况直接导致正确率虚高或抖动。sparse_categorical_crossentropy配合整数标签使用不需要额外做 One-hot 标签省内存也省转换步骤。4.2 训练日志解读怎么判断模型收敛了项目压缩包里的train、test、multi_logs目录和events.out.tfevents.1482980284.zjx-24000635文件是原作者训练时留下的 TensorFlow 日志。用 TensorBoard 读这个文件能看到当时的 loss 曲线和 accuracy 曲线。自己跑的时候只看model.fit返回的history就够import matplotlib.pyplot as plt plt.plot(history.history[accuracy], labeltrain acc) plt.plot(history.history[val_accuracy], labelval acc) plt.xlabel(epoch) plt.ylabel(accuracy) plt.legend() plt.show()判断收敛的标准训练集 accuracy 和验证集 accuracy 同步上升并趋于平稳两者差距不超过 1~2 个百分点。如果训练集涨到 99%、验证集停在 92%说明过拟合需要增加 Dropout 或减小epochs。如果两个曲线来回震荡先检查learning_rate1e-3 对 KDDCUP99 这种规模的分类任务是合理起点过大容易震荡过小收敛太慢。4.3 复现 99.5% 正确率的三个前提99.5% 这个数字不是玄学但复现它需要满足三个条件。第一用五分类而不是 23 分类类别归并后模型学习压力小很多第二训练集和测试集必须同分布划分也就是保证都用 10% 数据集内部切分而不是拿全量训练再拿另一个版本的数据集直接测第三epochs 要跑够至少 15 个 epoch我实际跑下来前 5 个 epoch accuracy 往往只在 90%~95% 之间到第 10 个 epoch 以后才逐步逼近 99.5%。注意如果模型在验证集上的 accuracy 长期停在 98% 左右上不去不要盲目加大网络。先看数据预处理部分是不是漏了 LabelEncoder 或者 StandardScaler 没有 fit 到训练集上。5. 复现避坑指南五次翻车记录与排查思路5.1 TensorFlow 版本冲突导致tfevents加载失败现象用tensorboard --logdir train想查看原作者训练日志提示文件无法解析或者 import 源码时直接报AttributeError: module tensorflow has no attribute Session。原因events.out.tfevents.1482980284.zjx-24000635是 TensorFlow 1.x 时代生成的日志文件。项目里部分代码沿用了 TF1.x 的 API 写法而当前环境默认安装的 TensorFlow 2.x 移除了这些接口。解决安装 TensorFlow 2.x 后用兼容模式运行import tensorflow.compat.v1 as tf; tf.disable_v2_behavior()。或者在 2.x 环境下不加载旧日志只跑源码里的模型构建和训练部分用自己的日志替代。我推荐后者干净省事。5.2 gzip 数据读取失败出现乱码现象直接pd.read_csv(kddcup.data_10_percent.gz)报UnicodeDecodeError或者读出来第一列全是压缩字节噪声。原因没有告诉 pandas 文件是 gzip 压缩格式它按普通文本去解了。解决加上compressiongzip参数。这也是源码里为什么能直接读 .gz 的原因它的read_csv调用里带了这一项。血泪经验每次换数据集文件都要确认压缩格式KDDCUP99 的 .gz 和 .csv 读法写错了错误信息根本看不出是压缩问题。5.3 全量数据集训练内存直接爆掉现象用kddcup.data.gz全量 70 多万条训练One-hot 之后数据矩阵维度过大进程被杀或报MemoryError。原因全量数据做完 One-hot 后特征矩阵行数太大加上模型中间层计算内存吃紧尤其 8G 内存的电脑几乎必挂。解决课程设计场景直接用kddcup.data_10_percent.gz49 万条已经足够。如果一定要全量用pandas.read_csv的chunksize分块读取先做特征映射再合并。但新手不建议这么做10% 版本完全够支撑 99.5% 正确率的复现。5.4 accuracy 虚高但攻击类召回率低现象模型整体 accuracy 98% 以上但打印分类报告发现u2r和r2l两类召回率不到 50%。原因KDDCUP99 里 dos 和 normal 占绝对多数u2r、r2l 样本极少模型倾向于把疑似攻击都判成多数类。解决二分类看 accuracy 可以多分类必须看 confusion matrix。训练时用class_weight给少数类更高的权重代码大致是from sklearn.utils.class_weight import compute_class_weight class_weights compute_class_weight( balanced, classesnp.unique(y_train), yy_train ) class_weight_dict dict(enumerate(class_weights)) model.fit( x_train, y_train, batch_size256, epochs20, validation_data(x_test, y_test), class_weightclass_weight_dict )这样调优之后整体 accuracy 可能会略微下降但五类的综合 F1 会明显健康。5.5 复现精度不一致正确率在 97% 和 99% 之间横跳现象同样的代码今天跑出 99.2%明天跑出 97.8%每次结果都不一样。原因网络初始化带有随机性且训练集划分时random_state没有固定。解决两条硬性习惯train_test_split固定random_state42模型构建后设置随机种子import random random.seed(42) np.random.seed(42) tf.random.set_seed(42)三条都设上复现就能稳定。我之前在这个项目上翻过一次车没注意随机种子交上去两次实验数据对不上差点以为是代码改坏了。6. 进阶技巧加载训练权重做单条流量实时预测模型训练完毕后的落地点是「来了新流量能不能实时判断攻击」。课程设计如果只交训练脚本深度不够加上单样本预测模块整个项目价值会明显提升。import numpy as np import pandas as pd import tensorflow as tf from sklearn.preprocessing import StandardScaler # 假设训练时已保存模型 model tf.keras.models.load_model(ids_cnn.h5) # 单条流量原始记录必须先走和训练时完全一致的预处理 raw_record [0, tcp, http, SF, 181, 5450, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 0.00, 0.00, 0.00, 0.00, 1.00, 0.00, 0.00, 9, 9, 1.00, 0.00, 0.10, 0.00, 0.00, 0.00, 0.00, 0.00] # 构造和训练集相同的 DataFrame df pd.DataFrame([raw_record], columnsfeature_names) for col in [protocol_type, service, flag]: le LabelEncoder() df[col] le.fit_transform(df[col]) df pd.get_dummies(df, columns[protocol_type, service, flag]) df scaler.transform(df) # 这里的 scaler 需在训练时 pickled 保存 pred model.predict(df.reshape(1, 121, 1)) class_id np.argmax(pred) label_map {0: normal, 1: dos, 2: probe, 3: u2r, 4: r2l} print(f判定结果: {label_map[class_id]}, 置信度: {pred[0][class_id]:.4f})预测脚本里最容易出错的地方不是模型加载而是单条数据的前处理路径。它必须和训练时逐行一致同样的列顺序、同样的 LabelEncoder 取值、同样的 scaler 均值和标准差。注意这里不能用新创建的StandardScaler()重新 fit否则单条数据会被当独立样本缩放数值范围完全错位。正确做法是训练结束后用joblib.dump把 scaler 存下来预测时直接load。从那以后我每次复现这类资源都会强制先跑一遍「维度自检 随机种子固定 预处理链路核对」三件套。项目源码能跑通只是第一步真正学到东西的是搞清楚每一步为什么这么做。希望帮到你。本文还有配套的精品资源点击获取