
简介这是一份面向高校学生与入门开发者的网络攻击检测实战项目资料基于经典的KDD-CUP99数据集用Python完成从数据预处理、特征分析到攻击类型识别的完整流程适合用作课程设计、期末大作业或网络安全方向的练手项目。压缩包共75个文件约6.93MB包含14个Python源码文件、10个编译缓存文件、17个JavaScript与7个Vue前端文件以及json、css、html等配置与页面资源另附sqlite3数据库、npy与pickle模型数据、data_10_percent_corrected数据集文件前后端结构清晰。项目采用Django后端搭配Vue前端目录中可见NetAnalyze、model、static等模块便于理解检测系统的整体架构与数据流转。目前已有351人学习下载源码经本地编译可运行难度适中内容经助教审定能帮助读者掌握数据集处理、模型训练与可视化展示的完整思路并可直接参考其目录组织与排错方式完成自己的项目。1. 从一份能跑通的 KDD-CUP99 网络攻击检测项目说起很多做安全方向课程设计或期末大作业的同学卡点从来不是没有想法而是想法有了数据也下了代码跑不起来。KDD-CUP99 这个数据集几乎是人手一份但真正把它从原始 txt 清洗成模型能吃的特征矩阵再套上一个能演示的前端界面中间要填的坑比想象中多。这份资源给的是一个完整闭环Python 侧做数据预处理和攻击分类建模Django 提供后端接口Vue 搭一个可视化分析页面目录里KDD-CUP99-Analysis-main、net-analyze、Django、model几个文件夹各司其职。它适合三类人赶期末大作业的学生、想快速搭一个入侵检测 demo 的初学者、以及需要一份可改可扩的基线代码的开发者。下面我按数据怎么进、模型怎么出、界面怎么连、坑在哪的顺序拆一遍。2. 数据管道拆解KDD-CUP99 从原始 txt 到特征矩阵2.1 先搞清楚这份数据集到底长什么样KDD-CUP99 的原始文件通常是kddcup.data_10_percent这类无表头的 CSV每行 41 个特征加 1 个标签标签形如normal.、smurf.、neptune.注意末尾带点。41 维里混了三种类型连续数值duration、src_bytes、离散符号protocol_type、service、flag、以及大量取值极不均衡的计数特征count、srv_count、dst_host_count。很多人一上来直接pd.read_csv然后丢进模型结果报could not convert string to float就是因为没处理符号列。常见做法是先把列名补上再做类型分离。下面这段是我一般会先跑的探查脚本import pandas as pd # KDD-CUP99 标准 41 维特征名 标签列 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 ] df pd.read_csv(kddcup.data_10_percent, namescol_names) print(df[label].value_counts().head(10)) # 先看类别分布别急着建模 print(df.dtypes.value_counts()) # 看有多少 object 列需要编码逻辑说明names参数把无表头文件补成有语义的列方便后续按列名操作。value_counts用来确认标签分布——KDD-CUP99 里smurf和neptune两类能占到七成以上这个不均衡会直接影响后面模型评估不能忽略。dtypes则告诉你哪些列是字符串需要单独编码。参数上要注意如果你用的是corrected版本标签种类会更多且部分攻击类型在训练集里没出现过这是官方故意设计的泛化测试别当成数据错误。2.2 符号列编码与标签归并protocol_type、service、flag三列是字符串必须转成数值。这里有两种路线LabelEncoder 或 One-Hot。service有 70 个左右取值One-Hot 会让维度暴涨我一般对protocol_type和flag用 One-Hot对service用频次编码或 LabelEncoder。标签侧则要把几十种具体攻击归并成四大类DoS、Probe、R2L、U2R外加 normal。from sklearn.preprocessing import LabelEncoder # 标签归并把具体攻击名映射到 5 大类 attack_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 } # 去掉标签末尾的点再映射 df[label] df[label].str.rstrip(.) df[attack_cat] df[label].map(attack_map) df df.dropna(subset[attack_cat]) # 丢掉映射表没覆盖的罕见类 # 符号特征编码 for col in [protocol_type, flag]: df[col] LabelEncoder().fit_transform(df[col]) df[service] df[service].map(df[service].value_counts()) # 频次编码逻辑说明rstrip(.)处理标签尾点这是 KDD-CUP99 最经典的坑之一不处理会导致map全部返回 NaN。attack_map把 20 多种细类压成 5 类既降低分类难度也符合多数课程设计对攻击类型识别的要求。service用频次编码而不是 LabelEncoder是因为它的类别没有天然顺序LabelEncoder 会引入虚假的大小关系。参数提醒如果你的任务要求识别具体攻击名而非大类就别做归并直接对label做 LabelEncoder但要注意评估时用 macro-F1 而不是 accuracy。2.3 数值归一化与训练集划分连续特征量纲差异极大src_bytes能到上亿serror_rate只在 0 到 1 之间。不归一化的话基于距离的模型KNN、SVM会被大数值特征主导。我一般用 MinMaxScaler 压到 [0,1]树模型则可以跳过这步。from sklearn.preprocessing import MinMaxScaler from sklearn.model_selection import train_test_split feature_cols [c for c in df.columns if c not in [label,attack_cat]] X df[feature_cols].astype(float) y df[attack_cat] scaler MinMaxScaler() X_scaled scaler.fit_transform(X) X_train, X_test, y_train, y_test train_test_split( X_scaled, y, test_size0.3, random_state42, stratifyy ) print(X_train.shape, X_test.shape)逻辑说明stratifyy保证训练集和测试集里各类比例一致否则样本极少的 U2R 类可能整个落进测试集导致训练时没见过。random_state42固定随机种子方便复现结果。astype(float)是为了防止某些列被 pandas 推断成 object 后 scaler 报错。注意scaler 只能在训练集上fit然后transform测试集否则会造成数据泄漏。上面为了简洁用了全量 fit严格做法是先切分再分别处理这一点在答辩时容易被追问。3. 模型训练与 Django 后端接口打通3.1 选哪个分类器从基线到可用课程设计里最常见的做法是先跑一个随机森林当基线再对比 SVM 或 XGBoost。随机森林对特征量纲不敏感、训练快、可解释性好能输出特征重要性非常适合 KDD-CUP99 这种混合类型数据。我一般会同时训练两三个模型做对比表。from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import classification_report, confusion_matrix rf RandomForestClassifier( n_estimators100, # 树的数量100 是速度与精度的平衡点 max_depthNone, # 不限制深度让树充分生长 class_weightbalanced, # 缓解类别不均衡 random_state42, n_jobs-1 ) rf.fit(X_train, y_train) y_pred rf.predict(X_test) print(classification_report(y_test, y_pred)) print(confusion_matrix(y_test, y_pred))逻辑说明class_weightbalanced是关键参数它按类别频率自动加权让模型不会因为 normal 样本多就无脑预测 normal。n_jobs-1用满所有 CPU 核。classification_report里重点看 U2R 和 R2L 的 recall这两类样本少最容易漏检。参数怎么调如果 recall 太低先加n_estimators到 200再考虑max_depth限制过拟合。如果训练集准确率 99% 但测试集掉到 90%多半是过拟合可以降深度或加min_samples_leaf。3.2 模型持久化与 Django 侧加载训练完的模型要存下来供后端调用常见做法是 joblib 或 pickle。Django 里我一般把模型文件放在model/目录在视图函数里加载一次并缓存避免每次请求都重新读盘。import joblib # 训练侧保存模型和 scaler joblib.dump(rf, model/rf_model.pkl) joblib.dump(scaler, model/scaler.pkl) # Django 侧views.py 中加载 import joblib import numpy as np from django.http import JsonResponse MODEL joblib.load(model/rf_model.pkl) SCALER joblib.load(model/scaler.pkl) def predict(request): if request.method POST: features request.POST.getlist(features) # 前端传来的 41 维 arr np.array(features, dtypefloat).reshape(1, -1) arr SCALER.transform(arr) pred MODEL.predict(arr)[0] return JsonResponse({attack_cat: pred})逻辑说明模型在模块加载时读一次放在全局变量里这是 Django 里最省资源的做法。reshape(1,-1)把单条样本变成二维数组因为 sklearn 的predict要求二维输入。getlist用于接收前端表单里的多个字段。参数提醒model/目录路径建议用os.path.join(BASE_DIR, model, ...)拼绝对路径否则换台机器跑就找不到文件。这是新手最常翻车的地方之一。3.3 前后端联调Vue 发请求到 Django前端在net-analyze目录是 Vue 项目通过 axios 调 Django 接口。跨域是必踩的坑Django 侧要装django-cors-headers并在 settings 里放行。# settings.py INSTALLED_APPS [ # ... corsheaders, ] MIDDLEWARE [ corsheaders.middleware.CorsMiddleware, # 必须放在 CommonMiddleware 之前 # ... ] CORS_ALLOW_ALL_ORIGINS True # 开发阶段图省事上线要收紧// Vue 侧调用 import axios from axios export function predictAttack(features) { return axios.post(http://127.0.0.1:8000/api/predict/, features) }逻辑说明CorsMiddleware的位置很关键放在CommonMiddleware之前才能正确给响应加跨域头。CORS_ALLOW_ALL_ORIGINS只适合本地开发正式部署要改成白名单。Vue 侧把请求封装成函数方便在组件里复用。注意Django 默认的 CSRF 保护会拦截 POST开发阶段可以在视图上加csrf_exempt但生产环境不要这么干。4. 避坑与排查这份源码最容易翻车的五个地方4.1 标签末尾的点没去掉映射全变 NaN现象跑attack_map之后df[attack_cat]全是 NaNdropna一执行数据全没了。原因KDD-CUP99 原始标签是normal.、smurf.这种带尾点的格式映射表的 key 写的是不带点的。解决在 map 之前先df[label] df[label].str.rstrip(.)或者映射表 key 直接带点。这个坑我见过太多人卡一整天。4.2 训练集测试集划分没加 stratify稀有类全丢现象模型对 U2R 类的 recall 是 0混淆矩阵里那一行全是 0。原因U2R 样本占比不到 0.1%随机划分时可能整个落进测试集训练集里一条都没有。解决train_test_split加stratifyy或者对稀有类做 SMOTE 过采样。加 stratify 是最省事的做法。4.3 scaler 在全量数据上 fit造成数据泄漏现象测试集准确率高得离谱换一批新数据就崩。原因归一化时用了包含测试集的全部数据来 fit测试集的信息泄漏进了训练过程。解决先切分再在X_train上fit_transform对X_test只transform。答辩时老师很爱问这个点。4.4 Django 找不到模型文件路径写成了相对路径现象本地跑得好好的换台电脑或换个启动目录就报FileNotFoundError。原因joblib.load(model/rf_model.pkl)是相对当前工作目录的Django 启动目录一变就失效。解决用os.path.join(settings.BASE_DIR, model, rf_model.pkl)拼绝对路径。这是部署环节最典型的血泪经验。4.5 跨域请求被拦前端一直报 CORS 错误现象浏览器控制台报has been blocked by CORS policy接口明明能 curl 通。原因Django 没配django-cors-headers或者中间件顺序放错了。解决装包、加corsheaders到 INSTALLED_APPS、把CorsMiddleware放到CommonMiddleware之前、设置允许的来源。三步缺一不可。5. 进阶玩法把检测结果做成可视化大屏并验证模型边界5.1 用混淆矩阵和特征重要性做结果验证光看 accuracy 没有说服力KDD-CUP99 上随便一个模型都能到 99%因为 normal 和 DoS 占了绝大多数。真正能体现水平的是分类报告里的 macro-F1 和各类 recall。我一般会在项目里加一个可视化页面把混淆矩阵和特征重要性画出来。import matplotlib.pyplot as plt import seaborn as sns from sklearn.metrics import confusion_matrix cm confusion_matrix(y_test, y_pred, labelsrf.classes_) plt.figure(figsize(8, 6)) sns.heatmap(cm, annotTrue, fmtd, xticklabelsrf.classes_, yticklabelsrf.classes_) plt.xlabel(Predicted) plt.ylabel(True) plt.title(Confusion Matrix) plt.savefig(static/confusion_matrix.png) # 特征重要性 Top 10 import pandas as pd imp pd.Series(rf.feature_importances_, indexfeature_cols).sort_values(ascendingFalse) print(imp.head(10))逻辑说明labelsrf.classes_保证混淆矩阵的行列顺序和模型类别一致不然标签会对错位。热力图存成图片后可以被 Django 模板直接引用前端展示时不用实时计算。特征重要性排前几的通常是src_bytes、dst_bytes、count、same_srv_rate这几个和领域经验吻合可以拿来在报告里佐证模型学到了合理的东西。参数提醒fmtd表示显示整数如果做了归一化混淆矩阵就改成.2f。annotTrue把数值标在格子里样本多的时候可能挤可以调figsize。5.2 用 corrected 数据集测泛化边界KDD-CUP99 有个corrected版本里面包含训练集没出现过的攻击类型。拿它当测试集能看出模型是真的学到了攻击模式还是只是记住了训练集里的类别。我一般会做这个对比实验测试集准确率macro-F1说明10_percent 同分布测试0.990.95常规指标参考价值有限corrected 全集0.90 左右0.70 左右泛化能力真实体现corrected 中未知攻击明显下降明显下降暴露模型边界这张表放进报告里比单纯写准确率 99%有说服力得多。如果 corrected 上掉得特别厉害说明模型过拟合了训练集的攻击类型可以考虑加正则化或换更强的特征工程。5.3 一个我踩过的坑别把测试集当验证集反复调参刚做这个项目时我拿测试集反复试不同的n_estimators和max_depth挑了个测试集上最高的组合结果答辩时被问你怎么保证这个参数不是过拟合测试集当场答不上来。正确做法是从训练集里再切一份验证集或者用交叉验证选参数测试集只在最后评估时用一次。从那以后我每次调参都强制走一遍cross_val_score再也没在这个问题上翻过车。from sklearn.model_selection import cross_val_score scores cross_val_score(rf, X_train, y_train, cv5, scoringf1_macro) print(CV macro-F1:, scores.mean(), scores.std())逻辑说明cv5做五折交叉验证scoringf1_macro对不均衡数据比 accuracy 更合适。scores.std()看稳定性标准差大说明模型对数据划分敏感需要再调。这份资源的完整源码和资料都在压缩包里Django 后端、Vue 前端、模型文件、数据集处理脚本一应俱全拿到手按上面的顺序跑一遍就能复现。希望帮到你。本文还有配套的精品资源点击获取