news 2026/9/23 21:44:11

基于Python机器学习的网络入侵检测系统实战:KDD99预处理与模型对比

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于Python机器学习的网络入侵检测系统实战:KDD99预处理与模型对比

简介:基于Python机器学习的网络入侵检测系统源码与全部数据以压缩包形式提供,主要面向计算机相关专业正在完成课程设计、期末大作业的学生,以及需要项目实战练习的入门学习者。资源共16个文件,包含4个Python程序文件、2个KDD CUP数据集压缩包、TensorFlow训练事件文件、XML工程配置文件及说明文档等,整体大小约17.52MB,结构清晰便于直接导入项目。其中Python脚本覆盖主程序、CNN模型构建与数据处理等关键环节,两个gz数据文件分别为KDD Cup 1999的10%样本和全量数据,可供模型训练与验证使用。压缩包内还附有说明文档和IDE工程配置,便于快速了解项目结构与运行方式。项目已经严格调试,下载解压后即可运行,能帮助学习者快速复现基于机器学习的网络入侵检测实验,理解特征提取、模型训练与评估的完整流程,也可作为课程报告或毕业设计的参考实现。目前已有279人学习使用,适合需要真实可运行代码与配套数据、希望通过实战巩固机器学习知识的学习者。

1. 基于 Python 机器学习的网络入侵检测系统源码:先搞清楚它解决什么问题

拿到压缩包先别急着跑 main.py。这份基于 python 机器学习的网络入侵检测系统源码,本质是以 KDD99 为数据集的完整实验工程:handle2.py 洗数据,main.py 跑传统机器学习基线,cnn_main.py 与 main_cnn.py 走卷积网络路线,再配合 kddcup.data_10_percent.gz 和 kddcup.data.gz 两档数据,能直接撑起课程设计里「传统方法 vs 深度方法」的对比章节。它适合两类人:正在做网络入侵检测课程设计或期末大作业、需要一套能跑通且能出对比图的学生;想快速上手 KDD99 数据集、验证自己预处理和建模思路的开发者。它不是生产级系统,而是能跑通、能出图、能写报告的完整闭环工程。

2. KDD99 数据预处理:41 维特征、五种攻击标签与 handle2.py 的转换逻辑

2.1 KDD99 的结构:23 种原始标签先归并成五分类

KDD99 是网络入侵检测领域被引用最多的公开数据集之一,每条记录代表一个 TCP 连接,特征一共 41 维,既包括连接时长、协议类型、服务端口、源/目的字节数这类基本属性,也包括连接窗口内的失败次数、创建文件次数、root shell 使用次数等内容特征。数据集的最后一列才是标签,也就是说,真正交给模型的特征是 41 个连续或枚举值,而标签是原始的那个字符串。

这里有个新手最容易忽略的点:KDD99 原始 label 并不是干净的「正常/攻击」二分类,而是 23 种字符串,比如 neptune、smurf、guess_passwd、buffer_overflow。直接把字符串塞给模型没有任何意义,处理字符串之前要先做一次归并。按官方和绝大多数论文的标准分法,23 种标签会被压缩成五个大类,映射关系如下:

五大类别包含的原始标签
normalnormal
dosback, land, neptune, pod, smurf, teardrop
probeipsweep, nmap, portsweep, satan
r2lftp_write, guess_passwd, imap, multihop, phf, spy, warezclient, warezmaster
u2rbuffer_overflow, loadmodule, perl, rootkit

这份源码包里同时出现了 kddcup.data_10_percent.gz(约 49 万条)和 kddcup.data.gz(约 489 万条),前者是官方划好的 10% 子集,后者是全量训练数据。从文件清单看,作者没有沿用官方修正测试集,而是用 train/test 目录存放自划分的数据,这对课程设计反而友好:报告里可以直接写清切分比例和随机种子,不必引入第三个外部文件。

2.2 handle2.py 在做什么:标签映射、数值化与归一化

压缩包里那个 handle2.py 是整个工程的预处理入口,我按文件清单和常见做法还原一下它的核心逻辑,你在复现时可以对照着改:

import pandas as pd import numpy as np from sklearn.preprocessing import MinMaxScaler, LabelEncoder from sklearn.model_selection import train_test_split from joblib import dump LABEL_MAP = { 'normal': 'normal', 'back': 'dos', 'land': 'dos', 'neptune': 'dos', 'pod': 'dos', 'smurf': 'dos', 'teardrop': 'dos', 'ipsweep': 'probe', 'nmap': 'probe', 'portsweep': 'probe', 'satan': 'probe', 'ftp_write': 'r2l', 'guess_passwd': 'r2l', 'imap': 'r2l', 'multihop': 'r2l', 'phf': 'r2l', 'spy': 'r2l', 'warezclient': 'r2l', 'warezmaster': 'r2l', 'buffer_overflow': 'u2r', 'loadmodule': 'u2r', 'perl': 'u2r', 'rootkit': 'u2r' } COLUMN_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'] def load_and_clean(path): df = pd.read_csv(path, compression='gzip', header=None) df.columns = COLUMN_NAMES + ['label', 'difficulty'] df['label'] = df['label'].str.strip().map(LABEL_MAP) df = df.dropna(subset=['label']) return df def preprocess(df): for col in ['protocol_type', 'service', 'flag']: encoder = LabelEncoder() df[col] = encoder.fit_transform(df[col]) X = df[COLUMN_NAMES].astype(np.float32) y = df['label'] return X, y def main(): df = load_and_clean('kddcup.data_10_percent.gz') X, y = preprocess(df) X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y) scaler = MinMaxScaler() X_train = scaler.fit_transform(X_train) X_test = scaler.transform(X_test) dump(scaler, 'scaler.joblib') np.save('X_train.npy', X_train) np.save('y_train.npy', y_train) np.save('X_test.npy', X_test) np.save('y_test.npy', y_test) if __name__ == '__main__': main()

这段处理的几个关键决策点,在课程设计答辩时经常被问到,建议提前想清楚。第一,protocol_type、service、flag 是枚举型特征,LabelEncoder 会把它们转成 0、1、2 这样的整数,效果上等价于给每个取值分配一个不重复编号,比 one-hot 省内存;如果换成 one-hot,service 有几十种取值,特征维度会从 41 直接膨胀到 100 以上,main.py 里逻辑回归的训练时间会明显变长。第二,标签映射用的是字符串小写形式,KDD99 原始文件里有少量带点和空格的脏数据,所以先做 str.strip() 再映射,map 之后如果发现某个标签不在 LABEL_MAP 里,会产生 NaN,dropna 会直接把这行过滤掉,相当于白名单校验。第三,MinMaxScaler 的 fit 对象必须只在训练集上做,测试集只能 transform 不能重新 fit,这正是第 4 章要展开的数据泄漏问题。

参数理解上,test_size=0.2 表示切 20% 出来当测试集,random_state=42 固定切分结果,stratify=y 保证五个大类的比例在训练集和测试集里与原始数据一致,这三项在论文或实验报告里都要写清楚。difficulty 列是 KDD99 自带的难度标记,对分类任务没有用处,直接忽略。

2.3 压缩包里的 train/test 目录与两档数据的选用顺序

从文件清单看,train 和 test 目录的存在,说明预处理后数据已经按目录分开存放。这里有一个关于「用哪份数据」的常见疑问:既然有全量 kddcup.data.gz,为什么不直接拿去训练?原因和课程设计场景有关——全量 489 万条跑传统基线没问题,但跑 CNN 时单轮 epoch 要很久,调参成本高;10% 子集已经保留了原始数据的类别分布,作为调参和对比实验样本完全够用。

我的建议顺序是:先用 kddcup.data_10_percent.gz 完成全部开发和调参,把 main.py 和 cnn_main.py 两个入口跑通,记录下这个子集上的指标;报告里可以加一小节说明「如使用全量数据,预计训练时间和内存开销会放大十倍左右」,如果时间和机器允许,再在提交前跑一次全量数据做终版数字。顺序反过来的话,每次改参数都要面对一次全量训练,血泪经验,调试效率会低很多。

3. 双模型入口:main.py 的传统基线、cnn_main.py 的 7×6 输入与训练记录

3.1 main.py:先用随机森林和逻辑回归把基线打出来

一份合格的入侵检测实验报告,通常要给出传统机器学习的基线成绩,再用深度学习去对比。main.py 承担的就是这个角色。我按源码目录还原的训练段大概是这样的:

from sklearn.ensemble import RandomForestClassifier from sklearn.linear_model import LogisticRegression from sklearn.metrics import classification_report from sklearn.model_selection import cross_val_score import numpy as np def train_baseline(X_train, y_train, X_test, y_test): rf = RandomForestClassifier( n_estimators=200, max_depth=30, min_samples_leaf=2, class_weight='balanced', n_jobs=-1, random_state=42 ) rf.fit(X_train, y_train) y_pred_rf = rf.predict(X_test) print('Random Forest') print(classification_report(y_test, y_pred_rf, target_names=['normal', 'dos', 'probe', 'r2l', 'u2r'])) lr = LogisticRegression(max_iter=500, class_weight='balanced', solver='lbfgs') lr.fit(X_train, y_train) y_pred_lr = lr.predict(X_test) print('Logistic Regression') print(classification_report(y_test, y_pred_lr, target_names=['normal', 'dos', 'probe', 'r2l', 'u2r'])) # 交叉验证分数,用于验证训练集上的稳定性 scores = cross_val_score(rf, X_train, y_train, cv=5, n_jobs=-1) print('RF CV acc:', np.mean(scores)) return rf, lr

对比实验做完,把 rf 和 lr 两个模型用 joblib.dump 存下来,后面推理脚本直接 load。交叉验证的 cv=5 表示五折,分数能看出模型在训练集上的波动幅度,如果五折分数差距超过一个百分点,通常说明数据切分或预处理还有不稳定因素。

class_weight='balanced' 是给少数类更高的权重,用来对抗 smurf、neptune 等多数类的压制;随机森林在 41 维特征上 n_estimators 取 200 左右性价比最高,再大收益很小;max_depth=30 是给深度一个约束,防止树在 49 万条数据上学过头。逻辑回归必须把 max_iter 调大,否则默认的 100 次迭代在 41 维特征、五分类目标下经常报 convergence warning,不是模型坏了,是没收敛完。

值得注意的一点:随机森林在这个数据集上几乎必然跑出 99% 以上的整体准确率,但整体准确率是骗人的。KDD99 里 smurf 和 neptune 两类攻击占了 10% 子集的绝大多数,如果只看 accuracy,模型把 r2l 和 u2r 全部判错也能拿到高分。所以我在 main.py 里刻意保留了对每个类别的 classification_report 输出,写报告时一定要贴这一份,不要只贴 accuracy。

3.2 cnn_main.py 与 main_cnn.py:为什么要把 41 维补成 42 再 reshape 成 7×6

CNN 做网络入侵检测的基本思路,是利用卷积核自动提取相邻特征的局部组合模式。问题在于 KDD99 的特征空间是一维的 41 维向量,而二维卷积要求 H×W×C 的输入。41 是素数,没法直接拆成两个整数相乘,工程上最省事的办法是补零到 42,再 reshape 成 7×6 的矩阵。源码里同时存在 cnn_main.py 和 main_cnn.py,合理的推断是两个版本分别尝试了不同的输入组织方式,你在复现时重点看 cnn_main.py 即可:

import numpy as np import tensorflow as tf from tensorflow.keras import layers, models def prepare_cnn_input(X): # 把 41 维补零到 42,再变成 (7, 6, 1) 的单通道图像 n = X.shape[0] x = np.zeros((n, 42), dtype=np.float32) x[:, :X.shape[1]] = X return x.reshape(-1, 7, 6, 1) def build_cnn(lr=1e-3): model = models.Sequential([ layers.Input(shape=(7, 6, 1)), layers.Conv2D(64, (3, 3), activation='relu', padding='same'), layers.MaxPooling2D((2, 2)), layers.Conv2D(32, (3, 3), activation='relu', padding='same'), layers.Flatten(), layers.Dense(128, activation='relu'), layers.Dropout(0.5), layers.Dense(5, activation='softmax') ]) model.compile( optimizer=tf.keras.optimizers.Adam(learning_rate=lr), loss='sparse_categorical_crossentropy', metrics=['accuracy'] ) return model model = build_cnn(lr=1e-3) history = model.fit( prepare_cnn_input(X_train), y_train, validation_data=(prepare_cnn_input(X_test), y_test), batch_size=64, epochs=30, verbose=1 )

参数说明:padding='same' 保证第一层卷积后 feature map 还是 7×6,池化后才变成 3×3,不至于过早缩小;Dropout 0.5 放在全连接 128 维之后,防止 49 万条样本把全连接层参数训过头;sparse_categorical_crossentropy 配合整数标签,y_train 不需要做 one-hot。learning_rate=1e-3 是 Adam 在这个数据集上的常见起点,loss 不降时往 3e-4 调。

如果不想补零,可以把模型换成 Conv1D,一维卷积直接吃 41 维向量,不需要 reshape,但局部感受野的含义就变成了沿着特征维滑动,可解释性比二维卷积弱一些。两种方案在 KDD99 上的最终指标差距通常在 1 个百分点以内,课程设计里选哪一种都不影响结论。

3.3 训练产出物的读法:events.out.tfevents 与 multi_logs 能告诉你什么

压缩包里那个 events.out.tfevents.1482980284.zjx-24000635 是 TensorFlow 的训练事件文件。1482980284 是 Unix 时间戳,换算回来是 2016 年 12 月底,也就是说这份源码最早跑在 TensorFlow 1.x 时代。multi_logs 目录里存的是各种训练日志。拿到源码之后,我建议你先别急着重训,先看事件文件里的 loss 曲线,确认之前的人跑到什么程度、有没有收敛,再决定是沿用还是改参:

# 有 TensorBoard 时直接启动 tensorboard --logdir=multi_logs --port=6006
# 不装 TensorBoard,用 Python 直接读 scalar from tensorflow.python.summary.summary_iterator import summary_iterator path = 'events.out.tfevents.1482980284.zjx-24000635' for event in summary_iterator(path): for value in event.summary.value: if value.tag in ('loss', 'accuracy'): print(event.step, value.tag, value.simple_value)

summary_iterator 是 TensorFlow 自带的事件文件解析接口,event.step 是训练轮次,value.simple_value 是对应指标值。你可以在命令行里把输出重定向到文件,再用 pandas 画 loss 曲线,不用打开 TensorBoard。这个技巧在课程设计里很实用——答辩老师如果问你「怎么证明模型收敛了」,直接贴一张从事件文件提取出来的 loss 曲线就很有说服力。

4. 复现避坑:数据泄漏、类别失衡与旧版 tfevents 的五个翻车现场

4.1 全量数据一读就卡死:先确认内存再动 kddcup.data.gz

现象:pd.read_csv('kddcup.data.gz', compression='gzip') 跑了几分钟还在转圈,或者内存占用飙到好几个 GB,只能强杀进程。

原因:kddcup.data.gz 解压后是约 489 万行、43 列的文本,按默认 dtype 全量读入,每个字段都要做类型推断,内存消耗远比你想象的大,而且在 gz 上随机访问本身就慢。

解决:课程设计和作业场景根本用不到全量数据。先用 kddcup.data_10_percent.gz(约 49 万条)调参,读的时候显式指定 dtype,避免 pandas 逐列推断:

dtypes = {i: np.float32 for i in range(41)} dtypes[41] = object dtypes[42] = object df = pd.read_csv('kddcup.data.gz', compression='gzip', header=None, dtype=dtypes)

前 41 列按 float32 读,后两列按字符串读,内存占用能降一半以上。如果确实想碰全量,用 chunksize 分批读取,先把 label 分布统计出来再决定要不要全量训练。我一般只在完整对比实验时才动全量文件,日常调参全部用 10% 子集。

4.2 准确率 99% 但 r2l、u2r 的 recall 是 0:类别不均衡的检查套路

现象:classification_report 里 dos 一行的 f1-score 接近 0.99,probe 也不错,但 r2l 和 u2r 的 recall、precision 都是 0.00,整体 accuracy 反而高达 99%。

原因:KDD99 10% 子集中,smurf 和 neptune 两类 dos 攻击占掉九成以上,r2l、u2r 的样本量加起来可能不到一千条。模型只要把所有样本都判成 dos,准确率就接近满分,少数类完全被吞掉。

解决:遇到这个现象,先看每类样本数,再看 confusion matrix,而不是看 accuracy。处理手法上有三件套:class_weight='balanced' 给少数类加权;训练测试切分时 stratify=y 保证少数类进测试集;必要时对少数类做 SMOTE 过采样。但 KDD99 的多数组边界重叠严重,SMOTE 容易生成噪声样本,我建议先做前两个,过采样最后再用。

4.3 切分前归一化导致测试集分数虚高:数据泄漏是隐藏的坑

现象:训练集和测试集准确率都高得离谱,换一个 random_state 结果波动很大,换模型后变化规律也不稳定。

原因:这是典型的归一化泄漏。如果在 train_test_split 之前就对整个 X 做 MinMaxScaler().fit_transform,scaler 的 min、max 是在包含测试集的全量数据上算出来的,等于测试集的信息提前进到了训练流程里,成绩虚高 2 到 5 个百分点。

解决:严格按「先切分,再 fit,再 transform」的顺序写:scaler = MinMaxScaler() 只用 X_train 拟合,X_test 用同一个 scaler 转换。LabelEncoder 同理,只允许在训练集上 fit,测试集拿到编码后直接 transform,否则测试集里新出现的类别会抛异常。复现这份源码时,我会先检查 handle2.py 里这两处顺序,而不是默认它没问题。

4.4 CNN 一跑就报维度错误:41 维不能直接进 Conv2D

现象:把 X_train 直接传给 model.fit,报 ValueError: Input 0 of layer conv2d is incompatible: expected ndim=4, found ndim=2。

原因:Conv2D 要求输入是 (batch, height, width, channels) 四维张量,KDD99 的一维特征既没有高宽结构,也没有 channel 维。41 是素数,直接 reshape 成行×列也凑不成整数。

解决:按 3.2 节的 prepare_cnn_input 处理,先补零到 42,reshape 成 (-1, 7, 6, 1),再进模型。报错时先打印 X_train.shape 和 model.input_shape 对一眼,八成是这里的尺寸没对齐。如果不想补零,把模型全部换成 Conv1D,一维卷积直接吃 41 维向量,不用 reshape,但可解释性比二维卷积弱一些。

4.5 老版 tfevents 在新环境打不开:事件文件的版本兼容处理

现象:用最新版 TensorFlow 的 summary_iterator 读 events.out.tfevents.1482980284.zjx-24000635 时,报 DataLossError,或者读出来没有 scalar 内容。

原因:事件文件是 2016 年底 TensorFlow 1.x 时代写入的,protobuf 版本和序列化格式与现在的 TensorFlow 2.x 不完全兼容,部分老文件还缺 version 字段,新解析器直接拒收。

解决:不要在新环境里硬啃这个文件。稳妥做法是装一个 tf 1.15 的独立虚拟环境专门读事件文件,或者直接看 multi_logs 里的日志文本,loss、acc 这些训练记录大概率已经落在了文本日志里。重训时用 SummaryWriter 输出一份新的 TF2 格式事件文件,覆盖原始记录,报告里展示新曲线更干净。

5. 最终的验证闭环:用切割好的测试集做推理、核对曲线与特征视图

5.1 用测试集跑最终推理,并把指标固化成一份报告

训练完成之后,模型到底行不行,要用训练时没见过的测试集跑一次 predict,再和 y_test 对指标。我一般会把这一步单独写成一个脚本,不放到训练脚本里,避免每次调参都重算推理:

from sklearn.metrics import classification_report, confusion_matrix import joblib, numpy as np X_test = np.load('X_test.npy') y_test = np.load('y_test.npy', allow_pickle=True) rf = joblib.load('rf_model.joblib') y_pred = rf.predict(X_test) print(classification_report(y_test, y_pred, target_names=['normal', 'dos', 'probe', 'r2l', 'u2r'])) print(confusion_matrix(y_test, y_pred))

classification_report 给出五分类各自的 precision、recall、f1-score,confusion_matrix 可以定位哪些攻击被误判成了哪一类。报告里这两样东西比 accuracy 单指标有说服力得多。注意 joblib.load 前要确认保存模型时用的是同一个 sklearn 版本,跨版本加载随机森林一般没问题,但 XGBoost 之类会有版本警告。

5.2 两条值得做的进阶验证:t-SNE 特征可视化和模型结构对比

如果作业要求再深一点,可以做两件事。第一,从测试集里随机采样几千条,用 t-SNE 把高维特征压到二维,按五分类标签着色,能直观看到 dos 和 normal 的分布几乎没有重叠,而 r2l、u2r 基本埋在 normal 区域里——这个图贴进报告,比说一百句「类别不均衡」都管用。第二,把 main.py 的随机森林和 cnn_main.py 的 CNN 在同一个测试集上的每类 f1-score 做成一张条形对比图,重点标注 u2r 这类少数类,结论自然就是「传统模型在少数类上有结构性劣势,CNN 也救不了样本量不足」。

从那以后我拿到任何课程设计源码,第一步永远是先解压看目录、读 README.md、拆事件文件和日志,确认前人训练到什么程度再动手改,而不是急着跑 main.py。这套流程帮我避免了很多次「跑通但说不出所以然」的尴尬。这份基于 python 机器学习的网络入侵检测系统源码的复现路线就是:先按 handle2.py 的映射和切分逻辑过一遍数据,再跑 main.py 出传统基线,最后用 cnn_main.py 对比,把每个步骤的输入、输出和指标都落实到报告里,希望帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/23 21:42:15

遥感舰船检测数据集实战:从VOC/YOLO格式到YOLO模型部署

简介:这份数据集面向遥感影像目标检测与舰船识别任务,提供Pascal VOC与YOLO两种通用标注格式,可直接用于训练和验证YOLO系列、SSD、Faster R-CNN等模型,适合高校实验室、算法工程师及竞赛选手。压缩包共2000个文件,含1…

作者头像 李华
网站建设 2026/9/23 21:40:33

PSO三参数联合优化RBF神经网络实战指南

简介:本资源是一个基于Python实现的PSO优化RBF神经网络的轻量级项目,面向机器学习初学者与算法实践者,聚焦于用粒子群优化算法自动调优径向基函数神经网络的关键参数(如中心、宽度、权值),提升非线性拟合与…

作者头像 李华
网站建设 2026/9/23 21:37:02

OpenClaw:跨越AI从聊天到执行的能力鸿沟

1. 从聊天到干活的AI进化论去年我在调试一个智能客服系统时,发现个有趣现象:当用户问"帮我查订单"时,AI能完美回答查询步骤,但当用户直接说"订单号XXXX,查物流"时,系统就卡壳了。这让我…

作者头像 李华