时间序列变化点检测方法对比:网格搜索与分段回归
传统统计方法在时间序列分析中虽然简洁有力,但面对大规模时间序列集合时,扩展性问题立刻凸显。现实中的趋势变化通常微弱、夹杂噪声且不止一条,靠肉眼判断既不靠谱也不现实。更不用说动辄几十上百条的序列,人工识别基本是天方夜谭。
Figure 1: 利用网格搜索确定最优节点数量与位置
解决思路其实很直接——让程序自动定位变化点。估计趋势变化点的手段不少,但本文聚焦的是网格搜索与分段回归的组合,自动确定变化点的数量和位置。
本文提供完整的R和Python代码,Streamlit应用也已准备好可交互体验。
网格搜索定位变化点
网格搜索是一种系统化的优化技术,原理不复杂:先预设离散的参数候选集,然后逐一评估,按指定准则选出表现最好的配置。
在分段回归语境下,搜索的参数就是节点(knot)的数量和位置,每个节点对应一个候选变化点。整体流程大致如下:
- 定义参数空间
- 定义代价函数
- 对每组网格组合拟合分段回归
- 计算评分
- 选取最优结果
为防止过拟合,还需设置节点间的最小距离约束,避免变化点在时间轴上过于集中。
定义代价函数
比较不同变化点数量的模型,首先需要统一的评价尺度。代价函数(也称损失函数、目标函数)用于接收一组模型参数,输出一个标量,衡量模型误差或拟合质量。机器学习和统计建模中的训练、模型选择,本质上都是寻找使代价函数取极值的参数组合。
具体到回归问题,代价函数度量预测值与观测值之间的偏差。常见选项包括:均方误差(MSE)计算平方残差的均值;平均绝对误差(MAE)计算绝对残差的均值;负对数似然(NLL)度量观测数据在给定模型下出现的概率。
但这些指标只关注拟合优度,完全忽略模型复杂度。变化点越多的分段回归模型,几乎总能压得更低的误差——即便它只是在拟合噪声。
应对方法是引入惩罚代价函数,在拟合质量与模型复杂度之间取得平衡。AIC(赤池信息准则)和BIC(贝叶斯信息准则)就是典型代表,它们以负对数似然为基础,加上与估计参数数量挂钩的惩罚项。
AIC:
AIC=-2log(L)+2k
BIC:
BIC=-2log(L)+klog(n)*
其中:
- L = 最大化似然值
- k = 估计参数数量
- n = 观测值数量
Figure 2: 使用BIC评分确定最优节点数量的示例
有了惩罚项这个“刹车”,不必要的复杂度得到抑制,在网格搜索中筛选出真正有意义的变化点个数也更可靠。值得一提的是,BIC的惩罚力度比AIC更重,选出的变化点数量通常更少——换句话说,更保守。
网格搜索工作流程
基础概念铺垫完毕,接下来确定网格搜索的具体流程。典型的变化点检测网格搜索包含四个环节:
- 准备数据并定义搜索空间
- 对给定的节点配置拟合分段回归模型
- 使用惩罚代价函数评估每个配置
- 选出最优节点数量及其位置
函数的输入是时间序列和搜索空间参数,输出是最优节点集合。
Figure 3: 网格搜索函数的工作流程
网格搜索最大的优势是直观、易实现,但计算开销会随候选变化点数量增加而快速膨胀。好在,引入搜索空间约束可以缓解这个问题:既减少了需要评估的配置数量,也在一定程度上防止了过拟合。
具体的约束手段主要有三条:一是排除序列头尾一定比例的观测值,确保每个分段都有足够数据支撑趋势估计;二是强制每个分段包含至少一定数量的观测值,稳定斜率估计,避免模型在短而嘈杂的片段上过度拟合;三是限定节点数量的上限,控制搜索范围。
在设置这些参数时,需要综合考虑观测值数量、序列频率以及业务逻辑。
需要注意的是,不含节点的简单趋势模型作为网格搜索结果的基准。
网格搜索实现
回到之前教程的示例——加利福尼亚州天然气消费者数量,看看网格搜索函数在R中的实现。Python版本可在对应的notebook中找到。
先加载所需库:
library(dplyr)
library(tsibble)
library(plotly)
再引入一组辅助函数,其中包括网格搜索函数 piecewise_regression:
fun_path <- "https://raw.githubusercontent.com/RamiKrispin/the-forecaster/refs/heads/main/functions.R"
source(fun_path)
接着加载序列并整理格式:
path <- "https://raw.githubusercontent.com/RamiKrispin/the-forecaster/refs/heads/main/data/ca_natural_gas_consumers.csv"
ts <- read.csv(path) |>
arrange(index) |>
filter(index > 1986) |>
as_tsibble(index = "index")
ts |> head()
序列是年度数据,index列是时间戳,y列是数值:
# A tsibble: 6 x 2 [1Y]
index y
1 1987 7904858
2 1988 8113034
3 1989 8313776
4 1990 8497848
5 1991 8634774
6 1992 8680613
绘制序列:
p <- plot_ly(data = ts) |>
add_lines(x = ~ index,
y = ~ y, name = "Actual") |>
layout(
title = "Number of Natural Gas Consumers in California",
yaxis = list(title = "Number of Consumers"),
xaxis = list(title = "Source: US energy information administration"),
legend = list(x = legend_x, y = legend_y)
)
p
Figure 4: 加利福尼亚州年度天然气消费者数量。序列呈上升趋势,无季节性模式
用 piecewise_regression 函数识别最优节点数量和位置:
grid <- piecewise_regression(
data = ts,
time_col = "index",
value_col = "y",
max_knots = 4,
min_segment_length = 8,
edge_buffer = 0.05,
grid_resolution = 20
)
搜索空间由以下参数定义:
max_knots- 最大节点数量min_segment_length- 两个节点之间的最小观测值数量edge_buffer- 从序列头尾排除的观测值比例grid_resolution- 每个节点数量对应的最大搜索组合数
这里把 max_knots 设为4,意味着搜索空间里节点数量的范围是0到4。函数会根据约束条件生成候选配置,并自动裁剪掉不满足条件的组合。
运行结果如下:
Testing 0 knot(s)...
Best BIC: 919.28 | RSS: 1.006639e+12 | Tested 1 configurations
Testing 1 knot(s)...
Best BIC: 858.05 | RSS: 182625404855 | Tested 18 configurations
Testing 2 knot(s)...
Best BIC: 844.26 | RSS: 115452860424 | Tested 25 configurations
Testing 3 knot(s)...
Best BIC: 852.94 | RSS: 131838198802 | Tested 5 configurations
Testing 4 knot(s)...
Optimal model: 2 knot(s) with BIC = 844.26
Warning message:
In generate_candidates(k, min_idx, max_idx, min_segment_length) :
Cannot fit 4 knots with min segment length 8
输出按节点数量分组,列出测试过的模型数量,最终确定最优节点数为2。函数还抛出一条警告:受搜索空间约束限制,观测值已不足以容纳4个节点,直接跳过对应拟合。这一行为完全符合预期——约束条件正在起作用。
下面的动画展示搜索空间中所有配置的拟合过程:
Figure 5: 网格搜索过程的动画展示
函数输出中包含搜索过程和最优结果的详细信息。最优节点数:
grid$optimal_knots
[1] 2
节点位置:
grid$knot_dates
[1] 1999 2007
最后,用 plot_knots 函数加上注释,把最优节点叠加到原始序列上进行可视化:
Figure 6: 基于网格搜索结果的最优节点数量与位置
局限性
网格搜索配合分段回归,对于识别趋势变化点的数量和位置,确实是一种可行的方案。它最适合的场景是相对“干净”的时间序列,主导信号就是底层趋势——本文的示例恰好就是这样的。
但现实中的时间序列通常没这么纯粹。季节性、突发水平偏移、异常值都可能干扰甚至扭曲趋势成分。在这些效应存在的情况下,网格搜索可能定位到虚假的变化点,也可能错过真正有意义的趋势断裂点。
一种可行的预处理策略是先做分解(比如用STL),把趋势成分单独提取出来,再在提取到的趋势上执行网格搜索,而不是直接在原始序列上操作。
对于那些结构复杂或噪声较大的序列,能够联合建模趋势与季节性的变化点检测方法,可能会是更合适的选择。
总结
本文的核心思路是把变化点检测转化为一个优化问题:通过网格搜索遍历候选节点配置,用惩罚似然准则(BIC)选出最优模型,配合分段回归完成趋势变化点的自动检测。
分段回归为建模趋势变化提供了一个可解释的框架;网格搜索虽然朴素,但在估计变化点位置上行之有效;BIC这类惩罚准则在拟合优度与模型复杂度之间做了取舍,有效抑制了过拟合倾向;搜索空间的约束——边缘缓冲区、最小分段长度、最大节点数——则进一步稳定了模型、降低了计算开销。
网格搜索在计算效率上确实算不上最优解,但它的透明度是一大优势,作为基线方法和实际工程中的可用方案都没问题。面对更复杂的场景,完全可以在这个框架基础上引入高级优化策略或贝叶斯变化点检测方法。下一篇教程,我们再来聊聊如何把这套方法应用到更复杂的实际场景中去。