简介这份资源面向计算机、网络安全相关专业的本科生与研究生用于课程设计、期末大作业或入门级科研实践核心是借助NSL-KDD数据集训练网络入侵检测模型并分别在KDDCup与NSL-KDD两个数据集上完成模型评估帮助读者理解入侵检测的完整流程与跨数据集泛化验证思路。压缩包共27个文件约29.98MB以csv数据文件、txt说明、ipynb实验笔记、m脚本及py代码为主另附md与docx文档覆盖数据处理、建模、评估等环节代码注释较为完整新手也能对照理解。目前已有403人学习下载。资源提供从数据读取、特征处理到PCA降维与无PCA对比建模的多套实验脚本并给出KDDCup与NSL-KDD的评估流程便于直接部署运行、复现结果也可作为报告撰写与答辩展示的参考素材具备较高的实际应用与学习价值。1. 从 NSL-KDD 到 KDDCup一个入侵检测大作业的完整落地路径很多同学做网络入侵检测大作业时第一反应是找个现成模型跑一遍 NSL-KDD 就交差结果答辩被问「KDDCup 和 NSL-KDD 的评估结果差异怎么解释」直接卡壳。这个标题真正要解决的不是「训一个模型」而是用 NSL-KDD 训练、用两个数据集交叉评估并说清楚模型评估与选择背后的逻辑。NSL-KDD 是 KDDCup99 的去冗余修订版训练集 125973 条、测试集 22544 条类别分布更均衡但两个数据集的特征空间和攻击类型覆盖并不完全一致。适合谁正在做网络安全、机器学习课程大作业需要一套能跑通、能解释、能写进报告的全流程方案的人。下面按「数据理解 → 模型训练 → 双数据集评估 → 避坑 → 进阶技巧」推进。2. NSL-KDD 与 KDDCup 的数据结构差异先搞懂再动手2.1 两个数据集到底差在哪NSL-KDD 和 KDDCup99 都是 41 维特征加 1 个标签特征分四类TCP 连接基本特征如 duration、protocol_type、内容特征如 num_failed_logins、基于时间的流量特征如 count、srv_count、基于主机的流量特征如 dst_host_count。标签分五大类Normal、DoS、Probe、R2L、U2R。关键差异在于KDDCup99 训练集有约 490 万条记录冗余度极高同一类攻击大量重复导致模型在测试集上虚高NSL-KDD 去掉了重复记录训练集降到 12 万条左右测试集里的攻击类型还刻意加入了训练集未出现的变种。这意味着在 NSL-KDD 上训练、在 KDDCup 上评估时不能假设标签体系完全对齐——KDDCup 原始标签有 23 种细分类别需要先映射到五大类才能和 NSL-KDD 的评估口径一致。常见做法是读入 KDDCup 的kddcup.data_10_percent或完整版把back、land、neptune、pod、smurf、teardrop等归为 DoSipsweep、nmap、portsweep、satan归为 Probeftp_write、guess_passwd、imap、multihop、phf、spy、warezclient、warezmaster归为 R2Lbuffer_overflow、loadmodule、perl、rootkit归为 U2Rnormal保持 Normal。这个映射表必须写死在代码里否则评估结果没法对齐。2.2 数据加载与标签映射的最小代码import pandas as pd import numpy as np # NSL-KDD 列名41 特征 label difficulty nsl_columns [ 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 ] # 读取 NSL-KDD train_df pd.read_csv(KDDTrain.txt, namesnsl_columns) test_df pd.read_csv(KDDTest.txt, namesnsl_columns) # KDDCup 标签映射到五大类 kddcup_map { normal: Normal, back: DoS, land: DoS, neptune: DoS, pod: DoS, smurf: DoS, teardrop: DoS, apache2: DoS, udpstorm: DoS, processtable: DoS, worm: DoS, ipsweep: Probe, nmap: Probe, portsweep: Probe, satan: Probe, mscan: Probe, saint: Probe, ftp_write: R2L, guess_passwd: R2L, imap: R2L, multihop: R2L, phf: R2L, spy: R2L, warezclient: R2L, warezmaster: R2L, sendmail: R2L, named: R2L, snmpgetattack: R2L, snmpguess: R2L, xlock: R2L, xsnoop: R2L, buffer_overflow: U2R, loadmodule: U2R, perl: U2R, rootkit: U2R, httptunnel: U2R, ps: U2R, sqlattack: U2R, xterm: U2R } def map_label(label): label label.strip().rstrip(.) return kddcup_map.get(label, Unknown) # 对 NSL-KDD 也做同样映射NSL-KDD 标签已是五大类但部分带子类名 def map_nsl_label(label): label label.strip() if label normal: return Normal # NSL-KDD 训练集标签直接是五大类测试集可能带子类 for k, v in kddcup_map.items(): if label k: return v return label # 已是 DoS/Probe/R2L/U2R这段代码的核心是统一标签口径。nsl_columns必须和文件实际列数对齐NSL-KDD 的KDDTrain.txt是 43 列41 特征 label difficulty少写一列后面全错位。kddcup_map覆盖了 KDDCup99 全部 23 种攻击加 normal映射时先strip().rstrip(.)去掉末尾句点因为 KDDCup 原始文件标签带点号。参数上difficulty列在建模时要 drop 掉它只是难度分级不是特征。3. 用 NSL-KDD 训练入侵检测模型从特征工程到模型选型3.1 特征编码与归一化的具体做法41 维里有 3 个分类特征protocol_typetcp/udp/icmp、service70 种左右、flag11 种。常见做法是 One-Hot 编码但service维度高直接 One-Hot 会让特征维度膨胀到 120 以上。我一般用pd.get_dummies对三个分类列一起处理然后对齐训练集和测试集的列——测试集里可能出现训练集没有的 service 类别这时要保证列对齐缺失的补 0。数值特征量纲差异大src_bytes可能到上亿duration可能只有 0 或 1。用StandardScaler做 z-score 归一化注意 scaler 只能在训练集上 fit然后 transform 测试集和 KDDCup 数据否则数据泄露。from sklearn.preprocessing import StandardScaler from sklearn.model_selection import train_test_split # 合并训练和测试做统一编码再切分仅用于编码对齐不用于训练 full_df pd.concat([train_df, test_df], axis0, ignore_indexTrue) full_df[label] full_df[label].apply(map_nsl_label) # One-Hot 编码分类特征 cat_cols [protocol_type, service, flag] full_encoded pd.get_dummies(full_df, columnscat_cols, prefixcat_cols) # 切回训练和测试 train_encoded full_encoded.iloc[:len(train_df)].copy() test_encoded full_encoded.iloc[len(train_df):].copy() # 特征列 去掉 label 和 difficulty feature_cols [c for c in train_encoded.columns if c not in [label, difficulty]] # 归一化 scaler StandardScaler() X_train scaler.fit_transform(train_encoded[feature_cols]) X_test scaler.transform(test_encoded[feature_cols]) y_train train_encoded[label].values y_test test_encoded[label].values # 标签编码 from sklearn.preprocessing import LabelEncoder le LabelEncoder() y_train_enc le.fit_transform(y_train) y_test_enc le.transform(y_test)这里有个关键点pd.get_dummies在合并数据上做保证训练集和测试集列完全一致。如果分开做测试集多一个 service 类别就会导致列数不同模型直接报错。StandardScaler的 fit 只在训练集上这是铁律。LabelEncoder的 fit 也只在训练集标签上测试集如果出现训练集没有的标签会抛异常——NSL-KDD 测试集确实有训练集未出现的攻击子类但映射到五大类后通常不会超出如果超出需要手动处理。3.2 模型选型随机森林、XGBoost 还是深度学习大作业场景下我推荐随机森林作为 baselineXGBoost 作为提升。原因NSL-KDD 是表格数据树模型天然适合训练快、可解释性强能输出特征重要性答辩时好讲。深度学习MLP、CNN、RNN在 NSL-KDD 上未必比 XGBoost 好而且调参成本高大作业时间有限。随机森林的关键参数n_estimators100起步max_depth不设或设 20 左右防止过拟合class_weightbalanced处理类别不平衡U2R 和 R2L 样本极少。XGBoost 用multi:softmax做五分类eval_metricmloglossearly_stopping_rounds防止过拟合。from sklearn.ensemble import RandomForestClassifier from xgboost import XGBClassifier from sklearn.metrics import classification_report, confusion_matrix, accuracy_score # 随机森林 rf RandomForestClassifier( n_estimators100, max_depth20, class_weightbalanced, random_state42, n_jobs-1 ) rf.fit(X_train, y_train_enc) rf_pred rf.predict(X_test) print(Random Forest on NSL-KDD Test:) print(classification_report(y_test_enc, rf_pred, target_namesle.classes_)) # XGBoost xgb XGBClassifier( n_estimators200, max_depth6, learning_rate0.1, objectivemulti:softmax, num_class5, eval_metricmlogloss, early_stopping_rounds20, random_state42 ) xgb.fit(X_train, y_train_enc, eval_set[(X_test, y_test_enc)], verboseFalse) xgb_pred xgb.predict(X_test) print(XGBoost on NSL-KDD Test:) print(classification_report(y_test_enc, xgb_pred, target_namesle.classes_))class_weightbalanced对随机森林很重要因为 Normal 占约 53%U2R 只有几十条不加权重模型会完全忽略 U2R。XGBoost 的early_stopping_rounds需要eval_set这里用测试集做早停其实有轻微泄露严格做法是从训练集再切一个验证集。大作业里如果追求严谨可以train_test_split切 10% 做验证。4. 用 KDDCup 和 NSL-KDD 做交叉评估指标、口径与结果解读4.1 评估指标不能只看准确率入侵检测是不平衡分类准确率会被 Normal 主导。必须看每类的 precision、recall、F1尤其是 U2R 和 R2L 的 recall。另外安全场景下**误报率False Positive Rate和漏报率False Negative Rate**比准确率更重要。常见做法是输出混淆矩阵再算 macro-F1 和 weighted-F1。在 NSL-KDD 测试集上随机森林通常能到 75%~80% 准确率XGBoost 略高。但注意NSL-KDD 测试集里有很多训练集未出现的攻击变种所以 recall 不会太高这是数据集设计使然不是模型不行。在 KDDCup 上评估时因为 KDDCup 冗余度高、攻击类型和训练集重叠大准确率往往能到 99% 以上——这个差异必须在报告里解释清楚否则会被质疑「为什么两个数据集差这么多」。4.2 在 KDDCup 上评估的完整流程# 读取 KDDCup 数据以 10% 版本为例 kddcup_columns nsl_columns[:-1] # 去掉 difficultyKDDCup 没有这一列 kddcup_df pd.read_csv(kddcup.data_10_percent, nameskddcup_columns) # 标签映射 kddcup_df[label] kddcup_df[label].apply(map_label) # 用训练好的编码器和 scaler 做同样的预处理 kddcup_encoded pd.get_dummies(kddcup_df, columnscat_cols, prefixcat_cols) # 对齐列缺失的补 0多余的丢掉 for col in feature_cols: if col not in kddcup_encoded.columns: kddcup_encoded[col] 0 kddcup_encoded kddcup_encoded[feature_cols] X_kddcup scaler.transform(kddcup_encoded) y_kddcup le.transform(kddcup_df[label].values) # 用 NSL-KDD 训练的模型预测 KDDCup kddcup_pred rf.predict(X_kddcup) print(Random Forest on KDDCup:) print(classification_report(y_kddcup, kddcup_pred, target_namesle.classes_)) # 交叉评估汇总表 results pd.DataFrame({ Dataset: [NSL-KDD Test, KDDCup 10%], Accuracy: [accuracy_score(y_test_enc, rf_pred), accuracy_score(y_kddcup, kddcup_pred)], Macro-F1: [ classification_report(y_test_enc, rf_pred, output_dictTrue)[macro avg][f1-score], classification_report(y_kddcup, kddcup_pred, output_dictTrue)[macro avg][f1-score] ] }) print(results)关键在列对齐feature_cols是从 NSL-KDD 训练集来的KDDCup 经过 One-Hot 后列名可能不完全一致必须用reindex或手动补 0 对齐。scaler和le都复用 NSL-KDD 训练时的对象不能重新 fit。le.transform如果遇到未知标签会报错KDDCup 映射后如果出现 Unknown需要先过滤或单独处理。评估结果解读NSL-KDD 上 macro-F1 通常 0.5~0.6因为 U2R/R2L 召回低KDDCup 上 macro-F1 能到 0.9 以上。这个差距说明模型对已知攻击模式识别好对未知变种泛化差这正是入侵检测的核心挑战也是报告里可以展开讨论的点。5. 避坑与排查双数据集评估最容易翻车的 5 个地方5.1 现象KDDCup 评估准确率 99%NSL-KDD 只有 75%被质疑模型造假原因KDDCup 冗余度高测试集和训练集攻击模式高度重叠模型只是记住了模式NSL-KDD 去冗余且测试集含未知攻击准确率自然低。解决在报告里明确说明两个数据集的构造差异把 KDDCup 的高分解释为「对已知攻击的检测能力」NSL-KDD 的低分解释为「对未知变种的泛化能力」并给出两类 recall 对比。5.2 现象One-Hot 后训练集和测试集列数不一致模型 predict 报错原因分开做get_dummies测试集多出训练集没有的 service 类别。解决合并后统一编码再切分或用reindex(columnsfeature_cols, fill_value0)对齐。我一般直接在合并数据上做编码省事且不会错。5.3 现象U2R 和 R2L 的 recall 为 0模型完全不预测这两类原因类别极度不平衡U2R 在 NSL-KDD 训练集只有 52 条R2L 约 995 条模型倾向于全部预测 Normal 和 DoS。解决class_weightbalanced或用 SMOTE 过采样少数类或调整决策阈值。注意 SMOTE 只能在训练集上做不能对测试集过采样。5.4 现象scaler 在全部数据上 fit评估结果虚高原因数据泄露测试集的均值和方差信息进入了训练过程。解决scaler.fit(X_train)只在训练集上然后transform测试集和 KDDCup。同理LabelEncoder和get_dummies的列对齐也要基于训练集。5.5 现象KDDCup 标签映射遗漏出现 Unknown 类别导致评估报错原因KDDCup99 有 23 种攻击映射表没写全。解决先value_counts()看所有唯一标签对照映射表补全。常见遗漏apache2、udpstorm、processtable、worm、mscan、saint、snmpgetattack、snmpguess、xlock、xsnoop、httptunnel、ps、sqlattack、xterm。这些在 10% 版本里可能不出现但完整版里有。6. 进阶技巧用特征重要性和混淆矩阵把评估做深6.1 特征重要性分析哪些特征真正在起作用随机森林和 XGBoost 都能输出feature_importances_。在 NSL-KDD 上通常src_bytes、dst_bytes、count、srv_count、same_srv_rate、dst_host_srv_count排前面。但注意One-Hot 后的特征重要性要映射回原始特征名否则报告里写「feature_23 最重要」没人看得懂。import matplotlib.pyplot as plt # 随机森林特征重要性 importances rf.feature_importances_ indices np.argsort(importances)[::-1][:15] plt.figure(figsize(10, 6)) plt.title(Top 15 Feature Importances (Random Forest)) plt.bar(range(15), importances[indices]) plt.xticks(range(15), [feature_cols[i] for i in indices], rotation45, haright) plt.tight_layout() plt.savefig(feature_importance.png, dpi150)这张图放进报告能直接说明模型依赖哪些流量特征答辩时被问「为什么选这些特征」也有据可依。6.2 混淆矩阵对比两个数据集的错误分布差异import seaborn as sns fig, axes plt.subplots(1, 2, figsize(14, 5)) cm_nsl confusion_matrix(y_test_enc, rf_pred) sns.heatmap(cm_nsl, annotTrue, fmtd, cmapBlues, xticklabelsle.classes_, yticklabelsle.classes_, axaxes[0]) axes[0].set_title(NSL-KDD Test Confusion Matrix) axes[0].set_xlabel(Predicted) axes[0].set_ylabel(True) cm_kdd confusion_matrix(y_kddcup, kddcup_pred) sns.heatmap(cm_kdd, annotTrue, fmtd, cmapGreens, xticklabelsle.classes_, yticklabelsle.classes_, axaxes[1]) axes[1].set_title(KDDCup 10% Confusion Matrix) axes[1].set_xlabel(Predicted) axes[1].set_ylabel(True) plt.tight_layout() plt.savefig(confusion_matrices.png, dpi150)对比两个混淆矩阵通常能看到KDDCup 上 Normal 和 DoS 几乎全对R2L 和 U2R 也有不错召回NSL-KDD 上 R2L 大量被误判为 NormalU2R 几乎全漏。这个对比直接支撑「模型对未知攻击泛化不足」的结论。6.3 一个我踩过的坑别在测试集上调参早期我做这个作业时为了让 NSL-KDD 测试集准确率好看反复调整 XGBoost 的max_depth和learning_rate结果测试集准确率上去了但换到 KDDCup 上反而下降。后来才意识到这是在测试集上过拟合。正确做法是从训练集切 10%~15% 做验证集用验证集调参测试集只在最后评估一次。这个习惯我后来一直保持验证集调参测试集只读一次。希望帮到你。本文还有配套的精品资源点击获取