稀疏突发计数数据预测:SARIMAX与负二项回归在漏洞活动预测中的实战对比
1. 项目概述当漏洞数据变得“吝啬”时在安全运营中心SOC或者漏洞管理团队里我们常常面临一个尴尬的局面警报要么不来一来就是一大堆。但更多时候我们面对的是另一种更令人头疼的数据——稀疏且突发。想象一下你负责监控一个大型企业网络每天从各种扫描器、蜜罐、入侵检测系统IDS和威胁情报源涌来的原始日志可能有TB级但经过过滤、去重和验证真正能确认的、针对特定资产或漏洞的“有效攻击尝试”事件可能一天就只有寥寥几条甚至连续几天风平浪静。然后在某个毫无征兆的下午突然因为一个刚曝出的高危漏洞比如某个流行中间件的远程代码执行漏洞相关攻击事件在几小时内激增到上百条随后又迅速归于平静。这种数据模式就是典型的“稀疏与突发”。它不像服务器CPU利用率或者网站日活用户数那样平滑、连续可以用经典的时间序列模型如ARIMA轻易拿捏。传统安全事件预测模型如果直接套用常规方法往往会“水土不服”在平静期零值或接近零值预测出毫无意义的低风险而在爆发期又严重滞后无法在攻击浪潮真正来临前给出有效预警。我最近投入了大量时间专门研究如何基于这种“吝啬”的数据来预测漏洞活动。核心思路是不把漏洞攻击事件数当作一个普通的连续数值来预测而是正视其“计数”的本质——它永远是非负整数0 1 2 …并且存在大量零值。这自然引出了两个技术路线的对比一方是经过改造以适应计数数据的时间序列模型另一方则是天生为计数而生的计数模型。本文将深入拆解这两种思路结合具体的模型如SARIMAX和泊松回归进行实战对比分享从数据预处理、模型选型、调参到效果评估的全过程以及我踩过的那些坑。无论你是安全分析师、威胁情报研究员还是对时序预测感兴趣的数据科学家这篇文章都将为你提供一个处理稀疏突发计数数据的完整框架和实操指南。2. 核心思路与模型选型背后的逻辑面对漏洞活动预测我们首先要明确预测目标。通常我们可能想预测“未来24小时内针对某特定CVE编号漏洞的攻击尝试次数”或“未来一周内企业内部被扫描探测的端口次数”。这个目标变量是一个时间序列但每个时间点如每小时、每天的值是一个计数。2.1 为什么通用时间序列模型会“失灵”经典时间序列模型如自回归积分滑动平均模型ARIMA及其季节性版本SARIMA假设数据背后是一个连续的、近似正态分布的随机过程。它们通过历史值AR、历史误差MA和差分I来捕捉趋势和周期性。但当数据是稀疏计数时问题接踵而至负值预测ARIMA模型可能会预测出负的攻击次数这显然没有物理意义。对零值不敏感模型将零视为一个普通的低数值无法有效建模“长时间无事件”这种状态背后的机制可能是防御有效也可能是攻击者暂时未关注。突发处理能力差当出现一个巨大的离群值爆发时模型需要很长时间才能“学习”到这个突变导致预测严重滞后且可能过度拉高后续平静期的预测值。为了解决这些问题我们引入了SARIMAX模型。这里的“X”代表外生变量。核心改进思路是我们仍然使用时序模型的结构来捕捉自相关性和季节性但通过连接函数Link Function和特定的误差分布使其适应计数数据。一种常见做法是假设数据服从泊松分布或负二项分布并使用对数连接函数确保预测值为正。这本质上是将通用时间序列模型“包装”成一个广义线性模型GLM的框架。它的优势在于能直接利用成熟的时间序列建模能力处理季节性和趋势。2.2 为什么计数模型是“天选之子”另一条路是直接使用专为计数数据设计的回归模型其代表是泊松回归和负二项回归。它们从根上就承认数据是非负整数的特性。泊松回归假设在给定的时间区间内事件发生的次数服从泊松分布。其核心特征是期望均值等于方差。它通过一个对数连接函数将预测的均值与一系列特征包括时间特征线性关联起来。负二项回归可以看作是泊松回归的扩展。它放松了“均值方差”这个强假设引入了一个离散参数来处理“过离散”现象——即数据的方差远大于均值这在突发性攻击数据中极其常见。对于时间序列预测我们需要将这些计数模型“时间序列化”。方法很简单将时间特征作为核心的自变量。例如对于日度数据我们可以构造以下特征day_of_week(周一至周日独热编码)is_weekend(是否周末)month(月份考虑季节性)time_index(一个简单的线性趋势项)lag_1,lag_2,lag_7(过去1天、2天、7天的攻击次数用来捕捉自相关性)这样计数模型就具备了处理时间依赖的能力。它的优势在于模型假设更贴近数据本质对零值和过离散有天然的良好处理能力模型解释性也更强。2.3 模型选型决策树在实际项目中我的选型逻辑通常遵循以下路径graph TD A[开始稀疏突发计数数据] -- B{数据探索检查过离散程度}; B -- 方差 ≈ 均值 -- C[选择泊松回归]; B -- 方差 均值常见 -- D[选择负二项回归]; C D -- E{是否存在强季节性或复杂趋势}; E -- 是 且季节模式稳定 -- F[优先尝试SARIMAX计数版本]; E -- 否 或季节模式复杂/多变 -- G[优先尝试计数模型时间特征]; F -- H[模型训练与评估]; G -- H; H -- I{验证集效果对比}; I -- SARIMAX更优 -- J[采用SARIMAX 关注外生变量]; I -- 计数模型更优 -- K[采用计数模型 可尝试加入滞后项];注意这个决策树不是绝对的。最可靠的方法是进行基准测试。我会同时构建SARIMAX如基于泊松分布的和负二项回归模型在一个预留的验证集上比较它们的预测性能。性能指标不能只看均方误差MSE因为对计数数据不敏感应重点关注平均绝对误差MAE、泊松偏差或零膨胀部分的预测准确率。3. 数据准备与特征工程的魔鬼细节模型再高级垃圾数据进去出来的也是垃圾。对于漏洞活动数据预处理和特征工程至关重要且与传统连续值时序数据有所不同。3.1 数据收集与聚合数据通常来自多个源头漏洞扫描日志Nessus, OpenVAS, Qualys等工具的每日扫描结果按CVE或资产聚合。网络流量日志IDS/IPS如Suricata, Snort警报防火墙拒绝日志。蜜罐交互数据记录攻击者探测、利用尝试的次数。威胁情报订阅关于特定漏洞被活跃利用的IOC失陷指标数量。第一步是时间对齐与聚合。确定预测的最小时间单位如1小时、1天。将原始事件日志按此单位进行聚合计算每个时间桶内的“攻击事件计数”。这里就会产生大量的零值桶。3.2 关键特征构建除了上一节提到的基础时间特征对于安全领域以下特征往往有奇效漏洞生命周期特征days_since_disclosure: 漏洞公开至今的天数。通常刚公开时活动激增随后衰减。has_exploit_public: 布尔值表示是否有公开的利用代码PoC/Exp。这是最强的活动驱动因子之一。severity_score: CVSS基础评分。高分漏洞更受关注。asset_value: 受影响资产的重要性权重需内部定义。外部威胁情报特征ti_mentions_24h: 过去24小时内在特定威胁情报论坛、Twitter、暗网提及该漏洞的次数可通过API获取。malware_family_activity: 已知利用该漏洞的恶意软件家族的近期活动水平。滞后特征与滚动统计lag_1,lag_2,lag_3,lag_7: 直接滞后项捕捉短期自相关。rolling_mean_7: 过去7天的移动平均反映近期基线。rolling_max_7: 过去7天的最大值捕捉近期是否已有过爆发。zero_run_length: 当前零值持续的长度。这个特征对于区分“常态平静”和“爆发后的平静”很有用。3.3 处理“零膨胀”问题我们的数据可能包含两种零一种是“真零”攻击者确实没来另一种是“漏报零”由于检测能力限制没发现。当零值比例极高时如80%就需要考虑零膨胀模型如零膨胀泊松回归ZIP或零膨胀负二项回归ZINB。这些模型内部包含两个过程一个逻辑回归过程判断当前时间点是否“必然为零”即处于非活动状态另一个计数过程泊松/负二项在非“必然为零”时生成计数。实操心得不要一上来就用零膨胀模型。先拟合一个普通的负二项回归检查残差。如果模型在零值区域系统性预测不佳再考虑升级到ZINB。零膨胀模型参数更多更易过拟合尤其是在数据量不大的情况下。4. 模型实战SARIMAX vs. 负二项回归我们以一个模拟数据集为例假设我们预测某企业面向互联网的SSH服务每日遭受的暴力破解尝试次数。数据具有每周周期性周末较低且偶尔因新的暴力破解工具发布而突发激增。4.1 基于Statsmodels的计数SARIMAX实现Python的statsmodels库的SARIMAX模块支持通过exog参数引入外生变量并通过指定mle_regressionFalse和使用log连接函数来近似实现计数特性。但更严谨的做法是使用GeneralizedLinearModelGLM框架下的Poisson或NegativeBinomial家族并手动加入ARIMA结构这比较复杂。这里展示一个使用statsmodels的GLM与滞后项结合的方法模拟计数时序模型。import pandas as pd import numpy as np import statsmodels.api as sm from statsmodels.genmod.generalized_linear_model import GLM from statsmodels.genmod.families import NegativeBinomial # 假设 df 是一个包含‘date’和‘attack_count’的DataFrame df[time_index] range(len(df)) df[day_of_week] df[date].dt.dayofweek # 创建滞后特征 for lag in [1, 2, 7]: df[flag_{lag}] df[attack_count].shift(lag) # 创建周末标识 df[is_weekend] df[day_of_week].isin([5, 6]).astype(int) # 删除因滞后产生的NaN行 df_model df.dropna().copy() # 定义特征X和目标y # 这里将滞后项、时间趋势、周几都作为外生变量 exog_features [time_index, is_weekend] [flag_{i} for i in [1, 2, 7]] # 为周几创建虚拟变量独热编码 exog_with_dummies pd.get_dummies(df_model[[day_of_week] exog_features], columns[day_of_week], drop_firstTrue) X sm.add_constant(exog_with_dummies) # 添加常数项 y df_model[attack_count] # 使用负二项回归GLM框架 # 注意statsmodels的NegativeBinomial家族默认使用log连接且需要指定alpha参数离散参数 # 这里使用‘nb2’方差形式并让模型通过MLE估计alpha try: nb_model sm.GLM(y, X, familysm.families.NegativeBinomial(alpha1.0)) # 初始化alpha nb_result nb_model.fit(methodlbfgs, maxiter1000) # 使用优化器 print(nb_result.summary()) except Exception as e: print(fGLM拟合失败: {e}) # 回退到泊松回归它更稳定但可能不适合过离散数据 poisson_model sm.GLM(y, X, familysm.families.Poisson()) poisson_result poisson_model.fit() print(poisson_result.summary())这段代码的关键在于将时间序列结构滞后项、趋势、季节性通过特征工程的方式融入到广义线性模型中。这本质上就是一个“计数模型”的思路但它清晰地利用了时间序列的依赖关系。4.2 基于Scikit-learn与标准化的负二项回归对于更复杂的特征工程和管道化操作scikit-learn的兼容性更好。我们可以使用statsmodels的离散模型或scikit-learn的PoissonRegressor和NegativeBinomialRegressor需sklearn版本支持或使用glmnet等库。这里以类scikit-learn的方式组织from sklearn.linear_model import PoissonRegressor from sklearn.preprocessing import StandardScaler, OneHotEncoder from sklearn.compose import ColumnTransformer from sklearn.pipeline import Pipeline from sklearn.model_selection import TimeSeriesSplit # 准备特征和数据 features [time_index, is_weekend, lag_1, lag_2, lag_7, day_of_week] X_raw df_model[features] y df_model[attack_count] # 定义预处理数值特征标准化分类特征独热编码 numeric_features [time_index, lag_1, lag_2, lag_7] categorical_features [day_of_week, is_weekend] preprocessor ColumnTransformer( transformers[ (num, StandardScaler(), numeric_features), (cat, OneHotEncoder(dropfirst), categorical_features) ]) # 创建泊松回归管道 poisson_pipeline Pipeline(steps[ (preprocessor, preprocessor), (regressor, PoissonRegressor(alpha0.01, max_iter1000)) # alpha是L2正则化强度 ]) # 使用时间序列交叉验证 tscv TimeSeriesSplit(n_splits5) mae_scores [] for train_idx, test_idx in tscv.split(X_raw): X_train, X_test X_raw.iloc[train_idx], X_raw.iloc[test_idx] y_train, y_test y.iloc[train_idx], y.iloc[test_idx] poisson_pipeline.fit(X_train, y_train) y_pred poisson_pipeline.predict(X_test) mae np.mean(np.abs(y_test - y_pred)) mae_scores.append(mae) print(f泊松回归平均MAE: {np.mean(mae_scores):.2f})注意事项对于计数模型特别是泊松回归特征缩放很重要。虽然模型本身对线性变换的尺度不敏感因为连接函数是非线性的但正则化项如L2会受到特征尺度影响。使用StandardScaler是个好习惯。另外PoissonRegressor预测的是计数的期望值均值通常是浮点数需要根据业务决定是四舍五入还是保留小数。4.3 模型评估超越MAE的指标对于稀疏突发数据MAE可能被大量的零值所主导掩盖了模型对突发峰值预测的好坏。我们需要多维度评估按计数区间评估将测试集的真实值分成几个区间如[0],[1-5],[6-20],[20]分别计算每个区间内的MAE或预测偏差。这能看出模型在平静期和活跃期的表现。峰值捕获能力定义一个“峰值”阈值如历史计数的95分位数。计算模型预测值超过该阈值的时刻中有多少比例与真实峰值时刻重合精确率以及真实峰值有多少被预测出来召回率。概率校准评估对于泊松/负二项模型它们本质上是概率模型。我们可以检查预测分布的可靠性。例如对于所有预测均值为λ的点真实值落在预测分布如泊松(λ)的某个置信区间如90%内的比例是否接近90%。零值预测准确率单独计算模型预测为零且真实值为零的准确率这对运营成本减少误报警很有意义。5. 常见陷阱、调试心得与进阶思考在实际操作中我遇到了无数坑这里分享几个最典型的。5.1 过离散与模型选择失误问题一开始使用了泊松回归模型训练似乎正常但预测值方差远小于真实值方差对突发峰值的预测严重不足。诊断绘制真实值的均值-方差关系。计算np.var(y_true) / np.mean(y_true)如果远大于1比如2则存在明显的过离散。解决立即切换到负二项回归。负二项回归引入了一个离散参数在statsmodels中常称为alpha专门捕捉超出泊松假设的变异。如果使用scikit-learn的PoissonRegressor可以考虑换成支持负二项分布的库或者尝试使用准泊松回归它允许方差与均值成比例但不指定完整分布。5.2 外生变量的“未来泄露”问题在构建“过去24小时威胁情报提及数”这个特征时不小心使用了包含预测时间点在内的数据导致模型在训练和验证时看到了“未来”信息得到虚假的高精度。解决对于任何需要聚合计算的特征必须严格进行时间窗滞后。例如预测t时刻的攻击数使用的特征数据只能来自t-1时刻及以前。在代码中这通常意味着对特征列使用.shift(1)操作。在管道中可以使用sklearn的FunctionTransformer自定义特征计算确保时序安全。5.3 突发导致的历史数据“污染”问题一次巨大的攻击爆发一个离群值会显著拉高移动平均rolling_mean等特征导致爆发过后很长一段时间模型仍会预测较高的活动水平形成“长尾假警报”。解决使用稳健的滚动统计用中位数rolling_median代替均值或用修剪均值。对特征进行缩尾处理将滚动统计值中超过99分位数的值截断到99分位数。引入“爆发后衰减”特征创建一个标志位在爆发后的N个时间段内设为1并作为一个特征输入模型让模型学习爆发后的衰减模式。5.4 模型持续学习与更新漏洞威胁 landscape 变化很快。一个静态模型很快就会过时。必须建立模型重训机制。滑动窗口训练始终只用最近N个月的数据训练模型。性能监控与触发重训持续监控模型在最新数据上的预测误差如MAE。当误差连续多日超过阈值或遇到概念漂移如新型攻击模式出现时自动触发模型重训。A/B测试框架将新训练的模型与当前生产模型进行A/B测试在影子模式下运行一段时间确认其性能提升后再切换。5.5 从预测到决策设定动态阈值模型输出的是未来一段时间内攻击次数的期望值。安全运营需要的是“是否报警”的二元决策。直接将预测值四舍五入后与固定阈值如5次比较效果往往不好。更好的做法根据预测值计算攻击次数超过某个业务可接受阈值如k的概率。例如对于泊松分布P(X k) 1 - sum_{i0}^{k} (exp(-λ) * λ^i / i!)。然后根据运营成本误报成本 vs. 漏报成本设定一个概率阈值如P(X 5) 0.3则报警。这个概率阈值可以通过历史数据上的成本效益分析来优化。最终选择SARIMAX还是计数模型没有银弹。在我的多数实践中特征工程良好的负二项回归或零膨胀负二项回归因其灵活性、解释性和对过离散数据的天然适配性往往成为首选。而SARIMAX在具有非常强且稳定的季节性模式如每日、每周规律极其明显时可能更有优势。最重要的永远是理解你的数据构建合理的评估框架然后用实验结果说话。这个从稀疏突发的安全日志中挖掘预测价值的过程本身就是一场对抗数据不确定性的精彩攻防。

相关新闻