简介:面向计算机相关专业毕业设计、课程设计与期末大作业的机器学习实践项目,围绕DDoS入侵检测场景,提供完整可运行的Python源码与配套文档说明,适合需要快速搭建入侵检测模型、完成论文撰写与答辩准备的学生参考。压缩包共5个文件,包含3个Python脚本(覆盖逻辑回归、正则化逻辑回归及多类别逻辑回归等模型实现)、1个Markdown说明文档和1个DOCX毕业设计简述文档,整体大小241KB,结构紧凑,便于快速下载查阅与二次开发。目前已有114人学习。该项目基于真实实践编写,评审分达98分,源码均经过本地编译可运行,内容经助教老师审定,难度适中,不需要额外复杂环境即可复现实验。通过阅读源码可了解DDoS特征处理、数据划分、模型训练与评估流程,配套文档则有助于梳理项目背景、技术选型、方案设计与结论分析,能显著提升课程设计、期末大作业与毕业设计的完成效率。
1. 毕业设计里的机器学习DDoS检测:模型只占四分之一工作量
基于机器学习的DDoS入侵检测,很多人在开题时把它想象成一个模型问题,真正动手才发现流量解析和特征工程才是消耗时间的大头。主线其实很固定:把网络流量切成语义完整的流,算出一批统计特征,交给随机森林或逻辑回归这类分类器学习正常与攻击流量的边界,最后把模型接进实时抓包流程做在线预测。下面按这条线拆开讲,从数据集选型、PCAP解析、特征提取、模型训练一路讲到部署踩坑,适合正在做毕业设计、想把项目做成能演示、能答辩、有验证数据支撑的高分项目的Python开发者直接照着复现。
2. 为什么选机器学习而不是硬编码规则:特征工程与数据集的取舍
2.1 传统规则检测为什么会被打穿:自适应入侵检测的由来
早期DDoS检测的核心是一套固定特征库,SYN Flood就盯SYN包比例,UDP Flood就盯大包速率,超过阈值就报警。这种方案在流量模型稳定时很有效,到现在防火墙还有大量这样的模块。但它默认攻击特征不变,攻击者换个参数、把速率压低、做成混合型攻击,规则就失效了,误报漏报一起来。做DDoS防御的同学应该都经历过这种尴尬:规则调严了误报多,调松了漏报多,阈值全靠经验试。
机器学习检测换了一条路:不写具体规则,而是从带标签的流量里学,学习“正常和异常在统计特征上的分界”。它照样依赖特征,只是把特征组合和边界判断交给模型完成,对新变种有一定泛化能力。自适应入侵检测这几年在很多高校论文里反复出现,本质就是这个思路——用训练好的分类器替代人工设定的阈值,让检测逻辑自己适应数据分布变化。
不过要澄清一个认知误区:机器学习没有取消特征工程,只是把“人写规则”换成“人构造特征+模型找边界”。如果你把原始报文直接塞给模型,不做流特征聚合,效果会非常差。网络流量和做量化交易策略里的行情数据很像,都需要先构造统计量再建模,差别在于网络侧一定要把“流”的概念立住:同一个五元组往返的包算一个流,包长、包间隔、标志位都按流来统计才有意义。
2.2 公开数据集怎么选:CICIDS2017、UNSW-NB15与KDD99的取舍
DDoS检测毕设最容易走偏的做法是一上来就自己抓包、自己做攻击实验。自采流量有两个硬伤:样本规模小,模型泛化能力没法证明;没有标准标签,你很难让答辩老师相信每一个流都标对了。所以稳定路线是先基于公开数据集做训练和评估,再用自采流量做最后验证。这个顺序不要反,反了很容易在数据集上反复返工。
常见公开数据集有三个。KDD99年代最早、文件最小、跑起来最快,但协议和攻击形态偏老,现在拿来交差会被老师追问“为什么用二十多年前的数据”。UNSW-NB15覆盖了2015年前后的流量,攻击类型比较全,文件大小适中,适合配置一般的机器。CICIDS2017是目前毕设使用最多的,包含完整PCAP和CSV特征文件,DDoS、DoS、暴力破解都有,资料多、踩坑经验多,缺点是整体体积大,下载和预处理要留足磁盘空间。CICIDS2019是它的后续扩展,流量更大,本科毕设反而很少直接用到。
| 数据集 | 攻击类型覆盖 | 文件规模 | 适合情况 | 主要坑 |
|---|---|---|---|---|
| KDD99 | 老式DoS、R2L等 | 小,几乎秒开 | 先跑通流程 | 年代久,答辩要补对比实验 |
| UNSW-NB15 | 多种现代攻击 | 中等 | 性能一般的机器 | 特征定义分散,要核对官方文档 |
| CICIDS2017 | DoS、DDoS、暴力破解等 | 大,几十GB级 | 毕设主流选择 | 按天分文件,拼接时列名不一致 |
选择优先级可以这样排:机器内存8G以上、想省事,选CICIDS2017;机器性能一般,选UNSW-NB15;只是想先跑通流程,KDD99也能用,但最后答辩要补一个现代数据集的对比实验。数据下载后会遇到一个很实际的坑:CICIDS2017的标签按日期分散在多个文件里,不同文件的列名和格式有细微差别,直接合并会掉数据。处理时先把列名统一,再把Label字段映射成二分类:Benign为0,DDoS相关为1,其他攻击类型可以单独留着做多分类分析。
2.3 标签与评价指标:别只用Accuracy交差
流量数据集天然不平衡,正常流的数量远大于攻击流。哪怕攻击在抓包里刷得很猛,折算成五元组流之后,攻击流通常也只占很小的比例。这种情况下模型全预测“正常”,准确率都能超过95%,所以毕业论文如果只报Accuracy,答辩基本会被问住。
要同时看Precision、Recall和F1。Precision是报警里真的攻击占多少,Recall是真实攻击里抓到多少,F1是两者的调和平均。DDoS检测场景里漏报的代价高于误报,所以一般先保证Recall在0.95以上,再去看F1能不能做到0.9附近。答辩时老师常问“阈值怎么定的”“误报怎么办”,提前把Recall和误报率的权衡曲线画出来,比背模型原理更有效。
评估时还有一个隐蔽问题:数据划分方式。同一个流里的前后包高度相关,如果按行随机切训练集和测试集,模型等于见过测试数据的“亲戚”,指标会虚高。严谨做法是按时间切分,前70%流量训练、后30%测试,模拟“模型没见过未来”的真实在线场景。CICIDS2017按天分文件,正好可以拿前几天的文件训练,留最后一天的文件做盲测,这个设计写进文档说明里非常加分。
3. 把PCAP流量变成训练集:Python特征提取最小实现
3.1 搭建最小处理环境:虚拟环境与依赖安装
标题里的python源码,实际上由三部分组成:流量解析脚本、模型训练脚本、在线检测脚本。毕设代码别贪图框架越新越好,用一套环境全搞定最省心,依赖关系越简单,答辩演示翻车的概率越低。我建议的依赖是pandas、numpy、scikit-learn、scapy、joblib,前四个几乎不会出兼容性问题,最后一个用来保存模型。
python3 -m venv ddos_env source ddos_env/bin/activate pip install pandas numpy scikit-learn scapy joblib先把项目目录建好,数据文件、脚本、模型输出分开放。scapy用来解析PCAP,pandas用来组织特征表,scikit-learn负责训练和评估。这里要注意scapy的版本,2.4之后的API基本稳定,但如果用的是很早的教程代码,rdpcap、sniff这些函数的返回类型可能有差异,报错优先查文档版本说明,不要直接换库。
提示:不要把依赖一股脑升级到最新。机器学习项目最怕“环境是对的,代码是旧的”,建议在项目里放一个requirements.txt,把版本号固定下来,避免答辩前换机器装出不同版本。
3.2 用Scapy按双向流提取统计特征
特征提取是整个项目的核心,也是工作量最大的部分。两个PCAP,一个正常流量、一个攻击流量,解析出来后要合并成统一格式的特征表。下面这段代码是完整的流特征提取骨架,直接放进项目里改成自己的文件路径就能跑。
from collections import defaultdict import statistics as st import pandas as pd from scapy.all import rdpcap, IP, TCP, UDP def extract_flow_features(pcap_path, idle_timeout=120): packets = rdpcap(pcap_path) flows = defaultdict(list) for pkt in packets: if IP not in pkt: continue if TCP in pkt: proto, sport, dport = 'tcp', pkt[TCP].sport, pkt[TCP].dport elif UDP in pkt: proto, sport, dport = 'udp', pkt[UDP].sport, pkt[UDP].dport else: continue # 双向流:把源和目标排序后合并 key = tuple(sorted([(pkt[IP].src, sport), (pkt[IP].dst, dport)])) + (proto,) flows[key].append((pkt.time, len(pkt))) rows = [] for key, pkts in flows.items(): times = [t for t, _ in pkts] sizes = [s for _, s in pkts] duration = max(times) - min(times) if duration < 1: continue rows.append({ 'flow_key': str(key), 'packet_count': len(pkts), 'avg_size': sum(sizes) / len(sizes), 'size_std': st.pstdev(sizes), 'duration': duration, 'pps': len(pkts) / duration, 'bytes_per_sec': sum(sizes) / duration }) return pd.DataFrame(rows) df_attack = extract_flow_features('data/attack.pcap') df_attack['label'] = 1 df_attack.to_csv('attack_features.csv', index=False)这段代码有两个关键设计。第一个是双向流,方向相反的包合并到同一个流,这样回复包的统计信息能用于判断这个流是不是攻击,比单向流信息更完整。第二个是idle_timeout参数,实际流量里流不会自己结束,需要靠超时判断:超过120秒没有新包,这条流就该结算了。代码里简化为直接按整个pcap结算,实际做在线检测时要用时间窗口断流。
上表只算了包数、平均包长、包长标准差、持续时长、每秒包数和字节速率,这是最小特征集。CICIDS2017官方给了八十多个特征,你不需要全做,但至少应补上协议类型、SYN包占比、ACK包占比、平均包间隔这类特征。攻击模式不同,区分度最高的特征也不同。建议先把基础特征跑通,再逐步加特征看F1有没有变化,这比一次堆太多特征更容易排错。
3.3 特征落地与数据集划分:CSV、分层抽样与时间切分
两个PCAP解析出两个CSV后,下一步是把正常流和攻击流合并成一张总表,再做训练集和测试集划分。注意合并时不要直接concat后什么都不做,先检查两边的特征列名和顺序是否一致,尤其注意缺失值,scapy解析出来的特征表不会像官方CSV那样帮你把空值补好。
import pandas as pd from sklearn.model_selection import train_test_split df_normal = pd.read_csv('normal_features.csv') df_attack = pd.read_csv('attack_features.csv') df = pd.concat([df_normal, df_attack], ignore_index=True) X = df.drop(['flow_key', 'label'], axis=1) y = df['label'] X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.3, stratify=y, random_state=42 ) print(df['label'].value_counts())stratify=y是这行代码里最重要的一个参数,它保证划分后训练集和测试集里正负样本的比例与原始数据一致。如果不加,不平衡数据下小概率会出现测试集里一条攻击流都没有的情况,模型报告直接没法看。random_state固定为42,保证重复运行结果一致,答辩时跑两遍结果不同是很尴尬的事。
到这里你已经拥有了一张可以直接喂给sklearn的特征表,下一步就是训练模型。如果自己抓的流量不够,想直接用CICIDS2017提供的CSV特征文件,也建议把列名映射成统一格式再来,不要即拿即用,官方文件里有很多空值和Inf值,直接喂给模型会触发一堆警告,这也是一个隐藏扣分点。
4. 用sklearn训练DDoS检测模型:两套模型选一套能答辩的
4.1 特征筛选:先砍相关性,再看重要性
特征列一多,第一个要做的不是调模型,而是筛选。随机森林自带特征重要性,算起来方便,但它的重要性有偏向性,比如对高基数数值特征更有利。常见做法是先算相关系数矩阵,把相关性大于0.9的列只保留一个,再用随机森林的重要性做第二轮排序,取Top N。两个步骤顺序不能反,先砍冗余再排重要性。
import numpy as np import pandas as pd corr = X_train.corr() upper = corr.where( np.triu(np.ones(corr.shape), k=1).astype(bool) ) high_corr = [col for col in upper.columns if any(upper[col] > 0.9)] print('high correlation columns:', high_corr) X_train_filtered = X_train.drop(columns=high_corr)代码逻辑:corr算出训练集特征间的相关系数矩阵,取上三角部分避免重复比较,把相关系数大于0.9的列名收集起来剔除。做这个筛选的原因很实际:两个高度相关的特征同时进入模型,不仅训练变慢,还给模型注入冗余信息,特征重要性解释起来会自相矛盾。答辩时你能说清楚“为什么删掉这几个特征”,比模型调参更有说服力。
4.2 随机森林与逻辑回归对比:选更快解释的那一个
毕业设计里的机器学习算法,第一原则是能解释。深度模型效果好,但DDoS流特征大多是非线性不强的统计量,用不上大模型。随机森林和逻辑回归是性价比最高的两个选择:随机森林能自动处理特征交互,逻辑回归能给出每个特征的权重方向,两个模型对比着写,正好覆盖“算法多样性”的要求,也容易解释。
from sklearn.ensemble import RandomForestClassifier from sklearn.linear_model import LogisticRegression from sklearn.metrics import classification_report from sklearn.pipeline import make_pipeline from sklearn.preprocessing import StandardScaler models = { 'rf': RandomForestClassifier( n_estimators=200, max_depth=10, random_state=42 ), 'lr': make_pipeline( StandardScaler(), LogisticRegression(max_iter=1000, class_weight='balanced') ), } for name, model in models.items(): model.fit(X_train_filtered, y_train) y_pred = model.predict(X_test) print(f'== {name} ==') print(classification_report(y_test, y_pred, target_names=['normal', 'ddos']))参数说明按重要性排:随机森林的n_estimators是树的数量,200棵足够稳定,再加收益很小;max_depth限制树深到10,防止模型记住训练集里的噪声,这是流特征模型最需要控制的点。逻辑回归套在make_pipeline里,先做标准化再训练,否则特征量纲不一致会拖慢收敛甚至不收敛;max_iter设到1000是因为标准化后仍需较多迭代,class_weight='balanced'是处理不平衡数据最省事的手段,等价于给少数类更高的误分类代价。
跑完两个模型,对比报告里的F1和Recall。正常情况下随机森林的F1会略高一点,但逻辑回归的优势是能在半秒内完成训练,随机森林则要等几秒。毕设的角度,两个都保留,逻辑回归用作解释特征方向,随机森林用作最终检测模型,这样写进论文里材料最扎实。
4.3 预测概率阈值与模型持久化:给在线检测留好接口
很多入门代码用的是model.predict,直接输出0或1。这个接口在离线评估里够用,但做在线检测时不够灵活。predict底层是拿预测概率跟0.5比,而DDoS场景里0.5未必是最优阈值,应该改用predict_proba取正类概率,再根据验证集调阈值。
import joblib import numpy as np from sklearn.metrics import f1_score y_prob = models['rf'].predict_proba(X_test)[:, 1] for threshold in [0.3, 0.5, 0.7]: y_pred = (y_prob >= threshold).astype(int) print(f'threshold={threshold}, F1={f1_score(y_test, y_pred):.4f}') joblib.dump(models['rf'], 'rf_ddos_model.joblib')这段代码把阈值从0.3试到0.7,观察F1的变化。如果你的评估结果里0.5不是最佳点,说明检测器的默认行为偏离了你的业务目标,这时候就能在文档说明里写清楚:调低阈值提高Recall、代价是误报增加,最后选定的阈值是多少,为什么。joblib.dump保存模型文件,在线检测脚本加载它做预测,这一步是把离线训练和在线检测解耦的关键。
提示:保存模型时连同特征列名一起保存为pickle或json,加载后先校验特征对齐再predict。常见错误是训练和预测用的特征顺序不一致,结果乱成一团,还以为是模型坏了。
5. 避坑清单:从离线模型到在线DDoS检测的五个坎
5.1 训练集F1很高,跑真实流量就翻车:分布不一致
现象:模型在CICIDS2017测试集上F1有0.95,换个环境抓一小段实时流量,检测结果完全乱套,正常流量疯狂误报。
原因:公开数据集的流量分布和真实局域网里跑的流量差别很大,办公网里大量的TLS、视频、文件传输,在训练集里占比很低,模型没见过的特征组合就会乱判。
解决:在线检测前置一个特征分布校验,把预测概率介于0.4到0.6之间的样本标记为“不确定”,不直接算攻击;同时把公开数据集的指标和自采流量的指标分开记录,不要混在一个结果里。答辩时承认分布差异并展示你在真实流量上的尝试,会比假装一个指标吃遍所有场景更有说服力。
5.2 Scapy抓包跟不上模型预测速度:离线脚本直接跑在线
现象:在线脚本一开,CPU直接拉满,抓包线程丢包,检测延迟越来越大,过几分钟进程就卡死。
原因:很多人图省事,把3.2节那段rdpcap读整个文件的代码直接搬进在线流程。rdpcap是把全部包读进内存,流量一大内存和CPU一起爆,模型再快也没用。
解决:在线脚本改用sniff(prn=handler, store=False),store=False指示scapy不要缓存原始包,每抓到一包就让回调函数处理,处理完即丢。统计特征用固定时间窗口累积,比如每5秒对窗口内积累的流做一次结算和预测,不要让单包预测拖着整个流程走。
5.3 流特征结算超时:窗口统计一直不落盘
现象:窗口内的流一直在更新,统计值永远“实时”,到窗口结束该输出特征时发现特征算不完,检测时间线整体后移。
原因:流没有超时机制,一条长期连接占着统计对象不放,窗口结算时所有流都在等它。这是把离线思路搬进在线场景的通病,离线可以读完整个pcap再算,在线不行。
解决:窗口内每条流记录最近一次出现的时间戳,每轮结算前把超过idle_timeout(比如30秒)的流强制结算并移除。窗口本身用time.time()控制快慢,不要用包数控制,网络速率变化会直接影响包数窗口的时长,时间窗口才是稳定的。
5.4 样本不平衡把模型带偏:只会输出“正常”
现象:训练了好几次,混淆矩阵里攻击类的Recall一直很低,模型对攻击流量几乎没反应,而Accuracy依然很高。
原因:训练集里正常流量远远多于攻击流量,模型发现全猜“正常”损失最小,class_weight没加,评估又只看Accuracy,问题被指标掩盖。
解决:评估改用F1和混淆矩阵,训练时给少数类加权或做欠采样。注意过采样必须在划分训练集之后做,很多新手先做SMOTE再划分,合成样本泄漏到测试集里,指标虚高而不自知。这个顺序在文档说明里要专门写一笔,老师问起来能接住。
5.5 换个环境模型加载报错:版本依赖是隐藏扣分项
现象:在自己机器上训练好、保存好的模型,到答辩机器或室友电脑上joblib.load直接报错,或者加载成功但predict结果和本地不一致。
原因:pickle序列化模型时绑定了sklearn的具体类路径,不同版本的sklearn类结构有差异,跨版本加载经常失败,这是机器学习项目换环境的经典坑。
解决:项目里放requirements.txt并固定版本号,答辩前在演示机重新跑一遍训练脚本,或者用joblib的兼容模式重存。最稳的方案是答辩只用自己笔记本,同时准备一个无模型依赖的演示脚本,加载失败时能现场快速重训。
6. 让项目经得起答辩:盲测验证与可视化提分
6.1 三步验证设计:盲测文件、回放、隔离攻击试验
模型和代码都跑通之后,最后一周把时间花在验证设计上。第一步:从数据集中留出至少一天的文件完全不进训练,作为盲测集,把模型在盲测集上的指标单独打印。第二步:写一个回放脚本,把PCAP按原始时间戳喂给在线检测脚本,画出检测时间线,看攻击发生到模型报警之间隔了多久。第三步:做小规模攻击实验,在隔离的实验网段里,用低强度的SYN、慢速HTTP脚本对本地靶机发起少量验证流量,确认模型能识别自采数据。这个设计把“离线指标”和“在线可用性”都覆盖了,答辩素材基本就够了。注意这类验证只允许在虚拟机或独立网段里对本地靶机做,不授权不要去碰真实网络。
6.2 这三类可视化最值得做
可视化别贪多,做三张图就够:混淆矩阵热力图,放在模型评估部分;特征重要性Top10的柱状图,放在特征工程部分;阈值与F1、误报率的曲线图,放在检测策略部分。三张图对应三个关键词:结果可靠、特征可解释、阈值有依据。每张图控制在五行代码内就能画出来,但一定要把图里的数字和文档说明里的描述对上,截图时注意坐标轴标注,别让老师挑出图例和正文不一致的细节。
6.3 一个收尾习惯,带出“希望帮到你”
我做这类项目最大的教训是:花在调模型上的时间远不如花在验证环境上的时间值钱。模型再好看,换一台机器、换一份流量就现原形。所以现在每次跑完一个检测模型,我都会先问自己一句:如果答辩现场只能演示三分钟,我会先展示哪一张图、讲清哪一个决策?回答完这个问题再去做PPT,项目才算真正收尾。希望你也能用这篇文章把数据集、特征、模型和验证这条线走通,做出一个在真实流量面前站得住的毕设项目,希望帮到你。
本文还有配套的精品资源,点击获取