
简介一套基于机器学习的网络入侵检测系统完整源码面向网络安全研究人员、算法工程师及高校相关专业学生用于解决传统IDS依赖规则签名、难以识别未知攻击、误报率高且模型解释性差等痛点。项目采用SMOTE处理类别不平衡融合传统机器学习、集成学习、深度学习和自动机器学习算法建模并借助SHAP、DALEX实现预测解释同时基于Streamlit、Vue和Flask搭建了从数据预处理、模型训练到入侵检测的全流程自动化系统。压缩包共30个文件、11.21MB以zip源码包为主体内含前端Vue、后端Flask、Streamlit应用等子模块另含SQL数据库脚本、Markdown说明文档及17张架构流程图和效果截图目录结构清晰便于按需取用。目前已有175人学习下载适合希望快速复现实验、观察可解释性分析或在此基础上做二次开发的读者。1. 入侵检测系统引入机器学习先解决一个关键认知问题把决策树或随机森林直接塞进传统入侵检测链路并不能像换插件一样立刻让检测率翻倍。实际项目里真正的瓶颈通常不是分类器选得不够新而是数据标注不一致、特征提取窗口不合理、误报在真实流量里根本压不住。下面内容围绕“基于机器学习的入侵检测系统源码”的完整应用流程展开从原始流量到可训练数据集的工程路径再到机器学习模型落地时最容易被忽略的边界条件。适合刚接触机器学习的安全工程师也适合想在实验室或内部网络搭建异常检测平台的算法方向同学。读完可以拿到一套能跑通的最小实现也能理解为什么多数项目的瓶颈发生在训练之前。2. 机器学习在入侵检测里的建模前提数据源与特征表示2.1 三种常见数据源Pcap、NetFlow、日志告警入侵检测系统要喂给机器学习模型的数据常见有三种来源。Pcap是最接近原始流的保留完整载荷和全部包头部但体积大、解析慢适合离线训练和取证。NetFlow是路由器或交换机导出的流记录只包含五元组、包数、字节数、起始时间等元数据体积小吞吐高适合在线监控但看不到载荷内容。日志告警来自已有安全设备比如WAF和EDR的产物特征维度高但存在漏报直接被日志系统吞掉的风险。三者选型的核心权衡在信息量与吞吐之间。常见做法是训练阶段用Pcap重放历史流量做特征富化上线阶段用NetFlow做实时特征两层之间用同样的会话聚合逻辑保持口径一致。这样既能拿到全量载荷数据离线建模又不会让在线推理被Pcap解析拖垮。如果业务侧对实时性要求不高也可以统一走NetFlow减少一套链路的维护成本。数据源信息量实时性常见用途Pcap载荷级低离线训练、取证NetFlow流级高在线检测日志告警语义级高规则补充、关联分析2.2 特征设计统计特征、标志位、时间窗口无论用哪种数据源最终都要转换成表格特征才能交给机器学习算法。最基本的几类基础流特征包含协议类型、源目端口、包总数、总字节数标志位特征包含TCP的SYN、ACK、FIN、RST出现次数和比例时间窗口特征统计单位时间内相同源IP的连接频率、相同目的IP的端口扫描次数。时间窗口是这里最容易出错的地方设短了看不到慢速扫描设长了会把多次不同攻击混进同一条特征里。做入侵检测特征工程时我会优先把端口熵算进去。端口熵统计目的端口的分布离散程度横向扫描在短时间内访问大量不同端口熵值明显偏高。这是一个简单但有效的异常信号比单纯统计端口数量更稳定。另一个值得关注的是包长标准差常见DDoS攻击的报文长度非常均匀标准差会明显低于正常业务流量。特征名说明类型flow_duration流持续时间(ms)数值avg_pkt_len平均包长数值syn_ratioSYN包占比数值dst_port_entropy目的端口熵数值conn_per_sec每秒连接数数值2.3 机器学习分类器选型随机森林、孤立森林与GBDT模型选型取决于标签条件。有明确标注的“正常/攻击”二分类数据集优先用随机森林或LightGBM只有少量标注甚至无标注孤立森林和一分类SVM是更现实的选择。随机森林的优势在于对高维稀疏特征不敏感能输出特征重要性业务侧也好解释。GBDT通常效果略好但训练参数多、对噪声敏感过拟合后往往分不清是树深度还是学习率的问题。孤立森林不依赖标签直接对样本空间做线性切分适合无标注异常发现但会把所有离群点都当攻击误报率偏高是常态。实际项目里可以先用随机森林跑一个基线记录F1再用孤立森林做异常召回对比。先看无监督能干到什么程度再决定往有监督方向投入多少标注成本这样可以避免一上来就陷进特征工程的调整循环。3. 用随机森林实现入侵检测分类器的可运行源码3.1 用TShark从Pcap提取候选特征在开始写机器学习代码之前先准备环境。下面命令创建一个虚拟环境并安装依赖包python -m venv ids-env source ids-env/bin/activate pip install pandas scikit-learn numpy shap拿到抓包文件后需要先把Pcap转换成结构化CSV。tshark是这里最顺手的工具它把协议解析能力直接暴露成命令行字段提取tshark -r capture.pcap -T fields \ -e frame.len -e ip.proto -e ip.src -e ip.dst \ -e tcp.srcport -e tcp.dstport -e tcp.flags.syn \ -e tcp.flags.ack -e frame.time_delta_displayed \ -E headery -E separator, traffic_raw.csv这段命令里每个参数都有实际作用-r指定离线抓包文件-T fields让tshark按字段输出表格-e声明要提取的协议字段-E headery让CSV首行保留字段名方便后面对照列名。frame.time_delta_displayed表示当前包与前一个包的时间差用来计算连接速率和请求频率。如果是在线场景可以用nfcapd采集NetFlow流记录再用nfdump -r nfcapd.current -o csv netflow.csv导出。需要提醒的是离线Pcap特征和在线NetFlow特征之间的列名和单位必须保持一致否则模型上线后输入分布会直接偏移。3.2 预处理与标签编码的Python实现拿到CSV之后进入数据清洗阶段。以下代码完成缺失值处理、方差过滤、标准化和数据集划分import pandas as pd from sklearn.model_selection import train_test_split from sklearn.preprocessing import LabelEncoder, StandardScaler from sklearn.feature_selection import VarianceThreshold df pd.read_csv(traffic_raw.csv) df df.dropna(thresh2) # 缺失超过两列的整行丢弃 df df.fillna(0) # 剩余缺失统一填空值 # 多类型攻击统一映射为二分类 df[label] df[label].map({normal: 0, DOS: 1, Probe: 1, R2L: 1, U2R: 1}) # 协议类型是离散值转成数值编码 for col in [ip.proto]: df[col] LabelEncoder().fit_transform(df[col]) X df.drop([label], axis1) y df[label] # 方差过滤丢弃所有样本上几乎不变的列 sel VarianceThreshold(threshold0.01) X pd.DataFrame(sel.fit_transform(X), columnsX.columns[sel.get_support()]) scaler StandardScaler() X[:] scaler.fit_transform(X) X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 )各步骤的逻辑dropna(thresh2)删除缺失字段太多的行避免半空的记录参与训练fillna(0)对剩余缺失补零因为树模型对零值不敏感。VarianceThreshold(threshold0.01)丢弃方差低于0.01的列这类列在样本间几乎没有变化对分类无信息量还能减少内存开销。标准化在树模型里不强制但如果后续要对比逻辑回归或神经网络就必须保留。切分数据前建议检查一下y_train里攻击样本占比。如果低于5%需要在train_test_split中加上stratifyy做分层抽样否则极少数攻击类可能在训练集和测试集中分布不均。3.3 训练随机森林分类器并理解关键参数from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import classification_report rf RandomForestClassifier( n_estimators300, # 树的数量越多方差越低 max_depth12, # 限制单棵树深度防止过拟合 min_samples_leaf5, # 叶子节点最小样本数越大模型越保守 max_featuressqrt, # 每次分裂随机抽样特征数 class_weightbalanced, # 自动按类别频率加权 n_jobs-1, random_state42 ) rf.fit(X_train, y_train) y_pred rf.predict(X_test) print(classification_report(y_test, y_pred, zero_division0))这里每个参数都值得单独理解。n_estimators300表示构建300棵决策树树足够多时模型方差下降但超过500后收益开始递减。max_depth限制树的生长深度入侵检测特征往往存在冗余和噪声深度过深会记住个别样本导致过拟合。min_samples_leaf控制叶子节点的最少样本数调大到10左右会让模型更保守适合压低误报。class_weightbalanced是入侵检测场景里的关键配置。当攻击样本只占3%时默认情况下模型会倾向把所有样本判为正常来降低损失函数这个参数按类别频率自动加权让每一条攻击样本在训练中获得更高的权重。max_featuressqrt让每棵树只随机取特征数量的平方根个候选特征降低树之间的相关性这是随机森林能在大特征集上保持稳定的核心机制。调参时优先动max_depth和min_samples_leaf它们是误报率的主要来源。3.4 训练阶段最常见的三个错误第一个坑是特征泄漏。常见于把整份抓包文件的总量特征标到每条连接上比如统计全部流量里的平均包长再赋给每条流模型在测试集上表现极好一会到在线推理就崩塌。正确做法是每个特征只使用当前时刻及之前的数据。第二个坑是忘记打乱时间序列就随机划分数据集。入侵检测数据天然有时间连续性攻击往往扎堆出现随机划分会让测试集和训练集高度相似。更合理的做法是按时间切分用前7天的数据训练用第8天验证这样评估结果才贴近真实上线表现。第三个坑是忽略类别不平衡。攻击样本只有1%-5%时默认的准确率可能高达95%但全部预测为正常就能拿到这个分数。此时必须看召回率和F1或者改用AUC-PR曲线评估别把准确率当成核心指标。4. 检测精度与漏报率评估指标和阈值调优实战4.1 准确率在入侵检测下的误导性在攻击样本占比只有2%的数据集上一个什么都不做的模型只要把所有样本判为正常准确率就是98%。因此入侵检测系统的评估必须围绕混淆矩阵展开。重点看三个数召回率表示真实攻击中被识别出来的比例漏报率就是1减去召回率误报率表示正常流量中被误判为攻击的比例直接对应安全运营里最反感的告警噪音F1是精确率与召回率的调和平均在不平衡场景下比准确率更真实。上线前额外关注P-R曲线下的面积即AUC-PR。ROC曲线在极端不平衡下偏向乐观P-R曲线能更直观地暴露分类器在少数类上的表现。机器学习检测场景中AUC-PR比AUC-ROC更值得写进汇报材料。4.2 用阈值扫描和类别加权压低漏报率随机森林的predict默认用0.5作为决策阈值但在攻击样本稀缺时模型输出的正类概率整体被压得很低0.5会漏掉大量真实攻击。可以把predict_proba输出的概率保存下来部署前做一次阈值扫描import numpy as np from sklearn.metrics import f1_score, recall_score prob rf.predict_proba(X_test)[:, 1] best_threshold 0.5 best_f1 0.0 for t in np.arange(0.2, 0.8, 0.05): pred (prob t).astype(int) f1 f1_score(y_test, pred, zero_division0) recall recall_score(y_test, pred, zero_division0) print(fthreshold{t:.2f} recall{recall:.3f} f1{f1:.3f}) if f1 best_f1: best_f1 f1 best_threshold t print(f最优阈值: {best_threshold:.2f})阈值越低召回越高误报也随之上升。扫描结果里选择0.35或0.40往往是安全运营能忍受的上限具体看业务对告警量的容忍度。除了调阈值还可以改class_weight为{0:1.0, 1:8.0}让攻击样本在训练时占更高权重这样即使阈值保持在0.5也能获得明显更高的召回率。如果换成LightGBM对应参数是scale_pos_weight设置为负样本数除以正样本数即可。逻辑与class_weight一致但入口更直接适合快速对比实验。4.3 用SHAP回溯误报的决策依据调完阈值运营团队一定会问这条流量为什么被判成攻击SHAP值能给出每条样本上每个特征的贡献度。以树模型为例import shap explainer shap.TreeExplainer(rf) sample X_test[:200] shap_values explainer.shap_values(sample) shap.summary_plot(shap_values[1], sample, feature_namesX.columns)shap_values是一个三维数组对二分类取[1]取出的是正类样本的SHAP值。summary_plot横坐标为正负贡献方向纵坐标按特征重要性排序。如果发现某条误报主要由flow_duration的异常值贡献而正常业务里恰好存在大量长连接就要回到特征工程里增加连接频率的差分特征而不是继续调模型参数。SHAP对随机森林的计算开销较大样本量大时先切片再做解释后续可以用shap.Explainer的独立接口单独计算指定样本。特征解释的价值在于把机器学习模型的行为翻译成安全运营能听懂的因果关系这是系统能否持续被信任的关键。5. 部署环节容易被忽略的两个工程技巧5.1 特征顺序校验比保存模型更关键训练完成之后很多项目用joblib.dump只保存模型文件却忽略了特征顺序。上线时如果线上预处理字段的顺序与训练时不一致模型会静默继承错误的特征映射某列丢了都不会报错但预测结果整体偏移。常见做法是训练结束后输出一份feature_list.json[flow_duration, avg_pkt_len, syn_ratio, dst_port_entropy, conn_per_sec]线上推理模块加载模型时同时加载这个JSON按该顺序对输入DataFrame做列重排。更彻底的办法是把预处理流程封装成sklearn的Pipeline一次保存from sklearn.pipeline import Pipeline pipe Pipeline([ (variance, VarianceThreshold(threshold0.01)), (scaler, StandardScaler()), (clf, RandomForestClassifier(...)) ]) pipe.fit(X_train, y_train)线上调用pipe.predict_proba直接出概率同时通过pipe.named_steps[scaler].mean_检查输入分布是否接近训练分布。均值偏离超过两个标准差时触发重新训练流程。5.2 用PSI监控采样窗口的分布漂移模型运行一段时间后流量特征会随业务版本迭代发生变化。典型场景是应用发版后平均响应包长整体上涨模型输出的正常概率分布整体右移误报率立刻飙升。监控这一漂移的常用指标是PSI即总体稳定性指数。计算方法是将训练集的预测分数划分为10个桶统计线上预测分数在每个桶的占比再计算两者的散度。def psi(expected, actual, bins10): edges np.percentile(expected, np.linspace(0, 100, bins 1)) exp_counts, _ np.histogram(expected, edges) act_counts, _ np.histogram(actual, edges) exp_ratio exp_counts / exp_counts.sum() act_ratio act_counts / act_counts.sum() return np.sum((act_ratio - exp_ratio) * np.log(act_ratio / exp_ratio))PSI小于0.1表示无明显偏移0.1到0.25之间需要警觉超过0.25就必须进入重训流程。这个函数可以挂在已有的定时任务里每天执行一次。检测到漂移后先排查当天是否有版本发布再决定重训数据的切分范围不要盲目追加数据重训先对齐流量侧的变化再训练才能避免二次漂移。本文还有配套的精品资源点击获取