首页 / 资讯中心 / 文章详情

机器学习DDoS入侵检测毕设源码解析:从数据预处理到实时检测

机器学习DDoS入侵检测毕设源码解析:从数据预处理到实时检测 ★ FEATURED ARTICLE
简介一份面向毕业设计应用的机器学习DDoS入侵检测Python源码资源围绕逻辑回归、正则化逻辑回归与多类别逻辑回归三种经典算法展开可直接用于网络流量中的攻击行为识别与分类实验。资源共包含4个文件其中3个Python脚本分别对应三种算法的完整实现代码结构清晰、关键步骤有注释便于二次修改和移植另附1份Word毕业设计简述文档系统梳理了算法原理、实验设计思路与结果分析方法帮助读者快速建立从理论到代码的对应关系。整个压缩包仅240KB轻量便捷部署与调试成本低。目前已有674人学习下载适合计算机及网络安全方向的学生用于课程设计、毕业设计或入侵检测入门实践。无论是快速验证算法效果还是深入比较不同逻辑回归变体在入侵检测任务中的表现都能提供直接可用的代码基底。1. 机器学习 DDoS 入侵检测毕设源码它解决什么问题值得你动手复现吗毕设选“基于机器学习的 DDoS 入侵检测”几乎是网络空间安全方向里最好出成果的题目之一数据集公开、Python 生态成熟、模型指标能写进论文、答辩时还能现场演示。但很多同学把源码包拿到手才发现真正要命的不是跑通模型而是数据从哪来、特征怎么对齐、训练好的模型怎么变成一条能检测攻击的命令。这套 python 源码通常包含数据预处理、特征提取、模型训练和检测脚本四块但每块的工程坑都不少。下面我就按最常见的源码结构拆开讲新手能照着复现熟手也能看到边界和参数在哪改。2. 把 DDoS 检测变成分类问题特征准备与数据集选型2.1 为什么先选公开数据集而不是自己抓包CICIDS2017 的优势与坑做 DDoS 入侵检测第一步不是写代码是选数据。自己抓包听起来很“真实”但从 pcap 里清洗出可用样本要花好几周而且没有攻击标签你没法训练监督模型。常见做法是用公开数据集其中 CICIDS2017 是论文里出现频率最高的因为它提供的是已经提取好的 CSV 特征文件每行是一条网络流附带 80 多个特征列和明确的标签省掉了从 pcap 到特征表的巨大工程。我自己更推荐 CICIDS2017 作为毕设基线原因有三一是数据有明确的时间戳可以按时间切分训练集和测试集避免随机划分带来的数据泄露二是标签覆盖 DDoS、PortScan、BruteForce 等多种攻击方便做多分类或二分类对比实验三是官方文档里给了特征说明写论文特征工程章节时可以直接引用。坑也明显整个数据集有几个 GB 级 CSV直接用 pandas 读容易内存爆炸后面我会专门讲。2.2 流量特征预处理合并 CSV、清洗标签与特征列筛选的代码实现拿到了原始 CSV第一步是合并和清洗。原始数据按天分成多个文件文件名带日期和攻击类型需要先合并成一个大表再把标签统一成“正常/BENIGN”和“攻击”两大类。下面这段是源码 data_preprocess.py 里的核心逻辑import pandas as pd import glob # 读取所有日期分片 CSV合并为一个 DataFrame files glob.glob(data/*.csv) df_list [] for f in files: df pd.read_csv(f, low_memoryFalse, encodingutf-8) df_list.append(df) df pd.concat(df_list, ignore_indexTrue) # 标签列经常带首尾空格统一清洗 df[Label] df[Label].astype(str).str.strip() # 把非 BENIGN 的标签一律归为 DDoS 攻击用于二分类 df[Label_binary] df[Label].apply(lambda x: 0 if x BENIGN else 1) # 删除全空列和完全重复的列 df df.dropna(axis1, howall) df df.loc[:, ~df.T.duplicated(keepfirst)]逻辑说明low_memoryFalse让 pandas 按完整列推断类型避免 CSV 分块读取导致部分数值列被误判成字符串标签清洗用str.strip()去掉常见空格否则BENIGN 会被当成另一个类别二分类映射把 DDoS 及其他攻击都归为 1便于先跑通模型。参数说明里最需要注意的是继承列顺序后面训练和预测时字段顺序必须和这里完全一致我会在部署阶段再强调一次。特征筛选上我一般会先丢掉Flow ID、Source IP、Destination IP这类标识字段以及Timestamp这种时间列——因为它们会泄露网络拓扑信息让模型学到“某个 IP 是攻击者”这种不可泛化的规则。但Flow Duration、Packet Length、Backward Packets这些行为特征要保留它们是 DDoS 检测的主要依据。筛选代码也很简单drop_cols [Flow ID, Source IP, Destination IP, Source Port, Destination Port, Timestamp, Label] X df.drop(columnsdrop_cols, errorsignore) y df[Label_binary]2.3 类别不均衡欠采样和 SMOTE 的实验取舍CICIDS2017 里良性流量比例往往远高于攻击流量直接训练会让模型把所有样本都判成良性因为 Accuracy 也能达到 90% 以上。这个坑很多源码包都会踩。处理不均衡有两种主流路线欠采样是把多数类随机删到和少数类一样多速度快但会丢失信息SMOTE 是用插值合成少数类样本保留信息但容易在特征重叠区域造出矛盾样本。我的建议是两条都做作为对照实验写进论文。实现上要用 imbalanced-learn 库重点是不能在切分前做任何采样否则同样会造成数据泄露。下面是源码 smote_compare.py 里的关键部分from imblearn.over_sampling import SMOTE from imblearn.under_sampling import RandomUnderSampler from imblearn.pipeline import Pipeline # 先在原始数据上按时间切分再对训练集采样 train_size int(len(X) * 0.6) X_train_raw X.iloc[:train_size] y_train_raw y.iloc[:train_size] smote_pipeline Pipeline([ (smote, SMOTE(sampling_strategyauto, random_state42, k_neighbors5)), ]) X_train_smote, y_train_smote smote_pipeline.fit_resample(X_train_raw, y_train_raw) print(SMOTE 后训练集样本分布, y_train_smote.value_counts().to_dict())逻辑说明sampling_strategyauto会自动把少数类样本数提升到与多数类一致k_neighbors5控制生成新样本时参考的近邻数量太小容易过拟合太大生成样本会偏离原始分布。注意这里只用训练集 fit_resample测试集保持原始分布这样测试指标才是真实场景下的表现。对比欠采样时把SMOTE换成RandomUnderSampler(random_state42)即可抽样策略通常留默认。3. 用 Python 搭建基线检测模型随机森林与 XGBoost 的最小可复现代码3.1 时间维度切分为什么随机 train_test_split 会让指标虚高很多源码包直接调用train_test_split(X, y, test_size0.3, random_state42)这在时序流量数据上是错误的。同一条 TCP 连接的前后报文会被切成训练样本和测试样本模型相当于“见过”测试集的一部分AUC 能虚高 5 到 10 个点。正确做法是按时间戳排序取前 60% 训练、中间 15% 验证、最后 25% 测试模拟“用过去检测未来”的真实场景。import pandas as pd # 确保按时间排序原始 CSV 的行序不一定和时间戳一致 df[Flow Start Time] pd.to_datetime(df[Flow Start Time]) df df.sort_values(Flow Start Time).reset_index(dropTrue) total len(df) train_end int(total * 0.6) val_end int(total * 0.75) train df.iloc[:train_end] val df.iloc[train_end:val_end] test df.iloc[val_end:] X_train, y_train train[X_cols], train[Label_binary] X_val, y_val val[X_cols], val[Label_binary] X_test, y_test test[X_cols], test[Label_binary]逻辑说明先转时间戳再排序避免原始文件里的乱序干扰。用三段切分是为了给 XGBoost 早停留一个独立的验证集不能直接拿测试集调参。参数说明total * 0.6和0.75是经验比例如果样本量很大训练集比例可以升到 70%验证集 15% 基本够用。3.2 随机森林基线先跑通再谈优化随机森林是 DDoS 检测最稳的起点它对特征缩放不敏感也不用做复杂的归一化适合快速验证特征工程是否有效。源码 train_rf.py 里的最小示例from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import classification_report, roc_auc_score rf RandomForestClassifier( n_estimators100, max_depth12, min_samples_leaf4, n_jobs-1, random_state42, ) rf.fit(X_train, y_train) y_prob rf.predict_proba(X_val)[:, 1] y_pred (y_prob 0.5).astype(int) print(RandomForest Val AUC:, roc_auc_score(y_val, y_prob)) print(classification_report(y_val, y_pred, digits4))参数说明n_estimators100是性价比比较高的值不是越大越好超过 300 后效果提升极慢且训练时间线性增长max_depth12限制单棵树深度防止一棵树记住整个训练集min_samples_leaf4强制叶子节点至少有 4 个样本和高深度配合控制过拟合。这里用predict_proba而不是predict是给后面调阈值留空间。如果验证集精确率和召回率差距大优先回来调这两个参数而不是加更多树。3.3 XGBoost 调参学习率、树深度与早停的配合相比随机森林XGBoost 在同样数据下通常能再涨 2 到 3 个点的 AUC但参数敏感得多。毕设里用一组保守参数加早停比盲目 grid search 更实用from xgboost import XGBClassifier xgb XGBClassifier( objectivebinary:logistic, eta0.05, max_depth5, subsample0.8, colsample_bytree0.8, eval_metricauc, early_stopping_rounds10, random_state42, ) xgb.fit( X_train, y_train, eval_set[(X_val, y_val)], verboseFalse, )逻辑说明eta0.05是学习率调小能提精度但会慢一般从 0.1 开始、降到 0.03 看验证集收益subsample0.8每轮随机采 80% 样本colsample_bytree0.8每棵树随机用 80% 特征这是 XGBoost 的两种随机化防过拟合手段。early_stopping_rounds10表示验证集 AUC 连续 10 轮不提升就停省掉手动选迭代次数。注意eval_set传的一定是验证集不是测试集否则测试集的“考试答案”就提前看到了。跑完用xgb.best_iteration拿到最优迭代数重新在完整训练集train val上按这个迭代数训练能再挤一点指标。4. 模型落地从离线训练到实时检测脚本的工程衔接4.1 保存模型和特征列清单joblib 与 pickle 的正确用法毕设答辩现场通常要演示实时检测最稳妥的做法是先把训练好的模型存成文件再写一个独立的检测脚本加载。这里有个常见错误只存模型不存特征列顺序换台机器或者换次数据就发现预测结果完全不对。DDoS 特征有几十列任何一列错位都会让模型输出垃圾结果。所以源码里我一般把模型和特征清单一起打包import joblib model_data { model: xgb, feature_cols: X_train.columns.tolist(), threshold: 0.7, } joblib.dump(model_data, ddos_model.joblib)读取时用joblib.load然后从字典里同时取模型、特征列和阈值。joblib比pickle更适合 numpy 数组和 sklearn 对象序列化速度快且文件更小。如果源码里用的是pickle.dump也没问题但要注意pickle加载时存在代码执行风险毕设分享时务必提醒使用者只加载可信来源的文件。4.2 实时抓包检测的最小轮询脚本scapy 抓包统计特征的边界实时检测不可能每来一个包就预测一次那样开销太大。常见做法是用一个时间窗口比如 5 秒聚合出窗口内各组流量的统计值再喂给模型。下面这个脚本是简化版用于演示接口每 5 秒抓一批包统计每个五元组的包数量、平均包长和字节数把这些特征拼成一行交给模型判断。from scapy.all import sniff import pandas as pd import joblib import time model_data joblib.load(ddos_model.joblib) model model_data[model] cols model_data[feature_cols] # 这个阈值来自验证集 PR 曲线后文会讲 def window_stats(pkts): stats {} for p in pkts: if IP not in p: continue key (p[IP].src, p[IP].dst, p[IP].proto) if key not in stats: stats[key] {count: 0, total_len: 0} stats[key][count] 1 stats[key][total_len] len(p) rows [] for (src, dst, proto), v in stats.items(): rows.append({ Flow Duration: 5000, Flow Packets/s: v[count] / 5.0, Flow Bytes/s: v[total_len] / 5.0, # 实际源码里这里会和 CICFlowMeter 对齐更多特征 }) return pd.DataFrame(rows) def detect(pkts): df window_stats(pkts) if df.empty: return missing set(cols) - set(df.columns) for m in missing: df[m] 0 # 缺失特征补 0和训练前保持一致 pred model.predict_proba(df[cols])[:, 1] for _, p in zip(df.iterrows(), pred): if p model_data[threshold]: print(检测到疑似 DDoS 流量:, p) while True: sniff(timeout5, prndetect, storeFalse)逻辑说明sniff(timeout5)抓 5 秒包后回调detectstoreFalse防止内存堆积。窗口聚合产生的特征必须和训练时特征名严格对应缺失列补零是一种粗暴兜底。参数说明timeout5决定检测延迟窗口越大统计越稳但响应越慢threshold用模型训练时计算的阈值而不是默认 0.5这个会直接影响误报率。这个脚本只能演示流程真正上线建议直接用 CICFlowMeter 或 nProbe 的流导出接口因为自己聚特征容易漏掉Packet Length Mean这类关键计算列。4.3 用 PR 曲线选阈值别让默认的 0.5 拖垮误报率训练完模型后predict_proba输出的概率本身不是“该不该报警”的最佳分界。DDoS 场景里误报太多会把正常业务流量拦掉漏报则直接导致服务不可用。常见做法是画 PR 曲线找一个 F1 分数最高的阈值from sklearn.metrics import precision_recall_curve, f1_score import numpy as np precision, recall, thresholds precision_recall_curve(y_val, y_prob) f1_scores 2 * precision * recall / (precision recall 1e-9) best_idx np.argmax(f1_scores) best_threshold thresholds[best_idx] print(最佳阈值:, best_threshold.round(4))逻辑说明precision_recall_curve返回三组数组thresholds每个点对应一个概率分界。选argmax(f1_scores)作为阈值是因为在类别不均衡时 F1 能综合平衡漏报和误报。参数说明如果你们学校老师更看重防御有效性可以把阈值往低调让召回率更高如果看重运营成本就把阈值调高。这一步计算出的数值要写进模型字典和检测脚本防止线上用默认 0.5 和毕设论文里写的最佳阈值对不上。5. 毕设源码里最常踩的 5 个坑现象、原因和解决办法5.1 训练集和测试集特征分布不一致AUC 虚高现象训练和预测都在同一个随机切分的数据集上做AUC 0.99但换到新抓的流量上检测率直接跌到 60%。原因随机切分把同一时间窗口的相似网络流同时分到训练集和测试集模型学到的是样本间的相似度而不是真正的攻击行为。另一个常见来源是原始 CSV 没有按时间排序文件拼接顺序本身带着时间趋势随机切分就会让近期流量混进训练集。解决严格按时间戳排序后做前 60%、后 40% 的时序切分。如果不能用公开数据集的时间戳就按文件批次切分用前几天的数据训练最后一天的数据测试。源码里我会强制检查train和test的时间范围是否有重叠重叠就报错。5.2 数据泄露MinMaxScaler 在切分前 fit 了全量数据现象归一化之后训练集和测试集分布看起来完美但模型部署后预测概率整体偏移特别是新流量的数值范围超出训练时见过的最小最大值时预测结果变成极端值。原因很多源码把StandardScaler或MinMaxScaler在train_test_split之前直接 fit 整个数据集测试集的均值、方差信息提前进入了训练流程属于数据泄露。解决所有缩放器、SMOTE、特征选择步骤都必须在训练集上 fit然后只 transform 验证集和测试集或者用 Pipeline 包起来。下面这是正确流程的骨架from sklearn.preprocessing import MinMaxScaler from sklearn.pipeline import Pipeline scaler MinMaxScaler().fit(X_train) # 只在训练集上 fit X_train_scaled scaler.transform(X_train) X_val_scaled scaler.transform(X_val) X_test_scaled scaler.transform(X_test)5.3 标签定义混乱把良性流量和攻击流量混在一个 label 里现象二分类模型在验证集上精确率很高但混淆矩阵显示把大量 PortScan 样本错分到良性类别。原因原始 CICIDS2017 里除了 DDoS 还有 PortScan、Botnet、BruteForce 类别如果只把名字里带 “DDoS” 的标签设为 1其他所有类别包括别的攻击都变成 0模型就会把非 DDoS 攻击也当成正常流量。解决做二分类时把所有攻击类型的标签统一映射为 1良性流量单独为 0。代码在 2.2 节已经用Label_binary实现过。如果论文想做多分类就保留原始标签做LabelEncoder但异常检测和 DDoS 检测的混搭会让评审问很多问题建议默认保持二分类。5.4 内存溢出几个 GB 的 CSV 直接 pandas 读取就卡死现象用pd.read_csv(Wednesday-workingHours.pcap_ISCX.csv)后 Jupyter 内核直接死掉小内存笔记本尤其常见。原因CICIDS2017 每个文件有几十万行、80 多列pandas 默认会读成 float64/int64一个文件就能吃掉几个 GB 内存。解决先用pd.read_csv(f, usecolsfeature_cols, dtypenp.float32, low_memoryFalse)只读需要的列并指定浮点精度。这样内存占用能降到原来的四分之一。如果还不行就用enginec加按块读取chunks pd.read_csv(data/large.csv, usecolsfeature_cols, dtypenp.float32, low_memoryFalse, chunksize100000) df pd.concat(ch both, ignore_indexTrue)...]5.5 论文指标和线上指标对不上测试集太干净现象答辩演示用实时抓包测试结果模型报出的 DDoS 全是误报和论文里 0.99 的精确率反差极大。原因公开数据集的测试集是实验室环境抓出来的背景流量干净现场演示的网络里存在大量非攻击但行为异常的流量比如视频通话、大文件传输它们的“长连接 高字节速率”特征和 DDoS 攻击流量很像。解决源码里要留一个“泛化性对比”实验用 CICIDS2017 训练然后在另一个数据集比如 CICIDS2019 或自己抓的一小段真实流量上做验证报告精确率和召回率的下降幅度。这个做法既能让答辩有更多内容也能提前暴露模型边界避免现场翻车。6. 让答辩多讲五分钟的小技巧混淆矩阵与 SHAP 特征贡献图到了答辩阶段很多同学只会贴 Accuracy、Precision、Recall 三个数老师一问“模型为什么把这条流量判成攻击”就卡住了。我的建议是提前生成两类图一类是混淆矩阵热力图说明错误发生在哪里另一类是 SHAP 摘要图说明哪些特征对判断贡献最大。这两个图能直接把“黑匣子”变透明也是源码包最能体现工程量的部分。import matplotlib.pyplot as plt from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay from xgboost import XGBClassifier import shap # 混淆矩阵 cm confusion_matrix(y_test, y_pred) disp ConfusionMatrixDisplay(confusion_matrixcm, display_labels[Benign, DDoS]) disp.plot(cmapBlues) plt.title(Confusion Matrix on Test Set) plt.savefig(confusion_matrix.png, dpi150) # SHAP 特征贡献图树模型直接用 TreeExplainer explainer shap.TreeExplainer(model) shap_values explainer.shap_values(X_test[:500]) shap.summary_plot(shap_values, X_test[:500], feature_namesfeature_cols, showFalse) plt.savefig(shap_summary.png, dpi150, bbox_inchestight)逻辑说明混淆矩阵要同时看行和列重点解释 False Positive 为什么比 False Negative 多或少SHAP 摘要图里每个点代表一个样本颜色越红表示该特征值越大横坐标表示贡献方向能直接看出Flow Packets/s这类特征是不是主导变量。参数说明X_test[:500]限制样本数否则 SHAP 计算很慢且图太密dpi150保证插入论文后图例还清晰。我自己的习惯是拿到任何 DDoS 检测源码第一件事就把混淆矩阵和 SHAP 图跑出来如果 SHAP 图里排前面的特征和论文里“特征工程”章节描述的不一致说明特征筛选有问题比调参重要得多。这个习惯帮我在毕设评审时少挨了很多问。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站