资讯动态

基于Python机器学习的网络入侵检测:KDD99与CNN实践

发布时间:2026/9/16 1:36:02 来源:尧图企业网站定制
简介基于Python机器学习实现的网络入侵检测系统完整项目包面向计算机相关专业正在做课程设计、期末大作业的学生以及需要实战练习的机器学习初学者。项目以KDD Cup网络入侵数据集为基础包含CNN等模型的训练与检测脚本、数据预处理与处理工具、README说明文档并配有训练日志与项目配置文件可直接用于复现实验或二次开发。压缩包共16个文件其中以.py源码为主辅以.gz数据集、.xml配置/项目描述文件、.iml模块文件、.md说明文档等整体大小约17.52MB结构简洁、上手成本低。项目代码经过严格调试下载解压即可运行免去环境搭建与数据准备的繁琐过程。已有279人学习适合希望快速获得可运行机器学习入侵检测项目参考的人群。1. 网络入侵检测系统课设为什么绕不开 KDD99网络安全方向的课程设计选题每年都在换花样但网络入侵检测系统这个方向最后多半都会落回同一份数据KDD Cup 99。这份基于 Python 机器学习的网络入侵检测系统源码把 kddcup.data.gz 和 10% 抽样版数据、CNN 训练主脚本 cnn_main.py、数据预处理模块 handle2.py以及带时间戳的 TensorBoard 事件文件全部打包在一起。解压后 train/test 目录、.idea 工程配置都是现成的真正需要动手改的只有标签映射字典、模型输入维度和训练轮数。适合做期末大作业和课程设计也适合想通过完整项目把机器学习流程串一遍的初学者。2. 先把 KDD99 预处理讲透handle2.py 里那几行最值钱2.1 数据形状与标签体系41 维特征加 4 类攻击KDD99 每一行代表一条网络连接记录前 41 列是特征最后一列是攻击类型标记。特征里既有 duration、src_bytes、dst_bytes 这类连续数值也有 protocol_type、service、flag 这三个字符型字段。攻击类型如果按小类算有 22 种以上但绝大多数项目会先做一次收敛把同属一个攻击大类的标签合并成一个类别。攻击大类典型小类KDD99 中出现训练集占比特征dosneptune、smurf、back、teardrop、pod、land数量最大常占 50% 以上probesatan、ipsweep、nmap、portsweep数量适中特征区分度较好r2lguess_passwd、warezclient、imap、spy样本少分类难度高u2rbuffer_overflow、rootkit、loadmodule样本极少最容易漏报normalnormal正常流量作为第五类handle2.py里最值得读的就是这个映射逻辑。源码中attack_map字典把几十个小类归并成 dos / probe / r2l / u2r / normal 五类这一层映射直接决定后面 CNN 的输出神经元个数和评估指标怎么算。我一般会在映射之后立刻检查value_counts()看看五类样本是否都还在如果某个大类因为攻击样本太少被意外过滤掉后面训练再久也学不到这一类。2.2 读取数据与类别特征编码的固定套路数据文件是 gzip 压缩过的Pandas 可以直接读但必须显式指定列名。列名列表用源码里的COLS注意一共 42 个字段41 个特征加 1 个标签列。import gzip import pandas as pd from sklearn.preprocessing import LabelEncoder, StandardScaler COLS [ duration, protocol_type, service, flag, src_bytes, dst_bytes, land, wrong_fragment, urgent, hot, # 第 11 到第 40 个特征按 KDD99 官方字段顺序补齐见 handle2.py 的 FEATURES num_outbound_cmds, is_host_login, is_guest_login, attack ] with gzip.open(kddcup.data_10_percent.gz, rt) as f: df pd.read_csv(f, namesCOLS) attack_map { normal: normal, neptune: dos, smurf: dos, back: dos, teardrop: dos, pod: dos, land: dos, satan: probe, ipsweep: probe, nmap: probe, portsweep: probe, # 其余 r2l / u2r 小类见源码 } df[label] df[attack].map(attack_map) df df.drop(columns[attack]) for col in [protocol_type, service, flag]: df[col] LabelEncoder().fit_transform(df[col])这段代码里有两个容易被忽略的细节。第一个是用gzip.open(..., rt)按文本模式读取如果不加rtPandas 会拿到二进制流虽然也能解析但在某些版本里会出现压缩文件识别异常。第二个是LabelEncoder只负责把字符串变成整数service字段取值很多编码结果与语义无关如果后面发现模型对特定端口不敏感可以考虑对这三个字段单独换成 OneHotEncoder其余数值特征继续走标准化。连续特征标准化要放在编码之后直接用StandardScaler对全部数值列做缩放然后保存这个 scaler 供后续预测阶段复用。注意不要在预处理阶段把 label 也顺手标准化了分类任务里标签必须保持整数形式。2.3 训练集测试集划分与数据泄漏防范KDD99 全量数据接近 490 万条kddcup.data_10_percent.gz 大约是 49 万条课设阶段用 10% 抽样版就够了。训练集和测试集划分时必须带上stratify参数否则 smurf、neptune 这类占比极高的 dos 攻击会把随机划分变成偏科样本集。from sklearn.model_selection import train_test_split label_idx df[label].astype(category).cat.codes cat_map dict(enumerate(df[label].astype(category).cat.categories)) X df.drop(columns[label]).values y label_idx.values X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, stratifyy, random_state42 ) print(cat_map) print(pd.Series(y_train).value_counts())stratifyy让训练集和测试集里五类样本的占比保持一致random_state42保证每次跑出来的划分结果一模一样方便前后对比实验。划分完成后先打印cat_map确认整数 0 到 4 分别对应哪个攻击大类这一步直接决定了后面Dense(5)输出的类别含义和训练时用sparse_categorical_crossentropy还是categorical_crossentropy也有关系。常见的错误是在划分前就对全量数据做了标准化scaler 在训练阶段已经见过测试集的分布测试集评估出来的准确率会虚高。标准做法是 scaler 只在X_train上fit然后transform测试集源码里handle2.py的逻辑也是这个顺序。3. 用 CNN 做分类器为什么流量特征也能上卷积3.1 从特征向量到卷积输入的重塑思路很多人在机器学习入门时把 CNN 默认成图像专属其实一维卷积天然适合处理这类定长向量数据。KDD99 每条记录是 41 维特征可以 reshape 成(41, 1)的形状一维卷积核沿特征轴滑动卷积核大小为 3 时相当于把相邻的 3 个特征组合成一个局部模式。流量记录里的某些攻击行为往往体现在连续几个字段的联动上比如连接时长、传输字节数、错误片段数量三者同时异常一维卷积刚好能捕捉这种局部相关性。源码目录里同时存在 cnn_main.py 和 mian_cnn.py两个脚本的核心思路一致先做 Embedding 或标准化处理再送入一维卷积层。如果走数值特征路线不需要 Embedding直接把标准化后的 41 维向量 reshape 成(样本数, 41, 1)即可。如果想把 protocol_type、service 等离散字段做成嵌入向量则需要预处理阶段先给每个字段单独编号再在网络入口分别 Embedding最后拼接这种结构代码量更大课设阶段通常不必要。3.2 模型结构与参数说明用 Keras 搭一个可复现的 1D CNN 模型结构如下import tensorflow as tf from tensorflow.keras import layers, models model models.Sequential([ layers.Input(shape(41, 1)), layers.Conv1D(filters64, kernel_size3, paddingsame, activationrelu), layers.MaxPooling1D(pool_size2), layers.Conv1D(filters128, kernel_size3, paddingsame, activationrelu), layers.GlobalMaxPooling1D(), layers.Dense(64, activationrelu), layers.Dropout(0.5), layers.Dense(5, activationsoftmax) ]) model.compile( optimizeradam, losssparse_categorical_crossentropy, metrics[accuracy] ) model.summary()整体结构与源码里 cnn_main.py 的搭建方式等价适合直接对照改。# 输入形状里的 41 要和预处理后的特征数一致 # 类别数 5 对应 dos/probe/r2l/u2r/normal # sparse_categorical_crossentropy 对应整数标签 0-4 # 如果用了 one-hot 标签则改用 categorical_crossentropy各层输出维度变化如下层输出形状说明Input(None, 41, 1)一行流量记录 41 个特征单通道Conv1D(64, 3)(None, 41, 64)卷积核长度 3提取局部特征MaxPooling1D(2)(None, 20, 64)长度减半降低计算量并保留显著特征Conv1D(128, 3)(None, 20, 128)第二层卷积抽象高维模式GlobalMaxPooling1D(None, 128)每个特征图取最大值输出 128 维向量Dense(64)(None, 64)全连接层进一步组合特征Dropout(0.5)(None, 64)随机丢弃一半神经元缓解过拟合Dense(5)(None, 5)softmax 输出五个类别的概率分布第一层卷积核数量 64、第二层 128 是这类小数据集的常见起点。KDD99 特征维度只有 41kernel_size 取 3 比较合适取 5 会把特征窗口拉长容易把不相关的字段组合进去效果反而变差。paddingsame保持特征图长度不变避免第一层卷积后维度缩水影响后续池化。3.3 训练配置与 TensorBoard 可视化训练阶段的超参直接影响最终效果常见的配置组合如下from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint, TensorBoard model.fit( X_train.reshape(-1, 41, 1), y_train, validation_split0.2, epochs15, batch_size256, callbacks[ EarlyStopping(monitorval_loss, patience3, restore_best_weightsTrue), ModelCheckpoint(ids_cnn.h5, monitorval_accuracy, save_best_onlyTrue), TensorBoard(log_dir./multi_logs, histogram_freq1) ] )validation_split0.2表示从训练集里再切 20% 当验证集验证集不参与梯度更新只用于判断模型什么时候开始过拟合。EarlyStopping的patience3表示验证集 loss 连续 3 个 epoch 不下降就停止训练restore_best_weightsTrue让模型回退到验证集表现最好的那组权重。batch_size256是 CPU 训练时比较平衡的选择如果机器内存紧张可以降到 128。源码里那个events.out.tfevents.1482980284.zjx-24000635文件就是训练时通过 TensorBoard 回调生成的日志1482980284是当时训练的时间戳zjx-24000635是训练机器的 hostname。训练完成后在命令行执行tensorboard --logdir./multi_logs就能看 loss 曲线和梯度直方图。自己复现时可以把log_dir改成multi_logs下的新子目录TensorBoard 会把多次训练结果组合同一页面里对比。4. 从解压到出结果的完整运行路径与常见报错4.1 一次跑通的命令序列拿到压缩包解压后目录里最核心的文件是kddcup.data_10_percent.gz、handle2.py、cnn_main.py和main.py。运行顺序是先解压数据再执行预处理脚本生成清洗后的特征文件最后跑 CNN 训练。# 解压 gzip 数据不需要手动删原文件-k 表示保留压缩包 gzip -dk kddcup.data_10_percent.gz # 生成处理后数据集一般会输出 processed.csv 或类似文件 python handle2.py # 开始训练 CNN 模型 python cnn_main.py # 启动 TensorBoard 查看训练曲线 tensorboard --logdir./multi_logs这里gzip -dk中的d表示解压k表示保留原始 .gz 文件方便多次实验重复使用。handle2.py是整条流水线的入口如果直接跳过它去跑cnn_main.py训练脚本会因为找不到清洗后的数据文件而报FileNotFoundError。4.2 训练输出指标怎么看训练启动后每轮会输出训练集准确率、验证集准确率、loss 和 val_loss 四个值。刚开始训练时准确率会快速上升到 90% 以上这是 KDD99 数据的典型现象不代表模型已经足够好。原因是 smurf、neptune 两类样本在数据集中占比极大模型只要学会识别这两类整体准确率就被抬上去了。真正要看的是每个类别的单独表现。训练结束后用测试集跑一次分类报告import numpy as np from sklearn.metrics import classification_report y_pred model.predict(X_test.reshape(-1, 41, 1), verbose0) y_pred_cls np.argmax(y_pred, axis1) print(classification_report(y_test, y_pred_cls, target_names[normal, dos, probe, r2l, u2r]))输出里重点看 r2l 和 u2r 的 recall。这两个类别的样本数量少分类器很容易为了整体准确率牺牲它们如果 recall 低于 50%说明模型漏掉了大量该类型攻击得考虑用类别权重或者修改损失函数来拉高这部分权重。4.3 高频报错与处理方式跑这份源码时最常遇到的几个问题基本集中预处理和输入维度两个环节。ParserError: Error tokenizing data通常是 Pandas 读取时列名数量对不上。KDD99 一行有 42 个字段COLS列表长度必须是 42数错一个都会触发这个报错。训练时报ValueError: Input 0 of layer conv1d is incompatible说明进入模型的数据第二维不是 41。常见原因是预处理阶段删除了某个特征列或者把标签列也当成了特征喂了进去。模型预测结果全部是同一个类别先检查测试集里是否包含某个占比极高的大类再用value_counts()看训练集标签分布很可能某个小类在划分训练集时被全部过滤掉了。tfevents文件无法在 TensorBoard 中显示检查路径是否填成了单次日志目录而不是包含所有日志的父目录--logdir需要指向multi_logs这一层。处理报错时一个通用的排查思路是先在代码里打印X_train.shape、y_train.shape确认形状符合预期再打印np.unique(y_train)确认所有类别都存在。这两步没问题模型训练基本不会出幺蛾子。5. 替换数据集与评估指标让这个课设从能跑变成能答辩5.1 换 NSL-KDD 时只需要改三处课设阶段用 KDD99 能跑通流程但答辩时如果能把数据集换成 NSL-KDD说服力会明显提升。NSL-KDD 是对 KDD99 的优化版本去掉了大量重复记录训练集约 12.5 万条特征列定义与 KDD99 完全一致只是多了最后一列difficulty。替换时只需要改三处handle2.py里的文件名和列名列表、attack_map字典中的小类名称NSL-KDD 同样基于 22 种攻击类型、以及删除target时保留difficulty或不读它。5.2 用混淆矩阵确认每类攻击是否真的被识别只输出准确率还不够加一张混淆矩阵热力图答辩时可以直观说明哪些攻击容易被漏判。from sklearn.metrics import confusion_matrix import seaborn as sns import matplotlib.pyplot as plt cm confusion_matrix(y_test, y_pred_cls) plt.figure(figsize(8, 6)) sns.heatmap(cm, annotTrue, fmtd, cmapBlues, xticklabels[normal, dos, probe, r2l, u2r], yticklabels[normal, dos, probe, r2l, u2r]) plt.xlabel(Predicted) plt.ylabel(True) plt.tight_layout() plt.savefig(confusion_matrix.png, dpi150)annotTrue把每个格子的数值直接标在图上fmtd表示整数格式否则 seaborn 默认用科学计数法显示。保存成 PNG 图后可以直接放进答辩 PPT比贴训练日志直观得多。矩阵里非对角线的格子就是误分样本重点看 u2r 所在行如果全部集中在 normal 列说明模型完全没学会这类攻击。5.3 把模型封装成单一预测函数最后收尾时一个实用技巧把预处理、模型加载和类别映射封装成一个函数这样演示时只需输入一条原始记录就能给出判断结果。import joblib import numpy as np scaler joblib.load(scaler.pkl) model tf.keras.models.load_model(ids_cnn.h5) labels [normal, dos, probe, r2l, u2r] def predict_one(raw_features): x np.array(raw_features).reshape(1, -1) x scaler.transform(x) x x.reshape(1, 41, 1) proba model.predict(x, verbose0)[0] return labels[np.argmax(proba)], float(np.max(proba)) # 示例一条测试记录的 41 个特征值 result, confidence predict_one(sample_features) print(result, confidence)joblib.load加载的是训练阶段保存的 scalermodel加载的是ModelCheckpoint保存的最优权重。predict_one内部完成了标准化、reshape、预测、类别映射四步把原本需要十几行代码的操作收敛成了一个函数。答辩现场拿一条训练集之外的真实连接记录过来直接调用这个函数输出攻击类型和置信度整个系统的完整性一下子就立住了。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价