Python网络入侵检测系统源码实现与数据集分析:从NSL-KDD到Flask可视化

发布时间:2026/10/3 13:46:36
Python网络入侵检测系统源码实现与数据集分析:从NSL-KDD到Flask可视化 简介这是一套面向高校计算机、软件工程、信息安全等专业学生的网络入侵检测系统毕业设计完整资料包含可运行的源码实现、标准化训练数据集与配套技术文档适合作为本科毕设、研究生课程实践或网络安全入门项目的参考案例。资源包共48个文件约16.84MB以C语言源码、HTML页面、PDF技术文档、gz压缩包及备份文件为主涵盖数据采集、特征处理、异常检测核心算法与可视化报告等模块并附有协同开发指南与系统说明文档。其中PDF文档详细讲解了libpcap、libnids、Snort等工具的入门与源码分析源码部分配有清晰注释便于理解检测流程与算法原理。目前已有72人学习下载读者可借助该资料快速搭建实验环境、复现检测流程、调整算法参数或扩展特征提取模块也可作为开发网络安全系统的基础框架用于功能增强与性能评估。1. 从一份 KDD 数据到能跑的 NIDS这套 Python 源码到底解决什么问题很多人做网络入侵检测系统NIDS的毕业设计卡住的地方根本不是算法而是数据。NSL-KDD 或 CIC-IDS 数据集下载下来几十万条记录、几十个特征标签还是字符串直接扔给模型跑不动。更麻烦的是导师问「你这个系统到底怎么判断一条流量是攻击」你只能回答「模型输出的」。这套基于 Python 的网络入侵检测系统源码实现与数据集分析核心要解决的就是把原始流量特征变成可训练、可解释、可复现的检测流水线。它适合两类人一是正在做计算机毕业设计、需要一套能讲清楚原理又能跑出结果的同学二是刚接触安全数据分析、想用 Python 把 NIDS 从概念落到代码的工程师。整条链路不依赖冷门框架用 pandas、scikit-learn 加一个轻量级 Web 界面就能闭环重点在于数据清洗、特征工程和模型评估这三个环节的细节处理。2. 数据集分析与预处理NSL-KDD 的 41 个特征怎么变成模型能吃的矩阵2.1 先搞清楚 NSL-KDD 里每一列代表什么NSL-KDD 是 KDD Cup 99 的改进版去掉了冗余记录训练集 125973 条测试集 22544 条。每条记录 41 个特征加 1 个标签列标签分正常和 4 大类攻击DoS、Probe、R2L、U2R。特征分三种类型连续型如 duration、src_bytes、离散型如 protocol_type、service、flag、二元型如 logged_in。很多人直接对离散列做独热编码结果维度爆炸到 120 多维训练慢还容易过拟合。我一般会先看基数protocol_type 只有 3 个值flag 有 11 个service 有 70 个左右。service 这一列独热之后占了大头实际做的时候可以按频率把低频 service 归为 other能压到 20 维以内。import pandas as pd # 列名按 NSL-KDD 官方定义 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,difficulty ] train pd.read_csv(KDDTrain.txt, namescol_names) test pd.read_csv(KDDTest.txt, namescol_names) # 把具体攻击名映射到 5 大类 attack_map { normal: normal, back:DoS,land:DoS,neptune:DoS,pod:DoS,smurf:DoS, teardrop:DoS,mailbomb:DoS,apache2:DoS,processtable:DoS, udpstorm:DoS,worm:DoS, satan:Probe,ipsweep:Probe,nmap:Probe,portsweep:Probe, mscan:Probe,saint:Probe, guess_passwd:R2L,ftp_write:R2L,imap:R2L,phf:R2L, multihop:R2L,warezmaster:R2L,warezclient:R2L,spy:R2L, xlock:R2L,xsnoop:R2L,snmpguess:R2L,snmpgetattack:R2L, httptunnel:R2L,sendmail:R2L,named:R2L, buffer_overflow:U2R,loadmodule:U2R,rootkit:U2R, perl:U2R,sqlattack:U2R,xterm:U2R,ps:U2R } train[label] train[label].map(attack_map) test[label] test[label].map(attack_map)这段代码做了两件事按官方列名读入数据把 39 种具体攻击名归到 5 个大类。注意difficulty列是 NSL-KDD 自带的难度分数训练时直接丢掉它跟标签有隐含关联留着会造成数据泄露。num_outbound_cmds这一列在 NSL-KDD 里全是 0方差为零也应该删掉。2.2 离散特征编码与数值归一化的取舍离散列处理有两种常见做法独热编码和标签编码。独热编码适合基数小的列比如 protocol_type 和 flagservice 基数大独热后维度太高我一般用频率编码或者目标编码。但目标编码在毕业设计里容易讲不清楚稳妥起见还是独热加低频合并。数值列的量纲差异很大src_bytes 可能到几亿serror_rate 只在 0 到 1 之间必须做归一化。MinMaxScaler 和 StandardScaler 都行但 NSL-KDD 里不少特征长尾分布严重StandardScaler 假设正态分布效果不一定好。我实测下来 MinMaxScaler 在这个数据集上更稳尤其是树模型对归一化不敏感但神经网络必须做。from sklearn.preprocessing import MinMaxScaler # 删除无用列 drop_cols [difficulty, num_outbound_cmds] train.drop(columnsdrop_cols, inplaceTrue) test.drop(columnsdrop_cols, inplaceTrue) # 分离特征和标签 X_train train.drop(columns[label]) y_train train[label] X_test test.drop(columns[label]) y_test test[label] # 离散列独热编码service 低频合并 cat_cols [protocol_type, service, flag] for col in cat_cols: freq X_train[col].value_counts(normalizeTrue) low_freq freq[freq 0.01].index X_train[col] X_train[col].replace(low_freq, other) X_test[col] X_test[col].replace(low_freq, other) X_train pd.get_dummies(X_train, columnscat_cols) X_test pd.get_dummies(X_test, columnscat_cols) # 对齐列测试集可能少某些 dummy 列 X_train, X_test X_train.align(X_test, joinleft, axis1, fill_value0) # 数值归一化 scaler MinMaxScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test)这里有个关键点align那一步必须做。训练集和测试集的 service 取值不完全一致独热之后列数可能不同不对齐直接报错。归一化只能在训练集上 fit测试集用同样的 scaler 做 transform否则就是数据泄露。很多人在这里翻车测试集准确率虚高答辩时被问一句「你的归一化参数从哪来的」就露馅了。3. 检测模型选型与训练从随机森林到 XGBoost 的落地对比3.1 为什么我优先推荐树模型而不是深度学习NSL-KDD 只有 12 万条训练数据特征 120 维左右这个规模下深度学习没有优势。我试过用三层全连接网络准确率跟随机森林差不多但训练时间长十倍调参还麻烦。树模型有三个好处训练快、可解释性强、对特征缩放不敏感。随机森林和 XGBoost 在这个数据集上都能做到 99% 以上的训练准确率测试集上大概 80% 到 85%差距主要来自 R2L 和 U2R 这两类样本太少。U2R 在训练集里只有 52 条测试集里更少任何模型都很难学好。毕业设计里如果导师要求每一类都好看可以试试 SMOTE 过采样但要注意过采样之后测试集不能动否则评估结果不可信。from sklearn.ensemble import RandomForestClassifier from xgboost import XGBClassifier from sklearn.metrics import classification_report, confusion_matrix # 随机森林基线 rf RandomForestClassifier( n_estimators100, max_depth20, min_samples_split5, class_weightbalanced, random_state42, n_jobs-1 ) rf.fit(X_train_scaled, y_train) y_pred_rf rf.predict(X_test_scaled) print(Random Forest:) print(classification_report(y_test, y_pred_rf)) # XGBoost 对比 xgb XGBClassifier( n_estimators200, max_depth6, learning_rate0.1, subsample0.8, colsample_bytree0.8, objectivemulti:softmax, num_class5, random_state42, n_jobs-1 ) xgb.fit(X_train_scaled, y_train) y_pred_xgb xgb.predict(X_test_scaled) print(XGBoost:) print(classification_report(y_test, y_pred_xgb))class_weightbalanced这个参数很关键NSL-KDD 类别极度不平衡normal 占一半以上不加这个参数模型会偏向多数类。XGBoost 的subsample和colsample_bytree控制行采样和列采样比例防止过拟合。max_depth6是经验值再深容易记住训练集噪声。两个模型跑完对比一下随机森林在 U2R 上召回率通常更高XGBoost 在 DoS 和 Probe 上更稳。毕业设计里可以两个都跑用表格对比显得工作量大。3.2 模型评估不能只看准确率准确率在类别不平衡场景下会骗人。假设测试集里 80% 是 normal模型全预测 normal 也有 80% 准确率但攻击一个没抓到。必须看每个类别的 precision、recall、f1-score尤其是 R2L 和 U2R 的召回率。混淆矩阵能直观看到哪两类容易混。我一般还会算一个宏平均 F1把所有类别同等对待比加权平均更能反映模型对少数类的检测能力。import matplotlib.pyplot as plt import seaborn as sns from sklearn.metrics import f1_score # 宏平均 F1 macro_f1 f1_score(y_test, y_pred_rf, averagemacro) print(fMacro F1: {macro_f1:.4f}) # 混淆矩阵可视化 cm confusion_matrix(y_test, y_pred_rf, labels[normal,DoS,Probe,R2L,U2R]) 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(Actual) plt.title(Confusion Matrix - Random Forest) plt.tight_layout() plt.savefig(confusion_matrix.png, dpi150)混淆矩阵里如果 R2L 大量被预测成 normal说明模型对这类攻击的特征不敏感。R2L 攻击在 NSL-KDD 里主要是猜测密码、FTP 写入这类行为跟正常流量的统计特征重叠度高。解决办法有两个一是加特征比如登录失败次数、文件创建次数这些列单独拎出来做特征交叉二是用代价敏感学习给 R2L 和 U2R 更高的误分类代价。毕业设计里做到这一步答辩时就有东西讲了。4. 避坑与排查数据泄露、编码错位和评估陷阱4.1 归一化在划分训练测试之前做测试集准确率虚高现象测试集准确率 99%换一批数据掉到 60%。原因先对整个数据集做归一化再划分训练测试测试集的统计信息泄露到了训练过程。解决先train_test_split再在训练集上fitscaler测试集只做transform。如果用的是官方 NSL-KDD 的 KDDTrain 和 KDDTest本身已经分好了直接按上面代码做就行。4.2 独热编码后训练集和测试集列数不一致现象X_test传给模型报错提示特征数量不匹配。原因训练集里出现过的 service 值测试集里没有或者反过来。解决用pd.get_dummies之后调align把两个 DataFrame 的列对齐缺失的列填 0。更稳妥的做法是用sklearn的OneHotEncoder(handle_unknownignore)但要注意它返回的是稀疏矩阵跟 DataFrame 混用需要转换。4.3 把 difficulty 列当特征喂给模型现象模型准确率异常高但实际部署时完全不能用。原因NSL-KDD 的 difficulty 列是官方根据记录被误分类次数统计出来的跟标签强相关留着就是标签泄露。解决读数据时直接 drop 掉别犹豫。同理任何在数据集构建阶段用标签信息生成的列都不能作为特征。4.4 用 accuracy 作为唯一评估指标现象模型准确率 85%但 R2L 和 U2R 的召回率不到 10%。原因类别不平衡多数类主导了准确率。解决看classification_report重点关注少数类的 recall 和 f1-score。如果导师只看准确率可以额外算一个宏平均 F1 和加权平均 F1 一起放上去说明模型在少数类上的表现。4.5 过采样只对训练集做测试集保持原始分布现象用了 SMOTE 之后测试集准确率暴涨但实际场景效果差。原因对测试集也做了过采样测试集的分布被人为改变了评估结果没有意义。解决fit_resample只在训练集上调用测试集一条都不动。评估时用原始测试集才能反映模型在真实不平衡数据上的表现。5. 从脚本到系统用 Flask 包一层检测接口和可视化面板5.1 最小可用的预测接口毕业设计如果只交一个 Jupyter Notebook工作量显得单薄。用 Flask 包一个预测接口前端传一条流量记录后端返回预测类别和置信度整个系统就完整了。模型训练完之后用joblib保存Flask 启动时加载避免每次请求都重新训练。import joblib from flask import Flask, request, jsonify import numpy as np app Flask(__name__) model joblib.load(rf_model.pkl) scaler joblib.load(scaler.pkl) feature_columns joblib.load(feature_columns.pkl) app.route(/predict, methods[POST]) def predict(): data request.get_json() # data 是一个字典key 是特征名 df pd.DataFrame([data]) # 确保列顺序和训练时一致 df df.reindex(columnsfeature_columns, fill_value0) scaled scaler.transform(df) pred model.predict(scaled)[0] proba model.predict_proba(scaled).max() return jsonify({label: pred, confidence: round(float(proba), 4)}) if __name__ __main__: app.run(host0.0.0.0, port5000, debugFalse)reindex那一步是保证输入特征顺序和训练时一致缺的列填 0。predict_proba返回最大概率作为置信度前端可以拿这个值做阈值过滤低于 0.7 的标记为「疑似」而不是直接告警。生产环境别开debugTrue有安全风险。5.2 可视化面板要展示什么前端不用太复杂一个表格展示最近预测记录一个饼图展示攻击类型分布一个折线图展示准确率变化就够了。数据可以存 SQLite每次预测插入一条。面板用 ECharts 或 Chart.js 都行重点是让导师一眼看到系统在跑。如果时间紧用 Streamlit 也能快速搭一个但 Streamlit 的交互定制性差一些看个人取舍。模块技术选型说明数据预处理pandas scikit-learn独热编码、归一化、低频合并模型训练RandomForest / XGBoost树模型优先可解释性强模型持久化joblib保存模型和 scaler后端接口Flask提供 /predict 接口前端展示ECharts HTML表格、饼图、折线图数据存储SQLite预测记录落库这套组合在毕业设计里足够用部署也简单一台普通笔记本就能跑。如果导师要求「大数据」可以把 SQLite 换成 MySQLFlask 换成 FastAPI但核心逻辑不变。6. 让检测结果可解释特征重要性与单条流量的归因分析模型跑出 85% 准确率只是及格线答辩时老师更关心「为什么这条流量被判定为攻击」。随机森林自带feature_importances_可以直接看哪些特征贡献最大。我一般会取前 15 个特征画条形图通常src_bytes、dst_bytes、count、srv_count、same_srv_rate这几个排前面。但全局重要性不等于单条预测的解释要定位具体一条记录为什么被分类成 Probe得用 SHAP 或者 LIME。SHAP 在树模型上算得快TreeExplainer几行代码就能出图。import shap explainer shap.TreeExplainer(rf) # 取测试集里第一条 Probe 攻击样本 probe_idx y_test[y_test Probe].index[0] sample X_test_scaled[probe_idx:probe_idx1] shap_values explainer.shap_values(sample) # 输出每个特征对 Probe 类别的贡献 probe_class_idx list(rf.classes_).index(Probe) shap_df pd.DataFrame({ feature: X_train.columns, shap_value: shap_values[probe_class_idx][0] }).sort_values(shap_value, keyabs, ascendingFalse) print(shap_df.head(10))shap_values返回的是一个列表每个类别一个数组。取Probe对应的索引看哪些特征把预测推向 Probe。如果count和srv_count的 SHAP 值很高说明这条流量在短时间内有大量连接符合端口扫描的行为模式。把这个分析写进毕业设计比单纯贴准确率有说服力得多。还有一个技巧把特征重要性跟领域知识对照。比如serror_rate高说明 SYN 错误多可能是 SYN Floodnum_failed_logins高说明有人在暴力破解。把这些映射关系整理成一张表放在论文的「结果分析」章节导师会觉得你真的理解了数据而不是调包跑了个模型。最后说个血泪经验别等到答辩前一周才开始跑模型。NSL-KDD 虽然不大但独热编码加 XGBoost 调参来回试几轮一天就没了。提前把预处理脚本固化下来保存成.pkl文件后面换模型只动训练那一段。还有随机森林的random_state一定要固定不然每次跑出来的结果都不一样论文里的数字对不上就尴尬了。希望帮到你。本文还有配套的精品资源点击获取